Phase 0 Research — Single-Authority Topology Cleanup & Dedup
All four unknowns were resolved by a live code probe during planning (the design was pre-decided by spec.md + the post-spec consistency squad). No open [NEEDS CLARIFICATION].
R1 — FR-013 conditional gate: does FR-001's .kind removal touch CommitResult?
- Decision: NO —
CommitResultis disjoint from the.kindwork → FR-013 is a standalone lane (IC-08), not folded into IC-02. - Rationale:
CommitResult(src/specify_cli/git/commit_helpers.py:422) is a frozen dataclass{sha: str, destination_ref: str, worktree_root: Path}— it has no.kindfield and noCommitTargetKindreference (probe:"kind" in body == False,"CommitTargetKind" in body == False). The #1891is not JSON serializablefailure is the un-serializablePathfield (worktree_root), unrelated to the enum eradication. - Alternatives considered: folding FR-013 into the FR-001 anchor WP (rejected — the probe proved no coupling; folding would create a false dependency and bloat the anchor lane). Carving FR-013 to a separate follow-up ticket (rejected — the fix is ~one method on a dataclass, already in the issue-matrix + assigned; a separate ticket defers a trivial bugfix for no benefit).
R2 — FR-005 vs C-002: are the 6 predicates disjoint from the genuine-fallback relays?
- Decision: YES, disjoint — FR-005 consolidates only the projection predicates; the C-002 relays are KEPT untouched.
- Rationale: the six predicates (
destination_kind_for_topology,_topology_uses_coord_surface,_topology_routes_through_coord,_mission_routes_through_coordination,_read_contract_routes_through_coordination,routes_through_coordination) are puretopology → boolprojections. The three relays (status_transition.py:599,surface_resolver.py:562,resolution.py:765) read stored topology first and callclassify_topologyonly on theexceptarm (un-backfilled-legacy fallback). A relay is an exception-arm meta-read, structurally opposite to a projection. (alphonso verified all three relays directly.) - Alternatives considered: collapsing relays into the consolidated predicate (rejected — would delete the migration fallback contract, C-002).
R3 — FR-004 boundary: absent-field vs corrupt-meta
- Decision: collapse only the absent-field
topology is Nonearms; KEEP the corrupt/unreadable-meta typed fallback (C-004). - Rationale: the meta reader returns
Nonefor a missingtopologyfield (→classify_from_metacan classify fromcoordination_branch+ lanes) but raises for malformed JSON (→ cannot classify without readable meta). The two are different return paths, so the collapse cannot accidentally delete the corrupt-meta fallback.read_topology(backfill_topology.py:68) already absorbsNoneby deriving a concrete topology; the husk-arms it makes dead are the absent-field ones. - Alternatives considered: collapsing all
topology is Nonearms including the malformed path (rejected — un-classifiable meta has no shape to derive; C-004).
R4 — IC-04 ownership strategy (the C2 sweep's collision risk)
- Decision: linearize the broad C2
meta.json-reader sweep after the smaller disjoint lanes (IC-06 accept, IC-07 merge, IC-08 #1891), OR scope per-subdirectory WPs with disjointowned_files;finalize-tasks --validate-onlymust pass. - Rationale: FR-006 touches ≥66 named call sites + ~107 inline reads across ~20 wrappers — including
tasks.pyandmission.py, which IC-06/IC-08 also touch. Parallel WPs with overlappingowned_fileswould fail finalize validation. The C2 lane is the biggest LOC reducer (~300+) and the most ownership-sensitive. - Alternatives considered: one monolithic C2 WP (rejected — exceeds the 10-subtask ceiling and would own half the CLI surface, colliding with every other lane). A single global
load_metarename in one pass (rejected — too large to review; tasks will split by subdirectory).
Sizing note (carried into FR-006/NFR-004; brownfield-corrected)
The original ~45 load_meta estimate is low (squad-verified: 66 named + ~71 inline json.loads(meta_path); the earlier ~107 inline figure was ~1.5× high — corrected by the post-plan brownfield pass). The meta-reader has 3 distinct error contracts (None-on-missing+raise-on-malformed; raise-on-missing+utf-8-sig; silent-empty-dict), not 2 adapters. NFR-004's ~750–1,000 LOC band remains a realistic floor (C6 ~400 + C2 ~250–300 + enum/predicate ~200); realized reduction reported per-cluster in the PR body.