# Batch 44 — Implementation Plan **Date drafted**: 2026-05-13 **Drafted in conversation**: `/autodev` session, prior to batch 44 start **Status**: PLAN ONLY — do NOT start implementation until next `/autodev` invocation. **Cycle**: 1 ## Why a plan instead of just doing it Two architectural decisions converged this batch and need to be executed as ONE atomic refactor; splitting them would leave the codebase in a transiently inconsistent state. 1. **C11's internal flight-state gate violates SRP.** AZ-317 placed an ON_GROUND check inside `HttpTileUploader`. "Upload bytes" and "decide when uploading is safe" are different responsibilities; the latter is an orchestrator-level policy decision that belongs in C12. 2. **The C12 package name `c12_operator_tooling` understates its role.** It owns orchestrators (build-cache, post-landing upload, operator-reloc) — "tooling" reads as a grab-bag. Rename: `c12_operator_tooling` → `c12_operator_orchestrator`. ## Decision recap (from the prior conversation) - The on-ground signal will be the **presence of the `flight_footer` FDR record** (kind already registered in `KNOWN_PAYLOAD_KEYS`; producer is C13's AZ-292, shipped) with `clean_shutdown == True`. No new FDR record kind needs to be defined. No 30-second threshold. No dependency on E-C8. - C11's `HttpTileUploader` becomes a dumb pipe: read pending from C6 → POST to ingest → mark uploaded. The flight-state-gating Protocol/Gate/error are deleted from C11. - C12's `PostLandingUploadOrchestrator` (AZ-329) performs the footer check; if the footer for `flight_id` exists and `clean_shutdown == True`, it calls `tile_uploader.upload_pending_tiles(UploadRequest(flight_id=...))`. Otherwise it refuses with `FlightStateNotConfirmedError`. ## Tasks delivered in batch 44 | Task | Type | Complexity | Notes | |------|------|-----------|-------| | **Rename** (no ticket, hygiene) | refactor | ~2pt | `c12_operator_tooling` → `c12_operator_orchestrator`. ~57 files touched. | | **C11 gate removal** (supersedes AZ-317, partial revert of AZ-319) | refactor | ~3pt | Create new tracker ticket `AZ-XXX` (or reopen AZ-317 marked superseded). | | **AZ-329** (rewritten) | feature | 3pt | `PostLandingUploadOrchestrator` against `flight_footer`. | | **AZ-330** | feature | 3pt | `OperatorReLocService` — unchanged from existing spec. | | **Total** | | **~11pt** | Within batch cap (4 tasks; 20-point budget). | ## Phase plan (execute in this order) ### Phase A — Component rename (`c12_operator_tooling` → `c12_operator_orchestrator`) **Goal**: one atomic rename with no behavioural change. Done before any code edits so subsequent phases work in the new namespace. **Source moves**: - `src/gps_denied_onboard/components/c12_operator_tooling/` → `src/gps_denied_onboard/components/c12_operator_orchestrator/` (recursive `git mv`). - `tests/unit/c12_operator_tooling/` → `tests/unit/c12_operator_orchestrator/` (recursive `git mv`). **Symbol updates** (kept as-is — only the package path changes): - Class names: `C12Config`, `C12CompanionConfig`, `C12BuildCacheConfig`, `OperatorToolServices` — KEEP. The `C12` prefix is the component number (12), not the word "tooling". `OperatorToolServices` stays because the CLI binary itself is unchanged. - Logger names: `c12_operator_tooling` → `c12_operator_orchestrator` (all occurrences in `c12_factory.py` and module-level `logging.getLogger("c12_operator_tooling.*")` calls). - Config-block slug: `register_component_block("c12_operator_tooling", C12Config)` → `register_component_block("c12_operator_orchestrator", C12Config)`. **Note**: this is a config-schema-key change; the deployment env will need to update YAML/JSON config files that key on this slug. List of changes goes into Phase F. - CMake flag: `BUILD_C12_OPERATOR_TOOLING` → `BUILD_C12_OPERATOR_ORCHESTRATOR` in `cmake/build_options.cmake`. - pyproject.toml console script: `operator-tool = "gps_denied_onboard.components.c12_operator_tooling.cli:cli"` → `... c12_operator_orchestrator.cli:cli`. **KEEP the binary name `operator-tool`** for operator UX continuity; this is the only ambiguity — see Open Questions. **Import updates** (mechanical replace across the workspace): ``` from gps_denied_onboard.components.c12_operator_tooling → from gps_denied_onboard.components.c12_operator_orchestrator ``` Applies to: c12 source (~30 files), c12 tests (~12 files), `runtime_root/c12_factory.py`, `runtime_root/__init__.py`, `tests/unit/test_ac1_scaffold_layout.py`, `tests/unit/test_ac3_compose_files.py`. **Docs / config updates** (deferred to Phase F so renaming stays code-only here): - `_docs/02_document/module-layout.md` — section heading, `Owns`/`Imports from`/`Consumed by` paths. - `_docs/02_document/components/13_c12_operator_tooling/` → `13_c12_operator_orchestrator/` (directory rename). - `_docs/02_document/contracts/c12_operator_tooling/` → `c12_operator_orchestrator/` (directory rename). - `_docs/02_document/epics.md`: "E-C12 Operator Pre-flight Tooling" → "E-C12 Operator Pre-flight Orchestrator". - `_docs/02_document/architecture.md`, `FINAL_report.md`, `glossary.md`, `data_model.md`, `system-flows.md`, `deployment/*`: all references. - Task specs: `AZ-326`, `AZ-327`, `AZ-328`, `AZ-329`, `AZ-330`, `AZ-489` `Component:` field; same for `AZ-401`, `AZ-403` references. - Already-completed batch reports and cumulative reviews — leave as historical record (do NOT rewrite history). **Verification after Phase A**: - `git grep c12_operator_tooling -- ':!_docs/03_implementation/batch_*' ':!_docs/03_implementation/cumulative_review_*' ':!_docs/03_implementation/reviews/*'` returns zero hits. - `pytest tests/unit/c12_operator_orchestrator -q` passes (no behavioural change, just the rename). - Full unit suite still **1522 passed, 80 skipped** (same as batch 43 baseline). ### Phase B — Remove C11 internal flight-state gate **Files to delete**: - `src/gps_denied_onboard/components/c11_tile_manager/flight_state_gate.py` - `tests/unit/c11_tile_manager/test_flight_state_gate.py` (or `test_az317_flight_state_gate.py` — locate the actual filename). **Files to modify**: - `src/gps_denied_onboard/components/c11_tile_manager/interface.py`: - Remove `FlightStateSource` Protocol class. - Update `__all__`. - Update module docstring. - `src/gps_denied_onboard/components/c11_tile_manager/errors.py`: - Remove `FlightStateNotOnGroundError` class + `__all__` entry + docstring mention. - `src/gps_denied_onboard/components/c11_tile_manager/_types.py`: - Audit `FlightStateSignal` usage. If only the gate referenced it → delete. If `HttpTileUploader` still emits it in FDR records or elsewhere → keep as a closed enum for `confirm_flight_state()` return shape, BUT note that `TileUploader.confirm_flight_state()` is itself going away (see next bullet). - The `confirm_flight_state` method on `TileUploader` Protocol exists per `interface.py` — this method only makes sense if there's a gate. Decision: **drop it**. Remove from Protocol, from `HttpTileUploader`, from any test fakes. - `src/gps_denied_onboard/components/c11_tile_manager/tile_uploader.py`: - Remove `FlightStateGate` import. - Remove `flight_state_gate: FlightStateGate` constructor parameter. - Remove the `confirm_on_ground()` call at the top of `upload_pending_tiles`. - Remove the `_LOG_KIND_REFUSED` / pass-through `confirm_flight_state` method. - Re-test all upload paths now run without the gate. - `src/gps_denied_onboard/components/c11_tile_manager/__init__.py`: - Remove `FlightStateGate`, `FlightStateNotOnGroundError`, `FlightStateSource` from imports + `__all__`. - Keep `FlightStateSignal` only if `confirm_flight_state` survives; otherwise remove. - `src/gps_denied_onboard/components/c11_tile_manager/idempotent_retry.py`: - Audit for `FlightStateGate` / `FlightStateNotOnGroundError` references. The retry decorator wraps `TileUploader`; if it catches the gate error it must stop. - `src/gps_denied_onboard/runtime_root/c11_factory.py`: - Remove `build_flight_state_gate(*, source: FlightStateSource) -> FlightStateGate`. - Remove `flight_state_gate=` from `build_tile_uploader(...)`. - Remove `FlightStateSource` from imports + module docstring. - `tests/unit/c11_tile_manager/test_tile_uploader.py`: - Remove all test cases that mock the gate / assert `confirm_on_ground` was called / assert `FlightStateNotOnGroundError` semantics. - Replace any fixture that injects a fake gate with a no-gate construction. - `tests/unit/c11_tile_manager/test_idempotent_retry.py`: - Adjust if any test cared about the gate's error class. - `tests/unit/c11_tile_manager/test_protocol_conformance.py`: - Drop `FlightStateSource` conformance assertions. **Files to LEAVE ALONE** (these reference the gate in completed task specs / batch reports — historical record): - `_docs/02_tasks/done/AZ-317_c11_flight_state_gate.md` — leave the spec as-is; mark `Status: superseded by batch 44 SRP refactor` in a one-line annotation at the top. - `_docs/02_tasks/done/AZ-319_c11_tile_uploader.md` — leave; the relevant gate text is now historical. - `_docs/03_implementation/batch_*_report.md`, `cumulative_review_*` — historical, untouched. - `_docs/03_implementation/reviews/batch_38_review.md`, `batch_39_review.md` — historical. **Contract document update** (Phase F covers): - `_docs/02_document/contracts/c11_tilemanager/tile_uploader.md` v1.0.0 → v2.0.0. Remove the gate clauses, the `confirm_flight_state` method row, `FlightStateNotOnGroundError`. **Verification after Phase B**: - `git grep -E 'FlightStateGate|FlightStateNotOnGroundError|FlightStateSource' src/ tests/` returns zero hits. - `pytest tests/unit/c11_tile_manager -q` passes. - Full unit suite passes (count will drop by ~10–15 tests; document the new baseline in the batch 44 report). ### Phase C — AZ-329 task spec rewrite Rewrite `_docs/02_tasks/todo/AZ-329_c12_post_landing_upload.md` from scratch around the new design. Outline: - **Description**: `PostLandingUploadOrchestrator` reads the C13 FDR for `flight_id`, locates the `flight_footer` record (the C13 writer emits exactly one per flight on clean shutdown), and if found with `clean_shutdown == True`, calls `tile_uploader.upload_pending_tiles(UploadRequest(flight_id=..., ...))`. Footer absent → flight didn't terminate cleanly → refuse. Footer present but `clean_shutdown == False` → operator inspects manually → refuse. - **Dependencies**: `AZ-326`, `AZ-319` (post-gate-removal `TileUploader`), `AZ-272` (FdrRecord schema; `flight_footer` kind already there), `AZ-292` (C13 footer producer; already shipped), `AZ-263`, `AZ-269`, `AZ-266`. - **DTOs** (in renamed `c12_operator_orchestrator/_types.py`): - `PostLandingUploadRequest(flight_id: UUID, satellite_provider_url: str, batch_size: int = 50)`. - Reuses C11's `UploadBatchReport` via the AZ-507 consumer-side cut pattern (mirror locally as `UploadBatchReportCut`). - **Reader** (`fdr_footer_reader.py`): `FdrFooterReader` Protocol + `LocalFdrFooterReader` concrete. Iterates `//segment_*.fdr` newest-to-oldest, returns the first `flight_footer` record found (or `None`). Streaming — never loads full segments into memory. - **Errors** (`c12_operator_orchestrator/errors.py`): - `FlightStateNotConfirmedError(Exception)` with `flight_id: str`, `not_confirmed_reason: Literal["flight_id_not_found", "footer_missing", "unclean_shutdown", "fdr_unreadable"]`, `remediation: str`. - **Acceptance criteria** (~8 ACs, all unit-testable via fake `FdrFooterReader` + fake `TileUploader`): - AC-1: footer present + `clean_shutdown=True` → upload invoked, returns `UploadBatchReport`. - AC-2: footer absent → `FlightStateNotConfirmedError("footer_missing")`. - AC-3: footer present + `clean_shutdown=False` → `FlightStateNotConfirmedError("unclean_shutdown")`. - AC-4: `//` does not exist → `FlightStateNotConfirmedError("flight_id_not_found")`. - AC-5: FDR parse error mid-stream → `FlightStateNotConfirmedError("fdr_unreadable: ...")`. - AC-6: footer search starts from newest segment (don't scan all segments if the latest has the footer). - AC-7: Returns C11's `UploadBatchReport` unchanged on success (passthrough). - AC-8: api_key in `PostLandingUploadRequest` is `SecretStr`; REDACTED in logs. - AC-9: integration test with a real FDR fixture that contains a clean-shutdown footer. - AC-10: integration test with a real FDR fixture that contains a `clean_shutdown=False` footer. - **Non-Functional Requirements**: - Footer read completes ≤ 1 s wall-clock even for 64 GB of FDR segments (newest-segment-first short-circuit). - Memory peak ≤ 50 MB (streaming reader). - **Excluded explicitly**: - Any `flight_state.tick` / `state.tick` payload-field reading. - The 30-second contiguous-ON_GROUND threshold. - A "force-upload" override (intentionally not supported). Update `_docs/02_document/components/13_c12_operator_orchestrator/tests.md` C12-IT-03 to match (footer-based test, drop the 30s assertion). ### Phase D — AZ-329 implementation In renamed `c12_operator_orchestrator/`: - **New files**: - `post_landing_upload.py` — `PostLandingUploadOrchestrator` class + the AZ-507 consumer-side cut (`TileUploaderCut` Protocol matching C11's `upload_pending_tiles(UploadRequest)`). - `fdr_footer_reader.py` — Protocol + concrete `LocalFdrFooterReader`. - **Modified files**: - `_types.py` — `PostLandingUploadRequest`, `UploadBatchReportCut`. - `errors.py` — `FlightStateNotConfirmedError`. - `config.py` — extend `C12Config` with `post_landing: C12PostLandingConfig(fdr_root: Path)`. - `cli.py` — `upload-pending` subcommand (already a placeholder in `cli.py` per the AZ-326 ship); wire to `services.post_landing_upload_orchestrator`. Map `FlightStateNotConfirmedError` → `EXIT_FLIGHT_STATE_NOT_CONFIRMED` (already defined). - `__init__.py` — re-export new public symbols. - **Composition root** (`runtime_root/c12_factory.py`): - Add `build_post_landing_upload_orchestrator(config, *, tile_uploader: TileUploaderCut) -> PostLandingUploadOrchestrator`. - Extend `OperatorToolServices` with `post_landing_upload_orchestrator: PostLandingUploadOrchestrator | None = None`. - Extend `build_operator_tool(...)` aggregator: when a `tile_uploader` is provided, build and wire. - **Tests** (`tests/unit/c12_operator_orchestrator/`): - `test_post_landing_upload_orchestrator.py` — covers AC-1 through AC-8 with fakes. - `test_fdr_footer_reader.py` — covers AC-5 + the streaming NFR. - Integration test fixtures for AC-9 + AC-10: scripted FDR segments with / without `clean_shutdown=True` footer. Live under `tests/fixtures/c12_orchestrator/fdr/` or similar. ### Phase E — AZ-330 implementation Implement per existing `_docs/02_tasks/todo/AZ-330_c12_operator_reloc_service.md`, adjusted only for the renamed package. Re-use `LatLonAlt` from `src/gps_denied_onboard/_types/geo.py` (the shared definition exists — AZ-330's risk-mitigation note flagged this; we confirmed it during the prior session). - **New files** (in `c12_operator_orchestrator/`): - `operator_reloc_service.py` — `OperatorReLocService` class. - `operator_command_transport.py` — `OperatorCommandTransport` Protocol. - **Modified files**: - `_types.py` — `ReLocHint` (consume shared `LatLonAlt`). - `errors.py` — `GcsLinkError`. - `cli.py` — wire `reloc-confirm` subcommand. - `__init__.py` — re-exports. - **Composition root**: - `build_operator_reloc_service(...)` factory. - Extend `OperatorToolServices` with `operator_reloc_service: OperatorReLocService`. - **Tests**: 10 ACs per the existing spec — all unit-testable via fake `OperatorCommandTransport`. - **Contract document**: `_docs/02_document/contracts/c12_operator_orchestrator/operator_command_transport.md` (already exists at the old path — gets moved during Phase A). ### Phase F — Documentation updates - `_docs/02_document/module-layout.md`: - Section heading and all paths: `c12_operator_tooling` → `c12_operator_orchestrator`. - C11 component section: drop `FlightStateSource`, `FlightStateGate` from the Public API + Internal lists. - `_docs/02_document/components/13_c12_operator_orchestrator/description.md`: - Rewrite the C12 § 2 row for `trigger_post_landing_upload` around the footer-based design. - Remove any reference to a 30-second threshold. - Update the component name in the title. - `_docs/02_document/components/13_c12_operator_orchestrator/tests.md`: - C12-IT-03 rewritten around `flight_footer` (no `FlightStateSignal`, no 30-second window). - `_docs/02_document/components/12_c11_tilemanager/description.md`: - Remove the AZ-317 gate description; C11 is now a dumb pipe. - `_docs/02_document/components/12_c11_tilemanager/tests.md`: - Remove gate-related tests. - `_docs/02_document/contracts/c11_tilemanager/tile_uploader.md`: - v1.0.0 → v2.0.0. Drop gate clauses, `confirm_flight_state`, `FlightStateNotOnGroundError`. - Add migration note pointing to batch 44. - `_docs/02_document/contracts/c12_operator_orchestrator/` — directory move (Phase A) + content tweaks. - `_docs/02_document/architecture.md`: - Update C12 component name in the system overview. - Update C11 component description (no internal gate). - `_docs/02_document/glossary.md`: - "operator tooling" → "operator orchestrator" (semantic). - `_docs/02_document/epics.md`: - E-C12 epic name updated. - `_docs/02_document/FINAL_report.md`: - Component table update. - `_docs/02_document/deployment/*`: - `containerization.md`, `environment_strategy.md`, `ci_cd_pipeline.md`, `deployment_procedures.md`, `observability.md` — replace package name references. - `README.md`: any references to `operator_tooling`. - `pyproject.toml`: package path in entry-point + any tool config keys. - `cmake/build_options.cmake`: `BUILD_C12_OPERATOR_TOOLING` → `BUILD_C12_OPERATOR_ORCHESTRATOR`. - `.github/workflows/release.yml`: any references. - `docker-compose.yml`, `docker-compose.test.yml`: service / image references. ### Phase G — Tracker (Jira) updates - **AZ-317** (`C11 Flight-State Gate`): transition to a "Superseded" or "Won't Do" status with a comment linking to batch 44. Spec stays in `_docs/02_tasks/done/` annotated. - **AZ-319** (`C11 TileUploader`): add a comment noting batch 44 removed the gate dependency; the AZ-319 ticket itself stays Done. - **AZ-329**: keep ticket; comment with the design pivot; replace spec in `_docs/02_tasks/todo/` and resync description in Jira. - **AZ-330**: comment with the package-rename note; nothing else changes. - **New ticket** for the C11 gate revert (suggested name: "C11 SRP correction — remove internal flight-state gate; AC-NEW-7 enforcement moves to C12"). Component: `c11_tile_manager`. Epic: `AZ-251` (E-C11). Complexity: 3pt. - **New ticket** for the C12 rename (suggested name: "Rename c12_operator_tooling → c12_operator_orchestrator"). Component: `c12_operator_orchestrator`. Epic: `AZ-253` (E-C12). Complexity: 2pt. - **Epic AZ-253** rename: "E-C12 Operator Pre-flight Tooling" → "E-C12 Operator Pre-flight Orchestrator". - Update `_docs/02_tasks/_dependencies_table.md`: - Add the two new tracker IDs to the table. - The C11 dependency on AZ-317 (if any task depends on the gate) goes away — none should, since AZ-317 was self-contained. - Increment task / point counts. ### Phase H — Tests, code review, commit 1. **Full repo unit suite**: must return to a clean green baseline. Document the new pass count in the batch 44 report (expected slightly lower than 1522 due to deleted gate tests + slightly higher due to new AZ-329 / AZ-330 tests; net ~+15). 2. **Targeted suites** at each phase boundary: - Phase A end: `pytest tests/unit/c12_operator_orchestrator -q` - Phase B end: `pytest tests/unit/c11_tile_manager -q` - Phase D end: `pytest tests/unit/c12_operator_orchestrator/test_post_landing_upload_orchestrator.py -v` - Phase E end: `pytest tests/unit/c12_operator_orchestrator/test_operator_reloc_service.py -v` 3. **AC coverage verification** for AZ-329 + AZ-330 per the implement skill's Step 8. 4. **Code review** (`/code-review`) on the full batch diff. Architecture findings expected (cross-task consistency: c11 and c12 cuts) — these escalate per the auto-fix matrix, never auto-fix. 5. **Single commit** with subject: ``` [AZ-329] [AZ-330] [AZ-XXX-C11-revert] [AZ-YYY-c12-rename] C12 orchestrator rename + C11 gate removal + post-landing upload + reloc service (Batch 44) ``` (Subject is over 72 chars — split into subject + body per `.cursor/rules/git-workflow.mdc`. Subject: `[Batch 44] C12 rename + C11 gate removal + post-landing upload + reloc service`. Body lists tracker IDs.) 6. **Batch 44 report**: `_docs/03_implementation/batch_44_cycle1_report.md` per the standard template, with sections for each phase. 7. **NO cumulative review this batch** — cumulative review fires at batch 45 (40-42, then 43-45). Batch 44 is mid-window. Per the implement skill Step 14.5 (K=3), the next cumulative review covers 43-45. ## File inventory snapshot (as of 2026-05-13) ### Files referencing `c12_operator_tooling` to be touched in Phase A (rename) + Phase F (docs) - **Source / tests (renamed in Phase A)**: ~38 files under `src/gps_denied_onboard/components/c12_operator_tooling/`, `src/gps_denied_onboard/runtime_root/c12_factory.py`, `tests/unit/c12_operator_tooling/`, `tests/unit/test_ac1_scaffold_layout.py`, `tests/unit/test_ac3_compose_files.py`, `pyproject.toml`, `src/gps_denied_onboard/runtime_root/__init__.py`, `src/gps_denied_onboard/healthcheck.py`. - **Docs (updated in Phase F)**: `_docs/02_document/module-layout.md`, `_docs/02_document/components/13_c12_operator_tooling/{description,tests}.md` (directory rename), `_docs/02_document/contracts/c12_operator_tooling/*` (directory rename), `_docs/02_document/architecture.md`, `_docs/02_document/FINAL_report.md`, `_docs/02_document/epics.md`, `_docs/02_document/glossary.md`, `_docs/02_document/data_model.md`, `_docs/02_document/system-flows.md`, `_docs/02_document/deployment/*`. - **Task specs (updated in Phase F)**: `_docs/02_tasks/done/AZ-{326,327,328,489}_*.md`, `_docs/02_tasks/todo/AZ-{329,330,401,403}_*.md`, `_docs/02_tasks/done/AZ-263_initial_structure.md`. - **Infra**: `docker-compose.yml`, `docker-compose.test.yml`, `cmake/build_options.cmake`, `.github/workflows/release.yml`, `README.md`. - **Historical** (leave alone): `_docs/03_implementation/batch_21_*.md`, `batch_42_*.md`, `batch_43_*.md`, `cumulative_review_batches_40-42_*.md` and any earlier batch reports. ### Files referencing `FlightStateGate` / `FlightStateNotOnGroundError` / `FlightStateSource` (Phase B) - `src/gps_denied_onboard/components/c11_tile_manager/{interface.py, errors.py, _types.py, tile_uploader.py, idempotent_retry.py, flight_state_gate.py, __init__.py}` - `src/gps_denied_onboard/runtime_root/c11_factory.py` - `tests/unit/c11_tile_manager/{test_flight_state_gate.py, test_tile_uploader.py, test_idempotent_retry.py, test_protocol_conformance.py}` - Docs: `_docs/02_document/contracts/c11_tilemanager/tile_uploader.md` (v1.0.0 → v2.0.0), `_docs/02_document/components/12_c11_tilemanager/{description,tests}.md`, `_docs/02_document/components/13_c12_operator_tooling/{description,tests}.md`, `_docs/02_document/architecture.md`, `_docs/02_document/module-layout.md`, `_docs/02_tasks/done/AZ-317_*.md` (annotate as superseded). ## Open questions for the next session These should be resolved BEFORE Phase A starts: 1. **CLI binary name**: keep `operator-tool` or rename to `operator-orchestrator`? Default proposal: **keep `operator-tool`** — operator-facing UX continuity matters more than internal package consistency. The user said "rename C12" — the C12 *package* — not "rename the CLI". If user wants both, change `pyproject.toml` script entry too. 2. **Internal class name `OperatorToolServices`**: keep or rename to `OperatorOrchestratorServices`? Default proposal: **rename** (it's internal; renaming costs nothing and stays consistent). 3. **AZ-317 spec file disposition**: leave annotated in `_docs/02_tasks/done/AZ-317_c11_flight_state_gate.md`, OR move to a new `_docs/02_tasks/superseded/` folder? Default proposal: annotate in place. Don't introduce a new lifecycle folder for one task. 4. **Whether AZ-318's `PerFlightKeyManager` lifetime hooks depend on flight-state**: low-likelihood but verify in Phase B before deleting `FlightStateSignal` from `_types.py`. 5. **C8 ↔ C11 relationship**: C8 was producing flight-state intended for C11's gate. Now that the gate is gone, does C8 still need to emit flight-state to anywhere? Check AZ-391 outputs; flight-state may still be used by C5 (state estimator) for sensor-fusion gating — independent of C11. Most likely C8's flight-state output stays and only C11 stops consuming it. ## Acceptance criteria for batch 44 completion Batch 44 is complete when ALL of the following hold: - [ ] `git grep c12_operator_tooling -- src/ tests/ cmake/ pyproject.toml docker-compose*.yml .github/` returns zero hits (excluding historical batch reports). - [ ] `git grep -E 'FlightStateGate|FlightStateNotOnGroundError|FlightStateSource' src/ tests/` returns zero hits. - [ ] `c12_operator_orchestrator` package exists with all prior `c12_operator_tooling` contents + AZ-329 + AZ-330 additions. - [ ] `pytest tests/ -q` is green (count documented in batch report). - [ ] AZ-329 + AZ-330 ACs all have direct test coverage (AC coverage verification step). - [ ] `/code-review` on the batch diff returns PASS or PASS_WITH_WARNINGS (Architecture findings escalate per the auto-fix matrix). - [ ] `_docs/02_tasks/todo/AZ-329_c12_post_landing_upload.md` rewritten and moved to `_docs/02_tasks/done/`. - [ ] `_docs/02_tasks/todo/AZ-330_c12_operator_reloc_service.md` moved to `_docs/02_tasks/done/`. - [ ] Jira tickets transitioned: AZ-317 superseded; AZ-329 + AZ-330 + new C11-revert + new C12-rename → In Testing. - [ ] Single commit on `dev` branch; user asked whether to push. - [ ] `_docs/03_implementation/batch_44_cycle1_report.md` written. - [ ] `_docs/_autodev_state.md` updated: `last_completed_batch: 44`, `in_flight_batch: null`, `in_flight_tasks: null`, `sub_step.phase: 2`, `name: detect-progress`, `detail: ""`. ## Recovery / abort guidance If Phase A breaks more than expected (e.g., test_ac1_scaffold_layout / test_ac3_compose_files hardcode the package path in surprising ways): pause and ask the user before patching test fixtures. Test scaffolds may encode invariants that the rename invalidates by design. If Phase B reveals that something outside C11 (a c4/c5/c8 wiring, or a contract conformance test) depends on `FlightStateSource` for reasons unrelated to upload gating: pause and ask. C11's `FlightStateSource` was supposed to be a C11-internal Protocol — but the C11 `_types.py` declares `FlightStateSignal` as a closed enum that other components may have started consuming. ## Pointer for the next `/autodev` invocation Re-read this plan first. Execute phases A → B → C → D → E → F → G → H in order, with the verification at each phase boundary. Do not skip Phase A's verification — every subsequent phase assumes `c12_operator_tooling` is a dead string. If any verification fails, stop and ask the user.