Skip to content

feat(context-engine): D3 P3 — two-phase write-once consequence writer (human --apply) - #48

Merged
ProtocolWarden merged 1 commit into
mainfrom
feat/d3-p3-consequence-writer
Jul 17, 2026
Merged

ProtocolWarden merged 1 commit into
mainfrom
feat/d3-p3-consequence-writer

Conversation

@ProtocolWarden

Copy link
Copy Markdown
Owner

What

The D3 P3 consequence WRITER: dispatches the P2 attribution plan (#47) into cold-item frontmatter, strictly inside the EXISTING human --apply interlock. With this, the D3 learn-loop is functional end-to-end — attribution → consequence write → promotion gate — still human-applied.

New context_engine/attribution_apply.py + a 4-line-visible integration in consolidate.py (additive only).

Two-phase semantics (spec §3.5, §4.3, §4.4)

Phase A — new attributions. For each planned (slug, sha, tests_green): the item is re-loaded FRESH from disk and skipped (with a recorded reason) unless it is still cold-tier AND acted_on_commit is not already a real sha — write-once is re-checked at write time, not just plan time (a stale plan skips: already_attributed, not_cold_tier, item_missing). The write is dataclasses.replace + cold.write_item (atomic whole-file rewrite, the single writer), with tests_green EXACTLY as planned: a planned "unknown" is written as "unknown", never upgraded; any nonstandard planned value degrades to "unknown", never to True.

Phase B — CI flip for attributed items. Every cold item on disk with a real acted_on_commit sha and tests_green == "unknown" (including fresh Phase-A writes — an immediate §3.5 "arrival") gets its CI resolved for THAT sha via an injectable seam (default: the P2/P1 fail-closed ci_status path). Only a literal True/False is ever written. Monotone, guarded in code + tests: "unknown" → True|False at most once; True/False items never enter the worklist AND are re-checked before the write — never True → anything, never False → anything, never a re-flip, no path defaults to True.

Never raises: every per-item failure is recorded (write_failed, flip_failed, ci_unresolved, …) and the batch continues; AppliedResult{applied, flipped, skipped} is fully auditable.

Human interlock preserved — no autonomous path

  • The writer has no CLI of its own — a standalone entrypoint would be a second, unreviewed mutation surface. The ONLY mutation path is the existing reviewed consolidate.py --apply.
  • Dry-run (plan_consolidation / the CLI without --apply) now additionally computes and RENDERS the attribution plan + the PENDING-CI-FLIP worklist for the human reviewer — zero writes (byte-identical tree, tested), and fail-soft: attribution failure never breaks the existing consolidation output (verified incl. a scaffolded-consumer sim where the attribution modules are absent).
  • Under --apply the writer runs FIRST, before the cold index loads, so gate_promotions sees the freshly-written consequences in the SAME pass (§4.3) — covered both ways (Phase-A True promotes; Phase-B flip promotes).
  • plan_consolidation keeps its exact behavior/signature for existing callers: one additive attribution_runner=None parameter + one additive ConsolidationPlan.attribution field.
  • Autonomous apply is P4 — an explicit operator decision. NOT in this PR.

Token via gh-auth seam (no argv/env secrets)

Phase B in the human-run CLI needs a GitHub token + repo identity. No secret on argv (the deliberate P2 CLI choice, preserved) and NO env reads (C13/E1): injectable providers default to gh auth token (gh is the repo's auth source of truth — ci_status already shells to gh) and git remote get-url origin parsed to owner/repo (https/ssh/scp forms). Both are bounded subprocesses; any failure ⇒ None ⇒ CI resolves "unknown" ⇒ Phase B safe-inert. Providers are consulted lazily — a Phase-A-only pass spawns no subprocess. The plan path stays token-less so Phase-A writes match the reviewed dry-run plan verbatim.

Tests (33 new, all seams faked; full suite 498 pass, ruff clean)

  • Phase A verbatim writes: True, False, "unknown"-stays-"unknown", nonstandard→"unknown"
  • Write-once re-checked at write time: attributed-on-disk / promoted-to-warm / deleted between plan and apply ⇒ skipped
  • Phase B: unknown→True, unknown→False; unresolved/nonstandard seam values leave "unknown"
  • Monotone: True stays True, False stays False, no re-flip across passes — even against a contradicting seam
  • Per-item isolation: seam raising mid-batch (rest still processed), write failure recorded, never raises
  • Provider failures (None + raising) ⇒ Phase B no-op; providers not consulted without pending flips
  • run_attribution dry-run byte-pure + plan/pending visibility; plan failure degrades without apply
  • Consolidate integration: dry-run renders attribution + pending flips with a byte-identical tree; failing runner never breaks consolidation; apply-then-gate in the same plan_consolidation(apply=True) pass (Phase A and Phase B variants); CLI default-runner smoke
  • Default providers: gh auth token / remote-URL parsing (subprocess seams faked)

Custodian pre-push: 0 findings (first pass flagged D11 _load clone — resolved by importing attribution.py's loader instead of cloning it).

🤖 Generated with Claude Code

… (human --apply)

New context_engine/attribution_apply.py: dispatches the P2 attribution
plan into cold-item frontmatter via dataclasses.replace +
cold.write_item (atomic whole-file rewrites), strictly inside the
existing reviewed --apply interlock. Phase A writes acted_on_commit +
the planned tests_green VERBATIM (a planned "unknown" stays "unknown";
nonstandard values degrade to "unknown", never True), with write-once
and cold-tier re-checked at write time against fresh disk state (a
stale plan skips, with recorded reasons). Phase B flips
previously-attributed "unknown" items only on a literal True/False
from the fail-closed CI seam — monotone: "unknown"->True|False at most
once, True/False never touched, no re-flip. The apply path never
raises; every per-item failure is recorded and the batch continues.

Phase-B identity carries no argv secret and reads no env: injectable
providers default to `gh auth token` and `git remote get-url origin`
(https/ssh/scp parsed to owner/repo); any failure => None => "unknown"
=> safe-inert.

consolidate.plan_consolidation gains an additive attribution_runner
(default None keeps every existing caller unchanged) and
ConsolidationPlan.attribution; the CLI wires run_attribution so the
dry-run renders the attribution plan + PENDING-CI-FLIP worklist for
the human reviewer (zero writes, fail-soft — attribution failure never
breaks consolidation, incl. scaffolded consumers lacking the
attribution modules), and under --apply the writer runs FIRST so
gate_promotions sees fresh consequences in the same pass (spec §4.3).

No autonomous apply: P4 is an explicit operator decision — no new CLI,
no mutation surface outside the human interlock. 33 new tests
(tests/test_attribution_apply.py, all seams faked); full suite 498
pass; ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ProtocolWarden
ProtocolWarden merged commit 1b40ac4 into main Jul 17, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant