Skip to content

fix(guard): auto-clear the per-turn gate on a genuine preflight miss (v5.0.1) - #184

Merged
CryptoJones merged 2 commits into
mainfrom
fix/omi-gate-miss-autoclear
Jul 28, 2026
Merged

fix(guard): auto-clear the per-turn gate on a genuine preflight miss (v5.0.1)#184
CryptoJones merged 2 commits into
mainfrom
fix/omi-gate-miss-autoclear

Conversation

@CryptoJones

Copy link
Copy Markdown
Owner

Summary

  • When the vault was searched and nothing scored as relevant to the turn's task, the gate used to stay armed and force a manual search-vault + recall-note round trip on a note that, by construction, isn't relevant — the "read any note to dodge the gate" failure the relevance mapping was built to prevent, one layer up. Auto-clear instead (always logged to the compliance log, never silent); OMI_GATE_MISS_STRICT=1 restores the old forced-consult behavior. An empty/uncaptured task still leaves the gate armed since that's "we never looked," not "we looked and found nothing."
  • Bumps pyproject.toml / __init__.py to 5.0.1.

Test plan

  • ./scripts/test.sh -q — 840 passed, 1 skipped
  • ruff check clean
  • mypy src/omind/guard.py clean
  • New tests: default auto-clear + compliance log entry, OMI_GATE_MISS_STRICT=1 opt-out, empty-task stays strict

🤖 Generated with Claude Code

CryptoJones and others added 2 commits July 28, 2026 02:34
When the vault was searched and nothing scored as relevant to the turn's
task, the gate used to stay armed and force a manual search-vault +
recall-note round trip on a note that, by construction, isn't relevant —
the "read any note to dodge the gate" failure retrieval was built to
prevent, one layer up. Auto-clear instead (always logged, never silent);
OMI_GATE_MISS_STRICT=1 restores the old behavior.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Co-Authored-By: Claude Sonnet 5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • The consult gate now automatically clears for a turn when a non-empty task has no relevant memory matches.
    • Automatic clears are recorded in the compliance log.
    • Set OMI_GATE_MISS_STRICT=1 to retain the previous forced-consult behavior.
  • Bug Fixes

    • Empty tasks continue to require consultation, preventing unverified turns.
  • Documentation

    • Updated the README and changelog to describe the revised gate behavior.
  • Chores

    • Updated the application version to 5.0.1.

Walkthrough

The OMI consult gate now auto-clears after a non-empty-task relevance miss, logs the decision, and preserves strict behavior through OMI_GATE_MISS_STRICT=1. Tests, documentation, and package version metadata were updated.

Changes

Consult gate behavior

Layer / File(s) Summary
Preflight miss handling and validation
src/omind/guard.py, tests/test_guard.py
Non-empty-task misses auto-clear and emit a compliance event; strict mode and empty tasks keep the gate armed, with tests covering both paths.
Behavior documentation
README.md, CHANGELOG.md
Documents auto-clear logging, empty-task behavior, and the strict-mode environment variable.
Release version update
pyproject.toml, src/omind/__init__.py
Updates the project and package versions from 5.0.0 to 5.0.1.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant preflight_turn
  participant retrieve.relevant_titles
  participant compliance
  Agent->>preflight_turn: submit task
  preflight_turn->>retrieve.relevant_titles: search for relevant titles
  retrieve.relevant_titles-->>preflight_turn: no relevant matches
  preflight_turn->>compliance: log auto-clear decision
  preflight_turn-->>Agent: proceed without forced read
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main guard behavior change and version bump.
Description check ✅ Passed The description matches the diff and objectives, covering auto-clear behavior, strict mode, empty-task handling, and the version bump.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/omi-gate-miss-autoclear

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Line 10: Insert a blank line immediately after the “### Changed” heading in
CHANGELOG.md, before the following list content, to satisfy Markdownlint MD022.

In `@src/omind/guard.py`:
- Around line 1297-1299: Update preflight_turn to normalize the captured task by
trimming whitespace before the empty-task miss check, so whitespace-only tasks
remain strict and cannot trigger auto-clear. Add a regression test covering a
task containing only whitespace.
- Line 1322: Update the compliance log detail construction near the task guard
to stop persisting any raw task content; replace task[:120] with a fixed reason
and only non-sensitive metadata such as task length or a one-way hash,
preserving the existing compliance logging flow.
- Around line 60-68: Revise the auto-clear audit wording to describe compliance
logging as best-effort because compliance.log_event may discard records on
OSError: update the explanation in src/omind/guard.py lines 60-68, document in
README.md lines 274-281 that logging is attempted but may be unavailable, and
apply the same best-effort wording to the release note in CHANGELOG.md lines
18-20.
- Around line 1313-1314: Update the condition in the task retrieval flow around
record_consult so it auto-clears only when titles is explicitly an empty result
([]), not when retrieval returns None. Preserve the older armed-gate fallback
for None retrieval failures, and add a regression test covering that failure
path.

In `@tests/test_guard.py`:
- Around line 507-515: Add the pytest monkeypatch fixture to
test_turn_preflight_without_match_auto_clears_gate and delete
guard.MISS_STRICT_ENV from the environment with raising=False before invoking
guard.preflight_turn, ensuring the test always exercises default mode.
- Around line 518-520: Update the assertions in the test around
compliance.read_events() to select the event belonging to the current session
and guard.GATE_NO_MATCH_RULE before checking its outcome. Replace the
order-dependent events[-1] access with session- and rule-filtered event
selection, then assert that the selected event has outcome "auto-clear".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e7e918ea-a650-4eb8-a8de-4ea636d469a3

📥 Commits

Reviewing files that changed from the base of the PR and between a91d3ea and 3543e45.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • CHANGELOG.md
  • README.md
  • pyproject.toml
  • src/omind/__init__.py
  • src/omind/guard.py
  • tests/test_guard.py
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: test (windows-latest, 3.10)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Retrieval must fail open: every search layer returns None on errors and callers fall back to the older path. Missing models, corrupt indexes, locked databases, and missing FTS5 support must degrade search rather than break it; failure branches must be tested.
All note writes must go through OmiStore; external writers use notes.upsert_note. Writes must retain flocking, atomic rename, Lamport Rev: stamping, and soft deletion. Deletes archive notes with Disabled: true; only omind mesh purge permanently removes them.
OmiStore.safe_name must guard every note read and write so path traversal remains impossible.
Credential notes must be de-prioritized in search and gate suggestions unless the query concerns credentials, using _CREDENTIAL_PENALTY; the gate must never steer agents toward secrets notes.
MCP tools must never return unbounded output. Every list-shaped tool must paginate through server._page and expose limit, offset, total, and has_more.
index.md and Memory Template.md are scaffolding, not memories; reading either must not clear the consult gate. Preserve paths.NON_CONSULT_FILENAMES.
Recency is a ranking leg only: it may re-rank notes already matched by content legs but must never add unmatched notes to search results.
link_targets() must preserve wikilink case for dangling-link reports; only link resolution lowercases it.
Do not mutate a NoteSummary returned from _cached_summary; it is shared cached state. Use dataclasses.replace, as in store._indexed_search.
Treat embed.encode results as potentially plain lists in tests; coerce vectors through searchindex._query_vector rather than assuming a .shape attribute.
Do not reintroduce document-frequency query-term filtering; use threshold-free graded matching, where all-words matches rank above any-words matches.

Files:

  • src/omind/__init__.py
  • src/omind/guard.py
  • tests/test_guard.py
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Markdown note files are the source of truth; indexes, caches, and vectors are derived and disposable and must live only in paths.state_dir(), never in the vault.

Files:

  • CHANGELOG.md
  • README.md
README.md

📄 CodeRabbit inference engine (AGENTS.md)

The README must use the centered banner version of the project footer rather than the standard documentation footer.

Files:

  • README.md
🪛 markdownlint-cli2 (0.23.0)
CHANGELOG.md

[warning] 10-10: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🔇 Additional comments (5)
pyproject.toml (1)

3-3: LGTM!

src/omind/__init__.py (1)

5-5: LGTM!

src/omind/guard.py (1)

69-71: LGTM!

Also applies to: 1283-1284

tests/test_guard.py (1)

18-18: LGTM!

Also applies to: 523-544

CHANGELOG.md (1)

11-17: LGTM!

Also applies to: 21-24

Comment thread CHANGELOG.md

## [Unreleased]

### Changed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a blank line after the heading.

Markdownlint MD022 flags ### Changed because the list begins immediately below it.

Proposed fix
 ### Changed
+
 - **A genuine preflight miss auto-clears
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Changed
### Changed
🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 10-10: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` at line 10, Insert a blank line immediately after the “###
Changed” heading in CHANGELOG.md, before the following list content, to satisfy
Markdownlint MD022.

Source: Linters/SAST tools

Comment thread src/omind/guard.py
Comment on lines +60 to +68
#: Opt back into the old strict behavior: a preflight MISS (the vault was
#: searched and nothing scored as relevant to the turn's task) forces a manual
#: search-vault/recall-note round trip instead of auto-clearing the gate. Default
#: off, mirroring OMI_VERIFY_REQUIRE's "cheap default, opt-in strictness" shape —
#: forcing a consult of a note that is, by construction, not relevant to the task
#: is the exact "read any note to dodge the gate" failure retrieval was built to
#: prevent; it just reappears one layer up when the vault genuinely has nothing
#: on-topic. The auto-clear is always logged to the compliance log (never silent)
#: so a session that's mostly missing is visible in `omind guard log`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Align the audit guarantee with the best-effort logger.

compliance.log_event can silently discard the record on OSError, so these descriptions overstate auditability. Qualify the behavior as best-effort or make logging failure observable before claiming the auto-clear is never silent.

  • src/omind/guard.py#L60-L68: change “always logged”/“never silent” to best-effort wording.
  • README.md#L274-L281: document that logging is attempted and may be unavailable.
  • CHANGELOG.md#L18-L20: use the same best-effort wording in the release note.
📍 Affects 3 files
  • src/omind/guard.py#L60-L68 (this comment)
  • README.md#L274-L281
  • CHANGELOG.md#L18-L20
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/omind/guard.py` around lines 60 - 68, Revise the auto-clear audit wording
to describe compliance logging as best-effort because compliance.log_event may
discard records on OSError: update the explanation in src/omind/guard.py lines
60-68, document in README.md lines 274-281 that logging is attempted but may be
unavailable, and apply the same best-effort wording to the release note in
CHANGELOG.md lines 18-20.

Comment thread src/omind/guard.py
Comment on lines +1297 to +1299
An EMPTY task (nothing captured to search with) is not a miss — it means we
never ran the search at all, so it stays strict; we can't judge "nothing is
relevant" without having looked.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Treat whitespace-only tasks as empty.

preflight_turn uses the raw captured string, so a task such as " " is treated as non-empty and can trigger auto-clear despite this documentation promising that empty tasks remain strict. Normalize the task before the miss check and add a whitespace-only regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/omind/guard.py` around lines 1297 - 1299, Update preflight_turn to
normalize the captured task by trimming whitespace before the empty-task miss
check, so whitespace-only tasks remain strict and cannot trigger auto-clear. Add
a regression test covering a task containing only whitespace.

Comment thread src/omind/guard.py
Comment on lines +1313 to +1314
if task and not titles and not _miss_strict():
record_consult(session, kind="no-match", target="", relevant=False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Distinguish retrieval errors from a successful empty result.

not titles conflates [] (a clean relevance miss) with None (a retrieval failure under the repository contract). Missing models, corrupt indexes, locked databases, or unavailable FTS5 could therefore be logged as no-match and auto-clear the gate instead of following the older armed-gate fallback. Only auto-clear on an explicit empty result and add a regression test for the error path.

Proposed condition
-        if task and not titles and not _miss_strict():
+        if task and titles == [] and not _miss_strict():

As per coding guidelines, retrieval errors must return None, callers must fall back to the older path, and failure branches must be tested.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if task and not titles and not _miss_strict():
record_consult(session, kind="no-match", target="", relevant=False)
if task and titles == [] and not _miss_strict():
record_consult(session, kind="no-match", target="", relevant=False)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/omind/guard.py` around lines 1313 - 1314, Update the condition in the
task retrieval flow around record_consult so it auto-clears only when titles is
explicitly an empty result ([]), not when retrieval returns None. Preserve the
older armed-gate fallback for None retrieval failures, and add a regression test
covering that failure path.

Source: Coding guidelines

Comment thread src/omind/guard.py
rule_id=GATE_NO_MATCH_RULE,
severity="soft",
outcome="auto-clear",
detail=f"task={task[:120]!r}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not persist the raw task in the compliance log.

detail is written to compliance.jsonl; truncating to 120 characters does not prevent prompts containing credentials, tokens, emails, or other user data from being retained. Log a fixed reason plus non-sensitive metadata such as length or a hash instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/omind/guard.py` at line 1322, Update the compliance log detail
construction near the task guard to stop persisting any raw task content;
replace task[:120] with a fixed reason and only non-sensitive metadata such as
task length or a one-way hash, preserving the existing compliance logging flow.

Comment thread tests/test_guard.py
Comment on lines +507 to +515
def test_turn_preflight_without_match_auto_clears_gate(tmp_path: Path) -> None:
omi = tmp_path / "OMI"
omi.mkdir()
context = guard.preflight_turn(
{"session_id": "preflight-none", "prompt": "unmatched subject"},
omi,
)
assert "found nothing relevant" in context
assert guard.MISS_STRICT_ENV in context

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Isolate the default-mode test from the ambient environment.

This test assumes OMI_GATE_MISS_STRICT is unset, but does not remove it. A developer or CI process with that variable exported will make the default auto-clear test fail. Add monkeypatch.delenv(guard.MISS_STRICT_ENV, raising=False).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_guard.py` around lines 507 - 515, Add the pytest monkeypatch
fixture to test_turn_preflight_without_match_auto_clears_gate and delete
guard.MISS_STRICT_ENV from the environment with raising=False before invoking
guard.preflight_turn, ensuring the test always exercises default mode.

Comment thread tests/test_guard.py
Comment on lines +518 to +520
events = compliance.read_events()
assert events[-1]["rule_id"] == guard.GATE_NO_MATCH_RULE
assert events[-1]["outcome"] == "auto-clear"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Assert against this session’s event, not the global last event.

compliance.read_events() reads the shared JSONL log, so events[-1] is order-dependent and can inspect another test’s event under parallel execution. Filter by session and rule ID before asserting the outcome.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_guard.py` around lines 518 - 520, Update the assertions in the
test around compliance.read_events() to select the event belonging to the
current session and guard.GATE_NO_MATCH_RULE before checking its outcome.
Replace the order-dependent events[-1] access with session- and rule-filtered
event selection, then assert that the selected event has outcome "auto-clear".

@CryptoJones
CryptoJones merged commit db72167 into main Jul 28, 2026
16 checks passed
@CryptoJones
CryptoJones deleted the fix/omi-gate-miss-autoclear branch July 28, 2026 07: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