Research — Silent Destructive-Write Hardening
Grounding by two profile-loaded opus lenses (paula-patterns structural + planner-priti scope), confirmed live on main @ d57619a900. No dependency changes → supply-chain adversarial-evidence section is N/A (no contested dependency findings).
WP01 — #4908 charter recompile hardcodes software-dev
at generate.py:249 with a read of the recorded mission type from the SSOT. In the activate.py recompile path (:558-566) the guard at :555 guarantees charter.yaml exists, so catalog.mission is present — read it (and/or answers.yaml mission) and pass as resolved_mission_type from both activate.py:562 and pack.py:212.
fabricated an input it should read from the source of truth. Reading the SSOT fixes both callers at one seam.
malformed-answers.yaml abort regression; (b) thread a new parameter through every call site — more churn than reading the SSOT at the fallback.
by the fix.
- Decision: Replace the
resolved_mission = resolved_mission_type or "software-dev"fallback - Rationale: The recompile is a derived-view writer (rebuilding
catalog.references) that - Alternatives considered: (a) flip
from_interview=True— REJECTED, reintroduces the #2940 - Bounds:
charter.md,answers.yaml,config.yamlactivations are untouched by the bug and
WP02 — #4897 mission-state --fix quarantines DecisionPoint rows
non-lane event-type registry and make both status/store.py:is_non_lane_event (:597-631) and migration/mission_state.py:_is_preserved_non_lane_row (:1919-1972) consult it. Also ensure a repair that would quarantine a canonical row does not report errors=0/success (_repair_mission :1671-1691).
DecisionPoint) as authoritative; the repair kept a hand-narrowed re-statement* of that rule on an inverted belief that DecisionPoint rows are a prunable mirror. status.events.jsonl is the authoritative store; decisions/index.json is a derived fold (decisions/index_fold.py:3-16). One shared registry closes the recurring whack-a-field class (#2376 retrospective, #3066 WPStatusChanged, #3541 review_result, #4897 DecisionPoint).
whack-a-field patch (spec C-002); (b) delegate to is_non_lane_event without a named registry — viable but leaves the authority implicit in a predicate; the registry makes it explicit and gate-testable.
already exports LIFECYCLE_EVENT_TYPES), as a sibling authoritative set / predicate that is_non_lane_event and the repair both import. Must keep the already-preserved classes (annotation, retrospective, WPStatusChanged, review_result) preserved — no regression.
- Decision (operator-chosen, decision
01M3560PHPQM617TH5MZHWRF8J): create ONE authoritative - Rationale: The durable reader already treats any row with
event_type(incl. all - Alternatives considered: (a) add
DecisionPoint*to the preserved set — REJECTED as another - Registry placement: with the existing status event-type authority (
status/lifecycle_events.py
WP03 — #4894 merge-driver-traces line-level global dedup
with section/block-level union — dedup only whole identical sections/blocks at the <!-- section:... --> delimiter the docstring already names, preserving repeated lines within distinct sections. Preferred enhancement: make merge_driver_traces (:328-338) 3-way base-aware (consume %O) like its sibling matrix drivers so base-common content is not misread.
legitimately). The sibling acceptance/issue-matrix drivers are 3-way base-aware and fail closed on a diverged verdict field (_merge_field:411-466); that fail-closed shape is correct for a keyed row artifact but WRONG for keyless append-union prose — failing closed on every repeat would abort nearly every real merge (spec C-003).
dedups whole history entries by canonical-JSON equality — correct record-granularity dedup, not the bug; leave it.
(b) pure section union without 3-way — acceptable minimum; 3-way base-awareness is the fuller fix.
- Decision: Replace line-level global dedup in
union_trace_texts(merge_driver.py:304-325) - Rationale: Line-level global dedup is unsound for markdown (fences, headings, prose recur
- Non-bug sibling: the second
seenset atmerge_driver.py:237-245(_union_acceptance_history) - Alternatives considered: (a) fail-closed like acceptance — REJECTED (aborts real merges);
Cross-cutting
seams that do NOT share a single guard. Do NOT route through the asset_preservation guard (asset_preservation/guard.py, test_mutation_ownership_routing.py): it is filesystem-path granularity (OwnershipProof over a Path); these are content/row/line-level writes → category error (spec C-001).
and WP04… (n/a) — WP01 and WP03 are first-order SSOT/granularity bugs fixable in isolation.
- These three are one pattern family (silent destructive write, exit 0) but three independent
- Only WP02 is a recurring whack-a-field class warranting a shared abstraction (the registry). WP01