@event4u/agent-config 16.3.0 → 16.5.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 (532) 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 +178 -78
  5. package/README.md +2 -2
  6. package/agents/templates/.ai-council.yml.example +26 -8
  7. package/dist/agent-src/commands/analyze/inbox.md +10 -7
  8. package/dist/agent-src/commands/bug/investigate.md +1 -1
  9. package/dist/agent-src/commands/commit/in-chunks.md +40 -31
  10. package/dist/agent-src/commands/commit.md +72 -33
  11. package/dist/agent-src/commands/condense.md +2 -8
  12. package/dist/agent-src/commands/council/debate.md +11 -7
  13. package/dist/agent-src/commands/council/default.md +10 -9
  14. package/dist/agent-src/commands/council.md +1 -1
  15. package/dist/agent-src/commands/design.md +1 -1
  16. package/dist/agent-src/commands/estimate-ticket.md +1 -1
  17. package/dist/agent-src/commands/feature/explore.md +3 -3
  18. package/dist/agent-src/commands/feature/plan.md +1 -1
  19. package/dist/agent-src/commands/fix/ci.md +17 -3
  20. package/dist/agent-src/commands/fix/commit-messages.md +48 -23
  21. package/dist/agent-src/commands/fix/pr-comments.md +3 -6
  22. package/dist/agent-src/commands/implement-ticket.md +3 -3
  23. package/dist/agent-src/commands/memory/add.md +4 -16
  24. package/dist/agent-src/commands/memory/load.md +5 -6
  25. package/dist/agent-src/commands/memory/promote.md +3 -16
  26. package/dist/agent-src/commands/mission/upgrade.md +3 -2
  27. package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
  28. package/dist/agent-src/commands/optimize/deep.md +4 -2
  29. package/dist/agent-src/commands/optimize/rtk.md +4 -26
  30. package/dist/agent-src/commands/pr/create/description-only.md +18 -15
  31. package/dist/agent-src/commands/pr/create.md +42 -34
  32. package/dist/agent-src/commands/pr/merge.md +136 -9
  33. package/dist/agent-src/commands/prepare-for-review.md +13 -11
  34. package/dist/agent-src/commands/project/analyze.md +7 -35
  35. package/dist/agent-src/commands/refine-ticket.md +1 -1
  36. package/dist/agent-src/commands/review/changes.md +14 -19
  37. package/dist/agent-src/commands/roadmap/next.md +10 -1
  38. package/dist/agent-src/commands/roadmap/process-full.md +52 -38
  39. package/dist/agent-src/commands/roadmap/resolve-blockers.md +64 -14
  40. package/dist/agent-src/commands/worktree/create.md +7 -3
  41. package/dist/agent-src/commands/worktree/verify.md +2 -1
  42. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
  43. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
  44. package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
  45. package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
  46. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +1 -1
  47. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
  48. package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
  49. package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
  50. package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
  51. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
  52. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +67 -52
  53. package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
  54. package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
  55. package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
  56. package/dist/agent-src/contexts/execution/toolchain-resolver.md +4 -10
  57. package/dist/agent-src/contexts/subagent-configuration.md +1 -1
  58. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  59. package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  60. package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  61. package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  62. package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
  63. package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  64. package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  65. package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
  66. package/dist/agent-src/guidelines/design-antipatterns.md +1 -1
  67. package/dist/agent-src/guidelines/php/git.md +15 -2
  68. package/dist/agent-src/rules/active-remediation.md +0 -1
  69. package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
  70. package/dist/agent-src/rules/architecture.md +0 -1
  71. package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
  72. package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
  73. package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
  74. package/dist/agent-src/rules/autonomous-execution.md +0 -1
  75. package/dist/agent-src/rules/brand-consistency.md +0 -1
  76. package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
  77. package/dist/agent-src/rules/broken-access-control.md +0 -1
  78. package/dist/agent-src/rules/cli-output-handling.md +0 -1
  79. package/dist/agent-src/rules/code-comment-discipline.md +0 -1
  80. package/dist/agent-src/rules/code-provenance.md +0 -1
  81. package/dist/agent-src/rules/command-suggestion-policy.md +0 -1
  82. package/dist/agent-src/rules/commit-conventions.md +1 -2
  83. package/dist/agent-src/rules/communication-through-line.md +1 -2
  84. package/dist/agent-src/rules/content-quoting-floor.md +1 -2
  85. package/dist/agent-src/rules/context-hygiene.md +5 -5
  86. package/dist/agent-src/rules/copilot-routing.md +1 -2
  87. package/dist/agent-src/rules/council-availability.md +4 -4
  88. package/dist/agent-src/rules/cross-source-consistency.md +4 -6
  89. package/dist/agent-src/rules/decision-revisit-gate.md +2 -3
  90. package/dist/agent-src/rules/delegation-policy.md +2 -3
  91. package/dist/agent-src/rules/design-fidelity.md +0 -1
  92. package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
  93. package/dist/agent-src/rules/devcontainer-routing.md +1 -2
  94. package/dist/agent-src/rules/doc-screenshot-hygiene.md +0 -1
  95. package/dist/agent-src/rules/docker-commands.md +0 -1
  96. package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
  97. package/dist/agent-src/rules/domain-safety-disclaimer.md +0 -1
  98. package/dist/agent-src/rules/domain-safety-pii.md +0 -1
  99. package/dist/agent-src/rules/domain-safety-retention.md +0 -1
  100. package/dist/agent-src/rules/downstream-changes.md +0 -1
  101. package/dist/agent-src/rules/engineering-safety-floor.md +0 -1
  102. package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
  103. package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
  104. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -2
  105. package/dist/agent-src/rules/finance-safety-floor.md +0 -1
  106. package/dist/agent-src/rules/fix-what-you-see.md +6 -32
  107. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
  108. package/dist/agent-src/rules/git-history-discipline.md +1 -2
  109. package/dist/agent-src/rules/guidelines.md +0 -1
  110. package/dist/agent-src/rules/history-discipline.md +0 -1
  111. package/dist/agent-src/rules/icon-consistency.md +0 -1
  112. package/dist/agent-src/rules/image-likeness-and-rights.md +0 -1
  113. package/dist/agent-src/rules/improve-before-implement.md +0 -1
  114. package/dist/agent-src/rules/invite-challenge.md +0 -1
  115. package/dist/agent-src/rules/laravel-routing.md +1 -2
  116. package/dist/agent-src/rules/laravel-translations.md +0 -1
  117. package/dist/agent-src/rules/legal-safety-floor.md +0 -1
  118. package/dist/agent-src/rules/lethal-trifecta-guard.md +0 -1
  119. package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
  120. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +0 -1
  121. package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
  122. package/dist/agent-src/rules/media-governance-routing.md +1 -2
  123. package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
  124. package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
  125. package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
  126. package/dist/agent-src/rules/missing-tool-handling.md +0 -1
  127. package/dist/agent-src/rules/model-recommendation.md +0 -1
  128. package/dist/agent-src/rules/neighbour-precedence.md +64 -0
  129. package/dist/agent-src/rules/no-attribution-footers.md +0 -1
  130. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
  131. package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
  132. package/dist/agent-src/rules/no-roadmap-references.md +0 -1
  133. package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
  134. package/dist/agent-src/rules/onboarding-gate.md +0 -1
  135. package/dist/agent-src/rules/output-discipline.md +0 -1
  136. package/dist/agent-src/rules/package-ci-checks.md +0 -1
  137. package/dist/agent-src/rules/persona-governance.md +2 -3
  138. package/dist/agent-src/rules/php-coding.md +0 -1
  139. package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
  140. package/dist/agent-src/rules/preservation-guard.md +0 -1
  141. package/dist/agent-src/rules/provider-lifecycle-discipline.md +2 -3
  142. package/dist/agent-src/rules/question-not-instruction.md +0 -1
  143. package/dist/agent-src/rules/reviewer-awareness.md +0 -1
  144. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
  145. package/dist/agent-src/rules/roadmap-progress-sync.md +3 -6
  146. package/dist/agent-src/rules/role-mode-adherence.md +0 -1
  147. package/dist/agent-src/rules/rule-type-governance.md +0 -1
  148. package/dist/agent-src/rules/runtime-safety.md +0 -1
  149. package/dist/agent-src/rules/scale-discipline.md +0 -1
  150. package/dist/agent-src/rules/secret-vcs-guard.md +0 -1
  151. package/dist/agent-src/rules/security-sensitive-stop.md +0 -1
  152. package/dist/agent-src/rules/self-repair-loop.md +0 -1
  153. package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
  154. package/dist/agent-src/rules/session-canary.md +0 -1
  155. package/dist/agent-src/rules/settings-ask-protocol.md +5 -6
  156. package/dist/agent-src/rules/size-enforcement.md +0 -1
  157. package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
  158. package/dist/agent-src/rules/skill-quality.md +0 -1
  159. package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
  160. package/dist/agent-src/rules/source-confidentiality.md +0 -1
  161. package/dist/agent-src/rules/source-discovery-gate.md +0 -1
  162. package/dist/agent-src/rules/source-of-truth.md +0 -1
  163. package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
  164. package/dist/agent-src/rules/strategy-safety-floor.md +0 -1
  165. package/dist/agent-src/rules/symfony-routing.md +1 -2
  166. package/dist/agent-src/rules/think-before-action.md +0 -1
  167. package/dist/agent-src/rules/token-budget-discipline.md +14 -48
  168. package/dist/agent-src/rules/token-efficiency.md +0 -1
  169. package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
  170. package/dist/agent-src/rules/tool-safety.md +0 -1
  171. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  172. package/dist/agent-src/rules/untrusted-input-defense.md +0 -1
  173. package/dist/agent-src/rules/upstream-proposal.md +0 -1
  174. package/dist/agent-src/rules/user-interaction.md +0 -1
  175. package/dist/agent-src/rules/user-interrupt-priority.md +0 -1
  176. package/dist/agent-src/scripts/gate_budget.ts +47 -23
  177. package/dist/agent-src/scripts/gate_execute.ts +12 -4
  178. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +2 -0
  179. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  180. package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
  181. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
  182. package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
  183. package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
  184. package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
  185. package/dist/agent-src/skills/canvas-design/SKILL.md +4 -2
  186. package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
  187. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
  188. package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
  189. package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
  190. package/dist/agent-src/skills/database/data/manifest.json +2 -2
  191. package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
  192. package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +13 -10
  193. package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
  194. package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
  195. package/dist/agent-src/skills/fe-design/references/design-patterns.md +49 -2
  196. package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
  197. package/dist/agent-src/skills/frontend-render-security/SKILL.md +9 -0
  198. package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
  199. package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
  200. package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
  201. package/dist/agent-src/skills/iconography/SKILL.md +4 -2
  202. package/dist/agent-src/skills/image-provider-routing/SKILL.md +16 -5
  203. package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
  204. package/dist/agent-src/skills/logo-generation/SKILL.md +12 -8
  205. package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
  206. package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
  207. package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
  208. package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
  209. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +4 -2
  210. package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
  211. package/dist/agent-src/skills/svg-authoring/SKILL.md +318 -0
  212. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
  213. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
  214. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  215. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
  216. package/dist/agent-src/skills/wireframe/SKILL.md +5 -2
  217. package/dist/agent-src/templates/AGENTS.md +1 -1
  218. package/dist/agent-src/templates/agent-settings.md +46 -129
  219. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
  220. package/dist/agent-src/templates/command.md +2 -2
  221. package/dist/agent-src/templates/roadmaps.md +6 -1
  222. package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  223. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  224. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  225. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  226. package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  227. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  228. package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  229. package/dist/agent-src/templates/skill.md +1 -1
  230. package/dist/cli/commands/uiRender.js +201 -4
  231. package/dist/cli/commands/uiRenderAsset.js +1308 -0
  232. package/dist/cli/commands/uiRenderAssetCompare.js +752 -0
  233. package/dist/cli/commands/uiRenderAssetGrid.js +265 -0
  234. package/dist/cli/commands/uiRenderAssetGrounds.js +323 -0
  235. package/dist/cli/commands/uiRenderAssetIsolation.js +85 -0
  236. package/dist/cli/commands/uiRenderAssetTypes.js +145 -0
  237. package/dist/cli/main.js +47 -1
  238. package/dist/cli/registry.js +2 -1
  239. package/dist/cli-delegate/check_update_banner.js +195 -119
  240. package/dist/cli-delegate/{chunk-S4UMZNES.js → chunk-4DBG5K7M.js} +5 -5
  241. package/dist/cli-delegate/{chunk-TRI6DQZ6.js → chunk-5GPULQBE.js} +60 -42
  242. package/dist/cli-delegate/{chunk-JQVYKIZA.js → chunk-6SRJTYJS.js} +33 -177
  243. package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
  244. package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
  245. package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
  246. package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
  247. package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
  248. package/dist/cli-delegate/{chunk-NXEILF2K.js → chunk-F3QDG2GT.js} +481 -72
  249. package/dist/cli-delegate/{chunk-CQWX336L.js → chunk-G5VVIZRC.js} +3 -3
  250. package/dist/cli-delegate/{chunk-JY2I2DXV.js → chunk-H76PHZBI.js} +137 -60
  251. package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
  252. package/dist/cli-delegate/{chunk-LIYTLH6O.js → chunk-KXXWJTDO.js} +5 -10
  253. package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
  254. package/dist/cli-delegate/{chunk-S4YWNRKO.js → chunk-MDPPKQCQ.js} +34 -1
  255. package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
  256. package/dist/cli-delegate/{chunk-GKE7ZVFA.js → chunk-PWX7CKMD.js} +1 -1
  257. package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
  258. package/dist/cli-delegate/{chunk-RQNNW7IF.js → chunk-T7XSJSUQ.js} +433 -632
  259. package/dist/cli-delegate/{chunk-NQ7WT43P.js → chunk-TMWYUOK4.js} +75 -12
  260. package/dist/cli-delegate/{chunk-EEHDXSYD.js → chunk-WQWXGIWB.js} +12 -273
  261. package/dist/cli-delegate/{chunk-QLDHIPWR.js → chunk-XQTPXKFA.js} +1 -1
  262. package/dist/cli-delegate/cmd_analyze_session.js +223 -43
  263. package/dist/cli-delegate/cmd_brand_status.js +3 -2
  264. package/dist/cli-delegate/cmd_conformance.js +489 -238
  265. package/dist/cli-delegate/cmd_converge.js +3 -3
  266. package/dist/cli-delegate/cmd_doctor.js +15 -12
  267. package/dist/cli-delegate/cmd_doctor_neighbours.js +11 -4
  268. package/dist/cli-delegate/cmd_explain.js +43 -19
  269. package/dist/cli-delegate/cmd_export.js +10 -10
  270. package/dist/cli-delegate/cmd_git_convention.js +1851 -0
  271. package/dist/cli-delegate/cmd_handoff.js +40 -21
  272. package/dist/cli-delegate/cmd_mcp_available.js +3 -2
  273. package/dist/cli-delegate/cmd_migrate.js +3 -3
  274. package/dist/cli-delegate/cmd_packs_active.js +4 -3
  275. package/dist/cli-delegate/cmd_prune.js +10 -10
  276. package/dist/cli-delegate/cmd_route_audit.js +7 -7
  277. package/dist/cli-delegate/cmd_route_explain.js +5 -5
  278. package/dist/cli-delegate/cmd_session_recycle.js +9 -7
  279. package/dist/cli-delegate/cmd_settings_check.js +54 -7
  280. package/dist/cli-delegate/cmd_settings_get.js +18 -8
  281. package/dist/cli-delegate/cmd_settings_migrate.js +10 -10
  282. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  283. package/dist/cli-delegate/cmd_sync.js +10 -10
  284. package/dist/cli-delegate/cmd_uninstall.js +10 -10
  285. package/dist/cli-delegate/cmd_update.js +7 -7
  286. package/dist/cli-delegate/cmd_upgrade.js +7 -7
  287. package/dist/cli-delegate/cmd_validate.js +10 -10
  288. package/dist/cli-delegate/cmd_versions.js +5 -5
  289. package/dist/cli-delegate/gate_budget.js +1 -1
  290. package/dist/cli-delegate/gate_execute.js +2 -2
  291. package/dist/cli-delegate/roadmap_gates.js +2 -2
  292. package/dist/discovery/deprecation-report.md +1 -1
  293. package/dist/discovery/discovery-manifest.json +171 -107
  294. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  295. package/dist/discovery/discovery-manifest.summary.md +7 -7
  296. package/dist/discovery/dormancy-report.md +1 -1
  297. package/dist/discovery/orphan-report.md +1 -1
  298. package/dist/discovery/packs.json +41 -22
  299. package/dist/discovery/trust-report.md +3 -3
  300. package/dist/discovery/workspaces.json +16 -11
  301. package/dist/hooks/dispatch.js +2520 -1924
  302. package/dist/hooks/dispatch.sha256 +1 -1
  303. package/dist/hooks/svg_rules.chunk.cjs +1626 -0
  304. package/dist/hooks/svg_rules.chunk.cjs.sha256 +1 -0
  305. package/dist/install/install.mjs +462 -431
  306. package/dist/install/installThinLayer.js +40 -12
  307. package/dist/install/installedRuleLinks.js +105 -19
  308. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  309. package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
  310. package/dist/mcp/registry-manifest.json +5 -5
  311. package/dist/mcp/server.json +2 -2
  312. package/dist/mcp/server.mjs +3063 -2160
  313. package/dist/router.json +1 -1
  314. package/dist/scripts/_lib/agent_settings.js +15 -120
  315. package/dist/scripts/_lib/compile_time_toggles.js +12 -21
  316. package/dist/scripts/_lib/git_convention.js +232 -0
  317. package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
  318. package/dist/scripts/_lib/installed_layer.js +344 -18
  319. package/dist/scripts/_lib/package_root.js +68 -0
  320. package/dist/scripts/_lib/rule_law_section.js +1 -1
  321. package/dist/scripts/_lib/scan_scope.js +105 -0
  322. package/dist/scripts/_lib/settings_removed_keys.js +168 -0
  323. package/dist/scripts/_lib/svg_dom.js +284 -0
  324. package/dist/scripts/_lib/svg_geometry.js +477 -0
  325. package/dist/scripts/_lib/svg_motion.js +179 -0
  326. package/dist/scripts/_lib/svg_rules.js +1337 -0
  327. package/dist/scripts/_lib/svg_urls.js +67 -0
  328. package/dist/scripts/_lib/thin_rules.js +80 -3
  329. package/dist/scripts/lint_svg_asset.js +324 -0
  330. package/dist/server/app.js +2 -1
  331. package/dist/server/gitKeysGate.js +105 -0
  332. package/dist/server/routes/settings.js +67 -8
  333. package/dist/server/routes/wizard.js +4 -0
  334. package/dist/server/schemas/settings.js +23 -81
  335. package/dist/shared/skillRanking.js +30 -4
  336. package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
  337. package/dist/ui/index.html +1 -1
  338. package/docs/MIGRATION.md +72 -1
  339. package/docs/contracts/settings-classes.md +35 -65
  340. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  341. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  342. package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  343. package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  344. package/docs/guidelines/agent-infra/layered-settings.md +13 -1
  345. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  346. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  347. package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
  348. package/docs/guidelines/design-antipatterns.md +1 -1
  349. package/docs/guidelines/php/git.md +15 -2
  350. package/package.json +4 -3
  351. package/src/agent-src/scripts/gate_budget.ts +47 -23
  352. package/src/agent-src/scripts/gate_execute.ts +12 -4
  353. package/src/agent-src/templates/AGENTS.md +1 -1
  354. package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  355. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  356. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  357. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  358. package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  359. package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  360. package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  361. package/src/config/agent-settings.template.yml +79 -312
  362. package/src/config/agents-paths.yml +16 -0
  363. package/src/config/assurance-capability-registry.json +7 -3
  364. package/src/config/ci-local-parity.yml +3 -0
  365. package/src/config/continuity-surface.json +3 -3
  366. package/src/config/discovery/packs.yml +17 -1
  367. package/src/config/evaluator-budgets.json +19 -6
  368. package/src/config/gate-coverage.yml +65 -23
  369. package/src/config/gate-violation-baselines.json +56 -16
  370. package/src/config/gitignore-block.txt +19 -0
  371. package/src/config/hook-bundle-budget.json +1 -1
  372. package/src/config/release-gate-locality.yml +2 -1
  373. package/src/config/routing-coverage-seed.json +55 -6
  374. package/src/config/rule-law-ceilings.json +12 -6
  375. package/src/config/rule-obligations.json +833 -0
  376. package/src/install/installThinLayer.ts +76 -10
  377. package/src/install/installedRuleLinks.ts +105 -17
  378. package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
  379. package/src/scripts/_cli/cmd_conformance.ts +13 -2
  380. package/src/scripts/_cli/cmd_doctor.ts +10 -10
  381. package/src/scripts/_cli/cmd_doctor_neighbours.ts +133 -0
  382. package/src/scripts/_cli/cmd_explain.ts +72 -14
  383. package/src/scripts/_cli/cmd_git_convention.ts +637 -0
  384. package/src/scripts/_cli/cmd_settings_check.ts +66 -8
  385. package/src/scripts/_cli/cmd_settings_get.ts +18 -3
  386. package/src/scripts/_cli/doctor_execution.ts +72 -1
  387. package/src/scripts/_dispatch.bash +37 -2
  388. package/src/scripts/_lib/agent_settings.ts +15 -124
  389. package/src/scripts/_lib/blocked_by_marker.ts +1 -1
  390. package/src/scripts/_lib/compile_time_toggles.ts +12 -21
  391. package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
  392. package/src/scripts/_lib/git_base_ref.ts +92 -0
  393. package/src/scripts/_lib/git_convention.ts +291 -0
  394. package/src/scripts/_lib/git_convention_carrier.ts +389 -0
  395. package/src/scripts/_lib/git_convention_grammar.ts +246 -0
  396. package/src/scripts/_lib/git_convention_measure.ts +344 -0
  397. package/src/scripts/_lib/global_deploy_inventory.ts +74 -0
  398. package/src/scripts/_lib/graph_feeder_record.ts +236 -13
  399. package/src/scripts/_lib/installed_layer.ts +448 -23
  400. package/src/scripts/_lib/knowledge_global.ts +14 -1
  401. package/src/scripts/_lib/module_reach.ts +456 -0
  402. package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
  403. package/src/scripts/_lib/neighbour_tool_use.ts +8 -10
  404. package/src/scripts/_lib/ratification_artifact.ts +220 -1
  405. package/src/scripts/_lib/ratification_subject.ts +92 -0
  406. package/src/scripts/_lib/release_findings_ingest.ts +15 -7
  407. package/src/scripts/_lib/release_highlights.ts +5 -0
  408. package/src/scripts/_lib/rule_law_section.ts +1 -1
  409. package/src/scripts/_lib/rule_obligations.ts +205 -0
  410. package/src/scripts/_lib/self_repair.ts +1 -1
  411. package/src/scripts/_lib/settings_removed_keys.ts +173 -0
  412. package/src/scripts/_lib/svg_dom.ts +318 -0
  413. package/src/scripts/_lib/svg_geometry.ts +480 -0
  414. package/src/scripts/_lib/svg_motion.ts +208 -0
  415. package/src/scripts/_lib/svg_rules.ts +1453 -0
  416. package/src/scripts/_lib/svg_urls.ts +72 -0
  417. package/src/scripts/_lib/test_red_state.ts +5 -0
  418. package/src/scripts/_lib/thin_rules.ts +83 -3
  419. package/src/scripts/_lib/touched_file_quality.ts +142 -6
  420. package/src/scripts/_lib/verification_command.ts +59 -0
  421. package/src/scripts/_lib/verify_clause.ts +27 -17
  422. package/src/scripts/_lib/worker_budget.ts +1 -1
  423. package/src/scripts/adr/regenerate_index.ts +16 -1
  424. package/src/scripts/ai_council/config.ts +7 -7
  425. package/src/scripts/ai_council/modification_review.ts +100 -0
  426. package/src/scripts/ai_council/modification_review_questions.ts +90 -0
  427. package/src/scripts/ai_council/orchestrator.ts +5 -5
  428. package/src/scripts/ai_council/spend_gate.ts +18 -7
  429. package/src/scripts/ai_team/config.ts +11 -16
  430. package/src/scripts/audit_user_type_axis.ts +20 -8
  431. package/src/scripts/bench_ab_clone.ts +1 -2
  432. package/src/scripts/bench_concern_spawn_path.ts +129 -0
  433. package/src/scripts/bench_graph_feeder_latency.ts +584 -0
  434. package/src/scripts/check_agent_artifact_location.ts +36 -0
  435. package/src/scripts/check_branch_freshness.ts +147 -24
  436. package/src/scripts/check_enforcement_coverage.ts +130 -0
  437. package/src/scripts/check_evaluator_budgets.ts +4 -0
  438. package/src/scripts/check_finding_dispositions.ts +88 -25
  439. package/src/scripts/check_gate_coverage.ts +4 -3
  440. package/src/scripts/check_gate_reachability.ts +48 -1
  441. package/src/scripts/check_generator_sync.ts +48 -0
  442. package/src/scripts/check_hook_bundle_composition.ts +107 -0
  443. package/src/scripts/check_kernel_edit_ratified.ts +86 -6
  444. package/src/scripts/check_pack_size.ts +1 -1
  445. package/src/scripts/check_release_manifest_content.ts +189 -0
  446. package/src/scripts/check_standing_rule_delivery.ts +96 -0
  447. package/src/scripts/check_suppression_hygiene.ts +2 -5
  448. package/src/scripts/ci_settle.ts +42 -5
  449. package/src/scripts/classify_merge_risk.ts +320 -0
  450. package/src/scripts/closure_scan.ts +91 -1
  451. package/src/scripts/command_suggester/settings.ts +8 -108
  452. package/src/scripts/command_suggester/types.ts +4 -5
  453. package/src/scripts/consumer_matrix.ts +102 -0
  454. package/src/scripts/context_hygiene_hook.ts +13 -23
  455. package/src/scripts/cost/preflight.mjs +12 -1
  456. package/src/scripts/council_cli.ts +7 -7
  457. package/src/scripts/dispatch_r2_reviewer.ts +24 -11
  458. package/src/scripts/explain_run.ts +8 -13
  459. package/src/scripts/generate_git_convention_grammar.ts +127 -0
  460. package/src/scripts/generate_host_cost_table.ts +20 -3
  461. package/src/scripts/handoff_context_hook.ts +86 -37
  462. package/src/scripts/hook_manifest.json +1 -1
  463. package/src/scripts/hook_manifest.yaml +25 -6
  464. package/src/scripts/hooks/block_config_weakening.ts +48 -5
  465. package/src/scripts/hooks/block_plumbing_writes.ts +8 -4
  466. package/src/scripts/hooks/block_speaking_inbox_dir.ts +16 -1
  467. package/src/scripts/hooks/concern_registry.ts +2 -0
  468. package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
  469. package/src/scripts/hooks/design_pass_hook.ts +157 -3
  470. package/src/scripts/hooks/evidence_independence.ts +12 -4
  471. package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
  472. package/src/scripts/hooks/push_settle_hook.ts +4 -3
  473. package/src/scripts/hooks/rule_inject_hook.ts +44 -3
  474. package/src/scripts/hooks/state_io.ts +8 -4
  475. package/src/scripts/hooks/svg_obligation.ts +497 -0
  476. package/src/scripts/hooks/svg_rules_loader.ts +133 -0
  477. package/src/scripts/hooks/telemetry_usage_hook.ts +7 -46
  478. package/src/scripts/hooks/turn_end_gate_hook.ts +6 -3
  479. package/src/scripts/install-hooks.sh +42 -14
  480. package/src/scripts/install.ts +7 -31
  481. package/src/scripts/lint_blocker_citations.ts +198 -0
  482. package/src/scripts/lint_consolidation_lineage.ts +6 -3
  483. package/src/scripts/lint_design_quality.ts +60 -12
  484. package/src/scripts/lint_evidence_artifacts.ts +9 -0
  485. package/src/scripts/lint_hook_concern_budget.ts +10 -21
  486. package/src/scripts/lint_motion_authority_drift.ts +27 -0
  487. package/src/scripts/lint_pack_boundaries.ts +406 -28
  488. package/src/scripts/lint_roadmap_blockers.ts +282 -29
  489. package/src/scripts/lint_roadmap_complexity.ts +4 -2
  490. package/src/scripts/lint_rule_law_section.ts +151 -21
  491. package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
  492. package/src/scripts/lint_settings_classes.ts +1 -1
  493. package/src/scripts/lint_svg_asset.ts +365 -0
  494. package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
  495. package/src/scripts/lint_workflow_security.ts +12 -1
  496. package/src/scripts/measure_skill_ranker_baseline.ts +5 -0
  497. package/src/scripts/model_rule_injection.ts +3 -3
  498. package/src/scripts/onboarding_gate_hook.ts +7 -6
  499. package/src/scripts/print_required_checks.ts +15 -3
  500. package/src/scripts/probe_host_compliance.ts +33 -5
  501. package/src/scripts/probe_turnaround.ts +183 -3
  502. package/src/scripts/ratification_header.ts +77 -0
  503. package/src/scripts/refusal_envelope.ts +138 -0
  504. package/src/scripts/release_publication.ts +29 -3
  505. package/src/scripts/report_dispatch_import_closure.ts +76 -0
  506. package/src/scripts/report_installed_rule_links.ts +118 -1
  507. package/src/scripts/report_module_reach.ts +212 -0
  508. package/src/scripts/report_obligation_mechanism.ts +16 -18
  509. package/src/scripts/report_parked_blockers.ts +222 -0
  510. package/src/scripts/report_required_checks_drift.ts +196 -0
  511. package/src/scripts/report_skill_ranker_confusion.ts +103 -2
  512. package/src/scripts/report_standing_payload_by_host.ts +1 -1
  513. package/src/scripts/routing_doctor.ts +2 -2
  514. package/src/scripts/schemas/agent-settings.schema.json +17 -31
  515. package/src/scripts/schemas/command.schema.json +1 -1
  516. package/src/scripts/schemas/skill.schema.json +1 -1
  517. package/src/scripts/self_review_gate.ts +39 -2
  518. package/src/scripts/skill_tools/score_skill_relevance.ts +28 -19
  519. package/src/scripts/sync_pr_branch.ts +403 -132
  520. package/src/scripts/trigger_eval_grandfather.json +0 -16
  521. package/src/server/schemas/settings.ts +33 -177
  522. package/src/shared/skillRanking.ts +53 -4
  523. package/dist/cli-delegate/chunk-NYX3L2PZ.js +0 -99
  524. package/src/scripts/_lib/config_chain.ts +0 -477
  525. package/src/scripts/_lib/conformance_report.ts +0 -403
  526. package/src/scripts/_lib/eval_discrimination.ts +0 -95
  527. package/src/scripts/_lib/evaluator_promotion.ts +0 -225
  528. package/src/scripts/_lib/file_slicer.ts +0 -117
  529. package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
  530. package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
  531. package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
  532. package/src/scripts/_lib/role_split.ts +0 -338
@@ -126,12 +126,13 @@ const _PATH_KEYS: readonly string[] = ['file_path', 'path', 'target_file', 'file
126
126
  export type ConfigKind = 'allowlist' | 'advisory' | 'class-c' | null;
127
127
 
128
128
  /**
129
- * Project settings files whose keys this guard fences. User-global files are
129
+ * Project settings files, and the committed git-convention carrier (ADR-283),
130
+ * whose keys this guard fences. User-global files are
130
131
  * never in reach — the hook only sees a path a tool call named, and the writes
131
132
  * that reach `~/.event4u/` go through `settings:set`, which applies its own
132
133
  * per-key refusal.
133
134
  */
134
- const CLASS_C_BASENAMES: readonly string[] = ['.agent-settings.yml', '.agent-settings.yaml', 'settings.json'];
135
+ const CLASS_C_BASENAMES: readonly string[] = ['.agent-settings.yml', '.agent-settings.yaml', '.git-convention.yml', 'settings.json'];
135
136
 
136
137
  /** Where the class contract lives, relative to the package root. */
137
138
  export const SETTINGS_CLASSES_RELATIVE = 'docs/contracts/settings-classes.md';
@@ -382,10 +383,25 @@ export interface Decision {
382
383
  * unreadable for the length of one tool call, which is a bypass with no
383
384
  * authorisation step in it.
384
385
  *
386
+ * The edit forms it models: `Write` (`content`), `Edit` (`old_string` /
387
+ * `new_string`, with `replace_all`) and `MultiEdit` (an `edits` list of those
388
+ * pairs, applied in order).
389
+ *
385
390
  * WHAT IT CANNOT SEE: an edit applied through a shell redirect rather than an
386
391
  * edit tool — `EDIT_TOOLS` is the corpus, and the shell shapes are
387
392
  * `block_plumbing_writes`' subject, not this one.
388
393
  */
394
+ /** The host writes `new_string` verbatim; `String.replace` would expand `$&`-style tokens. */
395
+ function _replaceFirstLiteral(text: string, from: string, to: string): string {
396
+ const at = text.indexOf(from);
397
+ return text.slice(0, at) + to + text.slice(at + from.length);
398
+ }
399
+
400
+ /** An empty `old_string` is the host's create form, which `split('')` does not model. */
401
+ function _applyEdit(text: string, from: string, to: string, all: unknown): string {
402
+ return all === true && from !== '' ? text.split(from).join(to) : _replaceFirstLiteral(text, from, to);
403
+ }
404
+
389
405
  export function classCVerdict(
390
406
  ti: JsonObject,
391
407
  on_disk: string | null,
@@ -397,11 +413,22 @@ export function classCVerdict(
397
413
  const content = ti['content'];
398
414
  const oldStr = ti['old_string'];
399
415
  const newStr = ti['new_string'];
416
+ const forms = [content !== undefined, oldStr !== undefined || newStr !== undefined, ti['edits'] !== undefined];
417
+ if (forms.filter(Boolean).length > 1) {
418
+ return (
419
+ `${rel_path}: this edit carries more than one edit form (content, old_string/new_string, edits), ` +
420
+ 'so the guard cannot tell which one the host will run and no key can be cleared. ' +
421
+ 'Re-send it as one form, or write the change through `agent-config settings:set`.'
422
+ );
423
+ }
424
+ // A file that does not exist yet is created by an edit with an empty
425
+ // `old_string`, so it is modelled as empty text rather than left unchecked.
426
+ const current = on_disk ?? '';
400
427
  let afterText: string | null = null;
401
428
  if (typeof content === 'string') {
402
429
  afterText = content;
403
- } else if (typeof oldStr === 'string' && typeof newStr === 'string' && on_disk !== null) {
404
- if (!on_disk.includes(oldStr)) return null; // the edit will not apply
430
+ } else if (typeof oldStr === 'string' && typeof newStr === 'string') {
431
+ if (!current.includes(oldStr)) return null; // the edit will not apply
405
432
  // `replace_all` is the host's own flag and it changes WHICH text the
406
433
  // edit produces. Modelling only the first occurrence let a
407
434
  // `replace_all` edit whose SECOND occurrence is the Class C one pass
@@ -417,7 +444,23 @@ export function classCVerdict(
417
444
  '`agent-config settings:set`.'
418
445
  );
419
446
  }
420
- afterText = all === true ? on_disk.split(oldStr).join(newStr) : on_disk.replace(oldStr, newStr);
447
+ afterText = _applyEdit(current, oldStr, newStr, all);
448
+ } else if (ti['edits'] !== undefined && !Array.isArray(ti['edits'])) {
449
+ return `${rel_path}: this MultiEdit carries an edit the guard cannot interpret, so the text it would produce is unknown and no key can be cleared. Re-send it as plain edits, or write the change through \`agent-config settings:set\`.`;
450
+ } else if (Array.isArray(ti['edits'])) {
451
+ // MultiEdit applies its pairs in order and applies none when one misses.
452
+ let text = current;
453
+ for (const e of ti['edits']) {
454
+ const o = _isObject(e) ? e['old_string'] : undefined;
455
+ const n = _isObject(e) ? e['new_string'] : undefined;
456
+ const a = _isObject(e) ? e['replace_all'] : undefined;
457
+ if (typeof o !== 'string' || typeof n !== 'string' || (a !== undefined && typeof a !== 'boolean')) {
458
+ return `${rel_path}: this MultiEdit carries an edit the guard cannot interpret, so the text it would produce is unknown and no key can be cleared. Re-send it as plain edits, or write the change through \`agent-config settings:set\`.`;
459
+ }
460
+ if (!text.includes(o)) return null;
461
+ text = _applyEdit(text, o, n, a);
462
+ }
463
+ afterText = text;
421
464
  }
422
465
  if (afterText === null) return null;
423
466
 
@@ -5,10 +5,14 @@
5
5
  * road-to-a-kernel-that-guards-its-plumbing 1.2 — the other half of a
6
6
  * deliberate split, and the split is the whole design:
7
7
  *
8
- * · plumbing SOURCES (`hook_manifest.yaml`, `host_lowering.yaml`, the
9
- * `*-dispatcher.sh` trampolines, both hook budget files) are edited
10
- * legitimately all the time. They carry a RECORD — the ADR-268 § 4
11
- * ratification gate, path set extended by step 1.1 — not a deny.
8
+ * · plumbing SOURCES are edited legitimately all the time. They carry a
9
+ * RECORD — the ADR-268 § 4 ratification gate — not a deny. Which files
10
+ * count is NOT listed here: it is `check_kernel_edit_ratified`'s
11
+ * `PLUMBING_SOURCE_RE` plus the verdict-deciding modules of the
12
+ * dispatcher's import closure (`derivedPlumbing`), computed at run time.
13
+ * A copy of that list here drifted once already — it named the pattern
14
+ * and missed the policy module a refactor moved out of the dispatcher —
15
+ * so this header points at the set instead of restating it.
12
16
  * · plumbing BUILD OUTPUTS have no legitimate hand edit at all. They carry a
13
17
  * DENY, which is this file.
14
18
  *
@@ -149,11 +149,26 @@ function isGlobOverAcceptableInboxDir(name: string): boolean {
149
149
  if (!/[*?[\]]/.test(name)) {
150
150
  return false;
151
151
  }
152
- const literal = name.replace(/[*?]|\[[^\]]*\]/g, '');
152
+ // Only a generic class is stripped. Any other bracket payload stays in the
153
+ // literal and is judged: `[acme]` names a source, and so does a run of
154
+ // singleton ranges like `[a-a][c-c]`.
155
+ const literal = name.replace(/[*?]|\[(?:a-z|0-9|a-f|0-9a-f|a-z0-9)\]/gi, '');
156
+ if (OPAQUE_FAMILY_WILDCARD_RE.test(name)) {
157
+ return true;
158
+ }
153
159
  const trimmed = literal.replace(/-+$/, '');
154
160
  return trimmed !== '' && isAcceptableInboxDir(trimmed);
155
161
  }
156
162
 
163
+ /**
164
+ * The fixed head of an opaque identifier family followed by wildcards only.
165
+ * `round-*` lists hex rounds; as a literal it would create `round-*`, which
166
+ * carries no source. Brackets are excluded because their payload survives as
167
+ * literal text: `round-[acme]-*` must not pass by stripping to `round`.
168
+ */
169
+ const OPAQUE_FAMILY_WILDCARD_RE =
170
+ /^(?:(?:inbox|round|src-set|source-set)(?:-\d{4}(?:-\d{2})?)?|set)-[*?]+$/i;
171
+
157
172
  /**
158
173
  * The paths a redirect inside one segment would create.
159
174
  *
@@ -65,6 +65,7 @@ import { main as suggestionCaptureMain } from './suggestion_capture_hook.js';
65
65
  import { main as uiRouteNudgeMain } from './ui_route_nudge_hook.js';
66
66
  import { main as orchestrationRecordMain } from './orchestration_record_hook.js';
67
67
  import { main as telemetryUsageMain } from './telemetry_usage_hook.js';
68
+ import { main as mcpUsageObservationMain } from './mcp_usage_observation_hook.js';
68
69
  import { main as telemetryFlushMain } from './telemetry_flush_hook.js';
69
70
  import { main as delegationNudgeMain } from './delegation_nudge_hook.js';
70
71
  import { main as skillRouteMain } from './skill_route_hook.js';
@@ -132,6 +133,7 @@ export const CONCERN_REGISTRY: Readonly<Record<string, ConcernMain>> = {
132
133
  'src/scripts/hooks/ui_route_nudge_hook.ts': uiRouteNudgeMain as ConcernMain,
133
134
  'src/scripts/hooks/orchestration_record_hook.ts': orchestrationRecordMain as ConcernMain,
134
135
  'src/scripts/hooks/telemetry_usage_hook.ts': telemetryUsageMain as ConcernMain,
136
+ 'src/scripts/hooks/mcp_usage_observation_hook.ts': mcpUsageObservationMain as ConcernMain,
135
137
  'src/scripts/hooks/telemetry_flush_hook.ts': telemetryFlushMain as ConcernMain,
136
138
  'src/scripts/hooks/delegation_nudge_hook.ts': delegationNudgeMain as ConcernMain,
137
139
  'src/scripts/hooks/skill_route_hook.ts': skillRouteMain as ConcernMain,
@@ -320,11 +320,10 @@ export function resolveActivation(
320
320
  const emergency = (settings["emergency"] ?? {}) as Record<string, unknown>;
321
321
  const halted = emergency["orchestration_halt"] === true;
322
322
  const host_manifest = probeHostCapabilities(hostId);
323
- const sub = (settings["subagents"] ?? {}) as Record<string, unknown>;
324
- const downshift = sub["downshift"] !== false;
325
323
  return {
326
324
  activation: { halted, subagent_spawn: host_manifest.subagent_spawn },
327
- downshift,
325
+ // Fixed on: the `subagents.downshift` key was retired with this default.
326
+ downshift: true,
328
327
  separate_quota_pool: host_manifest.separate_quota_pool,
329
328
  agentTeams: host_manifest.agent_teams,
330
329
  };
@@ -48,6 +48,37 @@
48
48
  * reason instead of passing silently. `src/rules/design-review-after-ui-write.md`
49
49
  * already required that in prose with nothing to read it; this emits it.
50
50
  *
51
+ * SVG ROUTING, AND HOW TO REVERT IT. Step 1.3 of
52
+ * `road-to-svg-rules-in-the-dispatch-path`. An `.svg` write reaches
53
+ * `lint_svg_asset`'s rules from here, inside or outside a UI tree, through
54
+ * `svg_rules_loader` — a CJS chunk beside the dispatch bundle, required only
55
+ * for a path ending in `.svg`. The review that withdrew the first attempt
56
+ * named the gap plainly: a statically bundled rule set has no off switch, so
57
+ * the triggers and the mechanism are written down before they are needed.
58
+ *
59
+ * REVERT WHEN any of these is read:
60
+ * · the composition gate exceeds its ceiling attributable to this routing
61
+ * (`check_hook_bundle_composition`; measured 2,128 B on 2026-10-11, with
62
+ * 16,910 B of headroom left);
63
+ * · a non-`.svg` hook event is observed failing with a stack that passes
64
+ * through the chunk — the containment case
65
+ * `design_pass_svg.test.ts > rule-failure-does-not-reach-an-unrelated-event`
66
+ * exists to make impossible, so one observation is enough;
67
+ * · an operator reports SVG false positives on files they accept, at a rate
68
+ * that would make them switch the carrier off — Risk 1 of the parent
69
+ * roadmap, and the failure this whole concern is shaped around.
70
+ *
71
+ * THE MECHANISM, in increasing order of blast radius:
72
+ * 1. Delete `dist/hooks/svg_rules.chunk.cjs`. The loader returns `null`,
73
+ * the pass reports `verification: degraded` naming the missing file,
74
+ * and every other finding family is unaffected. This is the per-install
75
+ * off switch the static import could not have had.
76
+ * 2. Drop `isSvgAsset` from `isPassSurface` below. An `.svg` outside a UI
77
+ * tree stops reaching the pass at all; one inside it reads as it did
78
+ * before this step.
79
+ * 3. Turn the concern off entirely (`hooks.design_pass.enabled: false`),
80
+ * which was already the default.
81
+ *
51
82
  * Default-OFF (`hooks.design_pass.enabled`), fail_closed: false.
52
83
  *
53
84
  * Exit codes (dispatcher contract): 0 allow · 1 block · 2 warn + JSON reason.
@@ -62,6 +93,7 @@ import { trigger_matches, type Trigger } from '../_lib/router_match.js';
62
93
  import { isUiPath, isUiTreePath } from '../_lib/ui_surface.js';
63
94
  import { loadDesignContext, scanFile } from '../lint_design_slop.js';
64
95
  import { readHookStdin } from './hook_stdin.js';
96
+ import { CHUNK_BASENAME, loadSvgScan } from './svg_rules_loader.js';
65
97
  import { EXIT_ALLOW, EXIT_WARN } from './exit_codes.js';
66
98
 
67
99
  const SETTINGS_FILE = '.agent-settings.yml';
@@ -129,8 +161,42 @@ export function enabled(root: string, key = 'design_pass'): boolean {
129
161
  return false;
130
162
  }
131
163
 
164
+ /**
165
+ * True for a path this pass should look at.
166
+ *
167
+ * `.svg` is admitted HERE and nowhere else — step 1.2 of
168
+ * `road-to-svg-rules-in-the-dispatch-path`. The obvious way to reach an `.svg`
169
+ * from this pass was to add `svg` to `UI_EXTENSIONS`, and that is the thing the
170
+ * parent roadmap's decision E10 and `design_pass_svg.test.ts` forbid: the same
171
+ * predicate backs `ui_route_nudge_hook`'s `isUiWrite`, so widening it would
172
+ * draw a UI routing nudge and its tokens on every icon dropped into `public/`.
173
+ * A local disjunct reaches the asset without touching the shared predicate, in
174
+ * either direction.
175
+ */
176
+ export const isSvgAsset = (p: string): boolean => /\.svg$/i.test(p);
177
+
178
+ /**
179
+ * UNCHANGED, and that is the point of the predicate below it.
180
+ *
181
+ * Finding 6 of this change's own completion review: widening THIS predicate to
182
+ * admit every `.svg` would have moved the UI-turn denominator the
183
+ * consultation-rate measurement is defined against — which the
184
+ * `HANDOVER_TRIGGERS` note further down reserves, in writing, as a different
185
+ * decision than any routing change. It was widened and is widened no longer.
186
+ */
132
187
  export const isUiSurface = (p: string): boolean => isUiPath(p) || isUiTreePath(p);
133
188
 
189
+ /**
190
+ * What this PASS looks at — a UI surface, or any `.svg`.
191
+ *
192
+ * The routing step 1.2 of `road-to-svg-rules-in-the-dispatch-path` asked for,
193
+ * in the one place it belongs: the pass's own entry gate. Keeping it out of
194
+ * `isUiSurface` leaves every other reading that predicate feeds — the
195
+ * conformance verdict's noteworthy test, the denominator above — reading
196
+ * exactly what it read before.
197
+ */
198
+ export const isPassSurface = (p: string): boolean => isUiSurface(p) || isSvgAsset(p);
199
+
134
200
  /**
135
201
  * The shadow read of `ui-conformance.json` — Phase 5 of
136
202
  * `road-to-behaviour-evidence-over-pixels`.
@@ -441,6 +507,53 @@ function renderAvailable(root: string): boolean {
441
507
  }
442
508
  }
443
509
 
510
+ /**
511
+ * SVG severity → this pass's severity.
512
+ *
513
+ * `error` and `warning` become P1 and P2; `inert` is dropped. Deliberately NOT
514
+ * P0: the P0 set above is a closed list of objective floors and it is the only
515
+ * thing that computes a block at stop. Routing a new rule family into it would
516
+ * change the block surface, which is a different decision than making the
517
+ * findings visible — and the one Risk 1 of the parent roadmap warns about.
518
+ */
519
+ export function svgSeverity(s: 'error' | 'warning' | 'inert'): Finding['severity'] | null {
520
+ if (s === 'error') return 'P1';
521
+ if (s === 'warning') return 'P2';
522
+ return null;
523
+ }
524
+
525
+ export interface SvgRuleFinding {
526
+ rule: string;
527
+ file: string;
528
+ line: number;
529
+ snippet: string;
530
+ severity: 'error' | 'warning' | 'inert';
531
+ }
532
+
533
+ /**
534
+ * Map the SVG rule set's findings onto this pass's shape.
535
+ *
536
+ * Pure, and exported, so the routing can be proven without building a bundle:
537
+ * the half that is expensive to exercise end-to-end is the chunk load, and that
538
+ * has its own test in `svg_rules_loader`.
539
+ */
540
+ export function svgFindings(raw: readonly SvgRuleFinding[], rel: string): Finding[] {
541
+ const out: Finding[] = [];
542
+ for (const f of raw) {
543
+ const severity = svgSeverity(f.severity);
544
+ if (severity === null) continue;
545
+ out.push({
546
+ file: rel,
547
+ severity,
548
+ catalogId: 'SVG',
549
+ rule: f.rule,
550
+ line: f.line,
551
+ message: `${f.rule}${f.snippet ? ` — ${f.snippet}` : ''}`,
552
+ });
553
+ }
554
+ return out;
555
+ }
556
+
444
557
  /** Extracts the written path from a tool payload, whichever key the host uses. */
445
558
  export function targetPath(payload: unknown): string | null {
446
559
  if (typeof payload !== 'object' || payload === null) return null;
@@ -499,7 +612,7 @@ function main(): number {
499
612
  targets = state.touched;
500
613
  } else {
501
614
  const t = targetPath(payload);
502
- if (!t || !isUiSurface(t)) return EXIT_ALLOW;
615
+ if (!t || !isPassSurface(t)) return EXIT_ALLOW;
503
616
  targets = [t];
504
617
  if (!state.touched.includes(t)) state.touched.push(t);
505
618
  }
@@ -508,6 +621,7 @@ function main(): number {
508
621
  // Findings come from the shipped registry, via the same entrypoint CI uses.
509
622
  const findings: Finding[] = [];
510
623
  let scanFailed: string | null = null;
624
+ let svgRulesUnavailable = false;
511
625
  try {
512
626
  const ctx = loadDesignContext(root);
513
627
  for (const rel of targets) {
@@ -517,6 +631,17 @@ function main(): number {
517
631
  // itself rather than handing over a path, which is also what lets a
518
632
  // post-write pass see the bytes that were just written.
519
633
  const content = fs.readFileSync(abs, 'utf-8');
634
+ if (isSvgAsset(rel)) {
635
+ // The conditional chunk. Loaded only here, so a hook event that
636
+ // never touched an `.svg` pays none of its bytes and runs none
637
+ // of its code — the containment `design_pass_svg.test.ts` pins.
638
+ // A null load is reported through `svgRulesUnavailable`, never
639
+ // thrown: a consumer whose install is missing the chunk must
640
+ // lose the SVG findings, not the whole dispatch.
641
+ const scan = loadSvgScan();
642
+ if (scan === null) svgRulesUnavailable = true;
643
+ else findings.push(...svgFindings(scan(content, rel) as SvgRuleFinding[], rel));
644
+ }
520
645
  for (const f of scanFile(content, rel, ctx)) {
521
646
  findings.push({
522
647
  file: rel,
@@ -532,11 +657,40 @@ function main(): number {
532
657
  scanFailed = String(err);
533
658
  }
534
659
 
535
- const auditMissing = targets.filter((t) => !auditIsFresh(root, t));
536
- recordAuditDischarge(root, targets, auditMissing);
660
+ // The audit question is asked of UI SURFACES, never of the pass surface.
661
+ // Findings 1 and 2 of the third completion review, both reproduced by
662
+ // running the hook: round one split the entry gate from the UI predicate,
663
+ // and `targets` carried the `.svg` past the split anyway. So a clean
664
+ // conformant icon write exited WARN with "no ui-audit.json newer than
665
+ // public/icon.svg" — an asset has no component inventory to be audited
666
+ // against — and, worse, `recordAuditDischarge` returns early on a non-empty
667
+ // `auditMissing`, so writing ONE `.svg` in a turn silently suppressed the
668
+ // ui-audit discharge row for every real UI file in that same turn.
669
+ const auditTargets = targets.filter((t) => isUiSurface(t));
670
+ const auditMissing = auditTargets.filter((t) => !auditIsFresh(root, t));
671
+ recordAuditDischarge(root, auditTargets, auditMissing);
537
672
  let result = decide(slot, findings, auditMissing, new Set(state.surfaced), renderAvailable(root));
538
673
  if (scanFailed) {
539
674
  result = { ...result, verification: 'unverified', degradation_reason: `detector unavailable: ${scanFailed}` };
675
+ } else if (svgRulesUnavailable) {
676
+ // Degraded, not unverified: the rest of the pass ran. An absent chunk
677
+ // is reported rather than silently producing a clean SVG verdict —
678
+ // "absent is not clean" is the same rule the conformance verdict keeps.
679
+ // APPENDED, never overwritten. Finding 6 of the fourth completion
680
+ // review: `decide` may already have set a reason — the renderer being
681
+ // absent is the one it sets — and assigning here threw it away, so a
682
+ // turn where both degradations held reported only the newer one. Two
683
+ // degradations are two facts; the operator needs both or they fix one
684
+ // and are surprised by the other.
685
+ const svgReason = `the SVG rule set could not be loaded (expected dist/hooks/${CHUNK_BASENAME} beside the dispatch bundle) — the .svg files in this turn were not checked`;
686
+ result = {
687
+ ...result,
688
+ verification: 'degraded',
689
+ degradation_reason:
690
+ result.degradation_reason === undefined || result.degradation_reason === ''
691
+ ? svgReason
692
+ : `${result.degradation_reason}; ${svgReason}`,
693
+ };
540
694
  }
541
695
 
542
696
  for (const f of result.findings) state.surfaced.push(`${f.file}::${f.rule}::${f.line}`);
@@ -53,6 +53,7 @@ import { atomic_write_json } from "./state_io.js";
53
53
  import { readHookStdin } from "./hook_stdin.js";
54
54
  import { isCodePath, isTestPath } from "../_lib/test_delta.js";
55
55
  import { EXIT_ALLOW, EXIT_BLOCK } from './exit_codes.js';
56
+ import { ledgerFileFor } from "../git_authorization_hook.js";
56
57
 
57
58
  // MUST equal dispatch_hook.EXIT_BLOCK. The dispatcher's internal ladder is
58
59
  // 0 allow / 1 block / 2 warn — NOT the 2-means-block shape a PreToolUse guard
@@ -198,11 +199,18 @@ interface DispatchState extends JsonObject {
198
199
  evaluations: JsonValue[];
199
200
  }
200
201
 
201
- /** The current user turn's stamp, or "" when no ledger exists yet. */
202
- function _ledgerStamp(consumer_root: string): string {
202
+ /**
203
+ * The current user turn's stamp, or "" when no ledger exists yet.
204
+ *
205
+ * Reads the ledger through the writer's own `ledgerFileFor(session_id)`
206
+ * rather than the legacy session-less path: with a session id the writer
207
+ * never touches that path, so reading it unconditionally found an empty or
208
+ * stale file and the stamp never changed per turn.
209
+ */
210
+ function _ledgerStamp(consumer_root: string, session_id: string): string {
203
211
  try {
204
212
  const raw = fs.readFileSync(
205
- path.join(consumer_root, "agents", "state", "git-authorization.json"),
213
+ path.join(consumer_root, ledgerFileFor(session_id)),
206
214
  "utf8",
207
215
  );
208
216
  const d = JSON.parse(raw) as Record<string, unknown>;
@@ -421,7 +429,7 @@ export function run(stdin_text: string, options: { consumer_root: string }): num
421
429
  // in a session blocked every later one. The only per-user-turn stamp that
422
430
  // actually exists is `detected_at` in the authorization ledger, which
423
431
  // `git_authorization_hook` rewrites on every `user_prompt_submit`.
424
- const turnMarker = `${session_id}:${_ledgerStamp(options.consumer_root)}`;
432
+ const turnMarker = `${session_id}:${_ledgerStamp(options.consumer_root, session_id)}`;
425
433
  if (state.session_id !== turnMarker) {
426
434
  state.session_id = turnMarker;
427
435
  state.turn_started_at = new Date().toISOString();
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Foreign MCP tool names, observed on `post_tool_use`
4
+ * (road-to-neighbours-that-pull-their-weight step 3.3, decision D12).
5
+ *
6
+ * DEFAULT-ON LOCAL OBSERVATION, AND IT BYPASSES THE TELEMETRY OPT-IN. Stated
7
+ * here first because it is the one property a reader must not have to infer.
8
+ * This concern writes on every install, whether or not the org telemetry switch
9
+ * was ever enabled, because the file it writes feeds `doctor neighbours` — a
10
+ * report the consumer runs on their own tree — and gating it on an org switch
11
+ * would publish `0` on every install that never enabled one. Nothing here is
12
+ * telemetry: no transport reads the file, it is never spooled, and it lives
13
+ * under gitignored `agents/runtime/` (decision D9).
14
+ *
15
+ * WHAT IS RECORDED: the tool NAME, `mcp__<server>__<tool>`, and the UTC day it
16
+ * was last seen. Nothing else — no arguments, no response, no session id, no
17
+ * host. The dispatcher serves this concern a REDUCED payload: its manifest
18
+ * entry declares no `needs_payload_bodies`, so `tool_input` and `tool_response`
19
+ * arrive as size-only stubs and cannot be recorded even by mistake.
20
+ *
21
+ * RETENTION. A map keyed by tool name, rewritten in place at most once per day
22
+ * per name — bounded by the neighbour's tool surface, so it needs no TTL job.
23
+ * The reader applies a 30-day window; older entries stop counting. Deleting
24
+ * `agents/runtime/neighbour-tool-use.json` erases all of it.
25
+ *
26
+ * Why a concern of its own rather than a branch of `telemetry-usage`: that
27
+ * concern's dispatcher filter is `tools: [Skill]` and matches exactly, so a
28
+ * branch there was never called for an MCP tool; and a concern named for
29
+ * opt-in telemetry should not also carry default-on local collection. Both
30
+ * were the AI council's findings (2026-10-02); the owner chose this shape.
31
+ *
32
+ * No `tools:` filter on the manifest entry, deliberately: MCP tool names are
33
+ * not enumerable and matching is exact. The prefix test below is the filter.
34
+ *
35
+ * Exit code is ALWAYS 0 and nothing is printed — it observes, never gates.
36
+ */
37
+ import * as path from 'node:path';
38
+ import { fileURLToPath, pathToFileURL } from 'node:url';
39
+ import * as fs from 'node:fs';
40
+
41
+ import { FOREIGN_TOOL_USE_REL, MCP_TOOL_PREFIX, resolveStoreRoot, usageDay } from '../_lib/neighbour_tool_use.js';
42
+ import { update_json_under_lock } from './state_io.js';
43
+ import { readHookStdin } from './hook_stdin.js';
44
+ import { EXIT_ALLOW } from './exit_codes.js';
45
+
46
+ type JsonObject = { [k: string]: unknown };
47
+
48
+ function isObject(v: unknown): v is JsonObject {
49
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
50
+ }
51
+
52
+ /** The MCP tool name off the envelope, or null for anything else. */
53
+ export function mcpToolName(envelope: unknown): string | null {
54
+ if (!isObject(envelope)) return null;
55
+ const payload = isObject(envelope['payload']) ? envelope['payload'] : envelope;
56
+ const v = payload['tool_name'] ?? payload['toolName'] ?? payload['tool'];
57
+ return typeof v === 'string' && v.startsWith(MCP_TOOL_PREFIX) && v.length > MCP_TOOL_PREFIX.length ? v : null;
58
+ }
59
+
60
+ function record(envelope: unknown, consumer_root: string): number {
61
+ try {
62
+ const tool = mcpToolName(envelope);
63
+ if (tool === null) return EXIT_ALLOW;
64
+ const day = usageDay(new Date());
65
+ update_json_under_lock<Record<string, string>>(
66
+ path.join(resolveStoreRoot(consumer_root), FOREIGN_TOOL_USE_REL),
67
+ (seen) => (seen[tool] === day ? null : ({ ...seen, [tool]: day } as Record<string, string>)),
68
+ { blocking: false },
69
+ );
70
+ } catch {
71
+ return EXIT_ALLOW;
72
+ }
73
+ return EXIT_ALLOW;
74
+ }
75
+
76
+ export function run(stdin_text: string, options: { consumer_root: string }): number {
77
+ let envelope: unknown;
78
+ try {
79
+ const raw = stdin_text.trim();
80
+ if (!raw) return EXIT_ALLOW;
81
+ envelope = JSON.parse(raw);
82
+ } catch {
83
+ return EXIT_ALLOW;
84
+ }
85
+ return record(envelope, options.consumer_root);
86
+ }
87
+
88
+ function _root(envelope: unknown): string {
89
+ if (isObject(envelope)) {
90
+ const v = envelope['cwd'] ?? envelope['workspace_root'] ?? envelope['project_root'];
91
+ if (typeof v === 'string' && v) return v;
92
+ }
93
+ return process.cwd();
94
+ }
95
+
96
+ export function main(): number {
97
+ const raw = readHookStdin();
98
+ let envelope: unknown = {};
99
+ try {
100
+ envelope = raw.trim() ? JSON.parse(raw) : {};
101
+ } catch {
102
+ return EXIT_ALLOW;
103
+ }
104
+ return record(envelope, _root(envelope));
105
+ }
106
+
107
+ // Bundle-safety: guarded at the call site so esbuild drops the whole entry
108
+ // check inside the shared hook bundle (see telemetry_usage_hook.ts, D11).
109
+ declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
110
+ function _isCliEntry(): boolean {
111
+ if (process.argv[1] === undefined) return false;
112
+ if (import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) return true;
113
+ try {
114
+ return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(process.argv[1]));
115
+ } catch {
116
+ return false;
117
+ }
118
+ }
119
+ if (typeof __AGENT_CONFIG_BUNDLE__ === 'undefined' || !__AGENT_CONFIG_BUNDLE__) {
120
+ if (_isCliEntry()) process.exit(main());
121
+ }
@@ -195,9 +195,10 @@ export function buildReminder(branch: string, pr: number | null): { reason: stri
195
195
  ` 2. Red? Read only the failing part:\n` +
196
196
  ` gh run view --job <id> --log-failed | grep -E '×|FAIL|Error'\n` +
197
197
  ` then fix it and push again (fix-what-you-see: the author is irrelevant).\n` +
198
- ` 3. Still red after 3 attempts on the same target, or blocked on something ` +
199
- `only the user can decide — stop and say so, with the three attempts named ` +
200
- `(autonomous-execution N=3).\n\n` +
198
+ ` 3. 3 attempts on the same target triggers autonomous-execution's ` +
199
+ `execution.fix_loop_max strategy shift — change approach, never stop. ` +
200
+ `Blocked on something only the user can decide is the one branch that halts: ` +
201
+ `say so, with what was tried named.\n\n` +
201
202
  `Handing the user a red PR with its cause named and unfixed is not a delivery ` +
202
203
  `either. If you are deliberately leaving this WIP, say that in the reply — ` +
203
204
  `silence is what this hook exists to stop.`;
@@ -147,7 +147,11 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
147
147
 
148
148
  import { hookSectionEnabled } from '../_lib/hook_settings.js';
149
149
  import * as user_global_paths from '../_lib/user_global_paths.js';
150
- import { deliversBodies, resolveLeanProjection } from '../_lib/lean_projection_mode.js';
150
+ import {
151
+ deliversBodies,
152
+ installerThinsHost,
153
+ resolveLeanProjection,
154
+ } from '../_lib/lean_projection_mode.js';
151
155
  import { enforcement_class_from_frontmatter } from '../_lib/obligation_frequency.js';
152
156
  import {
153
157
  appendDelivered,
@@ -168,7 +172,8 @@ import {
168
172
  } from '../_lib/rule_injection.js';
169
173
  import { readConsequenceClass, stubLawIds } from '../_lib/rule_consequence_class.js';
170
174
  import { lawText, ruleBody } from '../_lib/rule_law_section.js';
171
- import { hostRuleLayerIds } from '../_lib/rule_layer_overlap.js';
175
+ import { hostRuleLayerDirs, hostRuleLayerIds } from '../_lib/rule_layer_overlap.js';
176
+ import { is_thin_entry } from '../_lib/thin_rules.js';
172
177
  import { readHookStdin } from './hook_stdin.js';
173
178
  import { EXIT_ALLOW, EXIT_WARN } from './exit_codes.js';
174
179
 
@@ -853,6 +858,9 @@ export function buildInjection(
853
858
  openFiles: string[] | null,
854
859
  command: string | null,
855
860
  seen: Set<string>,
861
+ // `false` only for an offline measurement of WHAT the concern delivers,
862
+ // which must not read the measuring machine's installed rule files.
863
+ servedOnce = true,
856
864
  ): Injection | null {
857
865
  let router;
858
866
  try {
@@ -878,14 +886,47 @@ export function buildInjection(
878
886
  // `hostRuleLayerIds`. No filter then, because scoping to an empty set would
879
887
  // re-create the silence step 1.1 repaired.
880
888
  const scope = hostRuleLayerIds(root);
881
- const matches = matchTierRules(router, prompt, openFiles, command).filter(
889
+ let matches = matchTierRules(router, prompt, openFiles, command).filter(
882
890
  (m) => !seen.has(m.id) && (scope === null || scope.has(m.id)),
883
891
  );
884
892
  if (matches.length === 0) return null;
893
+ // ONE COPY (`road-to-a-default-install-served-once` 1.1). The gate opens on
894
+ // the template's `delivery`, but the installer thins only on an explicit
895
+ // user-global choice, so a default install carries every routed body in
896
+ // its rule files already. A rule whose installed copy is a full body gets
897
+ // nothing; a stub still gets its body. Asked only off the consent path, so
898
+ // an opted-in install's delivery is unchanged byte for byte.
899
+ if (servedOnce && scope !== null && !installerThinsHost(DELIVERY_HOST, { packageRoot: ruleSources(root).pkg })) {
900
+ const full = installedFullIds(root, matches.map((m) => m.id));
901
+ matches = matches.filter((m) => !full.has(m.id));
902
+ if (matches.length === 0) return null;
903
+ }
885
904
  const sel = selectForInjection(root, matches, CAP_BYTES);
886
905
  return compose(root, sel, highConsequenceIds(root));
887
906
  }
888
907
 
908
+ /**
909
+ * Ids among `ids` whose copy in EITHER host rule layer is a full body rather
910
+ * than a stub. Either, because the host loads both: one full copy is enough
911
+ * for the body to be standing in context already. Reads only matched ids,
912
+ * never the whole layer, because this runs on the per-prompt path.
913
+ */
914
+ export function installedFullIds(root: string, ids: readonly string[]): Set<string> {
915
+ const out = new Set<string>();
916
+ for (const dir of hostRuleLayerDirs(root)) {
917
+ for (const id of ids) {
918
+ let text: string;
919
+ try {
920
+ text = fs.readFileSync(path.join(dir, `${id}.md`), 'utf-8');
921
+ } catch {
922
+ continue;
923
+ }
924
+ if (!is_thin_entry(text)) out.add(id);
925
+ }
926
+ }
927
+ return out;
928
+ }
929
+
889
930
  // ── main ─────────────────────────────────────────────────────────────────
890
931
 
891
932
  /**