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>
This commit is contained in:
Abdul Wahab
2026-08-06 17:29:20 +05:00
co-authored by Cursor
parent 328db604b2
commit fd8bc04ff4
3 changed files with 42 additions and 12 deletions
+18 -4
View File
@@ -16,6 +16,7 @@ import path from 'node:path';
import {
ALLOWED_EXTS,
DEFAULT_CONFIG,
EDIT_COUNT_THRESHOLD,
GENERATED_PATH,
SENSITIVE_PATH,
@@ -348,13 +349,26 @@ async function detectProposedHtml(detector, content, filePath, scanOptions) {
}
}
// Cursor caps deny messages around 4000 chars. The cap feeds through the
// renderer's clamp, which preserves the policy footer, rather than tail-
// slicing the rendered text, which cut the footer off any message the
// default 8000-char budget let past 4000.
const CURSOR_DENY_LIMIT = 4000;
const BLOCK_PREFIX = 'Impeccable design hook blocked this write before it landed. ';
function cursorBlockMessage(findings, filePath, config, cwd, footerMode, reserveChars) {
const rendered = renderTemplate(findings, filePath, config, { cwd, footer: footerMode, reserveChars });
const blocked = rendered.replace(
const limits = config?.limits || DEFAULT_CONFIG.limits;
const budget = Math.min(
limits.maxChars || DEFAULT_CONFIG.limits.maxChars,
CURSOR_DENY_LIMIT - BLOCK_PREFIX.length,
);
const rendered = renderTemplate(findings, filePath,
{ ...config, limits: { ...limits, maxChars: budget } },
{ cwd, footer: footerMode, reserveChars });
return rendered.replace(
'[impeccable@1] Design hook findings requiring review',
'[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review',
`[impeccable@1] ${BLOCK_PREFIX}Design hook findings requiring review`,
);
return blocked.length > 4000 ? `${blocked.slice(0, 3984)}\n...(truncated)` : blocked;
}
function findingSignature(findings) {
+5 -8
View File
@@ -1702,14 +1702,15 @@ export function designNoteReserve(scanOptions, cache, sessionId) {
// emissions and Cursor denials share the session flag (`footerShown`), so a
// session pays the full policy exactly once however it first fires. The mode
// is a peek: the clamp can downgrade a requested full footer under a tight
// budget, so the flag commits only when the full policy actually reached the
// output (commitFooterShown, matched on the footer's opening words).
// budget, so the flag commits only when the complete full policy actually
// reached the output. Matching the whole footer text (not a sentinel) keeps
// the flag honest against any truncation that spares the opening words.
export function footerModeForSession(cache, sessionId) {
return ensureSession(cache, sessionId).footerShown ? 'short' : 'full';
}
export function commitFooterShown(cache, sessionId, text) {
if (!text || !text.includes(FULL_FOOTER_SENTINEL)) return;
if (!text || !text.includes(directiveFooter())) return;
const session = ensureSession(cache, sessionId);
if (session.footerShown) return;
session.footerShown = true;
@@ -1718,10 +1719,6 @@ export function commitFooterShown(cache, sessionId, text) {
const HOOK_ADMIN_COMMAND = `node ${quoteCommandArg(path.join(__dirname, 'hook-admin.mjs'))}`;
// Opening words of the full footer; commitFooterShown matches on it to tell
// whether the full policy survived the clamp.
const FULL_FOOTER_SENTINEL = 'Triage each finding';
// The directive footer is the part of the hook output that steers model
// behavior. Intentional moves, in order:
// 1. **Imperative, not advisory.** "Triage each finding..." beats
@@ -1749,7 +1746,7 @@ function directiveFooter(opts = {}) {
return 'Triage per the session policy: fix real problems; persist confident false-positive or sanctioned-exception ignores via `hook-admin.mjs ignore-value` and disclose them in your reply; unsure, ask in one line.';
}
return [
`${FULL_FOOTER_SENTINEL}, then state in your reply what you fixed, what you suppressed, and what you left standing:`,
'Triage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:',
'- Real design problem: fix it. Keep intentional design as designed.',
`- Confident false positive or sanctioned exception (an intentional demo or fixture, documentation of bad design, literal or domain-appropriate motion, a choice the user confirmed): persist the narrowest ignore yourself and disclose it. Run \`${HOOK_ADMIN_COMMAND} ignore-value <rule> "<value>" --reason "<who decided: evidence>"\` with the pair shown on the finding line, or value "*" plus \`--file <path>\` when the line shows none. Write "user confirmed" in a reason only when the user did.`,
'- Unsure: leave it as is and ask the user in one line.',
+19
View File
@@ -50,6 +50,7 @@ import {
coLocatedStylesheets,
runHook,
runStopHook,
commitFooterShown,
IMMEDIATE_TIER_RULES,
splitFindingsByTier,
perEditTieringActive,
@@ -2305,6 +2306,22 @@ describe('runHook() — session-scoped notices', () => {
const cache = readCache(cwd);
assert.ok(!cache.sessions['sid-1'].footerShown, 'a downgraded footer does not spend the session flag');
});
it('commits the footer flag only for the complete full policy, not its opening words', () => {
const cache = { version: 1, sessions: {} };
const full = renderTemplate(
[finding('tiny-text', 1, { name: 'Tiny text' })],
'/x/a.css', DEFAULT_CONFIG, { cwd: '/x' },
);
// A tail truncation can spare "Triage each finding" while cutting the
// policy body. That must not count as delivered.
commitFooterShown(cache, 'sid-1', full.slice(0, full.length - 40));
assert.ok(!cache.sessions['sid-1']?.footerShown, 'a truncated policy must not spend the flag');
commitFooterShown(cache, 'sid-1', full);
assert.ok(cache.sessions['sid-1'].footerShown, 'the intact policy commits the flag');
});
});
describe('runHook() — clean-ack noise', () => {
@@ -3113,6 +3130,8 @@ describe('Cursor hook scripts', () => {
assert.match(payload.user_message, /blocked this write/);
assert.match(payload.user_message, /side-tab/);
assert.match(payload.agent_message, /Triage each finding/);
assert.match(payload.agent_message, /Full suppression ladder/, 'the deny message carries the complete policy, not a truncated head');
assert.ok(payload.agent_message.length <= 4000, 'the deny message respects the Cursor cap');
const entries = fs.readFileSync(logPath, 'utf-8').trim().split('\n').map((line) => JSON.parse(line));
assert.equal(entries[0].event, 'preToolUse');