Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
1b70915
docs(invariants): add the two observed-harness-state rows their tests…
schickling Aug 23, 2026
a48d955
test(harness-state): prove the terminal record lands before SIGKILL e…
schickling Aug 23, 2026
317b509
docs(vrs): measured limits, pi ownership split, and the DQ-H2 resolution
schickling Aug 23, 2026
560297c
test(harness-state): synchronize the teardown proof on the provider's…
schickling Aug 23, 2026
d88da18
docs(vrs): session-owned writes, fenced live records, and the ask axis
schickling Aug 23, 2026
f7de7f1
docs(vrs): token ownership, honest Draft residuals, and the cycle-3 p…
schickling Aug 23, 2026
d6586e2
docs(vrs): directional ownership, preflight-first stop handling, and …
schickling Aug 23, 2026
b29af59
test(harness-state): the teardown matrix covers opencode's own stop i…
schickling Aug 23, 2026
eaff4f2
docs(vrs): state the dual-claim tie and hook-environment residuals
schickling Aug 23, 2026
97c14d0
style: format the opencode teardown additions
schickling Aug 23, 2026
be0dd6b
docs(vrs): dissolve the dual-claim residual and state the pty-kill wi…
schickling Aug 23, 2026
062337e
test(harness-state): escalation proofs assert the wrapper's own SIGKI…
schickling Aug 23, 2026
44500aa
docs(vrs): cycle-6 producer rules — guarded wrapperless claims, legac…
schickling Aug 24, 2026
2189a6b
docs(vrs): cycle-7 rules — atomic wrapperless claims, stop-aware star…
schickling Aug 24, 2026
ded3406
docs(vrs): the opencode seed and poison guarantees match the shipped …
schickling Aug 24, 2026
effdf49
docs(vrs): the integration layer's claim statement is floor-aware too
schickling Aug 24, 2026
01ed815
docs(vrs): the claim placeholder is documented as a fence, and DELTA-…
schickling-assistant Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions INVARIANTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ materialization, messaging, DING, or presence must preserve them.
| **Agent-declared presence discipline** | The shipped bus contract requires agents to declare `busy` before executing work, use `available` only while yielding or ready, and reserve `dnd` for an explicit hold. Both native harnesses materialize that contract. Busy remains observable but does not suppress DING; fresh `dnd` is the only delivery gate. | `tests/native_only.rs::clean_path_executes_the_maintained_native_authoring_guide`; `src/ding/mod.rs::pending_delivery_ignores_busy_but_respects_fresh_dnd_archive_and_retry` |
| **Stable roster JSON** | `st2 agents --json [--enrich]` preserves field names, order, null handling, presence, typed desired state and rationale, the retirement compatibility projection, opaque declared Resource descriptors, origin-timed activity, inbox counts, and the appended `observedState` object. Declared presence, desired lifecycle, and observed harness state are three independent axes: none is derived from another, and a missing observed record is `null`, never a derived `unknown`. | `src/agents.rs::agents_json_has_stable_wire_shape`; `src/agents.rs::agents_json_preserves_opaque_declared_resource_descriptors`; `src/agents.rs::observed_state_joins_declared_presence_without_touching_either`; `tests/status_agents.rs::roster_json_and_human_output_distinguish_retirement_from_presence`; `tests/status_agents.rs::roster_keeps_presence_separate_from_suspended_desired_state`; `tests/status_agents.rs::roster_uses_version_1_origin_time_for_last_activity` |
| **Agent-declared presence** | Refresh preserves non-DND declared status and advances the version 1 heartbeat. A missing status starts as `available`. Legacy DND migrates without renewing its hold. Version 1 DND is not refreshed. Stale, malformed, or implausibly future heartbeats read as `unknown`. The outer Codex, Claude, and pi session wrappers own a five-minute heartbeat while their provider remains alive. | `src/status.rs::refresh_preserves_value_and_changes_heartbeat_bytes`; `src/status.rs::refresh_upgrades_legacy_dnd_without_renewing_the_hold`; `src/status.rs::refresh_missing_writes_available_default`; `src/status.rs::version_1_staleness_and_future_skew_are_bounded`; `src/status.rs::malformed_versioned_record_is_unknown_without_mtime_fallback`; `src/claude_session.rs::idle_provider_refreshes_presence_without_mcp_input`; `src/pi_session.rs::idle_pi_provider_refreshes_presence_without_channel_input`; `src/codex_app_server.rs::inbox_fallback_does_not_write_a_fifteen_second_presence_heartbeat` |
| **Scoped delivery-input wakeups** | Native delivery pumps watch only their inputs: the agent's `resources/inbox` subtree and its `status` file. Runtime records written beside them by the pump's own process group — presence temp siblings, the `harness-state` record, stream state — never wake delivery, so a producer that writes on every turn boundary cannot pump its own delivery loop. | `src/watch.rs::delivery_watcher_ignores_runtime_records_but_wakes_on_inbox_and_status` |
| **Observed harness state discipline** | The `harness-state` record is written only by the owning session's driver processes — wrapper, channel, or hooks — serialized by a cross-process lock and coalesced against the on-disk record, atomically and byte-distinct on every write that lands, with freshness from its embedded timestamp and never file mtime; restating an unchanged state touches the record only when the refresh cadence is due. `unknown` is derived, never written: staleness, future skew, malformation, an unsupported schema, and a provably dead pty session each read as `unknown` with a distinct reason; an indeterminate liveness probe downgrades nothing; a missing record is no observation rather than `unknown`; no absence derives a definite state. A writer that loses sight of its harness stops heartbeating instead of refreshing a state it cannot see, and a predecessor session's record is never re-stamped. A reaped provider yields a terminal `ended` record carrying its real exit — written before the stop path's SIGKILL escalation and rewritten from the escalation cover when a grace-window reap observes the real status, proven against the real wrapper binaries of both stop implementations — and never a live state. | `src/harness_state.rs::unknown_state_is_derived_and_cannot_be_written`; `src/harness_state.rs::malformed_record_is_unknown_without_mtime_fallback`; `src/harness_state.rs::staleness_and_future_skew_derive_unknown_with_distinct_reasons`; `src/harness_state.rs::a_dead_session_reads_unknown_even_while_fresh_but_ended_survives`; `src/harness_state.rs::every_landed_write_is_byte_distinct_and_fresh_restatements_do_not_write`; `src/harness_state.rs::a_chatty_producer_restating_its_state_causes_zero_writes`; `src/harness_state.rs::concurrent_writers_defer_to_the_on_disk_record_not_their_cache`; `src/harness_state.rs::a_predecessor_sessions_record_is_never_heartbeat_eligible`; `src/harness_state.rs::missing_record_reads_as_none_not_unknown`; `src/codex_app_server.rs::pump_publishes_observations_and_stops_heartbeating_on_evidence_loss`; `src/claude_session.rs::a_provider_killed_mid_turn_reads_ended_rather_than_active`; `src/claude_session.rs::a_clean_provider_exit_writes_the_terminal_record`; `tests/harness_state_teardown.rs::stop_escalation_writes_the_terminal_record_before_sigkill`; `tests/harness_state_teardown.rs::opencode_stop_escalation_writes_the_cover_record_before_sigkill`; `tests/harness_state_teardown.rs::opencode_graceful_stop_records_the_real_reaped_exit` |
| **Retirement health** | A retired declaration is healthy only after every declared task ID is absent. Any live or dead declared task record reports incomplete retirement; retired declarations do not require presence. Live declarations retain their existing task and presence checks. | `tests/doctor.rs::retired_declaration_is_healthy_when_tasks_and_presence_are_absent`; `tests/doctor.rs::retired_declaration_is_unhealthy_while_a_declared_task_is_alive`; `tests/doctor.rs::retired_declaration_is_unhealthy_while_a_dead_task_record_remains` |
| **Suspension health** | A suspended declaration is healthy when no declared task is live and every retained dead record is explicitly keep-pinned. It requires no presence, but this weaker result never proves retirement. Resume preserves ordinary keep and adopt-only policy. | `tests/doctor.rs::suspended_declaration_is_healthy_when_tasks_are_absent_without_presence`; `tests/doctor.rs::suspended_declaration_distinguishes_live_dead_keep_and_dead_nonkeep`; `tests/reconcile.rs::resuming_uses_ordinary_reconcile_and_does_not_override_keep` |
| **Crash loops surface** | A task parked by a fail-mode restart policy notifies its supervisor once over the bus. | `tests/run.rs::surface_crash_loop_notifies_the_supervisor_over_the_bus` |
Expand Down

This file was deleted.

18 changes: 11 additions & 7 deletions docs/vrs/05-harness-state/open-questions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ hypotheses.
waits on) and it under-reports nothing, but the state axis says `active`
while the model is not running. Resolves by: a Claude build whose denial
emits any hook event; until then the deny window is the pinned limit.
- **DQ-H2 Transport cost of per-transition writes.** Presence refreshes every
five minutes; turn boundaries are far more frequent, and burst coalescing
measured 4 transitions per turn 0.1–0.4 ms apart. No measurement establishes
what per-transition replicated writes cost on a real catalog under a real
transport (OHS-T01 accepts this for v1). Resolves by: measuring write and
sync volume on a live catalog; if unacceptable, a minimum-interval
coalescing window is the tuning knob, at the cost of spinner latency.
- **DQ-H2 Transport cost of per-transition writes — measured 2026-08-23,
resolved by the restatement guard.** The live smoke run caught the failure
mode: the OpenCode producer restated its state per SSE frame and the
envelope re-stamped every restatement — 679 byte-distinct writes in 221 s
(~2.7/s while idle). The envelope now makes an unchanged observation a
no-op until the refresh cadence is due, so a seat writes on transitions
plus at most one re-stamp per five minutes (a Claude turn measured 3
writes; a pi turn 2–3). What remains open is only the fleet-scale sync
question: nothing yet measures what transition-rate writes cost a
600-seat catalog's transport over a day. Resolves by: that measurement on
a live catalog.
- **DQ-H3 `child` has no producer.** The word is reserved because the tuple's
reasoning needs it (a long-running foreground command is neither the model
working nor idle), but the producer that would have supplied it — the PTY
Expand Down
Loading
Loading