mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
* feat(design-lint): copy tier reports each tell at its line; empty-mount-node; content-realism ids at ask
The copy tier holds its own patterns (from lib/design-craft.ts) and reads
visible text, script bodies, inline handlers and parsed tags, so payloads,
commented markup and script text no longer count. A text-less body that
loads a script reports empty-mount-node. placeholder-name, placeholder-brand,
lorem, round-number and needs-proof move to ask so a page-specific waiver
answers them (Garry 2026-10-26, option a).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(design-html,design-review): run gstack-design-lint; waivers replace craft_exceptions; remove gstack-design-craft
/design-html Step 4 and /design-review's craft audit call the lint and read
its exit code. A waiver comment in the page replaces --accepted and
finalized.json craft_exceptions (supersedes #3422's persisted exceptions).
bin/gstack-design-craft, lib/design-craft.ts and its test are removed; the
handoff tests move to test/design-craft-resolvers.test.ts. The slop-gate paid
case and its touchfiles follow the lint.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(design-lint): the content-realism tells keep one checker, credited in NOTICE.md
Names the removed lib/design-craft.ts and test/design-craft.test.ts so pregate
selects a lane for the deletions (PREGATE_NO_LANE at 17635b6a7).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes
- /design-review files only craft-gate lint lines on a saved page, whose
linked CSS the lint cannot load (false missing-focus-visible blocks)
- missing-aria-current counts route links in one pass (unclosed navs were
superlinear); inline-handler tells report at the value's line; a shell
with no <body> tag is an empty mount node
- /design-html says the copy tier reads HTML only, and that a supplied
figure may also need an invented-proof waiver
- tests pin the five content-realism ids at ask and a waived copy tell
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(changelog): the saved-page craft audit files copy-tier lines only
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(design-lint): forward references say a later phase, not Phase 2
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(carve-guards): re-pin design-review and design-html parity to the merged tree's measured sizes
design-review skeleton 137735, union 144802, ratio 1.3681 (all lower);
design-html skeleton 55834, union 78626 (caps unchanged, comments updated).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
61 lines
3.5 KiB
TypeScript
61 lines
3.5 KiB
TypeScript
/**
|
|
* The lint over the repository's recorded design slop page (the review eval's
|
|
* ground truth, test/fixtures/review-eval-design-slop.{html,css}): every issue
|
|
* the page annotates that the lint has a rule for is reported, the clean
|
|
* fixture is not, and both agree with hallmark's own sloplint on the same
|
|
* files (checked by hand at hallmark 7ec8682; the gate set is identical).
|
|
* Phase 1 of the hallmark absorption plan, item D7: the free regression that a
|
|
* recorded output keeps linting the same way.
|
|
*/
|
|
import { describe, expect, test } from 'bun:test';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { lint } from '../lib/design-lint';
|
|
import { exitCodeOf, formatSummary } from '../lib/design-lint/report';
|
|
|
|
const ROOT = path.resolve(import.meta.dir, '..');
|
|
const FIXTURES = path.join(ROOT, 'test', 'fixtures');
|
|
|
|
/** The recorded page as it was served: index.html beside the styles.css it links. */
|
|
function recordedPage(): string {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'design-lint-recorded-'));
|
|
fs.copyFileSync(path.join(FIXTURES, 'review-eval-design-slop.html'), path.join(dir, 'index.html'));
|
|
fs.copyFileSync(path.join(FIXTURES, 'review-eval-design-slop.css'), path.join(dir, 'styles.css'));
|
|
return dir;
|
|
}
|
|
|
|
describe('design-lint over the recorded review slop page', () => {
|
|
test('each annotated issue the lint has a rule for is reported, with its line, and the page exits 2', async () => {
|
|
const dir = recordedPage();
|
|
try {
|
|
const r = await lint([dir], { cwd: dir });
|
|
const at = (rule: string) => r.findings.filter((f) => f.rule === rule).map((f) => [f.grade, path.basename(f.file), f.line]);
|
|
// Issue 1: the blacklisted face (Papyrus) is a banned face in any role
|
|
expect(at('banned-font')).toEqual([['ask', 'styles.css', 6]]);
|
|
// Issue 5: the indigo-to-violet gradient is the banned gradient
|
|
expect(at('purple-blue-gradient')).toEqual([['blocks', 'styles.css', 14]]);
|
|
// The page declares no --color-* or --font-* token, so its literals bypass nothing and literal-outside-tokens stays quiet
|
|
expect(at('literal-outside-tokens')).toEqual([]);
|
|
// Issue 3: outline: none with nothing in its place
|
|
expect(at('missing-focus-visible')).toEqual([['blocks', 'styles.css', 1]]);
|
|
// Issue 7: the three-column icon-tile grid and its coins
|
|
expect(at('template-cards')).toEqual([['ask', 'styles.css', 33]]);
|
|
expect(at('icon-coin')).toEqual([['polish', 'styles.css', 51]]);
|
|
// Issue 6: generic hero copy, the two lines the design catalog names for it and the buzzword behind them
|
|
expect(at('generic-hero-copy')).toEqual([['blocks', 'index.html', 12], ['blocks', 'index.html', 13]]);
|
|
expect(at('marketing-buzzword')).toEqual([['ask', 'index.html', 32], ['ask', 'index.html', 37]]);
|
|
// Tells the page does not annotate but hallmark's sloplint reports on the same files
|
|
expect(at('pure-black-white').map((f) => f[2])).toEqual([17, 63, 85]);
|
|
expect(at('overflow-x-unclipped')).toEqual([['ask', 'styles.css', 1]]);
|
|
expect(at('off-scale-spacing')).toEqual([['ask', 'styles.css', 78]]);
|
|
expect(at('skipped-heading')).toEqual([['polish', 'index.html', 21]]);
|
|
expect(r.findings.some((f) => f.grade === 'waived')).toBe(false);
|
|
expect(exitCodeOf(r.summary)).toBe(2);
|
|
expect(formatSummary(r.summary)).toMatch(/^LINT_SUMMARY: blocks=7 ask=\d+ waived=0 polish=4 render=skipped: not requested$/);
|
|
} finally {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|