Skip to content

Tell the leader how to read a finished run's report - #160

Merged
nikolasd merged 2 commits into
mainfrom
succeeded-digest-fetch-hint
Sep 10, 2026
Merged

nikolasd merged 2 commits into
mainfrom
succeeded-digest-fetch-hint

Conversation

@nikolasd

@nikolasd nikolasd commented Sep 10, 2026 •

Copy link
Copy Markdown
Owner

A terminal digest is a notification: it says the run ended, not what it produced. Nothing pushes the report — run/result is the only way to read it, and the leader has to ask. So Run <id> … succeeded. left a leader knowing a run finished and holding none of its output, with no indication anything remained to fetch.

Why the gap survived

The settled-turn digest has always told the leader exactly what to do — "Read the answer via crew_run { op: "result", runId }, then …" — and had a test pinning that instruction. The terminal digests carried neither the instruction nor any test asserting their content. The harder case was handled and tested; the simpler one was neither.

Scoped from the code, not assumed

run_result gates on is_terminal (orchestration.rs:1228), so it accepts failed, cancelled and lost as well as succeeded, and returns whatever visible text the journal accumulated — run_result_events_op's final_text.or(chunk_text) (query.rs:886), both Option<String>, both starting None.

For a run that died that is usually partial output and often the most useful diagnostic available — but it genuinely can be null, when the worker produced nothing before it went. So:

  • succeeded states plainly where the report is
  • failed / cancelled / lost offer partial output and say there may be none

A digest that promised output and returned nothing would be worse than one that never mentioned it.

The bridge's one silent branch

sendMessage is looked up through a cast so a renamed or removed omp method "degrades to no digest". The guard was then false forever and every milestone for the session was dropped with nothing logged — the surrounding catch only covers digests that throw. An omp version bump could switch the leader's notifications off entirely and look exactly like a quiet run.

It now warns once per session, lazily. The reason for lazy is where the reader will be, not noise: the warning exists to explain an absence to someone who has noticed a milestone did not arrive, and that person is reading the log around the moment it should have fired. Attach can be hours earlier, when nobody is troubleshooting anything.

Replay suppression is untouched.

Tests

Three, each confirmed to fail against the opposite behaviour before being trusted — the succeeded hint removed, the partial-output pointer removed, and the once-per-session guard forced to fire every time. The reviewer independently re-ran that control against a reverted milestones.ts rather than taking the claim.

Gate

bun test 433/433 · typecheck · format:check · generate --check up to date · check:markers 432/0 · cargo fmt --all --check.

cargo test --workspace was not run: no Rust changed. Stating that rather than implying a gate that did not cover the diff.

Bundle refresh

bundle-check failed on the first run and auto-commit-dist pushed the rebuilt linux-x64 bundle, exactly as designed. Worth recording that this PR body originally said "no protocol change, so no bundle refresh expected" — which applied the rule too narrowly. The trigger is anything the bundle embeds, and milestones.ts is extension source that gets bundled; a protocol change is only the least obvious way to hit it. The rule as written in CLAUDE.md is right; the person applying it here narrowed it back to the case that had bitten him before.

nikolasd and others added 2 commits September 10, 2026 13:04
A terminal digest is a notification: it says the run ended, not what it
produced. Nothing pushes the report -- `run/result` is the only way to
read it, and the leader has to ask. So `Run <id> ... succeeded.` left a
leader knowing a run finished and holding none of its output, with no
indication that anything remained to fetch.

The settled-turn digest has always told the leader exactly what to do
("Read the answer via crew_run { op: "result", runId }, then ..."), and
had a test pinning that instruction. The terminal digests carried neither
the instruction nor a test asserting their content, which is how the gap
survived: the harder case was handled and the simpler one was not.

Scoped from the code rather than assumed. `run/result` gates on
`is_terminal`, so it accepts failed, cancelled and lost as well as
succeeded, and returns whatever visible text the journal accumulated
(`run_result_events_op`'s `final_text.or(chunk_text)`). For a run that
died that is usually partial output and often the most useful diagnostic
available -- but it can be null, when the worker produced nothing. So the
succeeded digest states plainly where the report is, and the others offer
partial output while saying there may be none. A digest that promised
output and returned nothing would be worse than one that never mentioned
it.

Also closes the bridge's one silent branch. `sendMessage` is looked up
through a cast so a renamed or removed omp method degrades to "no
digest"; the guard was then false forever and every milestone for the
session was dropped with nothing logged, because the surrounding catch
only covers digests that throw. An omp version bump could switch the
leader's notifications off entirely and look exactly like a quiet run. It
now warns once per session, lazily -- a session that never reaches a
milestone stays silent, and a long run cannot flood the log with one
unchanging fact.

Replay suppression is untouched.

Each test was confirmed to fail against the opposite behaviour before
being trusted: the succeeded hint removed, the partial-output pointer
removed, and the once-per-session guard forced to fire every time.
@nikolasd
nikolasd merged commit d9adecf into main Sep 10, 2026
19 checks passed
@nikolasd
nikolasd deleted the succeeded-digest-fetch-hint branch September 10, 2026 10:45
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.

1 participant