@event4u/agent-config 14.10.0 → 14.11.0

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 (399) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +335 -0
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/design.md +88 -0
  7. package/dist/agent-src/commands/project/analyze.md +47 -0
  8. package/dist/agent-src/commands/review/changes.md +67 -6
  9. package/dist/agent-src/commands/roadmap/create.md +49 -0
  10. package/dist/agent-src/commands/roadmap/next.md +27 -11
  11. package/dist/agent-src/commands/roadmap/process-full.md +14 -0
  12. package/dist/agent-src/commands/video/from-script.md +60 -3
  13. package/dist/agent-src/commands/video/stitch.md +14 -0
  14. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
  16. package/dist/agent-src/rules/active-remediation.md +3 -3
  17. package/dist/agent-src/rules/code-provenance.md +2 -2
  18. package/dist/agent-src/rules/context-hygiene.md +13 -33
  19. package/dist/agent-src/rules/council-availability.md +2 -2
  20. package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
  21. package/dist/agent-src/rules/design-fidelity.md +18 -0
  22. package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
  23. package/dist/agent-src/rules/evaluator-independence.md +56 -44
  24. package/dist/agent-src/rules/fix-what-you-see.md +9 -1
  25. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  26. package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
  27. package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
  28. package/dist/agent-src/rules/playbook-precedence.md +105 -0
  29. package/dist/agent-src/rules/recurring-criticism.md +3 -1
  30. package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
  31. package/dist/agent-src/rules/scale-discipline.md +24 -0
  32. package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
  33. package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
  34. package/dist/agent-src/rules/ui-audit-gate.md +27 -15
  35. package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
  36. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  37. package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
  38. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
  39. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
  40. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
  41. package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
  42. package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
  43. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
  44. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  45. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  46. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  47. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  52. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  53. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  54. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  55. package/dist/agent-src/skills/condense-memory/SKILL.md +3 -0
  56. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  57. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
  58. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  59. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  60. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  61. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  62. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  63. package/dist/agent-src/skills/design-review/SKILL.md +79 -1
  64. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  65. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  66. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  67. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  68. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  69. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  70. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  71. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  72. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  73. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  74. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  75. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  76. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  77. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  78. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  79. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  80. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  81. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  82. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  83. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  84. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  85. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  86. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  87. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  88. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  89. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  90. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  91. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  92. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  93. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  94. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  95. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  96. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  97. package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
  98. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  99. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  100. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  101. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  102. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  103. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  104. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  105. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  106. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  107. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  108. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  109. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  110. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  111. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  112. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  113. package/dist/agent-src/skills/receiving-code-review/SKILL.md +2 -2
  114. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  115. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  116. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  117. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  118. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  119. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  120. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  121. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  122. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  123. package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
  124. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  125. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  126. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  127. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  128. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  129. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  130. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  131. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  132. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  133. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  134. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  135. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  136. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  137. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  138. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  139. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  140. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  141. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  142. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  143. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  144. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  145. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  146. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  147. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  148. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  149. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  150. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  151. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  152. package/dist/agent-src/templates/playbook.md +75 -0
  153. package/dist/agent-src/templates/roadmaps.md +37 -0
  154. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  155. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  156. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  157. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  158. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  159. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  160. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  161. package/dist/cli/commands/uiAudit.js +221 -0
  162. package/dist/cli/commands/uiAudit.js.map +1 -0
  163. package/dist/cli/commands/uiRender.js +206 -0
  164. package/dist/cli/commands/uiRender.js.map +1 -0
  165. package/dist/cli/main.js +22 -1
  166. package/dist/cli/main.js.map +1 -1
  167. package/dist/cli/mcp/content.js +64 -1
  168. package/dist/cli/mcp/content.js.map +1 -1
  169. package/dist/cli/mcp/dispatch.js +175 -10
  170. package/dist/cli/mcp/dispatch.js.map +1 -1
  171. package/dist/cli/registry.js +3 -0
  172. package/dist/cli/registry.js.map +1 -1
  173. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  174. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  175. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  176. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  177. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  178. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  179. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  180. package/dist/cli-delegate/cmd_conformance.js +3 -3
  181. package/dist/cli-delegate/cmd_doctor.js +3 -3
  182. package/dist/cli-delegate/cmd_export.js +4 -4
  183. package/dist/cli-delegate/cmd_handoff.js +1 -1
  184. package/dist/cli-delegate/cmd_preflight.js +2 -2
  185. package/dist/cli-delegate/cmd_prune.js +4 -4
  186. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  187. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  188. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  189. package/dist/cli-delegate/cmd_sync.js +4 -4
  190. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  191. package/dist/cli-delegate/cmd_validate.js +4 -4
  192. package/dist/discovery/deprecation-report.md +1 -1
  193. package/dist/discovery/discovery-manifest.json +359 -124
  194. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  195. package/dist/discovery/discovery-manifest.summary.md +4 -4
  196. package/dist/discovery/dormancy-report.md +1 -1
  197. package/dist/discovery/orphan-report.md +1 -1
  198. package/dist/discovery/packs.json +18 -8
  199. package/dist/discovery/trust-report.md +3 -3
  200. package/dist/discovery/workspaces.json +16 -6
  201. package/dist/hooks/dispatch.js +2337 -1436
  202. package/dist/install/install.mjs +795 -708
  203. package/dist/mcp/registry-manifest.json +3 -3
  204. package/dist/mcp/server.json +2 -2
  205. package/dist/mcp/server.mjs +1685 -1250
  206. package/dist/router.json +1 -1
  207. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  208. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  209. package/dist/scripts/_lib/scoped_projection.js +84 -0
  210. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  211. package/dist/scripts/_lib/ui_surface.js +89 -0
  212. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  213. package/dist/scripts/condense.js +6 -10
  214. package/dist/scripts/condense.js.map +1 -1
  215. package/dist/scripts/project_thin_rules.js +40 -2
  216. package/dist/scripts/project_thin_rules.js.map +1 -1
  217. package/dist/server/schemas/settings.js +3 -0
  218. package/dist/server/schemas/settings.js.map +1 -1
  219. package/dist/shared/skillRanking.js +159 -0
  220. package/dist/shared/skillRanking.js.map +1 -0
  221. package/docs/contracts/provider-lifecycle.md +47 -18
  222. package/docs/contracts/settings-classes.md +14 -4
  223. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  224. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  225. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  226. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  227. package/docs/guidelines/design-antipatterns.md +4 -2
  228. package/docs/guidelines/design-asset-discipline.md +38 -0
  229. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  230. package/docs/guidelines/design-handover-extraction.md +35 -5
  231. package/docs/guidelines/design-modes.md +41 -1
  232. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  233. package/llms.txt +50 -39
  234. package/package.json +1 -1
  235. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  236. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  237. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  238. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  239. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  240. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  241. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  242. package/src/config/agent-settings.template.yml +28 -0
  243. package/src/config/assurance-capability-registry.json +223 -0
  244. package/src/config/assurance-threshold-budget.json +65 -0
  245. package/src/config/ci-local-parity.yml +14 -0
  246. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  247. package/src/config/dispatch-economy-metrics.json +4 -2
  248. package/src/config/evaluator-budgets.json +14 -14
  249. package/src/config/gate-coverage.yml +207 -8
  250. package/src/config/gate-violation-baselines.json +76 -72
  251. package/src/config/hook-latency-budget.json +1 -0
  252. package/src/config/hook-token-budget.json +137 -125
  253. package/src/config/pack-size-budget.json +4 -2
  254. package/src/config/quorum-attendance-budget.json +12 -4
  255. package/src/config/rule-activation-census.json +7 -3
  256. package/src/config/rule-enforcement-baseline.json +0 -1
  257. package/src/config/rule-stub-ceilings.json +112 -91
  258. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  259. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  260. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  261. package/src/scripts/_dispatch.bash +17 -0
  262. package/src/scripts/_lib/as_of.ts +412 -0
  263. package/src/scripts/_lib/exec_evidence.ts +7 -0
  264. package/src/scripts/_lib/hook_settings.ts +33 -0
  265. package/src/scripts/_lib/host_listing_model.ts +192 -0
  266. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  267. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  268. package/src/scripts/_lib/review_independence.ts +62 -10
  269. package/src/scripts/_lib/rule_injection.ts +239 -0
  270. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  271. package/src/scripts/_lib/scoped_projection.ts +96 -0
  272. package/src/scripts/_lib/session_register.ts +44 -2
  273. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  274. package/src/scripts/_lib/transcript_reads.ts +207 -0
  275. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  276. package/src/scripts/_lib/ui_authority.ts +343 -0
  277. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  278. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  279. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  280. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  281. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  282. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  283. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  284. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  285. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  286. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  287. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  288. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  289. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  290. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  291. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  292. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  293. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  294. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  295. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  296. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  297. package/src/scripts/ai-video/stitch.sh +191 -10
  298. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  299. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  300. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  301. package/src/scripts/ai_council/clients.ts +3 -2
  302. package/src/scripts/ai_council/consensus.ts +63 -6
  303. package/src/scripts/ai_council/events_log.ts +46 -1
  304. package/src/scripts/ai_council/orchestrator.ts +103 -108
  305. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  306. package/src/scripts/ai_council/prompts.ts +12 -5
  307. package/src/scripts/ai_council/quorum.ts +82 -1
  308. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  309. package/src/scripts/ai_council/stance_tally.ts +22 -0
  310. package/src/scripts/bench_hook_latency.ts +62 -0
  311. package/src/scripts/build_proof.ts +12 -1
  312. package/src/scripts/capture_skill_catalogue.ts +21 -0
  313. package/src/scripts/check_always_budget.ts +2 -1
  314. package/src/scripts/check_augmentignore.ts +2 -1
  315. package/src/scripts/check_beta_review_markers.ts +2 -1
  316. package/src/scripts/check_composite_arming.ts +364 -0
  317. package/src/scripts/check_corpus_staleness.ts +2 -1
  318. package/src/scripts/check_council_pin_staleness.ts +3 -1
  319. package/src/scripts/check_enforcement_coverage.ts +116 -3
  320. package/src/scripts/check_enforcement_denominator.ts +365 -0
  321. package/src/scripts/check_gate_coverage.ts +2 -1
  322. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  323. package/src/scripts/check_knowledge_cards.ts +2 -1
  324. package/src/scripts/check_knowledge_pages.ts +3 -2
  325. package/src/scripts/check_memory.ts +2 -1
  326. package/src/scripts/check_no_automerge_key.ts +287 -0
  327. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  328. package/src/scripts/check_proposal.ts +2 -1
  329. package/src/scripts/check_reach_staleness.ts +2 -1
  330. package/src/scripts/check_release_adjacent_health.ts +2 -1
  331. package/src/scripts/check_release_pr_shape.ts +5 -0
  332. package/src/scripts/check_review_schema.ts +34 -1
  333. package/src/scripts/check_roadmap_trackable.ts +48 -3
  334. package/src/scripts/check_source_size_budget.ts +2 -1
  335. package/src/scripts/check_standing_payload_delta.ts +505 -0
  336. package/src/scripts/check_trigger_evals.ts +2 -1
  337. package/src/scripts/classify_change_risk.ts +281 -0
  338. package/src/scripts/compute_skill_tiers.ts +186 -0
  339. package/src/scripts/condense.ts +8 -13
  340. package/src/scripts/council_attendance_metrics.ts +183 -0
  341. package/src/scripts/council_cli.ts +10 -8
  342. package/src/scripts/council_parse_rate.ts +116 -0
  343. package/src/scripts/derive_playbooks.ts +406 -0
  344. package/src/scripts/design_slop_rules.test.ts +7 -0
  345. package/src/scripts/design_slop_rules.ts +56 -2
  346. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  347. package/src/scripts/frontend_corpus_hash.ts +116 -0
  348. package/src/scripts/grade_target_readiness.ts +478 -0
  349. package/src/scripts/hook_manifest.json +1 -1
  350. package/src/scripts/hook_manifest.yaml +97 -16
  351. package/src/scripts/hooks/concern_registry.ts +4 -0
  352. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  353. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  354. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  355. package/src/scripts/hot_context_hook.ts +74 -7
  356. package/src/scripts/install.ts +40 -89
  357. package/src/scripts/lint_adapter_tier.ts +663 -0
  358. package/src/scripts/lint_archived_skills.ts +93 -0
  359. package/src/scripts/lint_budget_ownership.ts +2 -1
  360. package/src/scripts/lint_deterministic_time.ts +470 -0
  361. package/src/scripts/lint_framework_leakage.ts +5 -0
  362. package/src/scripts/lint_handoffs.ts +525 -1
  363. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  364. package/src/scripts/lint_marketplace.ts +88 -0
  365. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  366. package/src/scripts/lint_one_off_age.ts +6 -1
  367. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  368. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  369. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  370. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  371. package/src/scripts/lint_spawn_payload.ts +42 -5
  372. package/src/scripts/lint_symptom_intake.ts +2 -1
  373. package/src/scripts/mcp_parity_smoke.ts +30 -4
  374. package/src/scripts/mcp_render.ts +66 -1
  375. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  376. package/src/scripts/measure_risk_class_drift.ts +140 -0
  377. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  378. package/src/scripts/media/lib/adapter-common.sh +211 -1
  379. package/src/scripts/media/lib/adapter-contract.md +74 -6
  380. package/src/scripts/model_rule_injection.ts +907 -0
  381. package/src/scripts/project_thin_rules.ts +45 -2
  382. package/src/scripts/release.ts +66 -860
  383. package/src/scripts/release_drill.ts +59 -3
  384. package/src/scripts/release_env.ts +298 -0
  385. package/src/scripts/release_publication.ts +720 -0
  386. package/src/scripts/roadmap_context.ts +778 -0
  387. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  388. package/src/scripts/schemas/rule.schema.json +24 -2
  389. package/src/scripts/schemas/skill.schema.json +39 -0
  390. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  391. package/src/scripts/session_register_hook.ts +29 -3
  392. package/src/scripts/sessions_cli.ts +67 -3
  393. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  394. package/src/scripts/token_sink_report.ts +239 -0
  395. package/src/scripts/trigger_coverage.ts +122 -3
  396. package/src/scripts/update_counts.ts +7 -3
  397. package/src/scripts/validate_frontmatter.ts +66 -0
  398. package/src/server/schemas/settings.ts +5 -0
  399. package/src/shared/skillRanking.ts +183 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * derive_playbooks — propose Playbook contexts (ADR-244) from a repository's own
3
+ * configuration, and refuse to claim more than the tree shows.
4
+ *
5
+ * The `grade` field is the whole point. `configured` asserts that every step invokes
6
+ * something PRESENT IN THE TREE AND SEEN, so this script may only write it when it has
7
+ * actually resolved the invoked id to a file or a declared task. When it cannot, it
8
+ * writes `observed` and says which id went unresolved — never `configured` with a
9
+ * plausible-looking `invokes` list. That is the Class-A rule of `standards-from-config`
10
+ * applied to procedure: the config IS the standard, and an unresolvable id is not config.
11
+ *
12
+ * Scope of the first release, decided in ADR-244 § What a playbook is and not re-litigated
13
+ * here: THREE resolvable kinds — `package.json#scripts`, `turbo.json` tasks, and
14
+ * `turbo gen` templates under `turbo/generators/`. Nx generators and Plop are deliberately
15
+ * out: discovering them needs a consumer binary (`nx list`, `plop --help`), and the
16
+ * Phase-3 staleness check must run without one. A repo carrying them is reported, so the
17
+ * omission is visible to its maintainer rather than silent.
18
+ *
19
+ * Usage:
20
+ * derive_playbooks --root <repo> # propose, print, write nothing
21
+ * derive_playbooks --root <repo> --write # write into the playbook home
22
+ * derive_playbooks --root <repo> --home <d> # override the home
23
+ * derive_playbooks --self-test
24
+ */
25
+
26
+ import * as fs from 'node:fs';
27
+ import * as path from 'node:path';
28
+
29
+ /**
30
+ * The playbook home. ADR-244 decided a playbook IS a context type, so this is the
31
+ * contexts home rather than a new directory — the path is not a fresh decision, and the
32
+ * `--home` flag exists because the ADR calls placement the part of the decision least
33
+ * likely to survive contact with a consumer.
34
+ */
35
+ export const DEFAULT_HOME = path.join('agents', 'settings', 'contexts');
36
+
37
+ export type Grade = 'configured' | 'observed';
38
+
39
+ export interface PlaybookStep {
40
+ readonly title: string;
41
+ /** The id this step invokes — a script name, a turbo task, or `turbo gen <t>`. */
42
+ readonly invokes: string;
43
+ /** Where the id was RESOLVED, or null when it could not be. */
44
+ readonly source_of_truth: string | null;
45
+ readonly verify: string;
46
+ }
47
+
48
+ export interface Playbook {
49
+ readonly slug: string;
50
+ readonly task: string;
51
+ readonly scope: string;
52
+ readonly grade: Grade;
53
+ readonly steps: readonly PlaybookStep[];
54
+ /** Ids that could not be resolved. Non-empty forces `observed`. */
55
+ readonly unresolved: readonly string[];
56
+ }
57
+
58
+ export interface DeriveReport {
59
+ readonly playbooks: readonly Playbook[];
60
+ /** Kinds seen in the tree that this release cannot resolve — reported, never guessed. */
61
+ readonly out_of_scope: readonly string[];
62
+ }
63
+
64
+ const readJson = (p: string): Record<string, unknown> | null => {
65
+ try {
66
+ return JSON.parse(fs.readFileSync(p, 'utf8')) as Record<string, unknown>;
67
+ } catch {
68
+ return null;
69
+ }
70
+ };
71
+
72
+ const asRecord = (v: unknown): Record<string, unknown> =>
73
+ v !== null && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : {};
74
+
75
+ /** Turbo generator templates present in the tree, as `turbo gen <name>` ids. */
76
+ export const discoverTurboGenerators = (root: string): Map<string, string> => {
77
+ const out = new Map<string, string>();
78
+ const dir = path.join(root, 'turbo', 'generators');
79
+ if (!fs.existsSync(dir)) return out;
80
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
81
+ const full = path.join(dir, entry.name);
82
+ if (entry.isDirectory()) {
83
+ out.set(`turbo gen ${entry.name}`, path.relative(root, full));
84
+ continue;
85
+ }
86
+ // A config file declares generators BY NAME, so read the names it registers
87
+ // rather than assuming the filename is the generator id. `@turbo/gen` wraps Plop
88
+ // and the registering call is `plop.setGenerator('component', …)`; `addGenerator`
89
+ // is accepted too because Plop's own API carries both spellings and a config
90
+ // written against either must resolve.
91
+ const text = fs.readFileSync(full, 'utf8');
92
+ for (const m of text.matchAll(/\.(?:set|add)Generator\(\s*['"]([A-Za-z0-9_-]+)['"]/g)) {
93
+ out.set(`turbo gen ${m[1]}`, `${path.relative(root, full)} (setGenerator)`);
94
+ }
95
+ }
96
+ return out;
97
+ };
98
+
99
+ /** Every workspace directory that carries its own `package.json`. */
100
+ export const discoverWorkspaces = (root: string): string[] => {
101
+ const pkg = readJson(path.join(root, 'package.json'));
102
+ const globs = Array.isArray(pkg?.workspaces) ? (pkg.workspaces as string[]) : [];
103
+ const found: string[] = [];
104
+ for (const g of globs) {
105
+ if (!g.endsWith('/*')) continue;
106
+ const base = path.join(root, g.slice(0, -2));
107
+ if (!fs.existsSync(base)) continue;
108
+ for (const e of fs.readdirSync(base, { withFileTypes: true })) {
109
+ if (!e.isDirectory()) continue;
110
+ const rel = path.join(g.slice(0, -2), e.name);
111
+ if (fs.existsSync(path.join(root, rel, 'package.json'))) found.push(rel);
112
+ }
113
+ }
114
+ return found.sort();
115
+ };
116
+
117
+ /**
118
+ * Resolve one invoked id against the tree. Returns the place it was SEEN, or null.
119
+ * Null is what forces `observed`, so this function is the honesty gate of the script.
120
+ */
121
+ export const resolveInvoked = (
122
+ root: string,
123
+ id: string,
124
+ generators: Map<string, string>,
125
+ ): string | null => {
126
+ if (generators.has(id)) return generators.get(id) ?? null;
127
+
128
+ const turbo = readJson(path.join(root, 'turbo.json'));
129
+ if (turbo && Object.prototype.hasOwnProperty.call(asRecord(turbo.tasks), id)) {
130
+ return 'turbo.json#tasks';
131
+ }
132
+ const pkg = readJson(path.join(root, 'package.json'));
133
+ if (pkg && Object.prototype.hasOwnProperty.call(asRecord(pkg.scripts), id)) {
134
+ return 'package.json#scripts';
135
+ }
136
+ return null;
137
+ };
138
+
139
+ /** Kinds this release does not resolve, reported so the gap is visible. */
140
+ export const detectOutOfScope = (root: string): string[] => {
141
+ const out: string[] = [];
142
+ if (fs.existsSync(path.join(root, 'nx.json'))) out.push('nx generators (needs `nx list`)');
143
+ for (const n of ['plopfile.js', 'plopfile.mjs', 'plopfile.ts', 'plopfile.cjs']) {
144
+ if (fs.existsSync(path.join(root, n))) {
145
+ out.push('plop generators (needs `plop --help`)');
146
+ break;
147
+ }
148
+ }
149
+ return out;
150
+ };
151
+
152
+ const scriptBody = (root: string, name: string): string =>
153
+ String(asRecord(readJson(path.join(root, 'package.json'))?.scripts)[name] ?? '');
154
+
155
+ /**
156
+ * A script whose body is a thin wrapper (`turbo gen component`) is a POINTER at the real
157
+ * procedure, so the playbook must invoke what it points at — otherwise a renamed
158
+ * generator leaves the playbook green while the procedure is broken, which is exactly the
159
+ * drift Phase 3 gates.
160
+ */
161
+ export const unwrapScript = (body: string): string | null => {
162
+ const m = /^\s*turbo\s+gen\s+([A-Za-z0-9_-]+)/.exec(body);
163
+ return m ? `turbo gen ${m[1]}` : null;
164
+ };
165
+
166
+ export const derive = (root: string): DeriveReport => {
167
+ const generators = discoverTurboGenerators(root);
168
+ const workspaces = discoverWorkspaces(root);
169
+ const pkg = readJson(path.join(root, 'package.json'));
170
+ const scripts = asRecord(pkg?.scripts);
171
+ const playbooks: Playbook[] = [];
172
+
173
+ for (const name of Object.keys(scripts).sort()) {
174
+ // Only creation-shaped scripts become playbooks. `build` and `test` are one
175
+ // command each — a playbook for them would be a rename of the script, not a
176
+ // procedure, and the estate does not need one file per npm script.
177
+ const noun = /^(?:new|gen|generate|create|scaffold|add)[:-]?(.*)$/.exec(name);
178
+ if (!noun) continue;
179
+
180
+ const wrapped = unwrapScript(scriptBody(root, name));
181
+ const invoked = wrapped ?? name;
182
+ const where = resolveInvoked(root, invoked, generators);
183
+ const subject = (noun[1] || 'artefact').replace(/[^A-Za-z0-9]+/g, '-');
184
+
185
+ // `scope` prefers a real workspace whose name matches the subject, so a component
186
+ // playbook lands on packages/ui rather than claiming the whole repo.
187
+ const ws = workspaces.find((w) => path.basename(w) === subject) ?? null;
188
+ const scope =
189
+ ws ??
190
+ workspaces.find((w) => subject === 'component' && path.basename(w) === 'ui') ??
191
+ 'repo';
192
+
193
+ const step: PlaybookStep = {
194
+ title: `Run the repository's own generator`,
195
+ invokes: invoked,
196
+ source_of_truth: where,
197
+ verify: where
198
+ ? `the generator's output appears under \`${scope}\` and the package builds`
199
+ : `UNRESOLVED — confirm \`${invoked}\` exists before trusting this step`,
200
+ };
201
+
202
+ // The slug carries the workspace when the playbook is workspace-scoped. Two
203
+ // workspaces may each own a `component` procedure with different conventions, and
204
+ // one file per subject would silently collapse them into whichever ran last.
205
+ const noun_slug = subject === 'artefact' ? name.replace(/[^a-z0-9]+/gi, '-') : subject;
206
+ const slug = scope === 'repo' ? `add-${noun_slug}` : `add-${path.basename(scope)}-${noun_slug}`;
207
+
208
+ playbooks.push({
209
+ slug,
210
+ task: `Add a new ${subject.replace(/-/g, ' ')} to this repository`,
211
+ scope,
212
+ grade: where ? 'configured' : 'observed',
213
+ steps: [step],
214
+ unresolved: where ? [] : [invoked],
215
+ });
216
+ }
217
+
218
+ return { playbooks, out_of_scope: detectOutOfScope(root) };
219
+ };
220
+
221
+ export const renderPlaybook = (p: Playbook): string => {
222
+ const lines: string[] = [
223
+ '---',
224
+ `task: "${p.task}"`,
225
+ `scope: "${p.scope}"`,
226
+ `grade: "${p.grade}"`,
227
+ 'invokes:',
228
+ ...p.steps.map((s) => ` - "${s.invokes}"`),
229
+ '---',
230
+ '',
231
+ `# Playbook: ${p.task}`,
232
+ '',
233
+ '> **This is THIS repository\'s answer, and it outranks a shipped skill.** A shipped',
234
+ '> skill is a generic answer to a generic question; this file encodes decisions this',
235
+ '> repository already made.',
236
+ '',
237
+ `Grade \`${p.grade}\` — ${
238
+ p.grade === 'configured'
239
+ ? 'every step below invokes something seen in the tree, cited per step.'
240
+ : 'at least one invoked id could NOT be resolved in the tree, so the steps are a hypothesis to confirm, not a procedure to trust.'
241
+ }`,
242
+ '',
243
+ '## Steps',
244
+ '',
245
+ ];
246
+ p.steps.forEach((s, i) => {
247
+ lines.push(
248
+ `${i + 1}. **${s.title}** — \`${s.invokes}\``,
249
+ ` - **Source of truth:** ${s.source_of_truth ?? '**unresolved** — nothing in the tree declares this id'}`,
250
+ ` - **Verify:** ${s.verify}`,
251
+ '',
252
+ );
253
+ });
254
+ if (p.unresolved.length > 0) {
255
+ lines.push(
256
+ '## Why this is not `configured`',
257
+ '',
258
+ `Unresolved: ${p.unresolved.map((u) => `\`${u}\``).join(', ')}. A \`configured\``,
259
+ 'grade asserts every step was seen in the tree; this one was not, and downgrading is',
260
+ 'the honest outcome rather than writing the stronger claim and hoping.',
261
+ '',
262
+ );
263
+ }
264
+ return lines.join('\n');
265
+ };
266
+
267
+ // ---------------------------------------------------------------------------
268
+ // Restatement detection — for the per-workspace AGENTS.md contract (1.3).
269
+ //
270
+ // A `packages/<n>/AGENTS.md` is allowed and its primary content is a POINTER LIST to the
271
+ // playbooks scoped to that workspace. The failure it must not become is a second copy of
272
+ // the steps: two files then disagree the first time one is edited, and a reader has no way
273
+ // to tell which one the repository actually follows. So the detector below looks for a
274
+ // playbook step's own text appearing verbatim in a workspace file, and deliberately does
275
+ // NOT flag a pointer AT the playbook — naming the file is the behaviour the contract wants.
276
+ // ---------------------------------------------------------------------------
277
+
278
+ export interface Restatement {
279
+ readonly file: string;
280
+ readonly playbook: string;
281
+ /** The step text found verbatim in `file`. */
282
+ readonly step: string;
283
+ }
284
+
285
+ /** Normalise for comparison: collapse whitespace, drop markdown emphasis and list markers. */
286
+ const normalise = (line: string): string =>
287
+ line
288
+ .replace(/^\s*(?:[-*+]|\d+\.)\s+/, '')
289
+ .replace(/[*_`>]/g, '')
290
+ .replace(/\s+/g, ' ')
291
+ .trim()
292
+ .toLowerCase();
293
+
294
+ /** A line that merely POINTS at a playbook — a link, or a path mention. Never a restatement. */
295
+ export const isPointerLine = (line: string): boolean =>
296
+ /\[[^\]]+\]\([^)]*\.md[^)]*\)/.test(line) || /`[^`]*\.md`/.test(line);
297
+
298
+ /**
299
+ * Steps of a playbook that a workspace file restates verbatim.
300
+ *
301
+ * The needle is the step's **invoked id**, not its title. Titles are generic by
302
+ * construction — every generator step in this repository's own fixture is called "Run the
303
+ * repository's own generator" — so a title match reports the same prose line once per
304
+ * playbook and tells the reader nothing about WHICH procedure was duplicated. The id is
305
+ * what makes a line actionable, and duplicating the actionable half is the failure.
306
+ *
307
+ * Short lines are skipped: a three-word step and a three-word sentence collide by accident,
308
+ * and a detector that fires on coincidence teaches its readers to ignore it.
309
+ */
310
+ export const findRestatedSteps = (
311
+ playbooks: readonly { readonly slug: string; readonly steps: readonly PlaybookStep[] }[],
312
+ file: string,
313
+ text: string,
314
+ minChars = 12,
315
+ ): Restatement[] => {
316
+ const lines = text.split('\n');
317
+ const found: Restatement[] = [];
318
+ for (const p of playbooks) {
319
+ for (const st of p.steps) {
320
+ const needle = normalise(st.invokes);
321
+ if (needle.length < minChars) continue;
322
+ for (const line of lines) {
323
+ if (isPointerLine(line)) continue;
324
+ if (normalise(line).includes(needle)) {
325
+ found.push({ file, playbook: p.slug, step: st.title });
326
+ break;
327
+ }
328
+ }
329
+ }
330
+ }
331
+ return found;
332
+ };
333
+
334
+ const selfTest = (): number => {
335
+ let failed = 0;
336
+ const check = (name: string, cond: boolean): void => {
337
+ if (!cond) {
338
+ console.error(`❌ ${name}`);
339
+ failed += 1;
340
+ }
341
+ };
342
+
343
+ check('a thin wrapper unwraps to the generator it points at', unwrapScript('turbo gen component') === 'turbo gen component');
344
+ check('an unrelated script body does not unwrap', unwrapScript('vite build') === null);
345
+ check('a wrapper with flags still unwraps', unwrapScript('turbo gen workspace --type package') === 'turbo gen workspace');
346
+
347
+ check(
348
+ 'a pointer at a playbook is not a restatement',
349
+ isPointerLine('- See [add a component](../../agents/settings/contexts/add-ui-component.md)'),
350
+ );
351
+ check('a prose line is not a pointer', !isPointerLine('Run the repository own generator first'));
352
+
353
+ const fixture = path.join('tests', 'fixtures', 'playbooks', 'mono-with-generator');
354
+ if (fs.existsSync(fixture)) {
355
+ const r = derive(fixture);
356
+ const comp = r.playbooks.find((p) => p.slug === 'add-ui-component');
357
+ check('the fixture yields a component playbook', comp !== undefined);
358
+ check('it is graded configured', comp?.grade === 'configured');
359
+ check('it invokes the generator, not the wrapper script', comp?.steps[0]?.invokes === 'turbo gen component');
360
+ check('its scope is the workspace, not the repo', comp?.scope === path.join('packages', 'ui'));
361
+ check('build and test produced no playbook', !r.playbooks.some((p) => /build|test/.test(p.slug)));
362
+ }
363
+
364
+ process.stdout.write(
365
+ failed === 0
366
+ ? '✅ derive_playbooks: self-test passed\n'
367
+ : `❌ derive_playbooks: ${failed} failure(s)\n`,
368
+ );
369
+ return failed === 0 ? 0 : 1;
370
+ };
371
+
372
+ const main = (): number => {
373
+ const argv = process.argv.slice(2);
374
+ if (argv.includes('--self-test')) return selfTest();
375
+
376
+ const arg = (flag: string): string | null => {
377
+ const i = argv.indexOf(flag);
378
+ return i >= 0 && i + 1 < argv.length ? String(argv[i + 1]) : null;
379
+ };
380
+ const root = arg('--root') ?? '.';
381
+ const home = arg('--home') ?? path.join(root, DEFAULT_HOME);
382
+ const write = argv.includes('--write');
383
+
384
+ const report = derive(root);
385
+ if (report.playbooks.length === 0) {
386
+ process.stdout.write('derive_playbooks: no creation-shaped procedure found — nothing proposed.\n');
387
+ }
388
+ for (const p of report.playbooks) {
389
+ const target = path.join(home, `${p.slug}.md`);
390
+ process.stdout.write(
391
+ `${p.grade === 'configured' ? '✅' : '⚠️ '} ${target} grade=${p.grade} invokes=[${p.steps.map((s) => s.invokes).join(', ')}]\n`,
392
+ );
393
+ if (!write) continue;
394
+ fs.mkdirSync(home, { recursive: true });
395
+ fs.writeFileSync(target, renderPlaybook(p), 'utf8');
396
+ }
397
+ for (const o of report.out_of_scope) {
398
+ process.stdout.write(`ℹ️ out of scope for this release: ${o} — reported, not guessed at (ADR-244)\n`);
399
+ }
400
+ process.stdout.write(`scanned: ${report.playbooks.length}\n`);
401
+ return 0;
402
+ };
403
+
404
+ if (process.argv[1] && /derive_playbooks/.test(process.argv[1])) {
405
+ process.exit(main());
406
+ }
@@ -93,6 +93,13 @@ const FIXTURES: Record<string, { ext: string; positive: string; negative: string
93
93
  positive: "We streamline and empower your robust, world-class workflow.",
94
94
  negative: "We cut page load by 40% and remove three steps from checkout.",
95
95
  },
96
+ "slop-cp6-generic-art-direction": {
97
+ ext: "md",
98
+ positive: "Build a Three.js hero with floating abstract shapes and a gradient mesh.",
99
+ negative:
100
+ "Build a Three.js hero: the assembled chassis rotates, one component " +
101
+ "highlighted per beat, ending on the price panel.",
102
+ },
96
103
  "slop-cp5-emoji-ui": {
97
104
  ext: "html",
98
105
  positive: '<h1>🚀 Get Started</h1><button>✅ Track progress</button>',
@@ -175,6 +175,35 @@ const BUZZWORDS = [
175
175
  "leverage",
176
176
  ];
177
177
 
178
+ /**
179
+ * Stock render subjects — the default thing an image or 3D model becomes when
180
+ * nobody decided what it should be about.
181
+ *
182
+ * Same mechanism as `BUZZWORDS` (CP2), applied to art direction instead of
183
+ * prose: each entry names a *visual* that carries no subject matter. The
184
+ * delete-test is the same one — swap the phrase for the product's actual
185
+ * subject and the brief says more, which is the proof it said nothing.
186
+ *
187
+ * On the `copy` engine deliberately. A CSS engine cannot see subject matter:
188
+ * `transform: translateZ()` looks identical whether it moves a product or a
189
+ * glowing orb. The only detectable surface is the brief's own wording.
190
+ */
191
+ const STOCK_RENDER_SUBJECTS = [
192
+ "floating abstract shapes",
193
+ "abstract floating shapes",
194
+ "gradient mesh",
195
+ "glowing orb",
196
+ "glowing orbs",
197
+ "floating particles",
198
+ "particle field",
199
+ "abstract 3d shape",
200
+ "abstract 3d shapes",
201
+ "futuristic hud",
202
+ "digital landscape",
203
+ "flowing ribbons",
204
+ "liquid metal blob",
205
+ ];
206
+
178
207
  // ---------------------------------------------------------------------------
179
208
  // The registry
180
209
  // ---------------------------------------------------------------------------
@@ -314,9 +343,15 @@ export const SLOP_RULES: SlopRule[] = [
314
343
  catalogId: "T7",
315
344
  severity: "P2",
316
345
  engines: ["css"],
317
- description: "Default AI font (Inter/Roboto/DM Sans/Geist/Space Grotesk/Instrument Serif) without a brand reason",
346
+ description: "Default AI font (Inter/Roboto/DM Sans/Geist/Space Grotesk/Instrument Serif) without a stated reason",
347
+ // Register-scoped by the catalog, NOT by this matcher: a CSS text pass carries
348
+ // no register signal, so the scope lives in the rebuttal rather than in the
349
+ // gate. `design-modes.md` sanctions a single reliable family in the PRODUCT
350
+ // register, which makes "product register" a complete reason on its own — the
351
+ // message names it so a product surface knows the rebuttal without reading two
352
+ // guidelines. P2 is a rebuttable presumption (see the header), never a block.
318
353
  message:
319
- "This is a default AI-coding-tool font pick. Declare it in DESIGN.md with a reason, or choose deliberately (T7).",
354
+ "This is a default AI-coding-tool font pick. Declare it with a reason (T7) — in the product register, \"single reliable family\" IS the reason; state it in DESIGN.md or the surface brief.",
320
355
  gated: (ctx) => DEFAULT_FONTS.some((f) => ctx.has(f)),
321
356
  detect: ({ lines }) =>
322
357
  lines.flatMap((l, i) => {
@@ -416,6 +451,25 @@ export const SLOP_RULES: SlopRule[] = [
416
451
  return [];
417
452
  },
418
453
  },
454
+ {
455
+ id: "slop-cp6-generic-art-direction",
456
+ catalogId: "CP6",
457
+ severity: "P2",
458
+ engines: ["copy"],
459
+ description: "Stock render subject named in a brief instead of the product's own subject matter",
460
+ message:
461
+ "This names a default visual, not a subject — swap it for what the product " +
462
+ "actually shows and the brief says more (CP6).",
463
+ detect: ({ content, ext }) => {
464
+ const text = visibleText(content, ext);
465
+ const lower = text.toLowerCase();
466
+ const hits = STOCK_RENDER_SUBJECTS.filter((s) => lower.includes(s));
467
+ if (hits.length === 0) {
468
+ return [];
469
+ }
470
+ return [{ line: 1, snippet: hits.slice(0, 3).join(", ") }];
471
+ },
472
+ },
419
473
  {
420
474
  id: "slop-cp5-emoji-ui",
421
475
  catalogId: "CP5",
@@ -39,6 +39,7 @@
39
39
  * (bad ref, unreadable file, crash).
40
40
  */
41
41
 
42
+ import { independenceFields } from './_lib/review_independence.js';
42
43
  import { execFileSync } from 'node:child_process';
43
44
  import { createHash } from 'node:crypto';
44
45
  import * as fs from 'node:fs';
@@ -872,6 +873,21 @@ function findingsSkeleton(args: {
872
873
  return [
873
874
  `# Findings: ${args.slug}`,
874
875
  `<!-- completion-review: v1 | reviewed: ${args.reviewedDate} | scope: ${args.scopeHash} | diff: ${args.headSha} | reviewer: r2-fresh-subagent-${args.slug} | prompt_hash: ${args.promptHash} -->`,
876
+ // SECOND PRODUCER of the independence record — step 2.2 of
877
+ // road-to-review-independence. Until this line, `self_review_gate.ts` was the
878
+ // only producer, which is why `check_review_schema` reported `scanned: 1`: a
879
+ // schema gate with one producer checks that one producer agrees with itself.
880
+ //
881
+ // The values are derived, never chosen. `single-member` because this dispatch
882
+ // has exactly one reviewer; `fresh` because a fresh subagent is what this file
883
+ // dispatches and is the property the whole artifact exists to carry (the
884
+ // `reviewer:` field above already says so in prose). Both axes go in, and the
885
+ // derived pair follows: single-member + fresh is `provisional` / `single-pass`.
886
+ //
887
+ // Recording `provisional` on a fresh reviewer is not a downgrade — it is the
888
+ // honest reading. One reviewer is one reviewer, however uncontaminated; the
889
+ // family axis is what `accepted` needs and one member cannot supply it.
890
+ `<!-- ${JSON.stringify({ 'review-independence': independenceFields([`r2-fresh-subagent-${args.slug}`], 'fresh') })} -->`,
875
891
  // Set at CREATION, never inferred later: an inferred type reads filename
876
892
  // and location, which are exactly the signals that already fail to
877
893
  // distinguish an input from a binding. This artifact binds a scope from
@@ -0,0 +1,116 @@
1
+ /**
2
+ * frontend_corpus_hash — freeze and verify the frontend benchmark corpus.
3
+ *
4
+ * Step 0.2 of `road-to-frontend-power` registers a labelled frontend population
5
+ * and requires its hash to be committed BEFORE any commit that moves the
6
+ * detector engine. Risk 6 of that roadmap names the failure the hash guards
7
+ * against: "One effort ships both the engine and the corpus that scores it."
8
+ *
9
+ * The hash is the weaker half of that mitigation and this header says so
10
+ * plainly: it pins the population against LATER edits, so a number published
11
+ * against `<digest>` cannot be re-scored on a quietly-different corpus. It says
12
+ * nothing about who authored the corpus in the first place. The authorship
13
+ * caveat — and the resulting rule that no detector row may be promoted to
14
+ * `backed` against this population — lives in
15
+ * `tests/eval/frontend-corpus/README.md` and is not restated here.
16
+ *
17
+ * Determinism: files are enumerated depth-first with a sorted directory walk,
18
+ * paths are emitted POSIX-style relative to the corpus root, and content is
19
+ * hashed as raw bytes. `CORPUS.sha256` itself is excluded, so `--write`
20
+ * followed by `--check` is stable.
21
+ *
22
+ * Not a CI gate, deliberately. `tests/eval/frontend-corpus.test.ts` runs the
23
+ * `--check` path, which is the enforcement surface; a fourth ratchet-tripping
24
+ * gate script for a manifest one test can assert would cost more than it buys.
25
+ *
26
+ * Exit 0 = manifest matches (or was written). Exit 1 = drift.
27
+ */
28
+
29
+ import * as crypto from 'node:crypto';
30
+ import * as fs from 'node:fs';
31
+ import * as path from 'node:path';
32
+ import { fileURLToPath, pathToFileURL } from 'node:url';
33
+
34
+ const _HERE = fileURLToPath(import.meta.url);
35
+ const REPO = path.resolve(path.dirname(_HERE), '..', '..');
36
+ export const CORPUS_ROOT = path.join(REPO, 'tests', 'eval', 'frontend-corpus');
37
+ export const MANIFEST_NAME = 'CORPUS.sha256';
38
+ /** Subtrees carrying corpus content. Anything else at the root is prose. */
39
+ const CONTENT_DIRS = ['cases', 'near-miss'] as const;
40
+
41
+ /** Depth-first, sorted, POSIX-relative file list under `root`. */
42
+ export function walk(root: string, rel = ''): string[] {
43
+ const abs = rel ? path.join(root, rel) : root;
44
+ if (!fs.existsSync(abs)) return [];
45
+ const out: string[] = [];
46
+ for (const name of fs.readdirSync(abs).sort()) {
47
+ const childRel = rel ? `${rel}/${name}` : name;
48
+ if (fs.statSync(path.join(root, childRel)).isDirectory()) out.push(...walk(root, childRel));
49
+ else if (childRel !== MANIFEST_NAME) out.push(childRel);
50
+ }
51
+ return out;
52
+ }
53
+
54
+ /**
55
+ * Pure core: the manifest text for a set of (path, bytes) pairs. Pure so the
56
+ * ordering guarantee is testable without a corpus on disk.
57
+ */
58
+ export function manifestText(entries: ReadonlyArray<readonly [string, Buffer]>): string {
59
+ const lines = entries.map(
60
+ ([p, bytes]) => `${crypto.createHash('sha256').update(bytes).digest('hex')} ${p}`,
61
+ );
62
+ const roll = crypto.createHash('sha256').update(lines.join('\n')).digest('hex');
63
+ return [
64
+ '# frontend benchmark corpus — sha256 manifest',
65
+ '# Produced by src/scripts/frontend_corpus_hash.ts. Do not hand-edit.',
66
+ `# files: ${lines.length}`,
67
+ `# corpus: ${roll}`,
68
+ ...lines,
69
+ '',
70
+ ].join('\n');
71
+ }
72
+
73
+ export function buildManifest(root: string = CORPUS_ROOT): string {
74
+ const files = CONTENT_DIRS.flatMap((d) => walk(root, d));
75
+ return manifestText(files.map((p) => [p, fs.readFileSync(path.join(root, p))] as const));
76
+ }
77
+
78
+ /** The rolling `# corpus:` digest — the value a published number cites. */
79
+ export function corpusDigest(text: string): string | null {
80
+ return /^# corpus: ([0-9a-f]{64})$/m.exec(text)?.[1] ?? null;
81
+ }
82
+
83
+ function main(argv: readonly string[]): number {
84
+ const manifestPath = path.join(CORPUS_ROOT, MANIFEST_NAME);
85
+ const built = buildManifest();
86
+ const digest = corpusDigest(built);
87
+
88
+ if (argv.includes('--write')) {
89
+ fs.writeFileSync(manifestPath, built);
90
+ process.stdout.write(`✅ wrote ${MANIFEST_NAME} — corpus ${digest}\n`);
91
+ return 0;
92
+ }
93
+ if (argv.includes('--check')) {
94
+ if (!fs.existsSync(manifestPath)) {
95
+ console.error(`❌ ${MANIFEST_NAME} is missing — run with --write`);
96
+ return 1;
97
+ }
98
+ const onDisk = fs.readFileSync(manifestPath, 'utf8');
99
+ if (onDisk !== built) {
100
+ console.error(
101
+ `❌ corpus drift — manifest says ${corpusDigest(onDisk)}, tree hashes to ${digest}.\n` +
102
+ ' A case changed without a rehash. Rerun with --write, and re-publish any\n' +
103
+ ' number that cited the old digest rather than carrying it over.',
104
+ );
105
+ return 1;
106
+ }
107
+ process.stdout.write(`✅ corpus matches manifest — ${digest}\n`);
108
+ return 0;
109
+ }
110
+ process.stdout.write(built);
111
+ return 0;
112
+ }
113
+
114
+ if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
115
+ process.exit(main(process.argv.slice(2)));
116
+ }