@event4u/agent-config 14.10.0 → 14.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +335 -0
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/design.md +88 -0
  7. package/dist/agent-src/commands/project/analyze.md +47 -0
  8. package/dist/agent-src/commands/review/changes.md +67 -6
  9. package/dist/agent-src/commands/roadmap/create.md +49 -0
  10. package/dist/agent-src/commands/roadmap/next.md +27 -11
  11. package/dist/agent-src/commands/roadmap/process-full.md +14 -0
  12. package/dist/agent-src/commands/video/from-script.md +60 -3
  13. package/dist/agent-src/commands/video/stitch.md +14 -0
  14. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
  16. package/dist/agent-src/rules/active-remediation.md +3 -3
  17. package/dist/agent-src/rules/code-provenance.md +2 -2
  18. package/dist/agent-src/rules/context-hygiene.md +13 -33
  19. package/dist/agent-src/rules/council-availability.md +2 -2
  20. package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
  21. package/dist/agent-src/rules/design-fidelity.md +18 -0
  22. package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
  23. package/dist/agent-src/rules/evaluator-independence.md +56 -44
  24. package/dist/agent-src/rules/fix-what-you-see.md +9 -1
  25. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  26. package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
  27. package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
  28. package/dist/agent-src/rules/playbook-precedence.md +105 -0
  29. package/dist/agent-src/rules/recurring-criticism.md +3 -1
  30. package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
  31. package/dist/agent-src/rules/scale-discipline.md +24 -0
  32. package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
  33. package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
  34. package/dist/agent-src/rules/ui-audit-gate.md +27 -15
  35. package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
  36. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  37. package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
  38. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
  39. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
  40. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
  41. package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
  42. package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
  43. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
  44. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  45. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  46. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  47. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  52. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  53. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  54. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  55. package/dist/agent-src/skills/condense-memory/SKILL.md +3 -0
  56. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  57. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
  58. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  59. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  60. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  61. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  62. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  63. package/dist/agent-src/skills/design-review/SKILL.md +79 -1
  64. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  65. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  66. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  67. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  68. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  69. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  70. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  71. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  72. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  73. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  74. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  75. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  76. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  77. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  78. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  79. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  80. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  81. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  82. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  83. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  84. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  85. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  86. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  87. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  88. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  89. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  90. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  91. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  92. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  93. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  94. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  95. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  96. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  97. package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
  98. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  99. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  100. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  101. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  102. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  103. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  104. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  105. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  106. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  107. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  108. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  109. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  110. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  111. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  112. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  113. package/dist/agent-src/skills/receiving-code-review/SKILL.md +2 -2
  114. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  115. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  116. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  117. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  118. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  119. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  120. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  121. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  122. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  123. package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
  124. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  125. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  126. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  127. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  128. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  129. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  130. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  131. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  132. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  133. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  134. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  135. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  136. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  137. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  138. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  139. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  140. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  141. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  142. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  143. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  144. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  145. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  146. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  147. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  148. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  149. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  150. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  151. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  152. package/dist/agent-src/templates/playbook.md +75 -0
  153. package/dist/agent-src/templates/roadmaps.md +37 -0
  154. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  155. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  156. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  157. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  158. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  159. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  160. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  161. package/dist/cli/commands/uiAudit.js +221 -0
  162. package/dist/cli/commands/uiAudit.js.map +1 -0
  163. package/dist/cli/commands/uiRender.js +206 -0
  164. package/dist/cli/commands/uiRender.js.map +1 -0
  165. package/dist/cli/main.js +22 -1
  166. package/dist/cli/main.js.map +1 -1
  167. package/dist/cli/mcp/content.js +64 -1
  168. package/dist/cli/mcp/content.js.map +1 -1
  169. package/dist/cli/mcp/dispatch.js +175 -10
  170. package/dist/cli/mcp/dispatch.js.map +1 -1
  171. package/dist/cli/registry.js +3 -0
  172. package/dist/cli/registry.js.map +1 -1
  173. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  174. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  175. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  176. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  177. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  178. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  179. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  180. package/dist/cli-delegate/cmd_conformance.js +3 -3
  181. package/dist/cli-delegate/cmd_doctor.js +3 -3
  182. package/dist/cli-delegate/cmd_export.js +4 -4
  183. package/dist/cli-delegate/cmd_handoff.js +1 -1
  184. package/dist/cli-delegate/cmd_preflight.js +2 -2
  185. package/dist/cli-delegate/cmd_prune.js +4 -4
  186. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  187. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  188. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  189. package/dist/cli-delegate/cmd_sync.js +4 -4
  190. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  191. package/dist/cli-delegate/cmd_validate.js +4 -4
  192. package/dist/discovery/deprecation-report.md +1 -1
  193. package/dist/discovery/discovery-manifest.json +359 -124
  194. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  195. package/dist/discovery/discovery-manifest.summary.md +4 -4
  196. package/dist/discovery/dormancy-report.md +1 -1
  197. package/dist/discovery/orphan-report.md +1 -1
  198. package/dist/discovery/packs.json +18 -8
  199. package/dist/discovery/trust-report.md +3 -3
  200. package/dist/discovery/workspaces.json +16 -6
  201. package/dist/hooks/dispatch.js +2337 -1436
  202. package/dist/install/install.mjs +795 -708
  203. package/dist/mcp/registry-manifest.json +3 -3
  204. package/dist/mcp/server.json +2 -2
  205. package/dist/mcp/server.mjs +1685 -1250
  206. package/dist/router.json +1 -1
  207. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  208. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  209. package/dist/scripts/_lib/scoped_projection.js +84 -0
  210. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  211. package/dist/scripts/_lib/ui_surface.js +89 -0
  212. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  213. package/dist/scripts/condense.js +6 -10
  214. package/dist/scripts/condense.js.map +1 -1
  215. package/dist/scripts/project_thin_rules.js +40 -2
  216. package/dist/scripts/project_thin_rules.js.map +1 -1
  217. package/dist/server/schemas/settings.js +3 -0
  218. package/dist/server/schemas/settings.js.map +1 -1
  219. package/dist/shared/skillRanking.js +159 -0
  220. package/dist/shared/skillRanking.js.map +1 -0
  221. package/docs/contracts/provider-lifecycle.md +47 -18
  222. package/docs/contracts/settings-classes.md +14 -4
  223. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  224. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  225. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  226. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  227. package/docs/guidelines/design-antipatterns.md +4 -2
  228. package/docs/guidelines/design-asset-discipline.md +38 -0
  229. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  230. package/docs/guidelines/design-handover-extraction.md +35 -5
  231. package/docs/guidelines/design-modes.md +41 -1
  232. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  233. package/llms.txt +50 -39
  234. package/package.json +1 -1
  235. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  236. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  237. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  238. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  239. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  240. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  241. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  242. package/src/config/agent-settings.template.yml +28 -0
  243. package/src/config/assurance-capability-registry.json +223 -0
  244. package/src/config/assurance-threshold-budget.json +65 -0
  245. package/src/config/ci-local-parity.yml +14 -0
  246. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  247. package/src/config/dispatch-economy-metrics.json +4 -2
  248. package/src/config/evaluator-budgets.json +14 -14
  249. package/src/config/gate-coverage.yml +207 -8
  250. package/src/config/gate-violation-baselines.json +76 -72
  251. package/src/config/hook-latency-budget.json +1 -0
  252. package/src/config/hook-token-budget.json +137 -125
  253. package/src/config/pack-size-budget.json +4 -2
  254. package/src/config/quorum-attendance-budget.json +12 -4
  255. package/src/config/rule-activation-census.json +7 -3
  256. package/src/config/rule-enforcement-baseline.json +0 -1
  257. package/src/config/rule-stub-ceilings.json +112 -91
  258. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  259. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  260. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  261. package/src/scripts/_dispatch.bash +17 -0
  262. package/src/scripts/_lib/as_of.ts +412 -0
  263. package/src/scripts/_lib/exec_evidence.ts +7 -0
  264. package/src/scripts/_lib/hook_settings.ts +33 -0
  265. package/src/scripts/_lib/host_listing_model.ts +192 -0
  266. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  267. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  268. package/src/scripts/_lib/review_independence.ts +62 -10
  269. package/src/scripts/_lib/rule_injection.ts +239 -0
  270. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  271. package/src/scripts/_lib/scoped_projection.ts +96 -0
  272. package/src/scripts/_lib/session_register.ts +44 -2
  273. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  274. package/src/scripts/_lib/transcript_reads.ts +207 -0
  275. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  276. package/src/scripts/_lib/ui_authority.ts +343 -0
  277. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  278. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  279. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  280. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  281. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  282. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  283. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  284. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  285. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  286. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  287. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  288. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  289. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  290. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  291. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  292. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  293. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  294. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  295. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  296. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  297. package/src/scripts/ai-video/stitch.sh +191 -10
  298. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  299. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  300. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  301. package/src/scripts/ai_council/clients.ts +3 -2
  302. package/src/scripts/ai_council/consensus.ts +63 -6
  303. package/src/scripts/ai_council/events_log.ts +46 -1
  304. package/src/scripts/ai_council/orchestrator.ts +103 -108
  305. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  306. package/src/scripts/ai_council/prompts.ts +12 -5
  307. package/src/scripts/ai_council/quorum.ts +82 -1
  308. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  309. package/src/scripts/ai_council/stance_tally.ts +22 -0
  310. package/src/scripts/bench_hook_latency.ts +62 -0
  311. package/src/scripts/build_proof.ts +12 -1
  312. package/src/scripts/capture_skill_catalogue.ts +21 -0
  313. package/src/scripts/check_always_budget.ts +2 -1
  314. package/src/scripts/check_augmentignore.ts +2 -1
  315. package/src/scripts/check_beta_review_markers.ts +2 -1
  316. package/src/scripts/check_composite_arming.ts +364 -0
  317. package/src/scripts/check_corpus_staleness.ts +2 -1
  318. package/src/scripts/check_council_pin_staleness.ts +3 -1
  319. package/src/scripts/check_enforcement_coverage.ts +116 -3
  320. package/src/scripts/check_enforcement_denominator.ts +365 -0
  321. package/src/scripts/check_gate_coverage.ts +2 -1
  322. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  323. package/src/scripts/check_knowledge_cards.ts +2 -1
  324. package/src/scripts/check_knowledge_pages.ts +3 -2
  325. package/src/scripts/check_memory.ts +2 -1
  326. package/src/scripts/check_no_automerge_key.ts +287 -0
  327. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  328. package/src/scripts/check_proposal.ts +2 -1
  329. package/src/scripts/check_reach_staleness.ts +2 -1
  330. package/src/scripts/check_release_adjacent_health.ts +2 -1
  331. package/src/scripts/check_release_pr_shape.ts +5 -0
  332. package/src/scripts/check_review_schema.ts +34 -1
  333. package/src/scripts/check_roadmap_trackable.ts +48 -3
  334. package/src/scripts/check_source_size_budget.ts +2 -1
  335. package/src/scripts/check_standing_payload_delta.ts +505 -0
  336. package/src/scripts/check_trigger_evals.ts +2 -1
  337. package/src/scripts/classify_change_risk.ts +281 -0
  338. package/src/scripts/compute_skill_tiers.ts +186 -0
  339. package/src/scripts/condense.ts +8 -13
  340. package/src/scripts/council_attendance_metrics.ts +183 -0
  341. package/src/scripts/council_cli.ts +10 -8
  342. package/src/scripts/council_parse_rate.ts +116 -0
  343. package/src/scripts/derive_playbooks.ts +406 -0
  344. package/src/scripts/design_slop_rules.test.ts +7 -0
  345. package/src/scripts/design_slop_rules.ts +56 -2
  346. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  347. package/src/scripts/frontend_corpus_hash.ts +116 -0
  348. package/src/scripts/grade_target_readiness.ts +478 -0
  349. package/src/scripts/hook_manifest.json +1 -1
  350. package/src/scripts/hook_manifest.yaml +97 -16
  351. package/src/scripts/hooks/concern_registry.ts +4 -0
  352. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  353. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  354. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  355. package/src/scripts/hot_context_hook.ts +74 -7
  356. package/src/scripts/install.ts +40 -89
  357. package/src/scripts/lint_adapter_tier.ts +663 -0
  358. package/src/scripts/lint_archived_skills.ts +93 -0
  359. package/src/scripts/lint_budget_ownership.ts +2 -1
  360. package/src/scripts/lint_deterministic_time.ts +470 -0
  361. package/src/scripts/lint_framework_leakage.ts +5 -0
  362. package/src/scripts/lint_handoffs.ts +525 -1
  363. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  364. package/src/scripts/lint_marketplace.ts +88 -0
  365. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  366. package/src/scripts/lint_one_off_age.ts +6 -1
  367. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  368. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  369. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  370. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  371. package/src/scripts/lint_spawn_payload.ts +42 -5
  372. package/src/scripts/lint_symptom_intake.ts +2 -1
  373. package/src/scripts/mcp_parity_smoke.ts +30 -4
  374. package/src/scripts/mcp_render.ts +66 -1
  375. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  376. package/src/scripts/measure_risk_class_drift.ts +140 -0
  377. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  378. package/src/scripts/media/lib/adapter-common.sh +211 -1
  379. package/src/scripts/media/lib/adapter-contract.md +74 -6
  380. package/src/scripts/model_rule_injection.ts +907 -0
  381. package/src/scripts/project_thin_rules.ts +45 -2
  382. package/src/scripts/release.ts +66 -860
  383. package/src/scripts/release_drill.ts +59 -3
  384. package/src/scripts/release_env.ts +298 -0
  385. package/src/scripts/release_publication.ts +720 -0
  386. package/src/scripts/roadmap_context.ts +778 -0
  387. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  388. package/src/scripts/schemas/rule.schema.json +24 -2
  389. package/src/scripts/schemas/skill.schema.json +39 -0
  390. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  391. package/src/scripts/session_register_hook.ts +29 -3
  392. package/src/scripts/sessions_cli.ts +67 -3
  393. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  394. package/src/scripts/token_sink_report.ts +239 -0
  395. package/src/scripts/trigger_coverage.ts +122 -3
  396. package/src/scripts/update_counts.ts +7 -3
  397. package/src/scripts/validate_frontmatter.ts +66 -0
  398. package/src/server/schemas/settings.ts +5 -0
  399. package/src/shared/skillRanking.ts +183 -0
@@ -29,11 +29,13 @@ import * as fs from 'node:fs';
29
29
  import * as path from 'node:path';
30
30
  import { fileURLToPath, pathToFileURL } from 'node:url';
31
31
 
32
+ import { checkRatchet } from './_lib/gate_baseline.js';
32
33
  import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
33
34
 
34
35
  const QUIET = process.argv.slice(2).includes('--quiet');
35
36
 
36
37
  const ROADMAP_ROOT = 'agents/roadmaps';
38
+ const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
37
39
 
38
40
  // --- Replicated from .augment/scripts/update_roadmap_progress.py ------------
39
41
  // CHECKBOX_RE = re.compile(r"^\s*[-*]\s+\[([ xX~\-])\]\s", re.MULTILINE)
@@ -221,6 +223,25 @@ function violations_for(p: string): string[] {
221
223
  return out;
222
224
  }
223
225
 
226
+ /**
227
+ * Does this roadmap declare a `relates:` block?
228
+ *
229
+ * `road-to-roadmap-situational-awareness` § 4.2. `depends:` was defined in the
230
+ * template and its measured adoption was **0** — a field nothing requires is a
231
+ * field nobody writes. So the wider relation field is required, but RATCHETED:
232
+ * the roadmaps that predate the check are held at their measured count and a
233
+ * NEW one without the block pushes the count above it.
234
+ *
235
+ * Presence only. Whether the declared relations are legal is
236
+ * `lint_roadmap_complexity`'s `_check_relates`, and whether an empty list is
237
+ * honest is the pre-save self-check in `roadmap-writing` — a gate cannot tell a
238
+ * genuinely-empty `relates: []` from a reflex one, and pretending otherwise
239
+ * would be the worse failure.
240
+ */
241
+ export function declares_relates(p: string): boolean {
242
+ return parse_frontmatter(fs.readFileSync(p, 'utf-8')).has('relates');
243
+ }
244
+
224
245
  function main(): number {
225
246
  if (!_isDir(ROADMAP_ROOT)) {
226
247
  process.stderr.write(`❌ ${ROADMAP_ROOT} not found — run from project root.\n`);
@@ -244,8 +265,9 @@ function main(): number {
244
265
  }
245
266
  throw exc;
246
267
  }
268
+ const active = find_active_roadmaps(ROADMAP_ROOT);
247
269
  const findings: string[] = [];
248
- for (const p of find_active_roadmaps(ROADMAP_ROOT)) {
270
+ for (const p of active) {
249
271
  findings.push(...violations_for(p));
250
272
  }
251
273
  if (findings.length > 0) {
@@ -259,11 +281,33 @@ function main(): number {
259
281
  );
260
282
  return 1;
261
283
  }
262
- const count = find_active_roadmaps(ROADMAP_ROOT).length;
284
+ // The `relates:` ratchet. Reported separately from trackability because the
285
+ // two are different obligations: one keeps a roadmap visible to the
286
+ // dashboard, this one keeps its relations to sibling work declared.
287
+ const no_relates = active.filter((p) => !declares_relates(p));
288
+ const verdict = checkRatchet({
289
+ gate: 'check_roadmap_trackable:relates',
290
+ actual: no_relates.length,
291
+ repoRoot: REPO_ROOT,
292
+ });
293
+ if (!verdict.ok) {
294
+ process.stderr.write(`❌ ${verdict.message}\n`);
295
+ for (const p of no_relates) {
296
+ process.stderr.write(` ${p}: no \`relates:\` block in the frontmatter\n`);
297
+ }
298
+ process.stderr.write(
299
+ '\n Run `agent-config roadmap:context` and write one row per hit\n' +
300
+ " ({slug, relation: extends|supersedes|depends|disjoint, note}), or an\n" +
301
+ " explicit `relates: []` carrying the probe's `scanned:` line as its\n" +
302
+ ' justification. See templates/roadmaps.md rule 18.\n',
303
+ );
304
+ return 1;
305
+ }
263
306
  if (!QUIET) {
264
307
  process.stdout.write(
265
- `✅ ${count} active roadmap(s) — all parseable, all phases have checkboxes.\n`,
308
+ `✅ ${active.length} active roadmap(s) — all parseable, all phases have checkboxes.\n`,
266
309
  );
310
+ process.stdout.write(`✅ ${verdict.message}\n`);
267
311
  }
268
312
  return 0;
269
313
  }
@@ -296,6 +340,7 @@ if (_isCliEntry() || process.argv[1] === _HERE) {
296
340
  }
297
341
 
298
342
  export {
343
+ REPO_ROOT,
299
344
  ROADMAP_ROOT,
300
345
  CHECKBOX_RE,
301
346
  PHASE_RE,
@@ -66,6 +66,7 @@ import * as os from 'node:os';
66
66
  import * as path from 'node:path';
67
67
  import { fileURLToPath, pathToFileURL } from 'node:url';
68
68
 
69
+ import { asOf } from './_lib/as_of.js';
69
70
  import { checkRatchet } from './_lib/gate_baseline.js';
70
71
  import { GateLedger } from './_lib/gate_ledger.js';
71
72
  import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
@@ -216,7 +217,7 @@ export function selfTest(): number {
216
217
  : {
217
218
  check_source_size_budget: {
218
219
  count: baseline,
219
- landed: new Date().toISOString().slice(0, 10),
220
+ landed: asOf().toISOString().slice(0, 10),
220
221
  note: 'self-test fixture',
221
222
  },
222
223
  };
@@ -0,0 +1,505 @@
1
+ #!/usr/bin/env -S npx tsx
2
+ /**
3
+ * Per-PR standing-payload delta against the merge-base — a REPORT, not a gate.
4
+ *
5
+ * `road-to-standing-payload-diet` Phase 0 steps 0.3 / 0.4 / 0.5.
6
+ *
7
+ * ## Why a report and not a second failing gate
8
+ *
9
+ * `check_preamble_payload_budget` already ratchets the same number and already
10
+ * fails on growth. A second blocking gate on one measurement double-fails every
11
+ * legitimate rule addition, which is how a reporting surface becomes noise
12
+ * people route around. So this script **never fails on the delta**, in either
13
+ * direction. It fails only when it cannot measure: a bad ref (exit 2) or a dead
14
+ * scan scope (exit 3). That split is what makes its self-test honest — there are
15
+ * real reject cases, and none of them is "the number moved".
16
+ *
17
+ * ## The ledger is TWO-SIDED, and that is step 0.5
18
+ *
19
+ * Steps 0.3/0.4 measured only the debit — what a change ADDS. A one-sided
20
+ * ledger can only ever report drift: a change that REMOVES standing payload
21
+ * scores zero and reads as neutral, which is exactly backwards for a roadmap
22
+ * whose whole purpose is removal. So every bucket reports debit and credit
23
+ * separately, and the credit side carries a standing booking that is already
24
+ * measured and already shipped — the ADR-236 one-rule-one-layer partition. See
25
+ * {@link partitionCredit}.
26
+ *
27
+ * ## Parity with the gate it reports beside
28
+ *
29
+ * The bucket definitions are IMPORTED from `preamble_byte_census`, never
30
+ * re-implemented, so the delta cannot drift from the ratchet it explains. The
31
+ * basis is therefore the census's own `chars/4` proxy for the bucket totals —
32
+ * stated in every rendering, because this tree ships an exact tokenizer and a
33
+ * report that hides which one it used is not evidence. `--rank` additionally
34
+ * publishes an EXACT-BPE per-rule ranking, and labels it as the other basis.
35
+ * The two are never mixed inside one subtraction.
36
+ *
37
+ * Modes:
38
+ * --base <ref> measure the same buckets at <ref> and print the signed delta
39
+ * --rank additionally print the exact-BPE per-rule ranking at HEAD
40
+ * --json machine-readable, for the PR-comment renderer
41
+ * --quiet verdict lines only
42
+ * --self-test prove the gate discriminates
43
+ *
44
+ * Exit codes: 0 measured (any delta) · 2 usage / unreadable ref · 3 dead scope.
45
+ */
46
+
47
+ import { execFileSync } from 'node:child_process';
48
+ import * as fs from 'node:fs';
49
+ import * as os from 'node:os';
50
+ import * as path from 'node:path';
51
+ import process from 'node:process';
52
+ import { fileURLToPath, pathToFileURL } from 'node:url';
53
+
54
+ import { GateLedger } from './_lib/gate_ledger.js';
55
+ import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
56
+ import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
57
+ import { gpt_tokens, method_note } from './_lib/token_count.js';
58
+ import { measureDeterministicPayload } from './check_preamble_payload_budget.js';
59
+ import { censusRuleDir } from './preamble_byte_census.js';
60
+
61
+ const PROG = 'check_standing_payload_delta';
62
+ const _HERE = fileURLToPath(import.meta.url);
63
+ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
64
+ const SCRIPT_REL = 'src/scripts/check_standing_payload_delta.ts';
65
+ const SELF_TEST_MIN_CASES = 4;
66
+ const SELF_TEST_MIN_REJECT = 2;
67
+
68
+ /** The three buckets `check_preamble_payload_budget` gates, by ledger name. */
69
+ const BUCKETS = [
70
+ 'project-scope rules',
71
+ 'preloaded skills catalog',
72
+ 'CLAUDE.md hierarchy (project only)',
73
+ ] as const;
74
+
75
+ export interface Bucket {
76
+ readonly name: string;
77
+ readonly tokens: number;
78
+ readonly files: number;
79
+ }
80
+
81
+ /** `chars/4`, the census's own basis — kept identical so the delta cannot drift. */
82
+ function proxyTokens(chars: number): number {
83
+ return Math.round(chars / 4);
84
+ }
85
+
86
+ /**
87
+ * Measure the three gated buckets over a checkout root.
88
+ *
89
+ * Delegates to `check_preamble_payload_budget.measureDeterministicPayload`, the
90
+ * ratchet's OWN bucket builder, rather than re-deriving the definitions. A
91
+ * second implementation would be free to drift from the number it explains, and
92
+ * the drift would look like a real delta.
93
+ *
94
+ * That function already pins the user half of the CLAUDE.md hierarchy to a
95
+ * non-existent home directory, so a ref-side measurement cannot read the
96
+ * measuring machine's home and attribute it to a past commit — the property this
97
+ * function needed, inherited rather than re-stated.
98
+ */
99
+ export function measureBuckets(root: string): Bucket[] {
100
+ return measureDeterministicPayload(root);
101
+ }
102
+
103
+ /** Raw bytes of one tracked path at `ref`, or `null` when it does not exist there. */
104
+ function catBlob(repoRoot: string, ref: string, rel: string): Buffer | null {
105
+ try {
106
+ return execFileSync('git', ['show', `${ref}:${rel}`], {
107
+ cwd: repoRoot,
108
+ maxBuffer: 1 << 28,
109
+ encoding: 'buffer',
110
+ stdio: ['ignore', 'pipe', 'ignore'],
111
+ });
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+
117
+ /** Git's file mode for one tracked path at `ref` (`'120000'` = symlink), or null. */
118
+ function blobMode(repoRoot: string, ref: string, rel: string): string | null {
119
+ try {
120
+ const line = execFileSync('git', ['ls-tree', ref, '--', rel], {
121
+ cwd: repoRoot,
122
+ encoding: 'utf-8',
123
+ stdio: ['ignore', 'pipe', 'ignore'],
124
+ }).trim();
125
+ return line === '' ? null : (line.split(/\s+/)[0] ?? null);
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * One tracked file's CONTENT at `ref`, following a tracked symlink.
133
+ *
134
+ * `CLAUDE.md` in this repo is a symlink to `AGENTS.md` (mode `120000`,
135
+ * verified), so `git show <ref>:CLAUDE.md` yields the nine-byte link TARGET
136
+ * rather than the file. Measured on the first run: the base side reported 2
137
+ * tokens against a real 746, fabricating a +744 debit out of the symlink. A
138
+ * checkout resolves the link transparently; a blob read does not, so the hop is
139
+ * taken here. Bounded to two hops — this repo has one level and a cycle must not
140
+ * become a hang.
141
+ */
142
+ function readBlob(repoRoot: string, ref: string, rel: string): Buffer | null {
143
+ let target = rel;
144
+ for (let hop = 0; hop < 2; hop += 1) {
145
+ const raw = catBlob(repoRoot, ref, target);
146
+ if (raw === null) return null;
147
+ if (blobMode(repoRoot, ref, target) !== '120000') return raw;
148
+ const link = raw.toString('utf-8').trim();
149
+ target = path.posix.normalize(path.posix.join(path.posix.dirname(target), link));
150
+ }
151
+ return null;
152
+ }
153
+
154
+ /** Materialise `<ref>`'s gated tree into a temp dir. Returns the root. */
155
+ export function checkoutRefTree(ref: string, repoRoot: string = REPO_ROOT): string {
156
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'spd-base-'));
157
+ // `git archive | tar -x` is one round trip and honours `export-ignore`;
158
+ // `dist/` carries no `export-ignore` entry (verified: .gitattributes has no
159
+ // `/dist` line), so the gated buckets survive the tarball intact.
160
+ try {
161
+ const tar = execFileSync('git', ['archive', ref, 'dist/agent-src/rules', 'dist/agent-src/skills'], {
162
+ cwd: repoRoot,
163
+ maxBuffer: 1 << 30,
164
+ encoding: 'buffer',
165
+ });
166
+ execFileSync('tar', ['-x', '-C', dir], { input: tar, maxBuffer: 1 << 30 });
167
+ // `CLAUDE.md` carries `export-ignore` (`.gitattributes:26`), so `git
168
+ // archive` omits it SILENTLY — measured here on the first run, which
169
+ // reported a fabricated 104-token movement in the wrong bucket. It is
170
+ // therefore fetched by blob, not by tarball. A missing project CLAUDE.md
171
+ // at `ref` is a real state (the file may not have existed), so absence
172
+ // is left absent rather than defaulted.
173
+ const claudeMd = readBlob(repoRoot, ref, 'CLAUDE.md');
174
+ if (claudeMd !== null) fs.writeFileSync(path.join(dir, 'CLAUDE.md'), claudeMd);
175
+ const claudeLocal = readBlob(repoRoot, ref, 'CLAUDE.local.md');
176
+ if (claudeLocal !== null) fs.writeFileSync(path.join(dir, 'CLAUDE.local.md'), claudeLocal);
177
+ } catch (err) {
178
+ fs.rmSync(dir, { recursive: true, force: true });
179
+ throw new Error(`${PROG}: cannot read tree at ref '${ref}': ${String(err)}`);
180
+ }
181
+ return dir;
182
+ }
183
+
184
+ export interface DeltaRow {
185
+ readonly name: string;
186
+ /** Positive tokens the change ADDS to this bucket. */
187
+ readonly debit: number;
188
+ /** Positive tokens the change REMOVES from this bucket. */
189
+ readonly credit: number;
190
+ readonly base: number;
191
+ readonly head: number;
192
+ readonly baseFiles: number;
193
+ readonly headFiles: number;
194
+ }
195
+
196
+ /**
197
+ * Split each bucket's signed movement into a debit and a credit column.
198
+ *
199
+ * A signed number collapses the two sides into one cell, and the whole point of
200
+ * step 0.5 is that a removal must be legible as a removal rather than as a
201
+ * smaller positive.
202
+ */
203
+ export function diffBuckets(base: readonly Bucket[], head: readonly Bucket[]): DeltaRow[] {
204
+ const byName = new Map(base.map((b) => [b.name, b]));
205
+ return head.map((h) => {
206
+ const b = byName.get(h.name) ?? { name: h.name, tokens: 0, files: 0 };
207
+ const signed = h.tokens - b.tokens;
208
+ return {
209
+ name: h.name,
210
+ debit: signed > 0 ? signed : 0,
211
+ credit: signed < 0 ? -signed : 0,
212
+ base: b.tokens,
213
+ head: h.tokens,
214
+ baseFiles: b.files,
215
+ headFiles: h.files,
216
+ };
217
+ });
218
+ }
219
+
220
+ export interface CreditBooking {
221
+ readonly what: string;
222
+ readonly gate: string;
223
+ readonly tokens: number;
224
+ readonly note: string;
225
+ }
226
+
227
+ /**
228
+ * The standing credit booking — ADR-236's one-rule-one-layer partition.
229
+ *
230
+ * `check_rule_layer_partition` publishes, per host directory, how many rules the
231
+ * project layer WITHHELD because the global layer already carries them. Those
232
+ * withheld files are a saving that is already measured and already shipped, and
233
+ * a debit-only ledger books it at zero.
234
+ *
235
+ * It is a **standing** booking, not a per-PR one: it does not move with the diff
236
+ * under review, and it is rendered as such. Its purpose is that a reader of the
237
+ * comment sees net movement against a real credit floor rather than inflow
238
+ * alone. The number is read from the live gate on the measuring machine; where
239
+ * the gate cannot run (no projections in a fresh CI checkout, which is the
240
+ * normal case) the booking reports as unavailable rather than as zero — a
241
+ * credit of zero and a credit nobody could read are different facts.
242
+ */
243
+ export function partitionCredit(repoRoot: string = REPO_ROOT): CreditBooking | null {
244
+ const globalDir = path.join(os.homedir(), '.claude', 'rules');
245
+ const projectDir = path.join(repoRoot, '.claude', 'rules');
246
+ if (!fs.existsSync(globalDir) || !fs.existsSync(projectDir)) return null;
247
+ const globalCensus = censusRuleDir(globalDir);
248
+ const projectCensus = censusRuleDir(projectDir);
249
+ if (globalCensus.files === 0) return null;
250
+ const globalNames = new Set(fs.readdirSync(globalDir).filter((f) => f.endsWith('.md')));
251
+ const projectNames = new Set(fs.readdirSync(projectDir).filter((f) => f.endsWith('.md')));
252
+ // Withheld = a global-owned rule the project layer did NOT re-emit. Under a
253
+ // duplicating emitter this set is empty and the credit is honestly zero.
254
+ let withheldChars = 0;
255
+ let withheld = 0;
256
+ for (const name of globalNames) {
257
+ if (projectNames.has(name)) continue;
258
+ try {
259
+ withheldChars += fs.statSync(path.join(globalDir, name)).size;
260
+ withheld += 1;
261
+ } catch {
262
+ /* unreadable entry — not counted, so the credit under-reports */
263
+ }
264
+ }
265
+ return {
266
+ what: `ADR-236 partition: ${String(withheld)} global-owned rule(s) withheld from .claude/rules`,
267
+ gate: 'check_rule_layer_partition',
268
+ tokens: proxyTokens(withheldChars),
269
+ note:
270
+ `project layer carries ${String(projectCensus.files)} file(s); global layer ` +
271
+ `${String(globalCensus.files)}. A duplicating emitter would book 0 here.`,
272
+ };
273
+ }
274
+
275
+ export interface PerRule {
276
+ readonly rule: string;
277
+ readonly tokens: number;
278
+ }
279
+
280
+ /** Exact-BPE per-rule ranking at HEAD — the other basis, labelled as such. */
281
+ export function rankRules(dir: string): PerRule[] {
282
+ let entries: string[];
283
+ try {
284
+ entries = fs.readdirSync(dir);
285
+ } catch {
286
+ return [];
287
+ }
288
+ const rows: PerRule[] = [];
289
+ for (const name of entries) {
290
+ if (!name.endsWith('.md')) continue;
291
+ try {
292
+ rows.push({ rule: name, tokens: gpt_tokens(fs.readFileSync(path.join(dir, name), 'utf-8')).tokens });
293
+ } catch {
294
+ /* unreadable entry — skipped rather than failing the whole ranking */
295
+ }
296
+ }
297
+ rows.sort((a, b) => b.tokens - a.tokens);
298
+ return rows;
299
+ }
300
+
301
+ export interface Report {
302
+ readonly base_ref: string | null;
303
+ readonly basis: string;
304
+ readonly rank_basis: string;
305
+ readonly buckets: Bucket[];
306
+ readonly rows: DeltaRow[];
307
+ readonly total_debit: number;
308
+ readonly total_credit: number;
309
+ readonly net: number;
310
+ readonly credit_bookings: CreditBooking[];
311
+ readonly ranking: PerRule[];
312
+ }
313
+
314
+ export function buildReport(opts: { base: string | null; rank: boolean }): Report {
315
+ const head = measureBuckets(REPO_ROOT);
316
+ let rows: DeltaRow[] = [];
317
+ if (opts.base !== null) {
318
+ const dir = checkoutRefTree(opts.base);
319
+ try {
320
+ rows = diffBuckets(measureBuckets(dir), head);
321
+ } finally {
322
+ fs.rmSync(dir, { recursive: true, force: true });
323
+ }
324
+ }
325
+ const booking = partitionCredit();
326
+ return {
327
+ base_ref: opts.base,
328
+ basis: 'chars/4 proxy over the projected tree (identical to preamble_byte_census)',
329
+ rank_basis: method_note(),
330
+ buckets: head,
331
+ rows,
332
+ total_debit: rows.reduce((s, r) => s + r.debit, 0),
333
+ total_credit: rows.reduce((s, r) => s + r.credit, 0),
334
+ net: rows.reduce((s, r) => s + r.debit - r.credit, 0),
335
+ credit_bookings: booking === null ? [] : [booking],
336
+ ranking: opts.rank ? rankRules(path.join(REPO_ROOT, 'dist', 'agent-src', 'rules')) : [],
337
+ };
338
+ }
339
+
340
+ export function renderMarkdown(r: Report): string {
341
+ const sign = (n: number): string => (n > 0 ? `+${String(n)}` : String(n));
342
+ const out: string[] = ['## Standing payload — per-PR delta (report only)', ''];
343
+ out.push(`**Basis:** ${r.basis}. This step **reports**; \`check_preamble_payload_budget\` is the gate.`, '');
344
+ if (r.base_ref === null) {
345
+ out.push('_No base ref given — HEAD buckets only._', '');
346
+ } else {
347
+ out.push(`**Merge-base:** \`${r.base_ref}\``, '');
348
+ out.push('| Bucket | base | head | debit | credit |', '|---|---:|---:|---:|---:|');
349
+ for (const row of r.rows) {
350
+ out.push(
351
+ `| ${row.name} (${String(row.baseFiles)} → ${String(row.headFiles)} files) ` +
352
+ `| ${String(row.base)} | ${String(row.head)} | ` +
353
+ `${row.debit === 0 ? '—' : `+${String(row.debit)}`} | ` +
354
+ `${row.credit === 0 ? '—' : `−${String(row.credit)}`} |`,
355
+ );
356
+ }
357
+ out.push(
358
+ `| **total** | | | **+${String(r.total_debit)}** | **−${String(r.total_credit)}** |`,
359
+ '',
360
+ `**Net:** \`${sign(r.net)}\` tok on the gated per-spawn payload.`,
361
+ '',
362
+ );
363
+ }
364
+ out.push('### Credit side (standing bookings)', '');
365
+ if (r.credit_bookings.length === 0) {
366
+ out.push(
367
+ '_Unavailable in this environment: no host rule projection to read, so the ' +
368
+ 'partition credit cannot be measured here. Unavailable is reported as ' +
369
+ 'unavailable, never as zero._',
370
+ '',
371
+ );
372
+ } else {
373
+ out.push('| Booking | measured by | tokens |', '|---|---|---:|');
374
+ for (const b of r.credit_bookings) {
375
+ out.push(`| ${b.what} | \`${b.gate}\` | −${String(b.tokens)} |`);
376
+ }
377
+ out.push('', ...r.credit_bookings.map((b) => `_${b.note}_`), '');
378
+ }
379
+ if (r.ranking.length > 0) {
380
+ out.push(`### Heaviest projected rules (${r.rank_basis})`, '');
381
+ out.push('| tokens | rule |', '|---:|---|');
382
+ for (const p of r.ranking.slice(0, 20)) out.push(`| ${String(p.tokens)} | \`${p.rule}\` |`);
383
+ out.push('');
384
+ }
385
+ return out.join('\n');
386
+ }
387
+
388
+ function parse(argv: readonly string[]): { base: string | null; rank: boolean; json: boolean; quiet: boolean } {
389
+ let base: string | null = null;
390
+ let rank = false;
391
+ let json = false;
392
+ let quiet = false;
393
+ for (let i = 0; i < argv.length; i += 1) {
394
+ const a = argv[i];
395
+ if (a === '--base') {
396
+ const v = argv[i + 1];
397
+ if (v === undefined || v.startsWith('--')) throw new Error(`${PROG}: --base needs a ref`);
398
+ base = v;
399
+ i += 1;
400
+ } else if (a === '--rank') rank = true;
401
+ else if (a === '--json') json = true;
402
+ else if (a === '--quiet') quiet = true;
403
+ else throw new Error(`${PROG}: unknown argument '${String(a)}'`);
404
+ }
405
+ return { base, rank, json, quiet };
406
+ }
407
+
408
+ export function main(argv: readonly string[] = process.argv.slice(2)): number {
409
+ if (argv.includes('--self-test')) return selfTest();
410
+ let opts: ReturnType<typeof parse>;
411
+ try {
412
+ opts = parse(argv);
413
+ } catch (err) {
414
+ process.stderr.write(`${String((err as Error).message)}\n`);
415
+ return 2;
416
+ }
417
+
418
+ let report: Report;
419
+ try {
420
+ report = buildReport({ base: opts.base, rank: opts.rank });
421
+ } catch (err) {
422
+ process.stderr.write(`${String((err as Error).message)}\n`);
423
+ return 2;
424
+ }
425
+
426
+ // Ledger: one target per gated bucket, so "measured nothing" and "measured
427
+ // one of three" stop being the same green line.
428
+ const ledger = new GateLedger(PROG);
429
+ ledger.plan([...BUCKETS]);
430
+ for (const b of report.buckets) {
431
+ if (b.files === 0) ledger.skip(b.name, 'no_applicable_files');
432
+ else ledger.complete(b.name);
433
+ }
434
+
435
+ if (opts.json) {
436
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
437
+ } else if (!opts.quiet) {
438
+ process.stdout.write(`${renderMarkdown(report)}\n`);
439
+ } else {
440
+ const sign = report.net > 0 ? `+${String(report.net)}` : String(report.net);
441
+ process.stdout.write(
442
+ `${PROG}: net ${sign} tok (debit +${String(report.total_debit)} / ` +
443
+ `credit −${String(report.total_credit)}) vs ${report.base_ref ?? '(no base)'}\n`,
444
+ );
445
+ }
446
+
447
+ const scanned = report.buckets.filter((b) => b.files > 0).length;
448
+ try {
449
+ reportScanned({ gate: PROG, scanned, units: 'gated payload bucket(s)', roots: ['dist/agent-src'] });
450
+ } catch (err) {
451
+ if (err instanceof DeadScopeError) {
452
+ process.stderr.write(`❌ ${err.message}\n`);
453
+ return 3;
454
+ }
455
+ throw err;
456
+ }
457
+ ledger.report();
458
+ // Report-only by contract: a moved number is never a failure here.
459
+ return 0;
460
+ }
461
+
462
+ /**
463
+ * Prove the gate discriminates.
464
+ *
465
+ * The reject cases are deliberately NOT "the delta is large" — a report that
466
+ * failed on its own number would be the second blocking gate this script exists
467
+ * not to be. They are the two ways it can fail to MEASURE: an unreadable ref and
468
+ * a usage error. The accept cases prove a real delta and a no-base run both
469
+ * still exit 0.
470
+ */
471
+ function selfTest(): number {
472
+ const cases: SelfTestCase[] = [
473
+ {
474
+ name: 'unreadable base ref → exit 2',
475
+ expect: 'reject',
476
+ run: () => runGateCli(REPO_ROOT, SCRIPT_REL, ['--base', 'no-such-ref-xyzzy', '--quiet'], REPO_ROOT),
477
+ },
478
+ {
479
+ name: 'unknown argument → exit 2',
480
+ expect: 'reject',
481
+ run: () => runGateCli(REPO_ROOT, SCRIPT_REL, ['--nonsense'], REPO_ROOT),
482
+ },
483
+ {
484
+ name: 'no base ref → HEAD-only report, exit 0',
485
+ expect: 'accept',
486
+ run: () => runGateCli(REPO_ROOT, SCRIPT_REL, ['--quiet'], REPO_ROOT),
487
+ },
488
+ {
489
+ name: 'real base ref → measured delta, still exit 0',
490
+ expect: 'accept',
491
+ run: () => runGateCli(REPO_ROOT, SCRIPT_REL, ['--base', 'HEAD', '--quiet'], REPO_ROOT),
492
+ },
493
+ ];
494
+ return runSelfTest({ gate: PROG, cases, minCases: SELF_TEST_MIN_CASES, minRejectCases: SELF_TEST_MIN_REJECT });
495
+ }
496
+
497
+ function invokedDirectly(): boolean {
498
+ const entry = process.argv[1];
499
+ if (entry === undefined) return false;
500
+ return import.meta.url === pathToFileURL(path.resolve(entry)).href;
501
+ }
502
+
503
+ if (invokedDirectly()) {
504
+ process.exit(main());
505
+ }
@@ -27,6 +27,7 @@ import * as fs from 'node:fs';
27
27
  import * as path from 'node:path';
28
28
  import { fileURLToPath, pathToFileURL } from 'node:url';
29
29
 
30
+ import { asOf } from './_lib/as_of.js';
30
31
  import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
31
32
 
32
33
  const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
@@ -99,7 +100,7 @@ function _days_between(a: PyDate, b: PyDate): number {
99
100
 
100
101
  /** Today's local date, matching `date.today()`. */
101
102
  function _today_local(): PyDate {
102
- const now = new Date();
103
+ const now = asOf();
103
104
  return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };
104
105
  }
105
106