diff --git a/tests/oracle/cases/hooks.mjs b/tests/oracle/cases/hooks.mjs new file mode 100644 index 000000000..321c53f3f --- /dev/null +++ b/tests/oracle/cases/hooks.mjs @@ -0,0 +1,185 @@ +/** + * `impeccable hook`, `hook-before-edit`, `hook-admin` corpus. + * Workspace: hook-project (package.json, PRODUCT.md web, UI files with and + * without findings). Multi-step cases share one staged workspace so the + * session cache carries between steps. + */ + +import fs from 'node:fs'; + +const WS = ''; +const CACHE_FILES = ['.impeccable/**', '.claude/settings.local.json', '.codex/hooks.json', '.cursor/hooks.json', '.github/hooks/impeccable.json']; + +const claudeEdit = (file, extra = {}) => ({ + session_id: 's1', cwd: WS, hook_event_name: 'PostToolUse', tool_name: 'Edit', + tool_input: { file_path: `${WS}/${file}` }, ...extra, +}); +const stop = (extra = {}) => ({ session_id: 's1', cwd: WS, hook_event_name: 'Stop', stop_hook_active: false, ...extra }); + +export default [ + // --- hook.mjs: per-edit --- + { id: 'hook-edit-tsx-fresh', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.tsx'), files: CACHE_FILES }, + { id: 'hook-edit-css-fresh', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.module.css'), files: CACHE_FILES }, + { id: 'hook-edit-html-fresh', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/page.html'), files: CACHE_FILES }, + { id: 'hook-edit-clean-tsx', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Clean.tsx'), files: CACHE_FILES }, + { id: 'hook-edit-non-ui-ts', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/util.ts'), files: CACHE_FILES }, + { id: 'hook-edit-missing-file', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/nope.tsx'), files: CACHE_FILES }, + { id: 'hook-edit-outside-project', verb: 'hook', workspace: 'hook-project', stdin: { ...claudeEdit('x'), tool_input: { file_path: '/tests/fixtures/antipatterns/blinking-cursor.html' } }, files: CACHE_FILES }, + { id: 'hook-edit-sensitive', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('.env.local'), files: CACHE_FILES }, + { id: 'hook-edit-generated', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('dist/bundle.css'), files: CACHE_FILES }, + { id: 'hook-edit-write-tool', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.tsx', { tool_name: 'Write' }), files: CACHE_FILES }, + { id: 'hook-edit-multiedit', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.tsx', { tool_name: 'MultiEdit' }), files: CACHE_FILES }, + { id: 'hook-edit-no-file-path', verb: 'hook', workspace: 'hook-project', stdin: { session_id: 's1', cwd: WS, hook_event_name: 'PostToolUse', tool_name: 'Edit', tool_input: {} }, files: CACHE_FILES }, + { id: 'hook-stdin-empty', verb: 'hook', workspace: 'hook-project', stdin: '', files: CACHE_FILES }, + { id: 'hook-stdin-malformed', verb: 'hook', workspace: 'hook-project', stdin: '{not json', files: CACHE_FILES }, + { id: 'hook-stdin-array', verb: 'hook', workspace: 'hook-project', stdin: '[1,2]', files: CACHE_FILES }, + { id: 'hook-env-disabled', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.tsx'), env: { IMPECCABLE_HOOK_DISABLED: '1' }, files: CACHE_FILES }, + { id: 'hook-env-reentrant', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.tsx'), env: { IMPECCABLE_HOOK_DEPTH: '1' }, files: CACHE_FILES }, + { id: 'hook-env-quiet', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.tsx'), env: { IMPECCABLE_HOOK_QUIET: '1' }, files: CACHE_FILES }, + { id: 'hook-harness-github-edit', verb: 'hook', workspace: 'hook-project', stdin: { sessionId: 'g1', cwd: WS, toolName: 'edit', toolArgs: JSON.stringify({ path: 'src/components/Card.module.css', old_str: 'a', new_str: 'b' }) }, files: CACHE_FILES }, + { id: 'hook-harness-github-apply-patch', verb: 'hook', workspace: 'hook-project', stdin: { sessionId: 'g1', cwd: WS, toolName: 'apply_patch', toolArgs: '*** Begin Patch\n*** Update File: src/components/Card.module.css\n@@\n-x\n+y\n*** End Patch' }, files: CACHE_FILES }, + { id: 'hook-harness-codex-apply-patch', verb: 'hook', workspace: 'hook-project', stdin: { session_id: 'c1', cwd: WS, hook_event_name: 'PostToolUse', tool_name: 'apply_patch', tool_input: { command: '*** Begin Patch\n*** Update File: src/components/Card.module.css\n@@\n-x\n+y\n*** End Patch' } }, files: CACHE_FILES }, + { id: 'hook-harness-cursor-shaped', verb: 'hook', workspace: 'hook-project', stdin: { conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: 'src/components/Card.module.css' } }, files: CACHE_FILES }, + { id: 'hook-harness-forced-github', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.module.css'), env: { IMPECCABLE_HOOK_HARNESS: 'github' }, files: CACHE_FILES }, + { id: 'hook-audit-log', verb: 'hook', workspace: 'hook-project', stdin: claudeEdit('src/components/Card.module.css'), env: { IMPECCABLE_HOOK_LOG: `${WS}/.impeccable/audit.ndjson` }, files: CACHE_FILES }, + { + id: 'hook-native-platform-skip', verb: 'hook', workspace: 'hook-project', + setup: (ws) => fs.writeFileSync(`${ws}/PRODUCT.md`, '# P\n\n## Platform\nios\n'), + stdin: claudeEdit('src/components/Card.module.css'), files: CACHE_FILES, + }, + { + id: 'hook-config-disabled', verb: 'hook', workspace: 'hook-project', + setup: (ws) => { fs.mkdirSync(`${ws}/.impeccable`, { recursive: true }); fs.writeFileSync(`${ws}/.impeccable/config.json`, JSON.stringify({ hook: { enabled: false } }, null, 2) + '\n'); }, + stdin: claudeEdit('src/components/Card.module.css'), files: CACHE_FILES, + }, + { + id: 'hook-config-ignore-rule', verb: 'hook', workspace: 'hook-project', + setup: (ws) => { fs.mkdirSync(`${ws}/.impeccable`, { recursive: true }); fs.writeFileSync(`${ws}/.impeccable/config.json`, JSON.stringify({ detector: { ignoreRules: ['gradient-text'] } }, null, 2) + '\n'); }, + stdin: claudeEdit('src/components/Card.module.css'), files: CACHE_FILES, + }, + { + id: 'hook-config-ignore-file', verb: 'hook', workspace: 'hook-project', + setup: (ws) => { fs.mkdirSync(`${ws}/.impeccable`, { recursive: true }); fs.writeFileSync(`${ws}/.impeccable/config.json`, JSON.stringify({ detector: { ignoreFiles: ['src/components/**'] } }, null, 2) + '\n'); }, + stdin: claudeEdit('src/components/Card.module.css'), files: CACHE_FILES, + }, + { + id: 'hook-config-per-edit-all', verb: 'hook', workspace: 'hook-project', + setup: (ws) => { fs.mkdirSync(`${ws}/.impeccable`, { recursive: true }); fs.writeFileSync(`${ws}/.impeccable/config.json`, JSON.stringify({ hook: { perEditRules: 'all' } }, null, 2) + '\n'); }, + stdin: claudeEdit('src/components/Card.tsx'), files: CACHE_FILES, + }, + { + id: 'hook-config-max-findings-1', verb: 'hook', workspace: 'hook-project', + setup: (ws) => { fs.mkdirSync(`${ws}/.impeccable`, { recursive: true }); fs.writeFileSync(`${ws}/.impeccable/config.json`, JSON.stringify({ hook: { perEditRules: 'all', limits: { maxFindings: 1, maxChars: 8000 } } }, null, 2) + '\n'); }, + stdin: claudeEdit('src/page.html'), files: CACHE_FILES, + }, + // Session flows + { + id: 'hook-session-fresh-then-pending-then-stop', verb: 'hook', workspace: 'hook-project', files: CACHE_FILES, + steps: [ + { stdin: claudeEdit('src/components/Card.module.css') }, + { stdin: claudeEdit('src/components/Card.module.css') }, + { stdin: claudeEdit('src/components/Clean.tsx') }, + { stdin: claudeEdit('src/components/Clean.tsx') }, + { stdin: stop() }, + { stdin: stop() }, + ], + }, + { + id: 'hook-session-stop-active', verb: 'hook', workspace: 'hook-project', files: CACHE_FILES, + steps: [{ stdin: claudeEdit('src/components/Card.module.css') }, { stdin: stop({ stop_hook_active: true }) }], + }, + { id: 'hook-stop-no-touched', verb: 'hook', workspace: 'hook-project', stdin: stop(), files: CACHE_FILES }, + { + id: 'hook-session-suppression-after-6', verb: 'hook', workspace: 'hook-project', files: CACHE_FILES, + steps: Array.from({ length: 9 }, () => ({ stdin: claudeEdit('src/components/Card.module.css') })), + }, + { + id: 'hook-session-two-sessions', verb: 'hook', workspace: 'hook-project', files: CACHE_FILES, + steps: [ + { stdin: claudeEdit('src/components/Card.module.css') }, + { stdin: claudeEdit('src/components/Card.module.css', { session_id: 's2' }) }, + { stdin: stop({ session_id: 's2' }) }, + ], + }, + + // --- hook-before-edit.mjs (Cursor) --- + { id: 'hbe-write-with-findings', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: 'src/new.css', content: '.t { background: linear-gradient(90deg,#f00,#00f); -webkit-background-clip: text; color: transparent; }\n' } }, files: CACHE_FILES }, + { id: 'hbe-write-clean', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: 'src/new.css', content: '.t { color: #111; }\n' } }, files: CACHE_FILES }, + { id: 'hbe-write-empty-content', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: 'src/new.css', content: '' } }, files: CACHE_FILES }, + { id: 'hbe-write-non-ui', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: 'src/data.json', content: '{}' } }, files: CACHE_FILES }, + { id: 'hbe-edit-projection', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'StrReplace', tool_input: { path: 'src/components/Card.module.css', old_string: '.card {', new_string: '.card-v2 {' } }, files: CACHE_FILES }, + { id: 'hbe-edit-fragment-only', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Edit', tool_input: { path: 'src/components/Clean.tsx', new_string: 'x' } }, files: CACHE_FILES }, + { id: 'hbe-edit-old-missing', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Edit', tool_input: { path: 'src/components/Clean.tsx', old_string: 'NOPE', new_string: 'x' } }, files: CACHE_FILES }, + { id: 'hbe-shell-heredoc', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Shell', tool_input: { command: 'cat > src/x.css <<\'EOF\'\n.t { background: linear-gradient(90deg,#f00,#00f); -webkit-background-clip: text; color: transparent; }\nEOF\n' } }, files: CACHE_FILES }, + { id: 'hbe-shell-redirect-no-content', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Shell', tool_input: { command: 'echo hi > src/x.css' } }, files: CACHE_FILES }, + { id: 'hbe-shell-cp', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Shell', tool_input: { command: 'cp src/components/Card.module.css src/copy.css' } }, files: CACHE_FILES }, + { id: 'hbe-html-engine', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: 'src/new.html', content: '

low contrast

' } }, files: CACHE_FILES }, + { id: 'hbe-no-file', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Shell', tool_input: { command: 'ls' } }, files: CACHE_FILES }, + { id: 'hbe-stdin-empty', verb: 'hook-before-edit', workspace: 'hook-project', stdin: '', files: CACHE_FILES }, + { id: 'hbe-stdin-malformed', verb: 'hook-before-edit', workspace: 'hook-project', stdin: '{', files: CACHE_FILES }, + { id: 'hbe-env-disabled', verb: 'hook-before-edit', workspace: 'hook-project', stdin: '{', env: { IMPECCABLE_HOOK_DISABLED: 'true' }, files: CACHE_FILES }, + { id: 'hbe-outside-project', verb: 'hook-before-edit', workspace: 'hook-project', stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: '/tests/x.css', content: '.a{}' } }, files: CACHE_FILES }, + { + id: 'hbe-denial-downgrade-after-6', verb: 'hook-before-edit', workspace: 'hook-project', files: CACHE_FILES, + steps: Array.from({ length: 8 }, () => ({ stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: 'src/new.css', content: '.t { background: linear-gradient(90deg,#f00,#00f); -webkit-background-clip: text; color: transparent; }\n' } } })), + }, + { + id: 'hbe-native-platform', verb: 'hook-before-edit', workspace: 'hook-project', + setup: (ws) => fs.writeFileSync(`${ws}/PRODUCT.md`, '# P\n\n## Platform\nandroid\n'), + stdin: { hook_event_name: 'preToolUse', conversation_id: 'cv1', workspace_roots: [WS], tool_name: 'Write', tool_input: { path: 'src/new.css', content: '.t { background: linear-gradient(90deg,#f00,#00f); -webkit-background-clip: text; color: transparent; }\n' } }, files: CACHE_FILES, + }, + + // --- hook-admin.mjs --- + { id: 'hadmin-status-default', verb: 'hook-admin', workspace: 'hook-project', args: ['status'], files: CACHE_FILES }, + { id: 'hadmin-status-noargs', verb: 'hook-admin', workspace: 'hook-project', args: [], files: CACHE_FILES }, + { id: 'hadmin-unknown-action', verb: 'hook-admin', workspace: 'hook-project', args: ['bogus'], files: CACHE_FILES }, + { id: 'hadmin-off', verb: 'hook-admin', workspace: 'hook-project', args: ['off'], files: CACHE_FILES }, + { id: 'hadmin-on', verb: 'hook-admin', workspace: 'hook-project', args: ['on'], files: CACHE_FILES }, + { id: 'hadmin-on-twice', verb: 'hook-admin', workspace: 'hook-project', files: CACHE_FILES, steps: [{ args: ['on'] }, { args: ['on'] }, { args: ['status'] }] }, + { id: 'hadmin-off-then-status', verb: 'hook-admin', workspace: 'hook-project', files: CACHE_FILES, steps: [{ args: ['off'] }, { args: ['status'] }, { args: ['on'] }, { args: ['status'] }] }, + { id: 'hadmin-ignore-rule', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-rule', 'Side-Tab', '--reason', 'because', 'reasons'], files: CACHE_FILES }, + { id: 'hadmin-ignore-rule-missing', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-rule'], files: CACHE_FILES }, + { id: 'hadmin-ignore-rule-overused-font', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-rule', 'overused-font'], files: CACHE_FILES }, + { id: 'hadmin-ignore-rule-overused-font-all', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-rule', 'overused-font', '--all-values'], files: CACHE_FILES }, + { id: 'hadmin-ignore-rule-unknown-flag', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-rule', 'side-tab', '--nope'], files: CACHE_FILES }, + { id: 'hadmin-ignore-file', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-file', 'src/legacy/**'], files: CACHE_FILES }, + { id: 'hadmin-ignore-file-local', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-file', 'src/legacy/**', '--local'], files: CACHE_FILES }, + { id: 'hadmin-ignore-file-both-scopes', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-file', 'a/**', '--local', '--shared'], files: CACHE_FILES }, + { id: 'hadmin-ignore-file-reason', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-file', 'a/**', '--reason', 'x'], files: CACHE_FILES }, + { id: 'hadmin-ignore-file-two-globs', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-file', 'a/**', 'b/**'], files: CACHE_FILES }, + { id: 'hadmin-ignore-file-none', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-file'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font', 'Inter'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-multiword', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font', 'Space', 'Grotesk', '--reason', 'user confirmed:', 'brand'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-scoped', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'design-system-font-size', '*', '--file', 'src/z.js', '--files=src/a.js', '--file', 'src/a.js'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-wildcard-unscoped', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'design-system-font-size', '*'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-wildcard-unscoped-font', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font', '*'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-file-missing-glob', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font', 'Inter', '--file'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-file-empty', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font', 'Inter', '--file='], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-file-flag-as-glob', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font', 'Inter', '--file', '--local'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-local', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font', 'Inter', '--local'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-missing', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-unknown-flag', verb: 'hook-admin', workspace: 'hook-project', args: ['ignore-value', 'overused-font', 'Inter', '--wat'], files: CACHE_FILES }, + { id: 'hadmin-ignore-value-twice-updates-reason', verb: 'hook-admin', workspace: 'hook-project', files: CACHE_FILES, steps: [{ args: ['ignore-value', 'overused-font', 'Inter'] }, { args: ['ignore-value', 'overused-font', 'inter', '--reason=second'] }, { args: ['status'] }] }, + { id: 'hadmin-reset-empty', verb: 'hook-admin', workspace: 'hook-project', args: ['reset'], files: CACHE_FILES }, + { id: 'hadmin-full-cycle', verb: 'hook-admin', workspace: 'hook-project', files: CACHE_FILES, steps: [{ args: ['ignore-rule', 'side-tab'] }, { args: ['ignore-file', 'x/**', '--local'] }, { args: ['ignore-value', 'overused-font', 'Inter'] }, { args: ['status'] }, { args: ['reset'] }, { args: ['status'] }] }, + { + id: 'hadmin-legacy-migration', verb: 'hook-admin', workspace: 'hook-project', files: CACHE_FILES, + setup: (ws) => { fs.mkdirSync(`${ws}/.impeccable`, { recursive: true }); fs.writeFileSync(`${ws}/.impeccable/config.json`, JSON.stringify({ hook: { enabled: true, quiet: true, ignoreRules: ['legacy-rule'], advisoryRules: 'include', consent: 'accepted' }, other: { keep: 1 } }, null, 2) + '\n'); }, + steps: [{ args: ['status'] }, { args: ['ignore-rule', 'side-tab'] }, { args: ['status'] }], + }, + { + id: 'hadmin-malformed-config', verb: 'hook-admin', workspace: 'hook-project', files: CACHE_FILES, + setup: (ws) => { fs.mkdirSync(`${ws}/.impeccable`, { recursive: true }); fs.writeFileSync(`${ws}/.impeccable/config.json`, '{ nope'); }, + steps: [{ args: ['status'] }, { args: ['off'] }], + }, + { + id: 'hadmin-on-repairs-existing-manifest', verb: 'hook-admin', workspace: 'hook-project', files: CACHE_FILES, + setup: (ws) => { fs.mkdirSync(`${ws}/.claude`, { recursive: true }); fs.writeFileSync(`${ws}/.claude/settings.local.json`, JSON.stringify({ permissions: { allow: ['Bash(ls)'] }, hooks: { PostToolUse: [{ matcher: 'Edit', hooks: [{ type: 'command', command: 'node old/skills/impeccable/scripts/hook.mjs' }] }, { matcher: 'Write', hooks: [{ type: 'command', command: 'echo other' }] }] } }, null, 2) + '\n'); }, + args: ['on'], + }, + { + id: 'hadmin-on-malformed-manifest-backup', verb: 'hook-admin', workspace: 'hook-project', files: [...CACHE_FILES, '.cursor/hooks.json.bak'], + setup: (ws) => { fs.mkdirSync(`${ws}/.cursor`, { recursive: true }); fs.writeFileSync(`${ws}/.cursor/hooks.json`, '{ broken'); }, + args: ['on'], + }, +]; diff --git a/tests/oracle/golden/hadmin-full-cycle.json b/tests/oracle/golden/hadmin-full-cycle.json new file mode 100644 index 000000000..3dfcb4a59 --- /dev/null +++ b/tests/oracle/golden/hadmin-full-cycle.json @@ -0,0 +1,41 @@ +{ + "steps": [ + { + "stdout": "Added \"side-tab\" to detector.ignoreRules. Current: side-tab\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Added \"x/**\" to local detector.ignoreFiles (.impeccable/config.local.json). Current: x/**\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Added overused-font=inter to shared detector.ignoreValues (.impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json\n local file: .impeccable/config.local.json\n ignoreRules: side-tab\n ignoreFiles: x/**\n ignoreValues: overused-font=inter\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Reset design hook config and cache (removed: .impeccable/config.json, .impeccable/config.local.json).\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json (using defaults; file not present)\n local file: .impeccable/config.local.json (not present)\n ignoreRules: (none)\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-file-both-scopes.json b/tests/oracle/golden/hadmin-ignore-file-both-scopes.json new file mode 100644 index 000000000..969d29cb5 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-file-both-scopes.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Pass only one scope flag: --shared or --local\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-file-local.json b/tests/oracle/golden/hadmin-ignore-file-local.json new file mode 100644 index 000000000..ad772b821 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-file-local.json @@ -0,0 +1,9 @@ +{ + "stdout": "Added \"src/legacy/**\" to local detector.ignoreFiles (.impeccable/config.local.json). Current: src/legacy/**\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.local.json": "{\n \"detector\": {\n \"ignoreRules\": [],\n \"ignoreFiles\": [\n \"src/legacy/**\"\n ],\n \"ignoreValues\": []\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-ignore-file-none.json b/tests/oracle/golden/hadmin-ignore-file-none.json new file mode 100644 index 000000000..c5dc0c323 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-file-none.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Pass a glob, e.g. /impeccable hooks ignore-file \"src/legacy/**\"\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-file-reason.json b/tests/oracle/golden/hadmin-ignore-file-reason.json new file mode 100644 index 000000000..ec27c2bd7 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-file-reason.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: --reason is not supported for ignore-file because detector.ignoreFiles stores globs only; use ignore-value when a documented rule-specific exception fits\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-file-two-globs.json b/tests/oracle/golden/hadmin-ignore-file-two-globs.json new file mode 100644 index 000000000..90eae9b00 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-file-two-globs.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Pass exactly one glob to ignore-file\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-file.json b/tests/oracle/golden/hadmin-ignore-file.json new file mode 100644 index 000000000..5bc5e046a --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-file.json @@ -0,0 +1,9 @@ +{ + "stdout": "Added \"src/legacy/**\" to shared detector.ignoreFiles (.impeccable/config.json). Current: src/legacy/**\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreRules\": [],\n \"ignoreFiles\": [\n \"src/legacy/**\"\n ],\n \"ignoreValues\": []\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-ignore-rule-missing.json b/tests/oracle/golden/hadmin-ignore-rule-missing.json new file mode 100644 index 000000000..0e5f9a179 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-rule-missing.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Pass a rule id, e.g. /impeccable hooks ignore-rule side-tab\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-rule-overused-font-all.json b/tests/oracle/golden/hadmin-ignore-rule-overused-font-all.json new file mode 100644 index 000000000..fd72aa7a2 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-rule-overused-font-all.json @@ -0,0 +1,9 @@ +{ + "stdout": "Added \"overused-font\" to detector.ignoreRules. Current: overused-font\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreRules\": [\n \"overused-font\"\n ],\n \"ignoreFiles\": [],\n \"ignoreValues\": []\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-ignore-rule-overused-font.json b/tests/oracle/golden/hadmin-ignore-rule-overused-font.json new file mode 100644 index 000000000..8e9f8bc0b --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-rule-overused-font.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: overused-font is value-specific by default. Use /impeccable hooks ignore-value overused-font for a confirmed font, or /impeccable hooks ignore-rule overused-font --all-values only when the user asked to ignore overused fonts generally.\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-rule-unknown-flag.json b/tests/oracle/golden/hadmin-ignore-rule-unknown-flag.json new file mode 100644 index 000000000..e1684de0c --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-rule-unknown-flag.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Unknown ignore-rule flag: --nope\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-rule.json b/tests/oracle/golden/hadmin-ignore-rule.json new file mode 100644 index 000000000..d532418c4 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-rule.json @@ -0,0 +1,9 @@ +{ + "stdout": "Added \"side-tab\" to detector.ignoreRules. Current: side-tab\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreRules\": [\n \"side-tab\"\n ],\n \"ignoreFiles\": [],\n \"ignoreValues\": []\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-ignore-value-file-empty.json b/tests/oracle/golden/hadmin-ignore-value-file-empty.json new file mode 100644 index 000000000..f198e870a --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-file-empty.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: --file requires a non-empty glob\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-value-file-flag-as-glob.json b/tests/oracle/golden/hadmin-ignore-value-file-flag-as-glob.json new file mode 100644 index 000000000..7fcbe5eba --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-file-flag-as-glob.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: --file requires a glob, got the flag --local\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-value-file-missing-glob.json b/tests/oracle/golden/hadmin-ignore-value-file-missing-glob.json new file mode 100644 index 000000000..62384999b --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-file-missing-glob.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: --file requires a glob\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-value-local.json b/tests/oracle/golden/hadmin-ignore-value-local.json new file mode 100644 index 000000000..62f88546e --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-local.json @@ -0,0 +1,9 @@ +{ + "stdout": "Added overused-font=inter to local detector.ignoreValues (.impeccable/config.local.json).\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.local.json": "{\n \"detector\": {\n \"ignoreRules\": [],\n \"ignoreFiles\": [],\n \"ignoreValues\": [\n {\n \"rule\": \"overused-font\",\n \"value\": \"inter\",\n \"createdAt\": \"\"\n }\n ]\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-ignore-value-missing.json b/tests/oracle/golden/hadmin-ignore-value-missing.json new file mode 100644 index 000000000..452fc99f1 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-missing.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Pass a rule id and value, e.g. /impeccable hooks ignore-value overused-font Inter\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-value-multiword.json b/tests/oracle/golden/hadmin-ignore-value-multiword.json new file mode 100644 index 000000000..967568079 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-multiword.json @@ -0,0 +1,9 @@ +{ + "stdout": "Added overused-font=space grotesk to shared detector.ignoreValues (.impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreRules\": [],\n \"ignoreFiles\": [],\n \"ignoreValues\": [\n {\n \"rule\": \"overused-font\",\n \"value\": \"space grotesk\",\n \"createdAt\": \"\",\n \"reason\": \"user confirmed: brand\"\n }\n ]\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-ignore-value-scoped.json b/tests/oracle/golden/hadmin-ignore-value-scoped.json new file mode 100644 index 000000000..3e3d69407 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-scoped.json @@ -0,0 +1,9 @@ +{ + "stdout": "Added design-system-font-size=* scoped to src/a.js, src/z.js to shared detector.ignoreValues (.impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreRules\": [],\n \"ignoreFiles\": [],\n \"ignoreValues\": [\n {\n \"rule\": \"design-system-font-size\",\n \"value\": \"*\",\n \"files\": [\n \"src/a.js\",\n \"src/z.js\"\n ],\n \"createdAt\": \"\"\n }\n ]\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-ignore-value-twice-updates-reason.json b/tests/oracle/golden/hadmin-ignore-value-twice-updates-reason.json new file mode 100644 index 000000000..977474242 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-twice-updates-reason.json @@ -0,0 +1,25 @@ +{ + "steps": [ + { + "stdout": "Added overused-font=inter to shared detector.ignoreValues (.impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Added overused-font=inter to shared detector.ignoreValues (.impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json\n local file: .impeccable/config.local.json (not present)\n ignoreRules: (none)\n ignoreFiles: (none)\n ignoreValues: overused-font=inter\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreRules\": [],\n \"ignoreFiles\": [],\n \"ignoreValues\": [\n {\n \"rule\": \"overused-font\",\n \"value\": \"inter\",\n \"createdAt\": \"\",\n \"reason\": \"second\"\n }\n ]\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-ignore-value-unknown-flag.json b/tests/oracle/golden/hadmin-ignore-value-unknown-flag.json new file mode 100644 index 000000000..dea9f9cfb --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-unknown-flag.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Unknown ignore-value flag: --wat\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-value-wildcard-unscoped-font.json b/tests/oracle/golden/hadmin-ignore-value-wildcard-unscoped-font.json new file mode 100644 index 000000000..7b203d969 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-wildcard-unscoped-font.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Wildcard value ignores must be scoped with --file , e.g. /impeccable hooks ignore-value design-system-font-size \"*\" --file \"src/widget.js\". To suppress the rule project-wide use /impeccable hooks ignore-rule overused-font --all-values.\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-value-wildcard-unscoped.json b/tests/oracle/golden/hadmin-ignore-value-wildcard-unscoped.json new file mode 100644 index 000000000..cee1eb547 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value-wildcard-unscoped.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: Wildcard value ignores must be scoped with --file , e.g. /impeccable hooks ignore-value design-system-font-size \"*\" --file \"src/widget.js\". To suppress the rule project-wide use /impeccable hooks ignore-rule design-system-font-size.\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-ignore-value.json b/tests/oracle/golden/hadmin-ignore-value.json new file mode 100644 index 000000000..049cf8c28 --- /dev/null +++ b/tests/oracle/golden/hadmin-ignore-value.json @@ -0,0 +1,9 @@ +{ + "stdout": "Added overused-font=inter to shared detector.ignoreValues (.impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreRules\": [],\n \"ignoreFiles\": [],\n \"ignoreValues\": [\n {\n \"rule\": \"overused-font\",\n \"value\": \"inter\",\n \"createdAt\": \"\"\n }\n ]\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-legacy-migration.json b/tests/oracle/golden/hadmin-legacy-migration.json new file mode 100644 index 000000000..7faf7ab97 --- /dev/null +++ b/tests/oracle/golden/hadmin-legacy-migration.json @@ -0,0 +1,25 @@ +{ + "steps": [ + { + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json\n local file: .impeccable/config.local.json (not present)\n ignoreRules: legacy-rule\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Added \"side-tab\" to detector.ignoreRules. Current: legacy-rule, side-tab\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json\n local file: .impeccable/config.local.json (not present)\n ignoreRules: legacy-rule, side-tab\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": true,\n \"quiet\": true,\n \"consent\": \"accepted\"\n },\n \"other\": {\n \"keep\": 1\n },\n \"detector\": {\n \"ignoreRules\": [\n \"legacy-rule\",\n \"side-tab\"\n ],\n \"ignoreFiles\": [],\n \"ignoreValues\": [],\n \"advisoryRules\": \"include\"\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-malformed-config.json b/tests/oracle/golden/hadmin-malformed-config.json new file mode 100644 index 000000000..5bf2ad84a --- /dev/null +++ b/tests/oracle/golden/hadmin-malformed-config.json @@ -0,0 +1,19 @@ +{ + "steps": [ + { + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json (malformed; ignored)\n local file: .impeccable/config.local.json (not present)\n ignoreRules: (none)\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Design hook disabled for this project (wrote .impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": false,\n \"limits\": {\n \"maxFindings\": 5,\n \"maxChars\": 8000\n }\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-off-then-status.json b/tests/oracle/golden/hadmin-off-then-status.json new file mode 100644 index 000000000..6730cfafa --- /dev/null +++ b/tests/oracle/golden/hadmin-off-then-status.json @@ -0,0 +1,34 @@ +{ + "steps": [ + { + "stdout": "Design hook disabled for this project (wrote .impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Impeccable design hook\n state: disabled\n shared file: .impeccable/config.json\n local file: .impeccable/config.local.json (not present)\n ignoreRules: (none)\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Design hook enabled for this project (wrote .impeccable/config.json). Recorded local hook consent in .impeccable/config.local.json. Installed or repaired hook manifests for: .claude, .cursor.\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json\n local file: .impeccable/config.local.json\n ignoreRules: (none)\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".claude/settings.local.json": "{\n \"description\": \"Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.\",\n \"hooks\": {\n \"PostToolUse\": [\n {\n \"matcher\": \"Edit|Write|MultiEdit\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 5,\n \"statusMessage\": \"Checking UI changes\"\n }\n ]\n }\n ],\n \"Stop\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 30,\n \"statusMessage\": \"Design deep pass\"\n }\n ]\n }\n ]\n }\n}\n", + ".cursor/hooks.json": "{\n \"version\": 1,\n \"hooks\": {\n \"preToolUse\": [\n {\n \"command\": \"node \\\".cursor/skills/impeccable/scripts/hook-before-edit.mjs\\\"\",\n \"timeout\": 5\n }\n ]\n }\n}\n", + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": true,\n \"limits\": {\n \"maxFindings\": 5,\n \"maxChars\": 8000\n }\n }\n}\n", + ".impeccable/config.local.json": "{\n \"hook\": {\n \"consent\": \"accepted\"\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-off.json b/tests/oracle/golden/hadmin-off.json new file mode 100644 index 000000000..d9f684ed3 --- /dev/null +++ b/tests/oracle/golden/hadmin-off.json @@ -0,0 +1,9 @@ +{ + "stdout": "Design hook disabled for this project (wrote .impeccable/config.json).\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": false,\n \"limits\": {\n \"maxFindings\": 5,\n \"maxChars\": 8000\n }\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-on-malformed-manifest-backup.json b/tests/oracle/golden/hadmin-on-malformed-manifest-backup.json new file mode 100644 index 000000000..0a2881f08 --- /dev/null +++ b/tests/oracle/golden/hadmin-on-malformed-manifest-backup.json @@ -0,0 +1,13 @@ +{ + "stdout": "Design hook enabled for this project (wrote .impeccable/config.json). Recorded local hook consent in .impeccable/config.local.json. Installed or repaired hook manifests for: .claude, .cursor. Backed up malformed manifest(s): .cursor/hooks.json.bak.\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".claude/settings.local.json": "{\n \"description\": \"Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.\",\n \"hooks\": {\n \"PostToolUse\": [\n {\n \"matcher\": \"Edit|Write|MultiEdit\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 5,\n \"statusMessage\": \"Checking UI changes\"\n }\n ]\n }\n ],\n \"Stop\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 30,\n \"statusMessage\": \"Design deep pass\"\n }\n ]\n }\n ]\n }\n}\n", + ".cursor/hooks.json": "{\n \"version\": 1,\n \"hooks\": {\n \"preToolUse\": [\n {\n \"command\": \"node \\\".cursor/skills/impeccable/scripts/hook-before-edit.mjs\\\"\",\n \"timeout\": 5\n }\n ]\n }\n}\n", + ".cursor/hooks.json.bak": "{ broken", + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": true,\n \"limits\": {\n \"maxFindings\": 5,\n \"maxChars\": 8000\n }\n }\n}\n", + ".impeccable/config.local.json": "{\n \"hook\": {\n \"consent\": \"accepted\"\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-on-repairs-existing-manifest.json b/tests/oracle/golden/hadmin-on-repairs-existing-manifest.json new file mode 100644 index 000000000..39259a3c8 --- /dev/null +++ b/tests/oracle/golden/hadmin-on-repairs-existing-manifest.json @@ -0,0 +1,12 @@ +{ + "stdout": "Design hook enabled for this project (wrote .impeccable/config.json). Recorded local hook consent in .impeccable/config.local.json. Installed or repaired hook manifests for: .claude, .cursor.\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".claude/settings.local.json": "{\n \"permissions\": {\n \"allow\": [\n \"Bash(ls)\"\n ]\n },\n \"hooks\": {\n \"PostToolUse\": [\n {\n \"matcher\": \"Write\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"echo other\"\n }\n ]\n },\n {\n \"matcher\": \"Edit|Write|MultiEdit\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 5,\n \"statusMessage\": \"Checking UI changes\"\n }\n ]\n }\n ],\n \"Stop\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 30,\n \"statusMessage\": \"Design deep pass\"\n }\n ]\n }\n ]\n },\n \"description\": \"Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.\"\n}\n", + ".cursor/hooks.json": "{\n \"version\": 1,\n \"hooks\": {\n \"preToolUse\": [\n {\n \"command\": \"node \\\".cursor/skills/impeccable/scripts/hook-before-edit.mjs\\\"\",\n \"timeout\": 5\n }\n ]\n }\n}\n", + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": true,\n \"limits\": {\n \"maxFindings\": 5,\n \"maxChars\": 8000\n }\n }\n}\n", + ".impeccable/config.local.json": "{\n \"hook\": {\n \"consent\": \"accepted\"\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-on-twice.json b/tests/oracle/golden/hadmin-on-twice.json new file mode 100644 index 000000000..2918d4f3b --- /dev/null +++ b/tests/oracle/golden/hadmin-on-twice.json @@ -0,0 +1,28 @@ +{ + "steps": [ + { + "stdout": "Design hook enabled for this project (wrote .impeccable/config.json). Recorded local hook consent in .impeccable/config.local.json. Installed or repaired hook manifests for: .claude, .cursor.\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Design hook enabled for this project (wrote .impeccable/config.json). Recorded local hook consent in .impeccable/config.local.json. Hook manifests already installed for: .claude, .cursor.\n", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json\n local file: .impeccable/config.local.json\n ignoreRules: (none)\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".claude/settings.local.json": "{\n \"description\": \"Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.\",\n \"hooks\": {\n \"PostToolUse\": [\n {\n \"matcher\": \"Edit|Write|MultiEdit\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 5,\n \"statusMessage\": \"Checking UI changes\"\n }\n ]\n }\n ],\n \"Stop\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 30,\n \"statusMessage\": \"Design deep pass\"\n }\n ]\n }\n ]\n }\n}\n", + ".cursor/hooks.json": "{\n \"version\": 1,\n \"hooks\": {\n \"preToolUse\": [\n {\n \"command\": \"node \\\".cursor/skills/impeccable/scripts/hook-before-edit.mjs\\\"\",\n \"timeout\": 5\n }\n ]\n }\n}\n", + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": true,\n \"limits\": {\n \"maxFindings\": 5,\n \"maxChars\": 8000\n }\n }\n}\n", + ".impeccable/config.local.json": "{\n \"hook\": {\n \"consent\": \"accepted\"\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-on.json b/tests/oracle/golden/hadmin-on.json new file mode 100644 index 000000000..741f1d483 --- /dev/null +++ b/tests/oracle/golden/hadmin-on.json @@ -0,0 +1,12 @@ +{ + "stdout": "Design hook enabled for this project (wrote .impeccable/config.json). Recorded local hook consent in .impeccable/config.local.json. Installed or repaired hook manifests for: .claude, .cursor.\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".claude/settings.local.json": "{\n \"description\": \"Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.\",\n \"hooks\": {\n \"PostToolUse\": [\n {\n \"matcher\": \"Edit|Write|MultiEdit\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 5,\n \"statusMessage\": \"Checking UI changes\"\n }\n ]\n }\n ],\n \"Stop\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"node \\\"${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs\\\"\",\n \"timeout\": 30,\n \"statusMessage\": \"Design deep pass\"\n }\n ]\n }\n ]\n }\n}\n", + ".cursor/hooks.json": "{\n \"version\": 1,\n \"hooks\": {\n \"preToolUse\": [\n {\n \"command\": \"node \\\".cursor/skills/impeccable/scripts/hook-before-edit.mjs\\\"\",\n \"timeout\": 5\n }\n ]\n }\n}\n", + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": true,\n \"limits\": {\n \"maxFindings\": 5,\n \"maxChars\": 8000\n }\n }\n}\n", + ".impeccable/config.local.json": "{\n \"hook\": {\n \"consent\": \"accepted\"\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hadmin-reset-empty.json b/tests/oracle/golden/hadmin-reset-empty.json new file mode 100644 index 000000000..f432dfd96 --- /dev/null +++ b/tests/oracle/golden/hadmin-reset-empty.json @@ -0,0 +1,7 @@ +{ + "stdout": "No hook config or cache to remove. Already at defaults.\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-status-default.json b/tests/oracle/golden/hadmin-status-default.json new file mode 100644 index 000000000..67090f654 --- /dev/null +++ b/tests/oracle/golden/hadmin-status-default.json @@ -0,0 +1,7 @@ +{ + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json (using defaults; file not present)\n local file: .impeccable/config.local.json (not present)\n ignoreRules: (none)\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-status-noargs.json b/tests/oracle/golden/hadmin-status-noargs.json new file mode 100644 index 000000000..67090f654 --- /dev/null +++ b/tests/oracle/golden/hadmin-status-noargs.json @@ -0,0 +1,7 @@ +{ + "stdout": "Impeccable design hook\n state: enabled\n shared file: .impeccable/config.json (using defaults; file not present)\n local file: .impeccable/config.local.json (not present)\n ignoreRules: (none)\n ignoreFiles: (none)\n ignoreValues: (none)\n maxFindings: 5\n maxChars: 8000\n env override: unset\n cache file: .impeccable/hook.cache.json (not present)\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hadmin-unknown-action.json b/tests/oracle/golden/hadmin-unknown-action.json new file mode 100644 index 000000000..42479f487 --- /dev/null +++ b/tests/oracle/golden/hadmin-unknown-action.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Unknown action: bogus\nValid: status, on, off, ignore-rule, ignore-file, ignore-value, reset\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-denial-downgrade-after-6.json b/tests/oracle/golden/hbe-denial-downgrade-after-6.json new file mode 100644 index 000000000..1ef14d8f9 --- /dev/null +++ b/tests/oracle/golden/hbe-denial-downgrade-after-6.json @@ -0,0 +1,55 @@ +{ + "steps": [ + { + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\"}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\"}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\"}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\"}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\"}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"permission\":\"allow\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\\n\\nThis is the 7th repeated denial for the same file and finding signature, so Impeccable is allowing this write to avoid a loop. Reconsider the issue immediately after the tool runs.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\\n\\nThis is the 7th repeated denial for the same file and finding signature, so Impeccable is allowing this write to avoid a loop. Reconsider the issue immediately after the tool runs.\"}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"permission\":\"allow\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\\n\\nThis is the 8th repeated denial for the same file and finding signature, so Impeccable is allowing this write to avoid a loop. Reconsider the issue immediately after the tool runs.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\\n\\nThis is the 8th repeated denial for the same file and finding signature, so Impeccable is allowing this write to avoid a loop. Reconsider the issue immediately after the tool runs.\"}", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"cv1\":{\"updatedAt\": ,\"files\":{\"/src/new.css\":{\"editCount\":0,\"findings\":[],\"cursorDenials\":{\"gradient-text:1\":8}}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hbe-edit-fragment-only.json b/tests/oracle/golden/hbe-edit-fragment-only.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-edit-fragment-only.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-edit-old-missing.json b/tests/oracle/golden/hbe-edit-old-missing.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-edit-old-missing.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-edit-projection.json b/tests/oracle/golden/hbe-edit-projection.json new file mode 100644 index 000000000..c9c8d67d2 --- /dev/null +++ b/tests/oracle/golden/hbe-edit-projection.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/components/Card.module.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/components/Card.module.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"cv1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":0,\"findings\":[],\"cursorDenials\":{\"gradient-text:2|side-tab:1\":1}}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hbe-env-disabled.json b/tests/oracle/golden/hbe-env-disabled.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-env-disabled.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-html-engine.json b/tests/oracle/golden/hbe-html-engine.json new file mode 100644 index 000000000..95a5ec555 --- /dev/null +++ b/tests/oracle/golden/hbe-html-engine.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.html (1 issue(s)):\\n- [low-contrast] Low contrast text. Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.html (1 issue(s)):\\n- [low-contrast] Low contrast text. Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"cv1\":{\"updatedAt\": ,\"files\":{\"/src/new.html\":{\"editCount\":0,\"findings\":[],\"cursorDenials\":{\"low-contrast:0\":1}}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hbe-native-platform.json b/tests/oracle/golden/hbe-native-platform.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-native-platform.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-no-file.json b/tests/oracle/golden/hbe-no-file.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-no-file.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-outside-project.json b/tests/oracle/golden/hbe-outside-project.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-outside-project.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-shell-cp.json b/tests/oracle/golden/hbe-shell-cp.json new file mode 100644 index 000000000..a1e5b5e26 --- /dev/null +++ b/tests/oracle/golden/hbe-shell-cp.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/copy.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/copy.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"cv1\":{\"updatedAt\": ,\"files\":{\"/src/copy.css\":{\"editCount\":0,\"findings\":[],\"cursorDenials\":{\"gradient-text:2|side-tab:1\":1}}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hbe-shell-heredoc.json b/tests/oracle/golden/hbe-shell-heredoc.json new file mode 100644 index 000000000..047ce7f7d --- /dev/null +++ b/tests/oracle/golden/hbe-shell-heredoc.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/x.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/x.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"cv1\":{\"updatedAt\": ,\"files\":{\"/src/x.css\":{\"editCount\":0,\"findings\":[],\"cursorDenials\":{\"gradient-text:1\":1}}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hbe-shell-redirect-no-content.json b/tests/oracle/golden/hbe-shell-redirect-no-content.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-shell-redirect-no-content.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-stdin-empty.json b/tests/oracle/golden/hbe-stdin-empty.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-stdin-empty.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-stdin-malformed.json b/tests/oracle/golden/hbe-stdin-malformed.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-stdin-malformed.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-write-clean.json b/tests/oracle/golden/hbe-write-clean.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-write-clean.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-write-empty-content.json b/tests/oracle/golden/hbe-write-empty-content.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-write-empty-content.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-write-non-ui.json b/tests/oracle/golden/hbe-write-non-ui.json new file mode 100644 index 000000000..06fbb2cd1 --- /dev/null +++ b/tests/oracle/golden/hbe-write-non-ui.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"permission\":\"allow\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hbe-write-with-findings.json b/tests/oracle/golden/hbe-write-with-findings.json new file mode 100644 index 000000000..4d9f3d59b --- /dev/null +++ b/tests/oracle/golden/hbe-write-with-findings.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"permission\":\"deny\",\"user_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\",\"agent_message\":\"[impeccable@1] Impeccable design hook blocked this write before it landed. Design hook findings requiring review in src/new.css (1 issue(s)):\\n- L1 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"cv1\":{\"updatedAt\": ,\"files\":{\"/src/new.css\":{\"editCount\":0,\"findings\":[],\"cursorDenials\":{\"gradient-text:1\":1}}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-audit-log.json b/tests/oracle/golden/hook-audit-log.json new file mode 100644 index 000000000..8ed6ea329 --- /dev/null +++ b/tests/oracle/golden/hook-audit-log.json @@ -0,0 +1,10 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/audit.ndjson": "{\"ts\":\"\",\"event\":\"PostToolUse\",\"harness\":\"claude\",\"cwd\":\"\",\"session\":\"s1\",\"tool\":\"Edit\",\"file\":\"/src/components/Card.module.css\",\"ext\":\".css\",\"editCount\":1,\"findings\":2,\"freshFindings\":1,\"deferred\":1,\"emitted\":true,\"freshFiles\":1,\"chars\":1250,\"durationMs\":10}\n", + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-config-disabled.json b/tests/oracle/golden/hook-config-disabled.json new file mode 100644 index 000000000..ce9ae4a2a --- /dev/null +++ b/tests/oracle/golden/hook-config-disabled.json @@ -0,0 +1,9 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"hook\": {\n \"enabled\": false\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hook-config-ignore-file.json b/tests/oracle/golden/hook-config-ignore-file.json new file mode 100644 index 000000000..65bdb920b --- /dev/null +++ b/tests/oracle/golden/hook-config-ignore-file.json @@ -0,0 +1,9 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreFiles\": [\n \"src/components/**\"\n ]\n }\n}\n" + } +} diff --git a/tests/oracle/golden/hook-config-ignore-rule.json b/tests/oracle/golden/hook-config-ignore-rule.json new file mode 100644 index 000000000..ed74cda70 --- /dev/null +++ b/tests/oracle/golden/hook-config-ignore-rule.json @@ -0,0 +1,10 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Card.module.css. No deterministic design-quality issues found. That does not mean the design is good: keep following the project design system and the impeccable skill guidance.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"detector\": {\n \"ignoreRules\": [\n \"gradient-text\"\n ]\n }\n}\n", + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[],\"cleanAcked\":true}}}}}" + } +} diff --git a/tests/oracle/golden/hook-config-max-findings-1.json b/tests/oracle/golden/hook-config-max-findings-1.json new file mode 100644 index 000000000..601ac81f2 --- /dev/null +++ b/tests/oracle/golden/hook-config-max-findings-1.json @@ -0,0 +1,10 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/page.html (4 issue(s)):\\n- [low-contrast] Low contrast text. Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\\n... and 3 more (see /impeccable audit).\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"hook\": {\n \"perEditRules\": \"all\",\n \"limits\": {\n \"maxFindings\": 1,\n \"maxChars\": 8000\n }\n }\n}\n", + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/page.html\":{\"editCount\":1,\"findings\":[\"low-contrast:0:1.3:1 (need 4.5:1) — text #666666 on #555555\",\"low-contrast:0:1.1:1 (need 4.5:1) — text #000000 on #111111\",\"dark-glow:0:Zero-offset box-shadow glow (#8b5cf6)\",\"cramped-padding:0:
\\\"hero\\\": children flush against bg on all sides (no inset)\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-config-per-edit-all.json b/tests/oracle/golden/hook-config-per-edit-all.json new file mode 100644 index 000000000..52e1de6d1 --- /dev/null +++ b/tests/oracle/golden/hook-config-per-edit-all.json @@ -0,0 +1,10 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review across 2 files (3 issue(s)):\\nsrc/components/Card.tsx (1 issue(s)):\\n- L5 [bounce-easing] Bounce or elastic easing. Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead. If intentional: `ignore-value bounce-easing 'cubic-bezier(0.68, -0.55, 0.265, 1.55)'`.\\nsrc/components/Card.module.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/config.json": "{\n \"hook\": {\n \"perEditRules\": \"all\"\n }\n}\n", + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.tsx\":{\"editCount\":1,\"findings\":[\"bounce-easing:5:cubic-bezier(0.68, -0.55, 0.265, 1.55)\"]},\"/src/components/Card.module.css\":{\"editCount\":0,\"findings\":[\"side-tab:1\",\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-edit-clean-tsx.json b/tests/oracle/golden/hook-edit-clean-tsx.json new file mode 100644 index 000000000..477f130ce --- /dev/null +++ b/tests/oracle/golden/hook-edit-clean-tsx.json @@ -0,0 +1,7 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Clean.tsx. No deterministic design-quality issues found. That does not mean the design is good: keep following the project design system and the impeccable skill guidance.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-edit-css-fresh.json b/tests/oracle/golden/hook-edit-css-fresh.json new file mode 100644 index 000000000..159fbd175 --- /dev/null +++ b/tests/oracle/golden/hook-edit-css-fresh.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-edit-generated.json b/tests/oracle/golden/hook-edit-generated.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-edit-generated.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-edit-html-fresh.json b/tests/oracle/golden/hook-edit-html-fresh.json new file mode 100644 index 000000000..354cbd2a9 --- /dev/null +++ b/tests/oracle/golden/hook-edit-html-fresh.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/page.html (3 issue(s)):\\n- [low-contrast] Low contrast text. Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\\n- [low-contrast] Low contrast text.\\n- [dark-glow] Glowing shadow accents. Colored glow shadows — a zero-offset chromatic halo (box- or text-shadow) on any background, or any colored blurred shadow on a dark background — are the default \\\"cool\\\" look of AI-generated UIs. Use neutral elevation shadows and subtle, purposeful lighting instead.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/page.html\":{\"editCount\":1,\"findings\":[\"low-contrast:0:1.3:1 (need 4.5:1) — text #666666 on #555555\",\"low-contrast:0:1.1:1 (need 4.5:1) — text #000000 on #111111\",\"dark-glow:0:Zero-offset box-shadow glow (#8b5cf6)\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-edit-missing-file.json b/tests/oracle/golden/hook-edit-missing-file.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-edit-missing-file.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-edit-multiedit.json b/tests/oracle/golden/hook-edit-multiedit.json new file mode 100644 index 000000000..6a07799e0 --- /dev/null +++ b/tests/oracle/golden/hook-edit-multiedit.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.tsx\":{\"editCount\":1,\"findings\":[],\"cleanAcked\":true},\"/src/components/Card.module.css\":{\"editCount\":0,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-edit-no-file-path.json b/tests/oracle/golden/hook-edit-no-file-path.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-edit-no-file-path.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-edit-non-ui-ts.json b/tests/oracle/golden/hook-edit-non-ui-ts.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-edit-non-ui-ts.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-edit-outside-project.json b/tests/oracle/golden/hook-edit-outside-project.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-edit-outside-project.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-edit-sensitive.json b/tests/oracle/golden/hook-edit-sensitive.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-edit-sensitive.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-edit-tsx-fresh.json b/tests/oracle/golden/hook-edit-tsx-fresh.json new file mode 100644 index 000000000..6a07799e0 --- /dev/null +++ b/tests/oracle/golden/hook-edit-tsx-fresh.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.tsx\":{\"editCount\":1,\"findings\":[],\"cleanAcked\":true},\"/src/components/Card.module.css\":{\"editCount\":0,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-edit-write-tool.json b/tests/oracle/golden/hook-edit-write-tool.json new file mode 100644 index 000000000..6a07799e0 --- /dev/null +++ b/tests/oracle/golden/hook-edit-write-tool.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.tsx\":{\"editCount\":1,\"findings\":[],\"cleanAcked\":true},\"/src/components/Card.module.css\":{\"editCount\":0,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-env-disabled.json b/tests/oracle/golden/hook-env-disabled.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-env-disabled.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-env-quiet.json b/tests/oracle/golden/hook-env-quiet.json new file mode 100644 index 000000000..7b081bcb5 --- /dev/null +++ b/tests/oracle/golden/hook-env-quiet.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.tsx\":{\"editCount\":1,\"findings\":[]},\"/src/components/Card.module.css\":{\"editCount\":0,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-env-reentrant.json b/tests/oracle/golden/hook-env-reentrant.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-env-reentrant.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-harness-codex-apply-patch.json b/tests/oracle/golden/hook-harness-codex-apply-patch.json new file mode 100644 index 000000000..910e89fcc --- /dev/null +++ b/tests/oracle/golden/hook-harness-codex-apply-patch.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"c1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-harness-cursor-shaped.json b/tests/oracle/golden/hook-harness-cursor-shaped.json new file mode 100644 index 000000000..55a92f774 --- /dev/null +++ b/tests/oracle/golden/hook-harness-cursor-shaped.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"additional_context\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"cv1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"side-tab:1\",\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-harness-forced-github.json b/tests/oracle/golden/hook-harness-forced-github.json new file mode 100644 index 000000000..37b22984f --- /dev/null +++ b/tests/oracle/golden/hook-harness-forced-github.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"side-tab:1\",\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-harness-github-apply-patch.json b/tests/oracle/golden/hook-harness-github-apply-patch.json new file mode 100644 index 000000000..a4a78c07c --- /dev/null +++ b/tests/oracle/golden/hook-harness-github-apply-patch.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"g1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"side-tab:1\",\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-harness-github-edit.json b/tests/oracle/golden/hook-harness-github-edit.json new file mode 100644 index 000000000..a4a78c07c --- /dev/null +++ b/tests/oracle/golden/hook-harness-github-edit.json @@ -0,0 +1,9 @@ +{ + "stdout": "{\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (2 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}", + "stderr": "", + "exit": 0, + "signal": null, + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"g1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"side-tab:1\",\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-native-platform-skip.json b/tests/oracle/golden/hook-native-platform-skip.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-native-platform-skip.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-session-fresh-then-pending-then-stop.json b/tests/oracle/golden/hook-session-fresh-then-pending-then-stop.json new file mode 100644 index 000000000..28f6f4550 --- /dev/null +++ b/tests/oracle/golden/hook-session-fresh-then-pending-then-stop.json @@ -0,0 +1,43 @@ +{ + "steps": [ + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Card.module.css. Still has 1 finding(s) flagged earlier this session (gradient-text:2). Handle them before finalizing — the previous reminder still applies.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Clean.tsx. No deterministic design-quality issues found. That does not mean the design is good: keep following the project design system and the impeccable skill guidance.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"Stop\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n\\nTriage 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.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"Stop\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage 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.\"}}", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":2,\"findings\":[\"gradient-text:2\"]},\"/src/components/Clean.tsx\":{\"editCount\":2,\"findings\":[],\"cleanAcked\":true}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-session-stop-active.json b/tests/oracle/golden/hook-session-stop-active.json new file mode 100644 index 000000000..a317d858b --- /dev/null +++ b/tests/oracle/golden/hook-session-stop-active.json @@ -0,0 +1,19 @@ +{ + "steps": [ + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-session-suppression-after-6.json b/tests/oracle/golden/hook-session-suppression-after-6.json new file mode 100644 index 000000000..7992ee142 --- /dev/null +++ b/tests/oracle/golden/hook-session-suppression-after-6.json @@ -0,0 +1,61 @@ +{ + "steps": [ + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Card.module.css. Still has 1 finding(s) flagged earlier this session (gradient-text:2). Handle them before finalizing — the previous reminder still applies.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Card.module.css. Still has 1 finding(s) flagged earlier this session (gradient-text:2). Handle them before finalizing — the previous reminder still applies.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Card.module.css. Still has 1 finding(s) flagged earlier this session (gradient-text:2). Handle them before finalizing — the previous reminder still applies.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Card.module.css. Still has 1 finding(s) flagged earlier this session (gradient-text:2). Handle them before finalizing — the previous reminder still applies.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook scanned src/components/Card.module.css. Still has 1 finding(s) flagged earlier this session (gradient-text:2). Handle them before finalizing — the previous reminder still applies.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Suppressing further design hints on src/components/Card.module.css. More than 6 edits in this session reached. Run /impeccable audit to revisit.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":9,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-session-two-sessions.json b/tests/oracle/golden/hook-session-two-sessions.json new file mode 100644 index 000000000..f9f86aa06 --- /dev/null +++ b/tests/oracle/golden/hook-session-two-sessions.json @@ -0,0 +1,25 @@ +{ + "steps": [ + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L2 [gradient-text] Gradient text. Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\\n\\nTriage each finding, then state in your reply what you fixed, what you suppressed, and what you left standing:\\n- Real design problem: fix it. Keep intentional design as designed.\\n- 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 ` ignore-value \\\"\\\" --reason \\\"\\\"` with the pair shown on the finding line, or value \\\"*\\\" plus `--file ` when the line shows none. Write \\\"user confirmed\\\" in a reason only when the user did.\\n- Unsure: leave it as is and ask the user in one line.\\nSelf-serve ends at ignore-value: `ignore-file` and `ignore-rule` need the user's explicit approval, and never add an ignore to push a blocked write through. Full suppression ladder: /impeccable hooks.\"}}", + "stderr": "", + "exit": 0, + "signal": null + }, + { + "stdout": "{\"hookSpecificOutput\":{\"hookEventName\":\"Stop\",\"additionalContext\":\"[impeccable@1] Design hook findings requiring review in src/components/Card.module.css (1 issue(s)):\\n- L1 [side-tab] Side-tab accent border. Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\\n\\nTriage 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.\"}}", + "stderr": "", + "exit": 0, + "signal": null + } + ], + "files": { + ".impeccable/hook.cache.json": "{\"version\":1,\"sessions\":{\"s1\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"gradient-text:2\"]}},\"footerShown\":true},\"s2\":{\"updatedAt\": ,\"files\":{\"/src/components/Card.module.css\":{\"editCount\":1,\"findings\":[\"side-tab:1\"]}},\"footerShown\":true}}}" + } +} diff --git a/tests/oracle/golden/hook-stdin-array.json b/tests/oracle/golden/hook-stdin-array.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-stdin-array.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-stdin-empty.json b/tests/oracle/golden/hook-stdin-empty.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-stdin-empty.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-stdin-malformed.json b/tests/oracle/golden/hook-stdin-malformed.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-stdin-malformed.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/hook-stop-no-touched.json b/tests/oracle/golden/hook-stop-no-touched.json new file mode 100644 index 000000000..7ea122487 --- /dev/null +++ b/tests/oracle/golden/hook-stop-no-touched.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/lib.mjs b/tests/oracle/lib.mjs index e2b033fb6..9595ef13f 100644 --- a/tests/oracle/lib.mjs +++ b/tests/oracle/lib.mjs @@ -91,6 +91,10 @@ export function normalize(text, { ws, home = os.homedir() }) { ]) { if (needle) out = out.split(needle).join(tag); } + // The hook footer embeds the admin command's own path; both runtimes name it differently. + out = out.replace(/node '[^']*\/hook-admin\.mjs'/g, ''); + out = out.replace(/node "[^"]*\/hook-admin\.mjs"/g, ''); + out = out.replace(/'[^']*\/impeccable(?:\.exe)?' hooks/g, ' hooks'); // ISO timestamps and epoch millis are run-dependent. out = out.replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z/g, ''); out = out.replace(/"(updatedAt|createdAt|checkedAt|lastCheck|lastChecked|timestamp|ts|mtimeMs|mtime|startedAt|endedAt)":\s*\d{10,}/g, '"$1": '); @@ -146,48 +150,61 @@ function isProbablyText(buf) { export function runCase(c, { impl = 'js', bin = process.env.IMPECCABLE_BIN } = {}) { const ws = stageWorkspace(c.workspace); try { - const cwd = path.join(ws, c.cwd || '.'); - let argv; - if (impl === 'js') { - const base = JS_VERBS[c.verb]; - if (!base) throw new Error(`no JS invocation for verb ${c.verb}`); - argv = [...base]; - } else { - if (!bin) throw new Error('IMPECCABLE_BIN not set'); - argv = binArgv(bin, c.verb); - } - const args = (c.args || []).map(a => String(a).replaceAll('', ws).replaceAll('', REPO_ROOT)); - argv.push(...args); const isolatedHome = path.join(ws, '.oracle-home'); - const env = { - ...process.env, - NO_COLOR: '1', - FORCE_COLOR: '0', - IMPECCABLE_NO_UPDATE_CHECK: '1', - IMPECCABLE_NO_TELEMETRY: '1', - DO_NOT_TRACK: '1', - ...(c.isolateHome === false ? {} : { HOME: isolatedHome, USERPROFILE: isolatedHome }), - ...(c.env || {}), - }; if (c.isolateHome !== false) fs.mkdirSync(isolatedHome, { recursive: true }); - const res = spawnSync(argv[0], argv.slice(1), { - cwd, env, input: c.stdin ?? '', encoding: 'utf8', timeout: c.timeoutMs || 60_000, - windowsHide: true, maxBuffer: 64 * 1024 * 1024, - }); + if (typeof c.setup === 'function') c.setup(ws); + const steps = c.steps || [c]; + const results = []; + for (const step of steps) { + results.push(runStep({ ...c, ...step, verb: step.verb || c.verb }, { impl, bin, ws, isolatedHome })); + } const files = snapshotFiles(ws, c.files); const ctx = { ws }; - return { - stdout: normalize(res.stdout ?? '', ctx), - stderr: normalize(res.stderr ?? '', ctx), - exit: res.status, - signal: res.signal || null, - files: Object.fromEntries(Object.entries(files).map(([k, v]) => [k, normalize(v, ctx)])), - }; + const norm = (r) => ({ + stdout: normalize(r.stdout ?? '', ctx), + stderr: normalize(r.stderr ?? '', ctx), + exit: r.status, + signal: r.signal || null, + }); + const filesNorm = Object.fromEntries(Object.entries(files).map(([k, v]) => [k, normalize(v, ctx)])); + if (c.steps) return { steps: results.map(norm), files: filesNorm }; + return { ...norm(results[0]), files: filesNorm }; } finally { fs.rmSync(ws, { recursive: true, force: true }); } } +function runStep(c, { impl, bin, ws, isolatedHome }) { + const cwd = path.join(ws, c.cwd || '.'); + let argv; + if (impl === 'js') { + const base = JS_VERBS[c.verb]; + if (!base) throw new Error(`no JS invocation for verb ${c.verb}`); + argv = [...base]; + } else { + if (!bin) throw new Error('IMPECCABLE_BIN not set'); + argv = binArgv(bin, c.verb); + } + const sub = (v) => String(v).replaceAll('', ws).replaceAll('', REPO_ROOT); + argv.push(...(c.args || []).map(sub)); + const env = { + ...process.env, + NO_COLOR: '1', + FORCE_COLOR: '0', + IMPECCABLE_NO_UPDATE_CHECK: '1', + IMPECCABLE_NO_TELEMETRY: '1', + DO_NOT_TRACK: '1', + ...(c.isolateHome === false ? {} : { HOME: isolatedHome, USERPROFILE: isolatedHome }), + ...Object.fromEntries(Object.entries(c.env || {}).map(([k, v]) => [k, v == null ? v : sub(v)])), + }; + for (const [k, v] of Object.entries(env)) if (v == null) delete env[k]; + const stdin = typeof c.stdin === 'string' ? sub(c.stdin) : c.stdin != null ? sub(JSON.stringify(c.stdin)) : ''; + return spawnSync(argv[0], argv.slice(1), { + cwd, env, input: stdin, encoding: 'utf8', timeout: c.timeoutMs || 60_000, + windowsHide: true, maxBuffer: 64 * 1024 * 1024, + }); +} + export function goldenPath(id) { return path.join(GOLDEN_DIR, `${id}.json`); } @@ -206,6 +223,15 @@ export function readGolden(id) { /** Return a list of human-readable differences, empty if equal. */ export function diffResults(golden, actual) { const diffs = []; + if (golden.steps || actual.steps) { + const g = golden.steps || [], a = actual.steps || []; + if (g.length !== a.length) diffs.push(`steps: expected ${g.length}, got ${a.length}`); + for (let i = 0; i < Math.min(g.length, a.length); i++) { + for (const d of diffResults({ ...g[i], files: {} }, { ...a[i], files: {} })) diffs.push(`step ${i + 1} ${d}`); + } + for (const d of diffResults({ files: golden.files, exit: 0, signal: null, stdout: '', stderr: '' }, { files: actual.files, exit: 0, signal: null, stdout: '', stderr: '' })) diffs.push(d); + return diffs; + } for (const k of ['exit', 'signal']) { if (golden[k] !== actual[k]) diffs.push(`${k}: expected ${golden[k]}, got ${actual[k]}`); } diff --git a/tests/oracle/record.mjs b/tests/oracle/record.mjs index 6a80b7088..f91aa11b8 100644 --- a/tests/oracle/record.mjs +++ b/tests/oracle/record.mjs @@ -13,6 +13,7 @@ for (const c of cases) { const res = runCase(c, { impl: 'js' }); writeGolden(c.id, res); n++; - process.stdout.write(`recorded ${c.id} (exit ${res.exit}, ${res.stdout.length}b out, ${Object.keys(res.files).length} files)\n`); + const head = res.steps ? `${res.steps.length} steps, exits ${res.steps.map(s => s.exit).join('/')}` : `exit ${res.exit}, ${res.stdout.length}b out`; + process.stdout.write(`recorded ${c.id} (${head}, ${Object.keys(res.files).length} files)\n`); } process.stdout.write(`\n${n} goldens written\n`); diff --git a/tests/oracle/workspaces/hook-project/PRODUCT.md b/tests/oracle/workspaces/hook-project/PRODUCT.md new file mode 100644 index 000000000..e196510b8 --- /dev/null +++ b/tests/oracle/workspaces/hook-project/PRODUCT.md @@ -0,0 +1,8 @@ + +# Product + +## What it is +A fixture app used by the oracle harness. + +## Platform +web diff --git a/tests/oracle/workspaces/hook-project/package.json b/tests/oracle/workspaces/hook-project/package.json new file mode 100644 index 000000000..baf996485 --- /dev/null +++ b/tests/oracle/workspaces/hook-project/package.json @@ -0,0 +1 @@ +{"name":"hook-fixture","private":true} diff --git a/tests/oracle/workspaces/hook-project/src/components/Card.module.css b/tests/oracle/workspaces/hook-project/src/components/Card.module.css new file mode 100644 index 000000000..e4fc8fde9 --- /dev/null +++ b/tests/oracle/workspaces/hook-project/src/components/Card.module.css @@ -0,0 +1,2 @@ +.card { border-left: 4px solid #6366f1; border-radius: 8px; } +.title { background: linear-gradient(90deg, #f472b6, #a78bfa); -webkit-background-clip: text; color: transparent; } diff --git a/tests/oracle/workspaces/hook-project/src/components/Card.tsx b/tests/oracle/workspaces/hook-project/src/components/Card.tsx new file mode 100644 index 000000000..62a279f92 --- /dev/null +++ b/tests/oracle/workspaces/hook-project/src/components/Card.tsx @@ -0,0 +1,8 @@ +export function Card() { + return ( +
+

Lightning Fast

+

Body

+
+ ); +} diff --git a/tests/oracle/workspaces/hook-project/src/components/Clean.tsx b/tests/oracle/workspaces/hook-project/src/components/Clean.tsx new file mode 100644 index 000000000..4af9b1e07 --- /dev/null +++ b/tests/oracle/workspaces/hook-project/src/components/Clean.tsx @@ -0,0 +1,3 @@ +export function Clean() { + return

Plain

Nothing to see.

; +} diff --git a/tests/oracle/workspaces/hook-project/src/page.html b/tests/oracle/workspaces/hook-project/src/page.html new file mode 100644 index 000000000..d48ca37c0 --- /dev/null +++ b/tests/oracle/workspaces/hook-project/src/page.html @@ -0,0 +1 @@ +

Kicker

Hi

diff --git a/tests/oracle/workspaces/hook-project/src/util.ts b/tests/oracle/workspaces/hook-project/src/util.ts new file mode 100644 index 000000000..bc81dd56d --- /dev/null +++ b/tests/oracle/workspaces/hook-project/src/util.ts @@ -0,0 +1 @@ +export const add = (a: number, b: number) => a + b;