@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
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * A1.2/A1.3 — answer the per-turn composite's arming question, and refuse to guess.
4
+ *
5
+ * `src/config/hook-latency-budget.json` → `per_turn_composite` ships
6
+ * `observe_only: true` with `p50_ci: null`, and its `arming_precondition` is
7
+ * **"at least 10 CI gate readings of the composite, from at least 2 distinct
8
+ * runner sessions"**. Until 2026-08-23 that precondition could not be evaluated
9
+ * at all: the bench printed the composite and stored nothing. This reads the
10
+ * store the bench now appends to and reports whether the precondition holds.
11
+ *
12
+ * IT NEVER WRITES `p50_ci`, AND THAT IS THE POINT. A2.1 is a MAINTAINER ACT
13
+ * gated by `b-composite-ceiling-value`. This script publishes a distribution to
14
+ * choose FROM; choosing is not its job, and a script that both measured and
15
+ * armed would be the agent setting its own ceiling.
16
+ *
17
+ * THREE REFUSALS, each from a recorded failure mode:
18
+ *
19
+ * 1. **A null composite is dropped, never extrapolated.** `perTurnComposite()`
20
+ * returns null rather than a number when a slot is missing from the run,
21
+ * because a composite over a subset reads LOW — and low is the direction that
22
+ * makes a ceiling look met. A record with `composite_ms: null` is counted as
23
+ * unusable and named, not averaged away.
24
+ * 2. **Never a bare boolean.** "not yet" always names the shortfall — which
25
+ * clause failed and by how much. `9 readings from 3 sessions` and `12 from 1`
26
+ * are both not-armable and fail for *different* reasons; a boolean loses that.
27
+ * Both are pinned as tests.
28
+ * 3. **Sessions are counted by identity, not by row.** The instability the floor
29
+ * exists to exclude was measured on ONE machine (a sibling metric read
30
+ * 44–157 % at n=12), so twelve readings from one session is exactly the shape
31
+ * the floor rejects. A bare counter cannot see it.
32
+ *
33
+ * Exit codes:
34
+ * 0 — the store was read and a verdict printed (armable OR not — "not yet" is
35
+ * a successful measurement, never a build failure)
36
+ * 1 — the store is unreadable or malformed
37
+ * 2 — bad args
38
+ */
39
+ import * as fs from 'node:fs';
40
+ import * as os from 'node:os';
41
+ import * as path from 'node:path';
42
+ import { fileURLToPath, pathToFileURL } from 'node:url';
43
+
44
+ import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
45
+ import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
46
+
47
+ const _HERE = fileURLToPath(import.meta.url);
48
+ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
49
+ const SELF = 'src/scripts/check_composite_arming.ts';
50
+
51
+ /** The tracked, append-only store the bench writes with `--record-composite`. */
52
+ export const STORE_REL = path.join('agents', 'evidence', 'hook-composite-readings.jsonl');
53
+
54
+ /** From `per_turn_composite.arming_precondition`. A STATED minimum, not a derived one. */
55
+ export const MIN_READINGS = 10;
56
+ export const MIN_SESSIONS = 2;
57
+
58
+ const SELF_TEST_MIN_CASES = 5;
59
+ const SELF_TEST_MIN_REJECT = 2;
60
+
61
+ export interface Reading {
62
+ /** null when a slot was missing from the run — unusable, never extrapolated. */
63
+ composite_ms: number | null;
64
+ parts?: Record<string, number>;
65
+ tool_calls?: number;
66
+ /** Distinguishes runner SESSIONS, not rows. See refusal 3. */
67
+ session: string;
68
+ platform?: string;
69
+ node?: string;
70
+ commit?: string;
71
+ recorded_at?: string;
72
+ }
73
+
74
+ export interface Verdict {
75
+ armable: boolean;
76
+ usable: number;
77
+ unusable: number;
78
+ sessions: string[];
79
+ /** Every clause that failed, each naming its shortfall. Empty iff armable. */
80
+ failures: string[];
81
+ }
82
+
83
+ export function parseStore(text: string): { readings: Reading[]; malformed: number } {
84
+ const readings: Reading[] = [];
85
+ let malformed = 0;
86
+ for (const line of text.split('\n')) {
87
+ const t = line.trim();
88
+ if (t === '') continue;
89
+ try {
90
+ const o = JSON.parse(t) as Partial<Reading>;
91
+ if (typeof o.session !== 'string' || !('composite_ms' in o)) {
92
+ malformed += 1;
93
+ continue;
94
+ }
95
+ readings.push({ ...o, session: o.session, composite_ms: o.composite_ms ?? null });
96
+ } catch {
97
+ malformed += 1;
98
+ }
99
+ }
100
+ return { readings, malformed };
101
+ }
102
+
103
+ export function evaluate(readings: readonly Reading[]): Verdict {
104
+ const usable = readings.filter((r) => typeof r.composite_ms === 'number');
105
+ const unusable = readings.length - usable.length;
106
+ const sessions = [...new Set(usable.map((r) => r.session))].sort();
107
+ const failures: string[] = [];
108
+ if (usable.length < MIN_READINGS) {
109
+ failures.push(
110
+ `readings: ${String(usable.length)} usable of ${String(MIN_READINGS)} required ` +
111
+ `(short by ${String(MIN_READINGS - usable.length)})`,
112
+ );
113
+ }
114
+ if (sessions.length < MIN_SESSIONS) {
115
+ failures.push(
116
+ `sessions: ${String(sessions.length)} distinct of ${String(MIN_SESSIONS)} required ` +
117
+ `(short by ${String(MIN_SESSIONS - sessions.length)}) — the floor exists because the ` +
118
+ `instability it excludes was measured on ONE machine, so N readings from one session ` +
119
+ `cannot substitute`,
120
+ );
121
+ }
122
+ return { armable: failures.length === 0, usable: usable.length, unusable, sessions, failures };
123
+ }
124
+
125
+ function _quantile(sorted: readonly number[], q: number): number {
126
+ if (sorted.length === 0) return 0;
127
+ const i = Math.min(sorted.length - 1, Math.max(0, Math.ceil(q * sorted.length) - 1));
128
+ return sorted[i] as number;
129
+ }
130
+
131
+ /**
132
+ * A1.3 — render the distribution to choose FROM. Writes no `p50_ci`.
133
+ *
134
+ * The pathology net is kept SEPARATE from the cap, per step 3 of the recorded
135
+ * arming procedure: one runner spike should be caught as a pathology rather than
136
+ * by lowering the cap for everyone.
137
+ */
138
+ export function renderDistribution(readings: readonly Reading[], v: Verdict): string {
139
+ const vals = readings
140
+ .map((r) => r.composite_ms)
141
+ .filter((x): x is number => typeof x === 'number')
142
+ .sort((a, b) => a - b);
143
+ const L: string[] = [];
144
+ L.push('# Per-turn composite — distribution, not a decision');
145
+ L.push('');
146
+ L.push(`n = ${String(v.usable)} usable reading(s) across ${String(v.sessions.length)} session(s).`);
147
+ if (v.unusable > 0) {
148
+ L.push('');
149
+ L.push(
150
+ `**${String(v.unusable)} reading(s) were DROPPED as unusable** — a slot the definition ` +
151
+ 'needs was missing from that run, so the composite would have read low, and low is the ' +
152
+ 'direction that makes a ceiling look met. They are excluded, not averaged.',
153
+ );
154
+ }
155
+ L.push('');
156
+ L.push('| statistic | ms |');
157
+ L.push('|---|---|');
158
+ L.push(`| min | ${String(vals[0] ?? 0)} |`);
159
+ L.push(`| p50 | ${String(_quantile(vals, 0.5))} |`);
160
+ L.push(`| p95 | ${String(_quantile(vals, 0.95))} |`);
161
+ L.push(`| max | ${String(vals[vals.length - 1] ?? 0)} |`);
162
+ L.push(`| spread (max − min) | ${String((vals[vals.length - 1] ?? 0) - (vals[0] ?? 0))} |`);
163
+ L.push('');
164
+ L.push('## Per session');
165
+ L.push('');
166
+ L.push('| session | n | p50 | max |');
167
+ L.push('|---|---|---|---|');
168
+ for (const s of v.sessions) {
169
+ const sv = readings
170
+ .filter((r) => r.session === s && typeof r.composite_ms === 'number')
171
+ .map((r) => r.composite_ms as number)
172
+ .sort((a, b) => a - b);
173
+ L.push(`| \`${s}\` | ${String(sv.length)} | ${String(_quantile(sv, 0.5))} | ${String(sv[sv.length - 1] ?? 0)} |`);
174
+ }
175
+ L.push('');
176
+ L.push('## What a maintainer does with this — and what this file deliberately does NOT do');
177
+ L.push('');
178
+ L.push(
179
+ 'This file writes **no `p50_ci`**. Setting it is step A2.1, a MAINTAINER ACT gated by ' +
180
+ '`b-composite-ceiling-value`. A script that both measured the distribution and armed the ' +
181
+ 'ceiling from it would be the agent setting its own budget.',
182
+ );
183
+ L.push('');
184
+ L.push(
185
+ 'Per step 2 of the recorded arming procedure, the cap is an **absolute** number derived from ' +
186
+ 'this distribution, never a creep window — shared CI runners flap, which is why ' +
187
+ '`regression_gate.max_regression_pct` already sits at 200.',
188
+ );
189
+ L.push('');
190
+ L.push(
191
+ 'Per step 3, the **pathology net stays separate from the cap**. Proposed shape, offered as a ' +
192
+ 'starting point and not a recommendation: cap from the p50 with the recorded headroom ' +
193
+ 'convention, and a separate net at roughly the observed max, so ONE spike is caught as a ' +
194
+ 'pathology rather than by lowering the cap for every run.',
195
+ );
196
+ if (!v.armable) {
197
+ L.push('');
198
+ L.push('## NOT ARMABLE YET');
199
+ L.push('');
200
+ for (const f of v.failures) L.push(`- ${f}`);
201
+ L.push('');
202
+ L.push(
203
+ 'The numbers above are printed anyway, because a distribution is informative before it is ' +
204
+ 'sufficient — but they must not be read as a basis for a ceiling until every clause ' +
205
+ 'above clears.',
206
+ );
207
+ }
208
+ return `${L.join('\n')}\n`;
209
+ }
210
+
211
+ export function selfTest(): number {
212
+ const nine3 = Array.from({ length: 9 }, (_, i) => ({ composite_ms: 1000 + i, session: `s${String(i % 3)}` }));
213
+ const twelve1 = Array.from({ length: 12 }, (_, i) => ({ composite_ms: 1000 + i, session: 'only' }));
214
+ const ok = Array.from({ length: 10 }, (_, i) => ({ composite_ms: 1000 + i, session: `s${String(i % 2)}` }));
215
+ const cases: SelfTestCase[] = [
216
+ {
217
+ name: '9 readings from 3 sessions is NOT armable, and names the readings clause',
218
+ expect: 'accept',
219
+ run: () => {
220
+ const v = evaluate(nine3);
221
+ return !v.armable && v.failures.length === 1 && v.failures[0]?.startsWith('readings:') === true ? 0 : 1;
222
+ },
223
+ },
224
+ {
225
+ name: '12 readings from 1 session is NOT armable, and names the sessions clause',
226
+ expect: 'accept',
227
+ run: () => {
228
+ const v = evaluate(twelve1);
229
+ return !v.armable && v.failures.length === 1 && v.failures[0]?.startsWith('sessions:') === true ? 0 : 1;
230
+ },
231
+ },
232
+ {
233
+ name: '10 readings from 2 sessions IS armable',
234
+ expect: 'accept',
235
+ run: () => (evaluate(ok).armable ? 0 : 1),
236
+ },
237
+ {
238
+ name: 'a null composite is dropped, not counted toward the floor',
239
+ expect: 'accept',
240
+ run: () => {
241
+ const v = evaluate([...ok.slice(0, 9), { composite_ms: null, session: 'sX' }]);
242
+ return !v.armable && v.unusable === 1 && v.usable === 9 ? 0 : 1;
243
+ },
244
+ },
245
+ {
246
+ name: 'the published distribution writes no p50_ci',
247
+ expect: 'accept',
248
+ run: () => (/p50_ci/.test(renderDistribution(ok, evaluate(ok))) && !/writes \*\*no `p50_ci`\*\*/.test(renderDistribution(ok, evaluate(ok))) ? 1 : 0),
249
+ },
250
+ {
251
+ name: 'rejects a missing store rather than reporting armable',
252
+ expect: 'reject',
253
+ run: () => runGateCli(REPO_ROOT, SELF, ['--store', path.join(REPO_ROOT, 'no-such.jsonl')], REPO_ROOT),
254
+ },
255
+ {
256
+ name: 'rejects a malformed store rather than silently skipping every line',
257
+ expect: 'reject',
258
+ run: () => {
259
+ const d = fs.mkdtempSync(path.join(os.tmpdir(), 'arm-'));
260
+ const f = path.join(d, 's.jsonl');
261
+ fs.writeFileSync(f, 'not json\n{also not\n');
262
+ try {
263
+ return runGateCli(REPO_ROOT, SELF, ['--store', f], REPO_ROOT);
264
+ } finally {
265
+ fs.rmSync(d, { recursive: true, force: true });
266
+ }
267
+ },
268
+ },
269
+ ];
270
+ return runSelfTest({ gate: 'check_composite_arming', cases, minCases: SELF_TEST_MIN_CASES, minRejectCases: SELF_TEST_MIN_REJECT });
271
+ }
272
+
273
+ function main(argv?: readonly string[]): number {
274
+ const args = argv ?? process.argv.slice(2);
275
+ if (args.includes('--self-test')) return selfTest();
276
+ const si = args.indexOf('--store');
277
+ const store = si >= 0 ? args[si + 1] : path.join(REPO_ROOT, STORE_REL);
278
+ if (store === undefined) {
279
+ process.stderr.write('check_composite_arming: --store requires a path\n');
280
+ return 2;
281
+ }
282
+ let text: string;
283
+ try {
284
+ text = fs.readFileSync(store, 'utf-8');
285
+ } catch {
286
+ process.stdout.write(
287
+ `❌ no composite store at ${path.relative(REPO_ROOT, store)}.\n` +
288
+ ` The bench appends to it with \`--record-composite\`; until CI runs have\n` +
289
+ ` accumulated readings there is nothing to evaluate. This is "not measured yet",\n` +
290
+ ` which is NOT the same as "armable".\n`,
291
+ );
292
+ return 1;
293
+ }
294
+ const { readings, malformed } = parseStore(text);
295
+ if (malformed > 0 && readings.length === 0) {
296
+ process.stderr.write(
297
+ `❌ ${path.relative(REPO_ROOT, store)}: ${String(malformed)} malformed line(s) and no usable ` +
298
+ `record. A store that parses to nothing must not read as "no readings yet".\n`,
299
+ );
300
+ return 1;
301
+ }
302
+ // Scope assertion. The unit is a READING, and zero of them is a dead scope
303
+ // rather than "nothing to report": a store that parsed to no records reads
304
+ // identically to a store nobody has written, and the second must not pass as
305
+ // the first. The missing-file case above already exits 1; this catches the
306
+ // present-but-empty and parsed-to-nothing cases.
307
+ try {
308
+ reportScanned({
309
+ gate: 'check_composite_arming',
310
+ scanned: readings.length,
311
+ units: 'composite reading(s)',
312
+ roots: [path.relative(REPO_ROOT, store) || store],
313
+ });
314
+ } catch (err) {
315
+ if (err instanceof DeadScopeError) {
316
+ process.stderr.write(`❌ ${err.message}\n`);
317
+ return 1;
318
+ }
319
+ throw err;
320
+ }
321
+
322
+ const v = evaluate(readings);
323
+
324
+ if (args.includes('--publish')) {
325
+ const out = path.join(REPO_ROOT, 'agents', 'evidence', 'reports', 'per-turn-composite-distribution.md');
326
+ fs.mkdirSync(path.dirname(out), { recursive: true });
327
+ fs.writeFileSync(out, renderDistribution(readings, v), 'utf-8');
328
+ process.stdout.write(`✅ wrote ${path.relative(REPO_ROOT, out)} (n=${String(v.usable)}, sessions=${String(v.sessions.length)})\n`);
329
+ return 0;
330
+ }
331
+
332
+ if (malformed > 0) {
333
+ process.stdout.write(`⚠️ ${String(malformed)} malformed line(s) ignored (${String(readings.length)} parsed)\n`);
334
+ }
335
+ if (v.armable) {
336
+ process.stdout.write(
337
+ `✅ ARMABLE — ${String(v.usable)} usable reading(s) from ${String(v.sessions.length)} session(s) ` +
338
+ `(floor: ${String(MIN_READINGS)} / ${String(MIN_SESSIONS)}).\n` +
339
+ ` Next is A2.1, a MAINTAINER ACT: run with --publish, then set p50_ci and flip\n` +
340
+ ` observe_only. This script never writes either.\n`,
341
+ );
342
+ } else {
343
+ process.stdout.write(
344
+ `⏳ NOT YET — ${String(v.usable)} usable reading(s) from ${String(v.sessions.length)} session(s)` +
345
+ `${v.unusable > 0 ? `, ${String(v.unusable)} dropped as unusable` : ''}.\n`,
346
+ );
347
+ for (const f of v.failures) process.stdout.write(` · ${f}\n`);
348
+ }
349
+ return 0;
350
+ }
351
+
352
+ function _isCliEntry(): boolean {
353
+ if (process.argv[1] === undefined) return false;
354
+ if (import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) return true;
355
+ try {
356
+ return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(process.argv[1]));
357
+ } catch {
358
+ return false;
359
+ }
360
+ }
361
+
362
+ if (_isCliEntry() || process.argv[1] === _HERE) process.exit(main());
363
+
364
+ export { main };
@@ -49,6 +49,7 @@ import * as os from 'node:os';
49
49
  import * as path from 'node:path';
50
50
  import { fileURLToPath, pathToFileURL } from 'node:url';
51
51
 
52
+ import { asOf } from './_lib/as_of.js';
52
53
  import { GateLedger } from './_lib/gate_ledger.js';
53
54
  import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
54
55
  import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
@@ -126,7 +127,7 @@ export function days_between(fromStamp: number, toStamp: number): number {
126
127
  }
127
128
 
128
129
  /** Today as `YYYY-MM-DD` in UTC — the only place the clock is read. */
129
- export function today_iso(now: Date = new Date()): string {
130
+ export function today_iso(now: Date = asOf()): string {
130
131
  return now.toISOString().slice(0, 10);
131
132
  }
132
133
 
@@ -36,6 +36,7 @@ import * as fs from 'node:fs';
36
36
  import * as os from 'node:os';
37
37
  import * as path from 'node:path';
38
38
 
39
+ import { asOf, asOfBanner } from './_lib/as_of.js';
39
40
  import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
40
41
  import { runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
41
42
 
@@ -234,7 +235,7 @@ function main(argv: string[]): number {
234
235
  const today =
235
236
  ti >= 0 && argv[ti + 1] !== undefined
236
237
  ? String(argv[ti + 1])
237
- : new Date().toISOString().slice(0, 10);
238
+ : asOf().toISOString().slice(0, 10);
238
239
  if (epochDay(today) === null) {
239
240
  process.stderr.write(`--today must be a real YYYY-MM-DD date (got ${today})\n`);
240
241
  return 2;
@@ -253,6 +254,7 @@ function main(argv: string[]): number {
253
254
  const bad = rows.filter((r) => r.verdict === 'stale' || r.verdict === 'future-date' || r.verdict === 'malformed');
254
255
  const unstamped = rows.filter((r) => r.verdict === 'unstamped');
255
256
 
257
+ process.stdout.write(`${asOfBanner(ti >= 0 ? today : undefined)}\n`);
256
258
  for (const r of rows) {
257
259
  const age = r.age_days === null ? '—' : `${r.age_days}d`;
258
260
  process.stdout.write(
@@ -38,6 +38,7 @@ import * as path from 'node:path';
38
38
  import { fileURLToPath, pathToFileURL } from 'node:url';
39
39
 
40
40
  import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
41
+ import { count as count_artefacts } from './update_counts.js';
41
42
  import { KERNEL_RULE_ID_SET } from './_lib/kernel_rules.js';
42
43
  import {
43
44
  carrier_frequency_by_platform,
@@ -81,7 +82,7 @@ export type Resolution =
81
82
  | 'observer' // instruments only — never blocks
82
83
  | 'unwired' // declared script exists but nothing runs it ← the D1 class
83
84
  | 'missing' // declared target does not exist at all
84
- | 'none'; // honest, recorded gap
85
+ | 'none'; // honest, recorded gap — `instruction-only: <reason>`, or the retired bare `none`
85
86
 
86
87
  /**
87
88
  * Whether the carrier's firing set covers the obligation's — the question
@@ -91,7 +92,8 @@ export type Resolution =
91
92
  * hook-capable platform.
92
93
  * `gap` — at least one hook-capable platform where no declared carrier
93
94
  * covers it. `gap_platforms` names them.
94
- * `declared-gap` — the rule declares `enforced_by: none`; the gap is stated in
95
+ * `declared-gap` — the rule declares `instruction-only:` (or the retired `none`);
96
+ * the gap is stated in
95
97
  * the rule's own text, so it is honest, not a defect.
96
98
  * `unclassified` — no `obligation_frequency` to join against. The nine kernel
97
99
  * rules sit here: `block_kernel_rule_writes.ts` denies agent
@@ -377,8 +379,32 @@ export function resolve_one(
377
379
  exists: (rel: string) => boolean;
378
380
  },
379
381
  ): { resolution: Resolution; note?: string } {
382
+ // `none` stays legal and counted — an honest gap beats a false claim — but it
383
+ // is the RETIRED spelling. `instruction-only: <reason>` is the same effective
384
+ // level with the triage record attached, because "nothing enforces this" and
385
+ // "nothing enforces this AND here is why that is the right call" are not the
386
+ // same statement, and only the second one survives review. The one remaining
387
+ // `none` is `non-destructive-by-default`, a kernel rule that
388
+ // `block_kernel_rule_writes` denies agent writes to — see
389
+ // `agents/roadmaps/stubs/road-to-kernel-instruction-only-migration.md`.
380
390
  if (decl === 'none') return { resolution: 'none' };
381
391
 
392
+ if (decl === 'instruction-only' || decl.startsWith('instruction-only:')) {
393
+ const reason = decl.slice('instruction-only:'.length).trim();
394
+ if (decl === 'instruction-only' || reason === '') {
395
+ // A bare marker is not a triage record. `missing` is the honest slot:
396
+ // the declaration names a mechanism it never supplies, and `missing`
397
+ // is what the `--check` ratchet reds on.
398
+ return {
399
+ resolution: 'missing',
400
+ note:
401
+ 'instruction-only declared with no reason — a reason is a triage record, ' +
402
+ 'not a pass. State in one line why this obligation is model-carried.',
403
+ };
404
+ }
405
+ return { resolution: 'none' };
406
+ }
407
+
382
408
  const idx = decl.indexOf(':');
383
409
  const kind = decl.slice(0, idx);
384
410
  const target = decl.slice(idx + 1);
@@ -471,7 +497,10 @@ export function join_frequency(
471
497
  if (declared.length === 0) {
472
498
  return { verdict: 'unmeasured', carrier_frequency: null, gap_platforms: [] };
473
499
  }
474
- if (declared.every((d) => d === 'none')) {
500
+ // `instruction-only:` is the same declared gap as `none`, with its reason
501
+ // attached; a rule that spelled it the new way must not become a frequency
502
+ // finding for doing so.
503
+ if (declared.every((d) => d === 'none' || d.startsWith('instruction-only:'))) {
475
504
  return { verdict: 'declared-gap', carrier_frequency: null, gap_platforms: [] };
476
505
  }
477
506
 
@@ -599,6 +628,12 @@ export interface Summary {
599
628
  frequency_gap: number;
600
629
  /** Rules with no `obligation_frequency` to join — the nine kernel rules. */
601
630
  frequency_unclassified: number;
631
+ /**
632
+ * The denominator every figure above is taken over, WITH its frame. See
633
+ * {@link denominator_frames} for why the frame ships even when the two
634
+ * populations agree.
635
+ */
636
+ frames: DenominatorFrames;
602
637
  }
603
638
 
604
639
  export function summarise(rows: RuleCoverage[]): Summary {
@@ -617,9 +652,83 @@ export function summarise(rows: RuleCoverage[]): Summary {
617
652
  blocking_pct: rows.length === 0 ? 0 : Math.round((blocking / rows.length) * 1000) / 10,
618
653
  frequency_gap: count((r) => r.frequency_verdict === 'gap'),
619
654
  frequency_unclassified: count((r) => r.frequency_verdict === 'unclassified'),
655
+ frames: denominator_frames(rows.length),
620
656
  };
621
657
  }
622
658
 
659
+ /**
660
+ * The denominator, WITH the frame that produced it.
661
+ *
662
+ * WHY A FRAME AND NOT JUST A NUMBER. Until 2026-08-23 the tree published five
663
+ * different figures for one property — `docs/proof.md` carried an 86 and an 89
664
+ * for "rules that declare no backstop" in two places, a frontmatter grep said
665
+ * 87 and an any-line grep said 82 — and a reader had no way to tell which was
666
+ * the answer. The cause was not arithmetic. It was that every figure was
667
+ * quoted without saying WHICH population it was taken over: the resolver was
668
+ * once scoped narrower than the governed-rule corpus, so "in-scope" and
669
+ * "governed total" were genuinely different numbers, and prose copied one under
670
+ * a heading that meant the other.
671
+ *
672
+ * They agree today, because the resolver now reads the whole of `src/rules/`.
673
+ * That agreement is exactly why the frame must still be printed: a silent
674
+ * agreement is indistinguishable from a silent conflation, and the next
675
+ * narrowing of either side would reintroduce the plurality with nothing
676
+ * reporting it. So the two frames are computed from INDEPENDENT sources — this
677
+ * resolver's own row count, and `update_counts.count('rules')`, the same
678
+ * function that keeps the published artefact counts honest — and their
679
+ * agreement is asserted rather than assumed.
680
+ */
681
+ export interface DenominatorFrames {
682
+ /** Rules this resolver actually resolved. Every percentage uses this frame. */
683
+ in_scope: number;
684
+ /** The governed-rule total, counted independently by `update_counts`. */
685
+ governed_total: number;
686
+ /** `false` means the two populations have diverged and prose must say which. */
687
+ agree: boolean;
688
+ /** Where the in-scope frame comes from, for a reader re-deriving it. */
689
+ source: string;
690
+ }
691
+
692
+ export function denominator_frames(
693
+ in_scope: number,
694
+ governed = (): number => count_artefacts('rules'),
695
+ ): DenominatorFrames {
696
+ let governed_total: number;
697
+ try {
698
+ governed_total = governed();
699
+ } catch {
700
+ // An unreadable second source is reported as a divergence, never as
701
+ // agreement: "I could not check" and "they match" must not print alike.
702
+ governed_total = -1;
703
+ }
704
+ return {
705
+ in_scope,
706
+ governed_total,
707
+ agree: governed_total === in_scope,
708
+ source: 'src/rules/*.md',
709
+ };
710
+ }
711
+
712
+ /** The one sanctioned sentence for the denominator. Prose quotes this, never a literal. */
713
+ export function denominator_line(f: DenominatorFrames): string {
714
+ if (f.governed_total < 0) {
715
+ return (
716
+ `denominator: ${String(f.in_scope)} rule(s), frame in-scope (${f.source}) — ` +
717
+ `governed-total UNAVAILABLE, so agreement is unverified`
718
+ );
719
+ }
720
+ if (f.agree) {
721
+ return (
722
+ `denominator: ${String(f.in_scope)} rule(s), frame in-scope (${f.source}) ` +
723
+ `== governed-total ${String(f.governed_total)}`
724
+ );
725
+ }
726
+ return (
727
+ `denominator: ${String(f.in_scope)} rule(s) in-scope (${f.source}) vs ` +
728
+ `${String(f.governed_total)} governed-total — FRAMES DIVERGE, name the frame on every figure`
729
+ );
730
+ }
731
+
623
732
  function main(argv: string[]): number {
624
733
  const as_json = argv.includes('--json');
625
734
  const check = argv.includes('--check');
@@ -702,6 +811,10 @@ function main(argv: string[]): number {
702
811
  ` frequency: ${summary.frequency_gap} gap · ${summary.frequency_unclassified} unclassified ` +
703
812
  `(kernel — block_kernel_rule_writes denies the field)`,
704
813
  );
814
+ // The denominator is published WITH its frame, and it is the only sanctioned
815
+ // source for that number: `check_enforcement_denominator` reds when a
816
+ // tracked doc carries an enforcement count this resolver did not produce.
817
+ lines.push(` ${denominator_line(summary.frames)}`);
705
818
 
706
819
  const gaps = rows.filter((r) => r.frequency_verdict === 'gap');
707
820
  if (gaps.length > 0) {