fix: craft audit capture, exceptions and trigger from /ship review (GSTA-92)

- craft-audit: no CRAFT_RESULT line means unavailable (exit 2 after a
  malformed --accepted record still printed findings); --accepted only for a
  /design-html finalized page, never for needs-proof or lorem; never add
  [Needs proof] markers to a live page; Aside capture via pg.evaluate; the
  saved raw DOM is deleted in the checking block and never reported
- manifest trigger covers an explicit craft audit (skeleton -6 bytes; pins
  lowered to measured size, union ratio back under its cap)
- autoFixEntries reads RENDERED; DESIGN_CRAFT_RULE needs exactly one lead
- tests: checkerOnly contract, resolver throw paths, Persuade checks in both
  skills, resolver touchfile selection, exception state cells
- CHANGELOG: host counts and the audit's new rules

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-26 01:58:03 +00:00
1 parent 62c0fa1333
commit 31ee450399
12 files changed
+118 -55

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
/**
* Craft resolvers (scripts/resolvers/design-craft.ts) shared by /design-review's
* craft audit and /plan-design-review.
*
* Value: protects=DESIGN_CRAFT_RULE returns a craft paragraph with its bullets and fails generation on a missing or renamed lead; fails_when=the lookup returns '' or the first paragraph instead of throwing, or drops bullets; why_new=only the freshness golden exercises the resolver, and no test reaches its throw paths; seam=none; no_claim=agent use of the rendered rules (behavioral, no paid case)
* Value: protects=design-review and plan-design-review render the same five Persuade checks; fails_when=either surface drops or hand-forks the {{DESIGN_PERSUADE_CHECKS}} block; why_new=carve-guards checks one phrase in design-review only, nothing checks plan-design-review; seam=none; no_claim=that an agent applies the checks (eval)
*/
import { describe, expect, test } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
import { DESIGN_CRAFT_RULE, DESIGN_PERSUADE_CHECKS } from '../scripts/resolvers/design-craft';
import type { TemplateContext } from '../scripts/resolvers/types';
const ROOT = path.resolve(import.meta.dir, '..');
const read = (...p: string[]) => fs.readFileSync(path.join(ROOT, ...p), 'utf-8');
const ctx = { skillName: 'design-review', tmplPath: 'design-review/sections/craft-audit.md.tmpl' } as TemplateContext;
describe('craft resolvers', () => {
test('DESIGN_CRAFT_RULE renders the craft paragraph with its bullets; a missing or unknown lead fails generation', () => {
const motion = DESIGN_CRAFT_RULE(ctx, ['Motion']);
expect(motion).toStartWith('**Motion.**');
expect(motion).toContain("Never an unthrottled `window.addEventListener('scroll')`");
expect(motion).toContain('`min-height: 100dvh`, not `100vh`.');
expect(motion).not.toContain('\n\n');
expect(DESIGN_CRAFT_RULE(ctx, ['Claims come from the user'])).toStartWith('**Claims come from the user, never from you.**');
expect(read('design-html', 'sections', 'craft.md')).toContain(motion);
expect(() => DESIGN_CRAFT_RULE(ctx, ['No such lead'])).toThrow('No such lead');
expect(() => DESIGN_CRAFT_RULE(ctx, [])).toThrow('requires the bold lead');
expect(() => DESIGN_CRAFT_RULE(ctx)).toThrow('requires the bold lead');
});
test('design-review and plan-design-review carry the same five Persuade checks', () => {
const checks = DESIGN_PERSUADE_CHECKS(ctx);
expect(checks.split('\n').map(l => l.slice(0, 3))).toEqual(['1. ', '2. ', '3. ', '4. ', '5. ']);
expect(read('design-review', 'sections', 'craft-audit.md')).toContain(checks);
expect(read('plan-design-review', 'sections', 'review-sections.md')).toContain(checks);
});
});
+43 -2
View File
@@ -3,9 +3,10 @@ import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { acceptFindings, BUZZWORDS, CRAFT_IDS, checkCraft, formatCraft, parseCraftExceptions, visibleText } from '../lib/design-craft';
import { catalogEntry } from '../lib/design-catalog';
import { catalogEntry, DESIGN_SLOP_CATALOG, detectorSlopEntries, judgmentTellEntries, renderCatalog, selectCatalog } from '../lib/design-catalog';
import { checklistSlopEntries } from '../scripts/resolvers/design-checklist';
import { selectTests } from './helpers/test-selection';
import { E2E_TOUCHFILES } from './helpers/touchfiles-data';
import { E2E_TOUCHFILES, LLM_JUDGE_TOUCHFILES } from './helpers/touchfiles-data';
const ROOT = path.resolve(import.meta.dir, '..');
const BIN = path.join(ROOT, 'bin', 'gstack-design-craft');
@@ -59,6 +60,20 @@ describe('design-craft check', () => {
for (const f of findings) expect(f.fix).toBe(catalogEntry(f.id)!.fix!);
});
// Value: protects=checker-only craft entries render into no skill prose or checklist, and each is a printed craft id; fails_when=a renderer reads DESIGN_SLOP_CATALOG, not RENDERED, or filters on fix; why_new=only goldens catch it, until regenerated; seam=none; no_claim=byte size of any one skill (size guard owns it)
test('checker-only catalog entries are craft ids that no rendered list carries; catalog entries with a fix still render', () => {
const checkerOnly = DESIGN_SLOP_CATALOG.filter(e => e.checkerOnly);
expect(checkerOnly.length).toBeGreaterThan(0);
const rendered = new Set([selectCatalog({}), detectorSlopEntries(), judgmentTellEntries(), checklistSlopEntries()].flat().map(e => e.id));
const prose = renderCatalog({ kind: 'slop' });
for (const e of checkerOnly) {
expect(CRAFT_IDS, e.id).toContain(e.id);
expect(rendered.has(e.id), e.id).toBe(false);
expect(prose, e.id).not.toContain(e.prose);
}
for (const id of ['marketing-buzzword', 'layout-transition']) expect(rendered.has(id), id).toBe(true);
});
test('BUZZWORDS covers every word the catalog marketing-buzzword heuristic names', () => {
const heuristic = catalogEntry('marketing-buzzword')?.heuristic ?? '';
const words = heuristic.replace(/^.*?for /, '').replace(/\.$/, '').split(/,\s*/);
@@ -198,6 +213,25 @@ describe('accepted craft exceptions (finalized.json craft_exceptions)', () => {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test('an exception the page no longer triggers prints nothing, and a record is read only with --accepted', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'design-craft-cells-'));
try {
const page = path.join(dir, 'finalized.html');
const record = path.join(dir, 'finalized.json');
fs.writeFileSync(page, CLEAN);
fs.writeFileSync(record, JSON.stringify({ craft_exceptions: [{ id: 'placeholder-brand', count: 1, reason: 'Acme is the customer' }] }));
expect(run(page, '--accepted', record)).toMatchObject({ code: 0, out: 'CRAFT_RESULT: clean\n' });
expect(acceptFindings([], [{ id: 'oops', count: 1, reason: 'x' }])).toEqual({ issues: [], accepted: [] });
fs.writeFileSync(page, CLEAN.replace('Halvorsen Freight', 'Acme and Nexus'));
expect(run(page)).toMatchObject({ code: 1, out: 'CRAFT_CHECK: placeholder-brand 2 use a supplied customer, or a fitting name in sample data\nCRAFT_RESULT: 1 issue(s)\n' });
fs.writeFileSync(page, CLEAN);
fs.writeFileSync(record, '{');
expect(run(page)).toMatchObject({ code: 0, out: 'CRAFT_RESULT: clean\n', err: '' });
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
});
describe('design-shotgun to design-html handoff', () => {
@@ -224,6 +258,13 @@ describe('design-shotgun to design-html handoff', () => {
}
});
// Value: protects=a change to the shared craft resolver reruns the paid cases whose prompts it renders; fails_when=scripts/resolvers/design-craft.ts drops from those touchfile lists; why_new=the row above covers lib/design-craft.ts and the bin only; seam=none; no_claim=that the selected cases pass
test('a change to the craft resolvers selects the design-review and plan-design-review cases that render them', () => {
const e2e = selectTests(['scripts/resolvers/design-craft.ts'], E2E_TOUCHFILES).selected;
for (const name of ['design-review-fix', 'design-review-detector-shim', 'design-review-detector-shim-dom', 'design-review-plugin-handoff', 'plan-design-review-plan-mode']) expect(e2e).toContain(name);
expect(selectTests(['scripts/resolvers/design-craft.ts'], LLM_JUDGE_TOUCHFILES).selected).toContain('plan-design-review/SKILL.md passes');
});
test('design-html persists accepted exceptions and passes them back with --accepted', () => {
const html = read('design-html', 'SKILL.md');
const craft = read('design-html', 'sections', 'craft.md');
+1 -1
View File
@@ -65,7 +65,7 @@ describe('C4: external section pointers', () => {
});
});
describe('C4: ship, plan-ceo-review and office-hours are carved on every external host', () => {
describe('C4: ship, plan-ceo-review, office-hours and design-review are carved on every external host', () => {
for (const config of EXTERNAL) {
test(`${config.name}: every pointer in the carved skill resolves to a generated section file`, () => {
for (const skill of ['ship', 'plan-ceo-review', 'office-hours', 'design-review']) {
+12 -12
View File
@@ -4,16 +4,16 @@
"pins": {
"agy/gstack-autoplan": 130504,
"agy/gstack-design-consultation": 108184,
"agy/gstack-design-review": 144142,
"agy/gstack-design-review": 144136,
"agy/gstack-land-and-deploy": 117722,
"agy/gstack-plan-design-review": 141095,
"agy/gstack-plan-devex-review": 135760,
"agy/gstack-plan-eng-review": 152582,
"agy/gstack-review": 124182,
"claude/design-review": 137115,
"claude/design-review": 137109,
"codex/gstack-autoplan": 130628,
"codex/gstack-design-consultation": 109452,
"codex/gstack-design-review": 145642,
"codex/gstack-design-review": 145636,
"codex/gstack-land-and-deploy": 122272,
"codex/gstack-office-hours": 102308,
"codex/gstack-plan-design-review": 143548,
@@ -23,7 +23,7 @@
"copilot/gstack-autoplan": 131637,
"copilot/gstack-codex": 100235,
"copilot/gstack-design-consultation": 109536,
"copilot/gstack-design-review": 145447,
"copilot/gstack-design-review": 145441,
"copilot/gstack-land-and-deploy": 118977,
"copilot/gstack-office-hours": 100464,
"copilot/gstack-plan-design-review": 141966,
@@ -32,7 +32,7 @@
"copilot/gstack-review": 125453,
"cursor/gstack-autoplan": 129278,
"cursor/gstack-design-consultation": 107222,
"cursor/gstack-design-review": 143121,
"cursor/gstack-design-review": 143115,
"cursor/gstack-land-and-deploy": 116645,
"cursor/gstack-plan-design-review": 140050,
"cursor/gstack-plan-devex-review": 134591,
@@ -41,7 +41,7 @@
"factory/gstack-autoplan": 131562,
"factory/gstack-codex": 100655,
"factory/gstack-design-consultation": 108670,
"factory/gstack-design-review": 144911,
"factory/gstack-design-review": 144905,
"factory/gstack-land-and-deploy": 120817,
"factory/gstack-office-hours": 101343,
"factory/gstack-plan-design-review": 142831,
@@ -49,34 +49,34 @@
"factory/gstack-plan-eng-review": 154550,
"factory/gstack-review": 144493,
"gbrain/gstack-autoplan": 129815,
"gbrain/gstack-design-review": 133456,
"gbrain/gstack-design-review": 133450,
"gbrain/gstack-land-and-deploy": 117167,
"gbrain/gstack-plan-design-review": 128937,
"gbrain/gstack-plan-devex-review": 115129,
"gbrain/gstack-plan-eng-review": 134168,
"hermes/gstack-autoplan": 129905,
"hermes/gstack-design-review": 133563,
"hermes/gstack-design-review": 133557,
"hermes/gstack-land-and-deploy": 117269,
"hermes/gstack-plan-design-review": 129046,
"hermes/gstack-plan-devex-review": 115215,
"hermes/gstack-plan-eng-review": 134253,
"kiro/gstack-autoplan": 129040,
"kiro/gstack-design-consultation": 107064,
"kiro/gstack-design-review": 142935,
"kiro/gstack-design-review": 142929,
"kiro/gstack-land-and-deploy": 116533,
"kiro/gstack-plan-design-review": 139890,
"kiro/gstack-plan-devex-review": 134431,
"kiro/gstack-plan-eng-review": 151286,
"kiro/gstack-review": 140526,
"openclaw/gstack-autoplan": 129520,
"openclaw/gstack-design-review": 132261,
"openclaw/gstack-design-review": 132255,
"openclaw/gstack-land-and-deploy": 116757,
"openclaw/gstack-plan-design-review": 127788,
"openclaw/gstack-plan-devex-review": 113968,
"openclaw/gstack-plan-eng-review": 132565,
"opencode/gstack-autoplan": 131640,
"opencode/gstack-design-consultation": 109357,
"opencode/gstack-design-review": 145319,
"opencode/gstack-design-review": 145313,
"opencode/gstack-land-and-deploy": 118762,
"opencode/gstack-office-hours": 100298,
"opencode/gstack-plan-design-review": 141820,
@@ -85,7 +85,7 @@
"opencode/gstack-review": 142949,
"slate/gstack-autoplan": 129159,
"slate/gstack-design-consultation": 107143,
"slate/gstack-design-review": 143028,
"slate/gstack-design-review": 143022,
"slate/gstack-land-and-deploy": 116589,
"slate/gstack-plan-design-review": 139970,
"slate/gstack-plan-devex-review": 134511,