@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
@@ -12,13 +12,20 @@
12
12
  * detection. No behaviour changes.
13
13
  */
14
14
 
15
+ import { execFileSync } from 'node:child_process';
15
16
  import * as fs from 'node:fs';
16
17
  import * as path from 'node:path';
17
18
  import { fileURLToPath, pathToFileURL } from 'node:url';
18
19
 
19
20
  import { SRC_SKILLS } from './_lib/agent_src.js';
21
+ import { asOf } from './_lib/as_of.js';
20
22
  import { checkRatchet } from './_lib/gate_baseline.js';
21
23
  import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
24
+ import {
25
+ compute_active_pack_ids,
26
+ is_pruned_under_scoped,
27
+ load_packs_registry,
28
+ } from './_lib/scoped_projection.js';
22
29
 
23
30
  const _HERE = fileURLToPath(import.meta.url);
24
31
  const REPO = path.resolve(path.dirname(_HERE), '..', '..');
@@ -237,6 +244,8 @@ function _resolve(p: string): string {
237
244
 
238
245
  export function lint(skills_dir: string): Violation[] {
239
246
  const senior_skills: Map<string, string> = new Map();
247
+ /** `file|line|href` of every dangling link the senior pass already reported. */
248
+ const reported_dangling = new Set<string>();
240
249
  const all_skills: Map<string, string> = new Map();
241
250
  for (const skill_md of _rglobSkillMd(skills_dir)) {
242
251
  const text = fs.readFileSync(skill_md, 'utf-8');
@@ -267,6 +276,7 @@ export function lint(skills_dir: string): Violation[] {
267
276
  }
268
277
  graph.get(skill_path)!.add(target);
269
278
  if (!all_skills.has(target)) {
279
+ reported_dangling.add(`${skill_path}|${String(lineno)}|${link}`);
270
280
  violations.push({
271
281
  file: skill_path,
272
282
  line: lineno,
@@ -291,6 +301,7 @@ export function lint(skills_dir: string): Violation[] {
291
301
  for (const [lineno, slug, link] of extract_links(when_not)) {
292
302
  const target = resolve_target(skill_path, link);
293
303
  if (!all_skills.has(target)) {
304
+ reported_dangling.add(`${skill_path}|${String(lineno)}|${link}`);
294
305
  violations.push({
295
306
  file: skill_path,
296
307
  line: lineno,
@@ -321,9 +332,487 @@ export function lint(skills_dir: string): Violation[] {
321
332
  message: `composition cycle: ${names}`,
322
333
  });
323
334
  }
335
+
336
+ // Whole-body dangling pass — see the BodyLink block below for why the
337
+ // scope widens here and NOT for tier mismatch or cycle detection.
338
+ for (const bl of collect_body_links(skills_dir)) {
339
+ if (all_skills.has(bl.target)) {
340
+ continue;
341
+ }
342
+ if (reported_dangling.has(`${bl.file}|${String(bl.line)}|${bl.link}`)) {
343
+ continue;
344
+ }
345
+ violations.push({
346
+ file: bl.file,
347
+ line: bl.line,
348
+ code: 'handoff_dangling',
349
+ message: `link to \`${bl.slug}\` resolves to missing file ${bl.link}`,
350
+ });
351
+ }
352
+ violations.sort((a, b) => (a.file === b.file ? a.line - b.line : a.file < b.file ? -1 : 1));
324
353
  return violations;
325
354
  }
326
355
 
356
+ // ── Whole-body cross-skill links + the link census (skill-link-integrity) ──
357
+ // The senior / `## Related Skills` pass above is a COMPOSITION contract: it
358
+ // judges tier and builds the DAG, and both of those only mean anything inside
359
+ // a declared handoff block. A dead link is a different fact — a body sentence
360
+ // telling the agent to read a file that is not on disk is wrong wherever it
361
+ // sits — and every file carrying today's dead links declares neither a `tier:`
362
+ // nor that heading, which is exactly why a gate that has owned
363
+ // `handoff_dangling` since 2026-08-02 had never seen one of them.
364
+ //
365
+ // So the scope widens for the dangling check ONLY. Tier mismatch and cycle
366
+ // detection keep the narrow scope on purpose: widening them would read every
367
+ // prose cross-reference as a composition edge, which it is not.
368
+
369
+ /** One `[slug](…SKILL.md)` link found anywhere in a SKILL.md body. */
370
+ export interface BodyLink {
371
+ /** Absolute, symlink-resolved path of the SKILL.md carrying the link. */
372
+ file: string;
373
+ line: number;
374
+ /** Link text. `LINK_RE` only matches slug-shaped text, so this is a slug. */
375
+ slug: string;
376
+ /** The href exactly as written. */
377
+ link: string;
378
+ /** `resolve_target(file, link)` — absolute, symlink-resolved. */
379
+ target: string;
380
+ }
381
+
382
+ /** Every `[slug](…SKILL.md)` link in every SKILL.md body under `skills_dir`. */
383
+ export function collect_body_links(skills_dir: string): BodyLink[] {
384
+ const out: BodyLink[] = [];
385
+ for (const skill_md of _rglobSkillMd(skills_dir)) {
386
+ const file = _resolve(skill_md);
387
+ const lines = fs.readFileSync(skill_md, 'utf-8').split('\n');
388
+ for (let idx = 0; idx < lines.length; idx++) {
389
+ const raw = lines[idx] as string;
390
+ LINK_RE.lastIndex = 0;
391
+ let m: RegExpExecArray | null;
392
+ while ((m = LINK_RE.exec(raw)) !== null) {
393
+ const link = m[2] as string;
394
+ out.push({
395
+ file,
396
+ line: idx + 1,
397
+ slug: m[1] as string,
398
+ link,
399
+ target: resolve_target(file, link),
400
+ });
401
+ }
402
+ }
403
+ }
404
+ return out;
405
+ }
406
+
407
+ /** Values of a block-list frontmatter key (`key:` then ` - item` lines). */
408
+ export function parse_frontmatter_list(text: string, key: string): string[] {
409
+ if (!text.startsWith('---\n')) {
410
+ return [];
411
+ }
412
+ const end = text.indexOf('\n---\n', 4);
413
+ if (end === -1) {
414
+ return [];
415
+ }
416
+ const out: string[] = [];
417
+ let inKey = false;
418
+ for (const raw of text.slice(4, end).split('\n')) {
419
+ if (/^\S/.test(raw)) {
420
+ inKey = raw.replace(/:.*$/, '').trim() === key;
421
+ continue;
422
+ }
423
+ if (inKey) {
424
+ const m = /^\s+-\s*(.+?)\s*$/.exec(raw);
425
+ if (m) out.push(_strip(_strip(m[1] as string, '"'), "'"));
426
+ }
427
+ }
428
+ return out;
429
+ }
430
+
431
+ /** A dead link, as the census records it. */
432
+ export interface DeadLinkRecord {
433
+ /** Repo-relative path of the referring SKILL.md. */
434
+ file: string;
435
+ line: number;
436
+ /** The href exactly as written. */
437
+ target: string;
438
+ }
439
+
440
+ /** A surviving skill linking a slug that `scoped` prunes. */
441
+ export interface ScopedDangleRecord {
442
+ survivor: string;
443
+ target: string;
444
+ }
445
+
446
+ /**
447
+ * The link census Phase 0 of `road-to-skill-link-integrity-and-manifest-sync`
448
+ * publishes. Produced by the SAME collector the gate above scans with, so the
449
+ * published figure and the gate verdict cannot disagree — which is the whole
450
+ * reason it lives here rather than in a script of its own.
451
+ */
452
+ export interface SkillLinkCensus {
453
+ schema_version: 1;
454
+ _comment: string;
455
+ commit: string | null;
456
+ /** `](../<slug>/SKILL.md` occurrences — the Reproduction B.1 grep. */
457
+ total_links_skill_md: number;
458
+ /** `](../<slug>/` occurrences — B.1 widened to bare directory targets. */
459
+ total_links_any_dir: number;
460
+ /**
461
+ * Links the GATE sees (`LINK_RE`). Lower than `total_links_skill_md`
462
+ * because LINK_RE additionally requires slug-shaped link TEXT — and, in
463
+ * the other direction, its href is unconstrained, so it catches targets
464
+ * the B.1 character class cannot. Both are recorded because the gap is
465
+ * where a dead link hid: `../create-pr:description-only/SKILL.md` carries
466
+ * a colon, so `[a-z0-9-]*` never matched it and the drafted census
467
+ * reported 14 dead links where the gate finds 16.
468
+ */
469
+ links_matched_by_gate: number;
470
+ files_with_skill_md_link: number;
471
+ files_with_any_relative_link: number;
472
+ skills_total: number;
473
+ dead_links: DeadLinkRecord[];
474
+ /** Links whose target slug is absent from the linker's `requires_skills:`. */
475
+ undeclared_in_requires: number;
476
+ skills_declaring_requires: number;
477
+ scoped_survivors: number;
478
+ scoped_pruned: number;
479
+ scoped_dangles: ScopedDangleRecord[];
480
+ survivors_with_dangle: number;
481
+ }
482
+
483
+ // ── Scoped-dangle follow rate, on a guarded instrument ──────────────────────
484
+ // Phase 4 of road-to-skill-link-integrity-and-manifest-sync. 24 links from 17
485
+ // surviving skills point at a skill that `projection.mode: scoped` prunes. Is
486
+ // that a defect, or behaviour the consumer opted into? The roadmap does not
487
+ // pick; it records the metric that decides, and it guards the instrument FIRST
488
+ // because an unguarded "zero read attempts" is not a null — it is a stopped
489
+ // clock reported as evidence.
490
+
491
+ /** Days back from `now` a record must fall in to count the instrument live. */
492
+ export const DANGLE_WINDOW_DAYS = 30;
493
+
494
+ /** Verdict on whether the usage store can answer anything at all. */
495
+ export interface InstrumentVerdict {
496
+ instrument_live: boolean;
497
+ /** Why it is dead. Empty when live. */
498
+ reason: string;
499
+ /** Records parsed. 0 for a missing file. */
500
+ records: number;
501
+ /** Newest record timestamp, ISO, or null. */
502
+ newest: string | null;
503
+ /** Distinct `kind` values observed, sorted — see the follow-rate note. */
504
+ kinds: string[];
505
+ window_start: string;
506
+ window_end: string;
507
+ }
508
+
509
+ /**
510
+ * Guard the instrument before any count is read.
511
+ *
512
+ * A MISSING FILE AND A STALE FILE ARE BOTH DEAD, and both had to be handled
513
+ * because both occur: the store is gitignored and machine-local, so it is
514
+ * ABSENT in a fresh checkout or worktree, and in the parent checkout it holds
515
+ * 181 records that all carry the SAME timestamp from a single session on
516
+ * 2026-05-15. Either way a naive count returns zero and a phase closes as
517
+ * "harmless" on a measurement that never happened.
518
+ */
519
+ export function instrument_verdict(
520
+ storePath: string,
521
+ now: Date = asOf(),
522
+ windowDays: number = DANGLE_WINDOW_DAYS,
523
+ ): InstrumentVerdict {
524
+ const end = now;
525
+ const start = new Date(end.getTime() - windowDays * 86_400_000);
526
+ const base = {
527
+ window_start: start.toISOString(),
528
+ window_end: end.toISOString(),
529
+ };
530
+ let text: string;
531
+ try {
532
+ text = fs.readFileSync(storePath, 'utf-8');
533
+ } catch {
534
+ return {
535
+ instrument_live: false,
536
+ // Repo-relative: this string lands in a COMMITTED metric row, and
537
+ // an absolute path would bake one machine's home directory into it.
538
+ reason:
539
+ `instrument absent — ${_isUnder(storePath, REPO) ? _relTo(storePath, REPO) : path.basename(storePath)}` +
540
+ ' does not exist (the store is gitignored and machine-local)',
541
+ records: 0,
542
+ newest: null,
543
+ kinds: [],
544
+ ...base,
545
+ };
546
+ }
547
+ let records = 0;
548
+ let newest: number | null = null;
549
+ let inWindow = 0;
550
+ const kinds = new Set<string>();
551
+ for (const line of text.split('\n')) {
552
+ if (line.trim() === '') continue;
553
+ let row: { ts?: unknown; kind?: unknown; slug?: unknown };
554
+ try {
555
+ row = JSON.parse(line) as typeof row;
556
+ } catch {
557
+ continue;
558
+ }
559
+ records += 1;
560
+ if (typeof row.kind === 'string') kinds.add(row.kind);
561
+ const t = typeof row.ts === 'string' ? Date.parse(row.ts) : NaN;
562
+ if (!Number.isNaN(t)) {
563
+ if (newest === null || t > newest) newest = t;
564
+ if (t >= start.getTime() && t <= end.getTime()) inWindow += 1;
565
+ }
566
+ }
567
+ const newestIso = newest === null ? null : new Date(newest).toISOString();
568
+ if (inWindow === 0) {
569
+ const age =
570
+ newest === null
571
+ ? 'no parseable timestamp in any record'
572
+ : `newest record is ${String(Math.floor((end.getTime() - newest) / 86_400_000))} days old (${String(newestIso)})`;
573
+ return {
574
+ instrument_live: false,
575
+ reason: `instrument dead — ${age}; no record inside the ${String(windowDays)}-day window`,
576
+ records,
577
+ newest: newestIso,
578
+ kinds: [...kinds].sort(),
579
+ ...base,
580
+ };
581
+ }
582
+ return {
583
+ instrument_live: true,
584
+ reason: '',
585
+ records,
586
+ newest: newestIso,
587
+ kinds: [...kinds].sort(),
588
+ ...base,
589
+ };
590
+ }
591
+
592
+ /** Event kinds that would represent an agent actually following a link. */
593
+ export const FOLLOW_KINDS: ReadonlySet<string> = new Set(['read', 'read_attempt', 'follow']);
594
+
595
+ /** The Phase-4 metric row. `attempts` is null whenever it was not measured. */
596
+ export interface ScopedDangleFollowRate {
597
+ schema_version: 1;
598
+ _comment: string;
599
+ commit: string | null;
600
+ window_start: string;
601
+ window_end: string;
602
+ instrument: string;
603
+ instrument_live: boolean;
604
+ instrument_reason: string;
605
+ instrument_records: number;
606
+ instrument_newest: string | null;
607
+ instrument_kinds: string[];
608
+ /** Read attempts against a pruned slug. null = not measured. */
609
+ attempts: number | null;
610
+ /** Distinct pruned slugs with at least one attempt. null = not measured. */
611
+ pruned_targets_hit: string[] | null;
612
+ scoped_dangles: number;
613
+ survivors_with_dangle: number;
614
+ null_branch: string;
615
+ measured_branch: string;
616
+ }
617
+
618
+ /** Build the Phase-4 row. Guard first; count only if the guard passes. */
619
+ export function scoped_dangle_follow_rate(
620
+ skills_dir: string,
621
+ package_root: string,
622
+ storePath: string,
623
+ commit: string | null = null,
624
+ now: Date = asOf(),
625
+ ): ScopedDangleFollowRate {
626
+ const census = skill_link_census(skills_dir, package_root, commit);
627
+ const v = instrument_verdict(storePath, now);
628
+ let attempts: number | null = null;
629
+ let hit: string[] | null = null;
630
+ if (v.instrument_live) {
631
+ // Same predicate as the census above, by construction: the pruned set
632
+ // comes from the census, which uses `is_pruned_under_scoped` — the
633
+ // function `install.ts` itself applies. Two definitions of "pruned"
634
+ // free to disagree is the defect, not the fix.
635
+ const prunedSlugs = new Set(census.scoped_dangles.map((d) => d.target));
636
+ const seen = new Set<string>();
637
+ let n = 0;
638
+ for (const line of fs.readFileSync(storePath, 'utf-8').split('\n')) {
639
+ if (line.trim() === '') continue;
640
+ let row: { ts?: unknown; kind?: unknown; slug?: unknown };
641
+ try {
642
+ row = JSON.parse(line) as typeof row;
643
+ } catch {
644
+ continue;
645
+ }
646
+ const t = typeof row.ts === 'string' ? Date.parse(row.ts) : NaN;
647
+ if (Number.isNaN(t) || t < Date.parse(v.window_start) || t > Date.parse(v.window_end)) {
648
+ continue;
649
+ }
650
+ if (typeof row.kind !== 'string' || !FOLLOW_KINDS.has(row.kind)) continue;
651
+ if (typeof row.slug !== 'string' || !prunedSlugs.has(row.slug)) continue;
652
+ n += 1;
653
+ seen.add(row.slug);
654
+ }
655
+ attempts = n;
656
+ hit = [...seen].sort();
657
+ }
658
+ return {
659
+ schema_version: 1,
660
+ _comment: DANGLE_COMMENT,
661
+ commit,
662
+ window_start: v.window_start,
663
+ window_end: v.window_end,
664
+ instrument: 'agents/runtime/metrics/skill-usage.jsonl',
665
+ instrument_live: v.instrument_live,
666
+ instrument_reason: v.reason,
667
+ instrument_records: v.records,
668
+ instrument_newest: v.newest,
669
+ instrument_kinds: v.kinds,
670
+ attempts,
671
+ pruned_targets_hit: hit,
672
+ scoped_dangles: census.scoped_dangles.length,
673
+ survivors_with_dangle: census.survivors_with_dangle,
674
+ null_branch:
675
+ 'Zero attempts over a LIVE window closes the question as a published ' +
676
+ 'null and the dangling links stay: a link to a pruned sibling is then ' +
677
+ 'behaviour the consumer opted into by choosing `projection.mode: ' +
678
+ 'scoped`, not a defect. A null over a DEAD window closes nothing and ' +
679
+ 'is not permitted to be reported as one — that is what the guard is ' +
680
+ 'for, and why `attempts` is null rather than 0 when the guard fails.',
681
+ measured_branch:
682
+ 'A nonzero count promotes the fix: rewrite each dangling link in the ' +
683
+ 'PROJECTED SKILL.md to name the slug and its pack instead of linking ' +
684
+ 'it — source tree untouched, projection honest about itself, using ' +
685
+ 'the same `is_pruned_under_scoped` predicate this counter uses so the ' +
686
+ 'two cannot disagree.',
687
+ };
688
+ }
689
+
690
+ /** Provenance carried inside the Phase-4 row. */
691
+ const DANGLE_COMMENT =
692
+ 'Written by `./scripts-run src/scripts/lint_handoffs --scoped-dangle-json`. Do ' +
693
+ 'not hand-edit — regenerate it. The instrument is guarded BEFORE any count is ' +
694
+ 'read: `attempts` and `pruned_targets_hit` are null, never 0, whenever ' +
695
+ '`instrument_live` is false, because a stopped clock reporting zero is not a ' +
696
+ 'null. Read `instrument_reason` first. NOTE ON KINDS: the store has only ever ' +
697
+ 'recorded `kind: "exposure"`, and no event in FOLLOW_KINDS (`read`, ' +
698
+ '`read_attempt`, `follow`) is emitted anywhere in the tree — so even a LIVE ' +
699
+ 'window could not answer this question today. Emitting a follow event is the ' +
700
+ 'prerequisite this measurement is blocked on, and it is not in this roadmap.';
701
+
702
+ /** Provenance carried inside the row, so a reader never has to find this file. */
703
+ const CENSUS_COMMENT =
704
+ 'Written by `./scripts-run src/scripts/lint_handoffs --census-json`. Do not ' +
705
+ 'hand-edit — regenerate it. The collector is `collect_body_links`, the same ' +
706
+ 'one the gate scans with, so the published figure and the gate verdict ' +
707
+ 'cannot drift apart. `dead_links` uses the gate predicate (a target absent ' +
708
+ 'from the live SKILL.md set), NOT the Reproduction B.1 grep — see ' +
709
+ '`links_matched_by_gate` for why the two differ and which dead link the ' +
710
+ 'grep was blind to.';
711
+
712
+ /** `](../<slug>/SKILL.md` — the Reproduction B.1 pattern, verbatim. */
713
+ const B1_SKILL_MD_RE = /\]\(\.\.\/[a-z0-9-]*\/SKILL\.md/g;
714
+ /** `](../<slug>/` — B.1 widened to bare directory targets. */
715
+ const B1_ANY_DIR_RE = /\]\(\.\.\/[a-z0-9-]*\//g;
716
+ /** `](../` — the "carries any relative link" file counter from B.1. */
717
+ const B1_ANY_REL_RE = /\]\(\.\.\//;
718
+
719
+ /** Build the census over a skills root. `commit` is passed in, never guessed. */
720
+ export function skill_link_census(
721
+ skills_dir: string,
722
+ package_root: string,
723
+ commit: string | null = null,
724
+ ): SkillLinkCensus {
725
+ const files = _rglobSkillMd(skills_dir);
726
+ const live = new Set(files.map(_resolve));
727
+
728
+ let total_links_skill_md = 0;
729
+ let total_links_any_dir = 0;
730
+ let files_with_skill_md_link = 0;
731
+ let files_with_any_relative_link = 0;
732
+ let skills_declaring_requires = 0;
733
+ const requires_by_file = new Map<string, Set<string>>();
734
+
735
+ for (const f of files) {
736
+ const text = fs.readFileSync(f, 'utf-8');
737
+ const a = text.match(B1_SKILL_MD_RE) ?? [];
738
+ const b = text.match(B1_ANY_DIR_RE) ?? [];
739
+ total_links_skill_md += a.length;
740
+ total_links_any_dir += b.length;
741
+ if (a.length > 0) files_with_skill_md_link += 1;
742
+ if (B1_ANY_REL_RE.test(text)) files_with_any_relative_link += 1;
743
+ const req = parse_frontmatter_list(text, 'requires_skills');
744
+ if (text.includes('requires_skills')) skills_declaring_requires += 1;
745
+ requires_by_file.set(_resolve(f), new Set(req));
746
+ }
747
+
748
+ const links = collect_body_links(skills_dir);
749
+ const dead_links: DeadLinkRecord[] = [];
750
+ let undeclared_in_requires = 0;
751
+ for (const bl of links) {
752
+ if (!live.has(bl.target)) {
753
+ dead_links.push({
754
+ file: _relTo(bl.file, package_root),
755
+ line: bl.line,
756
+ target: bl.link,
757
+ });
758
+ }
759
+ if (!(requires_by_file.get(bl.file) ?? new Set()).has(bl.slug)) {
760
+ undeclared_in_requires += 1;
761
+ }
762
+ }
763
+
764
+ // Scoped projection, with the installer's own predicate — never a second
765
+ // definition of "pruned" free to disagree with the one install.ts applies.
766
+ const active = compute_active_pack_ids(load_packs_registry(package_root), []);
767
+ let scoped_survivors = 0;
768
+ let scoped_pruned = 0;
769
+ const pruned_paths = new Set<string>();
770
+ for (const f of files) {
771
+ if (is_pruned_under_scoped(f, active)) {
772
+ scoped_pruned += 1;
773
+ pruned_paths.add(_resolve(f));
774
+ } else {
775
+ scoped_survivors += 1;
776
+ }
777
+ }
778
+ const scoped_dangles: ScopedDangleRecord[] = [];
779
+ const survivors = new Set<string>();
780
+ for (const bl of links) {
781
+ if (pruned_paths.has(bl.file) || !pruned_paths.has(bl.target)) {
782
+ continue;
783
+ }
784
+ scoped_dangles.push({
785
+ survivor: path.basename(path.dirname(bl.file)),
786
+ target: path.basename(path.dirname(bl.target)),
787
+ });
788
+ survivors.add(bl.file);
789
+ }
790
+ scoped_dangles.sort((x, y) =>
791
+ x.survivor === y.survivor
792
+ ? x.target.localeCompare(y.target)
793
+ : x.survivor.localeCompare(y.survivor),
794
+ );
795
+
796
+ return {
797
+ schema_version: 1,
798
+ _comment: CENSUS_COMMENT,
799
+ commit,
800
+ total_links_skill_md,
801
+ total_links_any_dir,
802
+ links_matched_by_gate: links.length,
803
+ files_with_skill_md_link,
804
+ files_with_any_relative_link,
805
+ skills_total: files.length,
806
+ dead_links,
807
+ undeclared_in_requires,
808
+ skills_declaring_requires,
809
+ scoped_survivors,
810
+ scoped_pruned,
811
+ scoped_dangles,
812
+ survivors_with_dangle: survivors.size,
813
+ };
814
+ }
815
+
327
816
  /** Python repr() of a string (single-quote preference). */
328
817
  function _pyRepr(s: string): string {
329
818
  if (s.includes("'") && !s.includes('"')) {
@@ -445,6 +934,15 @@ export function validate_handoff_open_questions(text: string): string | null {
445
934
  return null;
446
935
  }
447
936
 
937
+ /** `git rev-parse HEAD`, or null outside a repository. */
938
+ function _headCommit(): string | null {
939
+ try {
940
+ return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: REPO, encoding: 'utf-8' }).trim();
941
+ } catch {
942
+ return null;
943
+ }
944
+ }
945
+
448
946
  export function main(argv?: readonly string[]): number {
449
947
  let skills_dir = SKILLS_DIR;
450
948
  // Positional-only: flags are NOT paths. Before this filter the CI invocation
@@ -453,7 +951,33 @@ export function main(argv?: readonly string[]): number {
453
951
  // red under the exact argv CI runs while green when probed bare. That is the
454
952
  // inverse of this suite's dead-scope defect and the reason gate-coverage
455
953
  // rule 2 pins CI-identical argv.
456
- const args = (argv ?? process.argv.slice(2)).filter((a) => !String(a).startsWith('-'));
954
+ const rawArgs = argv ?? process.argv.slice(2);
955
+ const args = rawArgs.filter((a) => !String(a).startsWith('-'));
956
+ // Census mode. An added mode on the gate's own entry point rather than a
957
+ // new script: the census MUST be produced by the collector the gate scans
958
+ // with, or the published figure and the verdict are free to drift, which
959
+ // is the class of defect this whole roadmap is about.
960
+ if (rawArgs.includes('--scoped-dangle-json')) {
961
+ if (args.length > 0) {
962
+ skills_dir = _resolve(args[0] as string);
963
+ }
964
+ const row = scoped_dangle_follow_rate(
965
+ skills_dir,
966
+ REPO,
967
+ path.join(REPO, 'agents', 'runtime', 'metrics', 'skill-usage.jsonl'),
968
+ _headCommit(),
969
+ );
970
+ process.stdout.write(JSON.stringify(row, null, 2) + '\n');
971
+ return 0;
972
+ }
973
+ if (rawArgs.includes('--census-json')) {
974
+ if (args.length > 0) {
975
+ skills_dir = _resolve(args[0] as string);
976
+ }
977
+ const census = skill_link_census(skills_dir, REPO, _headCommit());
978
+ process.stdout.write(JSON.stringify(census, null, 2) + '\n');
979
+ return 0;
980
+ }
457
981
  if (args.length > 0 && String(args[0]).endsWith('HANDOFF.md')) {
458
982
  // artifact mode — validate the workflow-resume file's required fields
459
983
  const text = fs.readFileSync(_resolve(args[0] as string), 'utf-8');