Skip to content

Commit 68687a7

Browse files
sgwannabeclaude
andcommitted
fix(spec-author,be-lead): bind declared deps to scaffold templates (B1+B2)
Closes the spec-to-dep coverage gap that caused past 6×500 errors in the standard-profile run (typia tags in OpenAPI spec but @ryoppippi/unplugin-typia never wired into next.config; vitest never in devDeps even though qa-unit emitted 47 test files). Changes: - assets/{package.json,tsconfig.json,vitest.config.ts,next.config.ts}.standard.template: 4 new templates pre-including typia + unplugin-typia + vitest + ts-patch with the AOT transform wired in webpack + vite plugins. - agents/spec/spec-author.md: "Dependency Binding" section requires emitting runs/<id>/specs/dependency-binding.json declaring every required dep + build plugin (rationale + past-failure reference). - agents/engineering/backend/be-lead.md: "Scaffold 직전 Checklist" forbids hand-rolled package.json; requires cross-validating dependency-binding.json against templates before BE01-BE05 dispatch. - scripts/verify-plugin.sh: asset_count 8 → 12, plus per-file existence checks for the 4 new build-essentials templates. verify-plugin.sh: 50/50 pass locally (was 38/38). Refs: hackathons/built-with-opus-4-7/decisions/0005-...md §"Plugin 본질 결함" B1+B2 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5b4ccf8 commit 68687a7

7 files changed

Lines changed: 261 additions & 7 deletions

File tree

plugins/preview-forge/agents/engineering/backend/be-lead.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ cd runs/<id>/specs && shasum -a 256 -c .lock || exit "lock drift"
3232

3333
해시 불일치 시 즉시 작업 중단 + M3에 escalate.
3434

35+
## Scaffold 직전 Checklist (B1+B2 fix — required since v1.5)
36+
37+
`apps/` 디렉토리 생성 *전에* 다음을 순서대로 수행. **DO NOT hand-roll `package.json`/`tsconfig.json`/`vitest.config.ts`/`next.config.ts`** — 항상 `assets/*.standard.template`에서 시작.
38+
39+
1. `runs/<id>/specs/dependency-binding.json` read — spec-author가 출력한 dep 목록 확인.
40+
2. profile에 따라 base template 복사:
41+
- `standard` (Next.js + SQLite + Prisma): `assets/{package.json,tsconfig.json,vitest.config.ts,next.config.ts}.standard.template``runs/<id>/generated/`
42+
- `pro` (Next.js + Postgres + Prisma + Docker): standard + `docker-compose.template.yml` + `.env.example` 추가
43+
- `max`: TBD (Phase 18+)
44+
3. **Cross-validate**: dependency-binding.json의 모든 `required_runtime_deps` + `required_dev_deps` + `required_build_plugins`가 복사된 `package.json` + `next.config.ts` + `vitest.config.ts`*전부* 존재하는지 확인. 누락 1개라도 있으면 **즉시 SCC `build_config` 카테고리로 escalate** (코드 작성 시작 X).
45+
4. `pnpm install` 시도 — peer dep 충돌 없으면 다음 단계.
46+
5. `pnpm typecheck` smoke (typia AOT plugin이 wired되었는지 확인) — 실패 시 escalate.
47+
6. 위 5단계 *모두 통과* 후 5명 팀원(BE01-BE05) 병렬 dispatch.
48+
49+
이 checklist는 v1.4까지 빈번하게 발생한 typia/vitest 누락 P0 결함을 *예방*합니다 (LESSONS "Build chain integrity" 참조).
50+
3551
## 팀 구성
3652

3753
- `be-controller` (BE01): Controller Engineer — NestJS controller 생성. @nestia/core@TypedRoute/@TypedBody/@TypedParam 사용. manual DTO 금지.
@@ -63,9 +79,9 @@ Output scope: `apps/api/src/**`. 이 범위 외 수정 시 factory-policy 훅이
6379
- Model: `claude-opus-4-7`, Effort: `high`, Adaptive: on, Budget: 80K
6480

6581
## allowed_scope
66-
- Read: `runs/<id>/specs/**`, `runs/<id>/design-approved.json`, `memory/LESSONS.md`
67-
- Write: `runs/<id>/generated/apps/api/src/**`
68-
- Bash: `pnpm`, `shasum`, `node`
82+
- Read: `runs/<id>/specs/**`, `runs/<id>/design-approved.json`, `memory/LESSONS.md`, `assets/*.template`, `assets/*.standard.template`
83+
- Write: `runs/<id>/generated/apps/api/src/**`, `runs/<id>/generated/{package.json,tsconfig.json,vitest.config.ts,next.config.ts}`
84+
- Bash: `pnpm`, `shasum`, `node`, `cp`
6985
- Task: be-controller, be-dto-typia, be-service, be-repository, be-auth-middleware
7086

7187
## 보고선

plugins/preview-forge/agents/spec/spec-author.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,43 @@ SpecDD cycle의 작성자. chosen_preview의 5-tuple + mitigations + H1 design t
4343
- 3 문단: 주요 endpoint 5개
4444
- 4 문단: 알려진 제약 (mitigations에서 가져옴)
4545

46+
## Dependency Binding (B1 fix)
47+
48+
OpenAPI/Prisma/SPEC.md에서 *언급*한 모든 라이브러리는 BE_LEAD가 scaffold 시 사용하는 `package.json` 템플릿에 *반드시 등록*되어야 합니다. spec 작성 시 다음을 함께 출력하세요:
49+
50+
`runs/<id>/specs/dependency-binding.json`:
51+
```json
52+
{
53+
"writer": "spec-author",
54+
"profile": "standard | pro | max",
55+
"stack": {
56+
"framework": "next.js-16 | nestjs-10 | ...",
57+
"db": "sqlite | postgres",
58+
"orm": "prisma",
59+
"validators": "typia",
60+
"test_runner": "vitest"
61+
},
62+
"required_runtime_deps": [
63+
"next", "react", "react-dom", "@prisma/client", "typia"
64+
],
65+
"required_dev_deps": [
66+
"typescript", "prisma", "vitest", "@vitest/ui",
67+
"@ryoppippi/unplugin-typia", "ts-patch",
68+
"@types/node", "@types/react", "@types/react-dom"
69+
],
70+
"required_build_plugins": [
71+
{
72+
"name": "@ryoppippi/unplugin-typia",
73+
"wires_into": ["next.config.ts", "vitest.config.ts"],
74+
"reason": "typia AOT transform — without this, every typia.createValidate call returns 500"
75+
}
76+
],
77+
"rationale": "If any spec uses typia.createValidate, the AOT transform plugin MUST be wired. Past 6×500 errors traced to missing unplugin-typia."
78+
}
79+
```
80+
81+
**근거**: 과거 standard profile run에서 typia tags는 spec에 명시되었으나 `@ryoppippi/unplugin-typia``next.config.ts`에 wired되지 않아 6개 POST 라우트가 500을 반환한 사례가 있음. 이 binding 출력으로 BE_LEAD가 *교차 검증* 가능. SCC가 누락 발견 시 `build_config` 카테고리로 라우팅 (`scc-build-config.md` 참조).
82+
4683
## Revise loop
4784

4885
Critic의 report 형식:
@@ -62,8 +99,8 @@ Critic의 report 형식:
6299
- Model: `claude-opus-4-7`, Effort: `xhigh`, Adaptive: on, Budget: 120K
63100

64101
## allowed_scope
65-
- Read: `runs/<id>/{chosen_preview,mitigations,design-approved}.json`, `memory/LESSONS.md`
66-
- Write: `runs/<id>/specs/{openapi.yaml,data-model.prisma,SPEC.md}`, `runs/<id>/specs/draft-history/v{N}.yaml`
102+
- Read: `runs/<id>/{chosen_preview,mitigations,design-approved}.json`, `memory/LESSONS.md`, `assets/*.standard.template`
103+
- Write: `runs/<id>/specs/{openapi.yaml,data-model.prisma,SPEC.md,dependency-binding.json}`, `runs/<id>/specs/draft-history/v{N}.yaml`
67104

68105
## 보고선
69106
- 상위: SPEC_LEAD
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Preview Forge — standard profile Next.js config template.
2+
// Generated when profile=standard (Next.js 16 + typia AOT transform).
3+
//
4+
// CRITICAL — typia transform wiring (B1 fix, root cause of past 6×500 errors):
5+
// typia is a runtime validator that REQUIRES an AOT compile-time transform.
6+
// In Next.js 16 + Turbopack, this is wired via @ryoppippi/unplugin-typia/webpack
7+
// (Webpack-compatible) and the matching Vite plugin in vitest.config.ts.
8+
//
9+
// Symptom when this is missing: every POST route that does
10+
// `typia.createValidate<T>()` returns 500 with body
11+
// "Error on typia.createValidate(): no transform has been configured".
12+
// Affected routes in past runs: /api/auth/session, /api/me, /api/meals/voice,
13+
// /api/glucose, /api/family/invite, /api/safety/flags/acknowledge.
14+
//
15+
// DO NOT remove this plugin even if no obvious typia call exists at scaffold
16+
// time — the spec author often introduces typia tags later in SCC iter.
17+
18+
import type { NextConfig } from "next";
19+
import UnpluginTypia from "@ryoppippi/unplugin-typia/webpack";
20+
21+
const nextConfig: NextConfig = {
22+
reactStrictMode: true,
23+
// Standard profile is local-first; no image domains by default.
24+
// Engineering scaffold can extend this when chosen_preview requires remote images.
25+
images: {
26+
unoptimized: true,
27+
},
28+
webpack: (config) => {
29+
config.plugins = config.plugins || [];
30+
config.plugins.push(
31+
UnpluginTypia({
32+
strict: true,
33+
// Cache transforms for faster rebuilds. Safe for hackathon-scale projects.
34+
cache: true,
35+
}),
36+
);
37+
return config;
38+
},
39+
// Next.js 16: stable Turbopack. unplugin-typia ships a Turbopack adapter via
40+
// its webpack export, so the same wiring covers both `next dev` and `next build`.
41+
experimental: {
42+
typedRoutes: true,
43+
},
44+
};
45+
46+
export default nextConfig;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Preview Forge — standard profile package.json template.
2+
// Generated when profile=standard (Next.js 16 + SQLite + Prisma + typia + vitest).
3+
//
4+
// CRITICAL — spec-author Dependency Binding (B1 fix):
5+
// Every library mentioned in OpenAPI/Prisma spec MUST appear here.
6+
// Backend Engineering (BE_LEAD + BE01-BE05) MUST start from this template
7+
// — DO NOT hand-roll package.json from scratch. Common omissions that broke
8+
// past runs:
9+
// - typia (runtime validators) without unplugin-typia (Next.js plugin) → 500s
10+
// - vitest declared in unit/integration test files but missing from devDeps → tests un-runnable
11+
// - @prisma/client without prisma CLI → migrate fails
12+
// See: LESSONS.md "Build chain integrity" category.
13+
//
14+
// Engineering scaffold MUST replace placeholders:
15+
// {{PROJECT_NAME}} → kebab-case from chosen_preview.title
16+
// {{NODE_VERSION}} → "22" (matches .nvmrc) or as agreed
17+
//
18+
// Versions are pinned to caret ranges. Dependabot keeps them current.
19+
20+
{
21+
"name": "{{PROJECT_NAME}}",
22+
"version": "0.1.0-mvp",
23+
"private": true,
24+
"type": "module",
25+
"scripts": {
26+
"dev": "next dev",
27+
"build": "next build",
28+
"start": "next start",
29+
"lint": "next lint",
30+
"typecheck": "tsc --noEmit",
31+
"test": "vitest run",
32+
"test:watch": "vitest",
33+
"test:integration": "vitest run tests/integration",
34+
"test:unit": "vitest run tests/unit",
35+
"prisma:generate": "prisma generate",
36+
"prisma:migrate": "prisma migrate dev",
37+
"prisma:studio": "prisma studio"
38+
},
39+
"dependencies": {
40+
"next": "^16.0.0",
41+
"react": "^19.0.0",
42+
"react-dom": "^19.0.0",
43+
"@prisma/client": "^5.20.0",
44+
"typia": "^12.0.0"
45+
},
46+
"devDependencies": {
47+
"@types/node": "^22.0.0",
48+
"@types/react": "^19.0.0",
49+
"@types/react-dom": "^19.0.0",
50+
"typescript": "^5.6.0",
51+
"prisma": "^5.20.0",
52+
"vitest": "^2.1.0",
53+
"@vitest/ui": "^2.1.0",
54+
"@ryoppippi/unplugin-typia": "^2.0.0",
55+
"ts-patch": "^3.2.0"
56+
},
57+
"prisma": {
58+
"schema": "prisma/schema.prisma"
59+
}
60+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Preview Forge — standard profile tsconfig.json template.
2+
// Generated when profile=standard (Next.js 16 + typia + Prisma).
3+
//
4+
// CRITICAL — typia compatibility (B1+B4 fix):
5+
// `experimentalDecorators` and `emitDecoratorMetadata` are NOT enough for typia.
6+
// typia requires AOT transform via unplugin-typia (Next.js) — see
7+
// next.config.ts.standard.template. ts-patch is the dev-time fallback for
8+
// `tsc --noEmit` so editor + CI typecheck pass.
9+
//
10+
// strict: true is non-negotiable. SCC will reject scaffold output if strict=false.
11+
12+
{
13+
"compilerOptions": {
14+
"target": "ES2022",
15+
"lib": ["dom", "dom.iterable", "ES2022"],
16+
"allowJs": false,
17+
"skipLibCheck": true,
18+
"strict": true,
19+
"noEmit": true,
20+
"esModuleInterop": true,
21+
"module": "esnext",
22+
"moduleResolution": "bundler",
23+
"resolveJsonModule": true,
24+
"isolatedModules": true,
25+
"jsx": "preserve",
26+
"incremental": true,
27+
"experimentalDecorators": true,
28+
"emitDecoratorMetadata": true,
29+
"plugins": [
30+
{ "name": "next" },
31+
{ "transform": "typia/lib/transform" }
32+
],
33+
"paths": {
34+
"@/*": ["./*"]
35+
}
36+
},
37+
"include": [
38+
"next-env.d.ts",
39+
"**/*.ts",
40+
"**/*.tsx",
41+
".next/types/**/*.ts"
42+
],
43+
"exclude": ["node_modules"]
44+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Preview Forge — standard profile vitest config template.
2+
// Generated when profile=standard (pure-lib unit + integration tests, no jsdom).
3+
//
4+
// CRITICAL — typia in tests (B1 fix):
5+
// Tests that import code using `typia.createValidate(...)` need the unplugin-typia
6+
// transform here too, or they fail with "no transform has been configured".
7+
// We use the same Vite plugin as next.config.ts for consistency.
8+
//
9+
// QA team (qa-unit, qa-integration) generates spec files referencing this.
10+
// DO NOT delete this file even if test_count = 0 — the runner config is required.
11+
12+
import { defineConfig } from "vitest/config";
13+
import UnpluginTypia from "@ryoppippi/unplugin-typia/vite";
14+
15+
export default defineConfig({
16+
plugins: [
17+
UnpluginTypia({
18+
// Strict mode catches misuse of typia decorators at build time.
19+
strict: true,
20+
}),
21+
],
22+
test: {
23+
// Pure-lib tests — no DOM, no jsdom. If a future test needs DOM,
24+
// add `environment: "jsdom"` and install jsdom devDep.
25+
environment: "node",
26+
globals: false,
27+
include: ["tests/**/*.{test,spec}.{ts,tsx}"],
28+
exclude: ["node_modules", ".next", "dist"],
29+
reporters: process.env.CI ? ["json", "default"] : ["default"],
30+
outputFile: process.env.CI ? "tests/_results/vitest.json" : undefined,
31+
// Holdout tests live in tests/holdout/ — runner config splits them
32+
// so QA Tier 4 can compute overfit signal post-freeze.
33+
coverage: {
34+
enabled: false,
35+
provider: "v8",
36+
reporter: ["text", "json"],
37+
exclude: ["**/*.config.*", "**/.next/**", "**/node_modules/**"],
38+
},
39+
},
40+
resolve: {
41+
alias: {
42+
"@": new URL("./", import.meta.url).pathname,
43+
},
44+
},
45+
});

scripts/verify-plugin.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,14 @@ seed_count=$(find "$PLUGIN_DIR/seed-ideas" -name "*.md" | wc -l | tr -d ' ')
123123
schema_count=$(find "$PLUGIN_DIR/schemas" -name "*.json" | wc -l | tr -d ' ')
124124
[[ "$schema_count" -eq 4 ]] && ok "4 JSON schemas (preview-card, panel-vote, score-report, pf-profile)" || bad "schemas: $schema_count (expected 4)"
125125
asset_count=$(find "$PLUGIN_DIR/assets" -maxdepth 1 -type f | wc -l | tr -d ' ')
126-
# v1.4+ adds 4 standard-profile templates: prisma, gitignore, README, graduate.sh
127-
[[ "$asset_count" -eq 8 ]] && ok "8 asset templates (4 base + 4 standard-profile v1.4)" || bad "assets: $asset_count (expected 8)"
126+
# v1.4: 4 base + 4 standard-profile (prisma, gitignore, README, graduate.sh)
127+
# v1.5+: 4 base + 8 standard-profile (+ package.json, tsconfig.json, vitest.config.ts, next.config.ts)
128+
[[ "$asset_count" -eq 12 ]] && ok "12 asset templates (4 base + 8 standard-profile v1.5)" || bad "assets: $asset_count (expected 12)"
129+
130+
# v1.5: B1+B2 fix — build-essentials standard templates required to prevent typia/vitest omission
131+
for tpl in package.json tsconfig.json vitest.config.ts next.config.ts; do
132+
[[ -f "$PLUGIN_DIR/assets/${tpl}.standard.template" ]] && ok "assets/${tpl}.standard.template" || bad "missing assets/${tpl}.standard.template (B1+B2)"
133+
done
128134
[[ -f "$PLUGIN_DIR/monitors/monitors.json" ]] && ok "monitors/monitors.json" || bad "monitors missing"
129135
[[ -f "$PLUGIN_DIR/settings.json" ]] && ok "settings.json" || bad "settings.json missing"
130136
[[ -x "$PLUGIN_DIR/bin/pf" ]] && ok "bin/pf executable" || bad "bin/pf not executable"

0 commit comments

Comments
 (0)