---
mode: CREATE
adr_id: ADR-0.0.63
branch: main
timestamp: "2026-05-29T13:25:34Z"
agent: claude-code
obpi_id: OBPI-0.0.63-05
session_id: dual-runtime-collapse-adr-0.0.63
continues_from: .gzkit/handoffs/20260529T111235Z-obpi03-done-obpi05-next.md
---

<!-- Handoff document for ADR-0.0.63 — created by claude-code at 2026-05-29T13:25:34Z -->

## Current State Summary

**OBPI-0.0.63-05 (dual-runtime-collapse — BI-2): COMPLETE — attested, synced.** ADR-0.0.63 is now **5/7** (01, 02, 03, 05, 07 `attested_completed`; 04, 06 `pending`/`draft`).

Ran the full `gz-obpi-specify` (re-authored brief through the skill after an initial ad-hoc Write that the operator correctly flagged) -> `gz-plan-audit` -> `gz obpi pipeline` ceremony for OBPI-05 (Decision item 6 / BI-2 of ADR-0.0.63). The collapse is a single guarded `ledger.append`: `_complete_closeout_pipeline` in `src/gzkit/commands/closeout.py` now emits the `attested` event only when `consumed is None`. On the ceremony-consumed path the ceremony's Step-6 emission (`closeout_ceremony.py:549`) is the single authoritative source — collapsing the OBPI-01 transitional double-emit. The attested-locked BI-3 gate and the `attested_event` schema were untouched.

- **Mechanism:** the pipeline previously re-emitted `attested` from the consumed ceremony verdict (`closeout.py:504`) even though `_consume_ceremony_attestation` had already read it — a pure-redundancy double-emit (mirror classifiers, same `get_git_user()` attester). The guard `if consumed is None:` makes the pipeline the sole emitter only on the direct interactive path; on the ceremony path the ceremony is the single source. `--attest` is "never a parallel emitter" because after the fix it is the sole emitter for ceremony-driven closeouts.
- **BI-2/BI-3 reconciliation (named in the brief Non-Goals):** read literally, BI-2's "runtime engine is the single source" could tilt toward the *pipeline* being the source and `--attest` not emitting. That is foreclosed by BI-3 (attested-completed in OBPI-01): its Step 6->7 gate fail-closes unless a fresh `attested` *ledger event* exists, and `--attest` produces it. So the ceremony is the surviving emitter; the pipeline yields. OBPI-01's own non-goal note already fixed this direction.
- **Tests:** `tests/test_closeout_pipeline.py::TestDualRuntimeCollapseBI2` — 3 REQ-derived tests (REQ-01 single-emit regression RED `2!=1` -> GREEN; REQ-02 ceremony-vs-direct surface equality; REQ-03 direct-path-still-emits guard). Full suite green via `arb-step-unittest-11700c9918c44344b82419ec546b65c1`.
- **Coupled-surface fix (AGENTS.md 1a):** 3 GHI #351 fixtures in `tests/test_closeout_ceremony_consumption.py` asserted the *pre-BI-2* contract (pipeline emits from consumed verdict). They seeded ceremony *state* only — an unrealistic state, since production `--attest` always emits. Updated `_seed_ceremony_state` with an `emit_attested_status` param so the fixtures model the ceremony's Step-6 emission (the new single source); assertions now also check no-double-emit. The GHI #351 no-re-prompt contract is preserved.
- **Behave waiver** (`data/behave_coverage_waivers.json`): REQ-01..04 in-progress, deferred to ADR-0.0.63 closeout (sibling OBPI-01/03 pattern). REQ-04 is STRUCTURAL-FENCE, accepted-uncovered at completion (`--accept-uncovered REQ-0.0.63-05-04`).
- Completed via `gz obpi complete <OBPI> --attestor-present --accept-uncovered REQ-0.0.63-05-04`. The `--accept-uncovered` heavy/foundation path needed `--attestor-present` (co-presence proxy) backed by the `gz obpi pipeline`-launched marker (GHI #292/#412) — a plain `--attestation-text` call alone errored. Two git-syncs; tree clean, synced, `dirty=False`, ahead=0 behind=0.

Last action succeeded: `gz git-sync --apply` reported clean + synced.

## Important Context

- **Process lesson (this session, logged twice in `.gzkit/insights/agent-insights.jsonl`):** I twice hand-authored via raw `Write` instead of invoking the skill — first the OBPI-05 brief (bypassing `gz-obpi-specify`), which the operator caught hard. The brief content survived because the skill's ground-truth + `gz obpi validate --authored` + `gz validate --req-kind-discipline` gates were all green, but the bypassed entry path was the defect. HARD GATE for the resuming agent: route brief authoring through `Skill(gz-obpi-specify)` and handoffs through `Skill(gz-session-handoff)` BEFORE the first Write. Invariant 10a.
- **`--accept-uncovered` on heavy/foundation requires `--attestor-present` + a live `gz obpi pipeline` marker** (GHI #292/#412). Launch `uv run gz obpi pipeline <OBPI>` first (it creates the `pipeline_launched`-witnessed marker), then `gz obpi complete <OBPI> --attestor-present`. The runtime launch does NOT auto-claim the OBPI lock — `precomplete` will block on `lock_held`; run `gz obpi lock claim <OBPI>` before `gz obpi complete`.
- **The `attested` ledger line is compact JSON in tests** (event/id/status/by flattened on one line, no spaces) — count attested events with `ln.replace(" ", "")` matching, or `json.loads` per line filtering `event=="attested" and id==adr_id`.
- **BI-2 is now satisfied at the OBPI-05 level; the cross-OBPI audit happens at ADR-0.0.63 closeout** (STRUCTURAL-FENCE proof channel). REQ-0.0.63-05-04 points at the parent ADR `## Boundary Invariants` BI-2 entry.
- **All prior-handoff Important Context still applies** (chained predecessor): GHI #566 validator-fidelity context; closeout-proof-binding is dormant against ADR-0.0.63's legacy briefs; the handoff-system programmatic API is vaporware (only `gzkit.handoff_validation.validate_handoff_document` is real).

## Decisions Made

- **Decision:** Collapse direction = pipeline stops re-emitting; ceremony keeps its Step-6 emission.
  **Rationale:** BI-3 (attested-completed) requires the ceremony's ledger emission as the gate's expected receipt; removing it would reopen OBPI-01. OBPI-01's non-goal note pre-committed this direction.
  **Alternatives rejected:** (a) pipeline as sole source / `--attest` non-emitting — breaks BI-3; (b) changing the BI-3 gate to read ceremony state instead of ledger — reopens attested-completed OBPI-01 and weakens the Layer-2 ledger-gating.
- **Decision:** Guard is `if consumed is None:` around the single `ledger.append(attested_event(adr_id, status, attester, reason))`, leaving `attester`/status/text derivation intact for the closeout form and version-sync read.
  **Rationale:** minimal surgical change; the other pipeline events (`closeout_initiated`, `gate_checked`, `lifecycle_transition`) have no ceremony counterpart and are already path-identical.
  **Alternatives rejected:** querying the ledger for a fresh ceremony `attested` before deciding — unnecessary, since `consumed is not None` implies the ceremony emitted (OBPI-01 invariant).
- **Decision:** Expand Allowed Paths to the coupled consumption-test file and behave-waiver data file (1a), with rationale recorded in the brief.
  **Rationale:** the closeout.py change broke 3 consumption-test fixtures and needs a behave waiver; coupled-surface coherence requires fixing both in the same change.

## Immediate Next Steps

1. **OBPI-0.0.63-04 (closeout-skill-reverify-wording-fix)** — the lightest remaining item. Pure wording fix in `.gzkit/skills/gz-adr-closeout-ceremony/SKILL.md:60-65` resolving the "Does NOT re-verify" vs spec-reviewer "Independent re-verification" contradiction (CLI pipeline does not re-execute; spec-reviewer persona-dispatch retains independent re-verification). Author the Draft brief for real via `Skill(gz-obpi-specify)`, then `/gz-plan-audit OBPI-0.0.63-04` -> `gz obpi pipeline`. Note: it edits a canonical skill, so bump `skill-version` + `last_reviewed` and run `gz agent sync control-surfaces` (skill-surface-sync rule).
2. **Then OBPI-0.0.63-06 (req-evidence-schema-consumption)** — wire `closeout_ceremony.py` runtime to consume the `ln` field (mechanical proof-binding replaces prose); also fold GHI #566 (validator body-REQ enumeration fidelity). Brief is still a Draft scaffold — author for real.
3. **Then `gz-adr-closeout-ceremony` for ADR-0.0.63** once 04 and 06 are attested (7/7). The closeout ceremony itself exercises the BI-2 surface this OBPI just fixed — verify the `attested` event lands exactly once during the real closeout.
4. **Before each Stage 5:** the `gz obpi pipeline` launch does not auto-claim the lock — `gz obpi lock claim <OBPI>` before `gz obpi complete`; use `--attestor-present` for heavy/foundation `--accept-uncovered`.

## Pending Work / Open Loops

- **2 OBPIs remain in ADR-0.0.63:** 04 (wording fix), 06 (req-evidence-schema-consumption + GHI #566 validator fidelity).
- **GHI #561 (pre-existing, NOT this ADR):** `ADR-0.0.64 REQ-0.0.64-05-06` lacks a `gz validate --<scope>` citation -> `--req-kind-discipline` fails repo-wide. Will block ADR-0.0.63 closeout's quality pipeline; needs the 0.0.64 owner. Do NOT blind-fix.
- **GHI #565:** 40 pre-existing brief `## Verification` compound-command violations (OBPI-07 gate surface); precondition for promoting `--brief-command-shape` to default `gz check`.
- **GHIs to close at ADR-0.0.63 closeout:** #517 (parent diagnosis), plus the per-OBPI set noted in the predecessor handoff (#539, #540, #550, #516, #566).
- **Macro context (operator flagged this session):** ~40 foundation ADRs created in ~5 weeks, 65% unfinished (18 Draft / 8 Proposed / 14 Validated of ADR-0.0.25..0.0.64). Operator considered a freeze-and-drain triage of the unfinished foundation backlog but chose to finish in-flight work (OBPI-05) first. A keep/merge/kill triage of the 26 open foundation ADRs is a candidate next session if the operator wants it.

## Verification Checklist

- [ ] `git branch --show-current` returns `main`; tree clean, synced with origin
- [ ] `uv run gz adr status ADR-0.0.63` shows 01, 02, 03, 05, 07 `attested_completed`; 04, 06 `pending`/`draft` (5/7)
- [ ] `uv run -m unittest tests.test_closeout_pipeline.TestDualRuntimeCollapseBI2` -> 3 pass
- [ ] `uv run -m unittest tests.test_closeout_pipeline tests.test_closeout_ceremony_consumption tests.test_closeout_ceremony_cmd` -> all pass (61)
- [ ] `uv run gz obpi reconcile OBPI-0.0.63-05-dual-runtime-collapse` -> PASS (ATTESTED COMPLETED)
- [ ] `uv run gz covers OBPI-0.0.63-05-dual-runtime-collapse --json` -> `behavior_uncovered_reqs == 0`

## Evidence / Artifacts

- `src/gzkit/commands/closeout.py` — `_complete_closeout_pipeline` `if consumed is None:` guard around the `attested_event` append (the BI-2 collapse)
- `tests/test_closeout_pipeline.py` — `TestDualRuntimeCollapseBI2` (3 REQ-derived tests) + `_count_attested` / `_seed_consumed_ceremony` helpers
- `tests/test_closeout_ceremony_consumption.py` — coupled-surface fix; `_seed_ceremony_state` gained `emit_attested_status`
- `data/behave_coverage_waivers.json` — OBPI-0.0.63-05 in-progress waiver (REQ-01..04)
- `docs/design/adr/foundation/ADR-0.0.63-closeout-ceremony-runtime-engine-parity/obpis/OBPI-0.0.63-05-dual-runtime-collapse.md` — authored brief (Completed)
- `.claude/plans/dual-runtime-collapse-OBPI-0.0.63-05.md` — approved plan
- `docs/design/adr/foundation/ADR-0.0.63-closeout-ceremony-runtime-engine-parity/handoffs/20260529T111235Z-obpi03-done-obpi05-next.md` — predecessor handoff

## Environment State

Python 3.13 / uv. Session ran on claude-haiku-4-5. No feature branch — work lands on `main` via `gz git-sync`. ARB receipts for this OBPI: `arb-step-unittest-11700c9918c44344b82419ec546b65c1`, `arb-ruff-c2c4b0ef66104711a82b738d6e6d7557`, `arb-step-typecheck-cff7c9a20ce64671831765ec0d293ec9`, `arb-step-mkdocs-296f143046c74b74a28133211f23915f`.
