Move the slop defects back into the craft floor

The detector-blind slop review existed because the AI-tell rules had been
stripped out of SKILL.md and nothing carried them. The floor is a better
home: it loads after concept ideation and immediately before editing UI,
which is the placement that made stripping them necessary in the first
place. Models tread lightly when a ban list is present during ideation;
by the time the floor loads, the direction is already committed.

- Rename build-floor.md to craft-floor.md and restore the absolute bans
  (side-stripes, gradient text, glassmorphism, hero-metric, identical card
  grids, eyebrow-on-every-section, numbered markers, text overflow), the
  codex and gemini defect lists, and the reflexes no scanner catches.
  Rule ids match the ones the ablation catalog already knows.
- Delete lib/slop-review.mjs and both injections. The Stop hook is now
  purely a mechanical pass and stays silent with nothing to report.
- context.mjs replaces AI_SLOP_REVIEW_REQUIRED with the narrower
  MANUAL_DETECTOR_REQUIRED, emitted only when a session has no hook at
  all. A per-edit hook already covers the mechanical gap, and the floor
  covers the judgment one either way.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-07-21 02:04:36 -07:00
co-authored by Claude
parent d7d10277d1
commit 153b416f2e
9 changed files with 87 additions and 118 deletions
+9 -11
View File
@@ -1035,17 +1035,16 @@ describe('context.mjs CLI', () => {
// routes to new-work rather than back through product init.
assert.match(res.stdout, /\n---\n\n/);
assert.match(res.stdout, /WORLD_DISCOVERY_REQUIRED: PRODUCT\.md exists but no DESIGN\.md/);
assert.match(res.stdout, /AI_SLOP_REVIEW_REQUIRED:/);
assert.match(res.stdout, /Monospace used merely/);
assert.match(res.stdout, /MANUAL_DETECTOR_REQUIRED:/);
assert.match(res.stdout, /detect\.mjs --json <changed targets>/);
});
it('keeps LLM-only fallback guidance out of early context when the current provider hook is active', () => {
it('keeps the manual-detector directive out of early context when the current provider hook is active', () => {
const scripts = path.join(scratch, 'bundle', 'skills', 'impeccable', 'scripts');
const lib = path.join(scripts, 'lib');
fs.mkdirSync(lib, { recursive: true });
fs.copyFileSync(SCRIPT_PATH, path.join(scripts, 'context.mjs'));
for (const helper of ['target-args.mjs', 'surface-briefs.mjs', 'target-slug.mjs', 'slop-review.mjs']) {
for (const helper of ['target-args.mjs', 'surface-briefs.mjs', 'target-slug.mjs']) {
fs.copyFileSync(path.join(path.dirname(SCRIPT_PATH), 'lib', helper), path.join(lib, helper));
}
const provider = fs.readFileSync(path.join(path.dirname(SCRIPT_PATH), 'lib', 'provider.mjs'), 'utf8')
@@ -1065,7 +1064,7 @@ describe('context.mjs CLI', () => {
env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' },
});
assert.equal(res.status, 0, res.stderr);
assert.doesNotMatch(res.stdout, /AI_SLOP_REVIEW_REQUIRED:/);
assert.doesNotMatch(res.stdout, /MANUAL_DETECTOR_REQUIRED:/);
fs.mkdirSync(path.join(project, '.impeccable'), { recursive: true });
fs.writeFileSync(path.join(project, '.impeccable', 'config.json'), JSON.stringify({ hook: { enabled: false } }));
@@ -1075,16 +1074,16 @@ describe('context.mjs CLI', () => {
env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' },
});
assert.equal(disabled.status, 0, disabled.stderr);
assert.match(disabled.stdout, /AI_SLOP_REVIEW_REQUIRED:/);
assert.match(disabled.stdout, /MANUAL_DETECTOR_REQUIRED:/);
assert.match(disabled.stdout, /detect\.mjs --json <changed targets>/);
});
it('injects only detector-blind guidance when a per-edit-only hook is active', () => {
it('adds no detector directive when a per-edit-only hook is active', () => {
const scripts = path.join(scratch, 'bundle', 'skills', 'impeccable', 'scripts');
const lib = path.join(scripts, 'lib');
fs.mkdirSync(lib, { recursive: true });
fs.copyFileSync(SCRIPT_PATH, path.join(scripts, 'context.mjs'));
for (const helper of ['target-args.mjs', 'surface-briefs.mjs', 'target-slug.mjs', 'slop-review.mjs']) {
for (const helper of ['target-args.mjs', 'surface-briefs.mjs', 'target-slug.mjs']) {
fs.copyFileSync(path.join(path.dirname(SCRIPT_PATH), 'lib', helper), path.join(lib, helper));
}
const provider = fs.readFileSync(path.join(path.dirname(SCRIPT_PATH), 'lib', 'provider.mjs'), 'utf8')
@@ -1104,8 +1103,7 @@ describe('context.mjs CLI', () => {
env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' },
});
assert.equal(res.status, 0, res.stderr);
assert.match(res.stdout, /AI_SLOP_REVIEW_REQUIRED:/);
assert.match(res.stdout, /automatic detector covers mechanical rules/);
assert.doesNotMatch(res.stdout, /MANUAL_DETECTOR_REQUIRED:/);
assert.doesNotMatch(res.stdout, /detect\.mjs --json <changed targets>/);
});
@@ -1354,7 +1352,7 @@ describe('context.mjs update check', () => {
const providerSrc = path.join(path.dirname(SCRIPT_PATH), 'lib', 'provider.mjs');
const providerDest = path.join(path.dirname(skillScript), 'lib', 'provider.mjs');
fs.copyFileSync(providerSrc, providerDest);
for (const helper of ['surface-briefs.mjs', 'target-slug.mjs', 'slop-review.mjs']) {
for (const helper of ['surface-briefs.mjs', 'target-slug.mjs']) {
fs.copyFileSync(
path.join(path.dirname(SCRIPT_PATH), 'lib', helper),
path.join(path.dirname(skillScript), 'lib', helper),
+6 -13
View File
@@ -3281,8 +3281,6 @@ describe('runStopHook()', () => {
assert.match(out.hookSpecificOutput.additionalContext, /em-dash-overuse/);
assert.match(out.hookSpecificOutput.additionalContext, /side-tab/);
assert.doesNotMatch(out.hookSpecificOutput.additionalContext, /dark-glow/);
assert.match(out.hookSpecificOutput.additionalContext, /Detector-blind AI-slop review/);
assert.match(out.hookSpecificOutput.additionalContext, /Monospace used merely/);
assert.equal(stop.emission.kind, 'stop-deep-pass');
});
@@ -3307,7 +3305,7 @@ describe('runStopHook()', () => {
assert.equal(second.audit.skipped, 'stop-clean');
});
it('respects detector.ignoreRules while still delivering the one-time LLM-only review', async () => {
it('stays silent when detector.ignoreRules filters away every touched finding', async () => {
const sid = 'stop-ignored';
fs.mkdirSync(path.join(cwd, '.impeccable'), { recursive: true });
fs.writeFileSync(getConfigPath(cwd), JSON.stringify({
@@ -3318,12 +3316,8 @@ describe('runStopHook()', () => {
await runHook({ stdinJson: JSON.stringify(editEvent(file, sid)), env: {}, cwd, detector: det });
const stop = await runStopHook({ stdinJson: JSON.stringify(stopEvent(sid)), env: {}, cwd, detector: det });
const text = JSON.parse(stop.stdout).hookSpecificOutput.additionalContext;
assert.doesNotMatch(text, /em-dash-overuse/);
assert.match(text, /Detector-blind AI-slop review/);
const second = await runStopHook({ stdinJson: JSON.stringify(stopEvent(sid)), env: {}, cwd, detector: det });
assert.equal(second.stdout, '');
assert.equal(second.audit.skipped, 'stop-clean');
assert.equal(stop.stdout, '');
assert.equal(stop.audit.skipped, 'stop-clean');
});
it('honors kill switches and the re-entrancy guard', async () => {
@@ -3586,15 +3580,14 @@ describe('runStopHook() — direction-contract audit', () => {
assert.doesNotMatch(text, /Direction-contract audit/);
});
it('does not audit a malformed contract but still emits the one-time LLM-only review', async () => {
it('stays silent on a malformed contract with nothing else to report', async () => {
const sid = 'contract-malformed';
const file = write('index.html', '<!-- DIRECTION CONTRACT never closed\n<html><body></body></html>');
await touch(file, sid, fakeDetector([]));
const stop = await runStopHook({ stdinJson: JSON.stringify(stopEvent(sid)), env: {}, cwd, detector: fakeDetector([]) });
const text = JSON.parse(stop.stdout).hookSpecificOutput.additionalContext;
assert.doesNotMatch(text, /Direction-contract audit/);
assert.match(text, /Detector-blind AI-slop review/);
assert.equal(stop.stdout, '');
assert.equal(stop.audit.skipped, 'stop-clean');
});
it('truncates an oversized contract in the emitted message', async () => {