@event4u/agent-config 14.10.0 → 14.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +335 -0
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/design.md +88 -0
  7. package/dist/agent-src/commands/project/analyze.md +47 -0
  8. package/dist/agent-src/commands/review/changes.md +67 -6
  9. package/dist/agent-src/commands/roadmap/create.md +49 -0
  10. package/dist/agent-src/commands/roadmap/next.md +27 -11
  11. package/dist/agent-src/commands/roadmap/process-full.md +14 -0
  12. package/dist/agent-src/commands/video/from-script.md +60 -3
  13. package/dist/agent-src/commands/video/stitch.md +14 -0
  14. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
  16. package/dist/agent-src/rules/active-remediation.md +3 -3
  17. package/dist/agent-src/rules/code-provenance.md +2 -2
  18. package/dist/agent-src/rules/context-hygiene.md +13 -33
  19. package/dist/agent-src/rules/council-availability.md +2 -2
  20. package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
  21. package/dist/agent-src/rules/design-fidelity.md +18 -0
  22. package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
  23. package/dist/agent-src/rules/evaluator-independence.md +56 -44
  24. package/dist/agent-src/rules/fix-what-you-see.md +9 -1
  25. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  26. package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
  27. package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
  28. package/dist/agent-src/rules/playbook-precedence.md +105 -0
  29. package/dist/agent-src/rules/recurring-criticism.md +3 -1
  30. package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
  31. package/dist/agent-src/rules/scale-discipline.md +24 -0
  32. package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
  33. package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
  34. package/dist/agent-src/rules/ui-audit-gate.md +27 -15
  35. package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
  36. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  37. package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
  38. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
  39. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
  40. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
  41. package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
  42. package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
  43. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
  44. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  45. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  46. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  47. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  52. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  53. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  54. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  55. package/dist/agent-src/skills/condense-memory/SKILL.md +3 -0
  56. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  57. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
  58. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  59. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  60. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  61. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  62. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  63. package/dist/agent-src/skills/design-review/SKILL.md +79 -1
  64. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  65. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  66. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  67. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  68. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  69. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  70. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  71. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  72. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  73. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  74. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  75. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  76. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  77. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  78. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  79. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  80. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  81. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  82. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  83. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  84. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  85. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  86. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  87. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  88. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  89. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  90. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  91. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  92. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  93. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  94. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  95. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  96. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  97. package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
  98. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  99. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  100. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  101. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  102. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  103. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  104. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  105. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  106. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  107. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  108. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  109. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  110. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  111. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  112. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  113. package/dist/agent-src/skills/receiving-code-review/SKILL.md +2 -2
  114. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  115. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  116. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  117. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  118. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  119. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  120. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  121. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  122. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  123. package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
  124. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  125. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  126. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  127. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  128. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  129. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  130. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  131. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  132. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  133. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  134. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  135. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  136. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  137. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  138. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  139. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  140. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  141. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  142. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  143. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  144. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  145. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  146. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  147. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  148. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  149. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  150. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  151. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  152. package/dist/agent-src/templates/playbook.md +75 -0
  153. package/dist/agent-src/templates/roadmaps.md +37 -0
  154. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  155. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  156. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  157. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  158. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  159. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  160. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  161. package/dist/cli/commands/uiAudit.js +221 -0
  162. package/dist/cli/commands/uiAudit.js.map +1 -0
  163. package/dist/cli/commands/uiRender.js +206 -0
  164. package/dist/cli/commands/uiRender.js.map +1 -0
  165. package/dist/cli/main.js +22 -1
  166. package/dist/cli/main.js.map +1 -1
  167. package/dist/cli/mcp/content.js +64 -1
  168. package/dist/cli/mcp/content.js.map +1 -1
  169. package/dist/cli/mcp/dispatch.js +175 -10
  170. package/dist/cli/mcp/dispatch.js.map +1 -1
  171. package/dist/cli/registry.js +3 -0
  172. package/dist/cli/registry.js.map +1 -1
  173. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  174. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  175. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  176. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  177. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  178. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  179. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  180. package/dist/cli-delegate/cmd_conformance.js +3 -3
  181. package/dist/cli-delegate/cmd_doctor.js +3 -3
  182. package/dist/cli-delegate/cmd_export.js +4 -4
  183. package/dist/cli-delegate/cmd_handoff.js +1 -1
  184. package/dist/cli-delegate/cmd_preflight.js +2 -2
  185. package/dist/cli-delegate/cmd_prune.js +4 -4
  186. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  187. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  188. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  189. package/dist/cli-delegate/cmd_sync.js +4 -4
  190. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  191. package/dist/cli-delegate/cmd_validate.js +4 -4
  192. package/dist/discovery/deprecation-report.md +1 -1
  193. package/dist/discovery/discovery-manifest.json +359 -124
  194. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  195. package/dist/discovery/discovery-manifest.summary.md +4 -4
  196. package/dist/discovery/dormancy-report.md +1 -1
  197. package/dist/discovery/orphan-report.md +1 -1
  198. package/dist/discovery/packs.json +18 -8
  199. package/dist/discovery/trust-report.md +3 -3
  200. package/dist/discovery/workspaces.json +16 -6
  201. package/dist/hooks/dispatch.js +2337 -1436
  202. package/dist/install/install.mjs +795 -708
  203. package/dist/mcp/registry-manifest.json +3 -3
  204. package/dist/mcp/server.json +2 -2
  205. package/dist/mcp/server.mjs +1685 -1250
  206. package/dist/router.json +1 -1
  207. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  208. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  209. package/dist/scripts/_lib/scoped_projection.js +84 -0
  210. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  211. package/dist/scripts/_lib/ui_surface.js +89 -0
  212. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  213. package/dist/scripts/condense.js +6 -10
  214. package/dist/scripts/condense.js.map +1 -1
  215. package/dist/scripts/project_thin_rules.js +40 -2
  216. package/dist/scripts/project_thin_rules.js.map +1 -1
  217. package/dist/server/schemas/settings.js +3 -0
  218. package/dist/server/schemas/settings.js.map +1 -1
  219. package/dist/shared/skillRanking.js +159 -0
  220. package/dist/shared/skillRanking.js.map +1 -0
  221. package/docs/contracts/provider-lifecycle.md +47 -18
  222. package/docs/contracts/settings-classes.md +14 -4
  223. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  224. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  225. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  226. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  227. package/docs/guidelines/design-antipatterns.md +4 -2
  228. package/docs/guidelines/design-asset-discipline.md +38 -0
  229. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  230. package/docs/guidelines/design-handover-extraction.md +35 -5
  231. package/docs/guidelines/design-modes.md +41 -1
  232. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  233. package/llms.txt +50 -39
  234. package/package.json +1 -1
  235. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  236. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  237. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  238. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  239. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  240. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  241. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  242. package/src/config/agent-settings.template.yml +28 -0
  243. package/src/config/assurance-capability-registry.json +223 -0
  244. package/src/config/assurance-threshold-budget.json +65 -0
  245. package/src/config/ci-local-parity.yml +14 -0
  246. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  247. package/src/config/dispatch-economy-metrics.json +4 -2
  248. package/src/config/evaluator-budgets.json +14 -14
  249. package/src/config/gate-coverage.yml +207 -8
  250. package/src/config/gate-violation-baselines.json +76 -72
  251. package/src/config/hook-latency-budget.json +1 -0
  252. package/src/config/hook-token-budget.json +137 -125
  253. package/src/config/pack-size-budget.json +4 -2
  254. package/src/config/quorum-attendance-budget.json +12 -4
  255. package/src/config/rule-activation-census.json +7 -3
  256. package/src/config/rule-enforcement-baseline.json +0 -1
  257. package/src/config/rule-stub-ceilings.json +112 -91
  258. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  259. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  260. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  261. package/src/scripts/_dispatch.bash +17 -0
  262. package/src/scripts/_lib/as_of.ts +412 -0
  263. package/src/scripts/_lib/exec_evidence.ts +7 -0
  264. package/src/scripts/_lib/hook_settings.ts +33 -0
  265. package/src/scripts/_lib/host_listing_model.ts +192 -0
  266. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  267. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  268. package/src/scripts/_lib/review_independence.ts +62 -10
  269. package/src/scripts/_lib/rule_injection.ts +239 -0
  270. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  271. package/src/scripts/_lib/scoped_projection.ts +96 -0
  272. package/src/scripts/_lib/session_register.ts +44 -2
  273. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  274. package/src/scripts/_lib/transcript_reads.ts +207 -0
  275. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  276. package/src/scripts/_lib/ui_authority.ts +343 -0
  277. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  278. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  279. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  280. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  281. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  282. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  283. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  284. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  285. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  286. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  287. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  288. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  289. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  290. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  291. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  292. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  293. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  294. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  295. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  296. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  297. package/src/scripts/ai-video/stitch.sh +191 -10
  298. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  299. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  300. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  301. package/src/scripts/ai_council/clients.ts +3 -2
  302. package/src/scripts/ai_council/consensus.ts +63 -6
  303. package/src/scripts/ai_council/events_log.ts +46 -1
  304. package/src/scripts/ai_council/orchestrator.ts +103 -108
  305. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  306. package/src/scripts/ai_council/prompts.ts +12 -5
  307. package/src/scripts/ai_council/quorum.ts +82 -1
  308. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  309. package/src/scripts/ai_council/stance_tally.ts +22 -0
  310. package/src/scripts/bench_hook_latency.ts +62 -0
  311. package/src/scripts/build_proof.ts +12 -1
  312. package/src/scripts/capture_skill_catalogue.ts +21 -0
  313. package/src/scripts/check_always_budget.ts +2 -1
  314. package/src/scripts/check_augmentignore.ts +2 -1
  315. package/src/scripts/check_beta_review_markers.ts +2 -1
  316. package/src/scripts/check_composite_arming.ts +364 -0
  317. package/src/scripts/check_corpus_staleness.ts +2 -1
  318. package/src/scripts/check_council_pin_staleness.ts +3 -1
  319. package/src/scripts/check_enforcement_coverage.ts +116 -3
  320. package/src/scripts/check_enforcement_denominator.ts +365 -0
  321. package/src/scripts/check_gate_coverage.ts +2 -1
  322. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  323. package/src/scripts/check_knowledge_cards.ts +2 -1
  324. package/src/scripts/check_knowledge_pages.ts +3 -2
  325. package/src/scripts/check_memory.ts +2 -1
  326. package/src/scripts/check_no_automerge_key.ts +287 -0
  327. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  328. package/src/scripts/check_proposal.ts +2 -1
  329. package/src/scripts/check_reach_staleness.ts +2 -1
  330. package/src/scripts/check_release_adjacent_health.ts +2 -1
  331. package/src/scripts/check_release_pr_shape.ts +5 -0
  332. package/src/scripts/check_review_schema.ts +34 -1
  333. package/src/scripts/check_roadmap_trackable.ts +48 -3
  334. package/src/scripts/check_source_size_budget.ts +2 -1
  335. package/src/scripts/check_standing_payload_delta.ts +505 -0
  336. package/src/scripts/check_trigger_evals.ts +2 -1
  337. package/src/scripts/classify_change_risk.ts +281 -0
  338. package/src/scripts/compute_skill_tiers.ts +186 -0
  339. package/src/scripts/condense.ts +8 -13
  340. package/src/scripts/council_attendance_metrics.ts +183 -0
  341. package/src/scripts/council_cli.ts +10 -8
  342. package/src/scripts/council_parse_rate.ts +116 -0
  343. package/src/scripts/derive_playbooks.ts +406 -0
  344. package/src/scripts/design_slop_rules.test.ts +7 -0
  345. package/src/scripts/design_slop_rules.ts +56 -2
  346. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  347. package/src/scripts/frontend_corpus_hash.ts +116 -0
  348. package/src/scripts/grade_target_readiness.ts +478 -0
  349. package/src/scripts/hook_manifest.json +1 -1
  350. package/src/scripts/hook_manifest.yaml +97 -16
  351. package/src/scripts/hooks/concern_registry.ts +4 -0
  352. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  353. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  354. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  355. package/src/scripts/hot_context_hook.ts +74 -7
  356. package/src/scripts/install.ts +40 -89
  357. package/src/scripts/lint_adapter_tier.ts +663 -0
  358. package/src/scripts/lint_archived_skills.ts +93 -0
  359. package/src/scripts/lint_budget_ownership.ts +2 -1
  360. package/src/scripts/lint_deterministic_time.ts +470 -0
  361. package/src/scripts/lint_framework_leakage.ts +5 -0
  362. package/src/scripts/lint_handoffs.ts +525 -1
  363. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  364. package/src/scripts/lint_marketplace.ts +88 -0
  365. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  366. package/src/scripts/lint_one_off_age.ts +6 -1
  367. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  368. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  369. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  370. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  371. package/src/scripts/lint_spawn_payload.ts +42 -5
  372. package/src/scripts/lint_symptom_intake.ts +2 -1
  373. package/src/scripts/mcp_parity_smoke.ts +30 -4
  374. package/src/scripts/mcp_render.ts +66 -1
  375. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  376. package/src/scripts/measure_risk_class_drift.ts +140 -0
  377. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  378. package/src/scripts/media/lib/adapter-common.sh +211 -1
  379. package/src/scripts/media/lib/adapter-contract.md +74 -6
  380. package/src/scripts/model_rule_injection.ts +907 -0
  381. package/src/scripts/project_thin_rules.ts +45 -2
  382. package/src/scripts/release.ts +66 -860
  383. package/src/scripts/release_drill.ts +59 -3
  384. package/src/scripts/release_env.ts +298 -0
  385. package/src/scripts/release_publication.ts +720 -0
  386. package/src/scripts/roadmap_context.ts +778 -0
  387. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  388. package/src/scripts/schemas/rule.schema.json +24 -2
  389. package/src/scripts/schemas/skill.schema.json +39 -0
  390. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  391. package/src/scripts/session_register_hook.ts +29 -3
  392. package/src/scripts/sessions_cli.ts +67 -3
  393. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  394. package/src/scripts/token_sink_report.ts +239 -0
  395. package/src/scripts/trigger_coverage.ts +122 -3
  396. package/src/scripts/update_counts.ts +7 -3
  397. package/src/scripts/validate_frontmatter.ts +66 -0
  398. package/src/server/schemas/settings.ts +5 -0
  399. package/src/shared/skillRanking.ts +183 -0
@@ -0,0 +1,412 @@
1
+ /**
2
+ * `asOf()` — the one place a gate is allowed to ask what time it is.
3
+ *
4
+ * WHY THIS EXISTS
5
+ * ---------------
6
+ * A gate's verdict should be a function of the tree, not of the hour it ran.
7
+ * Measured on 2026-08-23 (`grep -lE 'Date\.now\(\)|new Date\(\)'
8
+ * src/scripts/check_*.ts src/scripts/lint_*.ts`): **17** gate scripts read the
9
+ * wall clock directly, and no `--as-of` / `AC_AS_OF` surface existed anywhere in
10
+ * `src/scripts/`. The consequence is not theoretical — every one of those
11
+ * verdicts is a staleness or age judgement, so a green on a reviewer's machine
12
+ * on Monday is a different verdict from the same tree on Friday, and neither is
13
+ * reproducible from the commit alone.
14
+ *
15
+ * THE RESOLUTION ORDER
16
+ * --------------------
17
+ * First rung that answers wins:
18
+ *
19
+ * 1. `--as-of <iso>` on argv (also `--as-of=<iso>`) — an explicit pin. This is
20
+ * what CI passes, and what a reviewer passes to reproduce a CI verdict.
21
+ * 2. `AC_AS_OF` in the environment — the same pin where argv is not reachable
22
+ * (a gate invoked through a wrapper, a hook, or a task runner).
23
+ * 3. The commit date of the tree under test, when `CI` is set. Committed,
24
+ * therefore reproducible; and the change under review cannot rewrite it
25
+ * without changing the commit.
26
+ * 4. The wall clock, with a one-line WARN on stderr naming the run as
27
+ * non-reproducible. The fallback STAYS so no gate loses its ability to run
28
+ * outside CI; it just stops being silent about what it did.
29
+ *
30
+ * WHY RUNG 3 IS THE **COMMIT** DATE AND NOT THE MERGE-BASE DATE
31
+ * ------------------------------------------------------------
32
+ * The roadmap that commissioned this seam
33
+ * (`agents/roadmaps/road-to-deterministic-time-in-gates.md`, step 1.1) specifies
34
+ * "the merge-base commit date when running in CI". Implementing that literally
35
+ * would **weaken every gate it touches**, which is why it is not implemented
36
+ * literally and the deviation is recorded here rather than in a commit message
37
+ * nobody reads.
38
+ *
39
+ * The merge-base date is, by construction, `<=` the HEAD commit date: it is
40
+ * where the branch forked from its base. All 17 callers are *age* gates — "this
41
+ * pin is stale", "this one-off has expired", "this review marker is overdue" —
42
+ * so an EARLIER "now" makes every one of them strictly more permissive. Pinning
43
+ * to the merge-base therefore hands a long-lived branch a free extension on
44
+ * every staleness budget in the tree, silently, proportional to the branch's
45
+ * age. That is a gate weakening bought with a determinism improvement, and the
46
+ * determinism improvement does not require it.
47
+ *
48
+ * The HEAD commit date has the same reproducibility property — it is committed,
49
+ * a reviewer can recover it from the commit, and two runs of the same commit
50
+ * agree — while being the *tightest* committed clock available. It is never
51
+ * later than real time and never earlier than the merge-base. So it is what
52
+ * rung 3 reads.
53
+ *
54
+ * WHAT THIS MODULE DELIBERATELY DOES NOT DO
55
+ * -----------------------------------------
56
+ * It does not change a single threshold, message, or exit code. Substituting
57
+ * `asOf()` for `new Date()` is mechanical by design (roadmap step 1.2): a gate
58
+ * that flips its verdict on the substitution has found a real dependency on the
59
+ * hour, which is the defect being fixed, not a regression introduced by it.
60
+ */
61
+ import { spawnSync } from 'node:child_process';
62
+ import * as fs from 'node:fs';
63
+ import * as path from 'node:path';
64
+ import process from 'node:process';
65
+ import { fileURLToPath } from 'node:url';
66
+
67
+ /** Which rung of the resolution order answered. */
68
+ export type AsOfRung = 'argv' | 'env' | 'commit' | 'wall-clock';
69
+
70
+ export interface AsOfResolution {
71
+ /** The resolved "now". */
72
+ at: Date;
73
+ /** Which rung produced it. */
74
+ rung: AsOfRung;
75
+ /**
76
+ * `true` when the same tree would resolve the same instant again. Only the
77
+ * wall-clock rung is `false`.
78
+ */
79
+ reproducible: boolean;
80
+ }
81
+
82
+ /** Injection seam — every source the resolver reads, so the self-test can drive it. */
83
+ export interface AsOfSources {
84
+ argv?: readonly string[];
85
+ env?: Record<string, string | undefined>;
86
+ /** Committed clock for rung 3. `null` when it cannot be established. */
87
+ commitDate?: () => Date | null;
88
+ /** Rung 4. */
89
+ wallClock?: () => Date;
90
+ /** Where the non-reproducibility WARN goes. */
91
+ warn?: (line: string) => void;
92
+ }
93
+
94
+ /**
95
+ * Thrown when an explicit pin is present but unparseable.
96
+ *
97
+ * A malformed pin is never silently downgraded to the next rung: the caller
98
+ * asked for a specific instant, and quietly substituting a different one is the
99
+ * failure mode this whole module exists to remove.
100
+ */
101
+ export class AsOfPinError extends Error {}
102
+
103
+ const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
104
+
105
+ /** `--as-of <iso>` / `--as-of=<iso>`; `undefined` when the flag is absent. */
106
+ function readArgvPin(argv: readonly string[]): string | undefined {
107
+ for (let i = 0; i < argv.length; i += 1) {
108
+ const arg = argv[i] ?? '';
109
+ if (arg === '--as-of') return argv[i + 1] ?? '';
110
+ if (arg.startsWith('--as-of=')) return arg.slice('--as-of='.length);
111
+ }
112
+ return undefined;
113
+ }
114
+
115
+ function parsePin(raw: string, source: string): Date {
116
+ const trimmed = raw.trim();
117
+ const ms = Date.parse(trimmed);
118
+ if (trimmed === '' || Number.isNaN(ms)) {
119
+ throw new AsOfPinError(
120
+ `${source} is not a parseable date: ${JSON.stringify(raw)}. ` +
121
+ 'Pass an ISO-8601 instant, e.g. 2026-08-23T00:00:00Z.',
122
+ );
123
+ }
124
+ return new Date(ms);
125
+ }
126
+
127
+ /** `true` for a set, non-falsey `CI` value. GitHub Actions sets `CI=true`. */
128
+ function inCi(env: Record<string, string | undefined>): boolean {
129
+ const raw = (env.CI ?? '').trim().toLowerCase();
130
+ return raw !== '' && raw !== '0' && raw !== 'false';
131
+ }
132
+
133
+ /** Commit date of `HEAD` as an ISO instant, or `null` when git cannot answer. */
134
+ export function headCommitDate(cwd: string = REPO_ROOT): Date | null {
135
+ const res = spawnSync('git', ['log', '-1', '--format=%cI', 'HEAD'], {
136
+ cwd,
137
+ encoding: 'utf-8',
138
+ });
139
+ if (res.status !== 0) return null;
140
+ const iso = (res.stdout ?? '').trim();
141
+ if (iso === '') return null;
142
+ const ms = Date.parse(iso);
143
+ return Number.isNaN(ms) ? null : new Date(ms);
144
+ }
145
+
146
+ /**
147
+ * Resolve "now" from the four rungs. Pure with respect to {@link AsOfSources},
148
+ * so the self-test drives every rung without touching the real environment.
149
+ *
150
+ * @throws {AsOfPinError} when an explicit pin is present but unparseable.
151
+ */
152
+ export function resolveAsOf(sources: AsOfSources = {}): AsOfResolution {
153
+ const argv = sources.argv ?? process.argv.slice(2);
154
+ const env = sources.env ?? process.env;
155
+
156
+ const argvPin = readArgvPin(argv);
157
+ if (argvPin !== undefined) {
158
+ return { at: parsePin(argvPin, '--as-of'), rung: 'argv', reproducible: true };
159
+ }
160
+
161
+ const envPin = env.AC_AS_OF;
162
+ if (envPin !== undefined && envPin.trim() !== '') {
163
+ return { at: parsePin(envPin, 'AC_AS_OF'), rung: 'env', reproducible: true };
164
+ }
165
+
166
+ if (inCi(env)) {
167
+ const commit = (sources.commitDate ?? headCommitDate)();
168
+ if (commit !== null) {
169
+ return { at: commit, rung: 'commit', reproducible: true };
170
+ }
171
+ }
172
+
173
+ const warn = sources.warn ?? ((line: string) => process.stderr.write(line));
174
+ const at = (sources.wallClock ?? (() => new Date()))();
175
+ warn(
176
+ `⚠️ as-of: unpinned run — using the wall clock (${at.toISOString()}); ` +
177
+ 'this verdict is not reproducible. Pass --as-of <iso> or set AC_AS_OF to pin it.\n',
178
+ );
179
+ return { at, rung: 'wall-clock', reproducible: false };
180
+ }
181
+
182
+ let cached: AsOfResolution | null = null;
183
+
184
+ /**
185
+ * The resolution for this process, computed once. Memoised so 17 gate scripts
186
+ * sharing one process cannot disagree about "now", and so the wall-clock WARN
187
+ * is emitted at most once per run.
188
+ */
189
+ export function asOfResolution(): AsOfResolution {
190
+ cached ??= resolveAsOf();
191
+ return cached;
192
+ }
193
+
194
+ /**
195
+ * "Now", for a gate. The one sanctioned reader of the wall clock in
196
+ * `src/scripts/`.
197
+ *
198
+ * Returns a fresh `Date` each call so a caller mutating it cannot poison the
199
+ * memoised value.
200
+ */
201
+ export function asOf(): Date {
202
+ return new Date(asOfResolution().at.getTime());
203
+ }
204
+
205
+ /** `Date.now()` equivalent, for the arithmetic call sites. */
206
+ export function asOfMs(): number {
207
+ return asOfResolution().at.getTime();
208
+ }
209
+
210
+ /**
211
+ * One line a gate prints so a reviewer can see WHICH "now" produced the verdict.
212
+ *
213
+ * Without it, pinning is invisible: a reproducible run and a wall-clock run look
214
+ * identical in CI output, and "reproduce it with --as-of" is advice nobody can
215
+ * act on because the value to pass was never published.
216
+ *
217
+ * @param override an explicit date the gate's own flag supplied (e.g. `--today`),
218
+ * which outranks the seam and must be reported as such.
219
+ */
220
+ export function asOfBanner(override?: string): string {
221
+ if (override !== undefined) return `as-of: ${override} (source=flag)`;
222
+ const r = asOfResolution();
223
+ return `as-of: ${r.at.toISOString()} (rung=${r.rung}, reproducible=${String(r.reproducible)})`;
224
+ }
225
+
226
+ /** Test seam — drops the memo so a self-test can exercise more than one rung. */
227
+ export function _resetAsOfCacheForTests(): void {
228
+ cached = null;
229
+ }
230
+
231
+ /* ------------------------------------------------------------------ *
232
+ * CLI
233
+ * ------------------------------------------------------------------ */
234
+
235
+ function tsxBin(): string {
236
+ return path.join(
237
+ REPO_ROOT,
238
+ 'node_modules',
239
+ '.bin',
240
+ process.platform === 'win32' ? 'tsx.cmd' : 'tsx',
241
+ );
242
+ }
243
+
244
+ interface ChildRun {
245
+ status: number;
246
+ stdout: string;
247
+ stderr: string;
248
+ }
249
+
250
+ /** Invoke THIS module's own CLI, so the self-test exercises the shipped path. */
251
+ function runSelf(args: readonly string[], env: Record<string, string | undefined>): ChildRun {
252
+ const res = spawnSync(tsxBin(), [fileURLToPath(import.meta.url), ...args], {
253
+ cwd: REPO_ROOT,
254
+ encoding: 'utf-8',
255
+ env: env as NodeJS.ProcessEnv,
256
+ });
257
+ return {
258
+ status: res.status ?? 1,
259
+ stdout: res.stdout ?? '',
260
+ stderr: res.stderr ?? '',
261
+ };
262
+ }
263
+
264
+ /** Environment with every pin and CI marker cleared, so a case sets only its own. */
265
+ function bareEnv(): Record<string, string | undefined> {
266
+ const env: Record<string, string | undefined> = { ...process.env };
267
+ delete env.AC_AS_OF;
268
+ delete env.CI;
269
+ delete env.GITHUB_ACTIONS;
270
+ return env;
271
+ }
272
+
273
+ function selfTest(write: (chunk: string) => unknown): number {
274
+ const commit = headCommitDate();
275
+ let failed = 0;
276
+ let checks = 0;
277
+
278
+ const expect = (name: string, ok: boolean, detail: string): void => {
279
+ checks += 1;
280
+ if (ok) {
281
+ write(`✅ ${name}\n`);
282
+ } else {
283
+ write(`❌ ${name}: ${detail}\n`);
284
+ failed += 1;
285
+ }
286
+ };
287
+
288
+ // Rung 1 — argv.
289
+ const argvRun = runSelf(['--print', '--as-of', '2026-01-02T03:04:05Z'], bareEnv());
290
+ expect(
291
+ 'rung 1 (argv) — an explicit --as-of wins',
292
+ argvRun.status === 0 &&
293
+ argvRun.stdout.includes('rung=argv') &&
294
+ argvRun.stdout.includes('at=2026-01-02T03:04:05.000Z'),
295
+ `exit ${String(argvRun.status)}, stdout ${JSON.stringify(argvRun.stdout)}`,
296
+ );
297
+
298
+ // Rung 1 — argv beats env, so a pin the caller typed is never shadowed.
299
+ const precedence = runSelf(['--print', '--as-of=2026-01-02T03:04:05Z'], {
300
+ ...bareEnv(),
301
+ AC_AS_OF: '2020-01-01T00:00:00Z',
302
+ });
303
+ expect(
304
+ 'rung 1 precedence — --as-of= overrides AC_AS_OF',
305
+ precedence.status === 0 &&
306
+ precedence.stdout.includes('rung=argv') &&
307
+ precedence.stdout.includes('at=2026-01-02T03:04:05.000Z'),
308
+ `exit ${String(precedence.status)}, stdout ${JSON.stringify(precedence.stdout)}`,
309
+ );
310
+
311
+ // Rung 2 — env.
312
+ const envRun = runSelf(['--print'], { ...bareEnv(), AC_AS_OF: '2025-06-07T08:09:10Z' });
313
+ expect(
314
+ 'rung 2 (env) — AC_AS_OF answers when argv is silent',
315
+ envRun.status === 0 &&
316
+ envRun.stdout.includes('rung=env') &&
317
+ envRun.stdout.includes('at=2025-06-07T08:09:10.000Z'),
318
+ `exit ${String(envRun.status)}, stdout ${JSON.stringify(envRun.stdout)}`,
319
+ );
320
+
321
+ // Rung 3 — the committed clock, under CI.
322
+ const ciRun = runSelf(['--print'], { ...bareEnv(), CI: 'true' });
323
+ expect(
324
+ 'rung 3 (commit) — CI pins to the tree under test',
325
+ ciRun.status === 0 &&
326
+ ciRun.stdout.includes('rung=commit') &&
327
+ commit !== null &&
328
+ ciRun.stdout.includes(`at=${commit.toISOString()}`),
329
+ `exit ${String(ciRun.status)}, stdout ${JSON.stringify(ciRun.stdout)}, ` +
330
+ `HEAD date ${commit === null ? 'UNAVAILABLE' : commit.toISOString()}`,
331
+ );
332
+
333
+ // Rung 4 — the wall clock, and it must say so.
334
+ const wallRun = runSelf(['--print'], bareEnv());
335
+ expect(
336
+ 'rung 4 (wall-clock) — unpinned runs resolve and WARN once',
337
+ wallRun.status === 0 &&
338
+ wallRun.stdout.includes('rung=wall-clock') &&
339
+ wallRun.stdout.includes('reproducible=false') &&
340
+ wallRun.stderr.includes('not reproducible'),
341
+ `exit ${String(wallRun.status)}, stdout ${JSON.stringify(wallRun.stdout)}, ` +
342
+ `stderr ${JSON.stringify(wallRun.stderr)}`,
343
+ );
344
+
345
+ // A malformed pin is a rejection, never a silent downgrade.
346
+ const badArgv = runSelf(['--print', '--as-of', 'not-a-date'], bareEnv());
347
+ expect(
348
+ 'malformed --as-of is rejected, not downgraded',
349
+ badArgv.status !== 0 && badArgv.stderr.includes('not a parseable date'),
350
+ `exit ${String(badArgv.status)}, stderr ${JSON.stringify(badArgv.stderr)}`,
351
+ );
352
+
353
+ const badEnv = runSelf(['--print'], { ...bareEnv(), AC_AS_OF: '2026-13-45' });
354
+ expect(
355
+ 'malformed AC_AS_OF is rejected, not downgraded',
356
+ badEnv.status !== 0 && badEnv.stderr.includes('not a parseable date'),
357
+ `exit ${String(badEnv.status)}, stderr ${JSON.stringify(badEnv.stderr)}`,
358
+ );
359
+
360
+ // A truncated suite must fail rather than print success — deleting cases is
361
+ // otherwise the cheapest route to a green self-test.
362
+ const FLOOR = 7;
363
+ if (checks < FLOOR) {
364
+ write(
365
+ `❌ as_of --self-test: ${String(checks)} case(s) is below the declared ` +
366
+ `floor of ${String(FLOOR)}.\n`,
367
+ );
368
+ return 1;
369
+ }
370
+
371
+ write(
372
+ `\nas_of --self-test: ${String(checks - failed)}/${String(checks)} case(s) behaved ` +
373
+ `(floor ${String(FLOOR)})\n`,
374
+ );
375
+ return failed > 0 ? 1 : 0;
376
+ }
377
+
378
+ function main(argv: readonly string[]): number {
379
+ if (argv.includes('--self-test')) {
380
+ return selfTest((chunk) => process.stdout.write(chunk));
381
+ }
382
+ let res: AsOfResolution;
383
+ try {
384
+ res = resolveAsOf({ argv });
385
+ } catch (e) {
386
+ if (e instanceof AsOfPinError) {
387
+ process.stderr.write(`❌ as-of: ${e.message}\n`);
388
+ return 2;
389
+ }
390
+ throw e;
391
+ }
392
+ process.stdout.write(
393
+ `rung=${res.rung} at=${res.at.toISOString()} reproducible=${String(res.reproducible)}\n`,
394
+ );
395
+ return 0;
396
+ }
397
+
398
+ function isCliEntry(): boolean {
399
+ if (process.argv[1] === undefined) return false;
400
+ try {
401
+ return (
402
+ fs.realpathSync(fileURLToPath(import.meta.url)) ===
403
+ fs.realpathSync(path.resolve(process.argv[1]))
404
+ );
405
+ } catch {
406
+ return false;
407
+ }
408
+ }
409
+
410
+ if (isCliEntry()) {
411
+ process.exit(main(process.argv.slice(2)));
412
+ }
@@ -53,6 +53,13 @@ export const ALLOWLIST_PREFIXES: readonly (readonly string[])[] = [
53
53
  // under `--check`; same deterministic, model-free class as its neighbours.
54
54
  ['update_counts'],
55
55
  ['check_enforcement_coverage'],
56
+ // The denominator half of the same property. `check_enforcement_coverage`
57
+ // PUBLISHES the number; this one refuses a second, hand-written copy of it
58
+ // in a published doc — so its exit code IS the verdict for
59
+ // `claim:enforcement-undeclared-denominator`, not merely evidence that some
60
+ // report exists. Read-only, deterministic, model-free, and already run in
61
+ // CI (`rule-backstops.yml`) for its own reasons.
62
+ ['check_enforcement_denominator'],
56
63
  ['check_token_regression'],
57
64
  ['lint_agent_security'],
58
65
  ['measure_lexical_ranking'],
@@ -77,3 +77,36 @@ export function hookSectionEnabled(root: string, section: string): boolean {
77
77
 
78
78
  return false;
79
79
  }
80
+
81
+ /**
82
+ * Raw `lean_projection.mode` string out of `.agent-settings.yml`, or `''`.
83
+ *
84
+ * Same indentation-shaped discipline as `hookSectionEnabled` above and for the
85
+ * same reason — no YAML parser in a hook — but a top-level two-level key rather
86
+ * than a `hooks.<section>.enabled` flag. Interpretation is NOT done here:
87
+ * `_lib/lean_projection_mode.ts` owns it, so the projector and the concern
88
+ * cannot disagree about what the string means.
89
+ */
90
+ export function leanProjectionModeRaw(root: string): string {
91
+ const file = path.join(root, SETTINGS_FILE);
92
+ let text: string;
93
+ try {
94
+ if (!fs.statSync(file).isFile()) return '';
95
+ text = fs.readFileSync(file, 'utf-8');
96
+ } catch {
97
+ return '';
98
+ }
99
+ let inSection = false;
100
+ for (const raw of text.split(/\r\n|\r|\n/)) {
101
+ const line = raw.replace(/\s+$/, '');
102
+ if (!line || line.replace(/^\s+/, '').startsWith('#')) continue;
103
+ if (!(line.startsWith(' ') || line.startsWith('\t'))) {
104
+ inSection = /^lean_projection\s*:\s*$/.test(line);
105
+ continue;
106
+ }
107
+ if (!inSection) continue;
108
+ const m = /^\s+mode\s*:\s*(.+)$/.exec(line);
109
+ if (m) return (m[1] ?? '').trim().replace(/^["']|["']$/g, '');
110
+ }
111
+ return '';
112
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * host_listing_model — which skill DESCRIPTIONS survive the host's listing budget.
3
+ *
4
+ * WHY THIS EXISTS. This package projects ~290 skills and the host does not
5
+ * deliver ~290 descriptions. Until now that was learned one bare catalogue entry
6
+ * at a time, from a single first-party observation
7
+ * (`agents/evidence/analysis/skill-catalogue-description-delivery.md`, 2026-08-08:
8
+ * five of eight sampled entries arrived bare while all 414 carried a description
9
+ * on disk). The mechanism behind it is documented upstream and was modelled
10
+ * nowhere in this tree — `grep -rn skillListingBudgetFraction src docs agents`
11
+ * returned zero hits before this file.
12
+ *
13
+ * THE MECHANISM, AS DOCUMENTED UPSTREAM (code.claude.com/docs/en/skills and
14
+ * anthropics/claude-code#64606, both read 2026-08-22). Claude Code lists every
15
+ * skill NAME, and keeps DESCRIPTIONS only up to a fraction of the context
16
+ * window — 1% by default, ≈8,000 characters on a 200k window — filling by
17
+ * invocation frequency and capping each entry at 1,536 characters. The fraction
18
+ * is consumer-settable as `skillListingBudgetFraction`.
19
+ *
20
+ * WHAT THIS FILE IS AND IS NOT. It is a pure function over a catalogue and a set
21
+ * of numbers. It is NOT a measurement of the host: every input above is upstream
22
+ * PROSE, and three further behaviours the prose leaves open had to be chosen
23
+ * here. Each such choice is returned in `assumptions`, by id, with the reason —
24
+ * so a caller that reports "skill X will arrive bare" can always say which part
25
+ * of that is documented and which part is this repo's guess. The single pinned
26
+ * observation disagrees with the model on two of eight entries, and
27
+ * `tests/scripts/host_listing_model.test.ts` asserts the disagreement rather
28
+ * than tuning it away.
29
+ *
30
+ * Pure — no I/O, no clock, no process state.
31
+ */
32
+
33
+ /** One catalogue entry as the host is offered it. */
34
+ export interface CatalogueEntry {
35
+ /** Skill (or wrapped-command) name, as the host lists it. */
36
+ name: string;
37
+ /** Length of the `description:` frontmatter value, in characters. */
38
+ descriptionChars: number;
39
+ }
40
+
41
+ /** A behaviour this model had to pin down that upstream states as prose. */
42
+ export interface ModelAssumption {
43
+ id:
44
+ | 'budget-unit-is-chars'
45
+ | 'fill-order-is-invocation-frequency'
46
+ | 'names-are-never-dropped'
47
+ | 'stop-at-first-overflow'
48
+ | 'wrapper-overhead-unmodelled';
49
+ /** Always `upstream-prose` today. A value measured in this repo would not be an assumption. */
50
+ provenance: 'upstream-prose';
51
+ why: string;
52
+ }
53
+
54
+ export type FillOrder = 'usage' | 'alphabetical-fallback';
55
+
56
+ export interface HostListingModelInput {
57
+ /** Host context window, in tokens (200_000 for the default Claude Code model). */
58
+ contextWindowTokens: number;
59
+ /** `skillListingBudgetFraction`. */
60
+ fraction?: number;
61
+ /** Per-entry description cap, in characters. */
62
+ perEntryCapChars?: number;
63
+ /** Characters per token used to turn the token-denominated budget into chars. */
64
+ charsPerToken?: number;
65
+ /** Per-entry wrapper cost (name, punctuation, newlines). Unmodelled upstream; default 0. */
66
+ perEntryOverheadChars?: number;
67
+ /** Names in invocation-frequency order, most-invoked first. Empty / omitted → fallback. */
68
+ usageOrder?: readonly string[];
69
+ }
70
+
71
+ export interface HostListingModelResult {
72
+ /** The description budget for this window, in characters. */
73
+ budgetChars: number;
74
+ /** Which order the fill actually used. */
75
+ fillOrder: FillOrder;
76
+ /** Names whose description is predicted to reach the model. */
77
+ surviving: string[];
78
+ /** Names predicted to arrive listed-but-bare. */
79
+ bare: string[];
80
+ /** Characters the surviving descriptions consume. */
81
+ spentChars: number;
82
+ /** Every behaviour pinned here that upstream leaves as prose. */
83
+ assumptions: readonly ModelAssumption[];
84
+ }
85
+
86
+ /** Documented Claude Code defaults, read 2026-08-22. Not measured in this repo. */
87
+ export const CLAUDE_CODE_LISTING_DEFAULTS = {
88
+ fraction: 0.01,
89
+ perEntryCapChars: 1536,
90
+ charsPerToken: 4,
91
+ perEntryOverheadChars: 0,
92
+ } as const;
93
+
94
+ const ASSUMPTIONS: readonly ModelAssumption[] = [
95
+ {
96
+ id: 'budget-unit-is-chars',
97
+ provenance: 'upstream-prose',
98
+ why:
99
+ 'The docs give the budget as a fraction of the context window (tokens) and then ' +
100
+ 'illustrate it as "≈8,000 characters on 200k", which only reconciles at 4 chars ' +
101
+ 'per token. That ratio is inferred from the illustration, never stated.',
102
+ },
103
+ {
104
+ id: 'fill-order-is-invocation-frequency',
105
+ provenance: 'upstream-prose',
106
+ why:
107
+ 'Upstream says the budget fills by invocation frequency, but does not say what ' +
108
+ 'orders entries when no invocation history exists. This model falls back to ' +
109
+ 'alphabetical, which is this repo\'s choice and not upstream behaviour.',
110
+ },
111
+ {
112
+ id: 'names-are-never-dropped',
113
+ provenance: 'upstream-prose',
114
+ why:
115
+ 'Claude Code lists every name and budgets only descriptions. Another host ' +
116
+ '(codex) drops whole entries instead, so this is a per-host property and is not ' +
117
+ 'transferable to a second host without its own model.',
118
+ },
119
+ {
120
+ id: 'stop-at-first-overflow',
121
+ provenance: 'upstream-prose',
122
+ why:
123
+ 'The docs describe filling a budget, not packing one. A shorter later entry ' +
124
+ 'might in principle still fit after a longer one has overflowed; this model ' +
125
+ 'stops at the first entry that does not fit rather than skipping past it.',
126
+ },
127
+ {
128
+ id: 'wrapper-overhead-unmodelled',
129
+ provenance: 'upstream-prose',
130
+ why:
131
+ 'Each delivered entry also costs its name, separators and newlines. Upstream ' +
132
+ 'quantifies none of that, so the default overhead here is 0 and the budget is ' +
133
+ 'therefore an upper bound on how many descriptions survive.',
134
+ },
135
+ ];
136
+
137
+ /**
138
+ * Predict which descriptions the host delivers.
139
+ *
140
+ * `usageOrder` names are placed first, in the given order; everything else
141
+ * follows alphabetically. Entries not present in the catalogue are ignored.
142
+ */
143
+ export function modelListingBudget(
144
+ catalogue: readonly CatalogueEntry[],
145
+ input: HostListingModelInput,
146
+ ): HostListingModelResult {
147
+ const fraction = input.fraction ?? CLAUDE_CODE_LISTING_DEFAULTS.fraction;
148
+ const perEntryCap = input.perEntryCapChars ?? CLAUDE_CODE_LISTING_DEFAULTS.perEntryCapChars;
149
+ const charsPerToken = input.charsPerToken ?? CLAUDE_CODE_LISTING_DEFAULTS.charsPerToken;
150
+ const overhead = input.perEntryOverheadChars ?? CLAUDE_CODE_LISTING_DEFAULTS.perEntryOverheadChars;
151
+ const usageOrder = input.usageOrder ?? [];
152
+
153
+ const budgetChars = Math.floor(input.contextWindowTokens * fraction * charsPerToken);
154
+ const fillOrder: FillOrder = usageOrder.length > 0 ? 'usage' : 'alphabetical-fallback';
155
+
156
+ const byName = new Map(catalogue.map((e) => [e.name, e]));
157
+ const ordered: CatalogueEntry[] = [];
158
+ const seen = new Set<string>();
159
+ for (const name of usageOrder) {
160
+ const e = byName.get(name);
161
+ if (e && !seen.has(name)) {
162
+ ordered.push(e);
163
+ seen.add(name);
164
+ }
165
+ }
166
+ const rest = catalogue
167
+ .filter((e) => !seen.has(e.name))
168
+ .slice()
169
+ .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
170
+ ordered.push(...rest);
171
+
172
+ const surviving: string[] = [];
173
+ const bare: string[] = [];
174
+ let spentChars = 0;
175
+ let overflowed = false;
176
+ for (const e of ordered) {
177
+ if (overflowed) {
178
+ bare.push(e.name);
179
+ continue;
180
+ }
181
+ const cost = Math.min(e.descriptionChars, perEntryCap) + overhead;
182
+ if (spentChars + cost <= budgetChars) {
183
+ surviving.push(e.name);
184
+ spentChars += cost;
185
+ } else {
186
+ overflowed = true;
187
+ bare.push(e.name);
188
+ }
189
+ }
190
+
191
+ return { budgetChars, fillOrder, surviving, bare, spentChars, assumptions: ASSUMPTIONS };
192
+ }