@playcraft/cli 0.0.44 → 0.0.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (984) hide show
  1. package/dist/atom-tree/build-skeleton.js +180 -0
  2. package/dist/atom-tree/fusion-hints.js +86 -0
  3. package/dist/atom-tree/selection.js +402 -0
  4. package/dist/commands/build-all.js +33 -5
  5. package/dist/commands/build.js +7 -1
  6. package/dist/commands/create.js +774 -50
  7. package/dist/commands/image.js +11 -0
  8. package/dist/commands/prad.js +61 -0
  9. package/dist/commands/remix.js +20 -0
  10. package/dist/commands/skills.js +961 -185
  11. package/dist/commands/tools-detect.js +56 -0
  12. package/dist/commands/tools-generation.js +21 -0
  13. package/dist/commands/tools-video-review.js +100 -0
  14. package/dist/commands/tools.js +4 -0
  15. package/dist/index.js +2 -0
  16. package/dist/prad/atom-ref.js +23 -0
  17. package/dist/prad/check.js +377 -0
  18. package/dist/prad/check.test.js +49 -0
  19. package/dist/prad/explain.js +109 -0
  20. package/dist/prad/load-spec.js +23 -0
  21. package/dist/prad/paths.js +83 -0
  22. package/dist/prad/skills-index.js +60 -0
  23. package/dist/remix/init-template.js +80 -0
  24. package/dist/scaffold/compose.js +211 -0
  25. package/engine-packs/phaser/game/adapters/phaser.ts +52 -0
  26. package/engine-packs/phaser/game/config/game-config.ts +46 -0
  27. package/engine-packs/phaser/game/contract/publish.ts +141 -0
  28. package/engine-packs/phaser/game/contract/types.ts +34 -0
  29. package/engine-packs/phaser/game/index.ts +81 -0
  30. package/engine-packs/phaser/game/runtime-contract.ts +28 -0
  31. package/engine-packs/phaser/manifest.json +1 -0
  32. package/engine-packs/phaser/package.deps.json +1 -0
  33. package/engine-packs/threejs/game/config/.gitkeep +0 -0
  34. package/engine-packs/threejs/game/config/game-config.ts +46 -0
  35. package/engine-packs/threejs/game/contract/publish.ts +141 -0
  36. package/engine-packs/threejs/game/contract/types.ts +34 -0
  37. package/engine-packs/threejs/game/gameplay/.gitkeep +0 -0
  38. package/engine-packs/threejs/game/index.ts +76 -0
  39. package/engine-packs/threejs/game/runtime-contract.ts +15 -0
  40. package/engine-packs/threejs/game/scenes/.gitkeep +0 -0
  41. package/engine-packs/threejs/manifest.json +1 -0
  42. package/engine-packs/threejs/package.deps.json +8 -0
  43. package/package.json +17 -3
  44. package/project-template/.claude/agents/artist.md +101 -0
  45. package/project-template/.claude/agents/developer.md +90 -93
  46. package/project-template/.claude/agents/game-designer.md +192 -0
  47. package/project-template/.claude/agents/refs/README.md +57 -51
  48. package/project-template/.claude/agents/refs/{designer-color-audio-recipes.md → artist-color-audio-recipes.md} +2 -2
  49. package/project-template/.claude/agents/refs/{designer-deliverable-spec.md → artist-deliverable-spec.md} +57 -57
  50. package/project-template/.claude/agents/refs/{designer-dimension-axis.md → artist-dimension-axis.md} +5 -5
  51. package/project-template/.claude/agents/refs/{designer-handoff-v2-checklist.md → artist-handoff-v2-checklist.md} +15 -15
  52. package/project-template/.claude/agents/refs/artist-legacy-fallback.md +14 -0
  53. package/project-template/.claude/agents/refs/{designer-master-composite-recipes.md → artist-master-composite-recipes.md} +3 -3
  54. package/project-template/.claude/agents/refs/artist-style-exploration-flow.md +55 -0
  55. package/project-template/.claude/agents/refs/developer-dev-handoff.md +83 -40
  56. package/project-template/.claude/agents/refs/developer-impl-cookbook.md +365 -67
  57. package/project-template/.claude/agents/refs/developer-legacy-fallback.md +15 -0
  58. package/project-template/.claude/agents/refs/developer-phase1-flow.md +131 -85
  59. package/project-template/.claude/agents/refs/framework-5-component-filter.md +252 -0
  60. package/project-template/.claude/agents/refs/framework-game-feel-juice.md +266 -0
  61. package/project-template/.claude/agents/refs/framework-mda.md +147 -0
  62. package/project-template/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
  63. package/project-template/.claude/agents/refs/game-designer-legacy-fallback.md +15 -0
  64. package/project-template/.claude/agents/refs/game-designer-workflow-detail.md +69 -0
  65. package/project-template/.claude/agents/refs/implementation-plan-guide.md +316 -0
  66. package/project-template/.claude/agents/refs/reviewer-convergence-eval.md +247 -33
  67. package/project-template/.claude/agents/refs/reviewer-legacy-fallback.md +14 -0
  68. package/project-template/.claude/agents/refs/skill-selector-guide.md +465 -0
  69. package/project-template/.claude/agents/refs/skill-value-layers.md +145 -0
  70. package/project-template/.claude/agents/refs/storyboard-generation-guide.md +110 -0
  71. package/project-template/.claude/agents/refs/ta-atlas-deliverable-standard.md +4 -4
  72. package/project-template/.claude/agents/refs/ta-pipeline-cookbook.md +101 -104
  73. package/project-template/.claude/agents/refs/technical-artist-legacy-fallback.md +14 -0
  74. package/project-template/.claude/agents/reviewer.md +81 -95
  75. package/project-template/.claude/agents/technical-artist.md +71 -95
  76. package/project-template/.claude/hooks/README.md +83 -35
  77. package/project-template/.claude/hooks/asr-coverage-validate.mjs +19 -19
  78. package/project-template/.claude/hooks/block-developer-preflight.mjs +185 -0
  79. package/project-template/.claude/hooks/block-ring1-tasklist.mjs +95 -0
  80. package/project-template/.claude/hooks/gate-dispatcher.mjs +78 -0
  81. package/project-template/.claude/hooks/gate-runner.mjs +557 -0
  82. package/project-template/.claude/hooks/gate.mjs +170 -0
  83. package/project-template/.claude/hooks/snapshot-milestone.mjs +246 -0
  84. package/project-template/.claude/hooks/user-prompt.mjs +193 -0
  85. package/project-template/.claude/hooks/validate-atom-tree-selection.mjs +281 -0
  86. package/project-template/.claude/hooks/validate-workflow-stop.mjs +1535 -95
  87. package/project-template/.claude/settings.json +54 -9
  88. package/project-template/.claude/skills/brainstorming/SKILL.md +161 -0
  89. package/project-template/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
  90. package/project-template/.claude/skills/brainstorming/scripts/helper.js +177 -0
  91. package/project-template/.claude/skills/brainstorming/scripts/server.cjs +354 -0
  92. package/project-template/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
  93. package/project-template/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
  94. package/project-template/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
  95. package/project-template/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  96. package/project-template/.claude/skills/brainstorming/visual-companion.md +309 -0
  97. package/project-template/.claude/skills/playcraft-ad-psychology/SKILL.md +3 -3
  98. package/project-template/.claude/skills/playcraft-art-style-guide/SKILL.md +3 -3
  99. package/project-template/.claude/skills/playcraft-asset-state-sheet/SKILL.md +35 -25
  100. package/project-template/.claude/skills/playcraft-audio-generation/SKILL.md +15 -15
  101. package/project-template/.claude/skills/playcraft-build-optimizer/SKILL.md +5 -5
  102. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +5 -5
  103. package/project-template/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +5 -5
  104. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +116 -27
  105. package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +1 -1
  106. package/project-template/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
  107. package/project-template/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
  108. package/project-template/.claude/skills/playcraft-research/SKILL.md +24 -21
  109. package/project-template/.claude/skills/playcraft-skill-recommender/SKILL.md +331 -0
  110. package/project-template/.claude/skills/playcraft-sprite-generation/SKILL.md +48 -48
  111. package/project-template/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
  112. package/project-template/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
  113. package/project-template/.claude/skills/playcraft-storyboard/SKILL.md +4 -4
  114. package/project-template/.claude/skills/playcraft-style-qa/SKILL.md +45 -45
  115. package/project-template/.claude/skills/playcraft-text-rendering/SKILL.md +27 -27
  116. package/project-template/.claude/skills/session-analyzer/SKILL.md +386 -0
  117. package/project-template/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
  118. package/project-template/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
  119. package/project-template/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
  120. package/project-template/.claude/skills/session-analyzer/scripts/resolve-session.mjs +103 -0
  121. package/project-template/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
  122. package/project-template/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
  123. package/project-template/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
  124. package/project-template/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
  125. package/project-template/.claude/snapshot.mjs +350 -0
  126. package/project-template/.codebuddy/settings.json +53 -0
  127. package/project-template/.cursor/hooks.json +1 -1
  128. package/project-template/.cursor/rules/playcraft-orchestrator.mdc +91 -86
  129. package/project-template/.cursor/rules/playcraft-subagent-boundary.mdc +16 -5
  130. package/project-template/CHANGELOG.md +105 -0
  131. package/project-template/CLAUDE.md +58 -254
  132. package/project-template/docs/DESIGN.md +65 -0
  133. package/project-template/docs/intent-clarifications.md +58 -0
  134. package/project-template/docs/pipeline-overview.md +69 -0
  135. package/project-template/docs/project-state.md +211 -0
  136. package/project-template/docs/team/agent-conduct.md +51 -105
  137. package/project-template/docs/team/agent-runtime-matrix.md +60 -74
  138. package/project-template/docs/team/atom-tree-format.md +169 -0
  139. package/project-template/docs/team/collaboration.md +319 -225
  140. package/project-template/docs/team/core-model.md +80 -46
  141. package/project-template/docs/team/game-designer-migration-todo.md +8 -0
  142. package/project-template/docs/team/harness-legacy-deprecation.md +23 -0
  143. package/project-template/docs/team/platform-capabilities.md +16 -13
  144. package/project-template/docs/team/workflow-changelog.md +95 -23
  145. package/project-template/docs/team/workflow-consistency-checklist.md +93 -45
  146. package/project-template/focus-lab/.gitkeep +0 -0
  147. package/project-template/game/.playcraft-scaffold-pending +0 -0
  148. package/project-template/game/index.ts +18 -0
  149. package/project-template/globals.d.ts +51 -0
  150. package/project-template/index.css +34 -0
  151. package/project-template/index.html +18 -0
  152. package/project-template/main.ts +9 -0
  153. package/project-template/package.json +79 -0
  154. package/project-template/skills/README.md +52 -0
  155. package/project-template/skills/_shared/agent-prep.md +136 -0
  156. package/project-template/skills/_shared/requirements.txt +1 -0
  157. package/project-template/skills/_shared/scripts/build-evidence-gate.mjs +87 -0
  158. package/project-template/skills/_shared/scripts/context-budget.mjs +215 -0
  159. package/project-template/skills/_shared/scripts/create-playable.mjs +140 -0
  160. package/project-template/skills/_shared/scripts/dev-start.mjs +151 -0
  161. package/project-template/skills/_shared/scripts/disclosure-config.mjs +480 -0
  162. package/project-template/skills/_shared/scripts/ensure-project.mjs +532 -0
  163. package/project-template/skills/_shared/scripts/gen-placeholder-audio.py +345 -0
  164. package/project-template/skills/_shared/scripts/gen-placeholder.py +244 -0
  165. package/project-template/skills/_shared/scripts/harness-smoke.mjs +257 -0
  166. package/project-template/skills/_shared/scripts/lib/validator-artifacts.mjs +129 -0
  167. package/project-template/skills/_shared/scripts/placeholder-pack-sheet.mjs +67 -0
  168. package/project-template/skills/_shared/scripts/placeholder_audio_lib.py +142 -0
  169. package/project-template/skills/_shared/scripts/placeholder_lib.py +993 -0
  170. package/project-template/skills/_shared/scripts/render-project-state.mjs +278 -0
  171. package/project-template/skills/_shared/scripts/ring-config.mjs +252 -0
  172. package/project-template/skills/_shared/scripts/skills-check.mjs +176 -0
  173. package/project-template/skills/_shared/scripts/slice-set.mjs +77 -0
  174. package/project-template/skills/_shared/scripts/state-advance-ring.mjs +63 -0
  175. package/project-template/skills/_shared/scripts/state-get.mjs +215 -0
  176. package/project-template/skills/_shared/scripts/state-handoff.mjs +120 -0
  177. package/project-template/skills/_shared/scripts/state-set.mjs +113 -0
  178. package/project-template/skills/_shared/scripts/state-store.mjs +893 -0
  179. package/project-template/skills/_shared/scripts/sync-template-skills.mjs +112 -0
  180. package/project-template/skills/_shared/scripts/verify-asset-code-sync.mjs +265 -0
  181. package/project-template/skills/_shared/scripts/verify-atom-conformance.mjs +181 -0
  182. package/project-template/skills/_shared/scripts/verify-env.mjs +285 -0
  183. package/project-template/skills/_shared/scripts/verify-placeholders.mjs +161 -0
  184. package/project-template/skills/_shared/scripts/workflow-doctor.mjs +348 -0
  185. package/project-template/skills/playable-animate/SKILL.md +52 -0
  186. package/project-template/skills/playable-animate/agents/openai.yaml +4 -0
  187. package/project-template/skills/playable-animate/references/board-refresh.md +30 -0
  188. package/project-template/skills/playable-animate/references/diagnosis-checklist.md +25 -0
  189. package/project-template/skills/playable-animate/references/entrance-sequencing.md +33 -0
  190. package/project-template/skills/playable-animate/references/interaction-timing.md +23 -0
  191. package/project-template/skills/playable-art/SKILL.md +203 -0
  192. package/project-template/skills/playable-art/agents/openai.yaml +4 -0
  193. package/project-template/skills/playable-art/references/alpha-postprocess.md +46 -0
  194. package/project-template/skills/playable-art/references/cta-art-spec.md +59 -0
  195. package/project-template/skills/playable-art/references/layout-proportions.md +30 -0
  196. package/project-template/skills/playable-art/references/prompt-patterns.md +83 -0
  197. package/project-template/skills/playable-art/references/reference-comparison.md +30 -0
  198. package/project-template/skills/playable-art/references/sheet-import-v2.md +91 -0
  199. package/project-template/skills/playable-art/scripts/build-atlas.py +332 -0
  200. package/project-template/skills/playable-art/scripts/chroma-key.py +50 -0
  201. package/project-template/skills/playable-art/scripts/extract-alpha-split.py +65 -0
  202. package/project-template/skills/playable-art/scripts/extract-dual-bg-alpha.py +118 -0
  203. package/project-template/skills/playable-art/scripts/import-gpt-art-sheets-v2.py +403 -0
  204. package/project-template/skills/playable-art/scripts/import-gpt-art-sheets.py +280 -0
  205. package/project-template/skills/playable-art/scripts/import-gpt-tile-sheet.py +114 -0
  206. package/project-template/skills/playable-art/scripts/import_gpt_art_pipeline.py +321 -0
  207. package/project-template/skills/playable-art/scripts/import_gpt_art_sheets_v2_test.py +126 -0
  208. package/project-template/skills/playable-art/scripts/placeholder_common.py +98 -0
  209. package/project-template/skills/playable-art/scripts/remove-bg.py +91 -0
  210. package/project-template/skills/playable-art/scripts/review-processed-assets.py +468 -0
  211. package/project-template/skills/playable-art/scripts/review_processed_assets_test.py +116 -0
  212. package/project-template/skills/playable-art/scripts/verify-alpha-clean.py +105 -0
  213. package/project-template/skills/playable-debug/SKILL.md +116 -0
  214. package/project-template/skills/playable-debug/agents/openai.yaml +4 -0
  215. package/project-template/skills/playable-debug/references/debug-config.md +40 -0
  216. package/project-template/skills/playable-focus/SKILL.md +148 -0
  217. package/project-template/skills/playable-focus/scripts/bootstrap-playground.mjs +84 -0
  218. package/project-template/skills/playable-focus/scripts/extract-focus-frames.mjs +87 -0
  219. package/project-template/skills/playable-focus/scripts/generate-focus-reports.mjs +121 -0
  220. package/project-template/skills/playable-focus/scripts/playground.mjs +390 -0
  221. package/project-template/skills/playable-focus/templates/focus-lab/frame.html +39 -0
  222. package/project-template/skills/playable-focus/templates/focus-lab/harness/clean-stage.js +114 -0
  223. package/project-template/skills/playable-focus/templates/focus-lab/harness/frame-lab-scene.js +94 -0
  224. package/project-template/skills/playable-focus/templates/focus-lab/harness/particle-lab-scene.js +224 -0
  225. package/project-template/skills/playable-focus/templates/focus-lab/model.html +16 -0
  226. package/project-template/skills/playable-focus/templates/focus-lab/particle.html +38 -0
  227. package/project-template/skills/playable-focus/templates/focus-lab/shatter.html +47 -0
  228. package/project-template/skills/playable-focus/templates/playground/registry.template.json +55 -0
  229. package/project-template/skills/playable-focus/templates/playground/vfx-catalog.template.json +280 -0
  230. package/project-template/skills/playable-fx-decompose/SKILL.md +212 -0
  231. package/project-template/skills/playable-fx-decompose/examples.md +89 -0
  232. package/project-template/skills/playable-fx-decompose/reference.md +201 -0
  233. package/project-template/skills/playable-fx-decompose/requirements.txt +2 -0
  234. package/project-template/skills/playable-fx-decompose/scripts/build-blank-tile-crack.py +192 -0
  235. package/project-template/skills/playable-fx-decompose/scripts/fix-shard-debris-frames.py +60 -0
  236. package/project-template/skills/playable-fx-decompose/scripts/generate-shard-debris.py +276 -0
  237. package/project-template/skills/playable-fx-peak/SKILL.md +237 -0
  238. package/project-template/skills/playable-fx-peak/requirements.txt +2 -0
  239. package/project-template/skills/playable-fx-peak/scripts/fix-frame-edges.py +70 -0
  240. package/project-template/skills/playable-fx-peak/scripts/repack-frame-sequence.py +526 -0
  241. package/project-template/skills/playable-fx-peak/scripts/validate-frame-anchors.py +130 -0
  242. package/project-template/skills/playable-placeholder/SKILL.md +239 -0
  243. package/project-template/skills/playable-record/SKILL.md +166 -0
  244. package/project-template/skills/playable-record/scripts/lib/build-artifact.mjs +92 -0
  245. package/project-template/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
  246. package/project-template/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
  247. package/project-template/skills/playable-record/scripts/lib/slice-structure-verify.mjs +199 -0
  248. package/project-template/skills/playable-record/scripts/record-playable.mjs +583 -0
  249. package/project-template/skills/playable-record/scripts/verify-build.mjs +185 -0
  250. package/project-template/skills/playable-record/scripts/verify-contract.mjs +261 -0
  251. package/project-template/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
  252. package/project-template/skills/playable-record/scripts/verify-fusion.mjs +320 -0
  253. package/project-template/skills/playable-record/scripts/verify-lifecycle.mjs +78 -0
  254. package/project-template/skills/playable-record/scripts/verify-slice-artist.mjs +53 -0
  255. package/project-template/skills/playable-record/scripts/verify-slice-ta.mjs +53 -0
  256. package/project-template/skills/playable-record/scripts/verify-slice.mjs +142 -0
  257. package/project-template/skills/playable-record/scripts/verify-visual-ring.mjs +250 -0
  258. package/project-template/skills/playable-record/scripts/verify-visual-ring1.mjs +190 -0
  259. package/project-template/skills/playable-record/scripts/verify-vlm-video.mjs +227 -0
  260. package/project-template/skills/playable-validate/SKILL.md +260 -0
  261. package/project-template/skills/playable-validate/references/checklist-detail.md +36 -0
  262. package/project-template/skills/playable-validate/references/single-html-bundle.md +76 -0
  263. package/project-template/skills/playable-validate/validation-rules.md +200 -0
  264. package/project-template/skills/playable-verify-ui/SKILL.md +295 -0
  265. package/project-template/skills/playable-verify-ui/examples/asset-manifest.example.json +36 -0
  266. package/project-template/skills/playable-verify-ui/examples/mahjong-match.profile.yaml +334 -0
  267. package/project-template/skills/playable-verify-ui/profiles/template.profile.yaml +84 -0
  268. package/project-template/skills/playable-verify-ui/reference.md +165 -0
  269. package/project-template/skills/playable-verify-ui/references/phaser-patterns.md +67 -0
  270. package/project-template/skills/playable-verify-ui/references/scaling-decision-tree.md +51 -0
  271. package/project-template/skills/playable-verify-ui/requirements.txt +7 -0
  272. package/project-template/skills/playable-verify-ui/schema/asset-manifest.schema.json +68 -0
  273. package/project-template/skills/playable-verify-ui/schema/playable-ui-profile.schema.json +171 -0
  274. package/project-template/skills/playable-verify-ui/scripts/asset_manifest.py +231 -0
  275. package/project-template/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
  276. package/project-template/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
  277. package/project-template/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
  278. package/project-template/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
  279. package/project-template/skills/playable-verify-ui/scripts/lib/report.py +106 -0
  280. package/project-template/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
  281. package/project-template/skills/playable-verify-ui/scripts/measure_frame_content.py +77 -0
  282. package/project-template/skills/playable-verify-ui/scripts/run_all.py +265 -0
  283. package/project-template/skills/playable-verify-ui/scripts/verify_asset_sizes.py +77 -0
  284. package/project-template/skills/playable-verify-ui/scripts/verify_assets_present.py +62 -0
  285. package/project-template/skills/playable-verify-ui/scripts/verify_atlas_budget.py +84 -0
  286. package/project-template/skills/playable-verify-ui/scripts/verify_atlas_frames.py +79 -0
  287. package/project-template/skills/playable-verify-ui/scripts/verify_bg_clean.py +365 -0
  288. package/project-template/skills/playable-verify-ui/scripts/verify_doc_asset_sync.py +90 -0
  289. package/project-template/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
  290. package/project-template/skills/playable-verify-ui/scripts/verify_game_config_load.js +54 -0
  291. package/project-template/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
  292. package/project-template/skills/playable-verify-ui/scripts/verify_layout_regions.js +110 -0
  293. package/project-template/skills/playable-verify-ui/scripts/verify_profile_schema.py +37 -0
  294. package/project-template/skills/playable-verify-ui/scripts/verify_ring1_visual.py +211 -0
  295. package/project-template/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
  296. package/project-template/skills/playable-verify-ui/scripts/verify_script_load_order.js +73 -0
  297. package/project-template/skills/playable-verify-ui/scripts/verify_slice_plan.py +238 -0
  298. package/project-template/skills/playable-verify-ui/scripts/verify_slice_plan_test.py +125 -0
  299. package/project-template/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
  300. package/project-template/skills/playable-verify-ui/scripts/verify_tile_consistency.py +323 -0
  301. package/project-template/skills/playable-verify-ui/scripts/verify_vision_evidence.py +894 -0
  302. package/project-template/skills/playable-verify-ui/scripts/verify_vision_evidence_test.py +122 -0
  303. package/project-template/skills/playable-verify-ui/scripts/verify_visual.py +422 -0
  304. package/project-template/templates/{designer-log.template.md → artist-log.template.md} +26 -31
  305. package/project-template/templates/atom-tree.template.json +133 -0
  306. package/project-template/templates/design-brief.template.md +59 -162
  307. package/project-template/templates/design-lens-checklist.reference.md +14 -14
  308. package/project-template/templates/design-methodology.md +25 -25
  309. package/project-template/templates/design.template.md +50 -0
  310. package/project-template/templates/developer-log.template.md +187 -63
  311. package/project-template/templates/five-axis-framework.md +89 -44
  312. package/project-template/templates/gen-placeholder-audio.template.mjs +63 -0
  313. package/project-template/templates/gen-placeholders.template.mjs +81 -0
  314. package/project-template/templates/intent-clarifications.template.md +7 -7
  315. package/project-template/templates/layout-spec.template.md +32 -20
  316. package/project-template/templates/plan.template.md +80 -0
  317. package/project-template/templates/project-state.template.md +90 -191
  318. package/project-template/templates/review-report.template.md +208 -24
  319. package/project-template/templates/style-exploration.template.md +8 -8
  320. package/project-template/templates/ta-log.template.md +58 -35
  321. package/project-template/tsconfig.json +20 -0
  322. package/project-template/vite.config.ts +27 -0
  323. package/project-templates/phaser/.claude/agents/artist.md +101 -0
  324. package/project-templates/phaser/.claude/agents/developer.md +123 -0
  325. package/project-templates/phaser/.claude/agents/game-designer.md +192 -0
  326. package/project-templates/phaser/.claude/agents/refs/README.md +77 -0
  327. package/project-templates/phaser/.claude/agents/refs/artist-art-style-catalog.md +533 -0
  328. package/project-templates/phaser/.claude/agents/refs/artist-color-audio-recipes.md +153 -0
  329. package/project-templates/phaser/.claude/agents/refs/artist-deliverable-spec.md +230 -0
  330. package/project-templates/phaser/.claude/agents/refs/artist-dimension-axis.md +27 -0
  331. package/project-templates/phaser/.claude/agents/refs/artist-handoff-v2-checklist.md +76 -0
  332. package/project-templates/phaser/.claude/agents/refs/artist-legacy-fallback.md +14 -0
  333. package/project-templates/phaser/.claude/agents/refs/artist-master-composite-recipes.md +208 -0
  334. package/project-templates/phaser/.claude/agents/refs/artist-style-exploration-flow.md +55 -0
  335. package/project-templates/phaser/.claude/agents/refs/developer-dev-handoff.md +152 -0
  336. package/project-templates/phaser/.claude/agents/refs/developer-impl-cookbook.md +432 -0
  337. package/project-templates/phaser/.claude/agents/refs/developer-legacy-fallback.md +15 -0
  338. package/project-templates/phaser/.claude/agents/refs/developer-phase1-flow.md +404 -0
  339. package/project-templates/phaser/.claude/agents/refs/framework-5-component-filter.md +252 -0
  340. package/project-templates/phaser/.claude/agents/refs/framework-game-feel-juice.md +266 -0
  341. package/project-templates/phaser/.claude/agents/refs/framework-mda.md +147 -0
  342. package/project-templates/phaser/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
  343. package/project-templates/phaser/.claude/agents/refs/game-designer-legacy-fallback.md +15 -0
  344. package/project-templates/phaser/.claude/agents/refs/game-designer-workflow-detail.md +69 -0
  345. package/project-templates/phaser/.claude/agents/refs/implementation-plan-guide.md +309 -0
  346. package/project-templates/phaser/.claude/agents/refs/reviewer-convergence-eval.md +350 -0
  347. package/project-templates/phaser/.claude/agents/refs/reviewer-legacy-fallback.md +14 -0
  348. package/project-templates/phaser/.claude/agents/refs/reviewer-six-dimension-eval.md +6 -0
  349. package/project-templates/phaser/.claude/agents/refs/skill-selector-guide.md +465 -0
  350. package/project-templates/phaser/.claude/agents/refs/skill-value-layers.md +145 -0
  351. package/project-templates/phaser/.claude/agents/refs/storyboard-generation-guide.md +110 -0
  352. package/project-templates/phaser/.claude/agents/refs/ta-3d-flip-recipe.md +88 -0
  353. package/project-templates/phaser/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
  354. package/project-templates/phaser/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
  355. package/project-templates/phaser/.claude/agents/refs/ta-image-generation-detail.md +300 -0
  356. package/project-templates/phaser/.claude/agents/refs/ta-image-ops-reference.md +495 -0
  357. package/project-templates/phaser/.claude/agents/refs/ta-pipeline-cookbook.md +1141 -0
  358. package/project-templates/phaser/.claude/agents/refs/ta-tools-reference.md +111 -0
  359. package/project-templates/phaser/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
  360. package/project-templates/phaser/.claude/agents/refs/technical-artist-legacy-fallback.md +14 -0
  361. package/project-templates/phaser/.claude/agents/reviewer.md +116 -0
  362. package/project-templates/phaser/.claude/agents/technical-artist.md +100 -0
  363. package/project-templates/phaser/.claude/hooks/README.md +120 -0
  364. package/project-templates/phaser/.claude/hooks/asr-coverage-validate.mjs +381 -0
  365. package/project-templates/phaser/.claude/hooks/block-developer-preflight.mjs +185 -0
  366. package/project-templates/phaser/.claude/hooks/block-ring1-tasklist.mjs +95 -0
  367. package/project-templates/phaser/.claude/hooks/gate-dispatcher.mjs +78 -0
  368. package/project-templates/phaser/.claude/hooks/gate-runner.mjs +557 -0
  369. package/project-templates/phaser/.claude/hooks/gate.mjs +171 -0
  370. package/project-templates/phaser/.claude/hooks/snapshot-milestone.mjs +247 -0
  371. package/project-templates/phaser/.claude/hooks/user-prompt.mjs +194 -0
  372. package/project-templates/phaser/.claude/hooks/validate-atom-tree-selection.mjs +281 -0
  373. package/project-templates/phaser/.claude/hooks/validate-workflow-stop.mjs +1889 -0
  374. package/project-templates/phaser/.claude/settings.json +81 -0
  375. package/project-templates/phaser/.claude/skills/brainstorming/SKILL.md +161 -0
  376. package/project-templates/phaser/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
  377. package/project-templates/phaser/.claude/skills/brainstorming/scripts/helper.js +177 -0
  378. package/project-templates/phaser/.claude/skills/brainstorming/scripts/server.cjs +354 -0
  379. package/project-templates/phaser/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
  380. package/project-templates/phaser/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
  381. package/project-templates/phaser/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
  382. package/project-templates/phaser/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  383. package/project-templates/phaser/.claude/skills/brainstorming/visual-companion.md +309 -0
  384. package/project-templates/phaser/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
  385. package/project-templates/phaser/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
  386. package/project-templates/phaser/.claude/skills/playcraft-asset-state-sheet/SKILL.md +205 -0
  387. package/project-templates/phaser/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
  388. package/project-templates/phaser/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
  389. package/project-templates/phaser/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
  390. package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +298 -0
  391. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
  392. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
  393. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +324 -0
  394. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
  395. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
  396. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
  397. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
  398. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
  399. package/project-templates/phaser/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
  400. package/project-templates/phaser/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
  401. package/project-templates/phaser/.claude/skills/playcraft-masking/SKILL.md +373 -0
  402. package/project-templates/phaser/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
  403. package/project-templates/phaser/.claude/skills/playcraft-research/SKILL.md +215 -0
  404. package/project-templates/phaser/.claude/skills/playcraft-skill-recommender/SKILL.md +331 -0
  405. package/project-templates/phaser/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
  406. package/project-templates/phaser/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
  407. package/project-templates/phaser/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
  408. package/project-templates/phaser/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
  409. package/project-templates/phaser/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
  410. package/project-templates/phaser/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
  411. package/project-templates/phaser/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
  412. package/project-templates/phaser/.claude/skills/playwright-cli/SKILL.md +390 -0
  413. package/project-templates/phaser/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
  414. package/project-templates/phaser/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
  415. package/project-templates/phaser/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
  416. package/project-templates/phaser/.claude/skills/playwright-cli/references/running-code.md +240 -0
  417. package/project-templates/phaser/.claude/skills/playwright-cli/references/session-management.md +226 -0
  418. package/project-templates/phaser/.claude/skills/playwright-cli/references/spec-driven-testing.md +308 -0
  419. package/project-templates/phaser/.claude/skills/playwright-cli/references/storage-state.md +275 -0
  420. package/project-templates/phaser/.claude/skills/playwright-cli/references/test-generation.md +134 -0
  421. package/project-templates/phaser/.claude/skills/playwright-cli/references/tracing.md +142 -0
  422. package/project-templates/phaser/.claude/skills/playwright-cli/references/video-recording.md +153 -0
  423. package/project-templates/phaser/.claude/skills/session-analyzer/SKILL.md +386 -0
  424. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
  425. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
  426. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
  427. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/resolve-session.mjs +103 -0
  428. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
  429. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
  430. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
  431. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
  432. package/project-templates/phaser/.claude/snapshot.mjs +257 -0
  433. package/project-templates/phaser/.cursor/hooks.json +17 -0
  434. package/project-templates/phaser/.cursor/rules/playcraft-orchestrator.mdc +142 -0
  435. package/project-templates/phaser/.cursor/rules/playcraft-subagent-boundary.mdc +29 -0
  436. package/project-templates/phaser/.playcraft-engine +1 -0
  437. package/project-templates/phaser/CHANGELOG.md +105 -0
  438. package/project-templates/phaser/CLAUDE.md +84 -0
  439. package/project-templates/phaser/assets/audio/bgm/.gitkeep +0 -0
  440. package/project-templates/phaser/assets/audio/sfx/.gitkeep +0 -0
  441. package/project-templates/phaser/assets/bundles/.gitkeep +0 -0
  442. package/project-templates/phaser/assets/images/bg/.gitkeep +0 -0
  443. package/project-templates/phaser/assets/images/reference/.gitkeep +0 -0
  444. package/project-templates/phaser/assets/images/storyboard/.gitkeep +0 -0
  445. package/project-templates/phaser/assets/images/tiles/.gitkeep +0 -0
  446. package/project-templates/phaser/assets/images/ui/.gitkeep +0 -0
  447. package/project-templates/phaser/assets/images/vfx/.gitkeep +0 -0
  448. package/project-templates/phaser/assets/models/.gitkeep +0 -0
  449. package/project-templates/phaser/docs/DESIGN.md +65 -0
  450. package/project-templates/phaser/docs/intent-clarifications.md +58 -0
  451. package/project-templates/phaser/docs/pipeline-overview.md +69 -0
  452. package/project-templates/phaser/docs/project-state.md +211 -0
  453. package/project-templates/phaser/docs/scaffold-manifest.md +10 -0
  454. package/project-templates/phaser/docs/team/agent-conduct.md +67 -0
  455. package/project-templates/phaser/docs/team/agent-runtime-matrix.md +80 -0
  456. package/project-templates/phaser/docs/team/atom-tree-format.md +114 -0
  457. package/project-templates/phaser/docs/team/collaboration.md +391 -0
  458. package/project-templates/phaser/docs/team/core-model.md +85 -0
  459. package/project-templates/phaser/docs/team/game-designer-migration-todo.md +8 -0
  460. package/project-templates/phaser/docs/team/harness-legacy-deprecation.md +23 -0
  461. package/project-templates/phaser/docs/team/platform-capabilities.md +18 -0
  462. package/project-templates/phaser/docs/team/workflow-changelog.md +137 -0
  463. package/project-templates/phaser/docs/team/workflow-consistency-checklist.md +240 -0
  464. package/project-templates/phaser/focus-lab/.gitkeep +0 -0
  465. package/project-templates/phaser/game/adapters/phaser.ts +52 -0
  466. package/project-templates/phaser/game/config/.gitkeep +0 -0
  467. package/project-templates/phaser/game/config/game-config.ts +46 -0
  468. package/project-templates/phaser/game/contract/publish.ts +141 -0
  469. package/project-templates/phaser/game/contract/types.ts +34 -0
  470. package/project-templates/phaser/game/gameplay/.gitkeep +0 -0
  471. package/project-templates/phaser/game/index.ts +81 -0
  472. package/project-templates/phaser/game/runtime-contract.ts +28 -0
  473. package/project-templates/phaser/game/scenes/.gitkeep +0 -0
  474. package/project-templates/phaser/globals.d.ts +51 -0
  475. package/project-templates/phaser/index.css +34 -0
  476. package/project-templates/phaser/index.html +18 -0
  477. package/project-templates/phaser/main.ts +9 -0
  478. package/project-templates/phaser/package.json +79 -0
  479. package/project-templates/phaser/skills/README.md +52 -0
  480. package/project-templates/phaser/skills/_shared/agent-prep.md +136 -0
  481. package/project-templates/phaser/skills/_shared/requirements.txt +1 -0
  482. package/project-templates/phaser/skills/_shared/scripts/build-evidence-gate.mjs +87 -0
  483. package/project-templates/phaser/skills/_shared/scripts/context-budget.mjs +215 -0
  484. package/project-templates/phaser/skills/_shared/scripts/create-playable.mjs +140 -0
  485. package/project-templates/phaser/skills/_shared/scripts/dev-start.mjs +151 -0
  486. package/project-templates/phaser/skills/_shared/scripts/disclosure-config.mjs +480 -0
  487. package/project-templates/phaser/skills/_shared/scripts/ensure-project.mjs +532 -0
  488. package/project-templates/phaser/skills/_shared/scripts/gen-placeholder-audio.py +345 -0
  489. package/project-templates/phaser/skills/_shared/scripts/gen-placeholder.py +244 -0
  490. package/project-templates/phaser/skills/_shared/scripts/harness-smoke.mjs +257 -0
  491. package/project-templates/phaser/skills/_shared/scripts/lib/validator-artifacts.mjs +129 -0
  492. package/project-templates/phaser/skills/_shared/scripts/placeholder-pack-sheet.mjs +67 -0
  493. package/project-templates/phaser/skills/_shared/scripts/placeholder_audio_lib.py +142 -0
  494. package/project-templates/phaser/skills/_shared/scripts/placeholder_lib.py +993 -0
  495. package/project-templates/phaser/skills/_shared/scripts/render-project-state.mjs +278 -0
  496. package/project-templates/phaser/skills/_shared/scripts/ring-config.mjs +252 -0
  497. package/project-templates/phaser/skills/_shared/scripts/skills-check.mjs +176 -0
  498. package/project-templates/phaser/skills/_shared/scripts/slice-set.mjs +77 -0
  499. package/project-templates/phaser/skills/_shared/scripts/state-advance-ring.mjs +63 -0
  500. package/project-templates/phaser/skills/_shared/scripts/state-get.mjs +215 -0
  501. package/project-templates/phaser/skills/_shared/scripts/state-handoff.mjs +120 -0
  502. package/project-templates/phaser/skills/_shared/scripts/state-set.mjs +113 -0
  503. package/project-templates/phaser/skills/_shared/scripts/state-store.mjs +893 -0
  504. package/project-templates/phaser/skills/_shared/scripts/sync-template-skills.mjs +112 -0
  505. package/project-templates/phaser/skills/_shared/scripts/verify-asset-code-sync.mjs +265 -0
  506. package/project-templates/phaser/skills/_shared/scripts/verify-atom-conformance.mjs +181 -0
  507. package/project-templates/phaser/skills/_shared/scripts/verify-env.mjs +285 -0
  508. package/project-templates/phaser/skills/_shared/scripts/verify-placeholders.mjs +161 -0
  509. package/project-templates/phaser/skills/_shared/scripts/workflow-doctor.mjs +348 -0
  510. package/project-templates/phaser/skills/playable-animate/SKILL.md +52 -0
  511. package/project-templates/phaser/skills/playable-animate/agents/openai.yaml +4 -0
  512. package/project-templates/phaser/skills/playable-animate/references/board-refresh.md +30 -0
  513. package/project-templates/phaser/skills/playable-animate/references/diagnosis-checklist.md +25 -0
  514. package/project-templates/phaser/skills/playable-animate/references/entrance-sequencing.md +33 -0
  515. package/project-templates/phaser/skills/playable-animate/references/interaction-timing.md +23 -0
  516. package/project-templates/phaser/skills/playable-art/SKILL.md +203 -0
  517. package/project-templates/phaser/skills/playable-art/agents/openai.yaml +4 -0
  518. package/project-templates/phaser/skills/playable-art/references/alpha-postprocess.md +46 -0
  519. package/project-templates/phaser/skills/playable-art/references/cta-art-spec.md +59 -0
  520. package/project-templates/phaser/skills/playable-art/references/layout-proportions.md +30 -0
  521. package/project-templates/phaser/skills/playable-art/references/prompt-patterns.md +83 -0
  522. package/project-templates/phaser/skills/playable-art/references/reference-comparison.md +30 -0
  523. package/project-templates/phaser/skills/playable-art/references/sheet-import-v2.md +91 -0
  524. package/project-templates/phaser/skills/playable-art/scripts/build-atlas.py +332 -0
  525. package/project-templates/phaser/skills/playable-art/scripts/chroma-key.py +50 -0
  526. package/project-templates/phaser/skills/playable-art/scripts/extract-alpha-split.py +65 -0
  527. package/project-templates/phaser/skills/playable-art/scripts/extract-dual-bg-alpha.py +118 -0
  528. package/project-templates/phaser/skills/playable-art/scripts/import-gpt-art-sheets-v2.py +403 -0
  529. package/project-templates/phaser/skills/playable-art/scripts/import-gpt-art-sheets.py +280 -0
  530. package/project-templates/phaser/skills/playable-art/scripts/import-gpt-tile-sheet.py +114 -0
  531. package/project-templates/phaser/skills/playable-art/scripts/import_gpt_art_pipeline.py +321 -0
  532. package/project-templates/phaser/skills/playable-art/scripts/import_gpt_art_sheets_v2_test.py +126 -0
  533. package/project-templates/phaser/skills/playable-art/scripts/placeholder_common.py +98 -0
  534. package/project-templates/phaser/skills/playable-art/scripts/remove-bg.py +91 -0
  535. package/project-templates/phaser/skills/playable-art/scripts/review-processed-assets.py +468 -0
  536. package/project-templates/phaser/skills/playable-art/scripts/review_processed_assets_test.py +116 -0
  537. package/project-templates/phaser/skills/playable-art/scripts/verify-alpha-clean.py +105 -0
  538. package/project-templates/phaser/skills/playable-debug/SKILL.md +116 -0
  539. package/project-templates/phaser/skills/playable-debug/agents/openai.yaml +4 -0
  540. package/project-templates/phaser/skills/playable-debug/references/debug-config.md +40 -0
  541. package/project-templates/phaser/skills/playable-focus/SKILL.md +148 -0
  542. package/project-templates/phaser/skills/playable-focus/scripts/bootstrap-playground.mjs +84 -0
  543. package/project-templates/phaser/skills/playable-focus/scripts/extract-focus-frames.mjs +87 -0
  544. package/project-templates/phaser/skills/playable-focus/scripts/generate-focus-reports.mjs +121 -0
  545. package/project-templates/phaser/skills/playable-focus/scripts/playground.mjs +390 -0
  546. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/frame.html +39 -0
  547. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/harness/clean-stage.js +114 -0
  548. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/harness/frame-lab-scene.js +94 -0
  549. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/harness/particle-lab-scene.js +224 -0
  550. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/model.html +16 -0
  551. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/particle.html +38 -0
  552. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/shatter.html +47 -0
  553. package/project-templates/phaser/skills/playable-fx-decompose/SKILL.md +212 -0
  554. package/project-templates/phaser/skills/playable-fx-decompose/examples.md +89 -0
  555. package/project-templates/phaser/skills/playable-fx-decompose/reference.md +201 -0
  556. package/project-templates/phaser/skills/playable-fx-decompose/requirements.txt +2 -0
  557. package/project-templates/phaser/skills/playable-fx-decompose/scripts/build-blank-tile-crack.py +192 -0
  558. package/project-templates/phaser/skills/playable-fx-decompose/scripts/fix-shard-debris-frames.py +60 -0
  559. package/project-templates/phaser/skills/playable-fx-decompose/scripts/generate-shard-debris.py +276 -0
  560. package/project-templates/phaser/skills/playable-fx-peak/SKILL.md +237 -0
  561. package/project-templates/phaser/skills/playable-fx-peak/requirements.txt +2 -0
  562. package/project-templates/phaser/skills/playable-fx-peak/scripts/fix-frame-edges.py +70 -0
  563. package/project-templates/phaser/skills/playable-fx-peak/scripts/repack-frame-sequence.py +526 -0
  564. package/project-templates/phaser/skills/playable-fx-peak/scripts/validate-frame-anchors.py +130 -0
  565. package/project-templates/phaser/skills/playable-placeholder/SKILL.md +239 -0
  566. package/project-templates/phaser/skills/playable-record/SKILL.md +166 -0
  567. package/project-templates/phaser/skills/playable-record/scripts/lib/build-artifact.mjs +92 -0
  568. package/project-templates/phaser/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
  569. package/project-templates/phaser/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
  570. package/project-templates/phaser/skills/playable-record/scripts/lib/slice-structure-verify.mjs +199 -0
  571. package/project-templates/phaser/skills/playable-record/scripts/record-playable.mjs +583 -0
  572. package/project-templates/phaser/skills/playable-record/scripts/verify-build.mjs +185 -0
  573. package/project-templates/phaser/skills/playable-record/scripts/verify-contract.mjs +261 -0
  574. package/project-templates/phaser/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
  575. package/project-templates/phaser/skills/playable-record/scripts/verify-fusion.mjs +320 -0
  576. package/project-templates/phaser/skills/playable-record/scripts/verify-lifecycle.mjs +78 -0
  577. package/project-templates/phaser/skills/playable-record/scripts/verify-slice-artist.mjs +53 -0
  578. package/project-templates/phaser/skills/playable-record/scripts/verify-slice-ta.mjs +53 -0
  579. package/project-templates/phaser/skills/playable-record/scripts/verify-slice.mjs +142 -0
  580. package/project-templates/phaser/skills/playable-record/scripts/verify-visual-ring.mjs +250 -0
  581. package/project-templates/phaser/skills/playable-record/scripts/verify-visual-ring1.mjs +190 -0
  582. package/project-templates/phaser/skills/playable-record/scripts/verify-vlm-video.mjs +227 -0
  583. package/project-templates/phaser/skills/playable-validate/SKILL.md +260 -0
  584. package/project-templates/phaser/skills/playable-validate/references/checklist-detail.md +36 -0
  585. package/project-templates/phaser/skills/playable-validate/references/single-html-bundle.md +76 -0
  586. package/project-templates/phaser/skills/playable-validate/validation-rules.md +200 -0
  587. package/project-templates/phaser/skills/playable-verify-ui/SKILL.md +295 -0
  588. package/project-templates/phaser/skills/playable-verify-ui/examples/asset-manifest.example.json +36 -0
  589. package/project-templates/phaser/skills/playable-verify-ui/examples/mahjong-match.profile.yaml +334 -0
  590. package/project-templates/phaser/skills/playable-verify-ui/profiles/template.profile.yaml +84 -0
  591. package/project-templates/phaser/skills/playable-verify-ui/reference.md +165 -0
  592. package/project-templates/phaser/skills/playable-verify-ui/references/phaser-patterns.md +67 -0
  593. package/project-templates/phaser/skills/playable-verify-ui/references/scaling-decision-tree.md +51 -0
  594. package/project-templates/phaser/skills/playable-verify-ui/requirements.txt +7 -0
  595. package/project-templates/phaser/skills/playable-verify-ui/schema/asset-manifest.schema.json +68 -0
  596. package/project-templates/phaser/skills/playable-verify-ui/schema/playable-ui-profile.schema.json +171 -0
  597. package/project-templates/phaser/skills/playable-verify-ui/scripts/asset_manifest.py +231 -0
  598. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
  599. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
  600. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
  601. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
  602. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/report.py +106 -0
  603. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
  604. package/project-templates/phaser/skills/playable-verify-ui/scripts/measure_frame_content.py +77 -0
  605. package/project-templates/phaser/skills/playable-verify-ui/scripts/run_all.py +265 -0
  606. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_asset_sizes.py +77 -0
  607. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_assets_present.py +62 -0
  608. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_atlas_budget.py +84 -0
  609. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_atlas_frames.py +79 -0
  610. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_bg_clean.py +365 -0
  611. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_doc_asset_sync.py +90 -0
  612. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
  613. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_game_config_load.js +54 -0
  614. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
  615. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_layout_regions.js +110 -0
  616. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_profile_schema.py +37 -0
  617. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_ring1_visual.py +211 -0
  618. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
  619. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_script_load_order.js +73 -0
  620. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_slice_plan.py +238 -0
  621. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_slice_plan_test.py +125 -0
  622. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
  623. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_tile_consistency.py +323 -0
  624. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_vision_evidence.py +894 -0
  625. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_vision_evidence_test.py +122 -0
  626. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_visual.py +422 -0
  627. package/project-templates/phaser/ta-workspace/scripts/.gitkeep +0 -0
  628. package/project-templates/phaser/templates/artist-log.template.md +182 -0
  629. package/project-templates/phaser/templates/atom-tree.template.json +133 -0
  630. package/project-templates/phaser/templates/design-brief.template.md +92 -0
  631. package/project-templates/phaser/templates/design-lens-checklist.reference.md +117 -0
  632. package/project-templates/phaser/templates/design-methodology.md +99 -0
  633. package/project-templates/phaser/templates/design.template.md +50 -0
  634. package/project-templates/phaser/templates/developer-log.template.md +258 -0
  635. package/project-templates/phaser/templates/five-axis-framework.md +194 -0
  636. package/project-templates/phaser/templates/gen-placeholder-audio.template.mjs +63 -0
  637. package/project-templates/phaser/templates/gen-placeholders.template.mjs +81 -0
  638. package/project-templates/phaser/templates/intent-clarifications.template.md +58 -0
  639. package/project-templates/phaser/templates/layout-spec.template.md +198 -0
  640. package/project-templates/phaser/templates/plan.template.md +80 -0
  641. package/project-templates/phaser/templates/project-state.template.md +136 -0
  642. package/project-templates/phaser/templates/review-report.template.md +275 -0
  643. package/project-templates/phaser/templates/style-exploration.template.md +93 -0
  644. package/project-templates/phaser/templates/ta-log.template.md +394 -0
  645. package/project-templates/phaser/tsconfig.json +20 -0
  646. package/project-templates/phaser/vite.config.ts +27 -0
  647. package/project-templates/threejs/.claude/agents/artist.md +101 -0
  648. package/project-templates/threejs/.claude/agents/developer.md +123 -0
  649. package/project-templates/threejs/.claude/agents/game-designer.md +192 -0
  650. package/project-templates/threejs/.claude/agents/refs/README.md +77 -0
  651. package/project-templates/threejs/.claude/agents/refs/artist-art-style-catalog.md +533 -0
  652. package/project-templates/threejs/.claude/agents/refs/artist-color-audio-recipes.md +153 -0
  653. package/project-templates/threejs/.claude/agents/refs/artist-deliverable-spec.md +230 -0
  654. package/project-templates/threejs/.claude/agents/refs/artist-dimension-axis.md +27 -0
  655. package/project-templates/threejs/.claude/agents/refs/artist-handoff-v2-checklist.md +76 -0
  656. package/project-templates/threejs/.claude/agents/refs/artist-legacy-fallback.md +14 -0
  657. package/project-templates/threejs/.claude/agents/refs/artist-master-composite-recipes.md +208 -0
  658. package/project-templates/threejs/.claude/agents/refs/artist-style-exploration-flow.md +55 -0
  659. package/project-templates/threejs/.claude/agents/refs/developer-dev-handoff.md +152 -0
  660. package/project-templates/threejs/.claude/agents/refs/developer-impl-cookbook.md +432 -0
  661. package/project-templates/threejs/.claude/agents/refs/developer-legacy-fallback.md +15 -0
  662. package/project-templates/threejs/.claude/agents/refs/developer-phase1-flow.md +404 -0
  663. package/project-templates/threejs/.claude/agents/refs/framework-5-component-filter.md +252 -0
  664. package/project-templates/threejs/.claude/agents/refs/framework-game-feel-juice.md +266 -0
  665. package/project-templates/threejs/.claude/agents/refs/framework-mda.md +147 -0
  666. package/project-templates/threejs/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
  667. package/project-templates/threejs/.claude/agents/refs/game-designer-legacy-fallback.md +15 -0
  668. package/project-templates/threejs/.claude/agents/refs/game-designer-workflow-detail.md +69 -0
  669. package/project-templates/threejs/.claude/agents/refs/implementation-plan-guide.md +309 -0
  670. package/project-templates/threejs/.claude/agents/refs/reviewer-convergence-eval.md +350 -0
  671. package/project-templates/threejs/.claude/agents/refs/reviewer-legacy-fallback.md +14 -0
  672. package/project-templates/threejs/.claude/agents/refs/reviewer-six-dimension-eval.md +6 -0
  673. package/project-templates/threejs/.claude/agents/refs/skill-selector-guide.md +465 -0
  674. package/project-templates/threejs/.claude/agents/refs/skill-value-layers.md +145 -0
  675. package/project-templates/threejs/.claude/agents/refs/storyboard-generation-guide.md +110 -0
  676. package/project-templates/threejs/.claude/agents/refs/ta-3d-flip-recipe.md +88 -0
  677. package/project-templates/threejs/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
  678. package/project-templates/threejs/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
  679. package/project-templates/threejs/.claude/agents/refs/ta-image-generation-detail.md +300 -0
  680. package/project-templates/threejs/.claude/agents/refs/ta-image-ops-reference.md +495 -0
  681. package/project-templates/threejs/.claude/agents/refs/ta-pipeline-cookbook.md +1141 -0
  682. package/project-templates/threejs/.claude/agents/refs/ta-tools-reference.md +111 -0
  683. package/project-templates/threejs/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
  684. package/project-templates/threejs/.claude/agents/refs/technical-artist-legacy-fallback.md +14 -0
  685. package/project-templates/threejs/.claude/agents/reviewer.md +116 -0
  686. package/project-templates/threejs/.claude/agents/technical-artist.md +100 -0
  687. package/project-templates/threejs/.claude/hooks/README.md +120 -0
  688. package/project-templates/threejs/.claude/hooks/asr-coverage-validate.mjs +381 -0
  689. package/project-templates/threejs/.claude/hooks/block-developer-preflight.mjs +185 -0
  690. package/project-templates/threejs/.claude/hooks/block-ring1-tasklist.mjs +95 -0
  691. package/project-templates/threejs/.claude/hooks/gate-dispatcher.mjs +78 -0
  692. package/project-templates/threejs/.claude/hooks/gate-runner.mjs +557 -0
  693. package/project-templates/threejs/.claude/hooks/gate.mjs +171 -0
  694. package/project-templates/threejs/.claude/hooks/snapshot-milestone.mjs +247 -0
  695. package/project-templates/threejs/.claude/hooks/user-prompt.mjs +194 -0
  696. package/project-templates/threejs/.claude/hooks/validate-atom-tree-selection.mjs +281 -0
  697. package/project-templates/threejs/.claude/hooks/validate-workflow-stop.mjs +1889 -0
  698. package/project-templates/threejs/.claude/settings.json +81 -0
  699. package/project-templates/threejs/.claude/skills/brainstorming/SKILL.md +161 -0
  700. package/project-templates/threejs/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
  701. package/project-templates/threejs/.claude/skills/brainstorming/scripts/helper.js +177 -0
  702. package/project-templates/threejs/.claude/skills/brainstorming/scripts/server.cjs +354 -0
  703. package/project-templates/threejs/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
  704. package/project-templates/threejs/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
  705. package/project-templates/threejs/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
  706. package/project-templates/threejs/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  707. package/project-templates/threejs/.claude/skills/brainstorming/visual-companion.md +309 -0
  708. package/project-templates/threejs/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
  709. package/project-templates/threejs/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
  710. package/project-templates/threejs/.claude/skills/playcraft-asset-state-sheet/SKILL.md +205 -0
  711. package/project-templates/threejs/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
  712. package/project-templates/threejs/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
  713. package/project-templates/threejs/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
  714. package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +298 -0
  715. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
  716. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
  717. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +324 -0
  718. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
  719. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
  720. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
  721. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
  722. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
  723. package/project-templates/threejs/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
  724. package/project-templates/threejs/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
  725. package/project-templates/threejs/.claude/skills/playcraft-masking/SKILL.md +373 -0
  726. package/project-templates/threejs/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
  727. package/project-templates/threejs/.claude/skills/playcraft-research/SKILL.md +215 -0
  728. package/project-templates/threejs/.claude/skills/playcraft-skill-recommender/SKILL.md +331 -0
  729. package/project-templates/threejs/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
  730. package/project-templates/threejs/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
  731. package/project-templates/threejs/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
  732. package/project-templates/threejs/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
  733. package/project-templates/threejs/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
  734. package/project-templates/threejs/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
  735. package/project-templates/threejs/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
  736. package/project-templates/threejs/.claude/skills/playwright-cli/SKILL.md +390 -0
  737. package/project-templates/threejs/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
  738. package/project-templates/threejs/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
  739. package/project-templates/threejs/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
  740. package/project-templates/threejs/.claude/skills/playwright-cli/references/running-code.md +240 -0
  741. package/project-templates/threejs/.claude/skills/playwright-cli/references/session-management.md +226 -0
  742. package/project-templates/threejs/.claude/skills/playwright-cli/references/spec-driven-testing.md +308 -0
  743. package/project-templates/threejs/.claude/skills/playwright-cli/references/storage-state.md +275 -0
  744. package/project-templates/threejs/.claude/skills/playwright-cli/references/test-generation.md +134 -0
  745. package/project-templates/threejs/.claude/skills/playwright-cli/references/tracing.md +142 -0
  746. package/project-templates/threejs/.claude/skills/playwright-cli/references/video-recording.md +153 -0
  747. package/project-templates/threejs/.claude/skills/session-analyzer/SKILL.md +386 -0
  748. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
  749. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
  750. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
  751. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/resolve-session.mjs +103 -0
  752. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
  753. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
  754. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
  755. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
  756. package/project-templates/threejs/.claude/snapshot.mjs +257 -0
  757. package/project-templates/threejs/.cursor/hooks.json +17 -0
  758. package/project-templates/threejs/.cursor/rules/playcraft-orchestrator.mdc +142 -0
  759. package/project-templates/threejs/.cursor/rules/playcraft-subagent-boundary.mdc +29 -0
  760. package/project-templates/threejs/.playcraft-engine +1 -0
  761. package/project-templates/threejs/CHANGELOG.md +105 -0
  762. package/project-templates/threejs/CLAUDE.md +84 -0
  763. package/project-templates/threejs/assets/audio/bgm/.gitkeep +0 -0
  764. package/project-templates/threejs/assets/audio/sfx/.gitkeep +0 -0
  765. package/project-templates/threejs/assets/bundles/.gitkeep +0 -0
  766. package/project-templates/threejs/assets/images/bg/.gitkeep +0 -0
  767. package/project-templates/threejs/assets/images/reference/.gitkeep +0 -0
  768. package/project-templates/threejs/assets/images/storyboard/.gitkeep +0 -0
  769. package/project-templates/threejs/assets/images/tiles/.gitkeep +0 -0
  770. package/project-templates/threejs/assets/images/ui/.gitkeep +0 -0
  771. package/project-templates/threejs/assets/images/vfx/.gitkeep +0 -0
  772. package/project-templates/threejs/assets/models/.gitkeep +0 -0
  773. package/project-templates/threejs/docs/DESIGN.md +65 -0
  774. package/project-templates/threejs/docs/intent-clarifications.md +58 -0
  775. package/project-templates/threejs/docs/pipeline-overview.md +69 -0
  776. package/project-templates/threejs/docs/project-state.md +211 -0
  777. package/project-templates/threejs/docs/scaffold-manifest.md +10 -0
  778. package/project-templates/threejs/docs/team/agent-conduct.md +67 -0
  779. package/project-templates/threejs/docs/team/agent-runtime-matrix.md +80 -0
  780. package/project-templates/threejs/docs/team/atom-tree-format.md +114 -0
  781. package/project-templates/threejs/docs/team/collaboration.md +391 -0
  782. package/project-templates/threejs/docs/team/core-model.md +85 -0
  783. package/project-templates/threejs/docs/team/game-designer-migration-todo.md +8 -0
  784. package/project-templates/threejs/docs/team/harness-legacy-deprecation.md +23 -0
  785. package/project-templates/threejs/docs/team/platform-capabilities.md +18 -0
  786. package/project-templates/threejs/docs/team/workflow-changelog.md +137 -0
  787. package/project-templates/threejs/docs/team/workflow-consistency-checklist.md +240 -0
  788. package/project-templates/threejs/focus-lab/.gitkeep +0 -0
  789. package/project-templates/threejs/game/config/.gitkeep +0 -0
  790. package/project-templates/threejs/game/config/game-config.ts +46 -0
  791. package/project-templates/threejs/game/contract/publish.ts +141 -0
  792. package/project-templates/threejs/game/contract/types.ts +34 -0
  793. package/project-templates/threejs/game/gameplay/.gitkeep +0 -0
  794. package/project-templates/threejs/game/index.ts +76 -0
  795. package/project-templates/threejs/game/runtime-contract.ts +15 -0
  796. package/project-templates/threejs/game/scenes/.gitkeep +0 -0
  797. package/project-templates/threejs/globals.d.ts +51 -0
  798. package/project-templates/threejs/index.css +34 -0
  799. package/project-templates/threejs/index.html +18 -0
  800. package/project-templates/threejs/main.ts +9 -0
  801. package/project-templates/threejs/package.json +79 -0
  802. package/project-templates/threejs/skills/README.md +52 -0
  803. package/project-templates/threejs/skills/_shared/agent-prep.md +136 -0
  804. package/project-templates/threejs/skills/_shared/requirements.txt +1 -0
  805. package/project-templates/threejs/skills/_shared/scripts/build-evidence-gate.mjs +87 -0
  806. package/project-templates/threejs/skills/_shared/scripts/context-budget.mjs +215 -0
  807. package/project-templates/threejs/skills/_shared/scripts/create-playable.mjs +140 -0
  808. package/project-templates/threejs/skills/_shared/scripts/dev-start.mjs +151 -0
  809. package/project-templates/threejs/skills/_shared/scripts/disclosure-config.mjs +480 -0
  810. package/project-templates/threejs/skills/_shared/scripts/ensure-project.mjs +532 -0
  811. package/project-templates/threejs/skills/_shared/scripts/gen-placeholder-audio.py +345 -0
  812. package/project-templates/threejs/skills/_shared/scripts/gen-placeholder.py +244 -0
  813. package/project-templates/threejs/skills/_shared/scripts/harness-smoke.mjs +257 -0
  814. package/project-templates/threejs/skills/_shared/scripts/lib/validator-artifacts.mjs +129 -0
  815. package/project-templates/threejs/skills/_shared/scripts/placeholder-pack-sheet.mjs +67 -0
  816. package/project-templates/threejs/skills/_shared/scripts/placeholder_audio_lib.py +142 -0
  817. package/project-templates/threejs/skills/_shared/scripts/placeholder_lib.py +993 -0
  818. package/project-templates/threejs/skills/_shared/scripts/render-project-state.mjs +278 -0
  819. package/project-templates/threejs/skills/_shared/scripts/ring-config.mjs +252 -0
  820. package/project-templates/threejs/skills/_shared/scripts/skills-check.mjs +176 -0
  821. package/project-templates/threejs/skills/_shared/scripts/slice-set.mjs +77 -0
  822. package/project-templates/threejs/skills/_shared/scripts/state-advance-ring.mjs +63 -0
  823. package/project-templates/threejs/skills/_shared/scripts/state-get.mjs +215 -0
  824. package/project-templates/threejs/skills/_shared/scripts/state-handoff.mjs +120 -0
  825. package/project-templates/threejs/skills/_shared/scripts/state-set.mjs +113 -0
  826. package/project-templates/threejs/skills/_shared/scripts/state-store.mjs +893 -0
  827. package/project-templates/threejs/skills/_shared/scripts/sync-template-skills.mjs +112 -0
  828. package/project-templates/threejs/skills/_shared/scripts/verify-asset-code-sync.mjs +265 -0
  829. package/project-templates/threejs/skills/_shared/scripts/verify-atom-conformance.mjs +181 -0
  830. package/project-templates/threejs/skills/_shared/scripts/verify-env.mjs +285 -0
  831. package/project-templates/threejs/skills/_shared/scripts/verify-placeholders.mjs +161 -0
  832. package/project-templates/threejs/skills/_shared/scripts/workflow-doctor.mjs +348 -0
  833. package/project-templates/threejs/skills/playable-animate/SKILL.md +52 -0
  834. package/project-templates/threejs/skills/playable-animate/agents/openai.yaml +4 -0
  835. package/project-templates/threejs/skills/playable-animate/references/board-refresh.md +30 -0
  836. package/project-templates/threejs/skills/playable-animate/references/diagnosis-checklist.md +25 -0
  837. package/project-templates/threejs/skills/playable-animate/references/entrance-sequencing.md +33 -0
  838. package/project-templates/threejs/skills/playable-animate/references/interaction-timing.md +23 -0
  839. package/project-templates/threejs/skills/playable-art/SKILL.md +203 -0
  840. package/project-templates/threejs/skills/playable-art/agents/openai.yaml +4 -0
  841. package/project-templates/threejs/skills/playable-art/references/alpha-postprocess.md +46 -0
  842. package/project-templates/threejs/skills/playable-art/references/cta-art-spec.md +59 -0
  843. package/project-templates/threejs/skills/playable-art/references/layout-proportions.md +30 -0
  844. package/project-templates/threejs/skills/playable-art/references/prompt-patterns.md +83 -0
  845. package/project-templates/threejs/skills/playable-art/references/reference-comparison.md +30 -0
  846. package/project-templates/threejs/skills/playable-art/references/sheet-import-v2.md +91 -0
  847. package/project-templates/threejs/skills/playable-art/scripts/build-atlas.py +332 -0
  848. package/project-templates/threejs/skills/playable-art/scripts/chroma-key.py +50 -0
  849. package/project-templates/threejs/skills/playable-art/scripts/extract-alpha-split.py +65 -0
  850. package/project-templates/threejs/skills/playable-art/scripts/extract-dual-bg-alpha.py +118 -0
  851. package/project-templates/threejs/skills/playable-art/scripts/import-gpt-art-sheets-v2.py +403 -0
  852. package/project-templates/threejs/skills/playable-art/scripts/import-gpt-art-sheets.py +280 -0
  853. package/project-templates/threejs/skills/playable-art/scripts/import-gpt-tile-sheet.py +114 -0
  854. package/project-templates/threejs/skills/playable-art/scripts/import_gpt_art_pipeline.py +321 -0
  855. package/project-templates/threejs/skills/playable-art/scripts/import_gpt_art_sheets_v2_test.py +126 -0
  856. package/project-templates/threejs/skills/playable-art/scripts/placeholder_common.py +98 -0
  857. package/project-templates/threejs/skills/playable-art/scripts/remove-bg.py +91 -0
  858. package/project-templates/threejs/skills/playable-art/scripts/review-processed-assets.py +468 -0
  859. package/project-templates/threejs/skills/playable-art/scripts/review_processed_assets_test.py +116 -0
  860. package/project-templates/threejs/skills/playable-art/scripts/verify-alpha-clean.py +105 -0
  861. package/project-templates/threejs/skills/playable-debug/SKILL.md +116 -0
  862. package/project-templates/threejs/skills/playable-debug/agents/openai.yaml +4 -0
  863. package/project-templates/threejs/skills/playable-debug/references/debug-config.md +40 -0
  864. package/project-templates/threejs/skills/playable-focus/SKILL.md +148 -0
  865. package/project-templates/threejs/skills/playable-focus/scripts/bootstrap-playground.mjs +84 -0
  866. package/project-templates/threejs/skills/playable-focus/scripts/extract-focus-frames.mjs +87 -0
  867. package/project-templates/threejs/skills/playable-focus/scripts/generate-focus-reports.mjs +121 -0
  868. package/project-templates/threejs/skills/playable-focus/scripts/playground.mjs +390 -0
  869. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/frame.html +39 -0
  870. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/harness/clean-stage.js +114 -0
  871. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/harness/frame-lab-scene.js +94 -0
  872. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/harness/particle-lab-scene.js +224 -0
  873. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/model.html +16 -0
  874. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/particle.html +38 -0
  875. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/shatter.html +47 -0
  876. package/project-templates/threejs/skills/playable-fx-decompose/SKILL.md +212 -0
  877. package/project-templates/threejs/skills/playable-fx-decompose/examples.md +89 -0
  878. package/project-templates/threejs/skills/playable-fx-decompose/reference.md +201 -0
  879. package/project-templates/threejs/skills/playable-fx-decompose/requirements.txt +2 -0
  880. package/project-templates/threejs/skills/playable-fx-decompose/scripts/build-blank-tile-crack.py +192 -0
  881. package/project-templates/threejs/skills/playable-fx-decompose/scripts/fix-shard-debris-frames.py +60 -0
  882. package/project-templates/threejs/skills/playable-fx-decompose/scripts/generate-shard-debris.py +276 -0
  883. package/project-templates/threejs/skills/playable-fx-peak/SKILL.md +237 -0
  884. package/project-templates/threejs/skills/playable-fx-peak/requirements.txt +2 -0
  885. package/project-templates/threejs/skills/playable-fx-peak/scripts/fix-frame-edges.py +70 -0
  886. package/project-templates/threejs/skills/playable-fx-peak/scripts/repack-frame-sequence.py +526 -0
  887. package/project-templates/threejs/skills/playable-fx-peak/scripts/validate-frame-anchors.py +130 -0
  888. package/project-templates/threejs/skills/playable-placeholder/SKILL.md +239 -0
  889. package/project-templates/threejs/skills/playable-record/SKILL.md +166 -0
  890. package/project-templates/threejs/skills/playable-record/scripts/lib/build-artifact.mjs +92 -0
  891. package/project-templates/threejs/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
  892. package/project-templates/threejs/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
  893. package/project-templates/threejs/skills/playable-record/scripts/lib/slice-structure-verify.mjs +199 -0
  894. package/project-templates/threejs/skills/playable-record/scripts/record-playable.mjs +583 -0
  895. package/project-templates/threejs/skills/playable-record/scripts/verify-build.mjs +185 -0
  896. package/project-templates/threejs/skills/playable-record/scripts/verify-contract.mjs +261 -0
  897. package/project-templates/threejs/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
  898. package/project-templates/threejs/skills/playable-record/scripts/verify-fusion.mjs +320 -0
  899. package/project-templates/threejs/skills/playable-record/scripts/verify-lifecycle.mjs +78 -0
  900. package/project-templates/threejs/skills/playable-record/scripts/verify-slice-artist.mjs +53 -0
  901. package/project-templates/threejs/skills/playable-record/scripts/verify-slice-ta.mjs +53 -0
  902. package/project-templates/threejs/skills/playable-record/scripts/verify-slice.mjs +142 -0
  903. package/project-templates/threejs/skills/playable-record/scripts/verify-visual-ring.mjs +250 -0
  904. package/project-templates/threejs/skills/playable-record/scripts/verify-visual-ring1.mjs +190 -0
  905. package/project-templates/threejs/skills/playable-record/scripts/verify-vlm-video.mjs +227 -0
  906. package/project-templates/threejs/skills/playable-validate/SKILL.md +260 -0
  907. package/project-templates/threejs/skills/playable-validate/references/checklist-detail.md +36 -0
  908. package/project-templates/threejs/skills/playable-validate/references/single-html-bundle.md +76 -0
  909. package/project-templates/threejs/skills/playable-validate/validation-rules.md +200 -0
  910. package/project-templates/threejs/skills/playable-verify-ui/SKILL.md +295 -0
  911. package/project-templates/threejs/skills/playable-verify-ui/examples/asset-manifest.example.json +36 -0
  912. package/project-templates/threejs/skills/playable-verify-ui/examples/mahjong-match.profile.yaml +334 -0
  913. package/project-templates/threejs/skills/playable-verify-ui/profiles/template.profile.yaml +84 -0
  914. package/project-templates/threejs/skills/playable-verify-ui/reference.md +165 -0
  915. package/project-templates/threejs/skills/playable-verify-ui/references/phaser-patterns.md +67 -0
  916. package/project-templates/threejs/skills/playable-verify-ui/references/scaling-decision-tree.md +51 -0
  917. package/project-templates/threejs/skills/playable-verify-ui/requirements.txt +7 -0
  918. package/project-templates/threejs/skills/playable-verify-ui/schema/asset-manifest.schema.json +68 -0
  919. package/project-templates/threejs/skills/playable-verify-ui/schema/playable-ui-profile.schema.json +171 -0
  920. package/project-templates/threejs/skills/playable-verify-ui/scripts/asset_manifest.py +231 -0
  921. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
  922. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
  923. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
  924. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
  925. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/report.py +106 -0
  926. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
  927. package/project-templates/threejs/skills/playable-verify-ui/scripts/measure_frame_content.py +77 -0
  928. package/project-templates/threejs/skills/playable-verify-ui/scripts/run_all.py +265 -0
  929. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_asset_sizes.py +77 -0
  930. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_assets_present.py +62 -0
  931. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_atlas_budget.py +84 -0
  932. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_atlas_frames.py +79 -0
  933. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_bg_clean.py +365 -0
  934. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_doc_asset_sync.py +90 -0
  935. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
  936. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_game_config_load.js +54 -0
  937. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
  938. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_layout_regions.js +110 -0
  939. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_profile_schema.py +37 -0
  940. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_ring1_visual.py +211 -0
  941. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
  942. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_script_load_order.js +73 -0
  943. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_slice_plan.py +238 -0
  944. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_slice_plan_test.py +125 -0
  945. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
  946. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_tile_consistency.py +323 -0
  947. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_vision_evidence.py +894 -0
  948. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_vision_evidence_test.py +122 -0
  949. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_visual.py +422 -0
  950. package/project-templates/threejs/ta-workspace/scripts/.gitkeep +0 -0
  951. package/project-templates/threejs/templates/artist-log.template.md +182 -0
  952. package/project-templates/threejs/templates/atom-tree.template.json +133 -0
  953. package/project-templates/threejs/templates/design-brief.template.md +92 -0
  954. package/project-templates/threejs/templates/design-lens-checklist.reference.md +117 -0
  955. package/project-templates/threejs/templates/design-methodology.md +99 -0
  956. package/project-templates/threejs/templates/design.template.md +50 -0
  957. package/project-templates/threejs/templates/developer-log.template.md +258 -0
  958. package/project-templates/threejs/templates/five-axis-framework.md +194 -0
  959. package/project-templates/threejs/templates/gen-placeholder-audio.template.mjs +63 -0
  960. package/project-templates/threejs/templates/gen-placeholders.template.mjs +81 -0
  961. package/project-templates/threejs/templates/intent-clarifications.template.md +58 -0
  962. package/project-templates/threejs/templates/layout-spec.template.md +198 -0
  963. package/project-templates/threejs/templates/plan.template.md +80 -0
  964. package/project-templates/threejs/templates/project-state.template.md +136 -0
  965. package/project-templates/threejs/templates/review-report.template.md +275 -0
  966. package/project-templates/threejs/templates/style-exploration.template.md +93 -0
  967. package/project-templates/threejs/templates/ta-log.template.md +394 -0
  968. package/project-templates/threejs/tsconfig.json +20 -0
  969. package/project-templates/threejs/vite.config.ts +27 -0
  970. package/dist/atom-plan/validate-atom-plan.js +0 -298
  971. package/project-template/.claude/agents/designer.md +0 -124
  972. package/project-template/.claude/agents/pm.md +0 -166
  973. package/project-template/.claude/agents/refs/designer-style-exploration-flow.md +0 -67
  974. package/project-template/.claude/agents/refs/pm-workflow-detail.md +0 -567
  975. package/project-template/.claude/hooks/validate-atom-plan.mjs +0 -224
  976. package/project-template/.claude/skills/playcraft-workflow/SKILL.md +0 -485
  977. package/project-template/docs/team/atom-plan-format.md +0 -173
  978. package/project-template/templates/atom-plan.template.json +0 -44
  979. package/project-template/templates/atom-plan.template.md +0 -108
  980. /package/dist/{atom-plan → asr}/validate-asr-coverage.js +0 -0
  981. /package/{project-template → engine-packs/phaser}/game/config/.gitkeep +0 -0
  982. /package/{project-template → engine-packs/phaser}/game/gameplay/.gitkeep +0 -0
  983. /package/{project-template → engine-packs/phaser}/game/scenes/.gitkeep +0 -0
  984. /package/project-template/.claude/agents/refs/{designer-art-style-catalog.md → artist-art-style-catalog.md} +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * PlayCraft workflow STOP validator — shared by Claude Code (SubagentStop) and Cursor (subagentStop).
4
- * Ensures TA / Developer filled logs/<role>-log.md § Upstream Intake before subagent may stop.
4
+ * Ensures Artist / TA / Developer filled required workflow logs and valid project-state handoff before subagent may stop.
5
5
  *
6
6
  * Exit 0 = pass
7
7
  * Exit 2 = block (Claude: decision block; Cursor: block stop)
@@ -10,56 +10,446 @@
10
10
 
11
11
  import fs from 'node:fs';
12
12
  import path from 'node:path';
13
+ import { createRequire } from 'node:module';
13
14
  import { fileURLToPath } from 'node:url';
14
- import { validateDesignerGate2b } from './asr-coverage-validate.mjs';
15
+ import { validateArtistGate2b } from './asr-coverage-validate.mjs';
16
+ import { diagnose } from '../../skills/_shared/scripts/workflow-doctor.mjs';
17
+ import {
18
+ reviewDoDKeysForRing,
19
+ } from '../../skills/_shared/scripts/ring-config.mjs';
20
+ import { normalizeSliceState, readState, stateExists, isSliceResolved } from '../../skills/_shared/scripts/state-store.mjs';
21
+ import { validateBuildArtifactForSlice } from '../../skills/playable-record/scripts/lib/build-artifact.mjs';
15
22
 
16
23
  const __filename = fileURLToPath(import.meta.url);
24
+ const require = createRequire(import.meta.url);
17
25
 
18
26
  const INTAKE_HEADING = '## Upstream Intake';
19
27
  const SKILL_PREFLIGHT_HEADING = '## Skill Preflight';
20
28
  const PLAN_HEADING = {
21
29
  'technical-artist': '## Production Plan',
22
- developer: '## UI Pass Plan', // default; gameplay_pass uses ## Gameplay Pass Plan
30
+ developer: SKILL_PREFLIGHT_HEADING,
23
31
  };
24
- const PLAN_HEADING_BY_STAGE = {
32
+ const PLAN_HEADING_BY_LOOP = {
25
33
  developer: {
26
- ui_pass: '## UI Pass Plan',
27
- ui_rework: '## UI Pass Plan',
28
- gameplay_pass: '## Gameplay Pass Plan',
34
+ build: SKILL_PREFLIGHT_HEADING,
29
35
  },
30
36
  };
31
37
  const PLAN_REQUIRED_SUBS = {
32
38
  developer: {
33
- ui_pass: ['### Scene-Asset Binding Plan', '### Scene navigation', '### Risk Checklist'],
34
- ui_rework: ['### Scene-Asset Binding Plan', '### Scene navigation', '### Risk Checklist'],
35
- gameplay_pass: ['### PGS Strategy', '### Risk Checklist'],
39
+ build: [],
36
40
  },
37
41
  'technical-artist': ['### Coverage Plan', '### Atlas Assembly Plan', '### Risk Checklist'],
38
42
  };
39
43
  const PLACEHOLDER_RE = /\{\{[^}]+\}\}/;
40
44
  const MIN_TAKEAWAY_LEN = 8;
41
45
 
42
- const TA_REQUIRED_DOCS = [
43
- 'docs/project-state.md',
44
- 'docs/design-brief.md',
45
- 'docs/layout-spec.md',
46
- 'docs/atom-plan.json',
47
- 'docs/atom-plan.md',
48
- 'docs/style-exploration.md',
49
- 'logs/designer-log.md',
50
- ];
46
+ /** Required upstream docs scale with growth ring — ring 1 stays minimal (DESIGN only; design-brief is optional scratch). */
47
+ const DEV_REQUIRED_DOCS_BY_RING = {
48
+ 1: ['docs/project-state.md', 'docs/DESIGN.md'],
49
+ 2: [
50
+ 'docs/project-state.md',
51
+ 'docs/design-brief.md',
52
+ 'docs/DESIGN.md',
53
+ 'docs/layout-spec.md',
54
+ 'docs/atom-tree.json',
55
+ 'docs/implementation-plan.md',
56
+ ],
57
+ 3: [
58
+ 'docs/project-state.md',
59
+ 'docs/design-brief.md',
60
+ 'docs/DESIGN.md',
61
+ 'docs/layout-spec.md',
62
+ 'docs/atom-tree.json',
63
+ 'docs/implementation-plan.md',
64
+ 'docs/style-exploration.md',
65
+ ],
66
+ 4: [
67
+ 'docs/project-state.md',
68
+ 'docs/design-brief.md',
69
+ 'docs/DESIGN.md',
70
+ 'docs/layout-spec.md',
71
+ 'docs/atom-tree.json',
72
+ 'docs/implementation-plan.md',
73
+ 'docs/style-exploration.md',
74
+ 'logs/artist-log.md',
75
+ ],
76
+ };
51
77
 
52
- const DEV_REQUIRED_DOCS = [...TA_REQUIRED_DOCS, 'logs/ta-log.md'];
78
+ const TA_REQUIRED_DOCS_BY_RING = {
79
+ 4: [
80
+ 'docs/project-state.md',
81
+ 'docs/design-brief.md',
82
+ 'docs/layout-spec.md',
83
+ 'docs/atom-tree.json',
84
+ 'docs/style-exploration.md',
85
+ 'logs/artist-log.md',
86
+ ],
87
+ };
53
88
 
54
- // Designer does not perform Upstream Intake (Designer is the upstream itself). Only Skill Preflight is enforced in Phase 2 (stage = production).
55
- const DESIGNER_REQUIRED_DOCS = [];
89
+ // Artist: Skill Preflight enforced in ring 3+ build and ring 4 Wave 1.
90
+ const ARTIST_REQUIRED_DOCS = [];
56
91
 
57
92
  const ROLE_LOG = {
58
- 'technical-artist': { log: 'logs/ta-log.md', docs: TA_REQUIRED_DOCS, label: 'Technical Artist' },
59
- developer: { log: 'logs/developer-log.md', docs: DEV_REQUIRED_DOCS, label: 'Developer' },
60
- designer: { log: 'logs/designer-log.md', docs: DESIGNER_REQUIRED_DOCS, label: 'Designer' },
93
+ 'technical-artist': { log: 'logs/ta-log.md', label: 'Technical Artist' },
94
+ developer: { log: 'logs/developer-log.md', label: 'Developer' },
95
+ artist: { log: 'logs/artist-log.md', label: 'Artist' },
96
+ reviewer: { log: 'logs/review-report.md', label: 'Reviewer' },
97
+ 'game-designer': { log: null, label: 'Game Designer' },
98
+ 'skill-selector': { log: null, label: 'Temporary Atom Selector' },
99
+ };
100
+
101
+ const DESIGNER_REQUIRED_DOCS_BY_RING = {
102
+ // Ring 1 = progressive disclosure: a single concise docs/DESIGN.md is the only
103
+ // required deliverable. design-brief.md is optional scratch (validated only if present).
104
+ 1: ['docs/DESIGN.md'],
105
+ 2: [
106
+ 'docs/design-brief.md',
107
+ 'docs/DESIGN.md',
108
+ 'docs/layout-spec.md',
109
+ 'docs/atom-tree.json',
110
+ ],
111
+ 3: [
112
+ 'docs/design-brief.md',
113
+ 'docs/DESIGN.md',
114
+ 'docs/layout-spec.md',
115
+ 'docs/atom-tree.json',
116
+ 'docs/style-exploration.md',
117
+ ],
118
+ 4: [
119
+ 'docs/design-brief.md',
120
+ 'docs/DESIGN.md',
121
+ 'docs/layout-spec.md',
122
+ 'docs/atom-tree.json',
123
+ 'docs/style-exploration.md',
124
+ ],
61
125
  };
62
126
 
127
+ const MIN_DOC_LEN = 120;
128
+
129
+ // `[*_\s]*` tolerates markdown emphasis in the §6 template form `- **Fusion type**:`.
130
+ const FUSION_TYPE_RE =
131
+ /Fusion\s+type[*_\s]*:\s*(Single|A-Visual|B1-Passive|B2-Active|C-Sequential|D-Parallel)/i;
132
+ const HIGH_RISK_FUSION_TYPES = new Set(['C-Sequential', 'D-Parallel']);
133
+
134
+ /**
135
+ * True only when a real pseudo-fusion / risk-accepted decision is documented.
136
+ *
137
+ * Judged by the *value* of the "Pseudo-fusion decision:" field — not the field
138
+ * label, which always contains "pseudo-fusion" in the §6 template (and the
139
+ * placeholder hint contains "risk accepted: …"). A naive substring match would
140
+ * make this high-risk gate a no-op for every template-derived DESIGN.md.
141
+ * @param {string} content
142
+ * @returns {boolean}
143
+ */
144
+ function hasFusionDowngradeDecision(content) {
145
+ // Drop unfilled {…} template placeholders so their hint text cannot satisfy the gate.
146
+ const filled = content.replace(/\{[^}]*\}/g, '');
147
+ // `[*_\s]*` tolerates markdown emphasis in `- **Pseudo-fusion decision**:`.
148
+ const field = filled.match(/Pseudo-?fusion decision[*_\s]*:\s*(.*)/i);
149
+ if (field) {
150
+ const value = field[1].trim();
151
+ const lower = value.toLowerCase();
152
+ const isEmpty = lower === '' || lower === 'n/a' || lower === 'na' || lower === 'none';
153
+ if (!isEmpty) return true;
154
+ // Field present but n/a → no decision documented; fall through to scan the rest.
155
+ }
156
+ // No structured field (or n/a): scan with the bare label stripped so it cannot self-satisfy.
157
+ const scrubbed = filled.replace(/Pseudo-?fusion decision[*_\s]*:/gi, '');
158
+ return /pseudo-?fusion/i.test(scrubbed) || /risk\s+accepted/i.test(scrubbed);
159
+ }
160
+
161
+ /**
162
+ * Ring 1 DESIGN.md must declare fusion classification (Single is valid default).
163
+ * @param {string} content
164
+ * @returns {string[]}
165
+ */
166
+ function validateDesignFusionSection(content) {
167
+ const errors = [];
168
+ const match = content.match(FUSION_TYPE_RE);
169
+ if (!match) {
170
+ errors.push(
171
+ 'DESIGN.md: missing "Fusion type:" — add ## 6. Gameplay Fusion with Fusion type: Single (or A/B/C/D type for fusion intent)',
172
+ );
173
+ return errors;
174
+ }
175
+
176
+ const fusionType = match[1];
177
+ const normalized =
178
+ fusionType.startsWith('B1') ? 'B1-Passive' : fusionType.startsWith('B2') ? 'B2-Active' : fusionType;
179
+ if (HIGH_RISK_FUSION_TYPES.has(normalized) && !hasFusionDowngradeDecision(content)) {
180
+ errors.push(
181
+ `DESIGN.md: Fusion type ${normalized} is high risk — document pseudo-fusion downgrade or explicit "risk accepted" reason in ## 6. Gameplay Fusion`,
182
+ );
183
+ }
184
+
185
+ return errors;
186
+ }
187
+
188
+ function hasAnyPattern(content, patterns) {
189
+ return patterns.some((pattern) => pattern.test(content));
190
+ }
191
+
192
+ function validatePlayableSeedDesign(content) {
193
+ const errors = [];
194
+ const checks = [
195
+ {
196
+ label: 'player action',
197
+ patterns: [/Player Action/i, /玩家.*(动作|操作)/, /Player does/i],
198
+ },
199
+ {
200
+ label: 'immediate feedback',
201
+ patterns: [/Immediate Feedback/i, /即时反馈/, /First feedback/i, /100ms/i],
202
+ },
203
+ {
204
+ label: 'progress signal',
205
+ patterns: [/Progress Signal/i, /进展信号/, /Progression signal/i, /3-5/],
206
+ },
207
+ {
208
+ label: 'first win',
209
+ patterns: [/First[- ]Level Win/i, /First Win/i, /首胜|首关.*赢|首关.*胜/],
210
+ },
211
+ {
212
+ label: 'semantic placeholders',
213
+ patterns: [/Semantic Placeholders/i, /语义占位/],
214
+ },
215
+ {
216
+ label: 'engine hint',
217
+ patterns: [/Engine Hint/i, /Engine/i, /引擎/],
218
+ },
219
+ ];
220
+ for (const check of checks) {
221
+ if (!hasAnyPattern(content, check.patterns)) {
222
+ errors.push(`DESIGN.md playable seed: missing ${check.label}`);
223
+ }
224
+ }
225
+ if (/^##\s+(ENDCARD|CTA|Endcard|Call[- ]to[- ]Action|Hook\s*\+\s*CTA)/im.test(content)) {
226
+ errors.push(
227
+ 'Ring 1 DESIGN.md must not include ENDCARD/CTA design sections — defer hook/CTA production to Ring 3+ (spine closeout slice only when orchestrator marks it)',
228
+ );
229
+ }
230
+ return errors;
231
+ }
232
+
233
+ function validateSkillUseHeuristic(slice) {
234
+ const errors = [];
235
+ const skillUse = String(slice.skillUse || '').trim();
236
+ const fromScratch = String(slice.fromScratchReason || '').trim();
237
+ if (!skillUse) return errors;
238
+ const claimsDefines = /\bdefines\b|定义了|实现了本 slice|defines the core|本 skill.*核心/i.test(skillUse);
239
+ if (fromScratch && claimsDefines) {
240
+ errors.push(
241
+ `slice "${slice.sliceId}": skillUse must not claim a skill defines the core mechanic when fromScratchReason is set`,
242
+ );
243
+ } else if (claimsDefines) {
244
+ errors.push(
245
+ `slice "${slice.sliceId}": skillUse must describe borrowed layout/lifecycle/input patterns only — do not claim a skill "defines" the core mechanic without playcraft skills read proof`,
246
+ );
247
+ }
248
+ if (fromScratch && /\b(implement|实现|covers?|覆盖)\b.*\b(core|核心|mechanic|机制)\b/i.test(skillUse)) {
249
+ errors.push(
250
+ `slice "${slice.sliceId}": skillUse must not claim skill coverage of the core mechanic when fromScratchReason is present`,
251
+ );
252
+ }
253
+ return errors;
254
+ }
255
+
256
+ function normalizeContextRole(role) {
257
+ const value = normalizeRole(role);
258
+ if (value === 'ref-flow' || value === 'temporary-ref-flow') return 'skill-selector';
259
+ return value;
260
+ }
261
+
262
+ function validateHandoffContext(projectDir, role) {
263
+ const errors = [];
264
+ const state = readState(projectDir);
265
+ const handoff = readHandoff(projectDir);
266
+ const shouldRequire = String(handoff.subagent_stop || '').toLowerCase() === 'true';
267
+ if (!shouldRequire) return errors;
268
+
269
+ const context = state.handoffContext || {};
270
+ const last = context.lastRole || {};
271
+ const next = context.nextRole || {};
272
+ const normalizedRole = normalizeContextRole(role);
273
+ const contextRole = normalizeContextRole(last.role);
274
+ if (!contextRole || contextRole === 'none') {
275
+ errors.push('handoffContext.lastRole.role is required on STOP');
276
+ } else if (contextRole !== normalizedRole) {
277
+ errors.push(`handoffContext.lastRole.role must match STOP role (${role}), found ${last.role}`);
278
+ }
279
+ if (!last.summary || String(last.summary).length < 20 || /No sub-agent has completed/i.test(last.summary)) {
280
+ errors.push('handoffContext.lastRole.summary must capture the completed work in one concrete sentence');
281
+ }
282
+ if (!Array.isArray(last.artifactRefs) || last.artifactRefs.length === 0) {
283
+ errors.push('handoffContext.lastRole.artifactRefs must list produced contract/evidence paths');
284
+ }
285
+ if (!next.role || String(next.role).toLowerCase() === 'none') {
286
+ errors.push('handoffContext.nextRole.role is required for orchestrator routing');
287
+ }
288
+ if (!Array.isArray(next.mustKnow) || next.mustKnow.length === 0) {
289
+ errors.push('handoffContext.nextRole.mustKnow must include the next role working-memory facts');
290
+ }
291
+ return errors;
292
+ }
293
+
294
+ function validateSkillsMatchSliceRefs(projectDir, ss) {
295
+ const errors = [];
296
+ const atomPath = path.join(projectDir, 'docs/atom-tree.json');
297
+ if (!fs.existsSync(atomPath)) {
298
+ errors.push('slice-axis slice seed requires docs/atom-tree.json with meta.skillsMatch snapshot');
299
+ return errors;
300
+ }
301
+ let atom;
302
+ try {
303
+ atom = JSON.parse(fs.readFileSync(atomPath, 'utf8'));
304
+ } catch {
305
+ errors.push('docs/atom-tree.json must be valid JSON');
306
+ return errors;
307
+ }
308
+ const meta = atom?.meta || {};
309
+ const items = Array.isArray(meta.skillsMatch?.items) ? meta.skillsMatch.items : [];
310
+ if (items.length === 0) {
311
+ errors.push(
312
+ 'atom-tree.meta.skillsMatch.items must be non-empty (run playcraft skills match before slice seed)',
313
+ );
314
+ }
315
+ const selectedAtoms = Array.isArray(meta.selectedAtoms)
316
+ ? meta.selectedAtoms.map((a) => String(a).trim()).filter(Boolean)
317
+ : [];
318
+ const noMatchReason = String(meta.compactSelectionNoMatchReason || '').trim();
319
+ if (selectedAtoms.length === 0 && !noMatchReason) {
320
+ errors.push(
321
+ 'atom-tree.meta.selectedAtoms must be non-empty or meta.compactSelectionNoMatchReason must explain why no compact atoms were selected',
322
+ );
323
+ }
324
+ const devSlices = ss.slices.filter((s) => s.role === 'developer');
325
+ for (const slice of devSlices) {
326
+ const skillRefs = Array.isArray(slice.skillRefs)
327
+ ? slice.skillRefs.map((r) => String(r).trim()).filter(Boolean)
328
+ : [];
329
+ const skillUse = String(slice.skillUse || '').trim();
330
+ const fromScratchReason = String(slice.fromScratchReason || '').trim();
331
+ if (skillRefs.length > 0) {
332
+ if (!skillUse) {
333
+ errors.push(`slice "${slice.sliceId}": skillRefs requires non-empty skillUse`);
334
+ }
335
+ } else if (!fromScratchReason) {
336
+ errors.push(
337
+ `slice "${slice.sliceId}": must include skillRefs + skillUse (skill-informed) or fromScratchReason (no matched skill)`,
338
+ );
339
+ }
340
+ errors.push(...validateSkillUseHeuristic(slice));
341
+ }
342
+ const activated = meta.activatedSlices;
343
+ if (!Array.isArray(activated) || activated.length === 0) {
344
+ errors.push('atom-tree.meta.activatedSlices must list sliceIds currently driving dispatch');
345
+ } else {
346
+ for (const id of activated) {
347
+ if (!ss.slices.some((s) => s.sliceId === id)) {
348
+ errors.push(`atom-tree.meta.activatedSlices "${id}" is not listed in sliceState.slices`);
349
+ }
350
+ }
351
+ }
352
+ return errors;
353
+ }
354
+
355
+ function validateGameDesignerSliceQueueSeed(projectDir) {
356
+ const errors = [];
357
+ const ss = normalizeSliceState(readState(projectDir).sliceState || {});
358
+ if (!ss.slices.length) return errors;
359
+
360
+ const ring = Math.min(Math.max(readIteration(projectDir) || 1, 1), 4);
361
+ const devSlices = ss.slices.filter((s) => s.role === 'developer');
362
+ if (devSlices.length === 0) {
363
+ errors.push('sliceState.slices must include at least one developer slice after Game Designer seed');
364
+ }
365
+ if (!ss.activeSliceId || ss.activeSliceId === 'none') {
366
+ errors.push('sliceState.activeSliceId must point at the first dispatchable slice after Game Designer seed');
367
+ } else {
368
+ const active = ss.slices.find((s) => s.sliceId === ss.activeSliceId);
369
+ if (!active) {
370
+ errors.push(`sliceState.activeSliceId "${ss.activeSliceId}" does not match any slice`);
371
+ } else if (active.role !== 'developer' && ring === 1) {
372
+ errors.push(`Ring 1 first active slice should be developer-owned (got "${active.role}")`);
373
+ }
374
+ }
375
+ for (const slice of devSlices) {
376
+ if (!String(slice.doWhat || '').trim()) {
377
+ errors.push(`slice "${slice.sliceId}": missing doWhat`);
378
+ }
379
+ if (!String(slice.verifier || '').trim()) {
380
+ errors.push(`slice "${slice.sliceId}": missing verifier`);
381
+ }
382
+ if (!Array.isArray(slice.acceptance) || slice.acceptance.length === 0) {
383
+ errors.push(`slice "${slice.sliceId}": missing acceptance criteria`);
384
+ }
385
+ }
386
+
387
+ errors.push(...validateSkillsMatchSliceRefs(projectDir, ss));
388
+
389
+ if (ring === 1) {
390
+ for (const rel of ['docs/layout-spec.md', 'docs/style-exploration.md']) {
391
+ if (fs.existsSync(path.join(projectDir, rel))) {
392
+ errors.push(`Ring 1 Game Designer must not create ${rel} yet`);
393
+ }
394
+ }
395
+ const briefPath = path.join(projectDir, 'docs/design-brief.md');
396
+ if (!fs.existsSync(briefPath)) {
397
+ errors.push('slice-axis Ring 1 requires docs/design-brief.md (progressive discovery log)');
398
+ }
399
+ }
400
+ if (ring === 2) {
401
+ if (devSlices.length < 2) {
402
+ errors.push('Ring 2 slice deepening requires at least 2 developer periphery slices');
403
+ }
404
+ if (devSlices.length > 4) {
405
+ errors.push('Ring 2 slice deepening allows at most 4 developer slices');
406
+ }
407
+ const briefPath = path.join(projectDir, 'docs/design-brief.md');
408
+ if (!fs.existsSync(briefPath)) {
409
+ errors.push('slice-axis Ring 2 requires docs/design-brief.md (periphery discovery log)');
410
+ }
411
+ if (fs.existsSync(path.join(projectDir, 'docs/style-exploration.md'))) {
412
+ errors.push('Ring 2 Game Designer clarify must not create docs/style-exploration.md yet (no Artist production)');
413
+ }
414
+ }
415
+ return errors;
416
+ }
417
+
418
+ function validateGameDesignerSeedContext(projectDir) {
419
+ const errors = [];
420
+ const state = readState(projectDir);
421
+ const ss = normalizeSliceState(state.sliceState || {});
422
+ if (ss.slices.length > 0) {
423
+ return validateGameDesignerSliceQueueSeed(projectDir);
424
+ }
425
+
426
+ const context = state.handoffContext || {};
427
+ const shared = context.sharedDecisions || {};
428
+ const next = context.nextRole || {};
429
+ if (!shared.coreAction || shared.coreAction === 'unset') {
430
+ errors.push('handoffContext.sharedDecisions.coreAction must describe the Ring 1 player action');
431
+ }
432
+ if (!Array.isArray(shared.semanticPlaceholders) || shared.semanticPlaceholders.length === 0) {
433
+ errors.push('handoffContext.sharedDecisions.semanticPlaceholders must list Ring 1 semantic placeholders');
434
+ }
435
+ const nextRole = normalizeContextRole(next.role);
436
+ if (nextRole !== 'skill-selector') {
437
+ errors.push('handoffContext.nextRole.role should route to temporary ref-flow atom-selection subagent');
438
+ }
439
+ return errors;
440
+ }
441
+
442
+ function requiredDocsForRole(role, iteration) {
443
+ const ring = Math.min(Math.max(iteration || 1, 1), 4);
444
+ if (role === 'developer') {
445
+ return DEV_REQUIRED_DOCS_BY_RING[ring] || DEV_REQUIRED_DOCS_BY_RING[1];
446
+ }
447
+ if (role === 'technical-artist') {
448
+ return TA_REQUIRED_DOCS_BY_RING[ring] || [];
449
+ }
450
+ return ARTIST_REQUIRED_DOCS;
451
+ }
452
+
63
453
  /** @param {string} raw */
64
454
  function parseHookInput(raw) {
65
455
  if (!raw.trim()) return {};
@@ -80,7 +470,7 @@ function normalizeRole(s) {
80
470
 
81
471
  /**
82
472
  * @param {Record<string, unknown>} input
83
- * @returns {'technical-artist' | 'developer' | 'designer' | null}
473
+ * @returns {'technical-artist' | 'developer' | 'artist' | 'reviewer' | 'game-designer' | 'skill-selector' | null}
84
474
  */
85
475
  function detectRole(input) {
86
476
  const candidates = [
@@ -95,8 +485,11 @@ function detectRole(input) {
95
485
  for (const c of candidates) {
96
486
  const n = normalizeRole(String(c));
97
487
  if (n.includes('technical-artist') || n === 'ta') return 'technical-artist';
98
- if (n === 'developer' || n === 'dev') return 'developer';
99
- if (n === 'designer') return 'designer';
488
+ if (n === 'developer-lead' || n === 'developer' || n === 'dev') return 'developer';
489
+ if (n === 'artist') return 'artist';
490
+ if (n === 'reviewer' || n === 'review') return 'reviewer';
491
+ if (n === 'game-designer' || n === 'game designer' || n === 'designer') return 'game-designer';
492
+ if (n === 'skill-selector' || n === 'skill selector' || n === 'ref-flow') return 'skill-selector';
100
493
  }
101
494
 
102
495
  const blobs = [
@@ -117,6 +510,422 @@ function detectRole(input) {
117
510
  return null;
118
511
  }
119
512
 
513
+ /**
514
+ * @param {string} projectDir
515
+ * @returns {Record<string, string>}
516
+ */
517
+ function readHandoff(projectDir) {
518
+ const statePath = path.join(projectDir, 'docs/project-state.md');
519
+ if (!fs.existsSync(statePath)) return {};
520
+ const content = fs.readFileSync(statePath, 'utf8');
521
+ const fence = content.match(/```yaml\n([\s\S]*?)```/);
522
+ if (!fence) return {};
523
+ /** @type {Record<string, string>} */
524
+ const handoff = {};
525
+ for (const line of fence[1].split('\n')) {
526
+ const m = line.match(/^(\w+):\s*(.+)$/);
527
+ if (m) handoff[m[1]] = m[2].trim();
528
+ }
529
+ return handoff;
530
+ }
531
+
532
+ /**
533
+ * @param {string} content
534
+ */
535
+ function parseRingGateCanAskUser(content) {
536
+ const m = content.match(/\|\s*ringGateCanAskUser\s*\|\s*(\S+)/i);
537
+ return m ? m[1].toLowerCase() : null;
538
+ }
539
+
540
+ /**
541
+ * @param {string} projectDir
542
+ * @param {string} relPath
543
+ */
544
+ function validateDocFile(projectDir, relPath) {
545
+ const errors = [];
546
+ const abs = path.join(projectDir, relPath);
547
+ if (!fs.existsSync(abs)) {
548
+ errors.push(`Missing required doc: ${relPath}`);
549
+ return errors;
550
+ }
551
+ const content = fs.readFileSync(abs, 'utf8');
552
+ if (content.length < MIN_DOC_LEN) {
553
+ errors.push(`${relPath}: too short — fill with real contract content (≥${MIN_DOC_LEN} chars)`);
554
+ }
555
+ const placeholders = content.match(/\{\{[^}]+\}\}/g) || [];
556
+ if (placeholders.length >= 3 || (PLACEHOLDER_RE.test(content) && placeholders.length > 0)) {
557
+ errors.push(`${relPath}: still contains {{placeholders}} — replace with real data`);
558
+ }
559
+ return errors;
560
+ }
561
+
562
+ /**
563
+ * @param {string} content
564
+ * @returns {Record<string, string>}
565
+ */
566
+ function parseReviewReportDoD(content) {
567
+ const start = content.indexOf('## Ring DoD Fields');
568
+ if (start === -1) return {};
569
+ const after = content.slice(start);
570
+ const nextHeading = after.search(/\n##\s+/);
571
+ const section = nextHeading === -1 ? after : after.slice(0, nextHeading);
572
+ /** @type {Record<string, string>} */
573
+ const fields = {};
574
+ for (const line of section.split('\n')) {
575
+ const trimmed = line.trim();
576
+ if (!trimmed.startsWith('|')) continue;
577
+ if (/^\|\s*---/.test(trimmed)) continue;
578
+ if (/^\|\s*Field/i.test(trimmed)) continue;
579
+ const parts = trimmed
580
+ .split('|')
581
+ .map((p) => p.trim())
582
+ .filter((_, i, arr) => i > 0 && i < arr.length - 1);
583
+ if (parts.length < 2) continue;
584
+ const key = parts[0];
585
+ if (/^(field|owner|verifier)$/i.test(key)) continue;
586
+ const value = parts.length >= 3 ? parts[parts.length - 1] : parts[1];
587
+ fields[key] = value;
588
+ }
589
+ return fields;
590
+ }
591
+
592
+ /**
593
+ * @param {string} projectDir
594
+ */
595
+ function validateReviewerHandoff(projectDir) {
596
+ const errors = [];
597
+ const handoff = readHandoff(projectDir);
598
+ const waitingFor = (handoff.waiting_for || '').toLowerCase();
599
+ const nextAction = handoff.next_orchestrator_action || '';
600
+
601
+ if (waitingFor === 'user_gate') {
602
+ errors.push(
603
+ 'Reviewer must not set waiting_for=user_gate — use waiting_for=orchestrator and next_orchestrator_action with npm run ring:gate',
604
+ );
605
+ }
606
+ if (/AskUserQuestion/i.test(nextAction) && !/ring:gate/i.test(nextAction)) {
607
+ errors.push(
608
+ 'Reviewer next_orchestrator_action must not route AskUserQuestion — orchestrator runs ring:gate first',
609
+ );
610
+ }
611
+ if (/ring:gate/i.test(nextAction) && waitingFor !== 'orchestrator') {
612
+ errors.push('When routing ring:gate, waiting_for must be orchestrator');
613
+ }
614
+ if (handoff.subagent_stop !== 'true') {
615
+ errors.push('Reviewer STOP requires subagent_stop: true');
616
+ }
617
+ if (!nextAction || nextAction === 'null') {
618
+ errors.push('Reviewer STOP requires next_orchestrator_action');
619
+ }
620
+ return errors;
621
+ }
622
+
623
+ /**
624
+ * @param {string} projectDir
625
+ * @param {number} iteration
626
+ */
627
+ function validateReviewReportDoD(projectDir, iteration) {
628
+ const errors = [];
629
+ const reportPath = path.join(projectDir, 'logs/review-report.md');
630
+ if (!fs.existsSync(reportPath)) {
631
+ return ['Reviewer: create logs/review-report.md from templates/review-report.template.md before STOP'];
632
+ }
633
+ const content = fs.readFileSync(reportPath, 'utf8');
634
+ if (!content.includes('## Ring DoD Fields')) {
635
+ errors.push('review-report.md: missing "## Ring DoD Fields" section');
636
+ return errors;
637
+ }
638
+ const fields = parseReviewReportDoD(content);
639
+ const required = reviewDoDKeysForRing(iteration);
640
+ if (required.length === 0) {
641
+ return errors;
642
+ }
643
+ for (const key of required) {
644
+ const val = (fields[key] || '').toLowerCase();
645
+ if (!val || val.includes('{{') || val === 'pending' || val === 'n-a' || val === 'n/a') {
646
+ errors.push(
647
+ `review-report.md Reviewer evidence: ${key} must be pass or fail before STOP (script fields stay pending for ring:gate)`,
648
+ );
649
+ }
650
+ }
651
+ if (!content.includes('## Verdict')) {
652
+ errors.push('review-report.md: missing "## Verdict" section');
653
+ }
654
+ return errors;
655
+ }
656
+
657
+ const UI_DIFF_RUBRIC_IDS = [
658
+ 'bg_clean',
659
+ 'no_double_board',
660
+ 'no_hud_ghost',
661
+ 'no_fake_ui',
662
+ 'tile_silhouette',
663
+ 'text_no_overlap',
664
+ 'mc_layout',
665
+ ];
666
+
667
+ const EVIDENCE_SCREENSHOT_RE = /\.(png|jpe?g|webp)\b/i;
668
+
669
+ /**
670
+ * Parse the Structured Visual Rubric table into `id → { result, evidence }`.
671
+ * @param {string} section
672
+ * @returns {Map<string, { result: string, evidence: string }>}
673
+ */
674
+ function parseRubricRows(section) {
675
+ const rows = new Map();
676
+ for (const line of section.split('\n')) {
677
+ const trimmed = line.trim();
678
+ if (!trimmed.startsWith('|')) continue;
679
+ if (/^\|[\s|:-]+\|?$/.test(trimmed)) continue;
680
+ const parts = trimmed
681
+ .split('|')
682
+ .map((p) => p.trim())
683
+ .filter((_, i, arr) => i > 0 && i < arr.length - 1);
684
+ if (parts.length < 3) continue;
685
+ const id = parts[0].replace(/`/g, '').trim().toLowerCase();
686
+ if (!id || id === 'check id' || id === 'check') continue;
687
+ rows.set(id, { result: parts[1], evidence: parts.slice(2).join(' | ').trim() });
688
+ }
689
+ return rows;
690
+ }
691
+
692
+ /**
693
+ * Ring 4 ui_diff must include a structured visual rubric where each check row is
694
+ * actually filled — an explicit PASS/FAIL verdict and evidence that cites a
695
+ * screenshot path. Earlier this only checked that each row *id* appeared anywhere
696
+ * in the file, so a report full of `{{PASS/FAIL}}` / `{{...}}` placeholders passed
697
+ * (the test11 "forced look-at-the-screenshot" gate degraded into a format check).
698
+ * @param {string} content
699
+ */
700
+ function validateUiDiffVisualRubric(content) {
701
+ const errors = [];
702
+ if (!/ui_diff|Structured Visual Rubric/i.test(content)) {
703
+ return errors;
704
+ }
705
+ if (!/###\s+Structured Visual Rubric/i.test(content)) {
706
+ errors.push(
707
+ 'review-report.md ui_diff: missing "### Structured Visual Rubric" — Read each screenshot and fill PASS/FAIL + evidence',
708
+ );
709
+ return errors;
710
+ }
711
+
712
+ const section = extractMarkdownSection(content, '### Structured Visual Rubric') || '';
713
+ const rows = parseRubricRows(section);
714
+
715
+ for (const id of UI_DIFF_RUBRIC_IDS) {
716
+ const row = rows.get(id);
717
+ if (!row) {
718
+ errors.push(`review-report.md Structured Visual Rubric: missing check row "${id}"`);
719
+ continue;
720
+ }
721
+ const result = row.result || '';
722
+ if (PLACEHOLDER_RE.test(result) || !/\b(PASS|FAIL)\b/i.test(result)) {
723
+ errors.push(
724
+ `review-report.md Structured Visual Rubric: "${id}" Result must be an explicit PASS or FAIL (found "${result || 'empty'}")`,
725
+ );
726
+ }
727
+ const evidence = row.evidence || '';
728
+ if (!evidence || PLACEHOLDER_RE.test(evidence)) {
729
+ errors.push(
730
+ `review-report.md Structured Visual Rubric: "${id}" Evidence is empty or a {{placeholder}} — describe what you saw in the screenshot`,
731
+ );
732
+ } else if (!EVIDENCE_SCREENSHOT_RE.test(evidence) && !/logs\//i.test(evidence)) {
733
+ errors.push(
734
+ `review-report.md Structured Visual Rubric: "${id}" Evidence must cite a screenshot path (e.g. logs/ui-diff-game.png — what you see)`,
735
+ );
736
+ }
737
+ }
738
+ return errors;
739
+ }
740
+
741
+ /**
742
+ * uiReviewStatus in project-state must align with uiDiff in review-report (B4 atomic writeback guard).
743
+ * @param {string} projectDir
744
+ * @param {string} reportContent
745
+ */
746
+ function validateUiReviewStatusConsistency(projectDir, reportContent) {
747
+ const errors = [];
748
+ const fields = parseReviewReportDoD(reportContent);
749
+ const uiDiff = (fields.uiDiff || '').toLowerCase();
750
+ if (!uiDiff || uiDiff.includes('{{') || uiDiff === 'pending' || uiDiff === 'n-a') {
751
+ return errors;
752
+ }
753
+ const uiReviewStatusRaw = readState(projectDir).devPreview?.uiReviewStatus;
754
+ if (!uiReviewStatusRaw) return errors;
755
+ const uiReviewStatus = String(uiReviewStatusRaw).toLowerCase();
756
+ const passSet = new Set(['pass', 'passed']);
757
+ const failSet = new Set(['fail', 'failed']);
758
+ const uiDiffPass = passSet.has(uiDiff);
759
+ const uiDiffFail = failSet.has(uiDiff);
760
+ const statusPass = passSet.has(uiReviewStatus);
761
+ const statusFail = failSet.has(uiReviewStatus);
762
+ if (uiDiffPass && statusFail) {
763
+ errors.push(
764
+ 'project-state uiReviewStatus=failed but review-report uiDiff=pass — align checkpoint with visual rubric verdict',
765
+ );
766
+ }
767
+ if (uiDiffFail && statusPass) {
768
+ errors.push(
769
+ 'project-state uiReviewStatus=passed but review-report uiDiff=fail — align checkpoint with visual rubric verdict',
770
+ );
771
+ }
772
+ return errors;
773
+ }
774
+
775
+ /**
776
+ * @param {string} projectDir
777
+ */
778
+ function validateReviewerStop(projectDir) {
779
+ const iteration = readIteration(projectDir) || 1;
780
+ const loop = readLoop(projectDir);
781
+ const handoff = readHandoff(projectDir);
782
+ const nextAction = handoff.next_orchestrator_action || '';
783
+ const errors = [...validateReviewerHandoff(projectDir), ...validateHandoffContext(projectDir, 'reviewer')];
784
+
785
+ if (loop === 'verify' && /ring:gate/i.test(nextAction)) {
786
+ errors.push(...validateReviewReportDoD(projectDir, iteration));
787
+ const reportPath = path.join(projectDir, 'logs/review-report.md');
788
+ if (fs.existsSync(reportPath)) {
789
+ const reportContent = fs.readFileSync(reportPath, 'utf8');
790
+ if (iteration >= 4) {
791
+ errors.push(...validateUiDiffVisualRubric(reportContent));
792
+ errors.push(...validateUiReviewStatusConsistency(projectDir, reportContent));
793
+ }
794
+ }
795
+ const loadCheck = readState(projectDir).devPreview?.loadCheck;
796
+ if (loadCheck && String(loadCheck).toLowerCase() === 'pending' && iteration < 4) {
797
+ errors.push('Dev Preview loadCheck still pending — complete load_check before routing ring:gate');
798
+ }
799
+ } else if (!fs.existsSync(path.join(projectDir, 'logs/review-report.md'))) {
800
+ errors.push('Reviewer: create logs/review-report.md before STOP');
801
+ }
802
+
803
+ return errors;
804
+ }
805
+
806
+ /**
807
+ * @param {string} projectDir
808
+ */
809
+ function validateGameDesignerStop(projectDir) {
810
+ const iteration = readIteration(projectDir) || 1;
811
+ const loop = readLoop(projectDir);
812
+ const errors = [];
813
+ const handoff = readHandoff(projectDir);
814
+
815
+ if (handoff.subagent_stop !== 'true') {
816
+ errors.push('Game Designer STOP requires subagent_stop: true');
817
+ }
818
+ if ((handoff.waiting_for || '').toLowerCase() !== 'orchestrator') {
819
+ errors.push('Game Designer STOP requires waiting_for: orchestrator');
820
+ }
821
+ if (!handoff.next_orchestrator_action || handoff.next_orchestrator_action === 'null') {
822
+ errors.push('Game Designer STOP requires next_orchestrator_action (e.g. Spawn temporary ref-flow atom-selection subagent for ring 1 atom-tree selection)');
823
+ }
824
+
825
+ if (loop !== 'clarify') {
826
+ return errors;
827
+ }
828
+
829
+ const ring = Math.min(Math.max(iteration, 1), 4);
830
+ const state = readState(projectDir);
831
+ const ss = normalizeSliceState(state.sliceState || {});
832
+ const sliceAxisClarify = ss.slices.length > 0 && (ring === 1 || ring === 2);
833
+
834
+ const requiredDocs = sliceAxisClarify
835
+ ? ['docs/DESIGN.md', 'docs/design-brief.md']
836
+ : DESIGNER_REQUIRED_DOCS_BY_RING[ring] || DESIGNER_REQUIRED_DOCS_BY_RING[1];
837
+ for (const doc of requiredDocs) {
838
+ errors.push(...validateDocFile(projectDir, doc));
839
+ }
840
+
841
+ if (ring === 1) {
842
+ const designPath = path.join(projectDir, 'docs/DESIGN.md');
843
+ if (fs.existsSync(designPath)) {
844
+ errors.push(...validatePlayableSeedDesign(fs.readFileSync(designPath, 'utf8')));
845
+ }
846
+ }
847
+
848
+ if (sliceAxisClarify) {
849
+ errors.push(...validateHandoffContext(projectDir, 'game-designer'));
850
+ errors.push(...validateGameDesignerSeedContext(projectDir));
851
+ const refs = state.handoffContext?.lastRole?.artifactRefs || [];
852
+ if (!refs.some((r) => String(r).includes('atom-tree.json'))) {
853
+ errors.push('handoffContext.lastRole.artifactRefs must include docs/atom-tree.json');
854
+ }
855
+ } else if (ring === 1) {
856
+ errors.push(...validateHandoffContext(projectDir, 'game-designer'));
857
+ errors.push(...validateGameDesignerSeedContext(projectDir));
858
+ }
859
+
860
+ const briefPath = path.join(projectDir, 'docs/design-brief.md');
861
+ if (fs.existsSync(briefPath)) {
862
+ const brief = fs.readFileSync(briefPath, 'utf8');
863
+ if (!/##\s+(Brainstorm|Core Gameplay|Discovery|Core Discovery)/i.test(brief)) {
864
+ errors.push('design-brief.md: missing Discovery / Brainstorm / Core Gameplay section with process record');
865
+ }
866
+ }
867
+
868
+ return errors;
869
+ }
870
+
871
+ /**
872
+ * @param {string} projectDir
873
+ */
874
+ function validateSkillSelectorStop(projectDir) {
875
+ const errors = [];
876
+ const handoff = readHandoff(projectDir);
877
+ errors.push(...validateHandoffContext(projectDir, 'skill-selector'));
878
+
879
+ if (handoff.subagent_stop !== 'true') {
880
+ errors.push('Temporary atom-selection STOP requires subagent_stop: true');
881
+ }
882
+ if ((handoff.waiting_for || '').toLowerCase() !== 'orchestrator') {
883
+ errors.push('Temporary atom-selection STOP requires waiting_for: orchestrator');
884
+ }
885
+ const next = handoff.next_orchestrator_action || '';
886
+ if (!next || next === 'null') {
887
+ errors.push('Temporary atom-selection STOP requires next_orchestrator_action');
888
+ } else if (!/init-template|scaffold|developer/i.test(next)) {
889
+ errors.push('Temporary atom-selection STOP should route orchestrator to scaffold gate, then @developer');
890
+ }
891
+
892
+ const atomTreePath = path.join(projectDir, 'docs/atom-tree.json');
893
+ if (!fs.existsSync(atomTreePath)) {
894
+ errors.push('Temporary atom-selection STOP requires docs/atom-tree.json');
895
+ return errors;
896
+ }
897
+
898
+ let tree;
899
+ try {
900
+ tree = JSON.parse(fs.readFileSync(atomTreePath, 'utf8'));
901
+ } catch (e) {
902
+ errors.push(`docs/atom-tree.json parse failed: ${e.message}`);
903
+ return errors;
904
+ }
905
+
906
+ const engineDecision = tree?.meta?.engineDecision;
907
+ if (!engineDecision || typeof engineDecision !== 'object') {
908
+ errors.push('docs/atom-tree.json missing meta.engineDecision');
909
+ } else {
910
+ if (!engineDecision.adapter || engineDecision.adapter === 'unset') {
911
+ errors.push('meta.engineDecision.adapter must be set for init-template');
912
+ }
913
+ if (!engineDecision.engineAtom) {
914
+ errors.push('meta.engineDecision.engineAtom must be set');
915
+ }
916
+ }
917
+
918
+ const selectedAtoms = tree?.meta?.selectedAtoms;
919
+ if (!Array.isArray(selectedAtoms) || selectedAtoms.length === 0) {
920
+ errors.push('meta.selectedAtoms must be a non-empty compact atom list');
921
+ }
922
+ if (!tree?.engine) {
923
+ errors.push('docs/atom-tree.json missing top-level engine atom');
924
+ }
925
+
926
+ return errors;
927
+ }
928
+
120
929
  /** @param {string} cell */
121
930
  function isReadChecked(cell) {
122
931
  const t = cell.trim();
@@ -142,11 +951,12 @@ function parseUpstreamIntake(content) {
142
951
  const section = nextHeading === -1 ? after : after.slice(0, nextHeading);
143
952
 
144
953
  const rows = [];
954
+ const unmatchedRows = [];
145
955
  for (const line of section.split('\n')) {
146
956
  const trimmed = line.trim();
147
957
  if (!trimmed.startsWith('|')) continue;
148
958
  if (/^\|\s*---/.test(trimmed)) continue;
149
- if (/^\|\s*Doc\s*\|/i.test(trimmed)) continue;
959
+ if (/^\|\s*Doc(ument)?\s*\|/i.test(trimmed)) continue;
150
960
 
151
961
  const parts = trimmed
152
962
  .split('|')
@@ -156,13 +966,24 @@ function parseUpstreamIntake(content) {
156
966
  if (parts.length < 3) continue;
157
967
 
158
968
  const doc = parts[0].replace(/^`/, '').replace(/`$/, '').trim();
159
- if (!doc.startsWith('docs/') && !doc.startsWith('logs/')) continue;
969
+ if (!doc.startsWith('docs/') && !doc.startsWith('logs/')) {
970
+ unmatchedRows.push(doc || '(empty)');
971
+ continue;
972
+ }
160
973
 
161
974
  rows.push({ doc, read: parts[1], takeaway: parts[2] });
162
975
  }
163
976
 
164
977
  if (rows.length === 0) {
165
- errors.push('Upstream Intake table has no data rows');
978
+ if (unmatchedRows.length > 0) {
979
+ errors.push(
980
+ `Upstream Intake table has ${unmatchedRows.length} row(s) but none start with "docs/" or "logs/" in the first column. Found: ${unmatchedRows.slice(0, 5).join(', ')}${unmatchedRows.length > 5 ? ` ... and ${unmatchedRows.length - 5} more` : ''}. Expected format: | \`docs/project-state.md\` | ✓ | one-line takeaway |`
981
+ );
982
+ } else {
983
+ errors.push(
984
+ 'Upstream Intake table has no data rows. Expected format:\n| `docs/project-state.md` | ✓ | one-line takeaway |\n| `docs/implementation-plan.md` | ✓ | ... |\n(Repeat for each required doc — see templates/developer-log.template.md for the full list.)'
985
+ );
986
+ }
166
987
  }
167
988
 
168
989
  return { rows, errors };
@@ -197,17 +1018,17 @@ function validateIntakeContent(content, requiredDocs) {
197
1018
  /**
198
1019
  * @param {string} content
199
1020
  * @param {'technical-artist' | 'developer'} role
200
- * @param {string} [stage]
1021
+ * @param {'clarify'|'build'|'verify'|null} [loop]
201
1022
  */
202
- function validatePlan(content, role, stage) {
1023
+ function validatePlan(content, role, loop) {
203
1024
  let heading = PLAN_HEADING[role];
204
1025
  /** @type {string[]} */
205
1026
  let requiredSubs = [];
206
1027
 
207
1028
  if (role === 'developer') {
208
- if (stage && PLAN_HEADING_BY_STAGE.developer[stage]) {
209
- heading = PLAN_HEADING_BY_STAGE.developer[stage];
210
- requiredSubs = PLAN_REQUIRED_SUBS.developer[stage] ?? [];
1029
+ if (loop && PLAN_HEADING_BY_LOOP.developer[loop]) {
1030
+ heading = PLAN_HEADING_BY_LOOP.developer[loop];
1031
+ requiredSubs = PLAN_REQUIRED_SUBS.developer[loop] ?? [];
211
1032
  }
212
1033
  } else if (role === 'technical-artist') {
213
1034
  requiredSubs = PLAN_REQUIRED_SUBS['technical-artist'] ?? [];
@@ -216,7 +1037,7 @@ function validatePlan(content, role, stage) {
216
1037
  const errors = [];
217
1038
  const start = content.indexOf(heading);
218
1039
  if (start === -1) {
219
- return [`Missing "${heading}" section — write plan before STOP (stage: ${stage || 'unknown'})`];
1040
+ return [`Missing "${heading}" section — write plan before STOP (loop: ${loop || 'unknown'})`];
220
1041
  }
221
1042
 
222
1043
  const after = content.slice(start + heading.length);
@@ -242,48 +1063,53 @@ function validatePlan(content, role, stage) {
242
1063
  return errors;
243
1064
  }
244
1065
 
245
- // All convergence-v1 stages
246
- const STAGE_NAMES =
247
- '(pm|style_exploration|production|ui_pass|ui_review|ui_rework|gameplay_pass|done)';
248
- const STAGE_MARKDOWN_BOLD_RE = new RegExp(`\\*\\*${STAGE_NAMES}\\*\\*`, 'i');
249
- const STAGE_BACKTICK_RE = new RegExp(`stage[=:\\s]+\`${STAGE_NAMES}\``, 'i');
250
- // YAML form inside ```yaml ... ``` block: `stage: ui_pass` (no backticks around value)
251
- const STAGE_YAML_RE = new RegExp(`^\\s*stage:\\s*${STAGE_NAMES}\\s*$`, 'im');
1066
+ const LOOP_NAMES = '(clarify|build|verify)';
1067
+ const LOOP_YAML_RE = new RegExp(`^\\s*loop:\\s*${LOOP_NAMES}\\s*$`, 'im');
1068
+ const ITERATION_YAML_RE = /^\s*iteration:\s*(\d+)\s*$/im;
252
1069
 
253
1070
  /**
254
- * Extract current `stage` value from `docs/project-state.md`. Returns null if not found.
255
- * Tries three forms (in order):
256
- * 1. Markdown bold: `**ui_pass**` — used in Current Stage header
257
- * 2. Backtick form: `` stage: `ui_pass` `` — used in prose
258
- * 3. YAML form: `stage: ui_pass` — used in `## Agent handoff` YAML fence (most authoritative)
259
1071
  * @param {string} projectDir
1072
+ * @returns {number|null}
260
1073
  */
261
- function readStage(projectDir) {
1074
+ function readIteration(projectDir) {
262
1075
  const statePath = path.join(projectDir, 'docs/project-state.md');
263
1076
  if (!fs.existsSync(statePath)) return null;
264
1077
  const content = fs.readFileSync(statePath, 'utf8');
265
- const m =
266
- content.match(STAGE_YAML_RE) ||
267
- content.match(STAGE_MARKDOWN_BOLD_RE) ||
268
- content.match(STAGE_BACKTICK_RE);
269
- return m ? m[1].toLowerCase() : null;
1078
+ const m = content.match(ITERATION_YAML_RE);
1079
+ if (!m) return null;
1080
+ const n = Number.parseInt(m[1], 10);
1081
+ return Number.isFinite(n) && n >= 1 && n <= 4 ? n : null;
270
1082
  }
271
1083
 
272
- function readGatePending(projectDir) {
1084
+ /**
1085
+ * @param {string} projectDir
1086
+ * @returns {'clarify'|'build'|'verify'|null}
1087
+ */
1088
+ function readLoop(projectDir) {
273
1089
  const statePath = path.join(projectDir, 'docs/project-state.md');
274
1090
  if (!fs.existsSync(statePath)) return null;
275
1091
  const content = fs.readFileSync(statePath, 'utf8');
276
- const m =
277
- content.match(/gate_pending:\s*["']?(1|2a|2b|3)["']?/i) ||
278
- content.match(/gate_pending:\s*(1|2a|2b|3)/i);
279
- return m ? m[1].toLowerCase() : null;
1092
+ const m = content.match(LOOP_YAML_RE);
1093
+ return m ? /** @type {'clarify'|'build'|'verify'} */ (m[1].toLowerCase()) : null;
1094
+ }
1095
+
1096
+ /** @deprecated use readLoop — maps build→development for legacy test imports only */
1097
+ function readStage(projectDir) {
1098
+ const loop = readLoop(projectDir);
1099
+ if (loop === 'build') return 'development';
1100
+ if (loop === 'verify') return 'ui_review';
1101
+ return 'pm';
1102
+ }
1103
+
1104
+ function readGatePending(projectDir) {
1105
+ return null;
280
1106
  }
281
1107
 
282
1108
  /**
283
- * Validate Designer Skill Preflight section (Phase 2 / production only).
1109
+ * Validate Artist Skill Preflight section (Phase 2 / production only).
284
1110
  * @param {string} content
285
1111
  */
286
- function validateDesignerSkillPreflight(content) {
1112
+ function validateArtistSkillPreflight(content) {
287
1113
  const errors = [];
288
1114
  const start = content.indexOf(SKILL_PREFLIGHT_HEADING);
289
1115
  if (start === -1) {
@@ -327,12 +1153,183 @@ function validateDesignerSkillPreflight(content) {
327
1153
  }
328
1154
 
329
1155
  /**
1156
+ * Static runtime-contract gate for Developer: the recording/debug harness needs
1157
+ * game/ to keep exposing its hooks. Feature-detected — silently skipped if the
1158
+ * harness scanner is absent, and never hard-fails the hook itself on scanner error.
330
1159
  * @param {string} projectDir
331
- * @param {'technical-artist' | 'developer' | 'designer'} role
1160
+ * @returns {string[]}
1161
+ */
1162
+ function validateDeveloperRuntimeContract(projectDir) {
1163
+ const pending = path.join(projectDir, 'game', '.playcraft-scaffold-pending');
1164
+ if (fs.existsSync(pending)) return [];
1165
+
1166
+ const libPath = path.join(
1167
+ projectDir,
1168
+ 'skills/playable-verify-ui/scripts/lib/runtime_contract.js',
1169
+ );
1170
+ if (!fs.existsSync(libPath)) return [];
1171
+ try {
1172
+ const { scanRuntimeContract } = require(libPath);
1173
+ const res = scanRuntimeContract(projectDir);
1174
+ if (!res.ok) {
1175
+ return res.missing.map((m) => `runtime contract broken — ${m}`);
1176
+ }
1177
+ } catch {
1178
+ // Scanner failure must not block an otherwise-valid STOP.
1179
+ return [];
1180
+ }
1181
+ return [];
1182
+ }
1183
+
1184
+ /**
1185
+ * @param {string} projectDir
1186
+ * @param {'technical-artist' | 'developer' | 'artist'} role
1187
+ */
1188
+ function validateNonRegression(projectDir, iteration) {
1189
+ const errors = [];
1190
+ if (!iteration || iteration <= 1) return errors;
1191
+ const archived = readState(projectDir).archivedRings || {};
1192
+ if (Object.keys(archived).length === 0) {
1193
+ errors.push(`Non-regression: archivedRings empty while iteration=${iteration}`);
1194
+ return errors;
1195
+ }
1196
+ for (let ring = 1; ring < iteration; ring += 1) {
1197
+ if (!(ring in archived) && !(String(ring) in archived)) {
1198
+ errors.push(`Non-regression: ring ${ring} not archived`);
1199
+ }
1200
+ }
1201
+ return errors;
1202
+ }
1203
+
1204
+ /**
1205
+ * slice-axis-v1: roles that EXECUTE (implement/verify) a dispatched slice. If one of
1206
+ * these is stopped on a slice it does not own, that is a dispatch mismatch (the
1207
+ * orchestrator spawned the wrong executor) and STOP is blocked. Non-executor roles
1208
+ * (game-designer seeds the queue, reviewer/skill-selector) may legitimately STOP at a
1209
+ * handoff point while another role's slice is pending, so they are not mismatch-gated.
1210
+ * Knife 4 only wires developer; TA/artist slice execution can be added later.
332
1211
  */
1212
+ const SLICE_EXECUTOR_ROLES = new Set(['developer']);
1213
+
1214
+ /** The active slice object, or null when none / drained. */
1215
+ function getActiveSliceFor(projectDir) {
1216
+ if (!stateExists(projectDir)) return null;
1217
+ try {
1218
+ const ss = readState(projectDir).sliceState || {};
1219
+ if (!ss.activeSliceId || ss.activeSliceId === 'none') return null;
1220
+ return (ss.slices || []).find((s) => s.sliceId === ss.activeSliceId) || null;
1221
+ } catch {
1222
+ return null;
1223
+ }
1224
+ }
1225
+
1226
+ function hasUnresolvedActiveSlice(projectDir) {
1227
+ const active = getActiveSliceFor(projectDir);
1228
+ return !!active && !isSliceResolved(active);
1229
+ }
1230
+
1231
+ /**
1232
+ * slice-axis-v1: derive the dispatched role from the active slice when there is no
1233
+ * explicit role marker on the hook input (top-level `claude -p` has no
1234
+ * `subagent_type` / PLAYCRAFT_STOP). The dispatched role IS the owner of the
1235
+ * active, UNRESOLVED slice — this guarantees an unverified (or failed-but-unrouted)
1236
+ * slice cannot slip past STOP even without subagent context. Returns null when the
1237
+ * active slice is resolved / absent, so those states fall back to normal detection.
1238
+ */
1239
+ function roleFromActiveSlice(projectDir) {
1240
+ const active = getActiveSliceFor(projectDir);
1241
+ if (!active || isSliceResolved(active)) return null;
1242
+ return normalizeRole(active.role || 'developer') || 'developer';
1243
+ }
1244
+
1245
+ function validateActiveSliceResolved(projectDir, role) {
1246
+ const active = getActiveSliceFor(projectDir);
1247
+ if (!active) return [];
1248
+ if (active.role && active.role !== role) return [];
1249
+ if (isSliceResolved(active)) return [];
1250
+ if (active.status === 'failed') {
1251
+ return [
1252
+ `slice "${active.sliceId}" is "failed" but has no route target — run \`npm run slice:set -- status=failed route=<role> reason="<failKey>"\` so the orchestrator has a precise recovery target. A failed slice without routeTo.role + routeTo.reason is unresolved.`,
1253
+ ];
1254
+ }
1255
+ const buildHint =
1256
+ active.role === 'developer' || !active.role
1257
+ ? '`npm run build` (writes validate/build.json)'
1258
+ : `\`npm run ${active.verifier || 'verify:build'}\``;
1259
+ return [
1260
+ `slice "${active.sliceId}" is "${active.status}" — before STOP run ${buildHint}, then \`npm run slice:set -- status=verified verification="build.json ok"\` on pass, or \`npm run slice:set -- status=failed route=<role> reason="..."\` to route. The active slice must be verified/failed/routed, not left ${active.status}.`,
1261
+ ];
1262
+ }
1263
+
1264
+ /**
1265
+ * slice-axis-v1: after a slice is resolved the queue may drain (activeSliceId=none).
1266
+ * The stopping role must not fall back to legacy ring STOP (e.g. review-report.md) when
1267
+ * it just wrote a structured verdict via slice:set — lastResolvedForStop bridges drain → STOP.
1268
+ */
1269
+ function validateResolvedSliceStop(projectDir, role) {
1270
+ if (!stateExists(projectDir)) return null;
1271
+ try {
1272
+ const ss = readState(projectDir).sliceState || {};
1273
+ const marker = ss.lastResolvedForStop;
1274
+ if (!marker || marker.role !== role || !marker.sliceId) return null;
1275
+ const slice = (ss.slices || []).find((s) => s.sliceId === marker.sliceId);
1276
+ if (slice && isSliceResolved(slice)) {
1277
+ if (role === 'developer' && slice.status === 'verified') {
1278
+ const buildErrors = validateBuildArtifactForSlice(projectDir, slice);
1279
+ if (buildErrors.length) return buildErrors;
1280
+ }
1281
+ return [];
1282
+ }
1283
+ } catch {
1284
+ return null;
1285
+ }
1286
+ return null;
1287
+ }
1288
+
333
1289
  function validateRole(projectDir, role) {
1290
+ // slice-axis-v1: when the stopping role OWNS an unresolved active slice, the slice
1291
+ // state machine is the ONLY STOP gate — checked FIRST, ahead of every role-specific
1292
+ // branch (reviewer/game-designer/skill-selector included), so a slice owned by ANY
1293
+ // role (e.g. a future game-designer-owned slice) goes through the slice gate, not
1294
+ // the legacy role STOP. Ring-era intake ceremony (developer-log Read✓/takeaway,
1295
+ // plan, runtime-contract dup, ring-1 self-gate) is bypassed — verify:slice already
1296
+ // judges the runtime contract semantically. With no unresolved active slice we fall
1297
+ // through to the legacy ring flow, so existing ring:loop projects and tests are
1298
+ // unaffected.
1299
+ const sliceOwner = roleFromActiveSlice(projectDir);
1300
+ if (sliceOwner) {
1301
+ if (sliceOwner === role) {
1302
+ return validateActiveSliceResolved(projectDir, role);
1303
+ }
1304
+ // dispatch mismatch (hardening): an EXECUTOR role was stopped on a slice it does
1305
+ // not own → the orchestrator spawned the wrong executor. Block with a precise
1306
+ // re-dispatch hint. Non-executor roles fall through (legitimate handoff STOP).
1307
+ if (SLICE_EXECUTOR_ROLES.has(role)) {
1308
+ const active = getActiveSliceFor(projectDir);
1309
+ return [
1310
+ `dispatch mismatch — active slice "${active?.sliceId}" is owned by "${sliceOwner}", but the stopping role is "${role}". Only @${sliceOwner} may resolve this slice; re-dispatch to @${sliceOwner} (or fix sliceState.slices[].role).`,
1311
+ ];
1312
+ }
1313
+ }
1314
+
1315
+ const resolvedSliceStop = validateResolvedSliceStop(projectDir, role);
1316
+ if (resolvedSliceStop !== null) return resolvedSliceStop;
1317
+
1318
+ if (role === 'reviewer') {
1319
+ return validateReviewerStop(projectDir);
1320
+ }
1321
+ if (role === 'game-designer') {
1322
+ return validateGameDesignerStop(projectDir);
1323
+ }
1324
+ if (role === 'skill-selector') {
1325
+ return validateSkillSelectorStop(projectDir);
1326
+ }
1327
+
334
1328
  const cfg = ROLE_LOG[role];
1329
+ const iteration = readIteration(projectDir) || 1;
1330
+ const loop = readLoop(projectDir);
335
1331
  const logPath = path.join(projectDir, cfg.log);
1332
+ const requiredDocs = requiredDocsForRole(role, iteration);
336
1333
 
337
1334
  if (!fs.existsSync(logPath)) {
338
1335
  return [
@@ -341,32 +1338,406 @@ function validateRole(projectDir, role) {
341
1338
  }
342
1339
 
343
1340
  const content = fs.readFileSync(logPath, 'utf8');
1341
+ const regressionErrors = validateNonRegression(projectDir, iteration);
1342
+ const contextErrors = validateHandoffContext(projectDir, role);
344
1343
 
345
- if (role === 'designer') {
346
- const stage = readStage(projectDir);
347
- if (stage === 'production') {
348
- return validateDesignerSkillPreflight(content);
1344
+ if (role === 'artist') {
1345
+ if (iteration >= 3 && loop === 'build') {
1346
+ return [...validateArtistSkillPreflight(content), ...regressionErrors, ...contextErrors];
1347
+ }
1348
+ if (iteration >= 3 && loop === 'verify') {
1349
+ return [...validateArtistGate2b(projectDir), ...regressionErrors, ...contextErrors];
349
1350
  }
350
- if (stage === 'style_exploration') {
351
- const gatePending = readGatePending(projectDir);
352
- if (gatePending === '2b') {
353
- return validateDesignerGate2b(projectDir);
1351
+ return [...regressionErrors, ...contextErrors];
1352
+ }
1353
+
1354
+ const intakeErrors =
1355
+ requiredDocs.length > 0 ? validateIntakeContent(content, requiredDocs) : [];
1356
+ const planErrors = validatePlan(content, role, loop);
1357
+ const contractErrors =
1358
+ role === 'developer' && loop === 'build'
1359
+ ? validateDeveloperRuntimeContract(projectDir)
1360
+ : [];
1361
+
1362
+ if (role === 'developer' && loop === 'build') {
1363
+ const checkpointErrors =
1364
+ iteration >= 2 ? validateDeveloperCheckpoints(projectDir, content) : [];
1365
+ const sliceErrors = validateActiveSliceResolved(projectDir, role);
1366
+ let ring1SelfGateErrors = [];
1367
+ // When a slice is active, slice verification supersedes the legacy ring-1
1368
+ // whole-project self-gate (which is what the experiment showed lets semantic
1369
+ // failures escape). Fall back to the ring-1 self-gate only with no active slice.
1370
+ if (iteration === 1 && sliceErrors.length === 0 && !hasUnresolvedActiveSlice(projectDir)) {
1371
+ const ring1 = validateDeveloperRing1SelfGate(projectDir, content, readHandoff(projectDir));
1372
+ ring1SelfGateErrors = ring1.errors;
1373
+ for (const w of ring1.warnings ?? []) {
1374
+ process.stderr.write(`[playcraft-workflow-stop] advisory: ${w}\n`);
354
1375
  }
355
1376
  }
1377
+ return [...regressionErrors, ...contextErrors, ...intakeErrors, ...planErrors, ...checkpointErrors, ...contractErrors, ...sliceErrors, ...ring1SelfGateErrors];
1378
+ }
1379
+
1380
+ if (role === 'technical-artist' && iteration >= 4) {
1381
+ const taErrors = [];
1382
+ const handoff = readHandoff(projectDir);
1383
+ const next = handoff.next_orchestrator_action || '';
1384
+ if (/reviewer ui_diff/i.test(next) && !/developer/i.test(next)) {
1385
+ taErrors.push(
1386
+ 'TA STOP must route to @developer (Ring 4 production integration) before @reviewer ui_diff',
1387
+ );
1388
+ }
1389
+ return [...regressionErrors, ...contextErrors, ...intakeErrors, ...planErrors, ...taErrors];
1390
+ }
1391
+
1392
+ return [...regressionErrors, ...contextErrors, ...intakeErrors, ...planErrors, ...contractErrors];
1393
+ }
1394
+
1395
+ /** Selected atoms that must be linked into .claude/skills/ (excludes bundled playcraft-* skills). */
1396
+ function readSelectedAtomsForLink(projectDir) {
1397
+ const jsonPath = path.join(projectDir, 'docs/atom-tree.json');
1398
+ if (!fs.existsSync(jsonPath)) return [];
1399
+ try {
1400
+ const tree = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
1401
+ const atoms = Array.isArray(tree?.meta?.selectedAtoms) ? tree.meta.selectedAtoms : [];
1402
+ return atoms
1403
+ .map((a) => (typeof a === 'string' ? a.trim() : ''))
1404
+ .filter((a) => a && !a.startsWith('{{') && !a.startsWith('playcraft-'));
1405
+ } catch {
356
1406
  return [];
357
1407
  }
1408
+ }
1409
+
1410
+ /** Extract a markdown section body from `heading` up to the next same-or-higher-level heading. */
1411
+ function extractMarkdownSection(content, heading) {
1412
+ const start = content.indexOf(heading);
1413
+ if (start === -1) return null;
1414
+ const level = (heading.match(/^#+/) || ['##'])[0].length;
1415
+ const after = content.slice(start + heading.length);
1416
+ const lines = after.split('\n');
1417
+ const body = [];
1418
+ for (let i = 1; i < lines.length; i += 1) {
1419
+ const m = lines[i].match(/^(#+)\s/);
1420
+ if (m && m[1].length <= level) break;
1421
+ body.push(lines[i]);
1422
+ }
1423
+ return body.join('\n');
1424
+ }
1425
+
1426
+ /** Count filled (non-template, non-header, non-separator) markdown table data rows. */
1427
+ function countFilledTableRows(section) {
1428
+ let count = 0;
1429
+ for (const line of section.split('\n')) {
1430
+ const trimmed = line.trim();
1431
+ if (!trimmed.startsWith('|')) continue;
1432
+ if (/^\|[\s|:-]+\|?$/.test(trimmed)) continue;
1433
+ if (/selectedAtomRef|Read path|怎么用|Asset contract path|Tier \/ Source/.test(trimmed)) continue;
1434
+ if (PLACEHOLDER_RE.test(trimmed)) continue;
1435
+ count += 1;
1436
+ }
1437
+ return count;
1438
+ }
1439
+
1440
+ /**
1441
+ * Shared skill-link + Skill Preflight + placeholder materialization evidence gate.
1442
+ * Applied at every ring's Developer build STOP so "use placeholders / implement per skill"
1443
+ * is enforced from ring 1 onward, not just documented.
1444
+ * @param {string} projectDir
1445
+ * @param {string} content developer-log.md content
1446
+ * @returns {string[]}
1447
+ */
1448
+ function validateSkillAndPlaceholderEvidence(projectDir, content) {
1449
+ const errors = [];
1450
+ const atoms = readSelectedAtomsForLink(projectDir);
1451
+
1452
+ // 1. Skill link evidence: selected atoms materialized under .claude/skills/
1453
+ const skillsDir = path.join(projectDir, '.claude/skills');
1454
+ const unlinked = atoms.filter((atom) => !fs.existsSync(path.join(skillsDir, atom)));
1455
+ if (unlinked.length > 0) {
1456
+ errors.push(
1457
+ `Skill link: selected atom skill(s) not linked into .claude/skills/: ${unlinked.join(', ')} — re-save docs/atom-tree.json (auto-links) or run playcraft skills link --from-atom-tree --prune`,
1458
+ );
1459
+ }
1460
+
1461
+ // 2. Skill Preflight filled (one non-template row per atom)
1462
+ const preflight = extractMarkdownSection(content, '## Skill Preflight');
1463
+ if (!preflight || countFilledTableRows(preflight) < Math.max(1, atoms.length)) {
1464
+ errors.push(
1465
+ 'Skill Preflight: developer-log.md § Skill Preflight is not filled — add one non-template row per selected atom (read .claude/skills/<atom>/SKILL.md: what it provides + how you use it)',
1466
+ );
1467
+ }
358
1468
 
359
- const intakeErrors = validateIntakeContent(content, cfg.docs);
360
- const stage = role === 'developer' ? readStage(projectDir) : undefined;
361
- const planErrors = validatePlan(content, role, stage ?? undefined);
362
- return [...intakeErrors, ...planErrors];
1469
+ // 3. Placeholder materialization evidence (placeholder-first, not code-draw-only)
1470
+ const placeholder = extractMarkdownSection(content, '### Placeholder materialization');
1471
+ if (!placeholder) {
1472
+ errors.push(
1473
+ 'Placeholder: developer-log.md § Placeholder materialization missing — fill the materialization table + "Placeholder run result" (run npm run placeholder:gen)',
1474
+ );
1475
+ } else {
1476
+ if (countFilledTableRows(placeholder) < 1) {
1477
+ errors.push(
1478
+ 'Placeholder: § Placeholder materialization table has no filled row — record each placeholder asset (path / Tier / size / generated) before STOP; Ring 1 onward forbids pure code-drawn visuals',
1479
+ );
1480
+ }
1481
+ if (!/Placeholder run result[^\n]*\b(success|partial)\b/i.test(placeholder)) {
1482
+ errors.push(
1483
+ 'Placeholder: § Placeholder materialization "Placeholder run result" must be success or partial (run npm run placeholder:gen and record the outcome)',
1484
+ );
1485
+ }
1486
+ }
1487
+
1488
+ // 4. verify:placeholders recorded as pass/OK in developer-log
1489
+ if (!/verify:placeholders[\s\S]{0,120}\b(OK|pass|passed)\b/i.test(content)) {
1490
+ errors.push(
1491
+ 'Placeholder: run npm run verify:placeholders and log pass/OK in developer-log § Dev self-check',
1492
+ );
1493
+ }
1494
+
1495
+ return errors;
1496
+ }
1497
+
1498
+ /**
1499
+ * The recorded devUrl must carry the `?debug=1` flag so opening it shows the
1500
+ * on-screen one-click clear (debug) button by default. Only flags when a devUrl
1501
+ * http(s) link is actually present (no false positive when none recorded yet).
1502
+ * @param {string} content developer-log.md content
1503
+ * @returns {string[]}
1504
+ */
1505
+ function validateDevUrlDebugFlag(content) {
1506
+ const errors = [];
1507
+ const m = content.match(/devUrl[^\n]*?(https?:\/\/[^\s)<>"']+)/i);
1508
+ if (m && !/[?&]debug=1\b/.test(m[1])) {
1509
+ errors.push(
1510
+ `devUrl: recorded devUrl (${m[1]}) must include ?debug=1 — every dev url is opened with debug=1 so the one-click clear debug button shows by default`,
1511
+ );
1512
+ }
1513
+ return errors;
1514
+ }
1515
+
1516
+ /**
1517
+ * Ring 1 build STOP: Developer self-gates objective DoD via scripts (no Reviewer invoke).
1518
+ * @param {string} projectDir
1519
+ * @param {string} content
1520
+ * @param {Record<string, string>} handoff
1521
+ * @returns {string[]}
1522
+ */
1523
+ function validateDeveloperRing1SelfGate(projectDir, content, handoff) {
1524
+ const errors = [];
1525
+ const warnings = [];
1526
+ const nextAction = handoff.next_orchestrator_action || '';
1527
+ if (!/ring:gate/i.test(nextAction)) {
1528
+ errors.push(
1529
+ 'Ring 1 build STOP: next_orchestrator_action must route orchestrator to npm run ring:gate -- --iteration 1 after per-slice verify:slice + terminal slice queue (ring:gate aggregates; it does not replace verify:slice)',
1530
+ );
1531
+ }
1532
+ if (!/\bverify:contract\b[\s\S]{0,120}\b(OK|pass|passed|satisfied)\b/i.test(content)) {
1533
+ errors.push(
1534
+ 'Ring 1 build STOP: run npm run verify:contract and log pass/OK in developer-log § Dev self-check',
1535
+ );
1536
+ }
1537
+ if (!/\bverify:lifecycle\b[\s\S]{0,120}\b(OK|pass|passed)\b/i.test(content)) {
1538
+ errors.push(
1539
+ 'Ring 1 build STOP: run npm run verify:lifecycle and log pass/OK in developer-log § Dev self-check',
1540
+ );
1541
+ }
1542
+ if (!/\bverify:firstwin\b[\s\S]{0,120}\b(OK|pass|passed)\b/i.test(content)) {
1543
+ errors.push(
1544
+ 'Ring 1 build STOP: run npm run verify:firstwin and log pass/OK in developer-log § Dev self-check',
1545
+ );
1546
+ }
1547
+ if (!/\bverify:placeholders\b[\s\S]{0,120}\b(OK|pass|passed)\b/i.test(content)) {
1548
+ errors.push(
1549
+ 'Ring 1 build STOP: run npm run verify:placeholders and log pass/OK in developer-log § Dev self-check',
1550
+ );
1551
+ }
1552
+ errors.push(...validateSkillAndPlaceholderEvidence(projectDir, content));
1553
+ errors.push(...validateDevUrlDebugFlag(content));
1554
+ const devStatus = readState(projectDir).devPreview?.devStatus;
1555
+ if (String(devStatus || '').toLowerCase() !== 'ready') {
1556
+ errors.push('Ring 1 build STOP: set devStatus: ready (npm run state:set -- devStatus=ready) before STOP');
1557
+ }
1558
+ const scenesDir = path.join(projectDir, 'game/scenes');
1559
+ if (fs.existsSync(scenesDir)) {
1560
+ const SCENE_LINE_WARN = 400;
1561
+ for (const name of fs.readdirSync(scenesDir)) {
1562
+ if (!/\.(ts|js)$/.test(name)) continue;
1563
+ const abs = path.join(scenesDir, name);
1564
+ if (!fs.statSync(abs).isFile()) continue;
1565
+ const lineCount = fs.readFileSync(abs, 'utf8').split('\n').length;
1566
+ if (lineCount > SCENE_LINE_WARN) {
1567
+ warnings.push(
1568
+ `Scene file ${name} has ${lineCount} lines (>${SCENE_LINE_WARN}) — split into game/gameplay/ submodules (hard target ≤350 lines/scene)`,
1569
+ );
1570
+ }
1571
+ }
1572
+ }
1573
+ return { errors, warnings };
1574
+ }
1575
+
1576
+ /**
1577
+ * Developer STOP checkpoint — 4 items:
1578
+ * 0. Feature Checklist existence (developer-log.md § Feature Checklist or § Feature Review)
1579
+ * 1. Delivery Checklist coverage (implementation-plan.md § 8 vs developer-log.md § Feature Review)
1580
+ * 2. Scaffold asset adoption (scaffold-manifest.md vs developer-log.md § Read / Load Log) + scaffold decision documented
1581
+ * 3. Upstream blockers closed (no open blocker without resolution)
1582
+ * @param {string} projectDir
1583
+ * @param {string} devLogContent
1584
+ */
1585
+ function validateDeveloperCheckpoints(projectDir, devLogContent) {
1586
+ const errors = [];
1587
+
1588
+ // --- 0. Feature Checklist existence ---
1589
+ const hasFeatureChecklist =
1590
+ devLogContent.includes('## Feature Checklist') ||
1591
+ devLogContent.includes('## Feature Review');
1592
+ const hasFeatureItem =
1593
+ /^\s*[-*]\s*\[[ xX]\]/.test(devLogContent.split('\n').find((l) => l.includes('## Feature')));
1594
+ if (!hasFeatureChecklist) {
1595
+ errors.push(
1596
+ 'Feature Checklist: missing "## Feature Checklist" or "## Feature Review" section in developer-log.md — create it from implementation-plan.md features before claiming readiness',
1597
+ );
1598
+ }
1599
+
1600
+ // --- 1. Delivery Checklist coverage ---
1601
+ const implPlanPath = path.join(projectDir, 'docs/implementation-plan.md');
1602
+ if (fs.existsSync(implPlanPath)) {
1603
+ const implPlan = fs.readFileSync(implPlanPath, 'utf8');
1604
+ const checklistStart = implPlan.indexOf('## 8. Delivery Checklist');
1605
+ if (checklistStart !== -1) {
1606
+ const checklistSection = implPlan.slice(checklistStart);
1607
+ // Extract unchecked items: lines starting with "- [ ]"
1608
+ const uncheckedItems = [];
1609
+ for (const line of checklistSection.split('\n')) {
1610
+ const trimmed = line.trim();
1611
+ if (/^- \[ \]/.test(trimmed)) {
1612
+ // Extract item text (strip "- [ ] " prefix and "— source: ..." suffix)
1613
+ const itemText = trimmed
1614
+ .replace(/^- \[ \]\s*/, '')
1615
+ .replace(/\s*—\s*source:.*$/, '')
1616
+ .trim();
1617
+ uncheckedItems.push(itemText);
1618
+ }
1619
+ }
1620
+ if (uncheckedItems.length > 0) {
1621
+ // Check if Feature Review section exists and has content
1622
+ const featureReviewStart = devLogContent.indexOf('## Feature Review');
1623
+ if (featureReviewStart === -1) {
1624
+ errors.push(
1625
+ `Delivery Checklist: ${uncheckedItems.length} unchecked item(s) — add "## Feature Review" section to developer-log.md and mark each item done/partial(reason)`,
1626
+ );
1627
+ } else {
1628
+ // Items exist but are unchecked — developer must address them
1629
+ errors.push(
1630
+ `Delivery Checklist: ${uncheckedItems.length} unchecked item(s) in implementation-plan.md § 8 — mark each [x] or add partial/blocked note in developer-log.md § Feature Review:\n ${uncheckedItems.slice(0, 5).join('\n ')}${uncheckedItems.length > 5 ? `\n ... and ${uncheckedItems.length - 5} more` : ''}`,
1631
+ );
1632
+ }
1633
+ }
1634
+ }
1635
+ }
1636
+
1637
+ // --- 2. Scaffold asset adoption + decision documentation ---
1638
+ // Check scaffold decision is documented in Skill Preflight (whether or not scaffold was executed)
1639
+ const skillPreflightStart = devLogContent.indexOf('## Skill Preflight');
1640
+ const hasScaffoldDecision =
1641
+ devLogContent.toLowerCase().includes('scaffold-candidate') ||
1642
+ devLogContent.toLowerCase().includes('skip-with-reason') ||
1643
+ devLogContent.toLowerCase().includes('scaffold decision') ||
1644
+ devLogContent.toLowerCase().includes('本轮 scaffold');
1645
+ if (skillPreflightStart !== -1 && !hasScaffoldDecision) {
1646
+ errors.push(
1647
+ 'Scaffold decision: no scaffold decision found in developer-log.md § Skill Preflight — document per-atom decision (scaffold-candidate / skip-with-reason) and add a summary line (e.g. "本轮 scaffold: none, because ...")',
1648
+ );
1649
+ }
1650
+
1651
+ const manifestPath = path.join(projectDir, 'docs/scaffold-manifest.md');
1652
+ if (fs.existsSync(manifestPath)) {
1653
+ const manifest = fs.readFileSync(manifestPath, 'utf8');
1654
+ // Count scaffold entries (lines with asset paths, e.g. "- game/assets/...")
1655
+ const scaffoldAssets = [];
1656
+ for (const line of manifest.split('\n')) {
1657
+ const trimmed = line.trim();
1658
+ if (/^-\s+game\/assets\//.test(trimmed) || /^-\s+`game\/assets\//.test(trimmed)) {
1659
+ scaffoldAssets.push(trimmed.replace(/^-\s+`?/, '').replace(/`?$/, '').trim());
1660
+ }
1661
+ }
1662
+ if (scaffoldAssets.length > 0) {
1663
+ // Check if developer-log has any load evidence
1664
+ const hasLoadLog =
1665
+ devLogContent.includes('## Read / Load Log') ||
1666
+ devLogContent.includes('this.load.') ||
1667
+ devLogContent.includes('load.image') ||
1668
+ devLogContent.includes('load.atlas') ||
1669
+ devLogContent.includes('load.audio');
1670
+ const hasAdoptionNote =
1671
+ devLogContent.toLowerCase().includes('scaffold') &&
1672
+ (devLogContent.toLowerCase().includes('unused') ||
1673
+ devLogContent.toLowerCase().includes('replaced') ||
1674
+ devLogContent.toLowerCase().includes('not used') ||
1675
+ devLogContent.toLowerCase().includes('adoption'));
1676
+ if (!hasLoadLog && !hasAdoptionNote) {
1677
+ errors.push(
1678
+ `Scaffold asset adoption: ${scaffoldAssets.length} scaffold asset(s) found in docs/scaffold-manifest.md but no load evidence or adoption note in developer-log.md — add load records or explain why assets were not used`,
1679
+ );
1680
+ }
1681
+ }
1682
+ }
1683
+
1684
+ // --- 3. Upstream blockers closed ---
1685
+ const blockersStart = devLogContent.indexOf('## Upstream blockers');
1686
+ if (blockersStart !== -1) {
1687
+ const after = devLogContent.slice(blockersStart + '## Upstream blockers'.length);
1688
+ const nextHeading = after.search(/\n##\s+/);
1689
+ const section = nextHeading === -1 ? after : after.slice(0, nextHeading);
1690
+ const openBlockers = [];
1691
+ for (const line of section.split('\n')) {
1692
+ const trimmed = line.trim();
1693
+ if (!trimmed.startsWith('|')) continue;
1694
+ if (/^\|\s*---/.test(trimmed)) continue;
1695
+ if (/^\|\s*(Blocker|blocker|Item|item|Path)\b/i.test(trimmed)) continue;
1696
+ const parts = trimmed
1697
+ .split('|')
1698
+ .map((p) => p.trim())
1699
+ .filter((_, i, arr) => i > 0 && i < arr.length - 1);
1700
+ // Template: | Path(0) | Issue(1) | Resolution(2) | routeTo(3) | Status(4) |
1701
+ if (parts.length < 5) continue;
1702
+ const status = (parts[4] || '').toLowerCase();
1703
+ const resolution = (parts[2] || '').trim();
1704
+ // Valid resolutions that genuinely close a blocker:
1705
+ // fixed, replaced(<reason>), escalated — anything else is a note, not a resolution.
1706
+ const isValidResolution =
1707
+ /^(fixed|replaced|escalated)\b/i.test(resolution) ||
1708
+ resolution === '—' ||
1709
+ resolution === '-' ||
1710
+ resolution === '';
1711
+ if (status === 'open' || !isValidResolution) {
1712
+ openBlockers.push(parts[0]);
1713
+ }
1714
+ }
1715
+ if (openBlockers.length > 0) {
1716
+ errors.push(
1717
+ `Upstream blockers: ${openBlockers.length} open blocker(s) without resolution — set resolution to fixed/replaced(reason)/escalated:\n ${openBlockers.join('\n ')}`,
1718
+ );
1719
+ }
1720
+ }
1721
+
1722
+ // --- 4. Skill link + Skill Preflight + placeholder materialization evidence (ring 2+ parity with ring 1) ---
1723
+ errors.push(...validateSkillAndPlaceholderEvidence(projectDir, devLogContent));
1724
+
1725
+ // --- 5. devUrl carries ?debug=1 (one-click clear debug button shown by default) ---
1726
+ errors.push(...validateDevUrlDebugFlag(devLogContent));
1727
+
1728
+ return errors;
1729
+ }
1730
+
1731
+ /** @param {string} projectDir */
1732
+ async function validateWorkflowState(projectDir) {
1733
+ const result = await diagnose(projectDir, { strict: 'stop' });
1734
+ return result.ok ? [] : result.errors;
363
1735
  }
364
1736
 
365
1737
  /** @param {string} projectDir */
366
1738
  function resolveProjectDir(projectDir) {
367
- const statePath = path.join(projectDir, 'docs/project-state.md');
368
- if (!fs.existsSync(statePath)) {
369
- return { skip: true, reason: 'no docs/project-state.md' };
1739
+ if (!stateExists(projectDir)) {
1740
+ return { skip: true, reason: 'no docs/project-state.json or .md' };
370
1741
  }
371
1742
  return { skip: false };
372
1743
  }
@@ -388,29 +1759,54 @@ async function readStdin() {
388
1759
 
389
1760
  export {
390
1761
  detectRole,
1762
+ roleFromActiveSlice,
1763
+ validateActiveSliceResolved,
1764
+ validateResolvedSliceStop,
1765
+ hasUnresolvedActiveSlice,
1766
+ isSliceResolved,
391
1767
  parseUpstreamIntake,
392
1768
  validateIntakeContent,
393
1769
  validatePlan,
394
- validateDesignerSkillPreflight,
1770
+ validateArtistSkillPreflight,
1771
+ validateDeveloperRuntimeContract,
395
1772
  validateRole,
1773
+ validateReviewerStop,
1774
+ validateGameDesignerStop,
1775
+ validateSkillSelectorStop,
1776
+ validateUiDiffVisualRubric,
1777
+ validatePlayableSeedDesign,
1778
+ validateHandoffContext,
1779
+ validateGameDesignerSeedContext,
1780
+ validateGameDesignerSliceQueueSeed,
1781
+ validateSkillsMatchSliceRefs,
1782
+ validateDesignFusionSection,
1783
+ validateReviewerHandoff,
1784
+ validateReviewReportDoD,
1785
+ readHandoff,
1786
+ readIteration,
1787
+ readLoop,
396
1788
  readStage,
397
1789
  readGatePending,
398
- TA_REQUIRED_DOCS,
399
- DEV_REQUIRED_DOCS,
400
- DESIGNER_REQUIRED_DOCS,
1790
+ requiredDocsForRole,
1791
+ validateWorkflowState,
1792
+ DEV_REQUIRED_DOCS_BY_RING,
1793
+ TA_REQUIRED_DOCS_BY_RING,
1794
+ ARTIST_REQUIRED_DOCS,
1795
+ DESIGNER_REQUIRED_DOCS_BY_RING,
1796
+ reviewDoDKeysForRing,
401
1797
  PLAN_HEADING,
402
1798
  PLAN_REQUIRED_SUBS,
403
1799
  ROLE_LOG,
1800
+ validateSkillAndPlaceholderEvidence,
1801
+ validateDevUrlDebugFlag,
1802
+ readSelectedAtomsForLink,
1803
+ extractMarkdownSection,
1804
+ countFilledTableRows,
404
1805
  };
405
1806
 
406
1807
  async function main() {
407
1808
  const stdin = await readStdin();
408
1809
  const input = parseHookInput(stdin);
409
- const role = detectRole(input);
410
-
411
- if (!role) {
412
- process.exit(0);
413
- }
414
1810
 
415
1811
  const projectDir =
416
1812
  process.env.CLAUDE_PROJECT_DIR ||
@@ -422,24 +1818,68 @@ async function main() {
422
1818
  process.exit(0);
423
1819
  }
424
1820
 
1821
+ // slice-axis-v1: an explicit role marker (subagent_type / PLAYCRAFT_STOP
1822
+ // self-declaration) wins; otherwise derive the dispatched role from an
1823
+ // unresolved active slice so a top-level `claude -p` developer cannot STOP past
1824
+ // an unverified slice even without subagent context.
1825
+ const role = detectRole(input) || roleFromActiveSlice(projectDir);
1826
+ if (!role) {
1827
+ process.exit(0);
1828
+ }
1829
+
1830
+ // slice mode = this role owns an unresolved active slice → the slice state
1831
+ // machine is the only gate; ring intake/handoff ceremony is bypassed.
1832
+ const sliceOwner = roleFromActiveSlice(projectDir);
1833
+ const sliceMode = sliceOwner === role;
1834
+ // dispatch mismatch = an executor role was stopped on someone else's slice.
1835
+ const sliceMismatch = !!sliceOwner && sliceOwner !== role && SLICE_EXECUTOR_ROLES.has(role);
1836
+
425
1837
  const errors = validateRole(projectDir, role);
426
1838
  if (errors.length > 0) {
427
- const logPath = ROLE_LOG[role].log;
1839
+ const cfg = ROLE_LOG[role];
1840
+ if (sliceMode || sliceMismatch) {
1841
+ const label = cfg?.label || role;
1842
+ const head = sliceMismatch ? 'dispatch mismatch' : 'resolve the active slice before STOP';
1843
+ emitBlock(`${label} STOP blocked — ${head}:\n- ${errors.join('\n- ')}`);
1844
+ }
1845
+ const loop = readLoop(projectDir);
1846
+ const iteration = readIteration(projectDir) || 1;
428
1847
  const section =
429
- role === 'designer'
430
- ? readStage(projectDir) === 'style_exploration'
431
- ? '§ Image Production Preflight / ASR Coverage Matrix'
432
- : '§ Skill Preflight'
433
- : '§ Upstream Intake / Plan';
434
- const reason = `${ROLE_LOG[role].label} STOP blocked — fix ${logPath} ${section}:\n- ${errors.join('\n- ')}`;
1848
+ role === 'reviewer'
1849
+ ? '§ Ring DoD Fields + handoff (no user_gate)'
1850
+ : role === 'game-designer'
1851
+ ? `§ ring ${iteration} clarify docs`
1852
+ : role === 'skill-selector'
1853
+ ? '§ atom-tree selection + handoff'
1854
+ : role === 'artist'
1855
+ ? loop === 'verify' && iteration >= 3
1856
+ ? '§ Image Production Preflight / ASR Coverage Matrix'
1857
+ : '§ Skill Preflight'
1858
+ : '§ Upstream Intake / Plan';
1859
+ const logRef = cfg.log ? `${cfg.log} ` : '';
1860
+ const reason = `${cfg.label} STOP blocked — fix ${logRef}${section}:\n- ${errors.join('\n- ')}`;
435
1861
  emitBlock(reason);
436
1862
  }
437
1863
 
1864
+ // In slice mode the active slice is the sole authority — skip the ring/handoff
1865
+ // workflow-state diagnosis (it carries ring-era expectations that do not apply
1866
+ // to a single dispatched slice). Legacy (no active slice) keeps full diagnosis.
1867
+ if (!sliceMode) {
1868
+ const stateErrors = await validateWorkflowState(projectDir);
1869
+ if (stateErrors.length > 0) {
1870
+ const reason = `Workflow STOP blocked — fix docs/project-state.md handoff / ring state:\n- ${stateErrors.join('\n- ')}`;
1871
+ emitBlock(reason);
1872
+ }
1873
+ }
1874
+
438
1875
  process.exit(0);
439
1876
  }
440
1877
 
1878
+ // Use basename comparison — on Windows, CLAUDE_PROJECT_DIR may be POSIX-style
1879
+ // (/d/project/...), and path.resolve would mangle it to D:\d\project\...,
1880
+ // never matching the Windows-native __filename.
441
1881
  const isMain =
442
- process.argv[1] && path.resolve(process.argv[1]) === __filename;
1882
+ process.argv[1] && path.basename(process.argv[1]) === path.basename(__filename);
443
1883
 
444
1884
  if (isMain) {
445
1885
  main().catch((err) => {