mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-06 00:44:07 +01:00
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:
1 parent
62c0fa1333
commit
31ee450399
12 files changed
+118
-55
No files matched your search
+3
-2
@@ -5,7 +5,7 @@
|
||||
### Changed
|
||||
- **The archetype the user picks on the /design-shotgun board now reaches /design-html.** Captions name the archetype id (`classic-hero`, `long-form-story`, `minimal-conversion`, `comparison`). On approval, approved.json also gets the chosen `archetype` and its `sections` outline, added with the Edit tool so no free text reaches a shell command. /design-html reads both in Case A, and the craft section uses them as given instead of choosing again or asking.
|
||||
- **Accepted craft exceptions persist.** /design-html's finalized.json records each accepted `CRAFT_CHECK` finding in `craft_exceptions` (`id`, `count`, `reason`) and carries forward earlier ones. A later run passes that file with `--accepted`, so a finding the user already accepted is reported as accepted instead of flagged again. A higher count is still flagged as a new issue.
|
||||
- **/design-review audits a Persuade page or a redesign for craft** (`sections/craft-audit.md`, read in Phases 1-6; design-review is now carved on every host, so the audit is one lazy section, not inlined text). It runs `bin/gstack-design-craft` on each page's served HTML (or the repo file when no app runs) and files each `CRAFT_CHECK` line as a category 9 finding; checks what the tool cannot see (claims only from the user, realistic sample content, the ship floor, a designed 404, cookie consent where required, the motion rules); files a failed Persuade check (one primary action, named objections, proof beside each claim, a real risk reversal, an index/noindex decision) as High impact; and in Phase 8 breaks impact ties with Elaya's fix order and reads the installed Tailwind major version before touching its config. The audit's rules render from /design-html's craft section, so the build and the audit cannot drift.
|
||||
- **/design-review audits a Persuade page or a redesign for craft, or on request** (`sections/craft-audit.md`, read in Phases 1-6; design-review is now carved on every host, so the audit is one lazy section, not inlined text). It runs `bin/gstack-design-craft` on each page's served HTML (or the repo file when no app runs) and files each `CRAFT_CHECK` line as a category 9 finding (no `CRAFT_RESULT` line means the check was unavailable; the saved page holds session data, so it is deleted in the block that checks it and never copied to the report; `--accepted` applies only to a page /design-html finalized, and never to `needs-proof` or `lorem`; a live page never gets a `[Needs proof: …]` marker); checks what the tool cannot see (claims only from the user, realistic sample content, the ship floor, a designed 404, cookie consent where required, the motion rules); files a failed Persuade check (one primary action, named objections, proof beside each claim, a real risk reversal, an index/noindex decision) as High impact; and in Phase 8 breaks impact ties with Elaya's fix order and reads the installed Tailwind major version before touching its config. The audit's rules render from /design-html's craft section, so the build and the audit cannot drift.
|
||||
- **/plan-design-review**: a Persuade plan gets the same five checks in Pass 1, Pass 4 rejects unsourced proof, and the runner checklist names them.
|
||||
- **The craft tells are catalog entries.** Placeholder people and brands, lorem, round sample numbers, `[Needs proof:` markers, "Oops" errors, scroll-listener reveals, dead `href="#"`, `alert()`, images without alt, `100vh`, and a missing title, description, favicon, skip link or `aria-current` live in `lib/design-catalog.ts` as `checkerOnly` entries: one id, one short line and the fix, reported by the craft check and kept out of Methodology category 9, `review/design-checklist.md` and the consultation bullets, so no skill carries their prose. `lib/design-craft.ts` prints only catalog ids, with each entry's `fix` text; `transition: all` now reports under the existing `layout-transition` id instead of `transition-all`.
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
- `test/helpers/placeholder-allowlist.ts` lists `<path-to-page.html>` (path grammar), the saved page the craft audit passes to the check.
|
||||
- The design-review and plan-design-review paid cases and judges that read a design resolver now also select on `scripts/resolvers/design-craft.ts`, and `design-html-slop-gate` on `lib/design-catalog.ts`, where the craft check's ids and fixes now live.
|
||||
- `test/design-craft.test.ts`: every id the check prints is a grep-detectable catalog entry, and the fix it prints is the catalog's. design-review gets its first carve guard (skeleton, union and parity ratio at their measured sizes).
|
||||
- `test/design-craft.test.ts` also pins that `checkerOnly` entries reach no rendered list while the craft check prints each one, that a change to `scripts/resolvers/design-craft.ts` selects the design-review and plan-design-review cases, and the exception cells a stale record, an over-count tell without `--accepted` and a malformed record on disk reach. New `test/design-craft-resolvers.test.ts`: `{{DESIGN_CRAFT_RULE}}` renders one paragraph with its bullets and fails generation on a missing or ambiguous lead, and both skills carry the same five Persuade checks.
|
||||
|
||||
### Gate integrity
|
||||
- Size budgets raised to measured sizes only (Garry 2026-10-25 "Do it" (GSTA-92)): the SKILL.md pins of design-review on 13 hosts (+584 Claude, +614 external: the Section index the carve test requires and the STOP pointer) and plan-design-review on 12 external hosts (+1,082: the five Persuade plan checks). The craft tells are catalog entries the craft check reports with their fix, kept out of every rendered skill list, so design-consultation and the review checklist do not grow. No other threshold, budget, timeout, retry or trial count changed.
|
||||
- Size budgets raised to measured sizes only (Garry 2026-10-25 "Do it" (GSTA-92)): the SKILL.md pins of design-review on 12 hosts (+578 Claude, +608 on the 11 external hosts: the Section index the carve test requires and the STOP pointer) and plan-design-review on the 11 external hosts (+1,082: the five Persuade plan checks). The craft tells are catalog entries the craft check reports with their fix, kept out of every rendered skill list, so design-consultation and the review checklist do not grow. No other threshold, budget, timeout, retry or trial count changed.
|
||||
|
||||
## [1.91.226.0] - 2026-10-26
|
||||
|
||||
|
||||
@@ -1573,9 +1573,9 @@ sections. Read a section in full before doing its step; do not work from memory.
|
||||
|
||||
| When | Read this section |
|
||||
|------|-------------------|
|
||||
| auditing a Persuade page or a redesign | `sections/craft-audit.md` |
|
||||
| a craft, Persuade or redesign audit | `sections/craft-audit.md` |
|
||||
|
||||
> **STOP.** Before auditing a Persuade page or a redesign, Read `~/.claude/skills/gstack/design-review/sections/craft-audit.md` and execute it
|
||||
> **STOP.** Before a craft, Persuade or redesign audit, Read `~/.claude/skills/gstack/design-review/sections/craft-audit.md` and execute it
|
||||
> in full. Do not work from memory — that section is the source of truth for this step.
|
||||
|
||||
Record baseline design score and AI slop score at end of Phase 6.
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
<!-- Regenerate: bun run gen:skill-docs -->
|
||||
## Craft audit: Persuade pages and redesigns
|
||||
|
||||
Run this in Phases 1-6, on every audited page, when a page is Persuade (the classifier in the Design Hard Rules) or the user asked for a redesign or a craft audit. It adds what generated sites ship and a reviewer catches in seconds: invented content, missing ship basics, scroll-listener reveals, `100vh`, and a Persuade page's conversion basics. Its rules render from /design-html's craft section, so a page is audited against the rules it was built to.
|
||||
Run this in Phases 1-6, on every audited page, when a page is Persuade (the classifier in the Design Hard Rules) or the user asked for a redesign or a craft audit.
|
||||
|
||||
### 1. Run the craft check on each page
|
||||
|
||||
With a URL, save the page in the script that opens it. Aside: add `await fs.writeFile(path.join(pwd, "{page}.page.html"), await pg.content());`. Fallback engine: `_TMP=$(mktemp -d "${TMPDIR:-/tmp}/gstack-craft.XXXXXX"); $B js 'document.documentElement.outerHTML' --out "$_TMP/{page}.page.html" --raw`. With no running app, use the page's HTML file in the repo. Then:
|
||||
With a URL, save the page in the script that opens it. Aside: add `await fs.writeFile(path.join(pwd, "{page}.page.html"), await pg.evaluate(() => document.documentElement.outerHTML));`. Fallback engine: `_TMP=$(mktemp -d "${TMPDIR:-/tmp}/gstack-craft.XXXXXX"); $B js 'document.documentElement.outerHTML' --out "$_TMP/{page}.page.html" --raw`. With no running app, use the page's HTML file in the repo. A saved page holds session data: `rm -f` it in the block that checks it, and never copy it to the report directory.
|
||||
|
||||
```bash
|
||||
~/.claude/skills/gstack/bin/gstack-design-craft <path-to-page.html>
|
||||
```
|
||||
|
||||
Each `CRAFT_CHECK: <id> <count> <fix>` line is one category 9 finding under that id; `CRAFT_RESULT: clean` adds none. For a /design-html page with a `finalized.json` beside it, add `--accepted <finalized.json>`: each `CRAFT_ACCEPTED` line is a finding the user already accepted, listed with its reason, not filed again. Exit 2 (framework source, an empty or unreadable file): say the check was unavailable for that page and judge the tells below by eye. A hit the user's real content explains (a customer named Nexus, a supplied 50% figure) is noted, not filed. Saved pages stay out of the report directory; delete them after the check.
|
||||
Each `CRAFT_CHECK: <id> <count> <fix>` line is one category 9 finding under that id; `CRAFT_RESULT: clean` adds none; no `CRAFT_RESULT` line (framework source, an empty or unreadable file): say the check was unavailable for that page and judge the tells below by eye. For a page /design-html finalized, add `--accepted <its finalized.json>` (under `$GSTACK_STATE_ROOT/projects/$SLUG/designs/`): a `CRAFT_ACCEPTED` line is a finding the user accepted, listed with its reason, not filed again, except `needs-proof` and `lorem`, which a live page cannot carry. After a `cannot use exceptions` warning, file every line. A hit the user's real content explains (a customer named Nexus, a supplied 50% figure) is noted, not filed. Never add a `[Needs proof: …]` marker to a live page: file the claim, then cut it or ask.
|
||||
|
||||
### 2. What the check cannot see
|
||||
|
||||
@@ -56,17 +56,8 @@ A failed check costs the page its one job: file it as a High-impact finding.
|
||||
|
||||
### 4. Fix order and stack (Phase 8)
|
||||
|
||||
Within one impact level, take the biggest visible gain at the least risk first:
|
||||
1. Typeface and type roles
|
||||
2. Color and surfaces
|
||||
3. Hover, active and focus states
|
||||
4. Layout and spacing: grid, max width, the spacing scale, nested radius
|
||||
5. Motion
|
||||
6. Generic components
|
||||
7. Loading, empty and error states
|
||||
8. Copy
|
||||
9. Type-scale polish
|
||||
Within one impact level, take the biggest visible gain at the least risk first: typeface and type roles; color and surfaces; hover, active and focus states; layout and spacing (grid, max width, spacing scale, nested radius); motion; generic components; loading, empty and error states; copy; type-scale polish.
|
||||
|
||||
Work inside the existing stack: no framework or styling migration, and check the dependency file before importing a library. If the project uses Tailwind, read the installed major version (`tailwindcss` in package.json or the lockfile) before touching its config: v3 configures in `tailwind.config.*`, v4 in CSS (`@import "tailwindcss"` and `@theme`). Write for that version only.
|
||||
Work inside the existing stack: no framework or styling migration, and check the dependency file before importing a library. With Tailwind, read the installed major version (`tailwindcss` in package.json or the lockfile) before touching its config: v3 configures in `tailwind.config.*`, v4 in CSS (`@import "tailwindcss"` and `@theme`). Write for that version only.
|
||||
|
||||
Adapted from Elaya's [redesign-skill](https://github.com/elayadesign/redesign-skill) and [ai-design-skills](https://github.com/elayadesign/ai-design-skills) (MIT, © 2026 Elaya), rewritten for gstack. See NOTICE.md.
|
||||
@@ -1,16 +1,16 @@
|
||||
## Craft audit: Persuade pages and redesigns
|
||||
|
||||
Run this in Phases 1-6, on every audited page, when a page is Persuade (the classifier in the Design Hard Rules) or the user asked for a redesign or a craft audit. It adds what generated sites ship and a reviewer catches in seconds: invented content, missing ship basics, scroll-listener reveals, `100vh`, and a Persuade page's conversion basics. Its rules render from /design-html's craft section, so a page is audited against the rules it was built to.
|
||||
Run this in Phases 1-6, on every audited page, when a page is Persuade (the classifier in the Design Hard Rules) or the user asked for a redesign or a craft audit.
|
||||
|
||||
### 1. Run the craft check on each page
|
||||
|
||||
With a URL, save the page in the script that opens it. Aside: add `await fs.writeFile(path.join(pwd, "{page}.page.html"), await pg.content());`. Fallback engine: `_TMP=$(mktemp -d "${TMPDIR:-/tmp}/gstack-craft.XXXXXX"); $B js 'document.documentElement.outerHTML' --out "$_TMP/{page}.page.html" --raw`. With no running app, use the page's HTML file in the repo. Then:
|
||||
With a URL, save the page in the script that opens it. Aside: add `await fs.writeFile(path.join(pwd, "{page}.page.html"), await pg.evaluate(() => document.documentElement.outerHTML));`. Fallback engine: `_TMP=$(mktemp -d "${TMPDIR:-/tmp}/gstack-craft.XXXXXX"); $B js 'document.documentElement.outerHTML' --out "$_TMP/{page}.page.html" --raw`. With no running app, use the page's HTML file in the repo. A saved page holds session data: `rm -f` it in the block that checks it, and never copy it to the report directory.
|
||||
|
||||
```bash
|
||||
{{BIN_DIR}}/gstack-design-craft <path-to-page.html>
|
||||
```
|
||||
|
||||
Each `CRAFT_CHECK: <id> <count> <fix>` line is one category 9 finding under that id; `CRAFT_RESULT: clean` adds none. For a /design-html page with a `finalized.json` beside it, add `--accepted <finalized.json>`: each `CRAFT_ACCEPTED` line is a finding the user already accepted, listed with its reason, not filed again. Exit 2 (framework source, an empty or unreadable file): say the check was unavailable for that page and judge the tells below by eye. A hit the user's real content explains (a customer named Nexus, a supplied 50% figure) is noted, not filed. Saved pages stay out of the report directory; delete them after the check.
|
||||
Each `CRAFT_CHECK: <id> <count> <fix>` line is one category 9 finding under that id; `CRAFT_RESULT: clean` adds none; no `CRAFT_RESULT` line (framework source, an empty or unreadable file): say the check was unavailable for that page and judge the tells below by eye. For a page /design-html finalized, add `--accepted <its finalized.json>` (under `$GSTACK_STATE_ROOT/projects/$SLUG/designs/`): a `CRAFT_ACCEPTED` line is a finding the user accepted, listed with its reason, not filed again, except `needs-proof` and `lorem`, which a live page cannot carry. After a `cannot use exceptions` warning, file every line. A hit the user's real content explains (a customer named Nexus, a supplied 50% figure) is noted, not filed. Never add a `[Needs proof: …]` marker to a live page: file the claim, then cut it or ask.
|
||||
|
||||
### 2. What the check cannot see
|
||||
|
||||
@@ -34,17 +34,8 @@ A failed check costs the page its one job: file it as a High-impact finding.
|
||||
|
||||
### 4. Fix order and stack (Phase 8)
|
||||
|
||||
Within one impact level, take the biggest visible gain at the least risk first:
|
||||
1. Typeface and type roles
|
||||
2. Color and surfaces
|
||||
3. Hover, active and focus states
|
||||
4. Layout and spacing: grid, max width, the spacing scale, nested radius
|
||||
5. Motion
|
||||
6. Generic components
|
||||
7. Loading, empty and error states
|
||||
8. Copy
|
||||
9. Type-scale polish
|
||||
Within one impact level, take the biggest visible gain at the least risk first: typeface and type roles; color and surfaces; hover, active and focus states; layout and spacing (grid, max width, spacing scale, nested radius); motion; generic components; loading, empty and error states; copy; type-scale polish.
|
||||
|
||||
Work inside the existing stack: no framework or styling migration, and check the dependency file before importing a library. If the project uses Tailwind, read the installed major version (`tailwindcss` in package.json or the lockfile) before touching its config: v3 configures in `tailwind.config.*`, v4 in CSS (`@import "tailwindcss"` and `@theme`). Write for that version only.
|
||||
Work inside the existing stack: no framework or styling migration, and check the dependency file before importing a library. With Tailwind, read the installed major version (`tailwindcss` in package.json or the lockfile) before touching its config: v3 configures in `tailwind.config.*`, v4 in CSS (`@import "tailwindcss"` and `@theme`). Write for that version only.
|
||||
|
||||
Adapted from Elaya's [redesign-skill](https://github.com/elayadesign/redesign-skill) and [ai-design-skills](https://github.com/elayadesign/ai-design-skills) (MIT, © 2026 Elaya), rewritten for gstack. See NOTICE.md.
|
||||
@@ -8,7 +8,7 @@
|
||||
"id": "craft-audit",
|
||||
"file": "craft-audit.md",
|
||||
"title": "Craft audit: Persuade pages and redesigns",
|
||||
"trigger": "auditing a Persuade page or a redesign"
|
||||
"trigger": "a craft, Persuade or redesign audit"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
*
|
||||
* Derived in part from pbakaus/impeccable (Apache-2.0), modified. See NOTICE.md.
|
||||
*/
|
||||
import { DESIGN_SLOP_CATALOG, RENDERED, BANNED_FONTS, type DesignSlopEntry } from '../../lib/design-catalog';
|
||||
import { RENDERED, BANNED_FONTS, type DesignSlopEntry } from '../../lib/design-catalog';
|
||||
import { SENTINEL, DETECT_EXIT_ECHO } from '../../lib/design-detect-contract';
|
||||
|
||||
export const DESIGN_CHECKLIST_HEADER =
|
||||
@@ -34,7 +34,7 @@ export function checklistSlopEntries(): DesignSlopEntry[] {
|
||||
|
||||
/** Catalog rules a review may fix without asking: mechanical CSS changes with HIGH confidence. */
|
||||
export function autoFixEntries(): DesignSlopEntry[] {
|
||||
return DESIGN_SLOP_CATALOG.filter(e => e.tier === 'auto-fix');
|
||||
return RENDERED.filter(e => e.tier === 'auto-fix');
|
||||
}
|
||||
|
||||
function endsWithPunctuation(s: string): boolean {
|
||||
|
||||
@@ -19,9 +19,9 @@ const CRAFT_SECTION = path.resolve(import.meta.dir, '../../design-html/sections/
|
||||
export const DESIGN_CRAFT_RULE: ResolverFn = (_ctx, args) => {
|
||||
const lead = args?.join(':');
|
||||
if (!lead) throw new Error('{{DESIGN_CRAFT_RULE:<lead>}} requires the bold lead of a craft paragraph');
|
||||
const paragraph = fs.readFileSync(CRAFT_SECTION, 'utf-8').split(/\n{2,}/).find(p => p.startsWith(`**${lead}`));
|
||||
if (!paragraph) throw new Error(`{{DESIGN_CRAFT_RULE:${lead}}}: no paragraph starting "**${lead}" in design-html/sections/craft.md.tmpl`);
|
||||
return paragraph.trimEnd();
|
||||
const matches = fs.readFileSync(CRAFT_SECTION, 'utf-8').split(/\n{2,}/).filter(p => p.startsWith(`**${lead}.**`) || p.startsWith(`**${lead},`));
|
||||
if (matches.length !== 1) throw new Error(`{{DESIGN_CRAFT_RULE:${lead}}}: ${matches.length} paragraphs starting "**${lead}.**" or "**${lead}," in design-html/sections/craft.md.tmpl; need exactly one`);
|
||||
return matches[0].trimEnd();
|
||||
};
|
||||
|
||||
export const DESIGN_PERSUADE_CHECKS: ResolverFn = () => `1. **One primary action**, named; nothing competes with it above the fold, and the closing CTA repeats it.
|
||||
|
||||
@@ -25,8 +25,9 @@ const ROOT = path.resolve(import.meta.dir, '..', '..');
|
||||
|
||||
export const QA_ASSET_BLOCKER = 'If missing or unreadable, report a QA setup blocker and its affected probes as blocked; continue other safe probes (independent functional/static checks). Missing/unreadable assets block required QA.';
|
||||
|
||||
/** Skills carved on every host: QA (portable assets) and those whose external
|
||||
* render would exceed SKILL_BYTE_CEILING inlined (C4, #2777). */
|
||||
/** Skills carved on every host: QA (portable assets), those whose external
|
||||
* render would exceed SKILL_BYTE_CEILING inlined (C4, #2777), and design-review,
|
||||
* whose on-demand craft audit stays out of every skeleton (GSTA-92). */
|
||||
const CARVED_ON_EVERY_HOST = ['qa', 'qa-only', 'ship', 'plan-ceo-review', 'office-hours', 'design-review'];
|
||||
|
||||
/** Hosts read at most this many UTF-8 bytes of one SKILL.md (~40K tokens). */
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user