@event4u/agent-config 14.9.0 → 14.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (426) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +384 -0
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/design.md +88 -0
  7. package/dist/agent-src/commands/project/analyze.md +47 -0
  8. package/dist/agent-src/commands/review/changes.md +67 -6
  9. package/dist/agent-src/commands/roadmap/create.md +49 -0
  10. package/dist/agent-src/commands/roadmap/next.md +27 -11
  11. package/dist/agent-src/commands/roadmap/process-full.md +14 -0
  12. package/dist/agent-src/commands/video/from-script.md +60 -3
  13. package/dist/agent-src/commands/video/stitch.md +14 -0
  14. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
  16. package/dist/agent-src/rules/active-remediation.md +3 -3
  17. package/dist/agent-src/rules/code-provenance.md +2 -2
  18. package/dist/agent-src/rules/context-hygiene.md +13 -33
  19. package/dist/agent-src/rules/council-availability.md +2 -2
  20. package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
  21. package/dist/agent-src/rules/design-fidelity.md +18 -0
  22. package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
  23. package/dist/agent-src/rules/evaluator-independence.md +56 -44
  24. package/dist/agent-src/rules/fix-what-you-see.md +9 -1
  25. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  26. package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
  27. package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
  28. package/dist/agent-src/rules/playbook-precedence.md +105 -0
  29. package/dist/agent-src/rules/recurring-criticism.md +3 -1
  30. package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
  31. package/dist/agent-src/rules/scale-discipline.md +24 -0
  32. package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
  33. package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
  34. package/dist/agent-src/rules/ui-audit-gate.md +27 -15
  35. package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
  36. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  37. package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
  38. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
  39. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
  40. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
  41. package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
  42. package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
  43. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
  44. package/dist/agent-src/skills/async-python-patterns/SKILL.md +1 -1
  45. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  46. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  47. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  52. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  53. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  54. package/dist/agent-src/skills/code-refactoring/SKILL.md +1 -1
  55. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  56. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  57. package/dist/agent-src/skills/condense-memory/SKILL.md +4 -1
  58. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  59. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +4 -1
  60. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  61. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  62. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  63. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  64. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  65. package/dist/agent-src/skills/design-review/SKILL.md +80 -2
  66. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  67. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  68. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  69. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  70. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  71. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  72. package/dist/agent-src/skills/discovery-interview/SKILL.md +1 -1
  73. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  74. package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -1
  75. package/dist/agent-src/skills/editorial-calendar/SKILL.md +1 -1
  76. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  78. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  79. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  80. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  81. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  82. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  83. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  84. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  85. package/dist/agent-src/skills/github-ci/SKILL.md +1 -1
  86. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  87. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  88. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  89. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  90. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  91. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  92. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  93. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  94. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  95. package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
  96. package/dist/agent-src/skills/judge-injection-defense/SKILL.md +1 -1
  97. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  98. package/dist/agent-src/skills/laravel-validation/SKILL.md +1 -1
  99. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  100. package/dist/agent-src/skills/legal-intake-triage/SKILL.md +1 -1
  101. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +1 -1
  102. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  103. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  104. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  105. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  106. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  107. package/dist/agent-src/skills/messaging-architecture/SKILL.md +1 -1
  108. package/dist/agent-src/skills/migration-architect/SKILL.md +1 -1
  109. package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +1 -1
  110. package/dist/agent-src/skills/module-management/SKILL.md +1 -1
  111. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  112. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  113. package/dist/agent-src/skills/motion-choreographer/SKILL.md +7 -1
  114. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  115. package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
  116. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  117. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  118. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  119. package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
  120. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  121. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  122. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  123. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  124. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  125. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  126. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +1 -1
  127. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  128. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  129. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  130. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  131. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  132. package/dist/agent-src/skills/receiving-code-review/SKILL.md +3 -3
  133. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  134. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  135. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  136. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  137. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  138. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  139. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  140. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  141. package/dist/agent-src/skills/scene-expander/SKILL.md +1 -1
  142. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  143. package/dist/agent-src/skills/secrets-management/SKILL.md +5 -1
  144. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  145. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  146. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  147. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  148. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  149. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  150. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  151. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  152. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  153. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  154. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  155. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  156. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  157. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  158. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  159. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  160. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  161. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  162. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  163. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  164. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  165. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  166. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  167. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  168. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  169. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  170. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  171. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  172. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  173. package/dist/agent-src/templates/playbook.md +75 -0
  174. package/dist/agent-src/templates/roadmaps.md +37 -0
  175. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  176. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  177. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  178. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  179. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  180. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  181. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  182. package/dist/cli/commands/uiAudit.js +221 -0
  183. package/dist/cli/commands/uiAudit.js.map +1 -0
  184. package/dist/cli/commands/uiRender.js +206 -0
  185. package/dist/cli/commands/uiRender.js.map +1 -0
  186. package/dist/cli/main.js +22 -1
  187. package/dist/cli/main.js.map +1 -1
  188. package/dist/cli/mcp/content.js +64 -1
  189. package/dist/cli/mcp/content.js.map +1 -1
  190. package/dist/cli/mcp/dispatch.js +175 -10
  191. package/dist/cli/mcp/dispatch.js.map +1 -1
  192. package/dist/cli/registry.js +3 -0
  193. package/dist/cli/registry.js.map +1 -1
  194. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  195. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  196. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  197. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  198. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  199. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  200. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  201. package/dist/cli-delegate/cmd_conformance.js +3 -3
  202. package/dist/cli-delegate/cmd_doctor.js +3 -3
  203. package/dist/cli-delegate/cmd_export.js +4 -4
  204. package/dist/cli-delegate/cmd_handoff.js +1 -1
  205. package/dist/cli-delegate/cmd_preflight.js +2 -2
  206. package/dist/cli-delegate/cmd_prune.js +4 -4
  207. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  208. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  209. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  210. package/dist/cli-delegate/cmd_sync.js +4 -4
  211. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  212. package/dist/cli-delegate/cmd_validate.js +4 -4
  213. package/dist/discovery/deprecation-report.md +1 -1
  214. package/dist/discovery/discovery-manifest.json +380 -145
  215. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  216. package/dist/discovery/discovery-manifest.summary.md +4 -4
  217. package/dist/discovery/dormancy-report.md +1 -1
  218. package/dist/discovery/orphan-report.md +1 -1
  219. package/dist/discovery/packs.json +18 -8
  220. package/dist/discovery/trust-report.md +3 -3
  221. package/dist/discovery/workspaces.json +16 -6
  222. package/dist/hooks/dispatch.js +2337 -1436
  223. package/dist/install/install.mjs +795 -708
  224. package/dist/mcp/registry-manifest.json +3 -3
  225. package/dist/mcp/server.json +2 -2
  226. package/dist/mcp/server.mjs +1685 -1250
  227. package/dist/router.json +1 -1
  228. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  229. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  230. package/dist/scripts/_lib/scoped_projection.js +84 -0
  231. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  232. package/dist/scripts/_lib/ui_surface.js +89 -0
  233. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  234. package/dist/scripts/condense.js +6 -10
  235. package/dist/scripts/condense.js.map +1 -1
  236. package/dist/scripts/project_thin_rules.js +40 -2
  237. package/dist/scripts/project_thin_rules.js.map +1 -1
  238. package/dist/server/schemas/settings.js +3 -0
  239. package/dist/server/schemas/settings.js.map +1 -1
  240. package/dist/shared/skillRanking.js +159 -0
  241. package/dist/shared/skillRanking.js.map +1 -0
  242. package/docs/contracts/provider-lifecycle.md +47 -18
  243. package/docs/contracts/settings-classes.md +14 -4
  244. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  245. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  246. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  247. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  248. package/docs/guidelines/design-antipatterns.md +4 -2
  249. package/docs/guidelines/design-asset-discipline.md +38 -0
  250. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  251. package/docs/guidelines/design-handover-extraction.md +35 -5
  252. package/docs/guidelines/design-modes.md +41 -1
  253. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  254. package/llms.txt +50 -39
  255. package/package.json +1 -1
  256. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  257. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  258. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  259. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  260. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  261. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  262. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  263. package/src/config/agent-settings.template.yml +28 -0
  264. package/src/config/assurance-capability-registry.json +223 -0
  265. package/src/config/assurance-threshold-budget.json +65 -0
  266. package/src/config/ci-local-parity.yml +14 -0
  267. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  268. package/src/config/dispatch-economy-metrics.json +4 -2
  269. package/src/config/evaluator-budgets.json +14 -14
  270. package/src/config/gate-coverage.yml +227 -8
  271. package/src/config/gate-violation-baselines.json +76 -72
  272. package/src/config/hook-latency-budget.json +1 -0
  273. package/src/config/hook-token-budget.json +137 -125
  274. package/src/config/pack-size-budget.json +4 -2
  275. package/src/config/preamble-payload-budget.json +71 -67
  276. package/src/config/quorum-attendance-budget.json +12 -4
  277. package/src/config/rule-activation-census.json +7 -3
  278. package/src/config/rule-enforcement-baseline.json +0 -1
  279. package/src/config/rule-stub-ceilings.json +112 -91
  280. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  281. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  282. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  283. package/src/scripts/_dispatch.bash +17 -0
  284. package/src/scripts/_lib/as_of.ts +412 -0
  285. package/src/scripts/_lib/exec_evidence.ts +7 -0
  286. package/src/scripts/_lib/hook_settings.ts +33 -0
  287. package/src/scripts/_lib/host_listing_model.ts +192 -0
  288. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  289. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  290. package/src/scripts/_lib/review_independence.ts +62 -10
  291. package/src/scripts/_lib/rule_injection.ts +239 -0
  292. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  293. package/src/scripts/_lib/scoped_projection.ts +96 -0
  294. package/src/scripts/_lib/session_register.ts +44 -2
  295. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  296. package/src/scripts/_lib/transcript_reads.ts +207 -0
  297. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  298. package/src/scripts/_lib/ui_authority.ts +343 -0
  299. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  300. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  301. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  302. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  303. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  304. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  305. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  306. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  307. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  308. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  309. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  310. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  311. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  312. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  313. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  314. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  315. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  316. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  317. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  318. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  319. package/src/scripts/ai-video/stitch.sh +191 -10
  320. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  321. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  322. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  323. package/src/scripts/ai_council/clients.ts +3 -2
  324. package/src/scripts/ai_council/consensus.ts +63 -6
  325. package/src/scripts/ai_council/events_log.ts +46 -1
  326. package/src/scripts/ai_council/orchestrator.ts +103 -108
  327. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  328. package/src/scripts/ai_council/prompts.ts +12 -5
  329. package/src/scripts/ai_council/quorum.ts +82 -1
  330. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  331. package/src/scripts/ai_council/seating.ts +211 -0
  332. package/src/scripts/ai_council/stance_tally.ts +22 -0
  333. package/src/scripts/bench_hook_latency.ts +62 -0
  334. package/src/scripts/build_proof.ts +12 -1
  335. package/src/scripts/capture_skill_catalogue.ts +21 -0
  336. package/src/scripts/check_always_budget.ts +2 -1
  337. package/src/scripts/check_augmentignore.ts +2 -1
  338. package/src/scripts/check_beta_review_markers.ts +2 -1
  339. package/src/scripts/check_composite_arming.ts +364 -0
  340. package/src/scripts/check_corpus_staleness.ts +2 -1
  341. package/src/scripts/check_council_pin_staleness.ts +3 -1
  342. package/src/scripts/check_enforcement_coverage.ts +116 -3
  343. package/src/scripts/check_enforcement_denominator.ts +365 -0
  344. package/src/scripts/check_gate_coverage.ts +2 -1
  345. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  346. package/src/scripts/check_knowledge_cards.ts +2 -1
  347. package/src/scripts/check_knowledge_pages.ts +3 -2
  348. package/src/scripts/check_memory.ts +2 -1
  349. package/src/scripts/check_no_automerge_key.ts +287 -0
  350. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  351. package/src/scripts/check_pack_passport_reconciliation.ts +274 -0
  352. package/src/scripts/check_proposal.ts +2 -1
  353. package/src/scripts/check_reach_staleness.ts +2 -1
  354. package/src/scripts/check_release_adjacent_health.ts +2 -1
  355. package/src/scripts/check_release_pr_shape.ts +5 -0
  356. package/src/scripts/check_review_schema.ts +34 -1
  357. package/src/scripts/check_roadmap_trackable.ts +48 -3
  358. package/src/scripts/check_source_size_budget.ts +2 -1
  359. package/src/scripts/check_standing_payload_delta.ts +505 -0
  360. package/src/scripts/check_trigger_evals.ts +2 -1
  361. package/src/scripts/classify_change_risk.ts +281 -0
  362. package/src/scripts/compute_skill_tiers.ts +186 -0
  363. package/src/scripts/condense.ts +8 -13
  364. package/src/scripts/council_attendance_metrics.ts +183 -0
  365. package/src/scripts/council_cli.ts +10 -8
  366. package/src/scripts/council_parse_rate.ts +116 -0
  367. package/src/scripts/derive_playbooks.ts +406 -0
  368. package/src/scripts/design_slop_rules.test.ts +7 -0
  369. package/src/scripts/design_slop_rules.ts +56 -2
  370. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  371. package/src/scripts/frontend_corpus_hash.ts +116 -0
  372. package/src/scripts/generate_pack_manifests.ts +78 -0
  373. package/src/scripts/grade_target_readiness.ts +478 -0
  374. package/src/scripts/hook_manifest.json +1 -1
  375. package/src/scripts/hook_manifest.yaml +97 -16
  376. package/src/scripts/hooks/concern_registry.ts +4 -0
  377. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  378. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  379. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  380. package/src/scripts/hot_context_hook.ts +74 -7
  381. package/src/scripts/install.ts +40 -89
  382. package/src/scripts/lint_adapter_tier.ts +663 -0
  383. package/src/scripts/lint_archived_skills.ts +93 -0
  384. package/src/scripts/lint_budget_ownership.ts +2 -1
  385. package/src/scripts/lint_deterministic_time.ts +470 -0
  386. package/src/scripts/lint_framework_leakage.ts +5 -0
  387. package/src/scripts/lint_handoffs.ts +525 -1
  388. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  389. package/src/scripts/lint_marketplace.ts +88 -0
  390. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  391. package/src/scripts/lint_one_off_age.ts +6 -1
  392. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  393. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  394. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  395. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  396. package/src/scripts/lint_spawn_payload.ts +42 -5
  397. package/src/scripts/lint_symptom_intake.ts +2 -1
  398. package/src/scripts/mcp_parity_smoke.ts +30 -4
  399. package/src/scripts/mcp_render.ts +66 -1
  400. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  401. package/src/scripts/measure_risk_class_drift.ts +140 -0
  402. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  403. package/src/scripts/media/lib/adapter-common.sh +211 -1
  404. package/src/scripts/media/lib/adapter-contract.md +74 -6
  405. package/src/scripts/model_rule_injection.ts +907 -0
  406. package/src/scripts/project_thin_rules.ts +45 -2
  407. package/src/scripts/release.ts +66 -860
  408. package/src/scripts/release_drill.ts +59 -3
  409. package/src/scripts/release_env.ts +298 -0
  410. package/src/scripts/release_publication.ts +720 -0
  411. package/src/scripts/roadmap_context.ts +778 -0
  412. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  413. package/src/scripts/schemas/pack.schema.json +62 -4
  414. package/src/scripts/schemas/rule.schema.json +24 -2
  415. package/src/scripts/schemas/skill.schema.json +41 -2
  416. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  417. package/src/scripts/session_register_hook.ts +29 -3
  418. package/src/scripts/sessions_cli.ts +67 -3
  419. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  420. package/src/scripts/tighten_skill_descriptions.ts +264 -0
  421. package/src/scripts/token_sink_report.ts +239 -0
  422. package/src/scripts/trigger_coverage.ts +122 -3
  423. package/src/scripts/update_counts.ts +7 -3
  424. package/src/scripts/validate_frontmatter.ts +66 -0
  425. package/src/server/schemas/settings.ts +5 -0
  426. package/src/shared/skillRanking.ts +183 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `lean_projection.mode` — ONE definition of what the three modes mean.
3
+ *
4
+ * The projector (`condense.ts`) resolves the value with a real YAML parse; the
5
+ * delivery concern (`hooks/rule_inject_hook.ts`) resolves it with an
6
+ * indentation-shaped read, because a hook must never fail a tool call because a
7
+ * parser could not load. Two readers is unavoidable. Two *normalisations* is
8
+ * not, and would be the defect worth preventing: a projector that writes thin
9
+ * files while the concern believes the mode is off delivers pointers and no
10
+ * bodies, which is exactly the 36.2 % arm the delivery mode exists to replace.
11
+ *
12
+ * So each side supplies the raw string from its own reader and this module
13
+ * decides what it means. Anything unrecognised — absent key, typo, `null`,
14
+ * a non-string — is `eager-all`, today's shipped behaviour: a mode nobody can
15
+ * spell must never silently thin the standing corpus.
16
+ */
17
+
18
+ /** The three projection shapes. `eager-all` is the shipped default. */
19
+ export type LeanProjectionMode = 'eager-all' | 'thin' | 'delivery';
20
+
21
+ export const DEFAULT_LEAN_PROJECTION_MODE: LeanProjectionMode = 'eager-all';
22
+
23
+ /** Map a raw settings value onto a mode. Unrecognised → `eager-all`. */
24
+ export function normalizeLeanProjectionMode(raw: unknown): LeanProjectionMode {
25
+ const v = typeof raw === 'string' ? raw.trim().toLowerCase() : '';
26
+ if (v === 'thin') return 'thin';
27
+ if (v === 'delivery') return 'delivery';
28
+ return DEFAULT_LEAN_PROJECTION_MODE;
29
+ }
30
+
31
+ /**
32
+ * True when the projector writes pointer stubs instead of bodies.
33
+ *
34
+ * `delivery` is a superset of `thin`: it writes the same thin files AND binds
35
+ * the concern that delivers the bodies back on a trigger.
36
+ */
37
+ export function writesThinFiles(mode: LeanProjectionMode): boolean {
38
+ return mode === 'thin' || mode === 'delivery';
39
+ }
40
+
41
+ /** True only in the mode where a hook delivers rule bodies at runtime. */
42
+ export function deliversBodies(mode: LeanProjectionMode): boolean {
43
+ return mode === 'delivery';
44
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * mcp_bridge — `.mcp.json` registration and the skill-tier projection lever.
3
+ *
4
+ * Extracted from `install.ts` by Phase 2.2 of `road-to-skill-delivery-over-mcp`,
5
+ * for a mechanical reason worth stating: `check_source_size_budget` is a ratchet
6
+ * over every line in a file above 1,500, `install.ts` is 5,466 of them, and the
7
+ * gate's own message says a new violation is fixed and never baselined away. So
8
+ * the feature lives here and `install.ts` keeps one-line call sites.
9
+ *
10
+ * Everything here is a pure function of its arguments or of the file system.
11
+ * `ensureMcpBridge` is the one exception and takes its writer by injection —
12
+ * `merge_json_file` lives in `install.ts` (eight other bridges use it) and
13
+ * importing it back would make the cycle.
14
+ */
15
+
16
+ import * as fs from 'node:fs';
17
+ import * as path from 'node:path';
18
+
19
+ /** `install.ts`'s deploy tuple: [written, skipped, status, paths]. */
20
+ export type DeployTuple = [number, number, string, string[]];
21
+
22
+ /** The `merge_json_file` signature this module is handed. */
23
+ export type MergeJsonFile = (
24
+ p: string,
25
+ new_data: Record<string, unknown>,
26
+ force: boolean,
27
+ label: string,
28
+ ) => Record<string, unknown>[];
29
+
30
+ /**
31
+ * The project-scope MCP server entry Claude Code reads from `.mcp.json`.
32
+ *
33
+ * Shape is fixed by `docs/getting-started-local-stdio.md` — the documented
34
+ * end-user path — so a consumer who followed the docs and a consumer who ran the
35
+ * installer end up with the same entry rather than two that drift.
36
+ */
37
+ export const MCP_SERVER_KEY = 'agent-config';
38
+ export const MCP_BRIDGE_ENTRY = {
39
+ mcpServers: {
40
+ [MCP_SERVER_KEY]: {
41
+ command: 'npx',
42
+ args: ['-y', '@event4u/agent-config', 'mcp-server'],
43
+ },
44
+ },
45
+ };
46
+
47
+ /**
48
+ * `.mcp.json` — the project-scope MCP config Claude Code reads.
49
+ *
50
+ * WHY THE INSTALLER OWNS THIS FILE AND `mcp_render` DOES NOT. `mcp_render`
51
+ * projects the consumer's OWN root `mcp.json` onto per-tool configs by
52
+ * overwriting them (`write_target`), which is correct for a file the renderer
53
+ * generates in full and wrong for one the consumer also edits: a render would
54
+ * delete both this entry and any server they added by hand. `merge_json_file`
55
+ * writes only our key, records it as an RFC-6901 pointer in
56
+ * `agents/installed-tools.lock`, and lets uninstall subtract exactly that key.
57
+ * `mcp:check` validates the file without ever writing it.
58
+ *
59
+ * Default ON for `claude-code`, because `rules/missing-skill-recovery.md` makes
60
+ * `suggest_skill_for_task` an Iron Law for every consumer and a rule that is
61
+ * unfulfillable by default is a defect, not a feature. Opting out is the same
62
+ * lever as every other bridge — deselect the tool (`--tools`), which is the gate
63
+ * the caller applies; `.agent-tools.yml` is deliberately NOT that lever, it is a
64
+ * maintainer-side projection-generator allowlist the installer cannot see and
65
+ * whose tool ids differ from `_VALID_TOOLS`.
66
+ */
67
+ export function makeEnsureMcpBridge(
68
+ mergeJsonFile: MergeJsonFile,
69
+ ): (projectRoot: string, force: boolean) => Record<string, unknown>[] {
70
+ return (projectRoot, force) =>
71
+ mergeJsonFile(path.join(projectRoot, '.mcp.json'), MCP_BRIDGE_ENTRY, force, '.mcp.json');
72
+ }
73
+
74
+ /**
75
+ * The three projection modes.
76
+ *
77
+ * `legacy-all` ships everything and stays the default. `scoped` narrows to the
78
+ * active packs. `tiered` narrows to the skills predicted to reach the model WITH
79
+ * their description, on the theory that a skill the host lists bare is better
80
+ * served by the MCP recovery tool than by a catalogue entry nobody can read.
81
+ *
82
+ * `tiered` is OPT-IN and stays opt-in until the roadmap's Phase 4.4 decides on
83
+ * evidence — which it did, at `measured-null`. The reason is stated rather than
84
+ * implied: the split usually comes from an ALPHABETICAL fallback order
85
+ * (`agents/runtime/metrics/skill-usage.jsonl` is absent on most machines), and
86
+ * the one real host observation this repo has disagrees with that fallback on
87
+ * four of eight sampled entries. Defaulting to `tiered` would hide skills on a
88
+ * prediction the tree itself can show to be wrong — strictly worse than the
89
+ * bare-but-listed defect it fixes (roadmap risk 4).
90
+ */
91
+ export type ProjectionMode = 'scoped' | 'legacy-all' | 'tiered';
92
+
93
+ /** Resolve a raw `projection.mode` value. Anything unrecognised is `legacy-all`. */
94
+ export function projectionModeOf(raw: unknown): ProjectionMode {
95
+ return raw === 'scoped' ? 'scoped' : raw === 'tiered' ? 'tiered' : 'legacy-all';
96
+ }
97
+
98
+ /**
99
+ * Tier B skill names, or `null` when no split exists on this machine.
100
+ *
101
+ * `null` is load-bearing: it means "nobody computed a split here", and a
102
+ * `tiered` install that finds it must ship the FULL surface rather than prune on
103
+ * an empty set — pruning on absence would delete the entire catalogue. A
104
+ * malformed file is `null` for the same reason.
105
+ */
106
+ export function resolveTierB(packageRoot: string): ReadonlySet<string> | null {
107
+ const p = path.join(packageRoot, 'agents', 'runtime', 'state', 'skill-tiers.json');
108
+ if (!fs.existsSync(p)) return null;
109
+ try {
110
+ const parsed = JSON.parse(fs.readFileSync(p, 'utf-8')) as { tier_b?: unknown };
111
+ if (!Array.isArray(parsed.tier_b)) return null;
112
+ return new Set(parsed.tier_b.filter((n): n is string => typeof n === 'string'));
113
+ } catch {
114
+ return null;
115
+ }
116
+ }
117
+
118
+ /** Predicate for `_prune_modules_by`: is this SKILL.md a Tier B skill? */
119
+ export function tierBPrunePredicate(
120
+ tierB: ReadonlySet<string>,
121
+ ): (mdPath: string) => boolean {
122
+ return (mdPath) => tierB.has(path.basename(path.dirname(mdPath)));
123
+ }
124
+
125
+ /**
126
+ * The consumer-side alternative to tiering, surfaced as a recommendation only.
127
+ *
128
+ * Claude Code's listing budget is settable: `skillListingBudgetFraction` raises
129
+ * the fraction of the context window spent on skill DESCRIPTIONS, restoring them
130
+ * at a measured token price instead of withholding skills from the catalogue.
131
+ * That is the other lever, and for many consumers the better one — a description
132
+ * they can read beats a tool call they have to make.
133
+ *
134
+ * Returns `null` unless a tier split exists AND Tier B is non-empty: with no
135
+ * split there is nothing to recommend against, and an empty Tier B means the
136
+ * host already describes everything. This NEVER writes the consumer's
137
+ * `settings.json` — the setting is theirs, the token cost is theirs, and an
138
+ * installer that edited a host's own config to buy itself context would be
139
+ * taking that decision in their name.
140
+ */
141
+ export function tierBAdvisory(packageRoot: string): string | null {
142
+ const tierB = resolveTierB(packageRoot);
143
+ if (tierB === null || tierB.size === 0) return null;
144
+ return (
145
+ `ℹ️ ${tierB.size} skill(s) are predicted to reach the model WITHOUT their ` +
146
+ 'description on a default 200k window.\n' +
147
+ ' Two levers, neither applied for you:\n' +
148
+ ' • raise `skillListingBudgetFraction` in your Claude Code settings — ' +
149
+ 'restores descriptions at a token cost (100% delivery of this catalogue ' +
150
+ 'measures ~13,003 tok)\n' +
151
+ ' • or set `projection.mode: tiered` to withhold them from the catalogue ' +
152
+ 'and reach them via the MCP server instead (opt-in, still unproven — see ' +
153
+ 'docs/mcp-server.md)'
154
+ );
155
+ }
156
+
157
+ export interface TieredPruneOutcome {
158
+ deployResults: Record<string, DeployTuple>;
159
+ /** One line for the caller to print, or `null` when QUIET. */
160
+ message: string | null;
161
+ /** `warn` when no split exists, `info` when a prune happened. */
162
+ level: 'warn' | 'info';
163
+ }
164
+
165
+ /**
166
+ * Apply the Tier-B prune to a completed deploy.
167
+ *
168
+ * Takes `pruneBy` rather than importing it, so the prune mechanics stay in one
169
+ * place and this module does not become a second implementation of them.
170
+ */
171
+ export function applyTieredPrune(
172
+ deployResults: Record<string, DeployTuple>,
173
+ packageRoot: string,
174
+ pruneBy: (
175
+ dr: Record<string, DeployTuple>,
176
+ isPruned: (mdPath: string) => boolean,
177
+ ) => [number, Record<string, DeployTuple>],
178
+ ): TieredPruneOutcome {
179
+ const tierB = resolveTierB(packageRoot);
180
+ if (tierB === null || tierB.size === 0) {
181
+ return {
182
+ deployResults,
183
+ level: 'warn',
184
+ message:
185
+ 'projection.mode: tiered but no skill-tier split exists ' +
186
+ '(agents/runtime/state/skill-tiers.json) — shipping the full surface. ' +
187
+ 'Compute one with `./scripts-run src/scripts/compute_skill_tiers` first.',
188
+ };
189
+ }
190
+ const [pruned, adjusted] = pruneBy(deployResults, tierBPrunePredicate(tierB));
191
+ return {
192
+ deployResults: adjusted,
193
+ level: 'info',
194
+ message:
195
+ `🧹 Tiered install: pruned ${pruned} Tier-B skill artefact(s) — still ` +
196
+ "reachable via the MCP server's suggest_skill_for_task / read_skill. " +
197
+ 'Set projection.mode: legacy-all to restore the full surface.',
198
+ };
199
+ }
@@ -27,6 +27,26 @@ const PROVIDER_FAMILY: Readonly<Record<string, string>> = {
27
27
  export type ReviewIndependence = 'cross-family' | 'same-family' | 'single-member' | 'unknown';
28
28
  export type AcceptanceStatus = 'accepted' | 'provisional';
29
29
 
30
+ /**
31
+ * How the reviewer relates to the AUTHOR — a second axis, added 2026-08-23 by
32
+ * `road-to-review-independence` step 2.1.
33
+ *
34
+ * Model family and author relation are different questions, and the type carried only
35
+ * the first. **A cross-family pair that both read the implementer's envelope is not
36
+ * independent in the sense that matters here**: they disagree about the model, and they
37
+ * share the framing of what the change is for and what counts as done.
38
+ *
39
+ * - `fresh` — no implementation context. A separate dispatch that received the diff and
40
+ * the scope, and not the author's envelope.
41
+ * - `same-session` — ran inside the author's session, with the implementation context in
42
+ * scope. The common path today, and the one this axis exists to make visible.
43
+ * - `unknown` — not recorded. Treated as `same-session` for derivation, because an
44
+ * unrecorded relation is not evidence of freshness and the safe direction on an
45
+ * integrity field is the weaker claim (the same reasoning `unknown` already gets on
46
+ * the family axis).
47
+ */
48
+ export type ContextRelation = 'fresh' | 'same-session' | 'unknown';
49
+
30
50
  /**
31
51
  * How much INDEPENDENT evidence backs the verdict — deliberately orthogonal to
32
52
  * how much effort the reviewer spent.
@@ -54,30 +74,56 @@ export function reviewIndependence(memberNames: readonly string[]): ReviewIndepe
54
74
  * is not evidence of independence, and the safe direction on an integrity field
55
75
  * is the weaker claim.
56
76
  */
57
- export function acceptanceStatus(independence: ReviewIndependence): AcceptanceStatus {
58
- return independence === 'cross-family' ? 'accepted' : 'provisional';
77
+ export function acceptanceStatus(
78
+ independence: ReviewIndependence,
79
+ relation: ContextRelation = 'unknown',
80
+ ): AcceptanceStatus {
81
+ if (independence !== 'cross-family') return 'provisional';
82
+ // BOTH axes, and this is the whole point of step 2.1: a cross-family pair that both
83
+ // sat in the author's session shares the framing even though it disagrees about the
84
+ // model. `unknown` falls here too — an unrecorded relation is not evidence of
85
+ // freshness. The parameter defaults to `unknown` so every existing caller keeps
86
+ // compiling and gets the SAFER answer rather than the old one.
87
+ return relation === 'fresh' ? 'accepted' : 'provisional';
59
88
  }
60
89
 
61
- export function assuranceFor(independence: ReviewIndependence): Assurance {
62
- if (independence === 'cross-family') return 'independent';
90
+ export function assuranceFor(
91
+ independence: ReviewIndependence,
92
+ relation: ContextRelation = 'unknown',
93
+ ): Assurance {
63
94
  if (independence === 'unknown') return 'unreviewed';
95
+ // `independent` requires both axes, for the same reason `accepted` does.
96
+ if (independence === 'cross-family' && relation === 'fresh') return 'independent';
64
97
  return 'single-pass';
65
98
  }
66
99
 
67
100
  export interface IndependenceFields {
68
101
  readonly review_independence: ReviewIndependence;
102
+ /** The recorded second axis. Derived fields below follow from BOTH. */
103
+ readonly context_relation: ContextRelation;
69
104
  readonly acceptance_status: AcceptanceStatus;
70
105
  readonly assurance: Assurance;
71
106
  readonly reviewers: readonly string[];
72
107
  }
73
108
 
74
- /** The three fields plus the member set they were derived from. */
75
- export function independenceFields(memberNames: readonly string[]): IndependenceFields {
109
+ /**
110
+ * The recorded axes plus the fields derived from them.
111
+ *
112
+ * `context_relation` defaults to `unknown` so an existing caller that names only its
113
+ * members keeps compiling — and gets `provisional` rather than the `accepted` it used to
114
+ * get. That direction is deliberate: a producer that has not said whether its reviewer
115
+ * was fresh has not earned the stronger claim.
116
+ */
117
+ export function independenceFields(
118
+ memberNames: readonly string[],
119
+ relation: ContextRelation = 'unknown',
120
+ ): IndependenceFields {
76
121
  const independence = reviewIndependence(memberNames);
77
122
  return {
78
123
  review_independence: independence,
79
- acceptance_status: acceptanceStatus(independence),
80
- assurance: assuranceFor(independence),
124
+ context_relation: relation,
125
+ acceptance_status: acceptanceStatus(independence, relation),
126
+ assurance: assuranceFor(independence, relation),
81
127
  reviewers: [...memberNames],
82
128
  };
83
129
  }
@@ -97,13 +143,19 @@ export function independenceViolations(doc: Record<string, unknown>): string[] {
97
143
  out.push('review_independence is absent — an artifact that does not declare its independence reads as acceptance by default');
98
144
  return out;
99
145
  }
100
- const expectedAccept = acceptanceStatus(indep as ReviewIndependence);
146
+ // The recorded relation, not a default: `independenceViolations` judges an artifact
147
+ // that already carries the fields, so reading `unknown` when the artifact says
148
+ // `fresh` would flag a correct producer.
149
+ const relRaw = doc['context_relation'];
150
+ const rel: ContextRelation =
151
+ relRaw === 'fresh' || relRaw === 'same-session' ? relRaw : 'unknown';
152
+ const expectedAccept = acceptanceStatus(indep as ReviewIndependence, rel);
101
153
  if (accept !== expectedAccept) {
102
154
  out.push(
103
155
  `acceptance_status is ${String(accept)} but review_independence ${String(indep)} derives ${expectedAccept}`,
104
156
  );
105
157
  }
106
- const expectedAssur = assuranceFor(indep as ReviewIndependence);
158
+ const expectedAssur = assuranceFor(indep as ReviewIndependence, rel);
107
159
  if (assur !== undefined && assur !== expectedAssur) {
108
160
  out.push(`assurance is ${String(assur)} but review_independence ${String(indep)} derives ${expectedAssur}`);
109
161
  }
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Rule-injection core — THE single module both the offline model and the
3
+ * runtime concern read.
4
+ *
5
+ * WHY ONE MODULE. `road-to-trigger-delivered-rule-bodies` step 0.5 states the
6
+ * failure this prevents in as many words: *an experiment whose offline pricing
7
+ * and runtime delivery use different matchers measures nothing*. So the price /
8
+ * recall model (`src/scripts/model_rule_injection.ts`) and the delivery concern
9
+ * (`src/scripts/hooks/rule_inject_hook.ts`) both import THIS file, and neither
10
+ * re-implements selection, ordering, capping, or body loading.
11
+ *
12
+ * IT OWNS NO MATCHER. Trigger semantics belong to
13
+ * [`_lib/router_match.ts`](router_match.ts), which is already the single
14
+ * implementation for every surface that answers "which rules fire on this
15
+ * prompt?" and is pinned by `tests/scripts/router_match_parity.test.ts`. A
16
+ * second matcher here would be the violation that test exists to catch, so
17
+ * `matchTierRules` is a thin, deterministic wrapper over `match_prompt` and
18
+ * nothing more. Step 0.3's pre-registered comparison found no reason to write
19
+ * one — see `model_rule_injection.ts`'s header for the measured verdict.
20
+ *
21
+ * KERNEL IS NEVER INJECTED. The nine kernel rules are in standing context by
22
+ * definition; injecting one would be paying twice for a body that is already
23
+ * there. `match_prompt` returns kernel ids in `activated_rules` because kernel
24
+ * rules are always active — this module filters them out by construction rather
25
+ * than relying on a caller to remember.
26
+ *
27
+ * IT IMPORTS NO TOKENIZER, AND THAT IS A HOT-PATH FACT RATHER THAN A STYLE
28
+ * CHOICE. `_lib/token_count.ts` resolves `js-tiktoken` AT MODULE LOAD, and the
29
+ * concern that imports this file is statically reachable from
30
+ * `concern_registry.ts` — so importing it here made every hook dispatch on
31
+ * every slot pay a tokenizer load for a concern that is default-OFF and emits
32
+ * nothing. Measured on this tree: unbinding the concern moved the
33
+ * `pre_tool_use` p95 from 202 ms to 196 ms, and the CI latency gate went red on
34
+ * the branch that introduced it while passing on main.
35
+ *
36
+ * So the runtime cap is in BYTES. That is not a weaker bound — it is the same
37
+ * bound in the unit the budget actually enforces: `hook-token-budget.json`
38
+ * measures "bytes of concern stdout payload fields", so the cap and its
39
+ * registered row are now the same number instead of two units that need a
40
+ * conversion factor to compare. The offline model keeps exact-BPE tokens for
41
+ * its price table, where the tokenizer is the point and nothing is on a hot
42
+ * path.
43
+ *
44
+ * DETERMINISM IS THE CONTRACT, and the collision cases are the reason it needs
45
+ * stating. Two triggers on the SAME rule collapse to one entry (a rule is
46
+ * delivered once, however many of its triggers fired) while the trigger count
47
+ * survives as `score`. Two DIFFERENT rules both firing are returned in router
48
+ * declaration order, tier_1 before tier_2 — never in match order, which would
49
+ * make the output depend on trigger authoring order inside a rule. Capping
50
+ * sorts by descending score with router order as the tie-break, so the same
51
+ * prompt against the same router always yields the same bytes.
52
+ */
53
+ import * as fs from 'node:fs';
54
+ import * as path from 'node:path';
55
+
56
+ import { match_prompt, type Router, type Trigger } from './router_match.js';
57
+
58
+ export type { Router, Trigger };
59
+
60
+ /** Tier a matched rule came from. Kernel is never a value here. */
61
+ export type Tier = 'tier_1' | 'tier_2';
62
+
63
+ export interface TierRuleMatch {
64
+ id: string;
65
+ tier: Tier;
66
+ /** How many of this rule's triggers matched. Ties break on router order. */
67
+ score: number;
68
+ /** Router declaration index within its tier — the deterministic tie-break. */
69
+ order: number;
70
+ }
71
+
72
+ /** Kinds of trigger a prompt alone can never fire. */
73
+ export const PATH_TRIGGER_KINDS = ['path_prefix', 'file_pattern'] as const;
74
+
75
+ export function loadRouter(repoRoot: string): Router {
76
+ const p = path.join(repoRoot, 'dist', 'router.json');
77
+ return JSON.parse(fs.readFileSync(p, 'utf8')) as Router;
78
+ }
79
+
80
+ /** Kernel rule ids, as declared by the router. */
81
+ export function kernelIds(router: Router): Set<string> {
82
+ const k = Array.isArray(router['kernel']) ? router['kernel'] : [];
83
+ return new Set(k.map((x) => String(x)));
84
+ }
85
+
86
+ function tierEntries(router: Router, tier: Tier): Array<{ id: string; triggers: Trigger[] }> {
87
+ const raw = router[tier];
88
+ if (!Array.isArray(raw)) return [];
89
+ return (raw as unknown as Array<Record<string, unknown>>).map((r) => ({
90
+ id: String(r['id'] ?? ''),
91
+ triggers: Array.isArray(r['triggers']) ? (r['triggers'] as Trigger[]) : [],
92
+ }));
93
+ }
94
+
95
+ /** Every tier rule declared by the router, with its trigger list. */
96
+ export function allTierRules(router: Router): Array<{ id: string; tier: Tier; triggers: Trigger[] }> {
97
+ const out: Array<{ id: string; tier: Tier; triggers: Trigger[] }> = [];
98
+ for (const tier of ['tier_1', 'tier_2'] as Tier[]) {
99
+ for (const e of tierEntries(router, tier)) out.push({ ...e, tier });
100
+ }
101
+ return out;
102
+ }
103
+
104
+ /** Rules whose triggers are ALL path-shaped — unreachable from a prompt alone. */
105
+ export function pathOnlyRuleIds(router: Router): Set<string> {
106
+ const ids = new Set<string>();
107
+ for (const r of allTierRules(router)) {
108
+ if (r.triggers.length === 0) continue;
109
+ const allPath = r.triggers.every((t) =>
110
+ PATH_TRIGGER_KINDS.some((k) => k in t),
111
+ );
112
+ if (allPath) ids.add(r.id);
113
+ }
114
+ return ids;
115
+ }
116
+
117
+ /** Rules declaring at least one path-shaped trigger. */
118
+ export function pathCapableRuleIds(router: Router): Set<string> {
119
+ const ids = new Set<string>();
120
+ for (const r of allTierRules(router)) {
121
+ if (r.triggers.some((t) => PATH_TRIGGER_KINDS.some((k) => k in t))) ids.add(r.id);
122
+ }
123
+ return ids;
124
+ }
125
+
126
+ /** Rules declaring no trigger at all — cannot be delivered, must stay eager. */
127
+ export function triggerlessRuleIds(router: Router): string[] {
128
+ return allTierRules(router)
129
+ .filter((r) => r.triggers.length === 0)
130
+ .map((r) => r.id);
131
+ }
132
+
133
+ /**
134
+ * Which tier rules fire on this prompt (+ optional open files / command).
135
+ *
136
+ * Kernel is excluded. Order is router declaration order, tier_1 first.
137
+ */
138
+ export function matchTierRules(
139
+ router: Router,
140
+ prompt: string,
141
+ openFiles?: Iterable<string> | null,
142
+ command?: string | null,
143
+ ): TierRuleMatch[] {
144
+ const kernel = kernelIds(router);
145
+ const res = match_prompt(router, prompt, 'full', openFiles, command);
146
+ const scores = new Map<string, number>();
147
+ for (const mt of res.matched_triggers) {
148
+ const id = String(mt.rule ?? '');
149
+ if (id === '' || kernel.has(id)) continue;
150
+ scores.set(id, (scores.get(id) ?? 0) + 1);
151
+ }
152
+ const out: TierRuleMatch[] = [];
153
+ let order = 0;
154
+ for (const r of allTierRules(router)) {
155
+ const s = scores.get(r.id);
156
+ if (s === undefined) {
157
+ order += 1;
158
+ continue;
159
+ }
160
+ out.push({ id: r.id, tier: r.tier, score: s, order });
161
+ order += 1;
162
+ }
163
+ return out;
164
+ }
165
+
166
+ /** Where the projected body of a rule lives. */
167
+ export function ruleBodyPath(repoRoot: string, id: string): string {
168
+ return path.join(repoRoot, 'dist', 'agent-src', 'rules', `${id}.md`);
169
+ }
170
+
171
+ /** The projected body, or `null` when the rule has no projected file. */
172
+ export function loadRuleBody(repoRoot: string, id: string): string | null {
173
+ const p = ruleBodyPath(repoRoot, id);
174
+ if (!fs.existsSync(p)) return null;
175
+ return fs.readFileSync(p, 'utf8');
176
+ }
177
+
178
+ /**
179
+ * UTF-8 byte length — the runtime measure, dependency-free by design.
180
+ *
181
+ * Deliberately NOT a token count: see the header. Bytes are exact, need no
182
+ * tokenizer, and are the unit `hook-token-budget.json` enforces.
183
+ */
184
+ export function bytesOf(text: string): number {
185
+ return Buffer.byteLength(text, 'utf8');
186
+ }
187
+
188
+ export interface SelectionResult {
189
+ selected: TierRuleMatch[];
190
+ /** Matched but dropped because the byte cap was reached. */
191
+ dropped: TierRuleMatch[];
192
+ /** Byte sum of the selected bodies. */
193
+ bytes: number;
194
+ /** Per-rule body byte counts, keyed by id, for every MATCHED rule. */
195
+ bodyBytes: Map<string, number>;
196
+ }
197
+
198
+ /**
199
+ * Apply the per-prompt BYTE cap.
200
+ *
201
+ * Highest score first, router order as tie-break; a rule whose body would push
202
+ * the running total past `capBytes` is dropped and the walk continues, so one
203
+ * oversized body cannot starve the rest. The returned `selected` list is
204
+ * re-sorted back into router order — cap order is a budgeting concern, delivery
205
+ * order is not.
206
+ *
207
+ * ONE selection for both callers. The offline model and the runtime concern
208
+ * call THIS function with THE SAME cap, so the set the price table prices is
209
+ * byte-for-byte the set the concern delivers. That is step 0.5's requirement,
210
+ * and expressing the cap in bytes makes it hold without a token/byte
211
+ * conversion sitting between the two arms.
212
+ */
213
+ export function selectForInjection(
214
+ repoRoot: string,
215
+ matches: TierRuleMatch[],
216
+ capBytes: number,
217
+ ): SelectionResult {
218
+ const bodyBytes = new Map<string, number>();
219
+ for (const m of matches) {
220
+ const body = loadRuleBody(repoRoot, m.id);
221
+ bodyBytes.set(m.id, body === null ? 0 : bytesOf(body));
222
+ }
223
+ const ranked = [...matches].sort((a, b) => b.score - a.score || a.order - b.order);
224
+ const selected: TierRuleMatch[] = [];
225
+ const dropped: TierRuleMatch[] = [];
226
+ let total = 0;
227
+ for (const m of ranked) {
228
+ const t = bodyBytes.get(m.id) ?? 0;
229
+ if (total + t > capBytes && selected.length > 0) {
230
+ dropped.push(m);
231
+ continue;
232
+ }
233
+ selected.push(m);
234
+ total += t;
235
+ }
236
+ selected.sort((a, b) => a.order - b.order);
237
+ dropped.sort((a, b) => a.order - b.order);
238
+ return { selected, dropped, bytes: total, bodyBytes };
239
+ }