Skip to content

feat(procedures): gate how-do-i on mutation + field-anchored mistakes.jsonl recall - #39

Merged
drewdrewthis merged 4 commits into
mainfrom
fix/how-do-i-gate-look-vs-act
Aug 11, 2026
Merged

feat(procedures): gate how-do-i on mutation + field-anchored mistakes.jsonl recall#39
drewdrewthis merged 4 commits into
mainfrom
fix/how-do-i-gate-look-vs-act

Conversation

@drewdrewthis

@drewdrewthis drewdrewthis commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Combined PR, two changes that together close out the open gate/recall friction set (per Drew: drive these in one PR).

1. Gate how-do-i on mutation, not the discovery path

(unchanged from the original PR — see commit b83c5b1)

Fixes the gate blocking read-only verification and discovery:

  • drewdrewthis/orchard-codex#291 (gate fires post-first-tool-call, bypass/retry cycles)
  • drewdrewthis/orchard-codex#339 (gate re-blocks read-only verification mid-turn-chain)
  • drewdrewthis/orchard-codex#344 (gate blocked Read of own background-watch output)

2. Field-anchored recall over mistakes.jsonl (closes #41)

query-records.sh gains a --recall '<term set>' mode backed by scripts/lib/recall-match.awk:

  • matches only inside semantic field values (pattern description correction face category skill summary what fix) — a term inside a path, URL, or session id cannot hit (the noise defect of the raw grep -i procedure-scout step 4 prescribed)
  • whole-word + phrase-aware: pickup-loop matches the phrase across any separator, never bare loop; common words no longer explode the match set
  • loud count line (recall: N matched — showing the 20 most recent), --limit override, never a silent cap
  • same fail-loud contract as the rest of the script: exit 2 usage / exit 3 missing store or broken awk, distinct from a genuine empty miss
  • tolerant of both "k":"v" and "k": "v" record styles (the live file mixes them)
  • procedure-scout step 4 now calls this instead of raw grep

Verified against the live 504KB/481-line store: exact hit-parity with the old grep on its own term set (5/5, zero lost hits), noise cases eliminated, 0.1–0.3s wall.

Tests: bats hooks/tests — 193/193 green, including new hooks/tests/recall.bats (11 tests).

Title is feat(procedures): so release-please cuts a minor bump (covers the fix too).

Related: #34 (query-records as sole retrieval surface — this extends that surface to recall).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added semantic recall search for recorded mistakes with whole-word and phrase matching.
    • Recall searches support result limits, recent-result ordering, and clear overflow reporting.
    • Safety gates now support a broader range of read-only inspection tools and commands.
  • Bug Fixes

    • Unknown or potentially mutating commands now fail closed.
    • Improved handling of command chaining, redirection, substitutions, and traversal attempts.
  • Tests

    • Added comprehensive coverage for recall searches and read-only command detection.

The deny message promises read-only inspection stays available; the
allowlist did not deliver it. A Read was allowed only under
references/procedures/, and a Bash command had to BOTH match a small
shape set AND name the discovery surface — so pure reads like
`tmux capture-pane | grep | tail`, `wc -l < f` and a Read of a task's
own output file were denied.

Replace the path co-requirement with a command classifier
(hooks/lib/readonly-shape.sh) that judges the whole line: separators are
split and every segment must be a known read-only invocation, while
command substitution, process substitution and output redirection to a
file are refused outright. Unrecognised shapes fail closed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@drewdrewthis, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f5d7ddbe-919f-4ce2-a90e-076af179c8bc

📥 Commits

Reviewing files that changed from the base of the PR and between e8b8398 and 0a1977c.

📒 Files selected for processing (1)
  • plugins/procedures/hooks/tests/recall.bats
📝 Walkthrough

Walkthrough

The PR adds field-anchored recall search for mistakes.jsonl and replaces narrow gate checks with fail-closed read-only command classification. It adds matcher logic, CLI validation, procedure guidance, gate integration, and Bats coverage.

Changes

Semantic Recall Search

Layer / File(s) Summary
Recall query flow
plugins/procedures/scripts/query-records.sh, plugins/procedures/scripts/lib/recall-match.awk, plugins/procedures/hooks/tests/recall.bats, plugins/procedures/agents/procedure-scout.md
query-records.sh --recall searches semantic JSON fields, applies whole-word and phrase matching, reports counts, and limits output to recent results. Tests cover matching, validation, errors, ordering, and procedure usage.

Read-Only Gate Enforcement

Layer / File(s) Summary
Read-only command classifier
plugins/procedures/hooks/lib/readonly-shape.sh
The classifier rejects unsafe shell syntax, output redirection, interpreters, unknown commands, and mutating command variants. It evaluates Git, find, sed, Docker, systemd, journalctl, and multiplexer commands.
Gate allowlist integration
plugins/procedures/hooks/lib/gate-allowlist.sh, plugins/procedures/hooks/how-do-i-gate.sh
The gate permits non-traversal inspection tools and delegates Bash decisions to ros_is_read_only. The denial message describes the expanded read-only access.
Gate behavior validation
plugins/procedures/hooks/tests/gate-libs.bats, plugins/procedures/hooks/tests/gate-failopen.bats
Tests cover accepted reads, pipelines, multiplexers, Git, redirections, command substitution, mutation paths, unknown commands, and fail-open recording.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Scout
  participant QueryRecords
  participant RecallMatcher
  participant MistakesStore
  Scout->>QueryRecords: --recall query
  QueryRecords->>RecallMatcher: normalized searchable tokens
  RecallMatcher->>MistakesStore: scan semantic JSON fields
  MistakesStore-->>RecallMatcher: matching records
  RecallMatcher-->>QueryRecords: matched records
  QueryRecords-->>Scout: capped recent results and total
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The gate classifier and gate allowlist changes are not covered by directly linked issue #41, which targets mistakes.jsonl recall. Link an issue that covers the gate behavior changes, or move those changes to a separate pull request.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies both primary changes: mutation-based gating and field-anchored mistakes.jsonl recall.
Linked Issues check ✅ Passed The recall implementation and procedure-scout update satisfy the field anchoring, matching, limits, fail-loud behavior, and Bats coverage requirements in issue #41.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/how-do-i-gate-look-vs-act

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.

@drewdrewthis drewdrewthis changed the title fix(procedures): gate how-do-i on mutation, not on the discovery path feat(procedures): gate how-do-i on mutation + field-anchored mistakes.jsonl recall Aug 11, 2026

@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 `@plugins/procedures/agents/procedure-scout.md`:
- Around line 95-98: The procedure-scout instructions currently hard-code
record-store paths instead of discovering all readable stores. Update the
guidance around the `query-records.sh` usage to run `--list-stores` first, use
its output as the readable store boundary, and select scans from those stores
rather than enumerating `references/solutions/` or other paths.
- Around line 107-108: Remove the two non-operational recap lines from the
procedure guidance near the recall discussion; retain only prose that gives the
agent an actionable instruction, boundary, or failure mode.

In `@plugins/procedures/hooks/lib/gate-allowlist.sh`:
- Around line 48-62: Update the jq extraction in the Read|NotebookRead|Grep|Glob
branch to include Glob’s pattern and Grep’s glob fields in the traversal check,
preserving the existing refusal for paths containing "..". Add a Bats test under
hooks/tests/ covering a traversal-only Glob pattern and keep the existing
pattern-only allow behavior for non-traversing patterns.
- Line 37: Update the path resolution around the readonly-shape.sh source in
gate-allowlist.sh to handle BASH_SOURCE[0] values without a slash, matching the
existing guard in how-do-i-gate.sh. Ensure bare-name sourcing resolves the
current directory correctly before sourcing readonly-shape.sh, while preserving
path-based sourcing behavior.

In `@plugins/procedures/hooks/lib/readonly-shape.sh`:
- Around line 108-116: Restrict the unconditional allowlist in the readonly
command classifier so write-capable invocations cannot return 0: remove or gate
sort, uniq, xxd, yq, ip, and date, preserving only read-only forms such as sort
without output, yq without in-place editing, ip show/list, and date without
setting. Add Bats coverage under hooks/tests/ for each newly denied write form.
- Around line 50-82: Update ros__git_is_read_only to reject external-program and
helper-enabling options before allowing read-only subcommands: deny grep’s
-O/--open-files-in-pager forms, diff’s --ext-diff and --config-env* forms, and
attached -c<name>=<value> forms while preserving fail-closed handling for
separate -c arguments. Add Bats coverage for each rejected option and ensure
ordinary read-only commands remain allowed.

In `@plugins/procedures/scripts/query-records.sh`:
- Around line 181-235: Remove the local recall-mode implementation from
plugins/procedures/scripts/query-records.sh lines 181-235 and remove the
plugin-local matcher from plugins/procedures/scripts/lib/recall-match.awk lines
1-60 until the upstream vendor includes them. Update
plugins/procedures/agents/procedure-scout.md lines 95-113 to match the
upstream-vendored behavior, keeping all three files byte-close to upstream.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 52499c0d-2782-41d1-8268-bce3c17cb974

📥 Commits

Reviewing files that changed from the base of the PR and between 30c77c7 and 9e9285e.

📒 Files selected for processing (9)
  • plugins/procedures/agents/procedure-scout.md
  • plugins/procedures/hooks/how-do-i-gate.sh
  • plugins/procedures/hooks/lib/gate-allowlist.sh
  • plugins/procedures/hooks/lib/readonly-shape.sh
  • plugins/procedures/hooks/tests/gate-failopen.bats
  • plugins/procedures/hooks/tests/gate-libs.bats
  • plugins/procedures/hooks/tests/recall.bats
  • plugins/procedures/scripts/lib/recall-match.awk
  • plugins/procedures/scripts/query-records.sh

Comment thread plugins/procedures/agents/procedure-scout.md Outdated
Comment thread plugins/procedures/agents/procedure-scout.md Outdated
Comment thread plugins/procedures/hooks/lib/gate-allowlist.sh Outdated
Comment thread plugins/procedures/hooks/lib/gate-allowlist.sh
Comment thread plugins/procedures/hooks/lib/readonly-shape.sh
Comment thread plugins/procedures/hooks/lib/readonly-shape.sh
Comment thread plugins/procedures/scripts/query-records.sh
…y-records.sh

Closes #41. scripts/lib/recall-match.awk: whole-word, phrase-aware matching
over semantic field values only (never paths/URLs/session ids), tolerant of
both JSONL spacing styles, field-boundary-safe. --recall mode: loud count
line, 20-most-recent default cap, --limit override, exit 2/3 fail-loud
contract. procedure-scout step 4 swapped off raw grep. CodeRabbit: store
boundary via --list-stores, recap prose dropped, gate-allowlist BASH_SOURCE
bare-name guard. hooks/tests/recall.bats: 16 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@drewdrewthis
drewdrewthis force-pushed the fix/how-do-i-gate-look-vs-act branch from 86918d4 to 12fb5f3 Compare August 11, 2026 11:41
- gate-allowlist: per-tool traversal fields (Grep path+glob, Glob
  path+pattern); Grep regex pattern exempt (.. is any-two-chars)
- readonly-shape: reject git options that hand execution to an external
  program (-O/--open-files-in-pager, --ext-diff, --config-env)
- readonly-shape: drop ip from the unconditional allowlist; gate the
  write modes of sort/-o, yq/-i, date/-s, uniq/xxd second operand
- PLUGIN ADAPTATION markers on query-records.sh recall + recall-match.awk
- bats coverage for every newly gated shape (203 green)
@drewdrewthis

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 59 minutes.

@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: 3

♻️ Duplicate comments (1)
plugins/procedures/hooks/lib/readonly-shape.sh (1)

123-127: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Reject execution and mutation options in the remaining reader allowlist.

rg --pre /path/to/program executes the preprocessor. tree -o output writes a file. ss -K ... closes sockets. GNU sort --compress-program=/path/to/program invokes a helper when it spills temporary files. Each command currently reaches return 0, so a Bash payload can act while the gate classifies it as read-only.

  • plugins/procedures/hooks/lib/readonly-shape.sh#L123-L127: Remove rg, tree, and ss from the unconditional branch, or reject --pre, --pre=*, -o*, -K, and --kill.
  • plugins/procedures/hooks/lib/readonly-shape.sh#L139-L143: Reject --compress-program and --compress-program=*.
  • Add Bats cases for every denied form.
#!/bin/bash
set -euo pipefail

for tool in rg sort tree ss; do
  if command -v "$tool" >/dev/null 2>&1; then
    printf '\n--- %s help ---\n' "$tool"
    "$tool" --help 2>&1 | rg -n -- '--pre|compress-program|output|-o|--kill|-K' || true
  fi
done

printf '\n--- classifier branches ---\n'
sed -n '119,158p' plugins/procedures/hooks/lib/readonly-shape.sh

As per path instructions: “Add Bats coverage for every new behavior.”

🤖 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 `@plugins/procedures/hooks/lib/readonly-shape.sh` around lines 123 - 127, The
readonly command classifier must reject execution and mutation options instead
of allowing them unconditionally. In
plugins/procedures/hooks/lib/readonly-shape.sh lines 123-127, update the rg,
tree, and ss handling to deny --pre/--pre=*, -o*, -K, and --kill (or remove
those tools from the unconditional branch); in lines 139-143, reject sort’s
--compress-program and --compress-program=* options. Add Bats coverage for every
denied form.

Source: Path instructions

🤖 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 `@plugins/procedures/agents/procedure-scout.md`:
- Around line 97-109: Update the recall instructions in the how-do-i skill text
to use query-records.sh with the --recall option and describe its semantic
whole-word matching and batch-read flow. Remove the stale claim that recall is
unsupported and that mistakes.jsonl should be grepped directly, while preserving
the explicit prohibition against raw grep fallback.

In `@plugins/procedures/hooks/tests/recall.bats`:
- Around line 15-20: Expand the recall fixture records in the heredoc used by
the recall tests so positive cases contain unique search terms in every
supported semantic field: pattern, correction, face, category, skill, summary,
what, and fix, in addition to description. Update or add corresponding
assertions to verify each term matches only through its intended field,
preserving the existing field-anchoring coverage.
- Around line 1-5: Add a literal “# PLUGIN ADAPTATION: <why>” comment near the
header of the Bats test file, explaining why this plugin-local recall behavior
diverges from upstream; keep the existing test content unchanged.

---

Duplicate comments:
In `@plugins/procedures/hooks/lib/readonly-shape.sh`:
- Around line 123-127: The readonly command classifier must reject execution and
mutation options instead of allowing them unconditionally. In
plugins/procedures/hooks/lib/readonly-shape.sh lines 123-127, update the rg,
tree, and ss handling to deny --pre/--pre=*, -o*, -K, and --kill (or remove
those tools from the unconditional branch); in lines 139-143, reject sort’s
--compress-program and --compress-program=* options. Add Bats coverage for every
denied form.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 17c45361-4241-4038-8225-d250085e3941

📥 Commits

Reviewing files that changed from the base of the PR and between 9e9285e and e8b8398.

📒 Files selected for processing (7)
  • plugins/procedures/agents/procedure-scout.md
  • plugins/procedures/hooks/lib/gate-allowlist.sh
  • plugins/procedures/hooks/lib/readonly-shape.sh
  • plugins/procedures/hooks/tests/gate-libs.bats
  • plugins/procedures/hooks/tests/recall.bats
  • plugins/procedures/scripts/lib/recall-match.awk
  • plugins/procedures/scripts/query-records.sh

Comment thread plugins/procedures/agents/procedure-scout.md
Comment thread plugins/procedures/hooks/tests/recall.bats
Comment thread plugins/procedures/hooks/tests/recall.bats
@drewdrewthis
drewdrewthis merged commit 2e3b232 into main Aug 11, 2026
2 checks passed
@drewdrewthis
drewdrewthis deleted the fix/how-do-i-gate-look-vs-act branch August 11, 2026 15:03
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.

procedures: mistakes.jsonl recall is a raw unanchored grep — add field-anchored recall to query-records.sh

2 participants