diff --git a/.fallowrc.json b/.fallowrc.json
index 57fa98c..9e91519 100644
--- a/.fallowrc.json
+++ b/.fallowrc.json
@@ -10,7 +10,8 @@
"**/turbo/generators/templates/**",
"**/*.generated.ts",
"**/*.d.ts",
- "docs/product/reference/**"
+ "docs/product/reference/**",
+ "fixtures/**"
],
"dynamicallyLoaded": [
"packages/**/__factories__/**",
diff --git a/.prettierignore b/.prettierignore
index d42c1c8..9e112cc 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -3,3 +3,7 @@ compliance/*.yml
# Design reference material (ADR-029) — committed byte-for-byte, never reformat
docs/product/reference/
+
+# Test fixture repos — served to tests as standalone git repos, outside the
+# pnpm workspace and turbo graph; never reformat
+fixtures/
diff --git a/eslint.config.js b/eslint.config.js
index b4b0b27..3f39561 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -29,6 +29,9 @@ export default [
"playwright.config.ts",
// Design reference material (ADR-029) — read-only prototypes, never linted
"docs/product/reference/**",
+ // Test fixture repos — standalone apps outside the pnpm workspace and
+ // turbo graph, served to tests as git remotes; never linted
+ "fixtures/**",
],
},
js.configs.recommended,
diff --git a/fixtures/vite-kitchen/.gitignore b/fixtures/vite-kitchen/.gitignore
new file mode 100644
index 0000000..804bd4a
--- /dev/null
+++ b/fixtures/vite-kitchen/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+dist
+*.local
diff --git a/fixtures/vite-kitchen/index.html b/fixtures/vite-kitchen/index.html
new file mode 100644
index 0000000..757f987
--- /dev/null
+++ b/fixtures/vite-kitchen/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ vite-kitchen
+
+
+
+
+
+
diff --git a/fixtures/vite-kitchen/package.json b/fixtures/vite-kitchen/package.json
new file mode 100644
index 0000000..14e7648
--- /dev/null
+++ b/fixtures/vite-kitchen/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "vite-kitchen",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc --noEmit && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.1.0",
+ "react-dom": "^19.1.0"
+ },
+ "devDependencies": {
+ "@tailwindcss/vite": "^4.1.7",
+ "@types/react": "^19.1.0",
+ "@types/react-dom": "^19.1.0",
+ "@vitejs/plugin-react": "^4.4.1",
+ "tailwindcss": "^4.1.7",
+ "typescript": "~5.8.3",
+ "vite": "^6.3.5"
+ }
+}
diff --git a/fixtures/vite-kitchen/src/App.tsx b/fixtures/vite-kitchen/src/App.tsx
new file mode 100644
index 0000000..9953650
--- /dev/null
+++ b/fixtures/vite-kitchen/src/App.tsx
@@ -0,0 +1,12 @@
+import { Button } from "./components/Button";
+
+export function App() {
+ return (
+
+ vite-kitchen
+
+
+ );
+}
diff --git a/fixtures/vite-kitchen/src/components/Button.tsx b/fixtures/vite-kitchen/src/components/Button.tsx
new file mode 100644
index 0000000..564a4b4
--- /dev/null
+++ b/fixtures/vite-kitchen/src/components/Button.tsx
@@ -0,0 +1,45 @@
+import type { ReactNode } from "react";
+
+export interface ButtonProps {
+ /** Visual emphasis of the button. */
+ variant?: "primary" | "secondary" | "ghost";
+ /** Control scale — padding and font size. */
+ size?: "sm" | "md" | "lg";
+ /** Disables interaction and dims the control. */
+ disabled?: boolean;
+ /** Button label content. */
+ children: ReactNode;
+}
+
+const variantClasses: Record, string> = {
+ primary: "bg-blue-600 text-white hover:bg-blue-700",
+ secondary: "bg-slate-200 text-slate-900 hover:bg-slate-300",
+ ghost: "bg-transparent text-blue-600 hover:bg-blue-50",
+};
+
+const sizeClasses: Record, string> = {
+ sm: "px-2.5 py-1.5 text-sm",
+ md: "px-4 py-2 text-base",
+ lg: "px-6 py-3 text-lg",
+};
+
+/**
+ * The kitchen's one interactive component. Its name and typed props are the
+ * target of the registry scan (react-docgen-typescript) in later stories.
+ */
+export function Button({
+ variant = "primary",
+ size = "md",
+ disabled = false,
+ children,
+}: ButtonProps) {
+ return (
+
+ );
+}
diff --git a/fixtures/vite-kitchen/src/index.css b/fixtures/vite-kitchen/src/index.css
new file mode 100644
index 0000000..f1d8c73
--- /dev/null
+++ b/fixtures/vite-kitchen/src/index.css
@@ -0,0 +1 @@
+@import "tailwindcss";
diff --git a/fixtures/vite-kitchen/src/main.tsx b/fixtures/vite-kitchen/src/main.tsx
new file mode 100644
index 0000000..8a76c8e
--- /dev/null
+++ b/fixtures/vite-kitchen/src/main.tsx
@@ -0,0 +1,15 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import { App } from "./App";
+import "./index.css";
+
+const rootElement = document.getElementById("root");
+if (!rootElement) {
+ throw new Error("vite-kitchen: #root element not found");
+}
+
+createRoot(rootElement).render(
+
+
+ ,
+);
diff --git a/fixtures/vite-kitchen/tsconfig.json b/fixtures/vite-kitchen/tsconfig.json
new file mode 100644
index 0000000..017b4fc
--- /dev/null
+++ b/fixtures/vite-kitchen/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "jsx": "react-jsx",
+ "strict": true,
+ "noEmit": true,
+ "isolatedModules": true,
+ "skipLibCheck": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true
+ },
+ "include": ["src", "vite.config.ts"]
+}
diff --git a/fixtures/vite-kitchen/vite.config.ts b/fixtures/vite-kitchen/vite.config.ts
new file mode 100644
index 0000000..4ff4f8f
--- /dev/null
+++ b/fixtures/vite-kitchen/vite.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import tailwindcss from "@tailwindcss/vite";
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react(), tailwindcss()],
+});
diff --git a/scripts/coverage/diff.mjs b/scripts/coverage/diff.mjs
index 8c99bbb..24507b9 100644
--- a/scripts/coverage/diff.mjs
+++ b/scripts/coverage/diff.mjs
@@ -55,6 +55,10 @@ const ALLOWED_GLOBS = [
// Design reference material (ADR-029) — read-only prototypes committed
// under docs/product/reference/; never executed, never tested.
/^docs\/product\/reference\//,
+ // Test fixture repos — standalone apps outside the pnpm workspace and
+ // turbo graph (walking-skeleton story 01); served to tests as git remotes,
+ // never executed by vitest, never in any lcov.
+ /^fixtures\//,
// Shell scripts (not Vitest-covered)
/\.sh$/,
/\.bash$/,
diff --git a/scripts/coverage/diff.test.mjs b/scripts/coverage/diff.test.mjs
index 6ca1aae..4279597 100644
--- a/scripts/coverage/diff.test.mjs
+++ b/scripts/coverage/diff.test.mjs
@@ -314,6 +314,21 @@ describe("computeDiffCoverage", () => {
assert.equal(result.summary.filesChanged, 3);
});
+ test("skips fixtures/ files (fixture repos, walking-skeleton story 01)", () => {
+ const lcov = parseLcov(lcovText);
+ const diff = new Map([
+ // Standalone fixture apps served to tests as git remotes — never
+ // executed by vitest, never in any lcov
+ ["fixtures/vite-kitchen/src/components/Button.tsx", new Set([1, 2, 3])],
+ ["fixtures/vite-kitchen/src/main.tsx", new Set([1, 2])],
+ ["fixtures/vite-kitchen/vite.config.ts", new Set([1])],
+ ]);
+ const result = computeDiffCoverage(diff, lcov);
+ assert.equal(result.status, "pass");
+ assert.equal(result.summary.filesGated, 0);
+ assert.equal(result.summary.filesChanged, 3);
+ });
+
test("end-to-end fixture: mixed pass/fail/skip/no-data", () => {
const lcov = parseLcov(lcovText);
const diff = parseGitDiff(diffText);