@event4u/agent-config 14.9.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 (426) 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 +384 -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/async-python-patterns/SKILL.md +1 -1
  45. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  46. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  47. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  52. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  53. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  54. package/dist/agent-src/skills/code-refactoring/SKILL.md +1 -1
  55. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  56. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  57. package/dist/agent-src/skills/condense-memory/SKILL.md +4 -1
  58. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  59. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +4 -1
  60. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  61. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  62. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  63. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  64. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  65. package/dist/agent-src/skills/design-review/SKILL.md +80 -2
  66. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  67. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  68. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  69. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  70. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  71. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  72. package/dist/agent-src/skills/discovery-interview/SKILL.md +1 -1
  73. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  74. package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -1
  75. package/dist/agent-src/skills/editorial-calendar/SKILL.md +1 -1
  76. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  78. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  79. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  80. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  81. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  82. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  83. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  84. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  85. package/dist/agent-src/skills/github-ci/SKILL.md +1 -1
  86. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  87. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  88. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  89. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  90. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  91. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  92. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  93. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  94. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  95. package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
  96. package/dist/agent-src/skills/judge-injection-defense/SKILL.md +1 -1
  97. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  98. package/dist/agent-src/skills/laravel-validation/SKILL.md +1 -1
  99. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  100. package/dist/agent-src/skills/legal-intake-triage/SKILL.md +1 -1
  101. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +1 -1
  102. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  103. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  104. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  105. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  106. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  107. package/dist/agent-src/skills/messaging-architecture/SKILL.md +1 -1
  108. package/dist/agent-src/skills/migration-architect/SKILL.md +1 -1
  109. package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +1 -1
  110. package/dist/agent-src/skills/module-management/SKILL.md +1 -1
  111. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  112. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  113. package/dist/agent-src/skills/motion-choreographer/SKILL.md +7 -1
  114. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  115. package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
  116. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  117. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  118. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  119. package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
  120. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  121. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  122. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  123. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  124. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  125. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  126. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +1 -1
  127. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  128. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  129. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  130. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  131. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  132. package/dist/agent-src/skills/receiving-code-review/SKILL.md +3 -3
  133. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  134. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  135. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  136. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  137. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  138. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  139. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  140. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  141. package/dist/agent-src/skills/scene-expander/SKILL.md +1 -1
  142. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  143. package/dist/agent-src/skills/secrets-management/SKILL.md +5 -1
  144. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  145. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  146. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  147. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  148. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  149. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  150. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  151. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  152. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  153. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  154. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  155. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  156. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  157. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  158. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  159. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  160. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  161. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  162. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  163. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  164. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  165. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  166. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  167. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  168. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  169. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  170. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  171. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  172. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  173. package/dist/agent-src/templates/playbook.md +75 -0
  174. package/dist/agent-src/templates/roadmaps.md +37 -0
  175. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  176. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  177. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  178. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  179. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  180. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  181. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  182. package/dist/cli/commands/uiAudit.js +221 -0
  183. package/dist/cli/commands/uiAudit.js.map +1 -0
  184. package/dist/cli/commands/uiRender.js +206 -0
  185. package/dist/cli/commands/uiRender.js.map +1 -0
  186. package/dist/cli/main.js +22 -1
  187. package/dist/cli/main.js.map +1 -1
  188. package/dist/cli/mcp/content.js +64 -1
  189. package/dist/cli/mcp/content.js.map +1 -1
  190. package/dist/cli/mcp/dispatch.js +175 -10
  191. package/dist/cli/mcp/dispatch.js.map +1 -1
  192. package/dist/cli/registry.js +3 -0
  193. package/dist/cli/registry.js.map +1 -1
  194. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  195. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  196. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  197. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  198. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  199. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  200. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  201. package/dist/cli-delegate/cmd_conformance.js +3 -3
  202. package/dist/cli-delegate/cmd_doctor.js +3 -3
  203. package/dist/cli-delegate/cmd_export.js +4 -4
  204. package/dist/cli-delegate/cmd_handoff.js +1 -1
  205. package/dist/cli-delegate/cmd_preflight.js +2 -2
  206. package/dist/cli-delegate/cmd_prune.js +4 -4
  207. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  208. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  209. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  210. package/dist/cli-delegate/cmd_sync.js +4 -4
  211. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  212. package/dist/cli-delegate/cmd_validate.js +4 -4
  213. package/dist/discovery/deprecation-report.md +1 -1
  214. package/dist/discovery/discovery-manifest.json +380 -145
  215. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  216. package/dist/discovery/discovery-manifest.summary.md +4 -4
  217. package/dist/discovery/dormancy-report.md +1 -1
  218. package/dist/discovery/orphan-report.md +1 -1
  219. package/dist/discovery/packs.json +18 -8
  220. package/dist/discovery/trust-report.md +3 -3
  221. package/dist/discovery/workspaces.json +16 -6
  222. package/dist/hooks/dispatch.js +2337 -1436
  223. package/dist/install/install.mjs +795 -708
  224. package/dist/mcp/registry-manifest.json +3 -3
  225. package/dist/mcp/server.json +2 -2
  226. package/dist/mcp/server.mjs +1685 -1250
  227. package/dist/router.json +1 -1
  228. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  229. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  230. package/dist/scripts/_lib/scoped_projection.js +84 -0
  231. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  232. package/dist/scripts/_lib/ui_surface.js +89 -0
  233. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  234. package/dist/scripts/condense.js +6 -10
  235. package/dist/scripts/condense.js.map +1 -1
  236. package/dist/scripts/project_thin_rules.js +40 -2
  237. package/dist/scripts/project_thin_rules.js.map +1 -1
  238. package/dist/server/schemas/settings.js +3 -0
  239. package/dist/server/schemas/settings.js.map +1 -1
  240. package/dist/shared/skillRanking.js +159 -0
  241. package/dist/shared/skillRanking.js.map +1 -0
  242. package/docs/contracts/provider-lifecycle.md +47 -18
  243. package/docs/contracts/settings-classes.md +14 -4
  244. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  245. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  246. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  247. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  248. package/docs/guidelines/design-antipatterns.md +4 -2
  249. package/docs/guidelines/design-asset-discipline.md +38 -0
  250. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  251. package/docs/guidelines/design-handover-extraction.md +35 -5
  252. package/docs/guidelines/design-modes.md +41 -1
  253. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  254. package/llms.txt +50 -39
  255. package/package.json +1 -1
  256. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  257. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  258. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  259. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  260. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  261. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  262. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  263. package/src/config/agent-settings.template.yml +28 -0
  264. package/src/config/assurance-capability-registry.json +223 -0
  265. package/src/config/assurance-threshold-budget.json +65 -0
  266. package/src/config/ci-local-parity.yml +14 -0
  267. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  268. package/src/config/dispatch-economy-metrics.json +4 -2
  269. package/src/config/evaluator-budgets.json +14 -14
  270. package/src/config/gate-coverage.yml +227 -8
  271. package/src/config/gate-violation-baselines.json +76 -72
  272. package/src/config/hook-latency-budget.json +1 -0
  273. package/src/config/hook-token-budget.json +137 -125
  274. package/src/config/pack-size-budget.json +4 -2
  275. package/src/config/preamble-payload-budget.json +71 -67
  276. package/src/config/quorum-attendance-budget.json +12 -4
  277. package/src/config/rule-activation-census.json +7 -3
  278. package/src/config/rule-enforcement-baseline.json +0 -1
  279. package/src/config/rule-stub-ceilings.json +112 -91
  280. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  281. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  282. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  283. package/src/scripts/_dispatch.bash +17 -0
  284. package/src/scripts/_lib/as_of.ts +412 -0
  285. package/src/scripts/_lib/exec_evidence.ts +7 -0
  286. package/src/scripts/_lib/hook_settings.ts +33 -0
  287. package/src/scripts/_lib/host_listing_model.ts +192 -0
  288. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  289. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  290. package/src/scripts/_lib/review_independence.ts +62 -10
  291. package/src/scripts/_lib/rule_injection.ts +239 -0
  292. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  293. package/src/scripts/_lib/scoped_projection.ts +96 -0
  294. package/src/scripts/_lib/session_register.ts +44 -2
  295. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  296. package/src/scripts/_lib/transcript_reads.ts +207 -0
  297. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  298. package/src/scripts/_lib/ui_authority.ts +343 -0
  299. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  300. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  301. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  302. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  303. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  304. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  305. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  306. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  307. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  308. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  309. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  310. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  311. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  312. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  313. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  314. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  315. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  316. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  317. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  318. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  319. package/src/scripts/ai-video/stitch.sh +191 -10
  320. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  321. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  322. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  323. package/src/scripts/ai_council/clients.ts +3 -2
  324. package/src/scripts/ai_council/consensus.ts +63 -6
  325. package/src/scripts/ai_council/events_log.ts +46 -1
  326. package/src/scripts/ai_council/orchestrator.ts +103 -108
  327. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  328. package/src/scripts/ai_council/prompts.ts +12 -5
  329. package/src/scripts/ai_council/quorum.ts +82 -1
  330. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  331. package/src/scripts/ai_council/seating.ts +211 -0
  332. package/src/scripts/ai_council/stance_tally.ts +22 -0
  333. package/src/scripts/bench_hook_latency.ts +62 -0
  334. package/src/scripts/build_proof.ts +12 -1
  335. package/src/scripts/capture_skill_catalogue.ts +21 -0
  336. package/src/scripts/check_always_budget.ts +2 -1
  337. package/src/scripts/check_augmentignore.ts +2 -1
  338. package/src/scripts/check_beta_review_markers.ts +2 -1
  339. package/src/scripts/check_composite_arming.ts +364 -0
  340. package/src/scripts/check_corpus_staleness.ts +2 -1
  341. package/src/scripts/check_council_pin_staleness.ts +3 -1
  342. package/src/scripts/check_enforcement_coverage.ts +116 -3
  343. package/src/scripts/check_enforcement_denominator.ts +365 -0
  344. package/src/scripts/check_gate_coverage.ts +2 -1
  345. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  346. package/src/scripts/check_knowledge_cards.ts +2 -1
  347. package/src/scripts/check_knowledge_pages.ts +3 -2
  348. package/src/scripts/check_memory.ts +2 -1
  349. package/src/scripts/check_no_automerge_key.ts +287 -0
  350. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  351. package/src/scripts/check_pack_passport_reconciliation.ts +274 -0
  352. package/src/scripts/check_proposal.ts +2 -1
  353. package/src/scripts/check_reach_staleness.ts +2 -1
  354. package/src/scripts/check_release_adjacent_health.ts +2 -1
  355. package/src/scripts/check_release_pr_shape.ts +5 -0
  356. package/src/scripts/check_review_schema.ts +34 -1
  357. package/src/scripts/check_roadmap_trackable.ts +48 -3
  358. package/src/scripts/check_source_size_budget.ts +2 -1
  359. package/src/scripts/check_standing_payload_delta.ts +505 -0
  360. package/src/scripts/check_trigger_evals.ts +2 -1
  361. package/src/scripts/classify_change_risk.ts +281 -0
  362. package/src/scripts/compute_skill_tiers.ts +186 -0
  363. package/src/scripts/condense.ts +8 -13
  364. package/src/scripts/council_attendance_metrics.ts +183 -0
  365. package/src/scripts/council_cli.ts +10 -8
  366. package/src/scripts/council_parse_rate.ts +116 -0
  367. package/src/scripts/derive_playbooks.ts +406 -0
  368. package/src/scripts/design_slop_rules.test.ts +7 -0
  369. package/src/scripts/design_slop_rules.ts +56 -2
  370. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  371. package/src/scripts/frontend_corpus_hash.ts +116 -0
  372. package/src/scripts/generate_pack_manifests.ts +78 -0
  373. package/src/scripts/grade_target_readiness.ts +478 -0
  374. package/src/scripts/hook_manifest.json +1 -1
  375. package/src/scripts/hook_manifest.yaml +97 -16
  376. package/src/scripts/hooks/concern_registry.ts +4 -0
  377. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  378. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  379. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  380. package/src/scripts/hot_context_hook.ts +74 -7
  381. package/src/scripts/install.ts +40 -89
  382. package/src/scripts/lint_adapter_tier.ts +663 -0
  383. package/src/scripts/lint_archived_skills.ts +93 -0
  384. package/src/scripts/lint_budget_ownership.ts +2 -1
  385. package/src/scripts/lint_deterministic_time.ts +470 -0
  386. package/src/scripts/lint_framework_leakage.ts +5 -0
  387. package/src/scripts/lint_handoffs.ts +525 -1
  388. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  389. package/src/scripts/lint_marketplace.ts +88 -0
  390. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  391. package/src/scripts/lint_one_off_age.ts +6 -1
  392. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  393. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  394. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  395. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  396. package/src/scripts/lint_spawn_payload.ts +42 -5
  397. package/src/scripts/lint_symptom_intake.ts +2 -1
  398. package/src/scripts/mcp_parity_smoke.ts +30 -4
  399. package/src/scripts/mcp_render.ts +66 -1
  400. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  401. package/src/scripts/measure_risk_class_drift.ts +140 -0
  402. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  403. package/src/scripts/media/lib/adapter-common.sh +211 -1
  404. package/src/scripts/media/lib/adapter-contract.md +74 -6
  405. package/src/scripts/model_rule_injection.ts +907 -0
  406. package/src/scripts/project_thin_rules.ts +45 -2
  407. package/src/scripts/release.ts +66 -860
  408. package/src/scripts/release_drill.ts +59 -3
  409. package/src/scripts/release_env.ts +298 -0
  410. package/src/scripts/release_publication.ts +720 -0
  411. package/src/scripts/roadmap_context.ts +778 -0
  412. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  413. package/src/scripts/schemas/pack.schema.json +62 -4
  414. package/src/scripts/schemas/rule.schema.json +24 -2
  415. package/src/scripts/schemas/skill.schema.json +41 -2
  416. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  417. package/src/scripts/session_register_hook.ts +29 -3
  418. package/src/scripts/sessions_cli.ts +67 -3
  419. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  420. package/src/scripts/tighten_skill_descriptions.ts +264 -0
  421. package/src/scripts/token_sink_report.ts +239 -0
  422. package/src/scripts/trigger_coverage.ts +122 -3
  423. package/src/scripts/update_counts.ts +7 -3
  424. package/src/scripts/validate_frontmatter.ts +66 -0
  425. package/src/server/schemas/settings.ts +5 -0
  426. package/src/shared/skillRanking.ts +183 -0
@@ -13,6 +13,15 @@
13
13
  * .windsurf/mcp.json (in-project)
14
14
  * ~/.config/claude-desktop/claude_desktop_config.json (user, opt-in)
15
15
  *
16
+ * `.mcp.json` — Claude Code's project-scope config — is a CHECK-ONLY target
17
+ * (Phase 1.3 of `road-to-skill-delivery-over-mcp`). It is deliberately not
18
+ * rendered: the installer writes it with `merge_json_file`, which touches only
19
+ * this package's own server key and records it in `agents/installed-tools.lock`
20
+ * so uninstall subtracts exactly that key. Rendering it here would overwrite a
21
+ * file the consumer also edits and silently delete servers they added by hand.
22
+ * `--check` therefore asserts CONTAINMENT of our entry rather than equality with
23
+ * a rendering, and says so on the same line.
24
+ *
16
25
  * All targets use the same `mcpServers` top-level key. The source file uses
17
26
  * `servers` to keep our internal schema stable if a downstream format ever
18
27
  * diverges.
@@ -40,6 +49,36 @@ export function default_project_root(): string {
40
49
  return fs.realpathSync(process.cwd());
41
50
  }
42
51
 
52
+ /** The one entry `.mcp.json` must contain. Mirrors `install.ts:MCP_SERVER_KEY`. */
53
+ export const MCP_JSON_SERVER_KEY = 'agent-config';
54
+
55
+ /** Claude Code's project-scope MCP config. Checked here, written by the installer. */
56
+ export function claude_code_mcp_target(projectRoot: string): string {
57
+ return path.join(projectRoot, '.mcp.json');
58
+ }
59
+
60
+ /**
61
+ * Does `.mcp.json` carry this package's server entry?
62
+ *
63
+ * `absent` is a distinct verdict from `missing-entry` on purpose: absent means
64
+ * the installer has not run for `claude-code` here, which is a hint; a file that
65
+ * exists WITHOUT our key means something removed it, which is a failure.
66
+ */
67
+ export function check_mcp_json(projectRoot: string): 'absent' | 'ok' | 'missing-entry' | 'unreadable' {
68
+ const target = claude_code_mcp_target(projectRoot);
69
+ if (!fs.existsSync(target)) return 'absent';
70
+ let parsed: unknown;
71
+ try {
72
+ parsed = JSON.parse(fs.readFileSync(target, 'utf-8'));
73
+ } catch {
74
+ return 'unreadable';
75
+ }
76
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return 'unreadable';
77
+ const servers = (parsed as Record<string, unknown>)['mcpServers'];
78
+ if (servers === null || typeof servers !== 'object' || Array.isArray(servers)) return 'missing-entry';
79
+ return MCP_JSON_SERVER_KEY in (servers as Record<string, unknown>) ? 'ok' : 'missing-entry';
80
+ }
81
+
43
82
  export function in_project_targets(projectRoot: string): Record<string, string> {
44
83
  return {
45
84
  cursor: path.join(projectRoot, '.cursor', 'mcp.json'),
@@ -274,7 +313,33 @@ function cmd_check(args: ParsedArgs): number {
274
313
  return 1;
275
314
  }
276
315
  process.stdout.write('✅ All MCP targets match source.\n');
277
- return 0;
316
+ return _report_mcp_json(projectRoot);
317
+ }
318
+
319
+ /**
320
+ * The `.mcp.json` leg of `--check`. Additive: it can only turn a green run red
321
+ * when the file EXISTS and has lost our entry, never on a fresh tree where the
322
+ * installer has not run.
323
+ */
324
+ function _report_mcp_json(projectRoot: string): number {
325
+ const verdict = check_mcp_json(projectRoot);
326
+ const rel = path.relative(projectRoot, claude_code_mcp_target(projectRoot)) || '.mcp.json';
327
+ if (verdict === 'ok') {
328
+ process.stdout.write(`✅ ${rel} carries the \`${MCP_JSON_SERVER_KEY}\` server (containment, not equality).\n`);
329
+ return 0;
330
+ }
331
+ if (verdict === 'absent') {
332
+ process.stdout.write(
333
+ `ℹ️ ${rel} not present — written by \`agent-config install\` for the ` +
334
+ 'claude-code tool, never rendered from mcp.json.\n',
335
+ );
336
+ return 0;
337
+ }
338
+ process.stderr.write(
339
+ `❌ ${rel} exists but does not carry the \`${MCP_JSON_SERVER_KEY}\` server ` +
340
+ `(${verdict}). Re-run \`agent-config install\`.\n`,
341
+ );
342
+ return 1;
278
343
  }
279
344
 
280
345
  class ArgError extends Error {}
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Fingerprint store for third-party MCP tool definitions.
3
+ *
4
+ * Step 1.1 of `road-to-mcp-runtime-integrity`. The threat is a **rug-pull**: a
5
+ * third-party MCP server whose tool definition is benign at connection time and
6
+ * mutated afterwards, so the description the model reasons about is no longer the
7
+ * description that was reviewed. Nothing in this tree detected that:
8
+ * `lint_mcp_config_security.ts` reads shipped CONFIG for supply-chain smells and
9
+ * never sees a definition change after connection, and `audit_mcp_tools.ts` is an
10
+ * inventory generator for THIS package's own consumer catalog.
11
+ *
12
+ * WHAT THIS FILE IS NOT, and the distinction is load-bearing. It is a store and a
13
+ * comparison, bound to **no hook slot**. The roadmap's `## The no-silent-downgrade
14
+ * rule` forbids shipping a `post_tool_use` or session-start variant — a check that
15
+ * fires after first use is a post-mortem, not a control, for a tool with
16
+ * irreversible side effects — and the pre-use slot is gated on a per-turn
17
+ * composite ceiling that is not armed (`hook-latency-budget.json`,
18
+ * `observe_only: true`, `p50_ci: null`). So the placement decision is deliberately
19
+ * absent here rather than quietly resolved: **rug-pull protection remains absent
20
+ * and the protection level is zero** until an owner records which side of that
21
+ * trade-off they chose. Wiring this module into a slot without that record is the
22
+ * silent downgrade the roadmap binds itself against.
23
+ *
24
+ * THIRD-PARTY ONLY. This package's own consumer catalog is already covered by
25
+ * `audit_mcp_tools.ts`; fingerprinting it here would produce two sources of truth
26
+ * for the same tools.
27
+ *
28
+ * KEYED BY SERVER PLUS TOOL NAME, so a renamed tool reads as a NEW tool rather
29
+ * than as a mutation of the old one. That is the difference between "this server
30
+ * shipped a new tool" and "this server changed a tool you already approved", and
31
+ * collapsing them is how a mismatch surface becomes noise.
32
+ *
33
+ * A FIRST SIGHTING IS RECORDED, NOT REPORTED. Third-party servers legitimately
34
+ * add tools; reporting every first sighting with the same weight as a mutation
35
+ * trains the reader to ignore the surface, and noise on a security surface is
36
+ * worse than silence because it reads as coverage (Risk 4 in the roadmap).
37
+ *
38
+ * PII-exclusion-by-construction: a record holds a server id, a tool name, a hex
39
+ * digest and an ISO date. There is NO field capable of holding the description
40
+ * text, the schema, or any free-form content — the digest is one-way and the
41
+ * inputs are not retained. Never widen it.
42
+ */
43
+ import crypto from 'node:crypto';
44
+ import fs from 'node:fs';
45
+ import path from 'node:path';
46
+
47
+ import { update_json_under_lock } from './hooks/state_io.js';
48
+
49
+ /** Where the store lives, relative to the workspace root. */
50
+ export const FINGERPRINT_STORE = path.join(
51
+ 'agents',
52
+ 'runtime',
53
+ 'state',
54
+ 'mcp-tool-fingerprints.json',
55
+ );
56
+
57
+ export const SCHEMA_VERSION = 1;
58
+
59
+ /**
60
+ * The three fields a rug-pull can mutate to change what the model reasons about.
61
+ *
62
+ * `name` is included even though it is also half the key: a tool whose key is
63
+ * `server/x` and whose declared `name` later reads `y` is a mismatch, not a
64
+ * rename, and leaving `name` out of the digest would let that pass.
65
+ */
66
+ export interface McpToolDefinition {
67
+ readonly name: string;
68
+ readonly description?: string;
69
+ readonly inputSchema?: unknown;
70
+ }
71
+
72
+ export interface FingerprintRecord {
73
+ /** Hex sha256 of the canonical definition. */
74
+ readonly digest: string;
75
+ /** ISO date (day resolution — a timestamp would be a behavioural fingerprint). */
76
+ readonly first_seen: string;
77
+ }
78
+
79
+ export type FingerprintOutcome =
80
+ /** No record existed. Written silently; nothing to report. */
81
+ | { readonly kind: 'first-sighting'; readonly key: string; readonly digest: string }
82
+ /** A record existed and matches. Nothing to report. */
83
+ | { readonly kind: 'unchanged'; readonly key: string; readonly digest: string }
84
+ /** A record existed and does NOT match. This is the surface. */
85
+ | {
86
+ readonly kind: 'mismatch';
87
+ readonly key: string;
88
+ readonly digest: string;
89
+ readonly recorded: string;
90
+ readonly first_seen: string;
91
+ };
92
+
93
+ /**
94
+ * The store key. Server id and tool name, separated by a character neither may
95
+ * contain unescaped, so two distinct pairs cannot collide onto one key — the
96
+ * same failure `session_state_file` exists to prevent for session ids.
97
+ */
98
+ export function fingerprintKey(server: string, toolName: string): string {
99
+ const esc = (s: string): string => s.replace(/%/g, '%25').replace(/\//g, '%2F');
100
+ return `${esc(server)}/${esc(toolName)}`;
101
+ }
102
+
103
+ /**
104
+ * Canonical JSON with object keys sorted at every depth.
105
+ *
106
+ * Without this the digest is a fingerprint of the SERIALISER's key order rather
107
+ * than of the definition, and a server that reorders its schema keys between two
108
+ * connections would read as a rug-pull. Arrays keep their order — order is
109
+ * meaningful in a schema's `required` list and reordering it IS a change.
110
+ */
111
+ export function canonicalize(value: unknown): unknown {
112
+ if (Array.isArray(value)) return value.map(canonicalize);
113
+ if (value !== null && typeof value === 'object') {
114
+ const out: Record<string, unknown> = {};
115
+ for (const k of Object.keys(value as Record<string, unknown>).sort()) {
116
+ out[k] = canonicalize((value as Record<string, unknown>)[k]);
117
+ }
118
+ return out;
119
+ }
120
+ return value;
121
+ }
122
+
123
+ /**
124
+ * Hex sha256 over the canonical form of the three mutable fields.
125
+ *
126
+ * `description` and `inputSchema` are normalised to `null` when absent so that
127
+ * "absent" and "explicitly null" hash alike — a server toggling between the two
128
+ * has changed nothing the model can read, and reporting it would be noise.
129
+ */
130
+ export function fingerprintDefinition(def: McpToolDefinition): string {
131
+ const canonical = canonicalize({
132
+ name: def.name,
133
+ description: def.description ?? null,
134
+ inputSchema: def.inputSchema ?? null,
135
+ });
136
+ return crypto.createHash('sha256').update(JSON.stringify(canonical), 'utf-8').digest('hex');
137
+ }
138
+
139
+ interface StoreDoc {
140
+ schema_version: number;
141
+ tools: Record<string, FingerprintRecord>;
142
+ }
143
+
144
+ function emptyDoc(): StoreDoc {
145
+ return { schema_version: SCHEMA_VERSION, tools: {} };
146
+ }
147
+
148
+ /** Read the store. A missing, unreadable or malformed store reads as empty. */
149
+ export function readStore(workspaceRoot: string): StoreDoc {
150
+ try {
151
+ const decoded: unknown = JSON.parse(
152
+ fs.readFileSync(path.join(workspaceRoot, FINGERPRINT_STORE), 'utf-8'),
153
+ );
154
+ if (typeof decoded === 'object' && decoded !== null && !Array.isArray(decoded)) {
155
+ const tools = (decoded as Record<string, unknown>)['tools'];
156
+ return {
157
+ schema_version: SCHEMA_VERSION,
158
+ tools:
159
+ typeof tools === 'object' && tools !== null && !Array.isArray(tools)
160
+ ? (tools as Record<string, FingerprintRecord>)
161
+ : {},
162
+ };
163
+ }
164
+ } catch {
165
+ // Absent or malformed both mean "nothing recorded", and the outcome that
166
+ // follows is `first-sighting` — which reports nothing. A corrupt store
167
+ // must never be read as a mismatch: that would turn one bad write into a
168
+ // rug-pull alert on every tool at once.
169
+ }
170
+ return emptyDoc();
171
+ }
172
+
173
+ /**
174
+ * Record `def` for `server`, and say what the record implies.
175
+ *
176
+ * Read-modify-write under ONE lock, not three steps: two hosts connected to the
177
+ * same workspace fingerprint concurrently, and `atomic_write_json` makes the
178
+ * publish atomic while leaving load -> merge -> publish racy — which loses a
179
+ * whole tool's record rather than merely an increment, because this mutator
180
+ * republishes the `tools` map. Same primitive and same reason as
181
+ * `rule-trips.json`.
182
+ *
183
+ * `today` is injected rather than read from the clock so a caller (and a test)
184
+ * can pin it; nothing in this module reads wall time on its own.
185
+ */
186
+ export function recordFingerprint(
187
+ workspaceRoot: string,
188
+ server: string,
189
+ def: McpToolDefinition,
190
+ today: string,
191
+ ): FingerprintOutcome {
192
+ const key = fingerprintKey(server, def.name);
193
+ const digest = fingerprintDefinition(def);
194
+ let outcome: FingerprintOutcome = { kind: 'first-sighting', key, digest };
195
+
196
+ update_json_under_lock<StoreDoc>(path.join(workspaceRoot, FINGERPRINT_STORE), (loaded) => {
197
+ const doc: StoreDoc = { ...emptyDoc(), ...((loaded ?? {}) as Partial<StoreDoc>) };
198
+ const tools: Record<string, FingerprintRecord> =
199
+ typeof doc.tools === 'object' && doc.tools !== null && !Array.isArray(doc.tools)
200
+ ? { ...doc.tools }
201
+ : {};
202
+ const prior = tools[key];
203
+ if (prior === undefined) {
204
+ tools[key] = { digest, first_seen: today };
205
+ outcome = { kind: 'first-sighting', key, digest };
206
+ } else if (prior.digest === digest) {
207
+ outcome = { kind: 'unchanged', key, digest };
208
+ } else {
209
+ // The recorded digest is NOT overwritten. A mismatch is a surface for
210
+ // a human decision, not a verdict, and silently re-baselining would
211
+ // make the second read of a mutated tool report `unchanged` — which
212
+ // is the one outcome a rug-pull must never produce.
213
+ outcome = {
214
+ kind: 'mismatch',
215
+ key,
216
+ digest,
217
+ recorded: prior.digest,
218
+ first_seen: prior.first_seen,
219
+ };
220
+ }
221
+ return { schema_version: SCHEMA_VERSION, tools };
222
+ });
223
+
224
+ return outcome;
225
+ }
226
+
227
+ /**
228
+ * Human-readable line for a mismatch, and nothing for the other two outcomes.
229
+ *
230
+ * Returning `null` rather than an empty string is deliberate: a caller that
231
+ * prints whatever it gets back cannot accidentally emit a blank line for a
232
+ * first sighting, which is the shape of "reported silently".
233
+ */
234
+ export function describeOutcome(outcome: FingerprintOutcome): string | null {
235
+ if (outcome.kind !== 'mismatch') return null;
236
+ return (
237
+ `mcp tool definition changed since it was first recorded: ${outcome.key} ` +
238
+ `(recorded ${outcome.recorded.slice(0, 12)} on ${outcome.first_seen}, ` +
239
+ `now ${outcome.digest.slice(0, 12)}) — review the change before using this tool`
240
+ );
241
+ }
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Standing R3-rate metric over recently merged changes.
4
+ *
5
+ * Step 3.1 of `road-to-target-project-assurance-readiness`, and it exists because of
6
+ * Risk 2 rather than for its own sake: `classify_change_risk` resolves ties and
7
+ * unknowns **upward**, which is safe per change and corrosive in aggregate — once
8
+ * most changes read R3, the owed gate set is noise and gets worked around rather
9
+ * than met. The roadmap's mitigation is explicit about where the defect would live:
10
+ * **> 40 % of changes classifying R3 is a defect in the override list**, not in the
11
+ * people meeting the gates. This is the instrument that can say so.
12
+ *
13
+ * WHAT IT CANNOT REPORT, and the null is recorded per row rather than omitted.
14
+ * The step asks for `agreement` and `r3_recall` alongside the rate. Both are
15
+ * measured against a **human-labelled** corpus, and step 0.2 recorded that no human
16
+ * labeller is reachable — so those two fields are written as `null` with
17
+ * `null_reason` naming why. Writing them absent would make a later reader unable to
18
+ * distinguish "not measured" from "measured as zero", which on a recall metric is
19
+ * the difference between no data and total failure.
20
+ *
21
+ * NIGHTLY, NOT PER-CHANGE. The classifier runs in-session per change; the
22
+ * *measurement* runs on a schedule. A per-PR trigger would make the metric a gate,
23
+ * and Phase 2 — the phase that gives the class authority — is cancelled on the
24
+ * pre-registered null route precisely because the class is unvalidated.
25
+ */
26
+ import { spawnSync } from 'node:child_process';
27
+ import * as fs from 'node:fs';
28
+ import * as path from 'node:path';
29
+ import process from 'node:process';
30
+ import { fileURLToPath } from 'node:url';
31
+
32
+ import { type RiskClass, classifyPaths } from './classify_change_risk.js';
33
+
34
+ const _HERE = fileURLToPath(import.meta.url);
35
+ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
36
+ export const DRIFT_LEDGER = path.join('agents', 'evidence', 'risk-classifier-drift.jsonl');
37
+
38
+ /** The rate above which the override list — not the reviewer — is the defect. */
39
+ export const R3_RATE_DEFECT_THRESHOLD = 0.4;
40
+
41
+ export interface DriftRow {
42
+ measured_at: string;
43
+ window_days: number;
44
+ commits: number;
45
+ counts: Record<RiskClass, number>;
46
+ r3_rate: number | null;
47
+ r3_rate_over_threshold: boolean | null;
48
+ agreement: null;
49
+ r3_recall: null;
50
+ null_reason: string;
51
+ }
52
+
53
+ /** One entry per merged commit in the window: its sha and its touched paths. */
54
+ export function commitsInWindow(days: number, cwd = REPO_ROOT): { sha: string; paths: string[] }[] {
55
+ const log = spawnSync(
56
+ 'git',
57
+ ['log', '--first-parent', `--since=${String(days)} days ago`, '--format=%H'],
58
+ { cwd, encoding: 'utf-8', maxBuffer: 64 * 1024 * 1024 },
59
+ );
60
+ if (log.status !== 0) return [];
61
+ const shas = (log.stdout || '').split('\n').map((s) => s.trim()).filter(Boolean);
62
+ const out: { sha: string; paths: string[] }[] = [];
63
+ for (const sha of shas) {
64
+ // `-m --first-parent` so a merge commit reports the paths it BROUGHT IN
65
+ // rather than nothing at all — a merge-heavy history would otherwise read
66
+ // as a stream of empty diffs, and an empty diff classifies R3 by the
67
+ // upward rule, which would invent an R3 rate of 1.0 out of bookkeeping.
68
+ const d = spawnSync('git', ['show', '--pretty=', '--name-only', '-m', '--first-parent', sha], {
69
+ cwd,
70
+ encoding: 'utf-8',
71
+ maxBuffer: 64 * 1024 * 1024,
72
+ });
73
+ const paths = (d.stdout || '').split('\n').map((s) => s.trim()).filter(Boolean);
74
+ if (paths.length > 0) out.push({ sha, paths });
75
+ }
76
+ return out;
77
+ }
78
+
79
+ export function buildRow(
80
+ commits: readonly { sha: string; paths: string[] }[],
81
+ measured_at: string,
82
+ window_days: number,
83
+ ): DriftRow {
84
+ const counts: Record<RiskClass, number> = { R0: 0, R1: 0, R2: 0, R3: 0 };
85
+ for (const c of commits) counts[classifyPaths(c.paths).cls] += 1;
86
+ const n = commits.length;
87
+ const r3_rate = n === 0 ? null : counts.R3 / n;
88
+ return {
89
+ measured_at,
90
+ window_days,
91
+ commits: n,
92
+ counts,
93
+ r3_rate,
94
+ // `null` rather than `false` on an empty window: no commits is not evidence
95
+ // that the rate is acceptable.
96
+ r3_rate_over_threshold: r3_rate === null ? null : r3_rate > R3_RATE_DEFECT_THRESHOLD,
97
+ agreement: null,
98
+ r3_recall: null,
99
+ null_reason:
100
+ 'no human-labelled corpus: step 0.2 recorded no reachable labeller, so ' +
101
+ 'agreement and r3_recall have no reference standard to be measured against. ' +
102
+ 'Reopens per agents/evidence/risk-classifier-prereg.md § Re-open threshold.',
103
+ };
104
+ }
105
+
106
+ export function main(argv: readonly string[]): number {
107
+ const wIdx = argv.indexOf('--window-days');
108
+ const window_days = wIdx >= 0 ? Number(argv[wIdx + 1] ?? '30') : 30;
109
+ const nowIdx = argv.indexOf('--now');
110
+ // Injected rather than read from the clock so a test can pin the row.
111
+ const measured_at = nowIdx >= 0 ? String(argv[nowIdx + 1]) : new Date().toISOString();
112
+ const dry = argv.includes('--dry-run');
113
+
114
+ const commits = commitsInWindow(window_days);
115
+ const row = buildRow(commits, measured_at, window_days);
116
+ const line = JSON.stringify(row);
117
+
118
+ if (dry) {
119
+ process.stdout.write(line + '\n');
120
+ return 0;
121
+ }
122
+ const target = path.join(REPO_ROOT, DRIFT_LEDGER);
123
+ fs.mkdirSync(path.dirname(target), { recursive: true });
124
+ fs.appendFileSync(target, line + '\n', 'utf-8');
125
+ process.stdout.write(
126
+ `risk-class drift: ${String(row.commits)} commit(s) over ${String(window_days)}d — ` +
127
+ `R3 ${String(row.counts.R3)} (rate ${row.r3_rate === null ? 'n/a' : row.r3_rate.toFixed(3)})` +
128
+ `${row.r3_rate_over_threshold === true ? ' ⚠️ OVER THRESHOLD — the override list is the defect' : ''}\n`,
129
+ );
130
+ return 0;
131
+ }
132
+
133
+ function _isCliEntry(): boolean {
134
+ const invoked = process.argv[1];
135
+ return invoked !== undefined && path.resolve(invoked) === path.resolve(_HERE);
136
+ }
137
+
138
+ if (_isCliEntry()) {
139
+ process.exit(main(process.argv.slice(2)));
140
+ }
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * measure_skill_ranker_baseline — the number Phase 3 has to beat.
4
+ *
5
+ * `road-to-skill-delivery-over-mcp` Phase 0.3 asked for a top-1 / top-3 hit
6
+ * rate for the incumbent keyword ranker, "over the 496-line corpus … against
7
+ * the corpus's expected skill per line". Building it surfaced two defects in
8
+ * that premise, and this file is written around them rather than over them.
9
+ *
10
+ * DEFECT A — the 496-line corpus has no expected SKILL. `tests/eval/routing-matrix/`
11
+ * is keyed by `rule:` and every prompt is labelled with the RULE it should
12
+ * activate. Scoring a skill ranker against it would need a skill label that
13
+ * does not exist there, so a "hit rate" over it would be invented.
14
+ *
15
+ * DEFECT B — 496 is stale. The matrix holds 499 prompts today
16
+ * (`grep -h "^\s*- prompt:" tests/eval/routing-matrix/*.yaml | wc -l`), and 496
17
+ * is still published in six places including `hook-token-budget.json`.
18
+ *
19
+ * So the baseline is measured against the corpus that DOES carry expected
20
+ * skills — `tests/eval/corpus-{dev,non-dev}.yaml`, 26 labelled prompts — and
21
+ * the routing matrix is reported alongside it as what it can honestly be: a
22
+ * COVERAGE measurement (does the ranker return anything at all, and how
23
+ * confident) with no ground truth and therefore no accuracy.
24
+ *
25
+ * Both halves are emitted in one JSON object so a later ranker revision can be
26
+ * compared on the same two axes in one run:
27
+ *
28
+ * ./scripts-run src/scripts/measure_skill_ranker_baseline --json
29
+ * ./scripts-run src/scripts/measure_skill_ranker_baseline --ranker keyword-v2 --json
30
+ *
31
+ * A 26-prompt denominator is small and is stated as such in the output. It is
32
+ * the whole labelled ground truth this repository has.
33
+ */
34
+
35
+ import * as fs from 'node:fs';
36
+ import * as path from 'node:path';
37
+ import { fileURLToPath } from 'node:url';
38
+
39
+ import { rank } from './skill_tools/score_skill_relevance.js';
40
+ import type { RankOptions } from '../shared/skillRanking.js';
41
+
42
+ export const REPO = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..');
43
+ const LABELLED_CORPORA = ['tests/eval/corpus-dev.yaml', 'tests/eval/corpus-non-dev.yaml'];
44
+ const MATRIX_DIR = 'tests/eval/routing-matrix';
45
+ /** Where the projected skills live in this checkout. */
46
+ export const SKILLS_DIR = path.join(REPO, 'src', 'skills');
47
+
48
+ export interface LabelledPrompt {
49
+ id: string;
50
+ corpus: string;
51
+ prompt: string;
52
+ expected: string[];
53
+ }
54
+
55
+ /**
56
+ * Minimal reader for the two eval corpora. They are hand-written YAML with a
57
+ * fixed two-space list shape; a full YAML parse is avoided so this script has
58
+ * the same zero-dependency profile as the ranker it measures.
59
+ */
60
+ export function readLabelledPrompts(repo = REPO): LabelledPrompt[] {
61
+ const out: LabelledPrompt[] = [];
62
+ for (const rel of LABELLED_CORPORA) {
63
+ const file = path.join(repo, rel);
64
+ if (!fs.existsSync(file)) continue;
65
+ const corpus = path.basename(rel, '.yaml');
66
+ let id = '';
67
+ let prompt = '';
68
+ for (const raw of fs.readFileSync(file, 'utf8').split('\n')) {
69
+ const line = raw.replace(/\r$/, '');
70
+ if (/^\s*#/.test(line)) continue;
71
+ const mId = /^\s*-\s+id:\s*(\S+)\s*$/.exec(line);
72
+ if (mId) {
73
+ id = mId[1]!;
74
+ prompt = '';
75
+ continue;
76
+ }
77
+ const mPrompt = /^\s*prompt:\s*"(.*)"\s*$/.exec(line);
78
+ if (mPrompt) {
79
+ prompt = mPrompt[1]!;
80
+ continue;
81
+ }
82
+ const mExp = /^\s*expected_skills:\s*\[(.*)\]\s*$/.exec(line);
83
+ if (mExp && id && prompt) {
84
+ const expected = mExp[1]!
85
+ .split(',')
86
+ .map((s) => s.trim())
87
+ .filter(Boolean);
88
+ out.push({ id, corpus, prompt, expected });
89
+ id = '';
90
+ prompt = '';
91
+ }
92
+ }
93
+ }
94
+ return out;
95
+ }
96
+
97
+ /** Every `- prompt:` string in the rule routing matrix. No skill label exists. */
98
+ export function readMatrixPrompts(repo = REPO): string[] {
99
+ const dir = path.join(repo, MATRIX_DIR);
100
+ if (!fs.existsSync(dir)) return [];
101
+ const out: string[] = [];
102
+ for (const name of fs.readdirSync(dir).sort()) {
103
+ if (!name.endsWith('.yaml')) continue;
104
+ for (const raw of fs.readFileSync(path.join(dir, name), 'utf8').split('\n')) {
105
+ const m = /^\s*-?\s*prompt:\s*"(.*)"\s*$/.exec(raw.replace(/\r$/, ''));
106
+ if (m && m[1]) out.push(m[1]);
107
+ }
108
+ }
109
+ return out;
110
+ }
111
+
112
+ export interface AccuracyArm {
113
+ corpus_prompts: number;
114
+ top1: number;
115
+ top3: number;
116
+ misses: string[];
117
+ denominator_note: string;
118
+ }
119
+
120
+ export interface CoverageArm {
121
+ corpus_prompts: number;
122
+ prompts_with_any_result: number;
123
+ mean_top_score: number;
124
+ note: string;
125
+ }
126
+
127
+ export interface RankerBaseline {
128
+ schema: 1;
129
+ ranker: string;
130
+ commit: string;
131
+ skills_dir: string;
132
+ skills_indexed: number;
133
+ accuracy: AccuracyArm;
134
+ matrix_coverage: CoverageArm;
135
+ }
136
+
137
+ function hitAt(rows: readonly (readonly [string, number, string[]])[], n: number, expected: readonly string[]): boolean {
138
+ const top = rows.slice(0, n).map((r) => r[0]);
139
+ return expected.some((e) => top.includes(e));
140
+ }
141
+
142
+ export function measure(opts: { ranker: string; commit: string; skillsDir?: string; repo?: string }): RankerBaseline {
143
+ const repo = opts.repo ?? REPO;
144
+ const skillsDir = opts.skillsDir ?? SKILLS_DIR;
145
+ // `keyword-v2` is Phase 3.1: the same formula with `triggers:` prose folded
146
+ // into each skill's term source. Any other label measures v1.
147
+ const rankOpts: RankOptions = opts.ranker === 'keyword-v2' ? { includeTriggers: true } : {};
148
+ const labelled = readLabelledPrompts(repo);
149
+ let top1 = 0;
150
+ let top3 = 0;
151
+ const misses: string[] = [];
152
+ for (const p of labelled) {
153
+ const rows = rank(p.prompt, skillsDir, rankOpts);
154
+ if (hitAt(rows, 1, p.expected)) top1++;
155
+ if (hitAt(rows, 3, p.expected)) top3++;
156
+ else misses.push(p.id);
157
+ }
158
+
159
+ const matrix = readMatrixPrompts(repo);
160
+ let withResult = 0;
161
+ let scoreSum = 0;
162
+ for (const prompt of matrix) {
163
+ const rows = rank(prompt, skillsDir, rankOpts);
164
+ if (rows.length > 0) {
165
+ withResult++;
166
+ scoreSum += rows[0]![1];
167
+ }
168
+ }
169
+
170
+ const skillsIndexed = fs.existsSync(skillsDir)
171
+ ? fs.readdirSync(skillsDir).filter((s) => fs.existsSync(path.join(skillsDir, s, 'SKILL.md'))).length
172
+ : 0;
173
+
174
+ return {
175
+ schema: 1,
176
+ ranker: opts.ranker,
177
+ commit: opts.commit,
178
+ skills_dir: path.relative(repo, skillsDir),
179
+ skills_indexed: skillsIndexed,
180
+ accuracy: {
181
+ corpus_prompts: labelled.length,
182
+ top1: labelled.length ? Math.round((top1 / labelled.length) * 1000) / 1000 : 0,
183
+ top3: labelled.length ? Math.round((top3 / labelled.length) * 1000) / 1000 : 0,
184
+ misses,
185
+ denominator_note:
186
+ 'tests/eval/corpus-dev.yaml + corpus-non-dev.yaml — the only expected-skill ground ' +
187
+ 'truth in this tree. Small; a single prompt moves each rate by ~4 points.',
188
+ },
189
+ matrix_coverage: {
190
+ corpus_prompts: matrix.length,
191
+ prompts_with_any_result: withResult,
192
+ mean_top_score: withResult ? Math.round((scoreSum / withResult) * 100) / 100 : 0,
193
+ note:
194
+ 'tests/eval/routing-matrix/ is labelled with the expected RULE, never a skill, so ' +
195
+ 'this arm carries no accuracy — only whether the ranker answers at all and how ' +
196
+ 'confident its top answer is. Its size is measured here, not assumed: the roadmap ' +
197
+ 'and five other sites still publish 496.',
198
+ },
199
+ };
200
+ }
201
+
202
+ export function main(argv: readonly string[]): number {
203
+ const ranker = argv.includes('--ranker') ? (argv[argv.indexOf('--ranker') + 1] ?? 'keyword-v1') : 'keyword-v1';
204
+ const commit = argv.includes('--commit') ? (argv[argv.indexOf('--commit') + 1] ?? 'unknown') : 'unknown';
205
+ const out = measure({ ranker, commit });
206
+ process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
207
+ return 0;
208
+ }
209
+
210
+ if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
211
+ process.exit(main(process.argv.slice(2)));
212
+ }