feat(overseer): deterministic per-turn fallback when no summary line (Half B, piece 2) - #87
Conversation
Rule compliance can never be 100% (an LLM may drop the trailing AGENT_NOTIFY_SUMMARY line), so add a deterministic hub backstop: when an agent turn produces visible assistant text but no parseable summary, synthesize a minimal progress event from the first non-empty line. No LLM. - attentionCandidate 0: lands in the Session Log only, never the attention inbox. - provenance marks it hub-synthesized so it is never counted as a real self-report; idempotent per messageId. - ordering: notify > validation_error > tool_failure > this fallback. Guarantees the overseer never has a fully blind agent turn even when the Cursor rule overlay's line is dropped. Fork-only overseer work. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: db755c7237
ℹ️ 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".
| if (!primary && plainText) { | ||
| primary = this.synthesizeTurnFallback(session, messageId, plainText, ts) |
There was a problem hiding this comment.
Defer fallback until the turn is complete
When an ACP-backed agent emits text before a tool call, AcpMessageHandler flushes that text as its own message before the final turn text (cli/src/agent/backends/acp/AcpMessageHandler.ts:613-620). This branch records a synthetic progress event immediately for that pre-tool segment, so even a compliant turn that later ends with a real AGENT_NOTIFY_SUMMARY still gets extra hub-synthesized progress rows in the session log/overseer memory. Please defer the fallback until a turn boundary or otherwise suppress it for non-final text segments.
Useful? React with 👍 / 👎.
| if (!primary && plainText) { | ||
| primary = this.synthesizeTurnFallback(session, messageId, plainText, ts) |
There was a problem hiding this comment.
Keep corrupted malformed summaries out of fallback
When Cursor applies the known AGENT_NOTIFY_SUMMARY → AGENT_NOTIFY_SUMARY corruption but the JSON is malformed, extractNotifySummary() returns null and the existing malformed-line detector does not match the corrupted token, so this fallback records the turn as ordinary progress instead of preserving the validation error precedence described for malformed summary lines. Please guard the fallback against any last-line notify token variant that collapse-matches the canonical token but fails to parse.
Useful? React with 👍 / 👎.
…nvisible Half-B overseer emission (fork-only, PR #86/#87, do NOT upstream), stacked: emit (CLI, 9ef0242) then fallback (hub-only, db755c7), inserted after feat/overseer-contract-invisible per peer request. Not yet activated — blocked behind the v0.23.4 upstream bump sweep (fork main 23 behind); layers will land on the fresh base during that rematerialize + hub restart. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Tracking: remaining flavor coverage (kimi / pi / generic ACP) is fork issue #89 — needed for next overseer phase full-fleet primary emission. Design doc: |
Summary
Stacked on #86. The Cursor rule overlay boosts
AGENT_NOTIFY_SUMMARYemission, but no rule can force an LLM to 100% always end with the line. This adds a deterministic hub backstop so the overseer never has a fully blind agent turn.When an agent turn produces visible assistant text but
extractNotifySummary()returns null (and it is not an empty-sentinel / malformed-line / tool-failure case),overseerEventRecordersynthesizes a minimalprogressevent from the first non-empty line of the text.attentionCandidate: 0- Session Log only, never the attention inbox.provenance: hub-synthesized ...- never mistaken for a real self-report; idempotent permessageId.Fork-only overseer work - never upstream.
Test plan
bun typecheckclean (cli/web/hub)overseerEventRecorder.fallback.test.ts- 6 tests (synth/skip-on-real-summary/skip-on-malformed/idempotent/cap/ignore-user)hapi-restart-hubto activateMade with Cursor