Research — Degod tasks.py (Wave 1)
Phase 0 output. Most decisions were settled by two pre-plan squads (sizing/arch/parity, then coherence/doctrine/program-alignment) against the current code; recorded here as the design rationale. No open NEEDS CLARIFICATION.
D1 — Golden-characterization-first (behavior-preserving safety net)
- Decision: Freeze the full observable
agent taskscontract with a golden CLI-characterization harness before any extraction; every WP must keep it byte-identical (pure parity). - Rationale: DIRECTIVE_041 (tests-as-scaffold, observable-contract). The refactor's safety rests entirely on this. The existing
test_tasks_cli_contract.py(#2058/#2114) exists but by its docstring punts the coord skip-exit-0 arm and covers no mutating command — so the harness must be extended with a coord-topology + protected-branch fixture that drivesmove_task/mark_status/map_requirementsand freezes their exit codes, polymorphic--jsonkeys, and side effects. - Alternatives rejected: relying on the existing helper tests (
test_move_task_guard.py, #1618) — they pin the helper structure the refactor dissolves, so they'd break on extraction rather than protect it.
D2 — Stratified port set; coord WRITE is a TWO-CAPABILITY port (post-squad correction)
- Decision:
FsReader(coord READ) andCoordCommitRouter(coord WRITE) are program-reference ports Wave 2 reuses;GitOpsandRenderare mission-local scaffolding, not reference ports. The WRITE port exposes two capability methods over two structurally disjoint real seams —commit_status(event, , capability)overemit_status_transition_transactional(keyedGuardCapability, self-atomic viaBookkeepingTransaction) andcommit_artifact(paths, message, , kind, policy)overcommit_for_mission(keyedMissionArtifactKind+ProtectionPolicy) — not a singlecommit()that fuses them. - Rationale (corrected against live Wave-2 seams): the three Wave-2 consumers use disjoint halves —
implement.pyuses only the emit leg (emit_status_transition_transactional, zerocommit_for_mission);acceptance/__init__.pyuses only the artifact-commit leg (routescommit_for_missionon protected primaries, directrun_git commitotherwise,write_meta— i.e. it is a writer, event-less);move_taskuses both. A single fusedcommit()whose consumers each touch one half is a mis-cut capability that Wave 2 re-cuts — the exact C-006/D2 failure. The earlier "acceptance does zero writes" premise was factually inverted; the real proof of CoordRead≠CoordWrite is capability-disjointness (acceptance usescommit_artifactwithoutcommit_status), which is precisely why the WRITE port needs two methods.#2173already adjudicated GitOps + Render as DROP. - Atomicity note (INV-2 restated): the #2160 atomicity is a property inside
emit_status_transition_transactional(BookkeepingTransaction.acquire), not of port packaging. A co-equalcommit_statusmethod routing through it is equally atomic — so there is no "peer StatusEmit reintroduces split-brain" hazard, and that spurious fear must not force a fused singlecommit(). - Alternatives rejected: 5 co-equal reference ports (mis-shapes the program); a single fused
commit()(mis-cut — Wave 2 re-cuts it); a unified coord read+write port (violates C-001).
D3 — #2072 composite-key re-key is a HARD PREDECESSOR
- Decision: #2072 Obligation-A (migrate the tasks.py
coord_authoritycensus entries to composite-key: qualname+token, line-independent) must land before the first body extraction, not run "in parallel". - Rationale: DIRECTIVE_041 stable-anchoring. Body-thinning shifts every line in tasks.py; file:line-keyed census entries would force a manual re-key on every WP (the exact friction #2072 removes). Under composite-key the entries survive the rewire untouched.
- Alternatives rejected: parallel #2072 (re-key thrash per WP); file:line re-key inside this mission (fights the shrink-only ratchet).
D4 — Pure parity; the cross-command unification is deferred (#2300)
- Decision: This mission preserves all current behavior. The real skip-vs-refuse inconsistency (
move_taskskips-exit-0;mark_status/map_requirementsrefuse-exit-1 on coord+protected) is NOT reconciled here. - Rationale: Reconciling it changes ≥1 command's observable behavior → contradicts the pure-parity guarantee that makes the golden test a clean safety net. Deferred to #2300 (characterize-then-intentionally-diff).
- Alternatives rejected: unify inside the degod (self-contradictory: FR-004-unify vs NFR-001-parity — flagged by the squad).
D5 — Inject at the orchestrator boundary, not the Typer command
- Decision: The port is a
*, ports=Nonekeyword param on an extracted_do_<command>(...)helper; the@app.commandstays a thin shell that reads its options and delegates. - Rationale: Typer introspects every parameter of a decorated command; a Protocol-typed
port=becomes an unwanted--portflag / registration failure. The#2056template sidesteps this the same way. - Alternatives rejected:
port=on the command signature (Typer collision); a module-global port (untestable, hidden state).
D6 — 9 strictly-linear work packages (post-tasks squad resize)
- Decision: golden harness → ports (+FR-010 proof) → 3 cores (move_task, mapping, status) → move_task rewire → core-backed rewire (map_requirements+status) → coreless rewire (mark_status+finalize_tasks, +non-import gate) → render+shim+census.
- Rationale: the pre-plan sizing squad lifted 6→8; the post-tasks squad then split the overloaded 4-body rewire WP into a core-backed slice (WP07) and a coreless slice (WP08) — leaving render+shim+census as WP09. Strictly linear per the #2056 template (each WP builds on the prior; shared
tasks.pyrewire surface forbids parallelism).
D7 — ATDD-for-refactor: the per-core unit test is the red-first artifact
- Decision: For the pure-parity extraction WPs, the failing-first (charter C-011) artifact is the per-core unit test (RED against the not-yet-extracted core), not a CLI-boundary test (the golden harness stays green throughout).
- Rationale: Pure-parity WPs deliver no new user-observable behavior, so nothing goes red at the CLI. The genuine red-first is the unit test of the core being created.
D8 — FR-010 pre30 read-authority unification: byte-identical ONLY with pinned kinds + a proof artifact
- Decision: Migrate the 3 kind-blind
resolve_feature_dir_for_missionreads (resolver calls atmove_task:1138,finalize_tasks:2373,list_dependents:3568) to kind-awareresolve_planning_read_dir, pinning the exactMissionArtifactKindper site (the in-file exemplars diverge — the migratedadd_historyguard usesTASKS_INDEX, the finalize/list_dependents other reads useWORK_PACKAGE_TASK— so the kind is a real per-site decision, not a default). - Rationale: DIRECTIVE_044 unification of a real coord-read split-brain: on a coord topology
resolve_feature_dir_for_missionreturns the-coordhusk whileresolve_planning_read_dirreturns the primary read dir — they differ by construction. So "same on-disk dir" is NOT free; it holds only for the correct kind. Because WP01 builds a coord fixture, a wrong kind is the single most likely parity break in the mission. - Guardrail: ship a dir-equivalence proof artifact as a WP02 deliverable — a targeted test asserting, for each in-scope kind on the coord fixture,
resolve_feature_dir_for_mission == resolve_planning_read_dir(kind=…)for the pre30 guard — before any WP06/WP08 rewire. Not a WP06 runtime "stop if it shifts."
D10 — Factual re-census against live tasks.py (squad-verified; anchors are indicative)
- Decision: Treat all cited line anchors as indicative and re-census at WP-start. Squad-verified facts:
tasks.py= 3617 LOC; 9 subcommands / 53 params (both TRUE); bodies move_task 831/19, status 488, map_requirements 426, mark_status 280, finalize_tasks 172 (not "mega");json.dumps= 13 (not 17) at lines 442/480/493/2035/2235/2726/2751/2765/2854/2926/3022/3264/3605; floorsCOORD_AUTHORITY_WRITE_FLOOR=12(12 live entries → at floor),CANONICALIZER_FLOOR=45,ROUTED_CANONICALIZER_FLOOR=38(all TRUE); refuse raises atmark_status:1952/map_requirements:2629; skip armskip_target_branch_commitfall-through at 1083→1648/1783 (notyper.Exit(0)in file). #2072 HAS landed (allowlist composite-keyed) → WP03 not blocked. - Rationale: FR-011/NFR-005 drain + the shim/render gates require truthful counts; a gate pinned to a phantom "17" fails on the true starting count. Rename the contract's
CommitResult(collides withgit/commit_helpers.py:424) toCommitStatusResult/CommitArtifactResult. Fix stalecoord_authority_baseline: 13→12.
D9 — FR-011 census: drain + shrink-only floor (not re-key)
- Decision: As bodies thin, resolve sites that lose their write indicators reclassify WRITE→READ and drain from the census;
COORD_AUTHORITY_WRITE_FLOOR/CANONICALIZER_FLOORlower shrink-only. - Rationale: DIRECTIVE_043 shrink-only ratchet. With #2072 composite-keying first (D3), there is no file:line to re-key — the honest move is drain + floor-lower. WP08 + mission-merge run the full arch cross-base sweep (
post-merge-arch-gate-adjudication).