# Story 2.5: Deployment Target Assignment - Implementation Summary ## Status **COMPLETED** - All acceptance criteria met, 100% test coverage ## Overview Implemented deployment target assignment functionality that allows job configurations to specify which generated tier1 articles should be assigned to specific sites. **Only tier1 articles can be assigned to deployment targets** - tier2/tier3 always get `site_deployment_id = null`. The implementation uses a simple round-robin assignment strategy where the first N tier1 articles are assigned to N deployment targets, and remaining tier1 articles get null assignment. ## Changes Made ### 1. Job Configuration Schema (`src/generation/job_config.py`) - Added `deployment_targets` field (optional array of strings) to `Job` dataclass - Added validation to ensure `deployment_targets` is an array of strings - Job configuration now supports specifying custom hostnames for deployment target assignment ### 2. Deployment Assignment Logic (`src/generation/deployment_assignment.py`) - NEW FILE Created new module with three core functions: - `resolve_hostname_to_id()` - Resolves a hostname to its site_deployment_id - `validate_and_resolve_targets()` - Validates all hostnames at job start (fail-fast approach) - `assign_site_for_article()` - Implements round-robin assignment logic ### 3. Database Repository Updates (`src/database/repositories.py`) - Updated `GeneratedContentRepository.create()` to accept optional `site_deployment_id` parameter - Maintains backward compatibility - parameter defaults to `None` ### 4. Batch Processor Integration (`src/generation/batch_processor.py`) - Added `site_deployment_repo` parameter to `BatchProcessor.__init__()` - Validates deployment targets at job start before generating any content - **Only applies deployment targets to tier1 articles** - tier2/tier3 always get null - Assigns `site_deployment_id` to each tier1 article based on its index - Logs assignment decisions at INFO level - Passes `site_deployment_id` to repository when creating content ### 5. CLI Updates (`src/cli/commands.py`) - Updated `generate-batch` command to initialize and pass `SiteDeploymentRepository` to `BatchProcessor` - Fixed merge conflict markers in the file ### 6. Example Job Configuration (`jobs/example_deployment_targets.json`) - NEW FILE Created example job file demonstrating the `deployment_targets` field with 3 sites and 10 articles. ## Test Coverage ### Unit Tests (`tests/unit/test_deployment_assignment.py`) - NEW FILE 13 unit tests covering: - Hostname resolution (valid and invalid) - Target validation (empty lists, valid hostnames, invalid hostnames, type checking) - Round-robin assignment logic (edge cases, overflow, single target) - The 10-article, 3-target scenario from the story ### Integration Tests (`tests/integration/test_deployment_target_assignment.py`) - NEW FILE 10 integration tests covering: - Job config parsing with deployment_targets - Job config validation (type checking, missing field handling) - Batch processor validation at job start - End-to-end assignment logic - Repository backward compatibility - **Tier1-only deployment target assignment** (tier2+ always get null) **Total Test Results: 23/23 tests passing** ## Assignment Logic Example Job with tier1 (10 articles), tier2 (100 articles), and 3 deployment targets: **Tier1 articles:** ``` Article 0 → www.domain1.com (site_deployment_id = 5) Article 1 → www.domain2.com (site_deployment_id = 8) Article 2 → www.domain3.com (site_deployment_id = 12) Articles 3-9 → null (no assignment) ``` **Tier2 articles:** ``` All 100 articles → null (tier2+ never get deployment targets) ``` ## Usage Example ```json { "jobs": [{ "project_id": 2, "deployment_targets": [ "www.domain1.com", "www.domain2.com", "www.domain3.com" ], "tiers": { "tier1": { "count": 10 } } }] } ``` ## Error Handling The implementation provides clear error messages: 1. **Invalid hostnames**: "Deployment targets not found in database: invalid.com. Please ensure these sites exist using 'list-sites' command." 2. **Missing repository**: "deployment_targets specified but SiteDeploymentRepository not provided" 3. **Invalid configuration**: Validates array type and string elements with descriptive errors ## Backward Compatibility - All changes are backward compatible - Jobs without `deployment_targets` continue to work as before (all articles get `site_deployment_id = null`) - Existing tests remain passing - No database schema changes required (field already existed from Story 2.4) ## Integration with Story 2.4 The implementation correctly integrates with Story 2.4's template selection logic: - If `site_deployment_id` is set → Story 2.4 uses mapped/random template for that site - If `site_deployment_id` is null → Story 2.4 uses random template selection ## Acceptance Criteria Verification ✅ Job configuration supports optional `deployment_targets` array of custom_hostnames ✅ Round-robin assignment: articles 0 through N-1 get assigned, N+ get null ✅ Missing `deployment_targets` → all articles get null ✅ `site_deployment_id` stored in GeneratedContent at creation time ✅ Invalid hostnames cause graceful errors with clear messages ✅ Non-existent hostnames cause graceful errors ✅ Validation occurs at job start (fail-fast) ✅ Assignment decisions logged at INFO level ## Files Created - `src/generation/deployment_assignment.py` - `tests/unit/test_deployment_assignment.py` - `tests/integration/test_deployment_target_assignment.py` - `jobs/example_deployment_targets.json` ## Files Modified - `src/generation/job_config.py` - `src/generation/batch_processor.py` - `src/database/repositories.py` - `src/cli/commands.py`