09 — DOT Lifecycle Hardening (Branch I) (2026-06-01)
09 — DOT Lifecycle Hardening (Branch I)
Reviews decision-pack doc 06 (
…/06-gov-siv-dot-lifecycle.md). Adversarial. Live-verified thegovernance_relationsCHECK that blocks the apply step.
I0. What the pack says (summary)
Doc 06 proposes 7 DOTs on the live Đ35 scan→propose→apply→verify pattern: SCAN[A,RO], ORPHAN-DETECT[A,RO], GAP-PROPOSE[B, writes approval_requests only], ASSIGNMENT-APPLY[B, writes owner edge, approval-required], COVERAGE-AUDIT[A,RO], EXCEPTION-REVIEW[B], ISSUE-ROUTE[B]. Each tier-B writer is paired_dot (Đ35 §3 trigger). §6.5 anti-bootstrap: the scanner is itself a governed object and must be covered or the watchdog flags it. Lifecycle state machine + pairing map provided.
Overall: the scan/propose/apply/audit separation and the paired-checker discipline are exactly the right reuse of Đ35. But two DOTs cannot actually run against the current substrate (apply has no expressible edge, propose has no action-type), the bootstrap order is hand-waved, and separation-of-duty is implied not stated.
I1 — GAP-PROPOSE writes approval_requests — but with WHAT action_code? (live trap)
- Original: doc 06 GAP-PROPOSE [B] "writes
approval_requestsrows proposing owner assignment / exception grant / DOT pairing." - Live fact:
apr_action_types= {add_field,amend_law,create_item,enact_nrm,patch_ops_code,update_item} only. None of these means "assign a governance owner" or "grant an exception." A well-formedapproval_requestsrow needs a validaction_code. So GAP-PROPOSE cannot write a well-formed proposal for owner-assignment or exception-grant today. - Self-referential irony: doc 07 #5
approval_path_gap= "mutating object with no APR action-type mapping." The proposer DOT, trying to fix gaps, itself hitsapproval_path_gap— there is no action-type for what it wants to propose. - Hardened wording: state as a hard prerequisite: register new
apr_action_types—assign_governance_owner,grant_governance_exception, and (C7)delegate_authority— before GAP-PROPOSE can run. These are governedadds toapr_action_types(COUNCIL-owned,risk_levelper scope: owner-assignment medium, law-domain-owner high). Until they exist, GAP-PROPOSE is design-only / blocked. The DOT design must reference an existing action-type, never a phantom one. - Acceptance test: GAP-PROPOSE's output references a registered action_code; a rehearsal without the new action-types produces a
proposal_blocked: no_action_typeresult, not a malformed row. - Open question: OQ-I1 — do these action-types need real handlers, or are they council-review-only (handler_ref like
amend_law='unimplemented', i.e. human-applied)? (Owner-assignment could have a handler that writes the edge; exception-grant likely council-review.)
I2 — ASSIGNMENT-APPLY writes governance_relations owner assignment — but object edges are CHECK-blocked (the load-bearing live contradiction)
- Original: doc 06 ASSIGNMENT-APPLY [B] "writes
governance_relations/ owner assignment … the owner edge / capability / exception record; closes the originating orphan." - Live fact (re-verified this session):
chk_relations_target_typeandchk_relations_source_typeboth constrain to∈ {law, agency}. Sogovernance_relationscan express agency→law and agency→agency edges only — never agency→object. Doc 02 §2.2 + doc 08 §5.4-EXT acknowledge this and defer the object-edge structural change. Therefore ASSIGNMENT-APPLY cannot write an object owner edge with the current schema — yet doc 06 describes it as if it works now. - Contradiction: the apply DOT is the operative remediation step (it's what drives the orphan count to zero), and it is inoperable for object-grain ownership until the deferred §5.4-EXT lands. The pack defers the structural change while building the system that needs it.
- Hardened wording — make the interim explicit and bound APPLY's two modes:
"Until §5.4-EXT (or a
governance_object_ownershiptable) exists, ASSIGNMENT-APPLY operates in law/domain mode only: it can write an agency→law edge (which propagates to objects vialaw_jurisdiction+ inheritance, doc 04 §4.4) — this covers law-domain-anchored objects (B7-i). It cannot assign an owner to a law-orphan object (a bespoke route/adapter/standalone-policy with no law domain, B7-ii); for those, APPLY returnsapply_blocked: object_edge_unexpressibleand the gap staysOWNER_GAPuntil the structural change. The apply step therefore has a known coverage ceiling that only §5.4-EXT removes." - Acceptance test: APPLY successfully assigns ownership for a law-domain-anchored object (via agency→law edge in rehearsal under BEGIN..ROLLBACK); for a law-orphan object it returns
apply_blocked, not a silent success or a CHECK-violation error. - Open question: OQ-I2 — reclassify §5.4-EXT from "deferred" to "Tier-1 prerequisite for object-grain apply." Without it, the entire remediation half of the pack only works at law-domain grain. This is the strongest argument for bringing §5.4-EXT forward (see B7, doc 14 Tier-1).
I3 — Anti-bootstrap rule (§6.5) is right but the bootstrap sequence is unspecified (chicken-egg)
- Original: §6.5: the scanner is a governed object; on registration it must be covered (owner SIV/DOT, paired, audited, law-ref Đ31/35), else the detector that finds anarchic objects is itself anarchic. COVERAGE-AUDIT watches SCAN.
- Chicken-egg: before the scanner exists, it cannot be covered by itself; and the L1 inventory + resolution-rule tables it reads are themselves new governed objects needing owners/approval. So the first scan runs against a half-built roof. The pack asserts the end-state (self-watching) but never the bootstrap order.
- Hardened wording — define the seed sequence:
"Bootstrap as a single seed package: (1) COUNCIL approves, under one APR, the seed set = {L1 source inventory, resolution-rule tables, the 7 DOTs, the issue/event vocabulary} as a coherent unit with explicit owners (SIV/DOT/COUNCIL). (2) The seed package's own coverage is asserted by a one-time recorded attestation (a
governance_audit_logminute), NOT by the scanner scanning itself before it exists — this is the defined, audited base case. (3) From the second scan onward, COVERAGE-AUDIT watches SCAN and SCAN covers the seed objects normally (self-watching). The base case is a recorded attestation, not memory." - Acceptance test: the seed package has a recorded approval + attestation before first scan; the second scan re-derives the seed's coverage from substrate (no permanent reliance on the attestation).
- Open question: none.
I4 — Separation-of-duty is implied (paired checkers) but never stated as a rule
- Original: doc 06 has propose/approve/apply as distinct steps + paired tier-A checkers; doc 06 ASSIGNMENT-APPLY "closes the originating issue."
- Risk: without an explicit SoD rule, a "convenience" refactor could collapse propose+apply into one DOT (self-grant), or let the applier also be the verifier (self-attestation) — re-creating a local-approval island under the roof. (This is C6 from the owner side, restated for DOTs.)
- Hardened wording (mirror C6 into Đ35): "Propose, approve, and apply-verify are held by distinct authorities: a DOT may propose and (post-approval) apply, but approval is always Đ32 quorum, never the DOT; the apply-verifier (tier-A paired) MUST be a different DOT from the applier (tier-B). No DOT both authors and approves the same change (Đ32 §4.3 no-self-approve, generalized). A DOT may never mint a law, an owner agency, an action-type, or an event type — it may only propose their creation for Đ32/Đ45 ratification."
- Acceptance test: the DOT family has no DOT that both approves and applies; a synthetic self-approving DOT fails CI; ASSIGNMENT-APPLY's verifier ≠ its applier.
- Open question: none.
I5 — ISSUE-ROUTE is tier-B (writes event_outbox) but the gate "no emit before registration" must hold at run time, not just design time
- Original: doc 06 ISSUE-ROUTE [B] writes
event_outbox; "event types must be pre-registered (Đ45 §3.2)"; failure mode "emit unregistered type → producer fail by Đ45 anti-pattern (registry CHECK)." - Note (good, with one tightening): the pack relies on a registry CHECK to fail-closed. Confirm that CHECK exists at the
event_outboxwrite path (the pack asserts it; verify in P4/P6). If the CHECK is only conceptual, ISSUE-ROUTE could emit an unregistered type. Also: ISSUE-ROUTE must respect the H3 cooldown/throttle, not emit one event per issue per scan. - Hardened wording: ISSUE-ROUTE emits only
event_types present-and-active inevent_type_registry(verified at write, fail-closed); applies cooldown/aggregate (H3/G7); is itself paired (-TEST asserts no unregistered emit + no payload leak, already in doc 06 §6.4). - Acceptance test: ISSUE-ROUTE in rehearsal cannot emit an unregistered/inactive type; emits aggregate-not-per-orphan for high-cardinality classes.
- Open question: OQ-I5 — verify the
event_outboxregistry CHECK actually exists (P4), since the whole fail-closed claim depends on it.
I6 — DOT family has no defined behavior when its input views are stale/unverifiable
- Original: the DOTs consume L1–L5; doc 11 §11.9 handles stale partitions in the UI, but the DOTs' behavior on stale input isn't specified.
- Risk: PROPOSE/APPLY acting on stale coverage data could propose assigning an owner to an object that already has one (stale orphan), or close an issue for an object that's still orphaned.
- Hardened wording: PROPOSE/APPLY refuse to act on stale/unverifiable input (G6) — they require a fresh SCAN within the cadence window; acting on stale data is a
scan_anomaly/fault. APPLY re-verifies the gap still exists immediately before applying (read-then-act under one transaction). - Acceptance test: PROPOSE/APPLY against a stale L4 produces
blocked: stale_input, not a stale-data action. - Open question: none.
I-summary — Branch I verdict
| ID | Severity | Type | Disposition |
|---|---|---|---|
| I1 | high | trap (live) | new APR action-types (assign_governance_owner/grant_governance_exception) are prerequisites |
| I2 | critical | contradiction (live) | APPLY is law-domain-mode-only until §5.4-EXT; object-grain apply BLOCKED — reclassify §5.4-EXT to Tier-1 |
| I3 | high | gap | define the seed bootstrap sequence + attestation base case |
| I4 | high | gap | state separation-of-duty explicitly (Đ35) |
| I5 | medium | run-time gate | verify event_outbox registry CHECK; cooldown on emit |
| I6 | medium | gap | DOTs refuse stale/unverifiable input |
I2 is the most consequential finding in the whole review: the remediation half of the system cannot assign object-grain owners until the "deferred" §5.4-EXT lands. I1 + I2 + E2 + C7 are all the same root: the substrate cannot yet express governance assignments/exceptions/delegations, so several new APR action-types AND the object-edge structural change are prerequisites, not deferrals.