feat(guard): consult continuity across long sessions (#296) - #298
Open
CryptoJones wants to merge 2 commits into
Open
feat(guard): consult continuity across long sessions (#296)#298CryptoJones wants to merge 2 commits into
CryptoJones wants to merge 2 commits into
Conversation
…#294) mcp 2.1.x hands the client a bare "Error executing tool <name>" for any exception that is not a deliberate ToolError. omind's tools let their domain failures escape as NoteError / NoteConflictError / ValueError, so every anticipated message — a missing note, an unsafe name, a bad graph argument, and the stale-version conflict that tells an agent to re-read before writing — vanished, and five test_server assertions went red on every PR. Re-raise those failures as ToolError at the tool boundary (every registration routes through one wrapper); a real crash stays masked as the SDK intends. Move uv.lock to mcp 2.1.1 so a local run sees what CI sees. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011T5zwA8x2zqEghR2aq86hc
Measured on a live box since the hooks went in (2026-08-24): 362 of 769 turns started with the consult gate auto-cleared and nothing injected, and the longest turn ran 152 tool calls. The preflight ranked notes against the user prompt alone, and a long session's prompts are continuations — "retry" (Claude Code's own API auto-retry), "Yes please", "go ahead", a task notification — so it missed, auto-cleared, and the turn ran with no memory contact. Compaction re-priming was already fine. Two controls, both in the harness-agnostic core so every adapter inherits them: * Continuation-aware preflight. A prompt with fewer than three meaningful terms (or a harness-injected <wrapper>) is retrieved against the prior turn's task plus the agent's recent activity; the gate auto-clears only when that composite also misses. An identical continuation prompt inside a two-minute window carries the turn's gate state instead of resetting. * Per-turn action budget. Every allowed non-consult action counts in the sentinel (with an activity trail); at OMIND_GATE_ACTION_BUDGET (25) the core retrieves against the turn's task and trail and, if a relevant note this session has not seen exists, surfaces it: injected after the tool call where the harness can (Claude PostToolUse additionalContext, via `omind hook PostToolUse --harness claude`), otherwise demanded as a gate re-arm at the next PreToolUse. No candidate resets the budget and logs. OMIND_GATE_MAX_REARM (4) caps re-gating per turn. Also: `doctor` gate_continuity check (7-day auto-clear rate, warn at 40%); compliance rollup; the omi-gate* family is a ceremony (not a blocking deny), excluded from the corpus and from the OpenCode plugin's enforced denies. Backlog mirrors #296 (shipped) and #297 (Windows CI, pre-existing). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011T5zwA8x2zqEghR2aq86hc
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| return False | ||
| if text.startswith("<"): | ||
| return True | ||
| from omind import retrieve |
| parts = [_trail_words(item) for item in action_trail(session)] | ||
| if omi_dir is not None: | ||
| try: | ||
| from omind import verify |
| from omind import verify | ||
|
|
||
| parts.append(verify.recent_activity(session, omi_dir)) | ||
| except Exception: |
| """``(filename, title)`` of the best note relevant to the work in progress | ||
| that this session has not seen yet, or ``None``. ``query`` defaults to the | ||
| turn's task plus the activity signal. Deterministic; no model call.""" | ||
| from omind import recall, retrieve |
| if budget and actions >= budget and rearm_count(session) < _max_rearm(): | ||
| found = midturn_candidate(session, omi_dir) | ||
| if found is not None: | ||
| from omind import recall |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #296. Depends on #295 (the #294 CI fix) — this branch is stacked on it; once #295 merges, only the one feature commit remains.
The problem, measured
Every Claude Code transcript on hermes since the hooks went live (2026-08-24):
Longest single turn: 152 tool calls. 91 auto-clears on 2026-09-03 alone. Compaction is not the cause (
SessionStart(source=compact)re-primes; verified in transcripts).Two mechanisms: the preflight ranked notes against the prompt alone, and a long session's prompts are continuations —
retry(Claude Code's own API auto-retry),Yes please,go ahead,Delete it,<task-notification>— so it missed, auto-cleared, and the turn ran with no memory contact; and the gate had no budget inside a turn, so one autonomous turn could run for hours on whatever was consulted at its start.The fix — in the harness-agnostic core, so every adapter inherits it
1. Continuation-aware preflight (
guard.preflight_turn, reached by ClaudeUserPromptSubmit, Hermespre_llm_call, DSHagent/pre-step). A prompt with fewer than three meaningful terms, or a harness-injected<wrapper>, is retrieved against the prior turn's task + the agent's recent activity; the gate auto-clears only when that composite also misses. An identical continuation prompt inside a two-minute window is the harness's API auto-retry and carries the turn's gate state instead of resetting it. A substantive prompt re-sent verbatim still gets a normal preflight.2. Per-turn action budget (
guard.check_action— every harness's PreToolUse path). Each allowed non-consult action counts in the sentinel, with an activity trail. AtOMIND_GATE_ACTION_BUDGET(default 25) the core retrieves against the turn's task and trail; if a relevant note this session has not seen exists it is surfaced:PostToolUseadditionalContext, viaomind hook PostToolUse --harness claude(written byomind setup; declared per harness inhooks.INJECTING_HARNESSESso a non-injecting harness never records a phantom consult);recall-noteclears it, and the verifier treats the demanded read as obedience.No candidate → the budget resets and the auto-clear is logged.
OMIND_GATE_MAX_REARM(default 4) caps re-gating per turn, so a turn can never be re-gated indefinitely. Inert commands, consults, and a paused gate don't count.3. Visibility.
omind doctorgainsgate_continuity(7-day auto-clear rate; warns at 40%). Every decision is compliance-logged (omi-gate-preflight,omi-gate-carry,omi-gate-rearm,omi-gate-rearm-no-match). Theomi-gate*family counts as a ceremony, not a blocking deny; it is excluded from the fine-tune corpus and from the OpenCode plugin's enforced denies (OpenCode's consult signals aren't live-verified).Per harness: Claude gets both controls plus the injection; Hermes, DSH, Codex, Gemini get both controls with the re-arm demand; OpenCode gets the preflight (it never enforced the gate). Codex has no
UserPromptSubmitmount today, so its gate stays per-session — a separate follow-up.Verified
ruff check .clean,mypy srcstrict clean.Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/
🤖 Generated with Claude Code
https://claude.ai/code/session_011T5zwA8x2zqEghR2aq86hc