Skip to content

feat(harness-state): add the observed-harness-state envelope record - #319

Merged
schickling-assistant merged 13 commits into
mainfrom
schickling/hs-01-envelope
Aug 24, 2026
Merged

feat(harness-state): add the observed-harness-state envelope record#319
schickling-assistant merged 13 commits into
mainfrom
schickling/hs-01-envelope

Conversation

@schickling

@schickling schickling commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

First slice of #268 (the activity-lease slice of #162): the generic observed-harness-state envelope.

  • src/harness_state.rs: a harness-state record beside status carrying state ∈ idle|active|child|ended (child reserved, no producer) × blockedOn ∈ none|human × inputBuffer ∈ empty|nonempty|unknown, plus diagnostic reason, terminal exit, ptySession, sinceMs, writtenAtMs, and a monotonic transitions counter (byte-distinct writes + compatible future history).
  • Presence-record transport discipline: embedded origin timestamp (no read path touches mtime), atomic tmp+rename, own staleness constants (HARNESS_STATE_STALE deliberately not an alias of STATUS_STALE).
  • unknown is derived-only through a single indeterminate constructor with a distinct reason per absence (malformed-record, stale, future-skew, session-dead); a missing record reads as no observation, not unknown.
  • Session-liveness cross-check hook (SessionLiveness): positive death downgrades a fresh live record to unknown; an indeterminate probe downgrades nothing; ended survives its writer by design.

11 unit tests incl. byte-distinctness, coalescing, restart counter continuity, forward-vocabulary degradation to indeterminate.

  • Cycle-2 hardening + the ask axis: session-owned writes (takeover write-through, producers mark session boundaries, foreign-schema records never re-stamped or coalesced), ptySession mandatory for live states with the unfenced-record derivation, IO errors read indeterminate rather than absent, and blockedOn gains the machine-readable ask ∈ none|permission|question|review kind (review reserved — no producer emits it) so consumers never branch on reason.

  • Cycle-3: ownership became structural — the record carries the writing session's incarnation token (wrappers mint it and export it to their sibling hook/channel processes); coalescing, heartbeat eligibility, and terminal suppression compare token equality, never timestamps, deciding same-millisecond takeovers and lingering-predecessor heartbeats correctly. Every landed write is byte-distinct via strictly monotonic per-record stamps.

  • Cycle-4: ownership gained a direction — a monotonic seq only a session claim advances; stragglers from superseded sessions are refused in live and terminal paths (no timestamp comparison anywhere), stamps are never inherited from beyond the future-skew trust bound, and the two bounded residuals (same-instant dual claims; hook-environment visibility) are stated in the spec.

  • Cycle-5: claims are now WRITTEN acts — a session claims by writing an exitless ended (superseded) takeover record under the record lock (racing claimers mint distinct sequences; a predecessor's fresh live record is superseded at relaunch), token-only writers never claim (adopt-or-refuse), and terminal suppression applies only to exit-bearing records so the claim placeholder never fences its own session.

  • Cycle-7: wrapperless claims are atomic (eligibility + written takeover under one lock; a wrapper's fresh placeholder counts as owned), reads are tri-state (unreadable bytes are never a virgin seat), and sequence exhaustion refuses claims loudly.

  • Cycle-8: a .harness-state.seq floor sidecar (written stage-and-rename under the record lock, loud on failure) keeps claim sequences monotonic across unreadable records, and writer-side reads are tri-state — only NotFound is absence; any other IO failure is an unreadable record that refuses virgin-seat treatment.

Part of the #268 stack (#319#326). Vocabulary defined here — the DING-R12 words #268 cites come from the still-open #123, not the tree.

🤖 Generated with Claude Code

Refinement pass (adversarial review + live smoke): writer operations now take the record's cross-process flock and treat the on-disk record as authoritative (a stale process cannot resurrect what it read before a peer's write; a wrapper heartbeat re-stamps the newest state, including hook-written ones); an unchanged observation is a no-op until the refresh cadence is due — the live smoke measured an SSE producer restating ~3×/s, which must never 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; an unreadable record is indeterminate rather than absent; and observe_unless_ended() lets a producer whose terminal record comes from a sibling process drop queued live frames after it.

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>
@schickling

Copy link
Copy Markdown
Contributor Author

Validation note (full workspace suite, this host): cargo test --workspace --all-targets --no-fail-fast -- --test-threads=1 shows 13 red tests, all 13 pre-existing and reproduced identically on a clean worktree at base e282a7e (git/pty/sleep binaries under the scrubbed test PATH on this NixOS host, plus two docs-string tests predating the branch). Zero reds introduced by this stack; every suite the stack touches is green (393 lib incl. all new producer/envelope/watcher tests, agent-spec 56, invariants incl. the new rows, status_agents, doctor, nomad_survival, hooks, driver expansion).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c8913eda68

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs Outdated
Comment thread src/harness_state.rs
Comment thread src/harness_state.rs Outdated
…erivations

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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad62a26214

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs
Comment thread src/harness_state.rs Outdated
…, 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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d503968b4e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs Outdated
Comment thread src/harness_state.rs
Comment thread src/harness_state.rs Outdated
…c 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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cce245c42c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs Outdated
Comment thread src/harness_state.rs Outdated
… 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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c669388c56

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs
Comment thread src/harness_state.rs Outdated
…ever 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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f070115eae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs
Comment thread src/harness_state.rs
Comment thread src/harness_state.rs
schickling and others added 2 commits August 24, 2026 01:46
…ements, 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>
… 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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f38bb426a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs Outdated
Comment thread src/harness_state.rs Outdated
Comment thread src/harness_state.rs Outdated
…oud 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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8044c8131b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs
Comment thread src/harness_state.rs Outdated
schickling and others added 2 commits August 24, 2026 02:44
…O 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>
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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c8bc39553

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/harness_state.rs Outdated
// the true successor out permanently. New sequences are minted only by [`claim`],
// the written act, and adopted from it.
None => match on_disk.as_ref() {
None => 1,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Persist the initial token-only ownership sequence

Fresh evidence after the sequence-floor fix is this virgin token-only path: it creates sequence 1 without updating .harness-state.seq. If that record later becomes unreadable, a replacement claim sees neither a parseable sequence nor a floor and reuses sequence 1; the lingering token-only predecessor also holds sequence 1, so its next observation passes the current.seq > seq fence and can replace the successor's record. Persist the floor when establishing this initial ownership so an unreadable first record cannot recreate equal-sequence owners.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a8d5915 on schickling/hs-01-envelope: the virgin token-only path now persists the .harness-state.seq floor through the same loud stage-and-rename as claim_locked when it mints sequence one, so if that record later goes unreadable, a replacement claim continues past it instead of re-minting an equal sequence and alternating ownership with the lingering writer. Regression fixture: a_virgin_token_only_write_persists_the_sequence_floor.

Posted on behalf of @schickling
field value
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a8d5915 on schickling/hs-01-envelope: the virgin token-only path now persists the .harness-state.seq floor through the same loud stage-and-rename as claim_locked when it mints sequence one, so if that record later goes unreadable, a replacement claim continues past it instead of re-minting an equal sequence and alternating ownership with the lingering writer. Regression fixture: a_virgin_token_only_write_persists_the_sequence_floor.

Posted on behalf of @schickling
field value
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

schickling-assistant and others added 2 commits August 24, 2026 15:30
… 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
…ver 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
@schickling-assistant
schickling-assistant merged commit 06e3e5b into main Aug 24, 2026
1 check 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.

2 participants