Skip to content

Specify replicated presence freshness - #159

Closed
myobie wants to merge 7 commits into
mainfrom
agent/presence-record-vrs
Closed

Specify replicated presence freshness#159
myobie wants to merge 7 commits into
mainfrom
agent/presence-record-vrs

Conversation

@myobie

@myobie myobie commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

What is broken

A live agent refreshes presence by rewriting its status file. Today that rewrite repeats the same state bytes, so a content-replicated catalog gets no new version and the replica keeps an old timestamp. Once that timestamp becomes stale, every remote reader reports the still-live agent as unknown.

Why freshness is in the bytes

No supported catalog transport is required to preserve file times. Git records no file mtime at all, and the catalog must remain syncable with Git over SSH without requiring Fabric. The heartbeat therefore has to be versioned content.

Record and compatibility

A current record is:

available
v1 1785802653486

Line one remains the state, so an old reader still parses it. Line two is the version and the writer's Unix-millisecond heartbeat. A refresh changes those bytes even when the state does not change.

A valid legacy one-line record temporarily uses its file mtime. A live non-DND record upgrades with the current heartbeat; legacy DND upgrades once with its existing mtime so migration cannot renew the hold. A malformed versioned record reads as unknown and never falls back to file time: a broken writer must not look healthy.

The reader allows at most sixty seconds of future clock skew. A timestamp farther in the future reads as unknown. This closes the defect where a sufficiently future dnd timestamp suppresses delivery until the clocks catch up.

Out of scope

Only the status contribution to lastActivity moves to the embedded heartbeat. Inbox and archive contributions still use their file times. The context freshness check also still uses file time. Both are unchanged and out of scope.

@myobie
myobie marked this pull request as ready for review August 10, 2026 15:18
@myobie

myobie commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #254, which already contains this specification in a more current form.

hetz.st2 conformed #254 to this PR on 15 August, so the version 1 record, the writer and atomicity rules, the clock and skew rules, and the malformed-record handling are all in #254 today. Verified against the implementation there: STATUS_REFRESH is 5 minutes, STATUS_STALE 15 minutes, STATUS_FUTURE_SKEW 60 seconds, every writer emits the v1 prefix, and a malformed versioned record reads unknown with no mtime fallback, with a test asserting exactly that.

Every line unique to this branch describes the pre-driver architecture. It says the DING sidecar refreshes presence, upgrades legacy records, and handles the DND migration. Under the harness drivers the session owner does all of that, which is the wording #254 carries. Merging this branch into #254 would reintroduce text that is now wrong.

Closing rather than merging, so the specification and the running fleet do not disagree.

@myobie myobie closed this Aug 16, 2026
myobie added a commit that referenced this pull request Aug 16, 2026
Replaces the DING sidecar with provider-native delivery, declared as typed driver blocks.

An agent now says what harness it is — `codex {}` or `claude {}` — and st2 expands that into the argv and render a person could have written by hand. The expansion is printable with `st2 driver expand`, and the same function backs reconcile, materialize, doctor, validate and inventory, so what you read is what runs.

Presence moves to a version 1 record carrying an embedded timestamp, so a heartbeat survives transports that do not preserve file metadata. That fixes cross-host presence, where a same-content rewrite gave a content-addressed sync nothing to carry and live agents read as dead on other machines. The session owner writes presence, so it stops the instant the session does.

Verified beyond CI: this code has run the whole fleet for 36 hours — eleven agents across three machines, continuous delivery. On macOS the suite goes from 761 passing to 775, fixing three Darwin tests and breaking none. The message_cli gate added by #253 passes 17/17 on Linux and macOS.

The spec ships with the implementation. #159 is closed as superseded: its content is here in a more current form, and its unique text described the pre-driver architecture where a DING sidecar owned presence.

`st2 driver claude-mcp` is the MCP server; `claude-session` runs Claude under the presence wrapper. The bare `claude` name remains as a hidden, deprecated alias that warns at runtime, so pre-rename `.mcp.json` files keep resolving through the migration.

Authored by hetz.st2. Merged by Silber.cos on Nathan's instruction.
@schickling schickling added area:presence Presence, status records, and heartbeats · Set: manual type:bug Something broken or a regression · Set: manual labels Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:presence Presence, status records, and heartbeats · Set: manual type:bug Something broken or a regression · Set: manual

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants