@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
@@ -0,0 +1,570 @@
1
+ /**
2
+ * touched_file_quality — run the project's OWN quality commands, in shadow,
3
+ * over the files a turn actually edited.
4
+ *
5
+ * loss_class: ephemeral-lossy
6
+ *
7
+ * The one place this module shortens content is `advisoryLine`, whose 200-byte
8
+ * cap (roadmap 2.2) trims a summary it synthesised itself from `QualityRun[]`.
9
+ * The trimmed tail is not stored anywhere and is not meant to be: the runs are
10
+ * in-memory for one invocation, the line goes to stderr, and the verdict it
11
+ * summarises is reproduced by re-running the same resolver command. A recovery
12
+ * locator would promise a retrieval nothing performs.
13
+ *
14
+ * `road-to-touched-files-that-pass-their-own-tools` Phase 1. The project already
15
+ * computes a per-stack quality list — `resolve_toolchain().quality` in
16
+ * `src/agent-src/templates/scripts/work_engine/stack/runner.ts` — and nothing at
17
+ * stop calls it. This module is the caller. It owns no list of its own: D2 of the
18
+ * roadmap pins the resolver as the single authority, because a second list is a
19
+ * second authority that drifts from the first in silence.
20
+ *
21
+ * WHAT THIS IS NOT. It is not verification. A command recorded here never reaches
22
+ * `_lib/verification_command.ts`, never raises `verifications_this_turn`, and
23
+ * never sets `verified_this_turn` — `eslint` exits 0 over a clean file set, and a
24
+ * zero exit that reached the completion gate would mean a turn could claim "I
25
+ * verified" by having edited nothing interesting. The producer writes
26
+ * `quality_runs[]`; the classifier reads `verification_runs[]`. Two arrays, two
27
+ * readers, no overlap — pinned by a negative fixture rather than by this comment.
28
+ *
29
+ * FOUR RULES, AND EACH ONE EXISTS BECAUSE THE NAIVE VERSION IS HARMFUL.
30
+ *
31
+ * 1. A command runs only if the resolver listed it. Nothing is synthesised.
32
+ * 2. A command that WRITES runs only in its check form, or not at all. The
33
+ * resolver emits `vendor/bin/pint`, which rewrites the consumer's source; a
34
+ * shadow pass that reformats the tree it is observing is not a shadow pass.
35
+ * Where a check flag is known (`--test`) it is used; where it is not, the row
36
+ * is `skipped: mutating` and the command is never spawned.
37
+ * 3. A command with no file-scoped form is `skipped: unscoped`. `tsc --noEmit`
38
+ * cannot take a file list without discarding `tsconfig.json`, and running it
39
+ * project-wide at every stop makes stop the slowest hook in the chain. The
40
+ * resolver carries no cost field, so nothing here guesses that an unknown
41
+ * command is cheap: an unrecognised command is `unscoped` and is not run.
42
+ * 4. ENOENT or exit 127 is `skipped: absent`, counted apart from verdicts.
43
+ * Detection upstream is by manifest (`typescript` in `devDependencies`), not
44
+ * by presence on PATH, so "the tool is not installed" must never read as a red.
45
+ *
46
+ * Output per command: the command, its exit code, and the first 20 lines of its
47
+ * combined output. Bounded on purpose — this lands in a per-session state file
48
+ * that another hook reads on every turn.
49
+ */
50
+ import { spawnSync } from 'node:child_process';
51
+ import * as fs from 'node:fs';
52
+ import * as path from 'node:path';
53
+
54
+ /** Lines of combined output retained per command. */
55
+ export const OUTPUT_HEAD_LINES = 20;
56
+
57
+ /** Default per-command wall-clock bound, milliseconds. */
58
+ export const DEFAULT_TIMEOUT_MS = 30_000;
59
+
60
+ /**
61
+ * Why a command was not executed.
62
+ *
63
+ * `no_files` is the fourth reason and is not in the roadmap's prose list, which
64
+ * named three. It is needed and it is honest: running `eslint` over a turn that
65
+ * touched only `.php` files produces a verdict about nothing. 2.1 counts
66
+ * `skipped:*` separately from verdicts, and the glob is what makes a fourth
67
+ * reason additive rather than a contract change.
68
+ */
69
+ export type SkipReason = 'mutating' | 'unscoped' | 'absent' | 'no_files';
70
+
71
+ /** One resolver command, classified into a runnable form or a skip reason. */
72
+ export interface QualityPlan {
73
+ /** The resolver's command string, verbatim — the provenance of this row. */
74
+ readonly source_command: string;
75
+ /** argv actually spawned, or `null` when the row is skipped. */
76
+ readonly argv: readonly string[] | null;
77
+ /** The touched files passed to it. */
78
+ readonly files: readonly string[];
79
+ readonly skipped: SkipReason | null;
80
+ }
81
+
82
+ /** One executed (or deliberately skipped) quality command. */
83
+ export interface QualityRun {
84
+ /** The command as run, or the resolver's command when skipped. */
85
+ readonly command: string;
86
+ readonly source_command: string;
87
+ /** `null` when the row was skipped — never `0`, which is a real verdict. */
88
+ readonly exit_code: number | null;
89
+ readonly output_head: readonly string[];
90
+ readonly skipped: SkipReason | null;
91
+ readonly files: readonly string[];
92
+ /**
93
+ * Matched files the tool reported it declined to look at. A run where EVERY
94
+ * matched file is listed here and the tool exited 0 is a verdict about
95
+ * nothing, so `exit_code` is `null` for it — the readings page § 5(a) found
96
+ * that row recorded `0, skipped: null`, the exact shape of a pass. A field
97
+ * rather than a fifth `SkipReason` (D2): a partly-ignored run keeps its real
98
+ * exit code, and a skip reason cannot sit beside a verdict.
99
+ */
100
+ readonly ignored_files: readonly string[];
101
+ /**
102
+ * Type-check commands the resolver emitted that produced no verdict on this
103
+ * turn — unscoped, absent or mutating. Identical on every record of the turn,
104
+ * so ANY single row says that a `0` beside it was a lint verdict and not a
105
+ * type verdict (§ 5(b): `tsc --noEmit` is unscoped on every stop).
106
+ */
107
+ readonly typecheck_not_run: readonly string[];
108
+ readonly at: string;
109
+ }
110
+
111
+ /**
112
+ * How one resolver command is turned into a file-scoped, non-writing form.
113
+ *
114
+ * Keyed on the exact string the resolver emits. Keying on the whole command
115
+ * rather than on a parsed head is deliberate: the key IS the resolver's output,
116
+ * so a resolver change that this table has not seen falls through to `unscoped`
117
+ * and is not run, instead of being half-recognised and run in a form nobody
118
+ * chose.
119
+ */
120
+ interface ToolSpec {
121
+ /** argv prefix; the touched files are appended. */
122
+ readonly argv: readonly string[];
123
+ /** Extensions this tool reads. Empty means "any file". */
124
+ readonly extensions: readonly string[];
125
+ /**
126
+ * The resolver's own command writes files, and `argv` above is its check
127
+ * form. Recorded so a reader can see that the executed command differs from
128
+ * the resolver's by exactly the flag that makes it read-only.
129
+ */
130
+ readonly check_form: boolean;
131
+ /**
132
+ * A line the tool prints, exit 0, for an input its own config ignores. Per
133
+ * row because the wording is the tool's (D1); a row without one reads no
134
+ * output as an ignore, which keeps the pre-existing behaviour rather than
135
+ * guessing at a message the tool might print.
136
+ */
137
+ readonly ignored_signal?: RegExp;
138
+ }
139
+
140
+ /**
141
+ * ESLint 9's stylish report for an ignored input: the absolute path on its own
142
+ * line, then this indented warning. Pinned verbatim by the ignored-file fixture.
143
+ */
144
+ const ESLINT_IGNORED = /^\s+\d+:\d+\s+warning\s+File ignored because of a matching ignore pattern\b/;
145
+
146
+ const JS_EXTENSIONS = ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx', '.mts', '.cts'] as const;
147
+
148
+ /**
149
+ * The scoped forms, one row per resolver command that HAS one.
150
+ *
151
+ * Everything the resolver can emit that is absent here — `npx tsc --noEmit`,
152
+ * `go vet ./...`, `cargo clippy` — is absent because it has no per-file form,
153
+ * and absence is what produces `skipped: unscoped`.
154
+ */
155
+ const SCOPED_FORMS: ReadonlyMap<string, ToolSpec> = new Map<string, ToolSpec>([
156
+ [
157
+ 'npx eslint .',
158
+ {
159
+ argv: ['npx', 'eslint'],
160
+ extensions: JS_EXTENSIONS,
161
+ check_form: false,
162
+ ignored_signal: ESLINT_IGNORED,
163
+ },
164
+ ],
165
+ [
166
+ 'vendor/bin/phpstan analyse',
167
+ { argv: ['vendor/bin/phpstan', 'analyse'], extensions: ['.php'], check_form: false },
168
+ ],
169
+ // `vendor/bin/pint` REWRITES source. `--test` is its check form: same tool,
170
+ // same rules, reports instead of writing. Risk 3 of the roadmap is that this
171
+ // module reformats the consumer's tree; this row plus the fixture that proves
172
+ // no file changed is the mitigation.
173
+ [
174
+ 'vendor/bin/pint',
175
+ { argv: ['vendor/bin/pint', '--test'], extensions: ['.php'], check_form: true },
176
+ ],
177
+ ['ruff check', { argv: ['ruff', 'check'], extensions: ['.py'], check_form: false }],
178
+ ['mypy .', { argv: ['mypy'], extensions: ['.py'], check_form: false }],
179
+ ]);
180
+
181
+ /**
182
+ * Resolver commands known to write files for which NO check form is known.
183
+ *
184
+ * Empty today — every mutating command the resolver emits (`vendor/bin/pint`)
185
+ * has one, and it is in `SCOPED_FORMS` above. The map exists so that the day a
186
+ * writing command without a `--check` lands, the answer is a row here
187
+ * (`skipped: mutating`, never spawned) rather than a judgement call at the call
188
+ * site. A mutating command that fell through to `unscoped` would be recorded
189
+ * under the wrong reason and counted in the wrong bucket by 2.1.
190
+ */
191
+ const MUTATING_WITHOUT_CHECK_FORM: ReadonlySet<string> = new Set<string>([]);
192
+
193
+ /**
194
+ * Resolver commands with no file-scoped form, unscoped ON PURPOSE (rule 3).
195
+ *
196
+ * Named so the parity test can tell a deliberate absence from `SCOPED_FORMS`
197
+ * apart from a resolver command nobody classified: every command the resolver
198
+ * emits is a scoped key, a mutating key, or on this list — or a test is red.
199
+ */
200
+ export const UNSCOPED_ON_PURPOSE: readonly string[] = [
201
+ 'npx tsc --noEmit',
202
+ 'go vet ./...',
203
+ 'cargo clippy',
204
+ ];
205
+
206
+ /** The keys of `SCOPED_FORMS`, for the parity test. */
207
+ export const SCOPED_FORM_COMMANDS: readonly string[] = [...SCOPED_FORMS.keys()];
208
+
209
+ /** The keys of `MUTATING_WITHOUT_CHECK_FORM`, for the parity test. */
210
+ export const MUTATING_COMMANDS: readonly string[] = [...MUTATING_WITHOUT_CHECK_FORM];
211
+
212
+ /**
213
+ * Resolver commands whose verdict is a type verdict. `go vet` and `cargo clippy`
214
+ * both compile the package, so a type error stops them; ESLint, ruff and pint do
215
+ * not type-check, which is the whole of § 5(b).
216
+ */
217
+ export const TYPE_CHECK_COMMANDS: ReadonlySet<string> = new Set<string>([
218
+ 'npx tsc --noEmit',
219
+ 'vendor/bin/phpstan analyse',
220
+ 'mypy .',
221
+ 'go vet ./...',
222
+ 'cargo clippy',
223
+ ]);
224
+
225
+ function extensionOf(file: string): string {
226
+ const base = path.basename(file);
227
+ const dot = base.lastIndexOf('.');
228
+ return dot <= 0 ? '' : base.slice(dot).toLowerCase();
229
+ }
230
+
231
+ /**
232
+ * Classify the resolver's list against the turn's touched files. PURE.
233
+ *
234
+ * Separated from execution so the four rules above are testable without
235
+ * spawning anything, and so a reader can see the whole policy in one return
236
+ * value before any process starts.
237
+ */
238
+ export function planQualityRuns(
239
+ commands: readonly string[],
240
+ files: readonly string[],
241
+ ): QualityPlan[] {
242
+ return commands.map((source_command): QualityPlan => {
243
+ if (MUTATING_WITHOUT_CHECK_FORM.has(source_command)) {
244
+ return { source_command, argv: null, files: [], skipped: 'mutating' };
245
+ }
246
+ const spec = SCOPED_FORMS.get(source_command);
247
+ if (spec === undefined) {
248
+ return { source_command, argv: null, files: [], skipped: 'unscoped' };
249
+ }
250
+ const matched =
251
+ spec.extensions.length === 0
252
+ ? [...files]
253
+ : files.filter((f) => spec.extensions.includes(extensionOf(f)));
254
+ if (matched.length === 0) {
255
+ return { source_command, argv: null, files: [], skipped: 'no_files' };
256
+ }
257
+ return { source_command, argv: [...spec.argv, ...matched], files: matched, skipped: null };
258
+ });
259
+ }
260
+
261
+ /** `argv` rendered for the record. Not re-parsed — display and audit only. */
262
+ export function renderCommand(argv: readonly string[]): string {
263
+ return argv.join(' ');
264
+ }
265
+
266
+ function headLines(text: string, limit: number): string[] {
267
+ if (text === '') return [];
268
+ return text
269
+ .split(/\r\n|\r|\n/)
270
+ .filter((line, i, all) => !(line === '' && i === all.length - 1))
271
+ .slice(0, limit);
272
+ }
273
+
274
+ /** ISO-8601 to seconds, matching the record's other timestamps. */
275
+ function now(): string {
276
+ return new Date().toISOString().replace(/\.\d{3}Z$/, '+00:00');
277
+ }
278
+
279
+ export interface RunOptions {
280
+ readonly root: string;
281
+ readonly commands: readonly string[];
282
+ readonly files: readonly string[];
283
+ readonly timeout_ms?: number;
284
+ /** Injected in tests so the table's behaviour is observable without a toolchain. */
285
+ readonly spawn?: (
286
+ argv: readonly string[],
287
+ cwd: string,
288
+ timeout_ms: number,
289
+ ) => { status: number | null; output: string; enoent: boolean };
290
+ }
291
+
292
+ function defaultSpawn(
293
+ argv: readonly string[],
294
+ cwd: string,
295
+ timeout_ms: number,
296
+ ): { status: number | null; output: string; enoent: boolean } {
297
+ const [head, ...rest] = argv;
298
+ if (head === undefined) return { status: null, output: '', enoent: true };
299
+ const res = spawnSync(head, rest, {
300
+ cwd,
301
+ timeout: timeout_ms,
302
+ encoding: 'utf-8',
303
+ // No shell. The argv comes from a committed table, and a shell would
304
+ // make a file path with a space into an injection surface.
305
+ shell: false,
306
+ maxBuffer: 4 * 1024 * 1024,
307
+ });
308
+ const enoent =
309
+ res.error !== undefined &&
310
+ (res.error as NodeJS.ErrnoException).code === 'ENOENT';
311
+ const output = `${res.stdout ?? ''}${res.stderr ?? ''}`;
312
+ return { status: res.status, output, enoent };
313
+ }
314
+
315
+ /**
316
+ * Execute the plan and return one record per resolver command.
317
+ *
318
+ * Never throws. A spawn that fails for any reason becomes a row, because a
319
+ * shadow pass that can take the stop hook down with it is worse than no shadow
320
+ * pass at all.
321
+ */
322
+ export function runTouchedFileQuality(opts: RunOptions): QualityRun[] {
323
+ const timeout_ms = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS;
324
+ const spawn = opts.spawn ?? defaultSpawn;
325
+ const out: Omit<QualityRun, 'typecheck_not_run'>[] = [];
326
+ for (const plan of planQualityRuns(opts.commands, opts.files)) {
327
+ if (plan.argv === null) {
328
+ out.push({
329
+ command: plan.source_command,
330
+ source_command: plan.source_command,
331
+ exit_code: null,
332
+ output_head: [],
333
+ skipped: plan.skipped,
334
+ files: [],
335
+ ignored_files: [],
336
+ at: now(),
337
+ });
338
+ continue;
339
+ }
340
+ let status: number | null = null;
341
+ let output = '';
342
+ let enoent = false;
343
+ try {
344
+ const res = spawn(plan.argv, opts.root, timeout_ms);
345
+ status = res.status;
346
+ output = res.output;
347
+ enoent = res.enoent;
348
+ } catch {
349
+ enoent = true;
350
+ }
351
+ // Exit 127 is the shell's "command not found" and reaches us when the
352
+ // tool is a wrapper script whose own dependency is missing. Same meaning
353
+ // as ENOENT for this module's purposes, and the same bucket in 2.1.
354
+ const absent = enoent || status === 127;
355
+ const signal = SCOPED_FORMS.get(plan.source_command)?.ignored_signal;
356
+ const ignored =
357
+ absent || signal === undefined ? [] : ignoredFiles(output, signal, plan.files);
358
+ // Every matched file ignored AND exit 0: the tool looked at nothing, so
359
+ // there is no verdict to record. A non-zero exit over the same set stays,
360
+ // because a red the tool raised (a config crash, say) is real.
361
+ const nothingLooked = ignored.length === plan.files.length && status === 0;
362
+ out.push({
363
+ command: renderCommand(plan.argv),
364
+ source_command: plan.source_command,
365
+ exit_code: absent || nothingLooked ? null : status,
366
+ output_head: absent ? [] : headLines(output, OUTPUT_HEAD_LINES),
367
+ skipped: absent ? 'absent' : null,
368
+ files: plan.files,
369
+ ignored_files: ignored,
370
+ at: now(),
371
+ });
372
+ }
373
+ // `no_files` is excluded: no file the type checker reads was touched, so
374
+ // there was nothing on this turn for it to have caught.
375
+ const typecheck_not_run = out
376
+ .filter(
377
+ (r) =>
378
+ TYPE_CHECK_COMMANDS.has(r.source_command) &&
379
+ r.exit_code === null &&
380
+ r.skipped !== 'no_files' &&
381
+ r.skipped !== null,
382
+ )
383
+ .map((r) => r.source_command);
384
+ return out.map((r) => ({ ...r, typecheck_not_run: [...typecheck_not_run] }));
385
+ }
386
+
387
+ /**
388
+ * The matched files the tool's output reports as ignored.
389
+ *
390
+ * The stylish report names the file on an unindented line and the warning on
391
+ * the indented lines under it, so a signal line is attributed to the last path
392
+ * line above it. A signal that cannot be attributed is attributed to the one
393
+ * matched file only when there is exactly one; otherwise it is dropped, so an
394
+ * unreadable report keeps today's behaviour instead of inventing an ignore.
395
+ */
396
+ export function ignoredFiles(
397
+ output: string,
398
+ signal: RegExp,
399
+ files: readonly string[],
400
+ ): string[] {
401
+ const reported: string[] = [];
402
+ let unattributed = false;
403
+ let current: string | null = null;
404
+ for (const line of output.split(/\r\n|\r|\n/)) {
405
+ if (signal.test(line)) {
406
+ if (current === null) unattributed = true;
407
+ else reported.push(normalizePath(current));
408
+ continue;
409
+ }
410
+ if (line !== '' && !/^\s/.test(line)) current = line.trim();
411
+ }
412
+ const hit = files.filter((f) => {
413
+ const want = normalizePath(f);
414
+ return reported.some((r) => r === want || r.endsWith(`/${want}`));
415
+ });
416
+ if (hit.length === 0 && unattributed && files.length === 1) return [...files];
417
+ return hit;
418
+ }
419
+
420
+ /**
421
+ * The single advisory line `warn` mode emits — command, first file, verdict.
422
+ *
423
+ * `null` when nothing failed. Capped at 200 bytes by the roadmap's 2.2, and the
424
+ * cap is enforced on the UTF-8 byte length rather than on `String.length`, which
425
+ * counts code units and would let a path with non-ASCII characters through at
426
+ * nearly twice the budget.
427
+ */
428
+ export const WARN_LINE_MAX_BYTES = 200;
429
+
430
+ export function advisoryLine(runs: readonly QualityRun[]): string | null {
431
+ const ignored = [...new Set(runs.flatMap((r) => r.ignored_files))];
432
+ const ignoredPart =
433
+ ignored.length === 0
434
+ ? ''
435
+ : `ignored ${ignored[0] ?? ''}${ignored.length > 1 ? ` (+${String(ignored.length - 1)} more)` : ''}`;
436
+ const failed = runs.find((r) => r.skipped === null && r.exit_code !== null && r.exit_code !== 0);
437
+ if (failed === undefined) {
438
+ // Nothing failed. Silence is right when every file was looked at; when a
439
+ // file was ignored, silence would read as clean, so say what was not seen.
440
+ if (ignored.length === 0) return null;
441
+ return truncateToBytes(
442
+ `touched-file quality: no verdict, ${ignoredPart}`,
443
+ WARN_LINE_MAX_BYTES,
444
+ );
445
+ }
446
+ const file = failed.files[0] ?? '(no file)';
447
+ const more = failed.files.length > 1 ? ` (+${String(failed.files.length - 1)} more)` : '';
448
+ const tail = ignoredPart === '' ? '' : `; ${ignoredPart}`;
449
+ const line = `touched-file quality: ${failed.source_command} exited ${String(failed.exit_code)} on ${file}${more}${tail}`;
450
+ return truncateToBytes(line, WARN_LINE_MAX_BYTES);
451
+ }
452
+
453
+ /** Cut `text` so its UTF-8 encoding is at most `max` bytes, never mid-character. */
454
+ export function truncateToBytes(text: string, max: number): string {
455
+ const buf = Buffer.from(text, 'utf-8');
456
+ if (buf.length <= max) return text;
457
+ let end = max;
458
+ // Back off into the start of a character: continuation bytes are 10xxxxxx.
459
+ while (end > 0 && (buf[end] ?? 0) >= 0x80 && (buf[end] ?? 0) < 0xc0) end -= 1;
460
+ return buf.subarray(0, end).toString('utf-8');
461
+ }
462
+
463
+ // ---------------------------------------------------------------------------
464
+ // Which files the turn touched
465
+ // ---------------------------------------------------------------------------
466
+
467
+ /** Where the touched-path set came from, recorded beside the runs. */
468
+ export type FileSource = 'minimal-safe-diff∩worktree' | 'none';
469
+
470
+ export interface TouchedFiles {
471
+ readonly files: readonly string[];
472
+ readonly source: FileSource;
473
+ }
474
+
475
+ /**
476
+ * The turn's touched files, intersected with what the working tree says changed.
477
+ *
478
+ * RISK 4 OF THE ROADMAP, AND THE REASON THIS IS NOT JUST A READ.
479
+ * `agents/state/minimal-safe-diff.json` is ONE FILE PER PROJECT ROOT, not per
480
+ * session — and under this repository's worktree workflow `CLAUDE_PROJECT_DIR`
481
+ * resolves to the parent checkout, so concurrent runs share it. Reading it
482
+ * straight would let a neighbouring session's edits become this session's
483
+ * quality target.
484
+ *
485
+ * Two independent narrowings, and both are needed:
486
+ *
487
+ * - OWNERSHIP. A state file whose `session_id` is not this session's is
488
+ * refused outright, which is the same stance `owns_session_state` takes on
489
+ * the verification record.
490
+ * - THE WORKING TREE. A path is kept only if git still reports it as changed.
491
+ * A path the recorder saw but the tree has since reverted is not a quality
492
+ * target, and an edit from a different checkout is not in THIS tree's status.
493
+ *
494
+ * An absent, foreign or unreadable recorder state yields `none` and NO files,
495
+ * which makes the shadow pass a no-op. The alternative — falling back to the
496
+ * whole working tree — is exactly the cross-session leak this is guarding, so
497
+ * the degradation is toward measuring nothing rather than toward measuring
498
+ * somebody else's work.
499
+ */
500
+ export function touchedFilesAtStop(
501
+ root: string,
502
+ session_id: string,
503
+ gitStatus: (root: string) => readonly string[] = defaultGitStatus,
504
+ ): TouchedFiles {
505
+ let recorded: string[];
506
+ try {
507
+ const raw = fs.readFileSync(
508
+ path.join(root, 'agents', 'state', 'minimal-safe-diff.json'),
509
+ 'utf-8',
510
+ );
511
+ const decoded: unknown = JSON.parse(raw);
512
+ if (typeof decoded !== 'object' || decoded === null || Array.isArray(decoded)) {
513
+ return { files: [], source: 'none' };
514
+ }
515
+ const state = decoded as Record<string, unknown>;
516
+ if (state['session_id'] !== session_id || session_id === '') {
517
+ return { files: [], source: 'none' };
518
+ }
519
+ const touched = state['files_touched_this_turn'];
520
+ if (!Array.isArray(touched)) return { files: [], source: 'none' };
521
+ recorded = touched.filter((x): x is string => typeof x === 'string');
522
+ } catch {
523
+ return { files: [], source: 'none' };
524
+ }
525
+
526
+ const changed = new Set(gitStatus(root));
527
+ const files = recorded.filter((f) => changed.has(normalizePath(f)));
528
+ return { files, source: 'minimal-safe-diff∩worktree' };
529
+ }
530
+
531
+ /** Repo-relative, forward slashes, no leading `./` — the shape git prints. */
532
+ export function normalizePath(p: string): string {
533
+ return p.replace(/\\/g, '/').replace(/^\.\//, '');
534
+ }
535
+
536
+ function defaultGitStatus(root: string): string[] {
537
+ const res = spawnSync('git', ['status', '--porcelain', '-z'], {
538
+ cwd: root,
539
+ encoding: 'utf-8',
540
+ timeout: 10_000,
541
+ shell: false,
542
+ maxBuffer: 4 * 1024 * 1024,
543
+ });
544
+ if (res.status !== 0 || typeof res.stdout !== 'string') return [];
545
+ return parsePorcelainZ(res.stdout);
546
+ }
547
+
548
+ /**
549
+ * Paths out of `git status --porcelain -z`.
550
+ *
551
+ * `-z` rather than the line-oriented form: without it git QUOTES a path holding
552
+ * a space or a non-ASCII byte, and the quoted form never matches a recorded
553
+ * path, so the intersection would silently drop exactly the files most likely
554
+ * to be mis-handled downstream. A rename entry carries two NUL-separated paths
555
+ * (`R <new>\0<old>`); the new path is the one that exists to be checked.
556
+ */
557
+ export function parsePorcelainZ(stdout: string): string[] {
558
+ const out: string[] = [];
559
+ const parts = stdout.split('\0');
560
+ for (let i = 0; i < parts.length; i += 1) {
561
+ const entry = parts[i];
562
+ if (entry === undefined || entry.length < 4) continue;
563
+ const status = entry.slice(0, 2);
564
+ out.push(normalizePath(entry.slice(3)));
565
+ // Rename / copy: the NEXT NUL-separated field is the ORIGIN path, which
566
+ // is consumed here so it is not read as a status entry of its own.
567
+ if (status.startsWith('R') || status.startsWith('C')) i += 1;
568
+ }
569
+ return out;
570
+ }
@@ -366,12 +366,44 @@ export function readInstallBoundary(): InstallBoundary {
366
366
  * would let another concern's retries inflate Q1's numerator against a
367
367
  * denominator that only counts this gate's refusals.
368
368
  */
369
+ /**
370
+ * Who set the `stop_hook_active` flag this row was recorded under.
371
+ *
372
+ * `layer` already says WHICH allow path produced the row, and the doc above
373
+ * says the host sets `stop_hook_active` on ANY stop hook's block — including a
374
+ * neighbour's. What it could not say is which: a `stop_hook_active` row and a
375
+ * `refused_turn` row were the only two things the record distinguished, so a
376
+ * retry caused by a foreign concern and a retry caused by this gate were
377
+ * indistinguishable once the layer was `stop_hook_active`.
378
+ *
379
+ * - `ours` — this session's refusal record exists and parses, so this gate
380
+ * refused at some point before this stop.
381
+ * - `foreign` — the record is ABSENT. This gate never refused in this session,
382
+ * and the host set the flag anyway, so something else did.
383
+ * - `unknown` — the record exists and could not be read. The distinction from
384
+ * `foreign` is the whole point: "we did not refuse" and "we
385
+ * cannot tell whether we refused" are different findings, and
386
+ * collapsing them would let an unreadable state directory read
387
+ * as evidence of a neighbour.
388
+ *
389
+ * On the `refused_turn` layer the answer is `ours` by construction — that
390
+ * marker is this gate's own and no other writer sets it.
391
+ */
392
+ export type ShadowSetBy = 'ours' | 'foreign' | 'unknown';
393
+
369
394
  export interface WouldRefuseAgainRow {
370
395
  detector: RefusalDetectorId;
371
396
  /** The turn ordinal the shadow ran on — joins to `refused_turn`. */
372
397
  turn: number;
373
398
  at: string;
374
399
  layer: ShadowLayer;
400
+ /**
401
+ * Absent on rows written before this field existed, which parse as
402
+ * `unknown` rather than being dropped — an old row is still a real
403
+ * observation of a retry, and discarding it would silently lower Q1's
404
+ * numerator.
405
+ */
406
+ set_by: ShadowSetBy;
375
407
  }
376
408
 
377
409
  /**
@@ -514,7 +546,12 @@ export function parseShadowRecord(raw: string): ShadowRecord | null {
514
546
  if (typeof turn !== 'number' || !Number.isFinite(turn)) continue;
515
547
  if (typeof at !== 'string') continue;
516
548
  if (layer !== 'stop_hook_active' && layer !== 'refused_turn') continue;
517
- rows.push({ detector: det, turn, at, layer });
549
+ const setByRaw = ro['set_by'];
550
+ const set_by: ShadowSetBy =
551
+ setByRaw === 'ours' || setByRaw === 'foreign' || setByRaw === 'unknown'
552
+ ? setByRaw
553
+ : 'unknown';
554
+ rows.push({ detector: det, turn, at, layer, set_by });
518
555
  }
519
556
  const num = (v: unknown): number =>
520
557
  typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : 0;
@@ -548,11 +585,24 @@ export function foldShadow(
548
585
  turnOrdinal: number;
549
586
  at: string;
550
587
  layer: ShadowLayer;
588
+ /**
589
+ * Omitted by a caller that cannot establish it, which is `unknown` —
590
+ * never `foreign`, because a missing answer is not evidence of a
591
+ * neighbour.
592
+ */
593
+ setBy?: ShadowSetBy;
551
594
  },
552
595
  ): ShadowRecord {
553
596
  const rows = [...(prev?.would_refuse_again ?? [])];
597
+ const set_by: ShadowSetBy = input.setBy ?? 'unknown';
554
598
  for (const d of input.detectors) {
555
- rows.push({ detector: d, turn: input.turnOrdinal, at: input.at, layer: input.layer });
599
+ rows.push({
600
+ detector: d,
601
+ turn: input.turnOrdinal,
602
+ at: input.at,
603
+ layer: input.layer,
604
+ set_by,
605
+ });
556
606
  }
557
607
  let dropped = prev?.dropped ?? 0;
558
608
  if (rows.length > SHADOW_MAX_ROWS) {
@@ -571,6 +621,31 @@ export function foldShadow(
571
621
  };
572
622
  }
573
623
 
624
+ /**
625
+ * Who set `stop_hook_active`, decided from this gate's OWN refusal record.
626
+ *
627
+ * Three states, and the third is not a hedge. `ENOENT` means this gate never
628
+ * refused in this session, so a flag the host set anyway came from somewhere
629
+ * else — `foreign`. Any OTHER read failure means the answer is unavailable, and
630
+ * reporting that as `foreign` would turn a broken state directory into evidence
631
+ * that a neighbour acted. The lane's own framing is the reason: what this suite
632
+ * can say about a neighbour is only what it observes, and an unreadable file is
633
+ * not an observation.
634
+ *
635
+ * `parseRecord` returning `null` on a present file is also `unknown`: the file
636
+ * exists, so something wrote it, and we cannot say what.
637
+ */
638
+ export function shadowSetBy(workspaceRoot: string, sessionKey: string): ShadowSetBy {
639
+ let raw: string;
640
+ try {
641
+ raw = fs.readFileSync(sessionRefusalFile(workspaceRoot, sessionKey), 'utf-8');
642
+ } catch (err) {
643
+ const code = (err as NodeJS.ErrnoException | undefined)?.code;
644
+ return code === 'ENOENT' ? 'foreign' : 'unknown';
645
+ }
646
+ return parseRecord(raw) === null ? 'unknown' : 'ours';
647
+ }
648
+
574
649
  /** This session's shadow record, or `null` when it has never retried. */
575
650
  export function readShadowRecord(workspaceRoot: string, sessionKey: string): ShadowRecord | null {
576
651
  try {