test: touchfiles, tiers, shim E2E, real-engine fixture

Touchfiles: the catalog, contract, detector bin, checklist resolver, review-
army resolver, and DESIGN.md lib join the dep lists of review-design-lite,
design-review-fix, the design-consultation cases, and plan-design-review-no-
ui-scope, so editing any of them re-selects the tests that read their output.
Three new E2E keys: design-review-detector-shim (gate; source mode on a
feature-branch diff), design-review-detector-shim-dom (gate; DOM mode: the
slop fixture served on loopback, dumped through the browse binary with
lib/dom-dump.js, persisted under a GSTACK_HOME-scoped REPORT_DIR, scanned once;
self-skips when browse/dist/browse is absent), and design-html-slop-gate
(periodic; one fix pass, at most two scans, remaining findings accepted with
reason). Every case reaches the engine through test/fixtures/fake-impeccable.ts
via IMPECCABLE_BIN from outside the temp repo, reads extracted skill sections
(never a whole SKILL.md) with the installed bin path rewritten to this
checkout, and asserts the probe ran, the right scan verb ran, `npx impeccable`
never did, and the output carries FINDING rows tagged [ai-color-palette] and
[low-contrast]. review-design-lite gets the fake engine and an eighth tally
signal for a detector row; its 4-hit threshold is unchanged.

test/gstack-design-detect.test.ts evaluates design-review's REPORT_DIR
expression with GSTACK_HOME set and proves a dump under it is accepted by the
wrapper's allow-list. The sample fixtures were real captures from commit 1
(engine 0.1.3), so there is nothing hand-written left to swap.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-09-08 16:38:46 +00:00
co-authored by Claude Fable 5.1
parent f1ce46eb8b
commit 9a03aa5e4a
6 changed files with 289 additions and 11 deletions
+26
View File
@@ -453,6 +453,32 @@ describe('scan', () => {
});
});
describe('design-review REPORT_DIR agrees with the allow-list', () => {
test.skipIf(!POSIX)('the template expression, evaluated with GSTACK_HOME set, lands under <gstack home>/projects/<slug>/designs/ and a dump there is scanned', () => {
const tmpl = fs.readFileSync(path.join(ROOT, 'design-review', 'SKILL.md.tmpl'), 'utf-8');
const m = tmpl.match(/^REPORT_DIR="(.+)"$/m);
expect(m).not.toBeNull();
const expr = m![1];
expect(expr.startsWith('${GSTACK_HOME:-$HOME/.gstack}/projects/$SLUG/designs/')).toBe(true);
const r = spawnSync('bash', ['-c', `SLUG=my-repo; echo "${expr}"`], { encoding: 'utf-8', timeout: 30_000, env: { PATH: process.env.PATH!, HOME: path.join(SANDBOX, 'fake-home'), GSTACK_HOME } });
const reportDir = r.stdout.trim();
expect(reportDir.startsWith(path.join(GSTACK_HOME, 'projects', 'my-repo', 'designs', 'design-audit-'))).toBe(true);
const dom = path.join(reportDir, 'dom', '120000-1');
fs.mkdirSync(dom, { recursive: true });
fs.writeFileSync(path.join(dom, 'home.dom.html'), '<html></html>');
const log = path.join(SANDBOX, 'argv-report.log');
fs.rmSync(log, { force: true });
try {
const s = run(['scan', '--format', 'gstack', dom + '/home.dom.html'], { env: { IMPECCABLE_BIN: FAKE, FAKE_IMPECCABLE_LOG: log } });
expect(s.err).not.toContain(SENTINEL.DETECT_REFUSED);
const argv = JSON.parse(fs.readFileSync(log, 'utf-8').trim().split('\n')[0]).argv as string[];
expect(argv.slice(2)).toEqual([fs.realpathSync(path.join(dom, 'home.dom.html'))]);
} finally {
fs.rmSync(path.join(GSTACK_HOME, 'projects'), { recursive: true, force: true });
}
});
});
describe('rules', () => {
test('prints every mapped id with kind/impact/tier/handoff and the tested engine versions', () => {
const r = run(['rules']);