feat(hooks): enforce research loop cap at PreToolUse or document the gap
This commit is contained in:
parent
f3874946ad
commit
e9ff8ab023
6 changed files with 441 additions and 2 deletions
|
|
@ -107,3 +107,48 @@ Two limits worth carrying forward, neither of which changes the verdict:
|
|||
always exited 0 by design.
|
||||
|
||||
RESULT: FIRES
|
||||
|
||||
## Enforcement outcome
|
||||
|
||||
Step 10 took the **enforcement branch**: `hooks/scripts/pre-agent-cap.mjs`
|
||||
(PreToolUse, matcher `WebSearch|WebFetch|Task`), pinned by
|
||||
`tests/hooks/agent-cap.test.mjs`.
|
||||
|
||||
What it does: counts turns spent by a run — read-only, from the append-only
|
||||
ledger that `lib/util/research-loop-cap.mjs` writes — and exits 2 once
|
||||
`turns_used >= max_conv_turns × maxDimensions`. It never appends to the
|
||||
ledger; a cap that recorded its own enforcement would count itself.
|
||||
|
||||
Scope key, the part that makes a globally-wired `PreToolUse` hook safe:
|
||||
`session_id` **+** a marker file only the Phase 5 loop writes, at
|
||||
`${CLAUDE_PLUGIN_DATA}/trekresearch-loop-scope/<session_id>.json`:
|
||||
|
||||
```json
|
||||
{ "runId": "<run id>", "startedAt": "<ISO-8601>" }
|
||||
```
|
||||
|
||||
No marker for the calling session ⇒ out of scope ⇒ allow, unconditionally.
|
||||
An unrelated session is never denied because some other run spent its budget.
|
||||
|
||||
Fail-open and fail-closed are split deliberately:
|
||||
|
||||
| Condition | Outcome | Why |
|
||||
|---|---|---|
|
||||
| No marker / no `session_id` / unparsable stdin | allow | Not evidence of a loop turn |
|
||||
| Marker older than TTL (default 6h, `VOYAGE_CAP_SCOPE_TTL_MS`) | allow + auto-reset | A crashed run must not deny tool calls forever |
|
||||
| `VOYAGE_DISABLE_CAP_HOOK=1` | allow | Kill switch |
|
||||
| `VOYAGE_STORM_ENABLED` ≠ `1` | allow | Default-off: no loop runs, nothing to enforce |
|
||||
| In scope, `CLAUDE_PLUGIN_DATA` absent | **deny** | A budget control that cannot count must not grant — same stance as `research-loop-cap.mjs` |
|
||||
| In scope, budget spent | **deny (exit 2)** | The bound |
|
||||
|
||||
Both limits recorded above still hold and are not closed by this step. Reach
|
||||
was measured on one CC version for one tool, and blocking-propagation from
|
||||
inside a sub-agent was never measured — so this hook is **defence in depth**,
|
||||
and `research-loop-cap.mjs` must remain correct on its own if the hook
|
||||
silently stops firing.
|
||||
|
||||
**Open follow-up (outside Step 10's scope fence):** the marker file is written
|
||||
by nothing yet. `commands/trekresearch.md` is on Session 4's never-touch list,
|
||||
so wiring Phase 5 to write and remove the marker belongs to a later session.
|
||||
Until then the hook is correct but latent: it enforces exactly when a marker
|
||||
exists, and no marker is ever created.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue