Skip to content

Context guard: size whole-file reads before they happen (0.15.0) - #28

Merged
davidcrowe merged 1 commit into
mainfrom
feat/context-guard
Sep 7, 2026
Merged

Context guard: size whole-file reads before they happen (0.15.0)#28
davidcrowe merged 1 commit into
mainfrom
feat/context-guard

Conversation

@davidcrowe

Copy link
Copy Markdown
Collaborator

Spotify's shunt plugin blocks Read/cat over 350 lines with a PreToolUse hook (their lesson: CLAUDE.md is advice, hooks are law). This is the ACP version: one parser, measured, shadow-first, every harness the installer serves.

What

  • readIntent() in decide.mjs (pure): sizes Read (offset/limit-aware) and cat/head/tail/less/more/bat. Pipes, redirects, byte ranges, tail -f are targeted and pass. head -n N counts as N lines.
  • govern.mjs measures the files (lines + bytes, capped at 8 MB then estimated) and sends tool_context: { read, files } on /govern/tool-use; local mode hands it to decide().
  • Policy: contextGuard: { maxLines, mode: off|shadow|enforce, action: deny|ask, guidance }. Shadow → allow, audit line carries contextGuard.estTokens. Enforce → deny/ask, reason names the sanctioned path (offset/limit or grep; sed -n for Codex; subagent).
  • No block on the policy → zero behavior change. Unmeasurable file → not guarded, never a lapse.

Tests

49 new (test/context-guard.test.mjs): Spotify's 17 Read + 17 Bash evals ported, plus the bypasses they document as known gaps (offset 0 / limit 0 now sized, head -100 now passes, head -n 5 passes on purpose). Layer 2 spawns the real hook against real files. Full suite 162/162.

Sandbox: install.sh --local in a throwaway HOME, then a 1,200-line Read → denied with the steer; head -n 20 → silent allow; audit.jsonl carries the ledger.

Order

Merge this first: install.sh fetches govern.mjs from this repo's main. Gateway side: gatewaystack-connect PR (same branch name). Tracks gatewaystack-connect#1072.

readIntent() sizes Read (offset/limit-aware) and cat/head/tail/less/more/bat;
piped, redirected, byte-ranged and followed reads pass. govern.mjs measures
the files (lines + bytes) and sends tool_context to the gateway, or hands it
to the local engine. Policy block contextGuard { maxLines, mode: off|shadow|
enforce, action, guidance }: shadow records estTokens on the audit line,
enforce denies (or asks) with a steer that names the sanctioned path per
harness. No block → no behavior change.

Ported Spotify shunt's 34 hook evals and closed its known bypasses (offset 0
/ limit 0 pass, head -100 blocks) by sizing the effective read.

Tracks gatewaystack-connect#1072.
@davidcrowe
davidcrowe merged commit fb40ee2 into main Sep 7, 2026
2 checks passed
@davidcrowe
davidcrowe deleted the feat/context-guard branch September 7, 2026 23:28
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