Skip to content

Commit 06e3e5b

Browse files
schicklingclaudeschickling-assistant
authored
feat(harness-state): add the observed-harness-state envelope record (#319)
* feat(harness-state): add the observed-harness-state envelope record The driver-owned catalog record of what a harness is seen doing: state (idle|active|child|ended, unknown derived-only) x blockedOn x inputBuffer, with presence-record transport discipline (embedded origin timestamp, atomic byte-distinct writes, own staleness constants) and a session-liveness cross-check hook where indeterminate probes downgrade nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): serialize writers, bound restatements, and gate derivations Review-pass hardening of the envelope: every writer operation takes the record's cross-process flock and treats the on-disk record as the authoritative current state (a stale process can no longer resurrect what it read before a peer's write, and a wrapper heartbeat re-stamps the newest state incl. hook-written ones); an unchanged observation is a no-op while the record is fresh and a heartbeat-equivalent re-stamp once the refresh cadence is due (measured: an SSE producer restated ~3x/s — restatements must not reach the transport); interrupt() marks evidence discontinuity so a restated tuple cannot claim continuity across an unproven interval; a predecessor session's record is never heartbeat-eligible; the schema discriminator gates interpretation (unsupported-schema, never definite words from an alien schema); an unreadable record is indeterminate, never absence; and observe_unless_ended() lets a producer whose terminal record comes from a sibling process drop queued live frames after it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): session-owned coalescing, schema-guarded rewrites, fenced live records, and the ask axis A new session's first observation always writes through a matching fresh predecessor (and marks itself discontinuous where the producer says so), heartbeats and coalescing never touch a record whose schema or session this writer does not own, live observations must name their pty session (unfenced live records read unknown under a probe), IO errors are indeterminate rather than absence, and blockedOn gains the machine-readable ask kind (none|permission|question|review). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): incarnation-token ownership and strictly monotonic stamps Ownership is token equality, never a timestamp comparison: coalescing, heartbeat eligibility, and terminal suppression all require the record to carry this session's incarnation token (additive field, empty in pre-token records, which no session owns). Same-millisecond takeovers and lingering predecessor writers are both decided correctly, sibling writer processes share one token, and every landed write is byte-distinct via a per-record strictly monotonic stamp (bounded +1ms skew per write). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): directional ownership sequences and trusted stamp inheritance Ownership gains a direction: only a session claim advances the record's monotonic seq (to disk+1), sibling writers adopt the claimer's exported token+seq, and any writer whose claim is below the on-disk sequence is a straggler whose live and terminal writes are refused — a lingering predecessor can no longer replace its successor's record through the takeover write-through. Stamps are only inherited from records inside the future-skew trust bound (a poisoned or overflowing stamp resets to the writer's clock, saturating throughout). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): claims are written acts, and token-only writers never claim A session claim is now a write under the record lock — an exitless ended(superseded) takeover record carrying the new token and the next sequence — so racing claimers mint distinct sequences (the dual-claim residual dissolves) and a predecessor's still-fresh live record is superseded at relaunch, where the pty-name probe cannot tell sessions apart. Token-only writers adopt or start virgin records but are refused against foreign tokens: sequences are minted only by the claim. Terminal suppression applies only to exit-bearing records, so a session's own claim placeholder never fences its first frames. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): schema-refusing writers, trusted-freshness restatements, and validated asks Non-claiming writers refuse foreign-schema records outright (their serde-default sequence of zero is below every claim; only the written claim supersedes an unsupported schema), an unchanged restatement is a no-op only under a stamp a reader would trust (a beyond-skew leftover falls through and repairs to the writer's clock), and the ask axis is validated at the write boundary — never Unknown, and only beside a human block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): wrapperless claims never supersede a live wrapper record Found by the cycle-6 self-review of the wrapperless-succession fix: a hook fired by any interactive session inheriting the project-scoped registration could otherwise claim over the live wrapper and fence it out until restart. Wrapperless claimers take over nothing, fellow wrapperless tokens, terminal records, and staleness — never a live wrapper-kept record. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): atomic wrapperless claims, tri-state reads, and loud sequence exhaustion The wrapperless claim is one act under the record lock — eligibility and the written takeover together, so a hooks-only SessionStart racing a wrapper's startup cannot steal the sequence between the wrapper's read and its write — and a wrapper's fresh claim placeholder counts as owned while an abandoned one ages into claimability. Reads are tri-state: unreadable bytes are never a virgin seat (non-claiming writers refuse; only the written claim supersedes, restarting sequence and counter). A saturated sequence refuses claims loudly instead of minting shared ownership. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): a sequence floor across unreadable records, and IO failures are never absence The claim writes a floor sidecar under the record lock, so a claim after record damage continues past the damaged sequence instead of restarting below a lingering predecessor (who would replace it and fence the new session out); read failures other than NotFound are Unreadable, never a virgin seat. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): the sequence floor writes loudly and atomically The floor exists to protect claims when the record goes unreadable, so its own write must not fail silently or tear: stage-and-rename like the record, and log the failure instead of swallowing it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(harness-state): the virgin token-only write persists the sequence floor Initial ownership is established by more than the claim: a token-only writer's first write on a virgin seat mints sequence one, so it now persists the floor sidecar through the same loud stage-and-rename as claim_locked — if that record later goes unreadable, a replacement claim continues past it instead of colliding with the lingering writer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> agent-identity: unknown agent-persona: generalist agent-supervisor: unavailable agent-tool: OMP agent-tool-version: 18.0.3 agent-runtime: OMP 18.0.3 tooling-profile: dotfiles@f33cd9c-dirty * fix(harness-state): a fresh claim placeholder reads indeterminate, never definite ended The claim placeholder is a fence, not an observation: the session wrote it at startup and has observed nothing yet. A live seat whose harness never publishes its first frame promptly — pi's extension failing open, for one — read as dead for the whole freshness horizon while its process ran. Readers now derive indeterminate with the distinct reason 'claimed' from a fresh exitless superseded record; fencing, aging, and persistence are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> agent-identity: unknown agent-persona: generalist agent-supervisor: unavailable agent-tool: OMP agent-tool-version: 18.0.3 agent-runtime: OMP 18.0.3 tooling-profile: dotfiles@f33cd9c-dirty --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: schickling-assistant <261620128+schickling-assistant@users.noreply.github.com>
1 parent e282a7e commit 06e3e5b

2 files changed

Lines changed: 1961 additions & 0 deletions

File tree

0 commit comments

Comments
 (0)