Skip to content

feat(guard): consult continuity across long sessions (#296) - #298

Open
CryptoJones wants to merge 2 commits into
mainfrom
feat/gate-continuity
Open

feat(guard): consult continuity across long sessions (#296)#298
CryptoJones wants to merge 2 commits into
mainfrom
feat/gate-continuity

Conversation

@CryptoJones

Copy link
Copy Markdown
Owner

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):

Preflight outcome at prompt submit Turns Tool calls in those turns Agent consults
Memory injected 407 2975 165
Gate auto-cleared, nothing injected 362 2037 97

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 Claude UserPromptSubmit, Hermes pre_llm_call, DSH agent/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. At OMIND_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:

  • injected after the tool call where the harness can inject context — Claude Code PostToolUse additionalContext, via omind hook PostToolUse --harness claude (written by omind setup; declared per harness in hooks.INJECTING_HARNESSES so a non-injecting harness never records a phantom consult);
  • otherwise demanded as a gate re-arm at the next PreToolUse, with the governing excerpt embedded (feat(guard): place governing rule text adjacent to the action it governs #241 style) — one recall-note clears 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 doctor gains gate_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). The omi-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 UserPromptSubmit mount today, so its gate stays per-session — a separate follow-up.

Verified

Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/

🤖 Generated with Claude Code

https://claude.ai/code/session_011T5zwA8x2zqEghR2aq86hc

CryptoJones and others added 2 commits September 3, 2026 15:07
…#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
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: f4b70cce-c6dc-49dd-a92e-b436eb305203


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/omind/guard.py
return False
if text.startswith("<"):
return True
from omind import retrieve
Comment thread src/omind/guard.py
parts = [_trail_words(item) for item in action_trail(session)]
if omi_dir is not None:
try:
from omind import verify
Comment thread src/omind/guard.py
from omind import verify

parts.append(verify.recent_activity(session, omi_dir))
except Exception:
Comment thread src/omind/guard.py
"""``(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
Comment thread src/omind/guard.py
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
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.

guard: long sessions run dozens of tool calls with no memory contact — the per-turn gate keys off continuation prompts

2 participants