Skip to content

Deliver Card context one phase at a time - #139

Merged
KimGLee merged 3 commits into
mainfrom
feat/phased-readback-v4
Aug 22, 2026
Merged

Deliver Card context one phase at a time#139
KimGLee merged 3 commits into
mainfrom
feat/phased-readback-v4

Conversation

@KimGLee

@KimGLee KimGLee commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Closes #138.

Implements an Accepted ADR held as a maintainer design record outside this
repository. Its decisions are restated below, so nothing here depends on
reading it.

The principle this restores

The ADR states it before any decision, and it is the whole point of the change:

Card 是面向当前工作阶段的最小可执行检查单,是规范的操作界面,而不是规范全文、知识加载容器、合规收据或 MCP 传输单元。系统只应在动作发生前交付完成该动作所需的最小 Card 集;需要追溯时再按需展开来源规范。

v3 froze one manifest of the task's whole route union and made all of it a precondition of running. That is a knowledge-loading container wearing a Card's name.

What changes

Phases, not one pile. Admission still freezes a closed set — that is what makes delivered falsifiable — but it now freezes it partitioned into five phases: batch-preflight, batch-running, batch-gate, governance, task-completion. Each is owed at the moment its work begins, not before the batch starts. Routes map to phases by a registered override table (R01→preflight, R09→governance, R12→gate, R08→task-completion); everything else lands in running. A batch that never enters governance never pays for R09.

The partition is frozen by content, not by rule. phase_plan_sha256 seals four things together: the phase→piece assignment, the resolver version, the Standards/Profile snapshot the routes were resolved under, and the Work Spec fingerprint that narrowed them. Change any of them and the plan invalidates, because "the rules were the same" is not the same claim as "the set was the same".

The current attempt has an authoritative pointer, and it is derived. delivery_attempt_id = sha256([card_bundle_sha256, execution_context_id])[7:39]. Consumers recompute it rather than reading a stored field, so "this evidence is for the current bundle" and "this actor is the one who read it" are the same check, and no new mutable state exists to drift.

Gates now have consumers. Previously frozen_piece_ids had no production caller and update_queue's comment deferred to a gate that did not exist. Now: record_batch_judgment requires the gate phase bound to the judging actor's own context; update_task requires the task-completion phase likewise; the open -> merge-ready edge requires the gate phase, plus the governance phase when the batch touches the control plane.

Governance triggers on the object, not the tool name. batch_touches_control_plane reads the batch manifest for kernel/, profiles/, Tools/ prefixes. A tool-name allowlist is bypassed by editing a file; an edge predicate over the manifest is not. Enforced at merge-ready rather than at each writer's admission, for the same reason.

Proof is three independent dimensions, not a ladder. host-conformance.yaml goes to schema_version: 2 with a channel list declaring proves_identity / proves_transport / proves_acknowledgement separately. agent-native-file-read proves identity and acknowledgement and explicitly does not prove transport — under v3's monotonic states that channel had to be misfiled as something it was not.

Kernel

New K13/21 Phased Reading Plan owns the phase set, the mapping, and the invalidation rules. K13/19 and K13/20 are revised to point at it rather than restate it; K13/20 gains a Phase Scope section that admits in writing which phase is spec-only today. K00/01 carries the principle above. Two size-register entries were added, one of them (K13/19, 11KB→12KB) with a registered split condition naming Frozen Review Plan as the next thing to leave.

Verification

make ci: 2142 tests, 3 failures — all three pre-existing on main (untracked profiles/_template-minimal/ and a stray .DS_Store), proven unrelated by git stash. New suite Tools/tests/test_phased_readback.py, 33 tests.

Adopted into a real corpus as Standards 3.14.0 and run end to end (require-readyopen--deliver-phase batch-preflight--ack-activation-phase), with run_gates at 12 pass / 0 fail / 3 standing holds:

v3 v4 preflight
pieces before running 23 6
bytes 180,120 42,946
MCP calls 46 1

The governance route's 15 pieces are still frozen and still owed — just not until governance begins.

Deviations from the ADR, and why

  • D4's authoritative pointer is derived, not stored. The ADR left the carrier open; delivery_attempt_id was already a deterministic function of the bundle and context, so a stored field would have added drift without adding information.
  • No Closed List member was added to check_batch_close. closed is reachable only from merge-ready, which enforces the predicate. A second copy of the check would be a second thing to keep true.
  • The batch-running phase is specified but has no consumer. There is no event that marks "running work begins" for a machine to hang a gate on. K13/20 Phase Scope says so in the spec rather than leaving the gap for a reader to discover.

KimGLee and others added 3 commits August 23, 2026 02:14
v3 made delivery provable and, in doing so, made every batch pay for the
whole task's route union before it could be called running. AR-001 measured
what that costs in practice: 23 pieces and 165KB over 560 seconds for a
single-page corrective batch, of which the governance route it never entered
was 15 pieces and 68% of the bytes. The Card that K00/01 defines as a
faithful compression for routine work had become the trigger for shipping a
manual.

v4 changes when frozen bytes travel, not what is frozen or what is proved.
Admission still resolves every identity at the same moment -- now including
the phase each piece belongs to and the environment that resolved them,
sealed together as phase_plan_sha256 -- and the bytes travel one phase at a
time. Each phase gate is still an exact set equality, so the property v3
bought is kept per phase rather than spent all at once.

Three things make that trade honest rather than a weakening.

The authoritative pointer is derived, not stored. delivery_attempt_id was
already the hash of (card_bundle_sha256, execution_context_id), so a
consumer recomputes the one value the current bundle and the acting context
could produce. A complete ack chain from a superseded bundle stays
internally self-consistent and stops matching that derivation; so does one
borrowed from another context. Consistency alone was never the test, and now
nothing has to store a field for that to be true.

The gates have consumers. v3's ack completeness had none: update_queue's own
comment deferred to an Assignment gate that does not exist, and
frozen_piece_ids had no production caller. The gate phase is now refused by
record_batch_judgment (bound to the actor's context, because a judgment is
somebody's judgment) and by the merge-ready edge (checking history, so
unbound). Governance is enforced by what a batch changes rather than which
tool it runs: a manifest touching kernel/, profiles/ or Tools/ owes the
governance phase at merge-ready, an edge no file edit can route around.
running itself is still spec-only and K13/20 now says so in its own text.

Phases are packed, not fragmented. A part is one tool result with one
trailing nonce -- v3's single-piece proof with the file count widened -- and
every file keeps its own frozen hash and drift check. A standard phase that
needs two parts fails admission: that is a plan cut too wide, not a
transport problem. An oversized leaf still fails closed on its own, checked
before packing so it can never be reported as a phase that was merely wide.

Also here: host-conformance moves to schema 2, grouping registrations by
channel with three independent dimensions, because a native file-read
channel can prove identity and acknowledgement while proving no transport
at all -- and a registry with one global "conformance_version" would have
let it borrow an assurance it cannot earn.

K00/01 carries the principle this all serves, as normative text rather than
implication: a Card is the minimal executable checklist for the current
phase, not the Standards in full, a knowledge container, a receipt, or a
transport unit. K13/21 owns the phase plan; K13/19 keeps transport and
budget; K13/20 keeps Assignment state. K00/16 records both size raises,
and registers the split K13/19 must take instead of a fourth one.

Sealed v1-v3 receipts replay under their own era untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adopting the branch into a real corpus found what the unit suite could not,
and both defects were the same shape: a check that named the current
protocol constant where it meant a class of shapes, and a value computed
correctly and then dropped on the floor.

Era detection compared against ACTIVATION_PROTOCOL. That constant now says
v4, so every sealed v3 receipt was re-filed under the embedded-payload rules
of v1 and the adopting runtime refused its own history -- "activation bundle
must carry at least one Card" against a bundle that correctly carries
pieces. The comparisons now test PIECE_DELIVERY_PROTOCOLS, which is what
they always meant, and the error text quotes the record's own era rather
than today's.

The phase delivery reached the receipt writer with phase_context still
unset, because _run's call site never passed it: an earlier edit replaced
the same two lines twice inside _write_receipt and left the caller alone.
The result was a receipt whose mode said deliver-phase and whose phase
fields were all null -- exactly the "receipt claims what it did not carry"
failure this protocol exists to prevent, reproduced in the protocol's own
plumbing.

Both gaps existed because the tests exercised the library and the adoption
exercised the tool. So the suite now covers what the library round trip
cannot see: a producer-era class that replays v3 contexts through validation,
single-piece delivery and the phase refusal, plus a CLI class that runs the
actual commands and asserts the receipt carries the phase and the payload
does not.

Also: the phase branch of the CLI now round-trips deliver and ack, verified
end to end against a real corpus -- preflight for a live batch is 6 pieces
and 42,946 bytes in one part, against 23 pieces and 180,120 bytes across 46
calls under v3, with the governance route's 15 pieces held out of startup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The predicate is about the "profiles/" prefix, not about any one adopter,
and a public test has no business naming a private instance's profile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@KimGLee
KimGLee merged commit 7caba0b into main Aug 22, 2026
12 checks passed
@KimGLee
KimGLee deleted the feat/phased-readback-v4 branch August 22, 2026 19:04
@KimGLee KimGLee added bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request agent-interface Host-neutral agent interface: contract, projection, MCP server, per-host configs labels Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-interface Host-neutral agent interface: contract, projection, MCP server, per-host configs bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Card activation delivers the whole task's route union before every batch

1 participant