diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0c4acfb76..185e5bcd0 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -352,6 +352,8 @@ Templates contain the workflows, tips, and examples that require human judgment. This is structurally sound — if a command exists in code, it appears in docs. If it doesn't exist, it can't appear. +The generator also owns two files that are not skill docs: `review/design-checklist.md` is rendered from `lib/design-catalog.ts` (through `scripts/resolvers/design-checklist.ts`), and `lib/dom-dump.js` is written from `lib/dom-dump-script.ts`. The checklist `/review` and `/ship` read and the DOM dump `/design-review` runs therefore cannot drift from the catalog and the script the templates describe; `test/design-checklist-sync.test.ts` pins both. + ### The preamble Every skill starts with a `{{PREAMBLE}}` block that runs before the skill's own logic. Since v1.71.0.0 the rendered block is a thin fence that invokes `bin/gstack-skill-start` (the consolidated preamble runtime — it replaced ~18KB of inline bash per tier-2+ skill) and reads back `KEY: value` STATUS lines that the skill prose branches on; `bin/gstack-skill-end` logs telemetry at skill end. One-time onboarding and consent text is emitted as session-bound `GSTACK_INSTRUCTION` blocks only when a runtime gate actually fires, instead of rendering in every skill. The startup still handles five things: diff --git a/CLAUDE.md b/CLAUDE.md index c23c19ab4..55bd25ace 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -106,8 +106,11 @@ engine) plus the `/browse` skill, `design/` design binary, `make-pdf/` PDF binary, `hosts/` typed host configs, `scripts/` build+DX tooling (gen-skill-docs, resolvers — `resolvers/aside.ts` is the Aside contract), `test/` validation+evals, `lib/` shared libraries (`aside-render.ts` renders -local HTML through Aside, falling back to the engine), `bin/` CLI utilities -(`gstack-render.ts` is the render CLI skills call), `extension/` Chrome +local HTML through Aside, falling back to the engine; `design-catalog.ts` is +the typed design anti-pattern catalog every design skill renders from), `bin/` +CLI utilities (`gstack-render.ts` is the render CLI skills call; +`gstack-design-detect.ts` and `gstack-design-md.ts` are the design detector +and open-DESIGN.md tools), `extension/` Chrome extension, one directory per skill (`ship/`, `review/`, `qa/`, ...), `.github/` CI, `contrib/` contributor tools, `docs/designs/` design documents. @@ -119,6 +122,13 @@ SKILL.md files are **generated** from `.tmpl` templates. To update docs: 2. Run `bun run gen:skill-docs` (or `bun run build` which does it automatically) 3. Commit both the `.tmpl` and generated `.md` files +The same `gen:skill-docs` run writes two more generated files from `lib/`: +`review/design-checklist.md` (from `lib/design-catalog.ts`, through +`scripts/resolvers/design-checklist.ts`) and `lib/dom-dump.js` (from +`lib/dom-dump-script.ts`). Edit the catalog or the script source, regenerate, +and commit both; never edit the generated file +(`test/design-checklist-sync.test.ts` fails on drift). + Generation uses each host's `defaultModel` (`claude` for existing hosts, `gpt` for Codex) unless `--model` is explicit. Codex installs additionally read the top-level model from `${CODEX_HOME:-~/.codex}/config.toml`; rerun diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d388f4f1..f4ca2a9f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,8 @@ gstack/ <- your working tree │ ├── src/ <- TypeScript source │ └── dist/ <- compiled binary (gitignored) ├── lib/ -│ └── aside-render.ts <- local-HTML rendering: Aside first, browse engine fallback +│ ├── aside-render.ts <- local-HTML rendering: Aside first, browse engine fallback +│ └── design-catalog.ts <- typed design anti-pattern catalog; review/design-checklist.md is generated from it ├── bin/ │ └── gstack-render.ts <- the CLI skills call to render a local HTML file └── ... @@ -188,6 +189,7 @@ eval files, and misses the strict classifier. No API keys needed. - **Aside render wrapper** (`test/aside-render.test.ts`) — Pins the option mapping and generated script of `lib/aside-render.ts` everywhere, and drives both engines hermetically with fake `aside` / `browse` executables (probe classification, the stdout contract, loopback-server policy, failure paths, the timeout kill, engine choice and the mid-run fallback); the live render (PDF + screenshot through a real Aside) runs only where Aside is open and self-skips elsewhere. make-pdf's render gates (`make-pdf/test/e2e/*-gate.test.ts`) and `test/skill-e2e-diagram.test.ts` are engine-agnostic: they run through whichever engine resolves (`browserAvailable()` — Aside, or the browse binary `bun run build:gates` compiles, which is what Linux CI does) and skip only when neither exists. - **Render CLI** (`test/gstack-render-cli.test.ts`) — Pins `bin/gstack-render.ts` against a fake daemon (`GSTACK_SKIP_ASIDE=1` + `GSTACK_BROWSE_BIN`): argv guards exit 1 with the usage line, `--help` exits 0, `ENGINE=` first then `OK ` then fenced `EVAL` / `PAGE_ERRORS`, `--serve-root` containment, the no-browser first line, and prompt exit after a successful render. `make-pdf/test/cli-exit-codes.test.ts` and `make-pdf/test/setup-smoke.test.ts` pin the `pdf` binary's error-to-exit-code map and `$P setup`'s engine report. - **Generator tests** (`test/gen-skill-docs.test.ts`) — Tests the template system: verifies placeholders resolve correctly, output includes value hints for flags (e.g. `-d ` not just `-d`), enriched descriptions for key commands (e.g. `is` lists valid states, `press` lists key examples). +- **Design detector, catalog, and DESIGN.md** (`test/gstack-design-detect.test.ts`, `test/design-detect-contract.test.ts`, `test/design-catalog.test.ts`, `test/design-checklist-sync.test.ts`, `test/design-md.test.ts`, `test/frontend-scope.test.ts`, `test/impeccable-fixtures.test.ts`) — Drive `bin/gstack-design-detect.ts` through the fake engine in `test/fixtures/fake-impeccable.ts` (probe order, the never-execute-a-repository-file rule, the `--changed` target allow-list, `design_detector: off`, analytics lines, output sanitizing), pin the catalog invariants and the generated `review/design-checklist.md`, round-trip the open DESIGN.md reader/writer, and check the real engine captures (`test/fixtures/impeccable-*.json`, engine 0.1.3) against the contract. `test/dom-dump-hygiene.test.ts` runs `lib/dom-dump.js` in a real Chromium page through the built browse binary; it self-skips without the binary and is opt-in outside CI (`GSTACK_DOM_DUMP_HYGIENE=1`). - **Tier-alignment invariant** (`test/e2e-tier-alignment.test.ts`) — For every self-gated `test/skill-e2e-*.test.ts` named in a touchfiles dep list, the file's `EVALS_TIER` self-gate must match its declared tier in `E2E_TIERS`. Kills the "inert demotion" class where a test is re-tiered in `touchfiles.ts` but the file still gates on the old tier and keeps running in the wrong lane. Unmapped or mixed-tier files are reported, never silently skipped. - **Catalog budget** (`test/catalog-budget.test.ts`) — Caps the aggregate discovery surface: the sum of every skill's frontmatter `name` + `description` (what every host loads at discovery, every session) must stay under 1,150 token-equivalents, with a 260-byte per-skill cap. Counting goes through the shared census in `test/helpers/skill-census.ts` (physical files vs authored skills vs registry entries — three deliberately different counts). Adding a skill? The failure message carries the re-measure + ratchet protocol. - **Context-budget ratchet** (`test/context-budget-ratchet.test.ts`) — CI ceilings on the two token ledgers the catalog budget doesn't cover: the always-on full-frontmatter aggregate and each skill's per-invocation eager tokens (SKILL.md + forced-read references), graded against `test/fixtures/context-budget.json` via `lib/context-bill.ts`. New skills fail until they have a ceiling; ceilings for removed skills must be pruned. Legitimate growth or a landed reduction: re-run `bun test/helpers/capture-context-budget.ts` and commit the refreshed fixture in the same commit, so the change is a visible decision in the diff. @@ -475,9 +477,9 @@ When Conductor creates a new workspace, `bin/dev-setup` runs automatically. It d ## Things to know -- **SKILL.md files are generated.** Edit the `.tmpl` template, not the `.md`. Run `bun run gen:skill-docs` to regenerate. +- **SKILL.md files are generated.** Edit the `.tmpl` template, not the `.md`. Run `bun run gen:skill-docs` to regenerate. The same run generates `review/design-checklist.md` from `lib/design-catalog.ts` and `lib/dom-dump.js` from `lib/dom-dump-script.ts`: edit those sources, never the generated files (`test/design-checklist-sync.test.ts` fails on drift). - **TODOS.md is the unified backlog.** Organized by skill/component with P0-P4 priorities. `/ship` auto-detects completed items. All planning/review/retro skills read it for context. -- **Browse, make-pdf, design, and `lib/` source changes need a rebuild.** If you touch `browse/src/*.ts`, `make-pdf/src/*.ts`, `design/src/*.ts`, or anything under `lib/` (the canonical `claude-bin.ts`, `error-handling.ts`, and `aside-render.ts` the binaries embed; `browse/src` re-exports them), run `bun run build`. `./setup` makes the same call on its own: it rebuilds when any of the three binaries is missing or when those sources, `package.json`, or `bun.lock` are newer than the browse binary (`test/setup-needs-build.test.ts` pins the decision). +- **Browse, make-pdf, design, and `lib/` source changes need a rebuild.** If you touch `browse/src/*.ts`, `make-pdf/src/*.ts`, `design/src/*.ts`, or anything under `lib/` (the canonical `claude-bin.ts`, `error-handling.ts`, and `aside-render.ts` the binaries embed, plus `design-catalog.ts`, whose `MOCKUP_NEVER_NAMES` the design binary's mockup prompt embeds; `browse/src` re-exports the first three), run `bun run build`. `./setup` makes the same call on its own: it rebuilds when any of the three binaries is missing or when those sources, `package.json`, or `bun.lock` are newer than the browse binary (`test/setup-needs-build.test.ts` pins the decision). - **Dev mode shadows your global install.** Project-local skills take priority over `~/.claude/skills/gstack`. `bin/dev-teardown` restores the global one. - **Conductor workspaces are independent.** Each workspace is its own git worktree. `bin/dev-setup` runs automatically via `conductor.json`. - **`.env` propagates across worktrees.** Set it once in the main repo, all Conductor workspaces get it. diff --git a/docs/PROJECT_STRUCTURE.md b/docs/PROJECT_STRUCTURE.md index 0e0859df4..c72a503f7 100644 --- a/docs/PROJECT_STRUCTURE.md +++ b/docs/PROJECT_STRUCTURE.md @@ -25,7 +25,7 @@ gstack/ │ ├── gen-agents-digest.ts # Generates the budget-capped instruction-tier digest (agents-digest/) │ ├── host-config.ts # HostConfig interface + validator │ ├── host-config-export.ts # Shell bridge for setup script -│ ├── resolvers/ # Template resolver modules (preamble, aside = the Aside driver contract + research, browse = $B fallback setup + command reference, design, review, gbrain, etc.) +│ ├── resolvers/ # Template resolver modules (preamble, aside = the Aside driver contract + research, browse = $B fallback setup + command reference, design, design-checklist = renders review/design-checklist.md from lib/design-catalog.ts, review, gbrain, etc.) │ ├── skill-check.ts # Health dashboard │ ├── test-free-shards.ts # Strict parallel free-suite runner (GSTACK_FREE_JOBS, opt-in flaky retry) │ ├── test-paid-shards.ts # Sharded paid-tier runner (one Bun process per shard) @@ -34,7 +34,7 @@ gstack/ │ └── dev-skill.ts # Watch mode ├── test/ # Skill validation + eval tests │ ├── helpers/ # skill-parser.ts, session-runner.ts, llm-judge.ts, eval-store.ts, aside-available.ts (Aside self-skip probe) -│ ├── fixtures/ # Ground truth JSON, planted-bug fixtures, eval baselines +│ ├── fixtures/ # Ground truth JSON, planted-bug fixtures, eval baselines, impeccable engine captures (impeccable-*.json, the dumped slop page, fake-impeccable.ts shim) │ ├── aside-driver.test.ts # Tier 1: pins the {{ASIDE_SETUP}} contract sentences + the fallback hand-off │ ├── aside-render.test.ts # Tier 1 pins + fake-executable runs on both engines + a live Aside render (self-skips without Aside) │ ├── gstack-render-cli.test.ts # Tier 1: bin/gstack-render.ts argv guards + output contract against a fake daemon @@ -47,7 +47,7 @@ gstack/ ├── plan-design-review/ # /plan-design-review skill (report-only design audit) ├── design-review/ # /design-review skill (design audit + fix loop) ├── ship/ # Ship workflow skill -├── review/ # PR review skill +├── review/ # PR review skill (checklist.md is hand-written; design-checklist.md is GENERATED from lib/design-catalog.ts) ├── plan-ceo-review/ # /plan-ceo-review skill ├── plan-eng-review/ # /plan-eng-review skill ├── autoplan/ # /autoplan skill (auto-review pipeline: CEO → design → DX → eng, eng always last) @@ -84,7 +84,7 @@ gstack/ ├── lib/ # Shared libraries (aside-render.ts = local-HTML rendering, Aside first, engine fallback; design-catalog.ts = the typed design anti-pattern catalog every design skill renders from; design-detect-contract.ts = detector sentinel vocabulary; design-md.ts = open DESIGN.md reader/writer; dom-dump-script.ts + generated dom-dump.js = rendered-DOM dump for the detector; frontend-scope.ts; claude-bin.ts, error-handling.ts, worktree.ts, egress-receipt.ts, context-bill.ts, redact-engine.ts, tracker-guard.ts, version-source.ts, code-intelligence/) │ └── diagram-render/ # Vendored mermaid + excalidraw runtimes, built into one offline bundle the renderer loads ├── patches/ # bun `patchedDependencies` patches (playwright-core windowsHide) -├── docs/designs/ # Design documents (incl. fork-port-residual-2026-09/ evaluation evidence) +├── docs/designs/ # Design documents (incl. IMPECCABLE_INTEROP.md = the design detector / catalog / open DESIGN.md record, and fork-port-residual-2026-09/ evaluation evidence) ├── setup-deploy/ # /setup-deploy skill (one-time deploy config) ├── .github/ # CI workflows + shared composite actions (.github/actions/) + Docker image (claude CLI pinned) │ ├── workflows/ # evals.yml (E2E on Ubicloud), quality-gate.yml (secret scan), dependency-review.yml, osv-scanner.yml, skill-docs.yml, actionlint.yml, and 8 more (windows, periodic evals, release gates, ci-image) diff --git a/docs/TESTING_INTERNALS.md b/docs/TESTING_INTERNALS.md index 4e16f0d40..8509df296 100644 --- a/docs/TESTING_INTERNALS.md +++ b/docs/TESTING_INTERNALS.md @@ -86,6 +86,12 @@ Chromium can launch, so on an install where the best-effort Chromium step was skipped (`GSTACK_SKIP_PLAYWRIGHT=1`) or failed, these gates run and fail at browser launch instead of skipping. Fix the bootstrap (or move the binary aside) before running them locally; CI always installs Chromium first. +`test/dom-dump-hygiene.test.ts` is the one free-suite case on the same leg: it +runs `lib/dom-dump.js` (the rendered-DOM dump `/design-review` hands the design +detector) inside a real Chromium page through the built browse binary, so it +self-skips when the binary is absent and, because a cold Chromium launch is +load-sensitive on a busy dev box, runs only in CI or on explicit opt-in +(`GSTACK_DOM_DUMP_HYGIENE=1`). **Free suite (`bun run test:free`).** `scripts/test-free-shards.ts` runs N concurrent shard processes (serial within each) with strict-output diff --git a/docs/skills.md b/docs/skills.md index db680a41b..994f8e1d1 100644 --- a/docs/skills.md +++ b/docs/skills.md @@ -324,6 +324,8 @@ After you agree on the system, it generates an interactive HTML preview page — Then it writes `DESIGN.md` to your repo root — your project's design source of truth — and updates `CLAUDE.md` so every future Claude Code session respects the system. From that point on, `/design-review` can audit against it, and any agent working on your frontend knows the rules. +The file is written in the open DESIGN.md format ([google-labs-code/design.md](https://github.com/google-labs-code/design.md)): every token in YAML front matter (`colors`, `typography`, `rounded`, `spacing`, `components`), the rationale in the spec's canonical sections, so impeccable, Google Stitch, and anything else that reads the format share one file. If you already have a legacy gstack `DESIGN.md`, the skill offers a conversion once (a backup is kept) and records your answer in the file so it never asks again. A `PRODUCT.md` in the repo root prefills the product questions instead of re-asking them. + ### Example ``` @@ -509,6 +511,8 @@ Not every page needs the full Pretext engine. The skill reads the design and pic 7. Surgical edits via the Edit tool (not full regeneration) 8. Repeat until you say "done" +**Slop gate.** If you have [impeccable](https://impeccable.style) installed, the finalized page gets one scan through its engine before the verification screenshots: findings trigger a single surgical fix pass over the non-advisory rules, then one more scan. Whatever remains is presented as accepted-with-reason (the approved mockup contains it, `DESIGN.md` blesses it, or you agreed to an inline `impeccable-disable` comment). One pass, never a loop; without impeccable the step is skipped silently. The skill's never-include list carries the same rule ids the detector reports, from `lib/design-catalog.ts`. + ### Framework detection If your project uses React, Svelte, or Vue (detected from `package.json`), the skill offers to generate a framework component instead of vanilla HTML. Framework output uses `npm install @chenglou/pretext` instead of inline vendoring. @@ -572,6 +576,8 @@ Findings get action, not just listed. Obvious mechanical fixes (dead code, stale One exception: a shortcut you took deliberately and logged. A `gstack-shortcut(dec-)` marker whose decision id resolves in the decision ledger downgrades the finding to acknowledged debt. An orphan marker — one with no ledger entry behind it — doesn't suppress anything; the gap is reported normally and the marker itself gets flagged. +**Design pass.** When the diff touches frontend files, the Design specialist reads `review/design-checklist.md`, which is generated from `lib/design-catalog.ts`, so `/review`, `/ship`, and `/design-review` flag the same patterns under the same rule ids. If you have [impeccable](https://impeccable.style) installed, its engine scans the changed frontend files first: its rows bucket by tier (auto-fix, ask, possible), a detector hit and a checklist hit at the same file:line collapse into one row, and your repo's `.impeccable/config*.json` ignores are read as settled decisions. Without it, the checklist pass runs alone. + ### Example Suppose the smart listing flow is implemented and the tests are green.