Skip to content

Commit e7cf67d

Browse files
committed
feat(prototypes): deepen prototype framework
1 parent 456e327 commit e7cf67d

20 files changed

Lines changed: 717 additions & 95 deletions

apps/prototypes/AGENTS.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,30 @@ Run prototype checks explicitly:
1212
pnpm --filter @kilocode/prototypes dev
1313
pnpm --filter @kilocode/prototypes build
1414
pnpm --filter @kilocode/prototypes typecheck
15+
pnpm --filter @kilocode/prototypes test
16+
pnpm --filter @kilocode/prototypes validate:routes
17+
pnpm --filter @kilocode/prototypes validate
1518
```
1619

1720
Do not wire this app into root `build`, root `validate`, or default CI unless a future task explicitly changes that policy.
1821

22+
## Prototype host contract
23+
24+
Root layout metadata, viewport, font variable composition, and production web theme ownership belong to the prototype-host module. The layout still imports `@web/app/globals.css` as the concrete theme stylesheet, matching the host contract.
25+
26+
The Next config sets output tracing and Turbopack roots to the monorepo root because this opt-in package imports production web source. Keep those assumptions package-local and do not add the app to root validation by default.
27+
1928
## Import boundaries
2029

2130
- `@/*` resolves to `apps/prototypes/src/*` for prototype-local code.
2231
- `@web/*` resolves to `apps/web/src/*` for production web theme, primitives, and product UI.
2332

24-
Prefer prototype-local code for catalog/discovery and review-shell utilities. Keep domain-specific prototype data and preview components colocated with the route that owns them.
33+
Prefer prototype-local code for catalog/discovery and review-shell utilities. Prototype routes import production web UI primitives and shared visual modules through prototype-local seams such as `@/components/ui/*`, `@/components/shared/*`, or `@/components/KiloCrabIcon`; only those bridge files should import `@web/components/*` directly. Keep domain-specific prototype data and preview components colocated with the route that owns them.
2534

2635
## Folder conventions
2736

2837
- Add prototype routes as `src/app/<prototype-slug>/page.tsx`.
38+
- Run `pnpm --filter @kilocode/prototypes validate:routes` to check route folder shape and metadata before review. Run `pnpm --filter @kilocode/prototypes validate` for the full opt-in prototype framework gate.
2939
- Add optional route metadata beside the page when catalog copy/tags are needed.
3040
- Keep fixtures, client wrappers, and preview components inside the route folder unless another prototype proves the abstraction reusable.
3141
- Shared prototype-kit primitives belong in `src/` outside app route folders.

apps/prototypes/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,28 @@ Run commands explicitly; the root `build`, `validate`, and default CI flows do n
1212
pnpm --filter @kilocode/prototypes dev
1313
pnpm --filter @kilocode/prototypes build
1414
pnpm --filter @kilocode/prototypes typecheck
15+
pnpm --filter @kilocode/prototypes test
16+
pnpm --filter @kilocode/prototypes validate:routes
17+
pnpm --filter @kilocode/prototypes validate
1518
```
1619

20+
## Prototype host contract
21+
22+
The root layout uses a prototype-host module for metadata, viewport, font variable composition, and the production web theme contract. The theme source is the production web globals stylesheet (`@web/app/globals.css`) so prototypes stay visually faithful to production UI without redefining tokens.
23+
24+
The Next config sets output tracing and Turbopack roots to the monorepo root because the app intentionally imports production web source. Keep that setup local to this opt-in package; do not enroll prototypes in root build, root validate, or default CI unless a future task explicitly changes the policy.
25+
1726
## Import aliases
1827

1928
- `@/*` points to prototype-local source in `apps/prototypes/src/*`.
2029
- `@web/*` points to production web source in `apps/web/src/*`.
2130

22-
Use `@/*` for prototype host code, discovery, catalog, and shared prototype-kit utilities. Use `@web/*` only when importing production web theme, primitives, or existing product UI needed to keep a prototype faithful.
31+
Use `@/*` for prototype host code, discovery, catalog, shared prototype-kit utilities, and production web component bridges. Prototype routes should import UI primitives and shared visual modules through prototype-local seams such as `@/components/ui/*`, `@/components/shared/*`, or `@/components/KiloCrabIcon`; the bridge files own direct `@web/components/*` imports. Use direct `@web/*` imports only for non-component production web contracts such as the shared theme stylesheet.
2332

2433
## Folder conventions
2534

2635
- Prototype routes live under `src/app/<prototype-slug>/`.
36+
- Run `pnpm --filter @kilocode/prototypes validate:routes` to check route folder shape and metadata before review. Run `pnpm --filter @kilocode/prototypes validate` for the full opt-in prototype framework gate.
2737
- Keep prototype-specific fixtures, role logic, preview components, and metadata colocated with that route.
2838
- Shared prototype-host utilities live under `src/` outside route folders.
2939
- Do not introduce a central manifest until multiple durable prototypes prove the need.

apps/prototypes/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
"scripts": {
66
"dev": "next dev",
77
"build": "next build",
8-
"typecheck": "tsgo --noEmit"
8+
"typecheck": "tsgo --noEmit",
9+
"test": "tsx --test src/lib/*.test.ts src/components/prototype-kit/*.test.ts",
10+
"check:import-seams": "node scripts/check-import-seams.mjs",
11+
"validate:routes": "tsx scripts/validate-routes.ts",
12+
"validate": "pnpm run check:import-seams && pnpm run validate:routes && pnpm run test && pnpm run typecheck"
913
},
1014
"dependencies": {
1115
"@radix-ui/react-dialog": "^1.1.15",
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { readdir, readFile } from 'node:fs/promises';
2+
import { extname, join, relative } from 'node:path';
3+
4+
const root = new URL('../', import.meta.url).pathname;
5+
const srcDir = join(root, 'src');
6+
const allowedDirectWebComponentImporters = new Set([
7+
'src/components/KiloCrabIcon.tsx',
8+
'src/components/shared/Banner.tsx',
9+
]);
10+
11+
const allowedDirectWebComponentPrefixes = ['src/components/ui/'];
12+
const sourceExtensions = new Set(['.ts', '.tsx', '.js', '.jsx']);
13+
const webComponentImportPattern = /from\s+['"]@web\/components\//g;
14+
15+
async function collectSourceFiles(dir) {
16+
const entries = await readdir(dir, { withFileTypes: true });
17+
const files = await Promise.all(
18+
entries.map(async entry => {
19+
const path = join(dir, entry.name);
20+
if (entry.isDirectory()) return collectSourceFiles(path);
21+
if (!sourceExtensions.has(extname(entry.name))) return [];
22+
return [path];
23+
})
24+
);
25+
26+
return files.flat();
27+
}
28+
29+
function isAllowedDirectImporter(relativePath) {
30+
return (
31+
allowedDirectWebComponentImporters.has(relativePath) ||
32+
allowedDirectWebComponentPrefixes.some(prefix => relativePath.startsWith(prefix))
33+
);
34+
}
35+
36+
const violations = [];
37+
38+
for (const file of await collectSourceFiles(srcDir)) {
39+
const relativePath = relative(root, file);
40+
if (isAllowedDirectImporter(relativePath)) continue;
41+
42+
const source = await readFile(file, 'utf8');
43+
if (!webComponentImportPattern.test(source)) continue;
44+
45+
violations.push(relativePath);
46+
webComponentImportPattern.lastIndex = 0;
47+
}
48+
49+
if (violations.length > 0) {
50+
console.error(
51+
'Prototype Routes must import production web components through prototype-local seams.'
52+
);
53+
console.error(
54+
'Move these imports behind src/components/ui/*, src/components/shared/*, or another approved bridge:'
55+
);
56+
for (const violation of violations) console.error(`- ${violation}`);
57+
process.exit(1);
58+
}
59+
60+
console.log('Prototype production web component imports use approved local seams.');
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { readdirSync } from 'fs';
2+
import { join } from 'path';
3+
import { validatePrototypeRoute } from '../src/lib/prototype-route-authoring';
4+
5+
const appDir = join(process.cwd(), 'src/app');
6+
const routeSlugs = readdirSync(appDir, { withFileTypes: true })
7+
.filter(entry => entry.isDirectory())
8+
.map(entry => entry.name);
9+
10+
const results = routeSlugs.map(slug => validatePrototypeRoute({ appDir, slug }));
11+
const invalidResults = results.filter(result => !result.valid);
12+
13+
for (const result of results) {
14+
if (result.issues.length === 0) continue;
15+
16+
console.log(`Prototype route ${result.slug}:`);
17+
for (const issue of result.issues) {
18+
console.log(`- ${issue.severity.toUpperCase()} ${issue.code}: ${issue.message}`);
19+
}
20+
}
21+
22+
if (invalidResults.length > 0) {
23+
process.exit(1);
24+
}
25+
26+
console.log(`Validated ${results.length} prototype route${results.length === 1 ? '' : 's'}.`);

apps/prototypes/src/app/kiloclaw-org-billing/components.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,29 +34,29 @@ import {
3434
Users,
3535
} from 'lucide-react';
3636
import { cn } from '@/lib/utils';
37-
import { Button } from '@web/components/ui/button';
38-
import { Badge } from '@web/components/ui/badge';
39-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@web/components/ui/card';
40-
import {
41-
Table,
42-
TableBody,
43-
TableCell,
44-
TableHead,
45-
TableHeader,
46-
TableRow,
47-
} from '@web/components/ui/table';
48-
import { Banner } from '@web/components/shared/Banner';
49-
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@web/components/ui/tabs';
50-
import { Switch } from '@web/components/ui/switch';
37+
import { Badge } from '@/components/ui/badge';
38+
import { Button } from '@/components/ui/button';
39+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
5140
import {
5241
Dialog,
5342
DialogContent,
5443
DialogDescription,
5544
DialogFooter,
5645
DialogHeader,
5746
DialogTitle,
58-
} from '@web/components/ui/dialog';
59-
import KiloCrabIcon from '@web/components/KiloCrabIcon';
47+
} from '@/components/ui/dialog';
48+
import { Switch } from '@/components/ui/switch';
49+
import {
50+
Table,
51+
TableBody,
52+
TableCell,
53+
TableHead,
54+
TableHeader,
55+
TableRow,
56+
} from '@/components/ui/table';
57+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
58+
import KiloCrabIcon from '@/components/KiloCrabIcon';
59+
import { Banner } from '@/components/shared/Banner';
6060
import {
6161
type AdminBillingStatus,
6262
type AssociatedUser,

0 commit comments

Comments
 (0)