Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
a714e16
docs(boringstack): Phase C build-loop plan + spec updates (port isola…
agjs Jul 12, 2026
3b6b39a
feat(boringstack): resource planner
agjs Jul 12, 2026
ec4dd67
feat(boringstack): deterministic API wiring
agjs Jul 12, 2026
40b0b3e
test(boringstack): assert wiring preserves existing content
agjs Jul 12, 2026
19149e2
feat(boringstack): generator + db:push runner
agjs Jul 12, 2026
713087a
test(boringstack): assert prettier in generate order + match Exec sig…
agjs Jul 12, 2026
7c9087d
feat(boringstack): full-repo gate runner
agjs Jul 12, 2026
52b0470
feat(boringstack): refine prompt
agjs Jul 12, 2026
dfe37fb
feat(boringstack): build driver
agjs Jul 12, 2026
2e0862d
fix(boringstack): inject generateResource + real driver tests + match…
agjs Jul 12, 2026
f642be8
feat(boringstack): headless entry (TSFORGE_BORINGSTACK) drives the fu…
agjs Jul 12, 2026
ee7649b
fix(boringstack): run gate on disk like a dev (bun install + host val…
agjs Jul 12, 2026
cc9a6ba
chore: align tooling with boringstack (prettier 3.9.4, eslint 10.6.0,…
agjs Jul 12, 2026
b52fdb3
fix(boringstack): implement generates the FULL slice (API + UI featur…
agjs Jul 12, 2026
c2daccf
refactor(boringstack): judge reads real resource code, dedupe toCamel…
agjs Jul 12, 2026
16a5500
refactor(self-harness): remove the UI-only web build corpus (evaluate…
agjs Jul 12, 2026
63f8ed8
refactor(self-harness): retarget campaign from the removed web corpus…
agjs Jul 12, 2026
8328b9c
feat(boringstack): wait for API readiness before generate:api (fixes …
agjs Jul 12, 2026
3149b12
fix(boringstack): make generateResource/generateFeature idempotent on…
agjs Jul 12, 2026
d2b9837
feat(greenfield): feed the failing attempt's gate/judge output into t…
agjs Jul 12, 2026
10d9bcf
feat(boringstack): push BoringStack API conventions into the refine p…
agjs Jul 12, 2026
e0671a3
refactor(boringstack): headless-build is now BoringStack-only (remove…
agjs Jul 12, 2026
7274db3
refactor(boringstack): remove dead web-build eval scripts (interactiv…
agjs Jul 12, 2026
c7ece6b
refactor(boringstack): remove the UI-only web build from the interact…
agjs Jul 12, 2026
77a2767
refactor(boringstack): remove the UI-only staged web-build engine + s…
agjs Jul 12, 2026
e9628ba
refactor(boringstack): remove orphaned web modules + untangle web-gat…
agjs Jul 12, 2026
432c0bb
test(boringstack): restore web-fetch/search/browse tool tests (C6 col…
agjs Jul 12, 2026
8a47064
refactor(loop): decouple pull_conventions from the web flag — offer p…
agjs Jul 12, 2026
8f6c9ca
refactor(loop): gate the convention PUSH on the backend, symmetric wi…
agjs Jul 12, 2026
3c23589
fix(boringstack): wire new table into tests/helpers/db + auto-format …
agjs Jul 12, 2026
73ad2d0
fix(boringstack): baseline-aware differential gate — judge only NEW f…
agjs Jul 12, 2026
b47f1d2
fix(boringstack): auto-fix pass also runs eslint --fix, not just pret…
agjs Jul 12, 2026
4f4caba
docs: retire the removed UI-only web scaffold, document the BoringSta…
agjs Jul 12, 2026
ad1ed13
feat(boringstack): expert-model rescue before a feature parks as stuck
agjs Jul 12, 2026
f7467dd
fix(boringstack): planner emits one resource per ENTITY, not per layer
agjs Jul 12, 2026
67f91b8
fix(boringstack): normalize the scaffold (regen + format) before base…
agjs Jul 12, 2026
f29306c
feat(boringstack): expert rescue engages on JUDGE failures, not just …
agjs Jul 12, 2026
d45843f
feat(scaffold): per-project host-port isolation (dynamic ports)
agjs Jul 12, 2026
0138d68
fix(boringstack): don't print '0 baseline failures hidden' on a green…
agjs Jul 12, 2026
4f355e7
refactor(boringstack): drop unused params instead of underscore-prefi…
agjs Jul 12, 2026
b5fe06d
feat(scaffold): wizard collects an optional initial superuser; fix st…
agjs Jul 12, 2026
e7a6726
docs(spec): interactive planning phase (spec-driven greenfield builds)
agjs Jul 12, 2026
89143a2
docs(plan): interactive planning phase implementation plan (8 tasks)
agjs Jul 12, 2026
8155331
feat(models): planner capability role
agjs Jul 13, 2026
104c62d
feat(planning): product-plan type + .specs artifact I/O
agjs Jul 13, 2026
b2763cd
fix(plan-store): type-safe screen validation & rejection test coverage
agjs Jul 13, 2026
e5f9519
fix(tests): remove duplicate test blocks in plan-store.test.ts
agjs Jul 13, 2026
bb18e35
feat(planning): planner proposes a structured product plan
agjs Jul 13, 2026
354c151
feat(planning): approved-plan gate helper
agjs Jul 13, 2026
df0e93c
feat(boringstack): refinePrompt carries the approved plan slice as co…
agjs Jul 13, 2026
606067f
feat(boringstack): require an approved plan; build from its slices wi…
agjs Jul 13, 2026
6733312
feat(planning): interactive plan → propose → approve orchestration
agjs Jul 13, 2026
01c358b
feat(planning): wizard→planning flow, REPL interception, headless --p…
agjs Jul 13, 2026
348150f
fix(task-8): replace REPL auto-approve with human-gated plan review +…
agjs Jul 13, 2026
8cbaaea
fix(planning): final-review cleanups + prettier
agjs Jul 13, 2026
72495f7
fix(planning): pin planner output schema so the live model produces a…
agjs Jul 13, 2026
41471bc
fix(boringstack): make scaffold→boot→build hands-off (db:push --force…
agjs Jul 13, 2026
0aa390d
fix(boringstack): let the model add real domain columns (was trapped …
agjs Jul 13, 2026
a8d1059
fix(boringstack): import-column-builders prompt rule + self-healing A…
agjs Jul 13, 2026
cb0a634
fix(tools): remove edit-size cap — stop forcing surgical edits (the 3…
agjs Jul 13, 2026
bf1ba7f
fix(boringstack): auto-wire the UI feature route (built pages were un…
agjs Jul 13, 2026
758586a
fix(boringstack): point FRONTEND_URL at the isolated UI port (email l…
agjs Jul 13, 2026
d9c6d97
fix(boringstack): seed feature i18n keys so pages render text, not ra…
agjs Jul 13, 2026
44030bf
feat(boringstack): first-class reachability gate — a feature isn't 'v…
agjs Jul 14, 2026
913563d
fix(boringstack): scope locale files in + instruct i18n keys (model w…
agjs Jul 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Keep tsforge's dependencies — especially the shared tooling it must keep on par
# with BoringStack (prettier / eslint / typescript-eslint) — current, so a version
# drift never silently breaks a BoringStack build the harness gates.
version: 2
updates:
- package-ecosystem: "npm"
directories:
- "/"
- "/packages/core"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
groups:
# Bump the lint/format/type toolchain together so it moves as one unit
# (mismatched patch versions are exactly what caused a formatting drift).
dev-toolchain:
patterns:
- "prettier"
- "eslint"
- "eslint-*"
- "@typescript-eslint/*"
- "typescript-eslint"
- "typescript"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
1 change: 0 additions & 1 deletion apps/docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ export default defineConfig({
{ label: "Stopping bad output early", link: "/uplift/ttsr/" },
{ label: "Learning from past runs", link: "/uplift/memory/" },
{ label: "Greenfield scaffolding", link: "/scaffold/boringstack/" },
{ label: "Web scaffolding (legacy)", link: "/scaffold/web/" },
{ label: "Model adapter", link: "/inference/adapter/" },
{ label: "Token metrics", link: "/observability/metrics/" },
{ label: "Trace a run", link: "/observability/trace/" },
Expand Down
8 changes: 4 additions & 4 deletions apps/docs/src/components/DocMap.astro
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ const cards: readonly IDocCard[] = [
linkLabel: "Language server →",
},
{
title: "Web scaffolding",
description: "Spin up a fresh Vite + React app inside a session, checked all the way to a browser smoke test.",
href: "/scaffold/web/",
linkLabel: "Web scaffolding →",
title: "Greenfield scaffolding",
description: "Stand up a full BoringStack app and build it resource-by-resource against the project's own gate.",
href: "/scaffold/boringstack/",
linkLabel: "Greenfield scaffolding →",
},
{
title: "Guardrails",
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/components/HarnessBoardDiagram.astro
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const subsystems: readonly ISubsystem[] = [
},
{
title: "GATE",
subtext: "tsc, ESLint, tests, browser smoke",
subtext: "tsc, ESLint, tests",
href: "/loop/gate-floor/",
},
{
Expand Down
6 changes: 3 additions & 3 deletions apps/docs/src/components/landing/Capabilities.astro
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ const capabilities: Capability[] = [
href: "/guardrails/policy/",
},
{
title: "Web scaffolding",
body: "Bootstraps a building Vite + React app for the model to fill in.",
href: "/scaffold/web/",
title: "Greenfield scaffolding",
body: "Stands up a full BoringStack stack and builds it feature-by-feature.",
href: "/scaffold/boringstack/",
},
{
title: "Opt-in oracles",
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/agent/model-agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ One approved task can involve many agent cycles until the gate passes or tsforge
| Script | `script` | always (programmatic tool calling — batch multi-step tool use in one program); withhold with `TSFORGE_NO_SCRIPT=1` for eval |
| Navigation | `search`, `symbol_search`, `find_references`, `type_at`, `diagnostics`, `rename_symbol`, `move_file`, `organize_imports` | existing-code repos |
| Git context | `git_context` | existing-code repos (read-only: diff/log/blame/show to scope a change) |
| Web | `scaffold_web`, `scaffold_ui`, `scaffold_routes`, `add_dependency` | web builds |
| Conventions | `pull_conventions` | build backends that ship a convention library (BoringStack) — fetch a how-to guide on demand |
| Web research | `package_info`, `package_docs`, `web_fetch`, `web_search`, `web_browse` | when **Web tools** is on in `/config` (no required API keys or paid browser/search service) |

On greenfield specs, navigation tools are often withheld so the model focuses on creating files instead of exploring an empty tree. See [TypeScript language server](/lsp/typescript-server/).
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/src/content/docs/big-picture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ tsforge is opinionated about TypeScript because the language gives you machine-c
- **The language server** (same engine as VS Code's TypeScript support) runs in-process for symbol search, rename, and diagnostics on every write. See [TypeScript language server](/lsp/typescript-server/).
- **ESLint packs** encode patterns `tsc` cannot catch (component file layout, no `console.log`, Drizzle query shape, etc.). See [Rule packs](/guardrails/rule-packs/).

For greenfield web apps, the harness can scaffold Vite + React and switch the gate to a build + browser smoke ladder. See [Web scaffolding](/scaffold/web/).
For greenfield web apps, the harness stands up the full BoringStack stack (Bun + Elysia + Drizzle API and a Vite/React UI) and builds it resource-by-resource against BoringStack's own gate. See [Greenfield scaffolding](/scaffold/boringstack/).

## Models and deployment

Expand All @@ -78,7 +78,7 @@ The default `http://localhost:8000/v1` is a convenience for local inference serv
| [How the gate is built](/loop/gate-floor/) | What acceptance check tsforge runs and how it picks tsc + ESLint |
| [When the gate fails](/loop/validation/) | Repair loop, stop conditions, error feedback to the model |
| [TypeScript language server](/lsp/typescript-server/) | Navigation tools and write-time diagnostics |
| [Web scaffolding](/scaffold/web/) | Bootstrapping a Vite app inside the loop |
| [Greenfield scaffolding](/scaffold/boringstack/) | Standing up a new BoringStack full-stack app |
| [Stack detection](/guardrails/stack-detection/) | Which ESLint packs turn on for your dependencies |
| [Rule packs](/guardrails/rule-packs/) | What each pack enforces |
| [Fix bad tool calls](/uplift/repair-ladder/) | Malformed tool JSON fixed before re-asking the model |
Expand Down
3 changes: 1 addition & 2 deletions apps/docs/src/content/docs/cli/interactive.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Most users run `tsforge` and stay in the interactive session.
| --- | --- | --- |
| **Interactive** | `tsforge` | default — open-ended tasks, steering, exploration |
| **One-shot** | `tsforge "task" --accept "gate cmd"` | drive a single task to green and exit |
| **Web build** | add `--web` | scaffold Vite app + web gate ladder on first message |
| **New app** | `tsforge scaffold` | stand up a full-stack [BoringStack](/scaffold/boringstack/) project, then build it with the [greenfield loop](/loop/greenfield/) |
| **Plan mode** | default — **Shift+Tab** or `/plan` to switch | read-only exploration before implementing — see [Plan mode](/cli/plan-mode/) |

## Flags
Expand All @@ -26,7 +26,6 @@ Most users run `tsforge` and stay in the interactive session.
| `--files <globs>` | narrow editable scope (comma-separated); default is whole repo (vendored/generated files are [always excluded](/edit/engine/#vendored--read-only-scope)) |
| `--accept` / `--gate <cmd>` | gate command for one-shot mode |
| `--no-gate` | skip auto gate detection |
| `--web` | pre-scaffold web stack + web gate on first build message |
| `--browser <html>` | append headless render check to gate |
| `--plan` | force plan mode on for an interactive session — plan is the default anyway, so this only matters to override a repo that configured an autonomous `policy.mode`; ignored by one-shot/headless |
| `--continue` / `-c` | resume latest saved session for this dir |
Expand Down
26 changes: 8 additions & 18 deletions apps/docs/src/content/docs/cli/plan-mode.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ Plan mode is a safety rail for ambiguous work. The model can **read** your repo

- Press **Shift+Tab** to cycle the mode (plan → normal → …), or type **`/plan`** to toggle it
- When the plan looks right, reply **`approve`**, **`go`**, or **`lgtm`** — the model implements it
- Web builds also accept **`yes`** / **`ok`** at the design checkpoint

There is no disable *flag*: it's a mode you cycle with Shift+Tab. (`tsforge --plan` forces plan mode on for an interactive session even in a repo that configured an autonomous `policy.mode` — one-shot and headless runs are autonomous regardless.)

Expand All @@ -31,27 +30,18 @@ Good for: "explore this repo and tell me how you'd refactor auth" or "what files

After approval, tsforge switches to the normal implement loop with full write tools.

## Staged web builds
## Whole-app builds

For new apps (`--web` or `scaffold_web`), plan mode splits work into two phases:

```mermaid
flowchart LR
scaffold[scaffold_web] --> design[Design phase]
design --> checkpoint[You approve]
checkpoint --> implement[Implement phase]
implement --> gate[Full web gate]
```

1. **Design phase**: types, services, route list. Lighter gate (types + lint, no Vite build yet).
2. **Checkpoint**: model shows the plan; you approve.
3. **Implement phase**: routes and UI get filled in. Full web gate runs.

This catches contract mistakes before UI work piles up.
Plan mode is for **exploring and approving a change** in a repo. Building a *new*
app from scratch is a different flow: [`tsforge scaffold`](/scaffold/boringstack/)
stands up BoringStack, then the [greenfield loop](/loop/greenfield/) drives the app
to green one resource at a time — the harness runs the generators + wiring and the
model fills the domain, each feature verified and frozen before the next. That loop
plans and checkpoints per feature on its own, so it doesn't need plan mode.

## Leaving plan mode

- **Shift+Tab** (or `/plan`) drops to normal mode for hands-on edits in a repo you know
- One-shot runs (`tsforge "task" --accept …`) and headless/eval runs are autonomous already — plan mode is interactive-only, since it needs a human to approve

→ [Web scaffolding](/scaffold/web/) · [Interactive CLI](/cli/interactive/)
→ [Greenfield scaffolding](/scaffold/boringstack/) · [Interactive CLI](/cli/interactive/)
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/cli/setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ One choice drives both halves of the harness, so they can never disagree:

Setup writes `tsforge.config.json` (merged — your `mcpServers`, `plugins`, and `policy` are preserved) plus `.tsforge/setup-evidence.json` for audit. Only the choices that differ from the defaults are written, so the file stays minimal. Re-running setup re-decides all four conventions, so the file always matches what the overview showed.

Conventions govern your everyday sessions (the auto gate, the write-time linter, and the prompts). Scaffolding a brand-new app with `--web` uses tsforge's house style for the generated skeleton, regardless of `conventions` — it's greenfield code tsforge writes itself.
Conventions govern your everyday sessions (the auto gate, the write-time linter, and the prompts). Scaffolding a brand-new app with [`tsforge scaffold`](/scaffold/boringstack/) is different — it stands up BoringStack and builds against **BoringStack's own** gate and conventions, not your `conventions` setting.

## Non-interactive use

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/edit/engine.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Writes a new file only. Refuses to overwrite an existing path. Creates parent di

## Vendored & read-only scope

In a web-scaffold session one path is **read-only**: the generated TanStack route tree.
In a full-stack [BoringStack build](/scaffold/boringstack/), generated paths are **read-only**: the TanStack route tree and the OpenAPI-derived typed client.

| Path | Why it's read-only |
| --- | --- |
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/guardrails/rule-packs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ When the `react-component-architecture` pack is enabled (React in `package.json`
| `component-folder-structure` | A component `.tsx` must live in `src/views/<Feature>/components/`, be the view root `src/views/<Feature>/index.tsx`, or a shared primitive in `src/components/ui/` |
| `no-cross-feature-imports` | Cross-feature runtime imports |

`component-file-purity` and `component-folder-structure` enforce the **views layout** that scaffolded web apps use — see [Web scaffolding → Project layout](/scaffold/web/#project-layout-views). On a **scaffolded web build** these run at **error** (they are part of the bundled web gate). When tsforge edits an **existing** repo, the architecture rules follow your [profile](/guardrails/config/#profiles): off in `recommended`, on in `opinionated` (or turn individual rules on in `rules`).
`component-file-purity` and `component-folder-structure` enforce a **views layout** for React code. On a [BoringStack build](/scaffold/boringstack/) they run at **error** as part of the project's own gate. When tsforge edits an **existing** repo, the architecture rules follow your [profile](/guardrails/config/#profiles): off in `recommended`, on in `opinionated` (or turn individual rules on in `rules`).

### No computation inside JSX

Expand Down
48 changes: 19 additions & 29 deletions apps/docs/src/content/docs/loop/gate-floor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -105,40 +105,30 @@ Two extra gate steps prove the work is real, not just well-formed. Both default

Coverage checks **function** coverage as well as lines, so a one-line export that's never exercised can't hide behind a "line executed at declaration" count.

## Web apps get a longer gate
## Full-stack (BoringStack) builds use the project's own gate

If the session scaffolds a new browser app (`scaffold_web` or `tsforge --web`), the gate grows. "Done" then roughly means: Vite build passes, types pass, lint passes, route stubs are filled in, formatting is clean, and a headless browser smoke test passes.
A web app built via [`tsforge scaffold`](/scaffold/boringstack/) doesn't get a
tsforge-invented gate — "done" is **BoringStack's own** composed gate, run exactly as
a developer runs it:

Details: [Web scaffolding](/scaffold/web/).

### Staged gate progress & failures

The web gate runs as **named stages** rather than one opaque `&&` chain. Each stage prints a `━━ <label> ━━` banner and streams its output live; a passing stage prints `✓ <label>`. On the first failure the runner prints

```
✗ <label> FAILED (exit N)
```

and **stops** — later stages don't run, and the failing stage's exit code is preserved. So when a build goes red, both you and the agent's feedback loop see *which* stage broke (`vite build`, `typecheck`, `lint`, `type-aware lint`, `stub check`, `format`, `tests`, or `browser smoke`) instead of a wall of interleaved output. The core (non-web) gate is short enough that it stays a plain command chain.

Add a one-off page render check with `--browser path/to/index.html`.

### Accessibility, screenshots, and a perf budget

The browser smoke can do more than check that a page renders. These extra oracles are **opt-in** (the default smoke is unchanged):

| Env var | Adds |
| --- | --- |
| `TSFORGE_A11Y=1` | [axe](https://github.com/dequelabs/axe-core) accessibility checks on every route — **serious/critical** violations (including colour contrast) fail the gate |
| `TSFORGE_SCREENSHOTS=1` | a desktop + mobile screenshot per route, written for review (never a pass/fail signal) |

Running the check directly (outside a build) exposes the same flags, plus a basic perf budget:

```bash
bun browser-check.ts dist/index.html --smoke --a11y --screenshots --perf
(cd apps/api && bun run validate) && (cd apps/ui && bun run validate) && bun run check
```

`--perf` is a tripwire (DOM node count + mount time), not a full Lighthouse audit. All of these skip cleanly when Chromium / `@axe-core/playwright` aren't installed.
Two things make this honest rather than brittle in the [greenfield loop](/loop/greenfield/):

- **Auto-fix before the gate.** After the model writes, the harness runs the project's
own `bun run format` (prettier) then `bun run lint:fix` (eslint `--fix`) over both
apps. Those classes are deterministic and 100% auto-fixable, so they never cost the
model a gate attempt — a developer gets them on save; the model should too. Only
genuine, non-auto-fixable violations reach the gate as feedback.
- **Baseline-aware differential gate.** The gate is captured once on the *pristine*
scaffold before any model work. A feature then passes when it introduces **no new
failures** — a pre-existing base-suite/scaffold defect the model is frozen out of
can never wedge the build, and the model's feedback shows only the failures *it*
caused. A red baseline is surfaced loudly (the scaffold itself should be fixed); it
is not silently tolerated. This is not a gate relaxation — the full gate still runs
on everything the model touches or newly breaks.

## Two kinds of checking

Expand Down
17 changes: 16 additions & 1 deletion apps/docs/src/content/docs/loop/greenfield.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,27 @@ Because `features.json` is the source of truth, re-running the same command **re

Each feature passes only if all three layers agree, checked cheapest-first and short-circuiting on the first failure:

1. **Gate** — the deterministic build gate (`--accept`: tsc / eslint / tests, and the browser smoke that the web gate already runs). The authority for "done".
1. **Gate** — the deterministic build gate (`--accept`: tsc / eslint / tests). The authority for "done".
2. **Browser** — optional per-feature interaction steps via the Playwright oracle (skipped gracefully when Playwright isn't installed, so it never blocks).
3. **Judge** — a harsh, reject-by-default quality review that sees **only the built code**, never how it was produced.

That last point is a deliberate rule: the evaluator never sees the generator's reasoning or tool trace, so it judges the result, not the persuasion behind it.

## Full-stack builds (BoringStack)

A web app scaffolded with [`tsforge scaffold`](/scaffold/boringstack/) rides this same
loop, with one resource (entity) per feature and a clear division of labor:

- **The harness runs the generators + wiring** — `bun run new:resource` / `new:feature`,
the deterministic edits that wire the resource into the route tree, app, swagger, and
the test-helper's schema re-export, then `db:push` to sync the schema.
- **The model fills the domain** — real fields, service logic, and the required test
siblings, scoped to just that resource's files and frozen once it passes.

The gate is BoringStack's own `validate`, run **baseline-aware and differential** with a
prettier + `eslint --fix` auto-fix pass first — see
[How tsforge builds the gate](/loop/gate-floor/#full-stack-boringstack-builds-use-the-projects-own-gate).

## Role-based model routing

Each role can run on its own model (names from your [models.json](/inference/models-json/)); any role left unset falls back to the active model, so a single-endpoint setup still works:
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ The **gate** is the acceptance check tsforge builds from your project ([How the
| --- | --- |
| Still orienting | [Big picture](/big-picture/) |
| Driving the REPL day to day | [Interactive CLI](/cli/interactive/) |
| Building a new Vite/React app | [Web scaffolding](/scaffold/web/) + [Plan mode](/cli/plan-mode/) |
| Building a new full-stack app | [Greenfield scaffolding](/scaffold/boringstack/) + [Greenfield loop](/loop/greenfield/) |
| Running benchmark specs | [Spec format](/spec/format/) |
| Tuning ESLint enforcement | [Stack detection](/guardrails/stack-detection/) · [Rule catalog](/reference/rules-catalog/) |
| Measuring feature wins | [A/B testing](/eval/ab-testing/) |
2 changes: 0 additions & 2 deletions apps/docs/src/content/docs/reference/flags.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ Extra gate steps (default off; each skips cleanly when nothing applies). See [Ho
| `TSFORGE_COVERAGE=<pct>` | fail if line/function coverage is below the floor |
| `TSFORGE_BOOT="<start cmd>"` | boot the server (`TSFORGE_BOOT_URL`, default `http://localhost:3000/`; `TSFORGE_BOOT_TIMEOUT`, default `15000` ms) and require a non-5xx |
| `TSFORGE_PROPTEST=1` | fuzz exported functions from their types; fail if any throws on valid input (`TSFORGE_PROPTEST_TIMEOUT_MS` bounds the generated suite) |
| `TSFORGE_A11Y=1` | web gate only: axe accessibility checks — serious/critical violations fail the browser-smoke stage |
| `TSFORGE_SCREENSHOTS=1` | web gate only: write per-route PNGs during the browser smoke (for review; never pass/fail) |

## Model / inference

Expand Down
Loading
Loading