You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: M4 closed — budget stays inline (owner decision B-analog); no gate beyond plan moved
Assessed budget-gate interceptor-suitability against the code before writing any
interceptor (per §13.3, the approval lesson). Finding: the budget "gate" is three
mechanisms — only the global cost cap (_assert_cost_budget) is stateless; the phase
budget (live phase_tracker) and warning ladder (two mutable dedup sets + interactive
on_prompt side-effect handler — the same assert_allowed shadow-coexistence trap that
blocked approval) are runtime-stateful, and even the cost cap is enforced at two
evolving-cost points per iteration with no 1:1 event mapping.
Owner chose B-analog: budget enforcement stays inline. M4 closes with NO gate moved
beyond the plan gate (M3). Approval and budget observability already reach the M6 fold
via their M2-typed audit events.
- New work-log: docs/work-log/m4-budget-stays-inline-2026-06-13.md (full assessment)
- Plan §5 graph + §7 M4 row updated to "no gate moved; both stay inline"
- M4-T001/T002/T003 tickets marked CLOSED — not implemented (planning record retained)
- Superseded salvage stash (ApprovalGateInterceptor + BudgetGateInterceptor) dropped
- Docs inventory regenerated
No code changed; enforcement paths unchanged.
Constraint: docs-only; budget/approval enforcement code unchanged; runtime-stateful gates stay inline by evidenced finding.
Tested: docs inventory --check passes; validate_docs_consistency runs (pre-existing hypothesis-missing collection error in 3.14 sandbox is environmental, unrelated).
Not-tested: full suite not run on 3.12 (no code change).
Confidence: high
Roadmap-Status: unchanged
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
budget-second: approval carries `pending_approval`/resume semantics, budget is
113
-
mostly threshold/action.
112
+
decision events whose contract M3 preserves. **M4 closed with no gate moved**:
113
+
on assessment, both remaining gates proved runtime-stateful. Approval carries
114
+
live JIT/session state + handler + auto-mode-swappable policy (decision B).
115
+
Budget is three mechanisms — only the global cost cap is stateless; the phase
116
+
budget (live phase-tracker) and the warning ladder (two mutable dedup sets + an
117
+
interactive `on_prompt` side-effect handler that mutates monitor state, the same
118
+
`assert_allowed` shadow-coexistence trap that blocked approval) are stateful,
119
+
and even the cost cap is checked at two evolving-cost points per iteration that
120
+
don't map 1:1 to events (decision B-analog). Plan gate (M3) is the one gate that
121
+
moved; approval and budget observability still reach the M6 fold via their typed
122
+
audit events.
114
123
115
124
## 6. Usage Scenarios
116
125
@@ -164,7 +173,7 @@ consumers by M6.
164
173
| ADR-0032-M1 | AuditLogger can consume RunEvents and produce byte-equivalent JSONL for golden proof runs; legacy call sites delegate instead of directly owning serialization decisions. |
165
174
| ADR-0032-M2 (REDEFINED, taxonomy-only §16) | Every audit event the evidence bundle reads is typed in `RunEventType` and mapped both directions, so the M2-T001 reader surfaces it **from the audit JSONL** (mapper is sufficient; emit-site migration is NOT in M2 — it is deferred to the component milestones, §16). Covers routes, git-sandbox, skills, tests, undo, provenance, approval/tool-call decision events, cancelled/pending lifecycle. Pure additive; zero behavior change. (Old M2 "evidence/receipt fold" moved to M6 — §14.) |
166
175
| ADR-0032-M3 | Plan gate is an interceptor using `PlanValidator`, landed parity-first (§13.3): a shadow-parity test asserting interceptor==inline per reason code went green before the inline branch was deleted in a separate commit. Denials and reason codes match current behavior; adversarial and first-hour tests remain green. |
167
-
| ADR-0032-M4 (REVISED — owner decision B, 2026-06-13) | Budget gate ONLY moves to an interceptor, parity-first in two commits (shadow parity green → enforce+delete), done alone. **Approval enforcement STAYS INLINE** — it is runtime-stateful (live JIT/session state, tool handler, auto-mode-swappable policy), a poor fit for the pure-interceptor model (every coupling gap was invisible to a unit parity test; see `docs/work-log/m4-approval-sliceB-blocked-2026-06-13.md`). Approval observability is already provided by M2 (approval audit events are typed + reader-surfaced); the M6 fold reads them. Budget warnings/exhausted behavior unchanged. |
176
+
| ADR-0032-M4 (CLOSED — owner decisions B + B-analog, 2026-06-13) | **No gate moves to an interceptor; approval AND budget enforcement both STAY INLINE.** Both proved runtime-stateful on assessment, a poor fit for the pure-interceptor model. **Approval** (decision B): live JIT/session state, tool handler, auto-mode-swappable policy — every coupling gap was invisible to a unit parity test (`docs/work-log/m4-approval-sliceB-blocked-2026-06-13.md`). **Budget** (decision B-analog): it is three mechanisms — only the global cost cap (`_assert_cost_budget`) is stateless; the phase budget (live `phase_tracker`) and the warning ladder (`_budget_warning_levels_emitted` + `BudgetMonitor._emitted_levels`/`_prompted` dedup sets + an interactive `on_prompt` side-effect handler — the same `assert_allowed` shadow-coexistence trap that blocked approval) are stateful, and even the cost cap is enforced at two evolving-cost points per iteration that do not map 1:1 to events (`docs/work-log/m4-budget-stays-inline-2026-06-13.md`). Both gates' observability is already provided by M2 (their audit events — `tool_call_*`, `approval_*`, `budget_warning`, `budget_prompt`, `phase_budget_warning` — are typed + reader-surfaced); the M6 fold reads them without owning enforcement. Approval/budget behavior unchanged. **Net: plan gate (M3) is the sole governance gate moved to an interceptor.** |
168
177
| ADR-0032-M5 | HookRegistry subscribes through the spine; Claude-Code-compatible hook names remain aliases; public hook API docs and tests pass. |
169
178
| ADR-0032-M6 (was M2 fold; corrected scope A) | Evidence and receipts are folded from the typed event stream and equal the legacy builder on success/failure/pending fixtures (cancelled once emitted in M2); the fold reads the full stream (no fallback flag, per Q1); synthetic receipt-only fixtures are retired or relabeled legacy. Runs only after M2 coverage + M3/M4 decision events exist. |
170
179
| ADR-0032-M7 (was M6) | ContextBus and webhook sinks consume the spine; inline emission paths are deleted; validator shows no orphaned eventing modules. |
@@ -425,7 +434,14 @@ full acceptance run):**
425
434
-`tests/lifecycle/test_run_event_spine.py`
426
435
- Risk: high. Parallelizable: no. Human Review Required: no.
427
436
428
-
### ADR32-M4-T001: Approval Interceptor Contract
437
+
> **M4 TICKETS CLOSED — NOT IMPLEMENTED (owner decisions B + B-analog, 2026-06-13).**
438
+
> T001/T002 (approval) and T003 (budget) below are retained as the *planning
439
+
> record only*. On assessment both gates proved runtime-stateful and stay inline
440
+
> (see the M4 row in §7 and the two work-log reports). The plan gate (M3) is the
441
+
> sole governance gate that moved to an interceptor. No code from these tickets
442
+
> shipped.
443
+
444
+
### ADR32-M4-T001: Approval Interceptor Contract [CLOSED — not implemented]
429
445
430
446
- Goal: define approval interceptor semantics without touching budget.
|**Global cost cap**|`_assert_cost_budget` (~line 213) | Pure function of `(cost_cents, budget.max_estimated_cost_cents)`; raises `BudgetExceededError`|**Yes** — the plan-gate analog |
0 commit comments