Commit f0868c3
feat(harness-context): the pi and omp producers over one extension path (#380)
* feat(harness-context): the pi and omp producers
One extension path, two harness tags. `ctx.getContextUsage()` answers the whole
fill triple in one call on both harnesses and rides the ctx of every lifecycle
event, so neither producer keeps an accumulator or reads a second source.
The two are separate producers because the identical call means different
things, which is HC-T03's second version-coupled constant: pi's `tokens` is the
last assistant message's `totalTokens` (input + output + cacheRead +
cacheWrite), omp's is its prompt figure alone. Measured 2026-08-29 in the
credential-free labs against pi 0.84.2 and omp 18.0.9, reproduced on 18.0.3. The
fixtures carry both numbers from each capture and assert the one the producer
must NOT publish, so a bump that silently swaps the meaning fails rather than
publishing a differently-meaning number under the same field name.
Emit cadence is deliberately liberal and holds no policy: st2's write guard
quantizes to 1% of the window, so at most one write per bucket entered however
chatty the harness is. The finest boundary is what matters — turn-boundary-only
observation was measured at 92% of pre-compaction warnings missed, because the
wedge case is a single long turn, and `message_end` defeats that since each tool
call and its result form their own assistant message.
Three measurements shaped the compaction path. Inside pi's own `session_compact`
handler `getContextUsage()` ALREADY reports `{tokens: null, percent: null}`
while the window stays populated, and `getEntries()` already counts the new
entry — so one frame carries the withheld reading, the trigger, and the durable
count, and lands as one write. That pairing is load-bearing: an edge always
writes while a withheld percent has no bucket, so an edge sent alone would
publish the stale pre-compaction numbers beside it and the truth would wait for
the heartbeat. omp's event names no reason at all, so its trigger is `unknown`;
it does call its auto-compaction "idle" and "threshold" internally, but those
words are not projected onto the event and inventing one would be a claim no
capture supports.
`costUsd` is the last assistant message's `usage.cost.total`, held in the stash
and restated on every frame: the record replaces a reading's fields wholesale —
deliberately, so a withheld number is never fabricated — so a frame from an
event carrying no cost would otherwise erase it. `sessionTotalTokens` stays null
on both, per HC-R16: summing it needs a producer-side accumulator whose
correctness depends on having seen every message.
The version pins extend the gates that exist rather than adding a mechanism.
omp's launch gate admits a minor series and the fixture pins the exact builds a
meaning was measured on; a new test fails if the fixture ever claims a build the
gate would refuse to launch. pi has no runtime gate, so its fixture pins the
flake tarball the extension check already type-checks and smokes against.
Both smokes now drive each handler with three contexts — bare, fully populated,
and one whose every telemetry pull throws. The bare one alone never executed the
producer's body at all, which is precisely the blind spot that once let a TDZ
ship green. A compile-time pin on pi's own declarations keeps the type gate's
teeth where the runtime guards widen the view.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
agent-identity: dev3.direct.claude.4f8m9tm7
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.250
agent-runtime: Claude Code 2.1.250
tooling-profile: dotfiles@a1a5f89
* docs(vrs): record what the pi and omp producers measured, and one delta
The spec's Status section claimed no producer ships, which two of five now do.
HC-R11 is partially satisfied and says so; the three unshipped harnesses still
read `context: null`, which stays the honest state rather than a defect.
Three facts settled while implementing pi's producer, each shaping the code
rather than only its constants, and each recorded in the pi section: the nulls
are already present inside pi's own `session_compact` handler (so the edge and
the withheld reading are one frame and one write, which the write policy makes
load-bearing rather than an optimization); `message_end` is the emit boundary
because the wedge case is a single long turn; and `costUsd` must be restated
from a producer-side hold, because the record replaces a reading's fields
wholesale and a frame from a cost-free event would otherwise erase it.
The omp section gains its two divergences from that path — omp does not null its
reading at the edge, and has no `agent_settled` — and states how the version pin
extends the existing launch gate. The gate admits a minor series and the fixture
pins exact builds; those answer different questions and are allowed to differ,
but a test now fails if they drift, because a build the fixture claims to have
measured but the gate refuses to launch is evidence for a version nothing runs.
The compaction table gains the one degradation path a "harness-durable" row has:
a producer that cannot read the session store still sends the edge, and st2
counts it. The scope narrows silently, which is exactly why it is written down.
DELTA-005 records a real contradiction rather than papering over it. HC-R16 says
the record carries "the harness-reported session cost", but pi and omp report a
per-message figure, and the spec's own producer table already said so — the two
halves disagreed before any producer existed. Summing to a session total needs
the producer-side accumulator HC-R16 refuses one field over, for
`sessionTotalTokens`, for the reason that transfers unchanged: a sum whose
correctness depends on having seen every message, taken by an extension that may
have joined a running session, is worse than an honest smaller number.
The *Harness context discipline* invariant row gains the producer half — a
harness's own numerator, pinned to the build it was measured on, so neither
harness's arithmetic can ship under the other's tag — with the eight new proofs
named. No new row: three producers do not exist yet, and a row claiming
all-harness fixture discipline would be false.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
agent-identity: dev3.direct.claude.4f8m9tm7
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.250
agent-runtime: Claude Code 2.1.250
tooling-profile: dotfiles@a1a5f89
* test(harness-context): make the extension smoke prove the wire, not just the load
The frame envelope between the shipped assets and `pi_channel` has its two
halves in different languages in different files, and nothing coupled them.
With `true` as the channel binary the frames went into a pipe nobody read, so
the smoke proved "does not throw" and the Rust fixtures decoded frames written
by hand in the tests. Flatten the reading or rename a key and all of it stays
green while the record is never written again — a failure indistinguishable from
the pre-producer state where every declaration's context reads null, and the
same shape as the replication include list that Replicated-path discipline
already pins names for.
The channel is now a recorder that speaks a protocol-1 hello and appends every
frame, and each smoke reads them back: a context frame is emitted at all, the
reading carries all five keys, the numerator is each harness's own (23,425 for
pi; 22,500 and explicitly not 22,525 for omp), the percent is unclamped, an edge
whose session store could not be read still arrives countless, and pi's withheld
reading rides the same frame as its edge. Verified non-vacuous: flattening the
envelope fails the check with "a context frame must carry a `reading` object".
Frames are selected by predicate rather than position, because which of the
three contexts produced a given frame is not what is under test and asserting on
an index would break whenever the loop gains a case.
Also tolerate a fractional token count off the wire. Both harnesses round today
— pi's fallback estimator is `Math.ceil(chars / 4)`, checked in the pinned
tarball — so this decodes nothing observed. It exists because the failure if one
ever stops rounding is silent in the worst direction: a strict integer parse
returns `None` for `1234.75` and the producer would WITHHOLD a reading the
harness actually had. Withholding is reserved for a harness saying it does not
know (HC-R03), and spending it on a JSON number shape would make the record lie
about which of those two happened. The percent leg already parsed as a float
because pi and omp genuinely emit one there; the operands were stricter for no
measured reason.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
agent-identity: dev3.direct.claude.4f8m9tm7
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.250
agent-runtime: Claude Code 2.1.250
tooling-profile: dotfiles@a1a5f89
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent f5ffdec commit f0868c3
11 files changed
Lines changed: 1407 additions & 57 deletions
File tree
- docs/vrs
- .delta
- 08-harness-context
- hooks
- typecheck
- src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
| 26 | + | |
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| |||
Lines changed: 68 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
0 commit comments