mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
* Denoise the design hook and let agents self-serve confident ignores (#497)
The directive footer now emits in full once per session (a one-line
reminder after), the DESIGN.md staleness note is mentioned once per
session, rule descriptions dedupe within an emission, and the per-line
ignore suggestion shrinks to the bare rule/value pair. The footer and
hooks.md replace the confirmation-gated ignore policy with a three-way
triage: fix real problems, self-serve the narrowest ignore for confident
false positives or sanctioned exceptions and disclose it (with an honest
--reason), ask when unsure. Self-serve stops at ignore-value, and the
footer now gives a runnable hook-admin.mjs command instead of a slash
command agents cannot execute.
Measured on a seeded lab session replaying 11 hook events: 33,658 to
14,063 chars of agent-visible output (-58%).
AI-assisted (Cursor agent), directed and reviewed by @abdulwahabone.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Preserve the policy footer and honor maxChars under constrained budgets
Greptile's runtime check found two pre-existing clamp gaps that matter
more now that the full policy emits once per session: the last-resort
tail slice cut the footer off an over-budget emission, and the DESIGN.md
staleness note was appended after clamping, pushing past maxChars.
The clamp now gives the footer the budget first, clipping the finding
line and downgrading full to short policy when needed. The staleness
note defers, without consuming its session flag, to a later emission
with room.
AI-assisted (Cursor agent), directed and reviewed by @abdulwahabone.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Harden the constrained-budget clamp: keep findings, honest flags, guaranteed note
Review follow-ups from Bugbot and Greptile on the clamp fix:
- The clamp retries with the short policy before dropping finding lines
that fit beside it, and a grouped result that kept only a file header
no longer counts as a fit.
- The full-footer session flag commits only when the full policy
actually survived the clamp, so a downgraded emission does not mark
the session as having seen a policy it never received.
- Render paths reserve room for a pending DESIGN.md staleness note, so
it is delivered inside the budget on the first emission instead of
deferring behind full emissions indefinitely.
AI-assisted (Cursor agent), directed and reviewed by @abdulwahabone.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Route the Cursor deny cap through the clamp and match the whole footer
Bugbot follow-up: cursorBlockMessage tail-sliced at 4000 chars after
render, which the default 8000-char budget made reachable, and a cut
that spared the footer's opening words still committed the session
flag. The 4000 cap now feeds through the renderer's footer-preserving
clamp, and commitFooterShown matches the complete footer text instead
of a sentinel.
AI-assisted (Cursor agent), directed and reviewed by @abdulwahabone.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Reconcile hook.test.mjs expectations with main's per-platform quoting
Three tests fell out of sync when main's quoteCommandArg change (#533,
building on #476) met this branch's footer/hint rework. Test-only
changes; production logic untouched:
- The full-footer test now accepts either close quote after the
hook-admin.mjs path, since quoteCommandArg single-quotes absolute
paths on POSIX and double-quotes them on Windows. The short-footer
guard rejects `node '` and `node "` alike.
- The #476 hostile-value test asserts the new bare
`ignore-value <rule> '<value>'` hint format. The security property is
unchanged: the value still passes through quoteCommandArg, so
$(touch pwned) stays single-quoted and inert.
- The #533 test previously asserted a concrete quoted `--file` path in
the footer; directiveFooter() now carries only literal placeholders,
so that surface is gone. The per-platform assertion moves to the
per-finding ignore hint, the remaining user-visible surface where
scanned file content flows through quoteCommandArg.
Prepared with AI assistance (Claude Code).
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix two Bugbot findings: Cursor prefix budget and footer-cutting tail slice
Both flagged by Cursor Bugbot on PR #508 after the main merge; both real.
1. cursorBlockMessage computed min(maxChars, 4000 - prefix), so a
configured maxChars at or below the Cursor ceiling never charged the
BLOCK_PREFIX against the budget: the final deny text could exceed
maxChars by the prefix length, and appendDesignSystemNoteOnce's size
check lost exactly the room designNoteReserve had held back. The
prefix now comes off whichever limit binds. Default-config behavior
is unchanged (min(8000, 4000) - 60 equals the old 4000 - 60).
2. The note reservation is subtracted after renderTemplate's 500-char
floor, so the clamp can run below the budget clampLastLine assumed
safe, and its last-resort path tail-sliced the rendered text, cutting
the policy footer (the failure mode this PR exists to eliminate) when
a deep file path met a pending DESIGN.md note. The reservation order
stays (the staleness-note delivery guarantee at floor budgets depends
on it); the last resort now drops the finding line and clips the head
instead, so the footer survives every path. New regression test pins
it: 6 findings, 100-char path, maxChars 500, reserveChars 134.
Prepared with AI assistance (Claude Code).
Co-Authored-By: Claude <noreply@anthropic.com>
* Charge the Cursor deny prefix after the renderer's floor, not before
Greptile's runtime check caught the residual from ead0346d: subtracting
BLOCK_PREFIX from the maxChars passed to renderTemplate does nothing at
floor-tier configs, because the renderer re-raises any budget below its
500-char floor. At maxChars 500 with a stale design sidecar, the
prefixed denial landed at 432 chars and appendDesignSystemNoteOnce
could not fit the staleness note inside 500, deferring it (flag
unconsumed) for every equivalent denial in the session.
The prefix now rides in reserveChars, which comes off after the floor,
so it is charged at every config tier and the final prefixed message
plus a pending note closes exactly at the binding limit (499 chars in
the regression scenario). Default-config output is byte-identical:
max(500, min(8000, 4000)) - prefix equals the old min(8000, 4000) -
prefix. New end-to-end Cursor preToolUse test pins the path with a real
stale sidecar at maxChars 500.
AI-assisted (Cursor agent), directed and reviewed by @abdulwahabone.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>