@event4u/agent-config 16.2.0 → 16.4.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 (651) 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 +337 -234
  5. package/README.md +15 -13
  6. package/agents/templates/.ai-council.yml.example +26 -8
  7. package/dist/agent-src/commands/analyze/inbox.md +11 -8
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  9. package/dist/agent-src/commands/bug/fix.md +17 -2
  10. package/dist/agent-src/commands/bug/investigate.md +1 -1
  11. package/dist/agent-src/commands/commit/in-chunks.md +40 -31
  12. package/dist/agent-src/commands/commit.md +72 -33
  13. package/dist/agent-src/commands/condense.md +2 -8
  14. package/dist/agent-src/commands/council/debate.md +11 -7
  15. package/dist/agent-src/commands/council/default.md +10 -9
  16. package/dist/agent-src/commands/council.md +1 -1
  17. package/dist/agent-src/commands/estimate-ticket.md +1 -1
  18. package/dist/agent-src/commands/feature/explore.md +3 -3
  19. package/dist/agent-src/commands/feature/plan.md +2 -2
  20. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  21. package/dist/agent-src/commands/fix/ci.md +18 -4
  22. package/dist/agent-src/commands/fix/commit-messages.md +48 -23
  23. package/dist/agent-src/commands/fix/pr-comments.md +3 -6
  24. package/dist/agent-src/commands/implement-ticket.md +4 -4
  25. package/dist/agent-src/commands/jira-ticket.md +1 -1
  26. package/dist/agent-src/commands/memory/add.md +4 -16
  27. package/dist/agent-src/commands/memory/load.md +5 -6
  28. package/dist/agent-src/commands/memory/promote.md +3 -16
  29. package/dist/agent-src/commands/mission/upgrade.md +3 -2
  30. package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
  31. package/dist/agent-src/commands/optimize/deep.md +4 -2
  32. package/dist/agent-src/commands/optimize/rtk.md +4 -26
  33. package/dist/agent-src/commands/pr/create/description-only.md +18 -15
  34. package/dist/agent-src/commands/pr/create.md +42 -34
  35. package/dist/agent-src/commands/pr/merge.md +136 -9
  36. package/dist/agent-src/commands/prepare-for-review.md +13 -11
  37. package/dist/agent-src/commands/project/analyze.md +7 -35
  38. package/dist/agent-src/commands/refine-ticket.md +1 -1
  39. package/dist/agent-src/commands/review/changes.md +14 -19
  40. package/dist/agent-src/commands/roadmap/create.md +1 -1
  41. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  42. package/dist/agent-src/commands/roadmap/next.md +10 -1
  43. package/dist/agent-src/commands/roadmap/process-full.md +56 -42
  44. package/dist/agent-src/commands/roadmap/resolve-blockers.md +411 -0
  45. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  46. package/dist/agent-src/commands/roadmap.md +18 -4
  47. package/dist/agent-src/commands/tests/create.md +17 -3
  48. package/dist/agent-src/commands/tests/execute.md +22 -5
  49. package/dist/agent-src/commands/worktree/create.md +7 -3
  50. package/dist/agent-src/commands/worktree/verify.md +2 -1
  51. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  52. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  53. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
  54. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
  55. package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
  56. package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
  57. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +7 -7
  58. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
  59. package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
  60. package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
  61. package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
  62. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
  63. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +99 -54
  64. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  65. package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
  66. package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
  67. package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
  68. package/dist/agent-src/contexts/execution/toolchain-resolver.md +95 -7
  69. package/dist/agent-src/contexts/subagent-configuration.md +1 -1
  70. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  71. package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  72. package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  73. package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  74. package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
  75. package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  76. package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  77. package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
  78. package/dist/agent-src/guidelines/php/git.md +15 -2
  79. package/dist/agent-src/rules/active-remediation.md +1 -2
  80. package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
  81. package/dist/agent-src/rules/architecture.md +0 -1
  82. package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
  83. package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
  84. package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
  85. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  86. package/dist/agent-src/rules/brand-consistency.md +0 -1
  87. package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
  88. package/dist/agent-src/rules/broken-access-control.md +1 -1
  89. package/dist/agent-src/rules/cli-output-handling.md +0 -1
  90. package/dist/agent-src/rules/code-comment-discipline.md +0 -1
  91. package/dist/agent-src/rules/code-provenance.md +0 -1
  92. package/dist/agent-src/rules/command-suggestion-policy.md +1 -1
  93. package/dist/agent-src/rules/commit-conventions.md +1 -2
  94. package/dist/agent-src/rules/communication-through-line.md +1 -2
  95. package/dist/agent-src/rules/content-quoting-floor.md +1 -2
  96. package/dist/agent-src/rules/context-hygiene.md +6 -6
  97. package/dist/agent-src/rules/copilot-routing.md +1 -2
  98. package/dist/agent-src/rules/council-availability.md +4 -4
  99. package/dist/agent-src/rules/cross-source-consistency.md +4 -6
  100. package/dist/agent-src/rules/decision-revisit-gate.md +17 -35
  101. package/dist/agent-src/rules/delegation-policy.md +2 -3
  102. package/dist/agent-src/rules/design-fidelity.md +12 -16
  103. package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
  104. package/dist/agent-src/rules/devcontainer-routing.md +1 -2
  105. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -1
  106. package/dist/agent-src/rules/docker-commands.md +0 -1
  107. package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
  108. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -1
  109. package/dist/agent-src/rules/domain-safety-pii.md +1 -1
  110. package/dist/agent-src/rules/domain-safety-retention.md +1 -1
  111. package/dist/agent-src/rules/downstream-changes.md +0 -1
  112. package/dist/agent-src/rules/engineering-safety-floor.md +1 -1
  113. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  114. package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
  115. package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
  116. package/dist/agent-src/rules/fast-path-marker-visibility.md +2 -2
  117. package/dist/agent-src/rules/finance-safety-floor.md +1 -1
  118. package/dist/agent-src/rules/fix-what-you-see.md +6 -32
  119. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
  120. package/dist/agent-src/rules/git-history-discipline.md +2 -2
  121. package/dist/agent-src/rules/guidelines.md +0 -1
  122. package/dist/agent-src/rules/history-discipline.md +0 -1
  123. package/dist/agent-src/rules/icon-consistency.md +0 -1
  124. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -1
  125. package/dist/agent-src/rules/improve-before-implement.md +0 -1
  126. package/dist/agent-src/rules/invite-challenge.md +0 -1
  127. package/dist/agent-src/rules/laravel-routing.md +1 -2
  128. package/dist/agent-src/rules/laravel-translations.md +0 -1
  129. package/dist/agent-src/rules/legal-safety-floor.md +3 -3
  130. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -1
  131. package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
  132. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -1
  133. package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
  134. package/dist/agent-src/rules/media-governance-routing.md +13 -17
  135. package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
  136. package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
  137. package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
  138. package/dist/agent-src/rules/missing-tool-handling.md +0 -1
  139. package/dist/agent-src/rules/model-recommendation.md +0 -1
  140. package/dist/agent-src/rules/neighbour-precedence.md +64 -0
  141. package/dist/agent-src/rules/no-attribution-footers.md +0 -1
  142. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
  143. package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
  144. package/dist/agent-src/rules/no-roadmap-references.md +0 -1
  145. package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
  146. package/dist/agent-src/rules/onboarding-gate.md +0 -1
  147. package/dist/agent-src/rules/output-discipline.md +0 -1
  148. package/dist/agent-src/rules/package-ci-checks.md +0 -1
  149. package/dist/agent-src/rules/persona-governance.md +4 -5
  150. package/dist/agent-src/rules/php-coding.md +0 -1
  151. package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
  152. package/dist/agent-src/rules/preservation-guard.md +0 -1
  153. package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -4
  154. package/dist/agent-src/rules/question-not-instruction.md +1 -1
  155. package/dist/agent-src/rules/reviewer-awareness.md +0 -1
  156. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
  157. package/dist/agent-src/rules/roadmap-progress-sync.md +4 -7
  158. package/dist/agent-src/rules/role-mode-adherence.md +0 -1
  159. package/dist/agent-src/rules/rule-type-governance.md +0 -1
  160. package/dist/agent-src/rules/runtime-safety.md +1 -1
  161. package/dist/agent-src/rules/scale-discipline.md +0 -1
  162. package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
  163. package/dist/agent-src/rules/security-sensitive-stop.md +3 -3
  164. package/dist/agent-src/rules/self-repair-loop.md +0 -1
  165. package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
  166. package/dist/agent-src/rules/session-canary.md +7 -62
  167. package/dist/agent-src/rules/settings-ask-protocol.md +6 -6
  168. package/dist/agent-src/rules/size-enforcement.md +0 -1
  169. package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
  170. package/dist/agent-src/rules/skill-quality.md +0 -1
  171. package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
  172. package/dist/agent-src/rules/source-confidentiality.md +1 -1
  173. package/dist/agent-src/rules/source-discovery-gate.md +0 -1
  174. package/dist/agent-src/rules/source-of-truth.md +0 -1
  175. package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
  176. package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
  177. package/dist/agent-src/rules/symfony-routing.md +1 -2
  178. package/dist/agent-src/rules/think-before-action.md +0 -1
  179. package/dist/agent-src/rules/token-budget-discipline.md +12 -46
  180. package/dist/agent-src/rules/token-efficiency.md +0 -1
  181. package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
  182. package/dist/agent-src/rules/tool-safety.md +1 -1
  183. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  184. package/dist/agent-src/rules/untrusted-input-defense.md +1 -1
  185. package/dist/agent-src/rules/upstream-proposal.md +0 -1
  186. package/dist/agent-src/rules/user-interaction.md +0 -1
  187. package/dist/agent-src/rules/user-interrupt-priority.md +1 -1
  188. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  189. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  190. package/dist/agent-src/scripts/gate_budget.ts +47 -23
  191. package/dist/agent-src/scripts/gate_execute.ts +12 -4
  192. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  193. package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
  194. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
  195. package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
  196. package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
  197. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  198. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  199. package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
  200. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  201. package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
  202. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
  203. package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
  204. package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
  205. package/dist/agent-src/skills/database/data/manifest.json +2 -2
  206. package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
  207. package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
  208. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  209. package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
  210. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
  211. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  212. package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
  213. package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
  214. package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
  215. package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
  216. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  217. package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
  218. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  219. package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
  220. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  221. package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
  222. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  223. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  224. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  225. package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
  226. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  227. package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
  228. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
  229. package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
  230. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  231. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +4 -2
  232. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  233. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  234. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  235. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
  236. package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
  237. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  238. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
  239. package/dist/agent-src/templates/AGENTS.md +1 -1
  240. package/dist/agent-src/templates/agent-settings.md +46 -129
  241. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
  242. package/dist/agent-src/templates/command.md +2 -2
  243. package/dist/agent-src/templates/roadmaps.md +6 -1
  244. package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  245. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  246. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  247. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  248. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  249. package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  250. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  251. package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  252. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  253. package/dist/agent-src/templates/skill.md +1 -1
  254. package/dist/cli/registry.js +2 -1
  255. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  256. package/dist/cli-delegate/check_update_banner.js +207 -120
  257. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  258. package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-5GPULQBE.js} +2797 -338
  259. package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-6SRJTYJS.js} +42 -176
  260. package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
  261. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  262. package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
  263. package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
  264. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  265. package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-AED2VQGM.js} +1245 -249
  266. package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
  267. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  268. package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
  269. package/dist/cli-delegate/chunk-F3QDG2GT.js +2569 -0
  270. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-G5VVIZRC.js} +3 -3
  271. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-H76PHZBI.js} +149 -61
  272. package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
  273. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  274. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  275. package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
  276. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-KXXWJTDO.js} +6 -11
  277. package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
  278. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-MDPPKQCQ.js} +58 -9
  279. package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-MSUL5NYP.js} +5 -5
  280. package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
  281. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  282. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  283. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-PWX7CKMD.js} +4 -4
  284. package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
  285. package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-T7XSJSUQ.js} +1144 -589
  286. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  287. package/dist/cli-delegate/chunk-WQWXGIWB.js +1779 -0
  288. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-XQTPXKFA.js} +1 -1
  289. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  290. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  291. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  292. package/dist/cli-delegate/cmd_analyze_session.js +223 -43
  293. package/dist/cli-delegate/cmd_brand_status.js +3 -2
  294. package/dist/cli-delegate/cmd_conformance.js +1252 -249
  295. package/dist/cli-delegate/cmd_converge.js +3 -3
  296. package/dist/cli-delegate/cmd_doctor.js +19 -9
  297. package/dist/cli-delegate/cmd_doctor_neighbours.js +30 -0
  298. package/dist/cli-delegate/cmd_explain.js +45 -21
  299. package/dist/cli-delegate/cmd_export.js +13 -10
  300. package/dist/cli-delegate/cmd_fleet.js +3 -3
  301. package/dist/cli-delegate/cmd_git_convention.js +1851 -0
  302. package/dist/cli-delegate/cmd_handoff.js +44 -22
  303. package/dist/cli-delegate/cmd_mcp_available.js +3 -2
  304. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  305. package/dist/cli-delegate/cmd_migrate.js +3 -3
  306. package/dist/cli-delegate/cmd_packs_active.js +5 -4
  307. package/dist/cli-delegate/cmd_preflight.js +6 -6
  308. package/dist/cli-delegate/cmd_prune.js +14 -11
  309. package/dist/cli-delegate/cmd_refresh.js +2 -1
  310. package/dist/cli-delegate/cmd_route_audit.js +10 -8
  311. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  312. package/dist/cli-delegate/cmd_session_recycle.js +10 -7
  313. package/dist/cli-delegate/cmd_settings_check.js +54 -7
  314. package/dist/cli-delegate/cmd_settings_get.js +18 -8
  315. package/dist/cli-delegate/cmd_settings_migrate.js +13 -10
  316. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  317. package/dist/cli-delegate/cmd_sync.js +14 -11
  318. package/dist/cli-delegate/cmd_uninstall.js +15 -12
  319. package/dist/cli-delegate/cmd_update.js +7 -7
  320. package/dist/cli-delegate/cmd_upgrade.js +9 -8
  321. package/dist/cli-delegate/cmd_validate.js +15 -12
  322. package/dist/cli-delegate/cmd_versions.js +5 -5
  323. package/dist/cli-delegate/deferral_carry.js +16 -0
  324. package/dist/cli-delegate/gate_budget.js +1 -1
  325. package/dist/cli-delegate/gate_execute.js +3 -3
  326. package/dist/cli-delegate/roadmap_gates.js +6 -6
  327. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  328. package/dist/discovery/deprecation-report.md +1 -1
  329. package/dist/discovery/discovery-manifest.json +232 -141
  330. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  331. package/dist/discovery/discovery-manifest.summary.md +3 -3
  332. package/dist/discovery/dormancy-report.md +1 -1
  333. package/dist/discovery/orphan-report.md +1 -1
  334. package/dist/discovery/packs.json +26 -9
  335. package/dist/discovery/trust-report.md +3 -3
  336. package/dist/discovery/workspaces.json +7 -4
  337. package/dist/hooks/dispatch.js +5655 -6623
  338. package/dist/hooks/dispatch.sha256 +1 -1
  339. package/dist/install/claudePathsPlan.js +47 -6
  340. package/dist/install/claudeRuleRewrite.js +7 -0
  341. package/dist/install/emit_host_rules_cli.js +39 -36
  342. package/dist/install/globalRuleScope.js +87 -0
  343. package/dist/install/host_rule_ownership.js +160 -0
  344. package/dist/install/install.mjs +2336 -1249
  345. package/dist/install/installThinLayer.js +289 -0
  346. package/dist/install/installedRuleLinks.js +262 -0
  347. package/dist/install/wizard-plan.js +24 -0
  348. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  349. package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
  350. package/dist/mcp/registry-manifest.json +5 -5
  351. package/dist/mcp/server.json +2 -2
  352. package/dist/mcp/server.mjs +36504 -30986
  353. package/dist/router.json +1 -1
  354. package/dist/scripts/_lib/agent_settings.js +26 -120
  355. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  356. package/dist/scripts/_lib/compile_time_toggles.js +12 -21
  357. package/dist/scripts/_lib/git_convention.js +232 -0
  358. package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
  359. package/dist/scripts/_lib/installed_layer.js +704 -0
  360. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  361. package/dist/scripts/_lib/package_root.js +68 -0
  362. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  363. package/dist/scripts/_lib/rule_injection.js +67 -10
  364. package/dist/scripts/_lib/rule_law_section.js +122 -0
  365. package/dist/scripts/_lib/settings_removed_keys.js +168 -0
  366. package/dist/scripts/_lib/thin_rules.js +535 -0
  367. package/dist/scripts/_lib/windsurf_render.js +60 -0
  368. package/dist/scripts/condense.js +14 -26
  369. package/dist/scripts/project_thin_rules.js +14 -294
  370. package/dist/server/app.js +2 -1
  371. package/dist/server/gitKeysGate.js +105 -0
  372. package/dist/server/routes/settings.js +67 -8
  373. package/dist/server/routes/wizard.js +4 -0
  374. package/dist/server/schemas/settings.js +29 -81
  375. package/dist/shared/skillRanking.js +103 -16
  376. package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
  377. package/dist/ui/index.html +1 -1
  378. package/docs/MIGRATION.md +72 -1
  379. package/docs/contracts/settings-classes.md +39 -67
  380. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  381. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  382. package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  383. package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  384. package/docs/guidelines/agent-infra/layered-settings.md +13 -1
  385. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  386. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  387. package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
  388. package/docs/guidelines/php/git.md +15 -2
  389. package/package.json +1 -1
  390. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  391. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  392. package/src/agent-src/scripts/gate_budget.ts +47 -23
  393. package/src/agent-src/scripts/gate_execute.ts +12 -4
  394. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  395. package/src/agent-src/templates/AGENTS.md +1 -1
  396. package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  397. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  398. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  399. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  400. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  401. package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  402. package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  403. package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  404. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  405. package/src/config/agent-settings.template.yml +141 -334
  406. package/src/config/agents-paths.yml +16 -0
  407. package/src/config/assurance-capability-registry.json +7 -3
  408. package/src/config/ci-local-parity.yml +3 -0
  409. package/src/config/continuity-surface.json +10 -3
  410. package/src/config/discovery/packs.yml +17 -1
  411. package/src/config/evaluator-budgets.json +23 -10
  412. package/src/config/gate-coverage.yml +138 -3
  413. package/src/config/gate-violation-baselines.json +64 -16
  414. package/src/config/gitignore-block.txt +19 -0
  415. package/src/config/hook-bundle-budget.json +25 -0
  416. package/src/config/hook-token-budget.json +6 -1
  417. package/src/config/host-instruction-limits.json +33 -0
  418. package/src/config/release-gate-locality.yml +2 -1
  419. package/src/config/routing-coverage-seed.json +67 -4
  420. package/src/config/rule-activation-census.json +7 -3
  421. package/src/config/rule-consequence-class.json +161 -0
  422. package/src/config/rule-law-ceilings.json +105 -0
  423. package/src/config/rule-obligations.json +833 -0
  424. package/src/install/claudePathsPlan.ts +46 -4
  425. package/src/install/claudeRuleRewrite.ts +10 -1
  426. package/src/install/emit_host_rules_cli.ts +43 -34
  427. package/src/install/globalRuleScope.ts +109 -0
  428. package/src/install/host_rule_ownership.ts +170 -0
  429. package/src/install/installThinLayer.ts +378 -0
  430. package/src/install/installedRuleLinks.ts +308 -0
  431. package/src/install/wizard-plan.ts +24 -0
  432. package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
  433. package/src/scripts/_cli/cmd_conformance.ts +13 -2
  434. package/src/scripts/_cli/cmd_doctor.ts +33 -81
  435. package/src/scripts/_cli/cmd_doctor_neighbours.ts +426 -0
  436. package/src/scripts/_cli/cmd_explain.ts +72 -14
  437. package/src/scripts/_cli/cmd_git_convention.ts +637 -0
  438. package/src/scripts/_cli/cmd_settings_check.ts +66 -8
  439. package/src/scripts/_cli/cmd_settings_get.ts +18 -3
  440. package/src/scripts/_cli/doctor_execution.ts +72 -1
  441. package/src/scripts/_dispatch.bash +53 -4
  442. package/src/scripts/_lib/agent_settings.ts +26 -124
  443. package/src/scripts/_lib/blocked_by_marker.ts +1 -1
  444. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  445. package/src/scripts/_lib/compile_time_toggles.ts +12 -21
  446. package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
  447. package/src/scripts/_lib/estate_offsets.ts +241 -0
  448. package/src/scripts/_lib/exemption_shape.ts +143 -0
  449. package/src/scripts/_lib/fnv.ts +23 -0
  450. package/src/scripts/_lib/foreign_scan.ts +106 -0
  451. package/src/scripts/_lib/git_base_ref.ts +92 -0
  452. package/src/scripts/_lib/git_convention.ts +291 -0
  453. package/src/scripts/_lib/git_convention_carrier.ts +389 -0
  454. package/src/scripts/_lib/git_convention_grammar.ts +246 -0
  455. package/src/scripts/_lib/git_convention_measure.ts +344 -0
  456. package/src/scripts/_lib/gitignore_block.ts +314 -0
  457. package/src/scripts/_lib/global_deploy_inventory.ts +106 -0
  458. package/src/scripts/_lib/graph_feeder_record.ts +437 -0
  459. package/src/scripts/_lib/hook_settings.ts +73 -0
  460. package/src/scripts/_lib/host_capability.ts +40 -19
  461. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  462. package/src/scripts/_lib/installed_layer.ts +963 -0
  463. package/src/scripts/_lib/json_merge.ts +82 -0
  464. package/src/scripts/_lib/json_pointers.ts +93 -11
  465. package/src/scripts/_lib/knowledge_global.ts +14 -1
  466. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  467. package/src/scripts/_lib/machine_wake.ts +16 -3
  468. package/src/scripts/_lib/module_reach.ts +456 -0
  469. package/src/scripts/_lib/neighbour_census.ts +760 -0
  470. package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
  471. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  472. package/src/scripts/_lib/neighbour_tool_use.ts +187 -0
  473. package/src/scripts/_lib/prompt_shape.ts +46 -0
  474. package/src/scripts/_lib/ratification_artifact.ts +220 -1
  475. package/src/scripts/_lib/ratification_subject.ts +92 -0
  476. package/src/scripts/_lib/release_findings_ingest.ts +15 -7
  477. package/src/scripts/_lib/release_highlights.ts +5 -0
  478. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  479. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  480. package/src/scripts/_lib/review_coverage.ts +134 -0
  481. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  482. package/src/scripts/_lib/rule_injection.ts +130 -10
  483. package/src/scripts/_lib/rule_law_section.ts +147 -0
  484. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  485. package/src/scripts/_lib/rule_obligations.ts +205 -0
  486. package/src/scripts/_lib/self_repair.ts +1 -1
  487. package/src/scripts/_lib/settings_removed_keys.ts +173 -0
  488. package/src/scripts/_lib/skill_origin.ts +254 -0
  489. package/src/scripts/_lib/structured_ask.ts +64 -21
  490. package/src/scripts/_lib/test_red_state.ts +5 -0
  491. package/src/scripts/_lib/thin_rules.ts +620 -0
  492. package/src/scripts/_lib/touched_file_quality.ts +570 -0
  493. package/src/scripts/_lib/turn_end_refusals.ts +77 -2
  494. package/src/scripts/_lib/windsurf_render.ts +63 -0
  495. package/src/scripts/_lib/worker_budget.ts +1 -1
  496. package/src/scripts/adr/regenerate_index.ts +16 -1
  497. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  498. package/src/scripts/ai_council/clients.ts +26 -57
  499. package/src/scripts/ai_council/config.ts +8 -8
  500. package/src/scripts/ai_council/modification_review.ts +100 -0
  501. package/src/scripts/ai_council/modification_review_questions.ts +90 -0
  502. package/src/scripts/ai_council/orchestrator.ts +5 -5
  503. package/src/scripts/ai_council/spend_gate.ts +18 -7
  504. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  505. package/src/scripts/ai_team/config.ts +11 -16
  506. package/src/scripts/audit_skill_overlap.ts +261 -0
  507. package/src/scripts/audit_user_type_axis.ts +20 -8
  508. package/src/scripts/before_complete_hook.ts +243 -12
  509. package/src/scripts/bench_ab_clone.ts +1 -2
  510. package/src/scripts/bench_concern_spawn_path.ts +129 -0
  511. package/src/scripts/bench_graph_feeder_latency.ts +584 -0
  512. package/src/scripts/bench_touched_file_quality.ts +238 -0
  513. package/src/scripts/check_agent_artifact_location.ts +36 -0
  514. package/src/scripts/check_branch_freshness.ts +147 -24
  515. package/src/scripts/check_enforcement_coverage.ts +130 -0
  516. package/src/scripts/check_enforcement_matrix.ts +377 -20
  517. package/src/scripts/check_estate_count.ts +130 -153
  518. package/src/scripts/check_evaluator_budgets.ts +4 -0
  519. package/src/scripts/check_finding_dispositions.ts +196 -21
  520. package/src/scripts/check_gate_coverage.ts +4 -3
  521. package/src/scripts/check_gate_reachability.ts +48 -1
  522. package/src/scripts/check_generator_sync.ts +48 -0
  523. package/src/scripts/check_hook_bundle_composition.ts +477 -0
  524. package/src/scripts/check_kernel_edit_ratified.ts +132 -14
  525. package/src/scripts/check_memory.ts +7 -1
  526. package/src/scripts/check_pack_size.ts +1 -1
  527. package/src/scripts/check_release_highlights.ts +10 -0
  528. package/src/scripts/check_release_manifest_content.ts +189 -0
  529. package/src/scripts/check_release_pr_shape.ts +38 -15
  530. package/src/scripts/check_standing_rule_delivery.ts +129 -13
  531. package/src/scripts/check_suppression_hygiene.ts +2 -5
  532. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  533. package/src/scripts/ci_settle.ts +42 -5
  534. package/src/scripts/classify_merge_risk.ts +320 -0
  535. package/src/scripts/closure_scan.ts +91 -1
  536. package/src/scripts/code_graph/detect.ts +127 -7
  537. package/src/scripts/code_graph/foreign.ts +194 -0
  538. package/src/scripts/code_graph/query.ts +217 -4
  539. package/src/scripts/code_graph/types.ts +26 -2
  540. package/src/scripts/code_graph/validate.ts +3 -1
  541. package/src/scripts/code_graph/verbs.ts +103 -0
  542. package/src/scripts/command_suggester/settings.ts +8 -108
  543. package/src/scripts/command_suggester/types.ts +4 -5
  544. package/src/scripts/compile_hook_manifest.ts +33 -1
  545. package/src/scripts/condense.ts +14 -27
  546. package/src/scripts/consumer_matrix.ts +102 -0
  547. package/src/scripts/context_hygiene_hook.ts +13 -23
  548. package/src/scripts/cost/preflight.mjs +12 -1
  549. package/src/scripts/council_cli.ts +7 -7
  550. package/src/scripts/dispatch_r2_reviewer.ts +24 -11
  551. package/src/scripts/estate_exemption_shape.ts +202 -0
  552. package/src/scripts/explain_run.ts +8 -13
  553. package/src/scripts/generate_git_convention_grammar.ts +127 -0
  554. package/src/scripts/generate_host_cost_table.ts +21 -4
  555. package/src/scripts/handoff_context_hook.ts +86 -37
  556. package/src/scripts/hook_manifest.json +1 -1
  557. package/src/scripts/hook_manifest.yaml +175 -15
  558. package/src/scripts/hooks/block_config_weakening.ts +115 -7
  559. package/src/scripts/hooks/block_plumbing_writes.ts +37 -5
  560. package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
  561. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  562. package/src/scripts/hooks/concern_registry.ts +2 -0
  563. package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
  564. package/src/scripts/hooks/dispatch_hook.ts +52 -52
  565. package/src/scripts/hooks/evidence_independence.ts +12 -4
  566. package/src/scripts/hooks/exit_codes.ts +2 -1
  567. package/src/scripts/hooks/host_lowering.json +1 -1
  568. package/src/scripts/hooks/host_lowering.ts +19 -1
  569. package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
  570. package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
  571. package/src/scripts/hooks/payload_stub.ts +10 -1
  572. package/src/scripts/hooks/push_settle_hook.ts +4 -3
  573. package/src/scripts/hooks/rule_inject_hook.ts +795 -60
  574. package/src/scripts/hooks/state_io.ts +8 -4
  575. package/src/scripts/hooks/table_fingerprint.ts +21 -0
  576. package/src/scripts/hooks/telemetry_usage_hook.ts +24 -7
  577. package/src/scripts/hooks/turn_end_gate_hook.ts +85 -0
  578. package/src/scripts/hooks_status.ts +112 -0
  579. package/src/scripts/injection_scan_hook.ts +7 -0
  580. package/src/scripts/install-hooks.sh +42 -14
  581. package/src/scripts/install.ts +78 -215
  582. package/src/scripts/installed_layer_report.ts +103 -0
  583. package/src/scripts/language_mirror_hook.ts +15 -6
  584. package/src/scripts/learning_sidecar.ts +119 -8
  585. package/src/scripts/lint_blocker_citations.ts +198 -0
  586. package/src/scripts/lint_evidence_artifacts.ts +9 -0
  587. package/src/scripts/lint_hook_concern_budget.ts +10 -21
  588. package/src/scripts/lint_hook_manifest.ts +53 -0
  589. package/src/scripts/lint_major_migration_sections.ts +156 -26
  590. package/src/scripts/lint_pack_boundaries.ts +465 -29
  591. package/src/scripts/lint_roadmap_blockers.ts +282 -29
  592. package/src/scripts/lint_roadmap_complexity.ts +4 -2
  593. package/src/scripts/lint_rule_law_section.ts +647 -0
  594. package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
  595. package/src/scripts/lint_settings_classes.ts +1 -1
  596. package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
  597. package/src/scripts/lint_workflow_security.ts +173 -6
  598. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  599. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  600. package/src/scripts/measure_routing_signal.ts +54 -16
  601. package/src/scripts/measure_skill_ranker_baseline.ts +221 -8
  602. package/src/scripts/memory_learn_hook.ts +91 -6
  603. package/src/scripts/model_rule_injection.ts +55 -8
  604. package/src/scripts/onboarding_gate_hook.ts +7 -6
  605. package/src/scripts/print_required_checks.ts +15 -3
  606. package/src/scripts/probe_host_compliance.ts +33 -5
  607. package/src/scripts/probe_turnaround.ts +183 -3
  608. package/src/scripts/project_thin_rules.ts +34 -327
  609. package/src/scripts/ratification_header.ts +77 -0
  610. package/src/scripts/refusal_envelope.ts +138 -0
  611. package/src/scripts/release_publication.ts +29 -3
  612. package/src/scripts/report_dispatch_import_closure.ts +76 -0
  613. package/src/scripts/report_evidence_temperature.ts +657 -0
  614. package/src/scripts/report_installed_rule_links.ts +274 -0
  615. package/src/scripts/report_label_agreement.ts +233 -0
  616. package/src/scripts/report_module_reach.ts +212 -0
  617. package/src/scripts/report_obligation_mechanism.ts +228 -0
  618. package/src/scripts/report_parked_blockers.ts +222 -0
  619. package/src/scripts/report_required_checks_drift.ts +196 -0
  620. package/src/scripts/report_single_token_triggers.ts +277 -0
  621. package/src/scripts/report_skill_ranker_confusion.ts +543 -0
  622. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  623. package/src/scripts/report_verification_record_coverage.ts +306 -0
  624. package/src/scripts/routing_doctor.ts +2 -2
  625. package/src/scripts/rule_activation_census.ts +5 -2
  626. package/src/scripts/rule_trigger_eval.ts +9 -9
  627. package/src/scripts/schemas/agent-settings.schema.json +17 -31
  628. package/src/scripts/schemas/command.schema.json +1 -1
  629. package/src/scripts/schemas/review-findings.schema.json +3 -2
  630. package/src/scripts/schemas/rule.schema.json +5 -0
  631. package/src/scripts/schemas/skill.schema.json +1 -1
  632. package/src/scripts/self_review_gate.ts +101 -9
  633. package/src/scripts/skill_tools/score_skill_relevance.ts +314 -40
  634. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  635. package/src/scripts/sync_gitignore.ts +29 -235
  636. package/src/scripts/sync_pr_branch.ts +403 -132
  637. package/src/scripts/trigger_eval_grandfather.json +0 -21
  638. package/src/server/schemas/settings.ts +42 -176
  639. package/src/shared/skillRanking.ts +169 -14
  640. package/dist/cli-delegate/chunk-5R7HOLAF.js +0 -99
  641. package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
  642. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  643. package/src/scripts/_lib/config_chain.ts +0 -477
  644. package/src/scripts/_lib/conformance_report.ts +0 -403
  645. package/src/scripts/_lib/eval_discrimination.ts +0 -95
  646. package/src/scripts/_lib/evaluator_promotion.ts +0 -225
  647. package/src/scripts/_lib/file_slicer.ts +0 -117
  648. package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
  649. package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
  650. package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
  651. package/src/scripts/_lib/role_split.ts +0 -338
@@ -45,7 +45,18 @@ import * as fs from 'node:fs';
45
45
  import * as path from 'node:path';
46
46
  import { fileURLToPath, pathToFileURL } from 'node:url';
47
47
 
48
- import * as yaml from 'js-yaml';
48
+ // `yaml`, not `js-yaml`, and the choice is about the BUNDLE rather than about
49
+ // this file. Every concern is inlined into one `dist/hooks/dispatch.js` that
50
+ // every dispatch loads, and this import was the only `js-yaml` one left on that
51
+ // path while six other modules — `dispatch_hook.ts` and `host_lowering.ts`
52
+ // among them — already pulled `yaml`. Measured on 2026-10-01 from the esbuild
53
+ // metafile: `js-yaml` contributed 95,846 bytes (6.13 %) of a 1,564,211-byte
54
+ // bundle, paid on every hook event, for one call site. The behavioural delta is
55
+ // normalised in `parseSettingsDoc` below — three constraints, each MEASURED
56
+ // against the old reader rather than read off documentation — and pinned both by
57
+ // the characterization tests written before the swap and by a differential test
58
+ // that runs BOTH readers over one corpus and asserts the same refusal set.
59
+ import { parseDocument as parseYamlDocument } from 'yaml';
49
60
 
50
61
  import {
51
62
  buildSettingsClassIndex,
@@ -115,12 +126,13 @@ const _PATH_KEYS: readonly string[] = ['file_path', 'path', 'target_file', 'file
115
126
  export type ConfigKind = 'allowlist' | 'advisory' | 'class-c' | null;
116
127
 
117
128
  /**
118
- * 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
119
131
  * never in reach — the hook only sees a path a tool call named, and the writes
120
132
  * that reach `~/.event4u/` go through `settings:set`, which applies its own
121
133
  * per-key refusal.
122
134
  */
123
- 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'];
124
136
 
125
137
  /** Where the class contract lives, relative to the package root. */
126
138
  export const SETTINGS_CLASSES_RELATIVE = 'docs/contracts/settings-classes.md';
@@ -179,10 +191,64 @@ export function leafPaths(value: unknown, prefix = ''): Map<string, string> {
179
191
  * `null` is NOT "no keys changed" — the caller treats it as an unreadable
180
192
  * corpus and refuses, because a guard that allows whatever it cannot parse is
181
193
  * bypassed by making the file unparseable for one call.
194
+ *
195
+ * THE YAML READER IS CONSTRAINED, and all three constraints exist to keep the
196
+ * refusal set the same as `js-yaml`'s rather than to express a preference.
197
+ * Each was MEASURED against the old reader over a shared corpus, which the
198
+ * differential test re-runs; the equivalence is not argued from documentation.
199
+ *
200
+ * · `schema: 'core'` + `resolveKnownTags: false` — WITHOUT THIS, THE SWAP
201
+ * WOULD HAVE WEAKENED THE GUARD. `js-yaml` v5's default CORE schema does
202
+ * not carry the YAML 1.1 tags, so `!!timestamp`, `!!binary`, `!!set`,
203
+ * `!!omap`, `!!pairs` and any unknown `!custom` tag THREW — the document
204
+ * was unparseable and the guard refused. The `yaml` package resolves them
205
+ * by default, so the same document would have parsed and the guard would
206
+ * have gone on to allow an edit it previously refused. Found by an
207
+ * independent council review (openai/codex-default, 2026-10-01) and
208
+ * confirmed by running both readers: six tagged inputs, `js-yaml` throws on
209
+ * all six, default `yaml` returns a value for all six.
210
+ * · A WARNING IS A REFUSAL. `yaml` reports an unresolvable tag as a warning
211
+ * and keeps parsing where `js-yaml` throws, so treating warnings as success
212
+ * would re-open the same hole one tag at a time. `parseDocument` is used
213
+ * rather than `parse` precisely to see them.
214
+ * · NULLISH IS UNPARSEABLE. `js-yaml` threw on an empty document and returned
215
+ * `null` for a value-less one (`~`, `null`); both landed on refusal. `yaml`
216
+ * returns `null` for all three instead of throwing on the first, so without
217
+ * this collapse an EMPTIED settings file would arrive at the caller as a
218
+ * parsed document with no keys — the one shape that turns "I cannot read
219
+ * this" into "nothing changed".
220
+ *
221
+ * · `merge: false` — PINNED, NOT FIXING AN OBSERVED DIFFERENCE. The `yaml`
222
+ * package documents `merge` as defaulting to the document's YAML VERSION:
223
+ * true for 1.1, false for 1.2. A second review round (openai/codex-default)
224
+ * flagged that a `%YAML 1.1` directive could therefore re-enable merge
225
+ * resolution past the schema pin, which would expose keys the old reader
226
+ * left nested under a literal `<<`. Measured, and it does NOT reproduce
227
+ * here — under `schema: 'core'` a `%YAML 1.1` document reads
228
+ * `{"personal":{"<<":{…}}}` identically with and without this option, same
229
+ * as `js-yaml`. It is set anyway because the cost is a word and the finding
230
+ * named a real documented coupling between two options: a future default
231
+ * cannot flip it silently. An option set without an observed failure behind
232
+ * it is said so rather than implied.
233
+ *
234
+ * A merge key (`<<: *anchor`) was raised as a difference by the other seat and
235
+ * is NOT one: both readers leave `<<` literal at these versions, with and
236
+ * without a version directive. The same differential corpus pins both forms, so
237
+ * the question is settled by a reading rather than re-litigated from
238
+ * documentation next time.
182
239
  */
183
240
  export function parseSettingsDoc(text: string, rel: string): unknown | null {
241
+ if (rel.endsWith('.json')) {
242
+ try {
243
+ return (JSON.parse(text) as unknown) ?? null;
244
+ } catch {
245
+ return null;
246
+ }
247
+ }
184
248
  try {
185
- return rel.endsWith('.json') ? (JSON.parse(text) as unknown) : yaml.load(text);
249
+ const doc = parseYamlDocument(text, { schema: 'core', resolveKnownTags: false, merge: false });
250
+ if (doc.errors.length > 0 || doc.warnings.length > 0) return null;
251
+ return (doc.toJS() as unknown) ?? null;
186
252
  } catch {
187
253
  return null;
188
254
  }
@@ -317,10 +383,25 @@ export interface Decision {
317
383
  * unreadable for the length of one tool call, which is a bypass with no
318
384
  * authorisation step in it.
319
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
+ *
320
390
  * WHAT IT CANNOT SEE: an edit applied through a shell redirect rather than an
321
391
  * edit tool — `EDIT_TOOLS` is the corpus, and the shell shapes are
322
392
  * `block_plumbing_writes`' subject, not this one.
323
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
+
324
405
  export function classCVerdict(
325
406
  ti: JsonObject,
326
407
  on_disk: string | null,
@@ -332,11 +413,22 @@ export function classCVerdict(
332
413
  const content = ti['content'];
333
414
  const oldStr = ti['old_string'];
334
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 ?? '';
335
427
  let afterText: string | null = null;
336
428
  if (typeof content === 'string') {
337
429
  afterText = content;
338
- } else if (typeof oldStr === 'string' && typeof newStr === 'string' && on_disk !== null) {
339
- 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
340
432
  // `replace_all` is the host's own flag and it changes WHICH text the
341
433
  // edit produces. Modelling only the first occurrence let a
342
434
  // `replace_all` edit whose SECOND occurrence is the Class C one pass
@@ -352,7 +444,23 @@ export function classCVerdict(
352
444
  '`agent-config settings:set`.'
353
445
  );
354
446
  }
355
- 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;
356
464
  }
357
465
  if (afterText === null) return null;
358
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
  *
@@ -24,8 +28,16 @@
24
28
  * invisible in a source review.
25
29
  * - `hooks/hooks.json` — the host binding file, written by `condense.ts` via
26
30
  * `task sync`. A hand edit here silently unbinds a guard.
31
+ * - `src/scripts/hook_manifest.json` and `src/scripts/hooks/host_lowering.json`
32
+ * — the compiled plumbing tables, written by `compile_hook_manifest`
33
+ * (road-to-blocking-severities 2.2). These are the ones the RUNTIME actually
34
+ * reads: `dispatch_hook._load_yaml` and `host_lowering.resolveTable` serve
35
+ * the compiled body in preference to the YAML whenever its fingerprint
36
+ * matches, so an edited table is what every host is told about which concern
37
+ * runs and which slot can refuse, while the reviewed YAML beside it still
38
+ * says something else.
27
39
  *
28
- * Both are matched under any parent directory, because a consumer install
40
+ * All four are matched under any parent directory, because a consumer install
29
41
  * carries them under its own project root rather than under this repository's.
30
42
  *
31
43
  * WHAT STILL PASSES, and why that is not a hole.
@@ -94,15 +106,35 @@ function _isObject(v: JsonValue | undefined): v is JsonObject {
94
106
  export const PLUMBING_BUILD_OUTPUTS: readonly string[] = [
95
107
  'dist/hooks/dispatch.js',
96
108
  'hooks/hooks.json',
109
+ 'src/scripts/hook_manifest.json',
110
+ 'src/scripts/hooks/host_lowering.json',
97
111
  ];
98
112
 
99
113
  /**
100
114
  * Where each output comes from, printed in the deny so the reader is told what
101
115
  * to run instead of only what not to do.
116
+ *
117
+ * road-to-blocking-severities 2.2 added the two compiled tables. They are the
118
+ * same KIND of file as the first two — written by a build, never by hand — and
119
+ * until this step they were refused by nothing, which mattered more here than
120
+ * it would have for an ordinary generated artifact: `dispatch_hook._load_yaml`
121
+ * and `host_lowering.resolveTable` both serve the compiled body IN PREFERENCE
122
+ * to the YAML whenever its fingerprint matches, so an edited JSON is what every
123
+ * host is told about which concern runs and which slot can refuse.
124
+ *
125
+ * Their YAML sources stay OUT of this set and keep carrying a ratification
126
+ * record instead — a hand edit there is legitimate and is exactly how the
127
+ * plumbing is maintained. The split is the one the guard's header already
128
+ * draws: a deny for files with no legitimate hand edit, a record for files
129
+ * whose hand edits are the point.
102
130
  */
103
131
  const REGENERATED_BY: Readonly<Record<string, string>> = {
104
132
  'dist/hooks/dispatch.js': 'npm run build:hooks',
105
133
  'hooks/hooks.json': 'task sync',
134
+ 'src/scripts/hook_manifest.json':
135
+ './scripts-run src/scripts/compile_hook_manifest --table manifest',
136
+ 'src/scripts/hooks/host_lowering.json':
137
+ './scripts-run src/scripts/compile_hook_manifest --table host-lowering',
106
138
  };
107
139
 
108
140
  // Keys across platforms that carry the tool's target file path.
@@ -31,10 +31,15 @@
31
31
  * "per-host `enforced_by` resolved from the table, not from a host name" means
32
32
  * in practice.
33
33
  *
34
- * Once per session (a latch keyed by session id), so it cannot nag.
34
+ * Once per distinct SEARCH TARGET, capped at five lines per session (step 2.2),
35
+ * so it cannot nag. It was once-per-session until step 2.1 added `Bash`: with
36
+ * shell searches in scope a single line covered the first `grep` of a session
37
+ * and nothing after it, which is the opposite of the problem — the state that
38
+ * matters is the state at the question being asked.
35
39
  * fail_closed: false — every error path returns allow; a context line must
36
40
  * never break a tool call.
37
41
  */
42
+ import { createHash } from 'node:crypto';
38
43
  import fs from 'node:fs';
39
44
  import path from 'node:path';
40
45
  import { fileURLToPath, pathToFileURL } from 'node:url';
@@ -55,13 +60,127 @@ function isObject(v: unknown): v is JsonObject {
55
60
  interface ToolIntent {
56
61
  isSearch: boolean;
57
62
  isCodeRead: boolean;
63
+ /**
64
+ * What this call is ABOUT — a Grep/Glob pattern, a read path, or the search
65
+ * term of a shell search. The per-target latch key (step 2.2): the same
66
+ * question asked twice is one question, and a hook that answered it once
67
+ * has nothing to add the second time.
68
+ *
69
+ * Empty when the call is neither a search nor a code read, and never the
70
+ * raw command line of an arbitrary Bash call — see {@link bashSearchToken}.
71
+ */
72
+ target: string;
73
+ }
74
+
75
+ /**
76
+ * The shell search heads this hook recognises.
77
+ *
78
+ * A closed list on purpose. "Any Bash command might be a search" is the reading
79
+ * that makes the hook chatty — Risk-Register rank 2 — so a command whose head
80
+ * is not here is silent AND does not latch, exactly as a non-code `Read` is.
81
+ */
82
+ const SEARCH_HEADS = new Set(['grep', 'rg', 'ag', 'find', 'ack', 'fd']);
83
+
84
+ /**
85
+ * Short options that consume the NEXT word as their value.
86
+ *
87
+ * Without this the value is read as the search term: a completion review of
88
+ * step 2.1 measured `grep -A 5 foo` keying the latch on `"5"` and
89
+ * `rg -g *.ts alpha` keying on `"*.ts"` — which also silenced a second,
90
+ * genuinely different search behind the same glob. A latch keyed on an option's
91
+ * argument is worse than no latch: it collapses distinct questions onto one slot.
92
+ */
93
+ const VALUE_TAKING = new Set([
94
+ '-A', '-B', '-C', '-m', '-e', '-f', '-g', '-t', '-T', '-d', '--include',
95
+ '--exclude', '--glob', '--max-count', '--after-context', '--before-context',
96
+ '--context', '--regexp', '--file', '--type', '--maxdepth', '--depth',
97
+ ]);
98
+
99
+ /** Asking a tool for its own usage is not a search, and must not spend a slot. */
100
+ const NOT_A_SEARCH = new Set(['--help', '-h', '--version', '-V', '--usage']);
101
+
102
+ /** `find` carries its subject in `-name`, not in the first positional. */
103
+ const FIND_SUBJECT = new Set(['-name', '-iname', '-path', '-ipath', '-regex']);
104
+
105
+ /**
106
+ * The search term inside ONE pipeline segment, or `null`.
107
+ *
108
+ * Split out from {@link bashSearchToken} so a pipeline can be scanned segment
109
+ * by segment: `cat x | grep needle` is a search, and reading only the head of
110
+ * the whole command missed every one of them. The same review measured that.
111
+ */
112
+ function segmentSearchToken(words: readonly string[]): string | null {
113
+ if (words.length === 0) return null;
114
+ let i = 0;
115
+ let head = (words[0] as string).split('/').pop() as string;
116
+ if (head === 'git') {
117
+ // `git -C <path> grep <term>` is the form this tree's own token-efficiency
118
+ // rule mandates, and a `words[1] === 'grep'` test missed all of it. Scan
119
+ // past git's own options to the subcommand.
120
+ let j = 1;
121
+ while (j < words.length && (words[j] as string).startsWith('-')) {
122
+ j += VALUE_TAKING.has(words[j] as string) || words[j] === '-C' ? 2 : 1;
123
+ }
124
+ if (words[j] !== 'grep') return null;
125
+ head = 'git grep';
126
+ i = j + 1;
127
+ } else if (SEARCH_HEADS.has(head)) {
128
+ i = 1;
129
+ } else {
130
+ return null;
131
+ }
132
+ const rest = words.slice(i);
133
+ if (rest.some((w) => NOT_A_SEARCH.has(w))) return null;
134
+ if (head === 'find') {
135
+ const at = rest.findIndex((w) => FIND_SUBJECT.has(w));
136
+ if (at !== -1 && at + 1 < rest.length) return strip(rest[at + 1] as string);
137
+ }
138
+ for (let k = 0; k < rest.length; k += 1) {
139
+ const w = rest[k] as string;
140
+ if (w.startsWith('-')) {
141
+ if (VALUE_TAKING.has(w)) k += 1; // its value is not the subject
142
+ continue;
143
+ }
144
+ return strip(w);
145
+ }
146
+ return head;
147
+ }
148
+
149
+ function strip(w: string): string {
150
+ return w.replace(/^["']|["']$/g, '');
151
+ }
152
+
153
+ /**
154
+ * The thing a shell search is searching FOR, or `null` when the command is not
155
+ * a structure search at all.
156
+ *
157
+ * Reads EVERY pipeline segment, not just the first: `cat x | grep needle` and
158
+ * `… | rg symbol` are searches, and they are the common shape. The first
159
+ * segment that is a search wins, so one line yields one latch key.
160
+ *
161
+ * `git grep` is the one two-word head and is resolved past git's own options.
162
+ * Failing a positional subject the key is the command's head, which collapses a
163
+ * flag-only search onto one slot rather than leaking a command line into a
164
+ * state file — and the key is stored as a digest in any case
165
+ * ({@link latchKey}).
166
+ */
167
+ export function bashSearchToken(command: string): string | null {
168
+ for (const segment of command.split(/\||&&|;/)) {
169
+ const words = segment.trim().split(/\s+/).filter((w) => w !== '');
170
+ const hit = segmentSearchToken(words);
171
+ if (hit !== null) return hit;
172
+ }
173
+ return null;
58
174
  }
59
175
 
60
176
  /**
61
177
  * Best-effort read of the intercepted tool + its target from the envelope.
62
178
  *
63
- * Carried over from the nudge unchanged, and the manifest's `tools:` filter is
64
- * pinned against exactly this branch surface — Grep, Glob, Read.
179
+ * The manifest's `tools:` filter is pinned against exactly this branch surface
180
+ * — Grep, Glob, Read, Bash. `Bash` joined it in step 2.1 for the reason the
181
+ * step gives: the searches an agent actually runs in this tree are shell
182
+ * searches, so a matcher that saw only the structured search tools was watching
183
+ * the door nobody uses.
65
184
  */
66
185
  export function classifyTool(envelope: JsonObject): ToolIntent {
67
186
  const payload = isObject(envelope['payload']) ? envelope['payload'] : envelope;
@@ -71,13 +190,29 @@ export function classifyTool(envelope: JsonObject): ToolIntent {
71
190
  const ti = (isObject(payload['tool_input']) ? payload['tool_input'] : envelope['tool_input']) as
72
191
  | JsonObject
73
192
  | undefined;
74
- const isSearch = name === 'Grep' || name === 'Glob';
75
- let isCodeRead = false;
193
+ const silent: ToolIntent = { isSearch: false, isCodeRead: false, target: '' };
194
+
195
+ if (name === 'Grep' || name === 'Glob') {
196
+ const pat = isObject(ti) ? (ti['pattern'] ?? ti['query']) : undefined;
197
+ return { isSearch: true, isCodeRead: false, target: typeof pat === 'string' ? pat : name };
198
+ }
76
199
  if (name === 'Read' && isObject(ti)) {
77
200
  const fp = ti['file_path'] ?? ti['path'];
78
- if (typeof fp === 'string') isCodeRead = CODE_EXT.test(fp);
201
+ if (typeof fp === 'string' && CODE_EXT.test(fp)) {
202
+ return { isSearch: false, isCodeRead: true, target: fp };
203
+ }
204
+ return silent;
205
+ }
206
+ if (name === 'Bash' && isObject(ti)) {
207
+ const cmd = ti['command'];
208
+ if (typeof cmd !== 'string') return silent;
209
+ const token = bashSearchToken(cmd);
210
+ // Any other Bash command is silent AND does not latch: it consumed no
211
+ // slot, so the next real search still gets its line.
212
+ if (token === null) return silent;
213
+ return { isSearch: true, isCodeRead: false, target: token };
79
214
  }
80
- return { isSearch, isCodeRead };
215
+ return silent;
81
216
  }
82
217
 
83
218
  function sessionId(envelope: JsonObject): string {
@@ -88,33 +223,117 @@ function sessionId(envelope: JsonObject): string {
88
223
  function latchFile(root: string): string {
89
224
  return path.join(root, 'agents', 'runtime', 'state', 'code-graph-context.json');
90
225
  }
91
- function alreadySpoke(root: string, session: string): boolean {
226
+
227
+ /**
228
+ * At most this many context lines in one session (step 2.2).
229
+ *
230
+ * The cap is the chattiness bound, and it is the half that does not depend on
231
+ * the agent's behaviour: per-target alone is unbounded, because a session that
232
+ * greps thirty distinct symbols would hear thirty lines and the consumer would
233
+ * turn the hook off. Five is a stated default, not a measurement — a session
234
+ * that has been told the graph's state five times has been told.
235
+ */
236
+ export const MAX_CONTEXT_LINES_PER_SESSION = 5;
237
+
238
+ /** How many sessions the latch file retains before evicting the oldest. */
239
+ export const MAX_SESSIONS_LATCHED = 200;
240
+
241
+ /**
242
+ * What the latch file records for a target: a truncated digest, never the
243
+ * target itself.
244
+ *
245
+ * A council review of step 2.1 (2026-10-01, anthropic + openai) asked for one
246
+ * concrete thing before the shell tool was routed here — that command text is
247
+ * neither persisted nor carried into diagnostics. Routing on the shell tool
248
+ * makes the target a search TERM an operator typed, and a term can be a
249
+ * customer name, a token fragment or an internal hostname. Nothing downstream
250
+ * needs to read it back: the latch answers "have I spoken about this one", and
251
+ * equality over a digest answers that exactly as well.
252
+ *
253
+ * Not a security boundary and not claimed as one — a short digest of a short
254
+ * term is guessable by anyone who can enumerate terms. It is the same
255
+ * PII-exclusion-by-construction move `domain-safety-pii` § Surface 2 asks for:
256
+ * the file is shaped so the plaintext is not in it to leak.
257
+ */
258
+ export function latchKey(target: string): string {
259
+ return createHash('sha256').update(target).digest('hex').slice(0, 16);
260
+ }
261
+
262
+ /**
263
+ * Should this target get a line, given what this session already heard?
264
+ *
265
+ * Pure, so the two rules it encodes can be tested without a filesystem: a
266
+ * repeat is silent at any count, and a NEW target is silent once the cap is
267
+ * reached. Takes the target in the clear and hashes here, so no caller can
268
+ * forget to.
269
+ */
270
+ export function speaksFor(spoken: readonly string[], target: string): boolean {
271
+ if (spoken.includes(latchKey(target))) return false;
272
+ return spoken.length < MAX_CONTEXT_LINES_PER_SESSION;
273
+ }
274
+
275
+ function readLatch(root: string): Record<string, string[]> {
92
276
  try {
93
- const state = JSON.parse(fs.readFileSync(latchFile(root), 'utf-8')) as Record<string, boolean>;
94
- return state[session] === true;
277
+ const raw = JSON.parse(fs.readFileSync(latchFile(root), 'utf-8')) as Record<string, unknown>;
278
+ const out: Record<string, string[]> = {};
279
+ for (const [k, v] of Object.entries(raw)) {
280
+ // A pre-2.2 file stored `true` per session. Read it as "this session
281
+ // is done", which is what it meant — never as an empty list, which
282
+ // would hand an upgraded session a fresh budget it already spent.
283
+ if (Array.isArray(v)) out[k] = v.filter((x): x is string => typeof x === 'string');
284
+ else if (v === true) out[k] = Array.from({ length: MAX_CONTEXT_LINES_PER_SESSION }, (_, i) => `legacy:${String(i)}`);
285
+ }
286
+ return out;
95
287
  } catch {
96
- return false;
288
+ return {};
97
289
  }
98
290
  }
99
- function latch(root: string, session: string): void {
291
+
292
+ /**
293
+ * The cheap read-only half, so the `git status` probe behind `graphState` runs
294
+ * only for a call that could still produce a line (Risk-Register rank 3).
295
+ *
296
+ * Advisory only — {@link latchTarget} re-reads and remains the authority. A
297
+ * disagreement between the two can only come from a concurrent write, and it
298
+ * resolves toward silence, which is the safe direction for a context line.
299
+ */
300
+ export function wouldSpeak(root: string, session: string, target: string): boolean {
301
+ return speaksFor(readLatch(root)[session] ?? [], target);
302
+ }
303
+
304
+ /**
305
+ * Decide and record in one step, because the two must not drift apart: a reader
306
+ * that says yes and a writer that records something else is how a cap leaks.
307
+ * Returns whether the caller may speak.
308
+ */
309
+ export function latchTarget(root: string, session: string, target: string): boolean {
310
+ const state = readLatch(root);
311
+ const spoken = state[session] ?? [];
312
+ if (!speaksFor(spoken, target)) return false;
100
313
  try {
101
- const p = latchFile(root);
102
- let state: Record<string, boolean> = {};
103
- try {
104
- state = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, boolean>;
105
- } catch {
106
- /* fresh */
314
+ state[session] = [...spoken, latchKey(target)];
315
+ // Nothing prunes a finished session, so without this the file
316
+ // accumulated up to five digests per session id for the life of the
317
+ // repository — a review's finding, and the sibling instrument added in
318
+ // the same change caps itself and says why local-and-gitignored is not
319
+ // the same as free. Insertion order is the eviction order: the oldest
320
+ // session is the one least likely to speak again, and re-latching a
321
+ // dropped session costs one extra line, never a wrong answer.
322
+ const ids = Object.keys(state);
323
+ for (const old of ids.slice(0, Math.max(0, ids.length - MAX_SESSIONS_LATCHED))) {
324
+ if (old !== session) delete state[old];
107
325
  }
108
- state[session] = true;
326
+ const p = latchFile(root);
109
327
  fs.mkdirSync(path.dirname(p), { recursive: true });
110
328
  fs.writeFileSync(p, JSON.stringify(state));
111
329
  } catch {
112
330
  /* fail-open: a persistence failure must not break the tool call */
113
331
  }
332
+ return true;
114
333
  }
115
334
 
116
335
  /**
117
- * The three-state staleness token this hook reports.
336
+ * The four-state staleness token this hook reports.
118
337
  *
119
338
  * Re-exported, not defined here: Phase 3's verbs must print the same token, and
120
339
  * an engine module importing this hook to learn it would invert the dependency
@@ -139,6 +358,13 @@ export function contextLine(state: GraphState): string {
139
358
  'grep is the other path — say which one answered.'
140
359
  );
141
360
  }
361
+ if (state === 'edited') {
362
+ return (
363
+ 'code-graph: level with HEAD, but indexed files are edited in the working tree. ' +
364
+ 'Relationship answers miss anything written since the last build — ' +
365
+ '`agent-config code-graph refresh` picks the edits up, or use grep and say so.'
366
+ );
367
+ }
142
368
  const n = state.slice('behind:'.length);
143
369
  return (
144
370
  `code-graph: ${n} commit(s) behind. Relationship answers may miss anything newer — ` +
@@ -160,11 +386,14 @@ export function main(): number {
160
386
  const pr = envelope['workspace_root'] ?? envelope['project_root'];
161
387
  const root = typeof cwd === 'string' && cwd ? cwd : typeof pr === 'string' && pr ? pr : '.';
162
388
 
163
- const { isSearch, isCodeRead } = classifyTool(envelope);
389
+ const { isSearch, isCodeRead, target } = classifyTool(envelope);
164
390
  if (!isSearch && !isCodeRead) return EXIT_ALLOW;
165
391
 
166
392
  const session = sessionId(envelope);
167
- if (alreadySpoke(root, session)) return EXIT_ALLOW; // once per session
393
+ // Once per distinct target, at most five per session (2.2). Checked BEFORE
394
+ // `graphState`, because that call now runs a `git status` probe and a line
395
+ // this session will not emit must not pay for one.
396
+ if (!wouldSpeak(root, session, target)) return EXIT_ALLOW;
168
397
 
169
398
  let state: GraphState;
170
399
  try {
@@ -176,7 +405,7 @@ export function main(): number {
176
405
  // so a session that builds a graph mid-flight still gets the line once.
177
406
  if (state === 'absent') return EXIT_ALLOW;
178
407
 
179
- latch(root, session);
408
+ if (!latchTarget(root, session, target)) return EXIT_ALLOW;
180
409
  process.stdout.write(`${JSON.stringify({ decision: 'warn', reason: contextLine(state) })}\n`);
181
410
  return EXIT_WARN;
182
411
  }