@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
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
2
2
  import {
3
- readHookStdin
4
- } from "./chunk-F7DQWYR5.js";
3
+ read_lockfile
4
+ } from "./chunk-LQOCRFWT.js";
5
5
  import {
6
6
  has_stable_session_id,
7
7
  owns_session_state,
@@ -9,213 +9,13 @@ import {
9
9
  prune_stale_session_states,
10
10
  session_state_file,
11
11
  update_json_under_lock
12
- } from "./chunk-E4XIZDIB.js";
12
+ } from "./chunk-7HKRE54K.js";
13
13
  import {
14
- read_lockfile
15
- } from "./chunk-T7L2QK3K.js";
16
-
17
- // src/scripts/_lib/secret_detector.ts
18
- var ENTROPY_MIN_TOKEN_LENGTH = 20;
19
- var BASE64_ENTROPY_THRESHOLD = 4.5;
20
- var HEX_ENTROPY_THRESHOLD = 3;
21
- var RULES = [
22
- { rule: "anthropic-api-key", kind: "anthropic-api-key", regex: /sk-ant-[A-Za-z0-9_-]{20,}/g },
23
- { rule: "anthropic-project-key", kind: "anthropic-api-key", regex: /sk-proj-[A-Za-z0-9_-]{20,}/g },
24
- { rule: "aws-access-key", kind: "aws-access-key", regex: /AKIA[0-9A-Z]{16}/g },
25
- { rule: "google-api-key", kind: "google-api-key", regex: /AIza[0-9A-Za-z_-]{35}/g },
26
- { rule: "github-pat", kind: "github-pat", regex: /ghp_[0-9A-Za-z]{36}/g },
27
- // Fine-grained PATs (github_pat_…) were a measured detector gap: the
28
- // 2026-07-28 adversarial corpus pass flagged them as missed positives.
29
- { rule: "github-fine-grained-pat", kind: "github-pat", regex: /github_pat_[0-9A-Za-z_]{22,}/g },
30
- {
31
- rule: "jwt",
32
- kind: "jwt",
33
- regex: /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
34
- },
35
- // Extensions.
36
- { rule: "stripe-secret-key", kind: "stripe-secret-key", regex: /sk_(?:live|test)_[0-9A-Za-z]{16,}/g },
37
- { rule: "slack-token", kind: "slack-token", regex: /xox[baprs]-[0-9A-Za-z-]{10,}/g },
38
- { rule: "pem-private-key", kind: "pem-private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----/g },
39
- {
40
- rule: "db-connection-uri",
41
- kind: "db-connection-uri",
42
- regex: /\b[a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:[^\s:/@]+@/g
43
- },
44
- {
45
- rule: "generic-assignment",
46
- kind: "generic-assignment",
47
- regex: /(?:password|passwd|pwd|secret|api[_-]?key|access[_-]?token|auth[_-]?token)\s*[:=]\s*['"]?([^'"\s]{8,})['"]?/gi,
48
- valueGroup: 1
49
- }
50
- ];
51
- var SECRET_ALLOW = /secret-allow/;
52
- var PLACEHOLDER_PATTERNS = [
53
- /x{4,}/i,
54
- /example/i,
55
- /changeme/i,
56
- /your[_-].*here/i,
57
- /<[^>]+>/,
58
- /\.\.\./,
59
- /^your[_-]/i,
60
- /placeholder/i
61
- ];
62
- var DATA_URI = /data:[\w.+-]+\/[\w.+-]+;base64,/i;
63
- var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
64
- var KEYWORD_CONTEXT = /password|secret|token|api[_-]?key|credential/i;
65
- var ENTROPY_TOKEN = /[A-Za-z0-9+=_-]{20,}/g;
66
- var IS_HEX = /^[0-9a-fA-F]+$/;
67
- var HASH_HEX_LENGTHS = /* @__PURE__ */ new Set([32, 40, 64]);
68
- function isAllSameChar(value) {
69
- return value.length > 0 && /^(.)\1*$/.test(value);
70
- }
71
- var GENERIC_MIN_LENGTH = 16;
72
- var GENERIC_MIN_ENTROPY = 3.3;
73
- var CODE_EXPR = /[()$`{}<>[\];,]|->|=>|\.\w/;
74
- function looksLikeSecretValue(value) {
75
- if (value.length < GENERIC_MIN_LENGTH) {
76
- return false;
77
- }
78
- if (CODE_EXPR.test(value)) {
79
- return false;
80
- }
81
- const hasDigit = /[0-9]/.test(value);
82
- const mixedCase = /[a-z]/.test(value) && /[A-Z]/.test(value);
83
- if (!hasDigit && !mixedCase) {
84
- return false;
85
- }
86
- return shannonEntropy(value) >= GENERIC_MIN_ENTROPY;
87
- }
88
- function isPlaceholderValue(value, pathHint) {
89
- if (pathHint !== void 0) {
90
- if (pathHint.endsWith(".example") || pathHint.endsWith(".sample") || pathHint.includes(".env.example")) {
91
- return true;
92
- }
93
- }
94
- if (isAllSameChar(value)) {
95
- return true;
96
- }
97
- return PLACEHOLDER_PATTERNS.some((p) => p.test(value));
98
- }
99
- function maskSecret(value) {
100
- const front = value.slice(0, 4);
101
- const back = value.length > 6 ? value.slice(-2) : "";
102
- if (front.length + back.length >= value.length) {
103
- return `${value.slice(0, 1)}\u2026`;
104
- }
105
- return `${front}\u2026${back}`;
106
- }
107
- function shannonEntropy(value) {
108
- const freq = /* @__PURE__ */ new Map();
109
- for (const ch of value) {
110
- freq.set(ch, (freq.get(ch) ?? 0) + 1);
111
- }
112
- const n = value.length;
113
- let entropy = 0;
114
- for (const count of freq.values()) {
115
- const p = count / n;
116
- entropy -= p * Math.log2(p);
117
- }
118
- return entropy;
119
- }
120
- function overlaps(spans, start, end) {
121
- return spans.some(([s, e]) => start < e && s < end);
122
- }
123
- function scanText(text, opts = {}) {
124
- const pathHint = opts.path;
125
- const findings = [];
126
- const lines = text.split(/\r\n|\r|\n/);
127
- for (let i = 0; i < lines.length; i++) {
128
- const line = lines[i];
129
- const lineNo = i + 1;
130
- if (SECRET_ALLOW.test(line)) {
131
- continue;
132
- }
133
- const covered = [];
134
- for (const rule of RULES) {
135
- rule.regex.lastIndex = 0;
136
- let m;
137
- while ((m = rule.regex.exec(line)) !== null) {
138
- const value = rule.valueGroup !== void 0 ? m[rule.valueGroup] : m[0];
139
- if (value === void 0) {
140
- if (m.index === rule.regex.lastIndex) {
141
- rule.regex.lastIndex += 1;
142
- }
143
- continue;
144
- }
145
- if (rule.rule === "generic-assignment" && !looksLikeSecretValue(value)) {
146
- if (m.index === rule.regex.lastIndex) {
147
- rule.regex.lastIndex += 1;
148
- }
149
- continue;
150
- }
151
- const valueStart = m.index + m[0].indexOf(value);
152
- const confidence = isPlaceholderValue(value, pathHint) ? "low" : "high";
153
- findings.push({
154
- rule: rule.rule,
155
- kind: rule.kind,
156
- line: lineNo,
157
- column: valueStart + 1,
158
- masked: maskSecret(value),
159
- confidence
160
- });
161
- covered.push([valueStart, valueStart + value.length]);
162
- if (m.index === rule.regex.lastIndex) {
163
- rule.regex.lastIndex += 1;
164
- }
165
- }
166
- }
167
- if (!DATA_URI.test(line)) {
168
- const hasKeyword = KEYWORD_CONTEXT.test(line);
169
- ENTROPY_TOKEN.lastIndex = 0;
170
- let tm;
171
- while ((tm = ENTROPY_TOKEN.exec(line)) !== null) {
172
- const token = tm[0];
173
- const start = tm.index;
174
- const end = start + token.length;
175
- if (token.length < ENTROPY_MIN_TOKEN_LENGTH) {
176
- continue;
177
- }
178
- if (overlaps(covered, start, end)) {
179
- continue;
180
- }
181
- if (UUID.test(token)) {
182
- continue;
183
- }
184
- if (/^sha(?:256|384|512)-/.test(token)) {
185
- continue;
186
- }
187
- const entropy = shannonEntropy(token);
188
- const isHex = IS_HEX.test(token);
189
- if (isHex && HASH_HEX_LENGTHS.has(token.length)) {
190
- continue;
191
- }
192
- let confidence = null;
193
- if (isHex) {
194
- if (entropy >= HEX_ENTROPY_THRESHOLD && hasKeyword) {
195
- confidence = "high";
196
- }
197
- } else if (entropy >= BASE64_ENTROPY_THRESHOLD) {
198
- confidence = hasKeyword ? "high" : "medium";
199
- }
200
- if (confidence === null) {
201
- continue;
202
- }
203
- if (isPlaceholderValue(token, pathHint)) {
204
- confidence = "low";
205
- }
206
- findings.push({
207
- rule: "entropy",
208
- kind: "high-entropy-string",
209
- line: lineNo,
210
- column: start + 1,
211
- masked: maskSecret(token),
212
- confidence
213
- });
214
- }
215
- }
216
- }
217
- return findings;
218
- }
14
+ scanText
15
+ } from "./chunk-KB5FZ655.js";
16
+ import {
17
+ readHookStdin
18
+ } from "./chunk-F7DQWYR5.js";
219
19
 
220
20
  // src/scripts/_lib/subagent_capsule.ts
221
21
  var EPISTEMIC_STATES = ["verified", "assumed", "gap"];
@@ -561,10 +361,57 @@ function validateAssumption(input, label = "assumption") {
561
361
  }
562
362
 
563
363
  // src/scripts/before_complete_hook.ts
564
- import fs from "node:fs";
565
- import path from "node:path";
364
+ import fs4 from "node:fs";
365
+ import path4 from "node:path";
566
366
  import { fileURLToPath, pathToFileURL } from "node:url";
567
367
 
368
+ // src/scripts/_lib/hook_settings.ts
369
+ import * as fs from "node:fs";
370
+ import * as path from "node:path";
371
+ var SETTINGS_FILE = ".agent-settings.yml";
372
+ function hookSectionValue(root, section, key) {
373
+ const file = path.join(root, SETTINGS_FILE);
374
+ let text;
375
+ try {
376
+ if (!fs.statSync(file).isFile()) return "";
377
+ text = fs.readFileSync(file, "utf-8");
378
+ } catch {
379
+ return "";
380
+ }
381
+ const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
382
+ const sectionPattern = new RegExp(`^(\\s+)${esc(section)}\\s*:\\s*$`);
383
+ const keyPattern = new RegExp(`^\\s+${esc(key)}\\s*:\\s*(.+)$`);
384
+ let inHooks = false;
385
+ let sectionIndent = null;
386
+ const indentOf = (line) => line.length - line.replace(/^\s+/, "").length;
387
+ for (const raw of text.split(/\r\n|\r|\n/)) {
388
+ const line = raw.replace(/\s+$/, "");
389
+ if (!line || line.replace(/^\s+/, "").startsWith("#")) continue;
390
+ if (!(line.startsWith(" ") || line.startsWith(" "))) {
391
+ inHooks = /^hooks\s*:\s*$/.test(line);
392
+ sectionIndent = null;
393
+ continue;
394
+ }
395
+ if (inHooks) {
396
+ const opened = sectionPattern.exec(line);
397
+ if (opened) {
398
+ sectionIndent = opened[1].length;
399
+ continue;
400
+ }
401
+ if (sectionIndent !== null && indentOf(line) <= sectionIndent) {
402
+ sectionIndent = null;
403
+ }
404
+ }
405
+ if (sectionIndent !== null) {
406
+ const m = keyPattern.exec(line);
407
+ if (m) {
408
+ return (m[1] ?? "").replace(/\s+#.*$/, "").trim().replace(/^["']|["']$/g, "");
409
+ }
410
+ }
411
+ }
412
+ return "";
413
+ }
414
+
568
415
  // src/scripts/_lib/verification_command.ts
569
416
  var VERIFY_BINARIES = /* @__PURE__ */ new Set([
570
417
  "vitest",
@@ -733,6 +580,241 @@ function mightBeVerification(command) {
733
580
  return _WIDE_CANDIDATE_RE.test(command);
734
581
  }
735
582
 
583
+ // src/scripts/_lib/touched_file_quality.ts
584
+ import { spawnSync } from "node:child_process";
585
+ import * as fs2 from "node:fs";
586
+ import * as path2 from "node:path";
587
+ var OUTPUT_HEAD_LINES = 20;
588
+ var DEFAULT_TIMEOUT_MS = 3e4;
589
+ var ESLINT_IGNORED = /^\s+\d+:\d+\s+warning\s+File ignored because of a matching ignore pattern\b/;
590
+ var JS_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"];
591
+ var SCOPED_FORMS = /* @__PURE__ */ new Map([
592
+ [
593
+ "npx eslint .",
594
+ {
595
+ argv: ["npx", "eslint"],
596
+ extensions: JS_EXTENSIONS,
597
+ check_form: false,
598
+ ignored_signal: ESLINT_IGNORED
599
+ }
600
+ ],
601
+ [
602
+ "vendor/bin/phpstan analyse",
603
+ { argv: ["vendor/bin/phpstan", "analyse"], extensions: [".php"], check_form: false }
604
+ ],
605
+ // `vendor/bin/pint` REWRITES source. `--test` is its check form: same tool,
606
+ // same rules, reports instead of writing. Risk 3 of the roadmap is that this
607
+ // module reformats the consumer's tree; this row plus the fixture that proves
608
+ // no file changed is the mitigation.
609
+ [
610
+ "vendor/bin/pint",
611
+ { argv: ["vendor/bin/pint", "--test"], extensions: [".php"], check_form: true }
612
+ ],
613
+ ["ruff check", { argv: ["ruff", "check"], extensions: [".py"], check_form: false }],
614
+ ["mypy .", { argv: ["mypy"], extensions: [".py"], check_form: false }]
615
+ ]);
616
+ var MUTATING_WITHOUT_CHECK_FORM = /* @__PURE__ */ new Set([]);
617
+ var SCOPED_FORM_COMMANDS = [...SCOPED_FORMS.keys()];
618
+ var MUTATING_COMMANDS = [...MUTATING_WITHOUT_CHECK_FORM];
619
+ var TYPE_CHECK_COMMANDS = /* @__PURE__ */ new Set([
620
+ "npx tsc --noEmit",
621
+ "vendor/bin/phpstan analyse",
622
+ "mypy .",
623
+ "go vet ./...",
624
+ "cargo clippy"
625
+ ]);
626
+ function extensionOf(file) {
627
+ const base = path2.basename(file);
628
+ const dot = base.lastIndexOf(".");
629
+ return dot <= 0 ? "" : base.slice(dot).toLowerCase();
630
+ }
631
+ function planQualityRuns(commands, files) {
632
+ return commands.map((source_command) => {
633
+ if (MUTATING_WITHOUT_CHECK_FORM.has(source_command)) {
634
+ return { source_command, argv: null, files: [], skipped: "mutating" };
635
+ }
636
+ const spec = SCOPED_FORMS.get(source_command);
637
+ if (spec === void 0) {
638
+ return { source_command, argv: null, files: [], skipped: "unscoped" };
639
+ }
640
+ const matched = spec.extensions.length === 0 ? [...files] : files.filter((f) => spec.extensions.includes(extensionOf(f)));
641
+ if (matched.length === 0) {
642
+ return { source_command, argv: null, files: [], skipped: "no_files" };
643
+ }
644
+ return { source_command, argv: [...spec.argv, ...matched], files: matched, skipped: null };
645
+ });
646
+ }
647
+ function renderCommand(argv) {
648
+ return argv.join(" ");
649
+ }
650
+ function headLines(text, limit) {
651
+ if (text === "") return [];
652
+ return text.split(/\r\n|\r|\n/).filter((line, i, all) => !(line === "" && i === all.length - 1)).slice(0, limit);
653
+ }
654
+ function now() {
655
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "+00:00");
656
+ }
657
+ function defaultSpawn(argv, cwd, timeout_ms) {
658
+ const [head, ...rest] = argv;
659
+ if (head === void 0) return { status: null, output: "", enoent: true };
660
+ const res = spawnSync(head, rest, {
661
+ cwd,
662
+ timeout: timeout_ms,
663
+ encoding: "utf-8",
664
+ // No shell. The argv comes from a committed table, and a shell would
665
+ // make a file path with a space into an injection surface.
666
+ shell: false,
667
+ maxBuffer: 4 * 1024 * 1024
668
+ });
669
+ const enoent = res.error !== void 0 && res.error.code === "ENOENT";
670
+ const output2 = `${res.stdout ?? ""}${res.stderr ?? ""}`;
671
+ return { status: res.status, output: output2, enoent };
672
+ }
673
+ function runTouchedFileQuality(opts) {
674
+ const timeout_ms = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS;
675
+ const spawn = opts.spawn ?? defaultSpawn;
676
+ const out = [];
677
+ for (const plan of planQualityRuns(opts.commands, opts.files)) {
678
+ if (plan.argv === null) {
679
+ out.push({
680
+ command: plan.source_command,
681
+ source_command: plan.source_command,
682
+ exit_code: null,
683
+ output_head: [],
684
+ skipped: plan.skipped,
685
+ files: [],
686
+ ignored_files: [],
687
+ at: now()
688
+ });
689
+ continue;
690
+ }
691
+ let status = null;
692
+ let output2 = "";
693
+ let enoent = false;
694
+ try {
695
+ const res = spawn(plan.argv, opts.root, timeout_ms);
696
+ status = res.status;
697
+ output2 = res.output;
698
+ enoent = res.enoent;
699
+ } catch {
700
+ enoent = true;
701
+ }
702
+ const absent = enoent || status === 127;
703
+ const signal = SCOPED_FORMS.get(plan.source_command)?.ignored_signal;
704
+ const ignored = absent || signal === void 0 ? [] : ignoredFiles(output2, signal, plan.files);
705
+ const nothingLooked = ignored.length === plan.files.length && status === 0;
706
+ out.push({
707
+ command: renderCommand(plan.argv),
708
+ source_command: plan.source_command,
709
+ exit_code: absent || nothingLooked ? null : status,
710
+ output_head: absent ? [] : headLines(output2, OUTPUT_HEAD_LINES),
711
+ skipped: absent ? "absent" : null,
712
+ files: plan.files,
713
+ ignored_files: ignored,
714
+ at: now()
715
+ });
716
+ }
717
+ const typecheck_not_run = out.filter(
718
+ (r) => TYPE_CHECK_COMMANDS.has(r.source_command) && r.exit_code === null && r.skipped !== "no_files" && r.skipped !== null
719
+ ).map((r) => r.source_command);
720
+ return out.map((r) => ({ ...r, typecheck_not_run: [...typecheck_not_run] }));
721
+ }
722
+ function ignoredFiles(output2, signal, files) {
723
+ const reported = [];
724
+ let unattributed = false;
725
+ let current = null;
726
+ for (const line of output2.split(/\r\n|\r|\n/)) {
727
+ if (signal.test(line)) {
728
+ if (current === null) unattributed = true;
729
+ else reported.push(normalizePath(current));
730
+ continue;
731
+ }
732
+ if (line !== "" && !/^\s/.test(line)) current = line.trim();
733
+ }
734
+ const hit = files.filter((f) => {
735
+ const want = normalizePath(f);
736
+ return reported.some((r) => r === want || r.endsWith(`/${want}`));
737
+ });
738
+ if (hit.length === 0 && unattributed && files.length === 1) return [...files];
739
+ return hit;
740
+ }
741
+ var WARN_LINE_MAX_BYTES = 200;
742
+ function advisoryLine(runs) {
743
+ const ignored = [...new Set(runs.flatMap((r) => r.ignored_files))];
744
+ const ignoredPart = ignored.length === 0 ? "" : `ignored ${ignored[0] ?? ""}${ignored.length > 1 ? ` (+${String(ignored.length - 1)} more)` : ""}`;
745
+ const failed = runs.find((r) => r.skipped === null && r.exit_code !== null && r.exit_code !== 0);
746
+ if (failed === void 0) {
747
+ if (ignored.length === 0) return null;
748
+ return truncateToBytes(
749
+ `touched-file quality: no verdict, ${ignoredPart}`,
750
+ WARN_LINE_MAX_BYTES
751
+ );
752
+ }
753
+ const file = failed.files[0] ?? "(no file)";
754
+ const more = failed.files.length > 1 ? ` (+${String(failed.files.length - 1)} more)` : "";
755
+ const tail = ignoredPart === "" ? "" : `; ${ignoredPart}`;
756
+ const line = `touched-file quality: ${failed.source_command} exited ${String(failed.exit_code)} on ${file}${more}${tail}`;
757
+ return truncateToBytes(line, WARN_LINE_MAX_BYTES);
758
+ }
759
+ function truncateToBytes(text, max) {
760
+ const buf = Buffer.from(text, "utf-8");
761
+ if (buf.length <= max) return text;
762
+ let end = max;
763
+ while (end > 0 && (buf[end] ?? 0) >= 128 && (buf[end] ?? 0) < 192) end -= 1;
764
+ return buf.subarray(0, end).toString("utf-8");
765
+ }
766
+ function touchedFilesAtStop(root, session_id, gitStatus = defaultGitStatus) {
767
+ let recorded;
768
+ try {
769
+ const raw = fs2.readFileSync(
770
+ path2.join(root, "agents", "state", "minimal-safe-diff.json"),
771
+ "utf-8"
772
+ );
773
+ const decoded = JSON.parse(raw);
774
+ if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) {
775
+ return { files: [], source: "none" };
776
+ }
777
+ const state = decoded;
778
+ if (state["session_id"] !== session_id || session_id === "") {
779
+ return { files: [], source: "none" };
780
+ }
781
+ const touched = state["files_touched_this_turn"];
782
+ if (!Array.isArray(touched)) return { files: [], source: "none" };
783
+ recorded = touched.filter((x) => typeof x === "string");
784
+ } catch {
785
+ return { files: [], source: "none" };
786
+ }
787
+ const changed = new Set(gitStatus(root));
788
+ const files = recorded.filter((f) => changed.has(normalizePath(f)));
789
+ return { files, source: "minimal-safe-diff\u2229worktree" };
790
+ }
791
+ function normalizePath(p) {
792
+ return p.replace(/\\/g, "/").replace(/^\.\//, "");
793
+ }
794
+ function defaultGitStatus(root) {
795
+ const res = spawnSync("git", ["status", "--porcelain", "-z"], {
796
+ cwd: root,
797
+ encoding: "utf-8",
798
+ timeout: 1e4,
799
+ shell: false,
800
+ maxBuffer: 4 * 1024 * 1024
801
+ });
802
+ if (res.status !== 0 || typeof res.stdout !== "string") return [];
803
+ return parsePorcelainZ(res.stdout);
804
+ }
805
+ function parsePorcelainZ(stdout) {
806
+ const out = [];
807
+ const parts = stdout.split("\0");
808
+ for (let i = 0; i < parts.length; i += 1) {
809
+ const entry = parts[i];
810
+ if (entry === void 0 || entry.length < 4) continue;
811
+ const status = entry.slice(0, 2);
812
+ out.push(normalizePath(entry.slice(3)));
813
+ if (status.startsWith("R") || status.startsWith("C")) i += 1;
814
+ }
815
+ return out;
816
+ }
817
+
736
818
  // src/scripts/_lib/verification_evidence.ts
737
819
  var INSTRUMENT_GAP_REASONS = /* @__PURE__ */ new Set([
738
820
  "exit_code_unavailable",
@@ -1029,9 +1111,814 @@ function describeRecordFinding(failed, reasons) {
1029
1111
  }
1030
1112
  var NO_RED_EVIDENCE_REASON = "a completion claim over production code this turn changed, with a test file touched but `no_red_evidence` \u2014 no recorded run shows that test target failing and then passing after the last edit. A test never seen red has unknown sensitivity: run it against the unfixed code, watch it fail for the intended reason, then fix and re-run";
1031
1113
 
1114
+ // src/agent-src/templates/scripts/work_engine/stack/runner.ts
1115
+ import * as fs3 from "node:fs";
1116
+ import * as path3 from "node:path";
1117
+ var BEHAVIOR_UNKNOWN = "unknown";
1118
+ var SPEED_FAST = "fast";
1119
+ var SPEED_SLOW = "slow";
1120
+ var SPEED_E2E = "e2e";
1121
+ var HIGH = "HIGH";
1122
+ var MEDIUM = "MEDIUM";
1123
+ var LOW = "LOW";
1124
+ var _MANIFESTS = [
1125
+ "composer.json",
1126
+ "package.json",
1127
+ "pyproject.toml",
1128
+ "go.mod",
1129
+ "Cargo.toml",
1130
+ "Makefile",
1131
+ "Taskfile.yml",
1132
+ "Taskfile.yaml",
1133
+ "Gemfile",
1134
+ "pom.xml",
1135
+ "build.gradle",
1136
+ "build.gradle.kts",
1137
+ // `settings.gradle[.kts]` is a Gradle signal `_jvm_build` acts on, so it
1138
+ // must be in the key: a settings-only multi-project root emits a junit row
1139
+ // and would otherwise report the greenfield `mtime: 0` forever.
1140
+ "settings.gradle",
1141
+ "settings.gradle.kts",
1142
+ "global.json",
1143
+ "Directory.Build.props"
1144
+ ];
1145
+ var _DOTNET_PROJECT_EXTS = [".sln", ".slnx", ".csproj", ".fsproj", ".vbproj"];
1146
+ var _DOTNET_TEST_STACK = /Microsoft\.NET\.Test\.Sdk|xunit|nunit|mstest/i;
1147
+ var _DOTNET_SCAN_DEPTH = 2;
1148
+ var _DOTNET_MAX_PROJECTS = 50;
1149
+ var _DOTNET_SOLUTION_EXTS = [".sln", ".slnx"];
1150
+ function _has_dotnet_solution(dir) {
1151
+ try {
1152
+ return fs3.readdirSync(dir, { withFileTypes: true }).some((e) => e.isFile() && _DOTNET_SOLUTION_EXTS.includes(path3.extname(e.name).toLowerCase()));
1153
+ } catch {
1154
+ return false;
1155
+ }
1156
+ }
1157
+ var _BEHAVIOR_MARKERS = [
1158
+ "pnpm-workspace.yaml",
1159
+ "requirements.txt",
1160
+ "gems.rb",
1161
+ ".rspec",
1162
+ "behat.yml",
1163
+ "behat.yaml",
1164
+ "behat.yml.dist",
1165
+ "behat.dist.yml",
1166
+ "cucumber.js",
1167
+ "cucumber.cjs",
1168
+ "cucumber.mjs",
1169
+ "cucumber.json"
1170
+ ];
1171
+ var RunnerResult = class {
1172
+ ecosystem;
1173
+ runner;
1174
+ command;
1175
+ speed;
1176
+ confidence;
1177
+ basis;
1178
+ constructor(ecosystem, runner, command, speed = SPEED_FAST, confidence = HIGH, basis = "") {
1179
+ this.ecosystem = ecosystem;
1180
+ this.runner = runner;
1181
+ this.command = command;
1182
+ this.speed = speed;
1183
+ this.confidence = confidence;
1184
+ this.basis = basis;
1185
+ }
1186
+ };
1187
+ var BehaviorRunnerResult = class {
1188
+ constructor(ecosystem, runner, command, scope_root, confidence = HIGH, basis = "", conflict = []) {
1189
+ this.ecosystem = ecosystem;
1190
+ this.runner = runner;
1191
+ this.command = command;
1192
+ this.scope_root = scope_root;
1193
+ this.confidence = confidence;
1194
+ this.basis = basis;
1195
+ this.conflict = conflict;
1196
+ }
1197
+ ecosystem;
1198
+ runner;
1199
+ command;
1200
+ scope_root;
1201
+ confidence;
1202
+ basis;
1203
+ conflict;
1204
+ };
1205
+ var ToolchainResult = class {
1206
+ ecosystems;
1207
+ runners;
1208
+ selected;
1209
+ quality;
1210
+ confidence;
1211
+ mtime;
1212
+ /**
1213
+ * Per-scope behavior inventory — detection only, deliberately NOT merged
1214
+ * into `runners` nor reachable from `selected`, so nothing is ever run
1215
+ * BECAUSE this axis detected it.
1216
+ *
1217
+ * That is a claim about reachability, not about execution, and the two
1218
+ * came apart in review: for four of the eight labels the behavior command
1219
+ * is byte-identical to a native command already in `selected` —
1220
+ * `pytest-bdd`/`pytest`, `cucumber-jvm`/`jvm.command`, and both
1221
+ * `reqnroll` and `specflow`/`dotnet test`. Those native rows are
1222
+ * SPEED_FAST, so the suite does run by default; it runs as the native
1223
+ * runner's work, which this axis neither caused nor can prevent. Stating
1224
+ * it as "reported, never run" overreached in exactly the half a reader
1225
+ * would act on.
1226
+ */
1227
+ behavior_runners;
1228
+ constructor(args) {
1229
+ this.ecosystems = args.ecosystems;
1230
+ this.runners = args.runners;
1231
+ this.selected = args.selected;
1232
+ this.quality = args.quality;
1233
+ this.confidence = args.confidence;
1234
+ this.mtime = args.mtime;
1235
+ this.behavior_runners = args.behavior_runners ?? [];
1236
+ }
1237
+ /**
1238
+ * Serialise to the auto-generated project-config shape.
1239
+ *
1240
+ * Written to `agents/runtime/state/toolchain.json` by
1241
+ * {@link write_config} so the per-stack commands are captured once and
1242
+ * re-read cheaply (keyed on `mtime`) instead of re-detected every turn.
1243
+ */
1244
+ to_config() {
1245
+ return {
1246
+ ecosystems: [...this.ecosystems],
1247
+ confidence: this.confidence,
1248
+ // `mtime` is a Python float; wrap so `write_config`'s serialiser
1249
+ // renders an integral value as `N.0` (Python float repr).
1250
+ mtime: pyFloat(this.mtime),
1251
+ runners: this.runners.map((r) => ({
1252
+ ecosystem: r.ecosystem,
1253
+ runner: r.runner,
1254
+ command: r.command,
1255
+ speed: r.speed,
1256
+ confidence: r.confidence,
1257
+ basis: r.basis
1258
+ })),
1259
+ selected: this.selected.map((r) => r.command),
1260
+ quality: [...this.quality],
1261
+ behavior_runners: this.behavior_runners.map((r) => ({
1262
+ ecosystem: r.ecosystem,
1263
+ runner: r.runner,
1264
+ command: r.command,
1265
+ scope_root: r.scope_root,
1266
+ confidence: r.confidence,
1267
+ basis: r.basis,
1268
+ conflict: [...r.conflict]
1269
+ }))
1270
+ };
1271
+ }
1272
+ };
1273
+ function resolve_toolchain(project_root, opts = {}) {
1274
+ const include_slow = opts.include_slow ?? false;
1275
+ const include_e2e = opts.include_e2e ?? false;
1276
+ const php_only = opts.php_only ?? false;
1277
+ const runners = [];
1278
+ const quality = [];
1279
+ const composer = _read_json(path3.join(project_root, "composer.json"));
1280
+ const pkg = _read_json(path3.join(project_root, "package.json"));
1281
+ const has_composer = _is_file(path3.join(project_root, "composer.json"));
1282
+ const has_package = _is_file(path3.join(project_root, "package.json"));
1283
+ const pyproject_text = _read_text(path3.join(project_root, "pyproject.toml"));
1284
+ const has_python = Boolean(pyproject_text) || _is_file(path3.join(project_root, "requirements.txt")) || _is_file(path3.join(project_root, "setup.cfg")) || _is_file(path3.join(project_root, "pytest.ini"));
1285
+ const has_go = _is_file(path3.join(project_root, "go.mod"));
1286
+ const has_cargo = _is_file(path3.join(project_root, "Cargo.toml"));
1287
+ const gemfile_text = _ruby_gemfile_text(project_root);
1288
+ const jvm = _jvm_build(project_root);
1289
+ const dotnet_basis = _dotnet_basis(project_root);
1290
+ const wrappers = _task_runner_wrappers(project_root, pkg);
1291
+ if (has_composer) {
1292
+ runners.push(..._php_runners(project_root, composer, wrappers));
1293
+ quality.push(..._php_quality(project_root, composer, wrappers));
1294
+ }
1295
+ if (has_package) {
1296
+ runners.push(..._js_runners(pkg, wrappers, _package_manager(project_root, pkg)));
1297
+ quality.push(..._js_quality(pkg, wrappers));
1298
+ }
1299
+ if (has_python) {
1300
+ runners.push(..._python_runners(pyproject_text));
1301
+ quality.push(..._python_quality(pyproject_text));
1302
+ }
1303
+ if (has_go) {
1304
+ runners.push(
1305
+ new RunnerResult("go", "go-test", "go test ./...", SPEED_FAST, HIGH, "go.mod present")
1306
+ );
1307
+ quality.push("go vet ./...");
1308
+ }
1309
+ if (has_cargo) {
1310
+ runners.push(
1311
+ new RunnerResult("rust", "cargo-test", "cargo test", SPEED_FAST, HIGH, "Cargo.toml present")
1312
+ );
1313
+ quality.push("cargo clippy");
1314
+ }
1315
+ runners.push(..._ruby_runners(project_root, gemfile_text));
1316
+ if (jvm !== null) {
1317
+ runners.push(..._jvm_runners(jvm));
1318
+ }
1319
+ if (dotnet_basis !== null) {
1320
+ runners.push(..._dotnet_runners(dotnet_basis));
1321
+ }
1322
+ const ecosystems = _dictFromKeys(runners.map((r) => r.ecosystem));
1323
+ const selected = _apply_guard(runners, { include_slow, include_e2e, php_only });
1324
+ const confidence = _overall_confidence(runners);
1325
+ const scopes = _behavior_scopes(project_root);
1326
+ return new ToolchainResult({
1327
+ ecosystems,
1328
+ runners: [...runners],
1329
+ selected: [...selected],
1330
+ quality: _dictFromKeys(quality),
1331
+ confidence,
1332
+ // Computed ONCE and threaded into both consumers: workspace discovery
1333
+ // reads a manifest and lists per glob parent, and both the cache probe
1334
+ // and the behavior axis need the same list.
1335
+ mtime: latest_manifest_mtime(project_root, scopes),
1336
+ behavior_runners: resolve_behavior_runners(project_root, scopes)
1337
+ });
1338
+ }
1339
+ function latest_manifest_mtime(project_root, scopes) {
1340
+ const mtimes = [];
1341
+ const names = [..._MANIFESTS, ..._BEHAVIOR_MARKERS];
1342
+ for (const scope of scopes ?? _behavior_scopes(project_root)) {
1343
+ const dir = scope === "." ? project_root : path3.join(project_root, scope);
1344
+ for (const name of names) {
1345
+ const p = path3.join(dir, name);
1346
+ if (_is_file(p)) {
1347
+ mtimes.push(_stat_mtime(p));
1348
+ }
1349
+ }
1350
+ }
1351
+ return mtimes.length > 0 ? Math.max(...mtimes) : 0;
1352
+ }
1353
+ function _php_runners(root, composer, wrappers) {
1354
+ const deps = _all_dependencies(composer, "require", "require-dev");
1355
+ if ("pestphp/pest" in deps || _is_file(path3.join(root, "vendor", "bin", "pest"))) {
1356
+ const cmd2 = wrappers["php-test"] || "vendor/bin/pest";
1357
+ const basis = "pestphp/pest" in deps ? "pestphp/pest in composer require" : "vendor/bin/pest present";
1358
+ return [new RunnerResult("php", "pest", cmd2, SPEED_FAST, HIGH, basis)];
1359
+ }
1360
+ if (_is_file(path3.join(root, "artisan"))) {
1361
+ const cmd2 = wrappers["php-test"] || "php artisan test";
1362
+ return [new RunnerResult("php", "phpunit", cmd2, SPEED_FAST, HIGH, "artisan present (Laravel)")];
1363
+ }
1364
+ if ("phpunit/phpunit" in deps || _is_file(path3.join(root, "vendor", "bin", "phpunit"))) {
1365
+ const cmd2 = wrappers["php-test"] || "vendor/bin/phpunit";
1366
+ return [new RunnerResult("php", "phpunit", cmd2, SPEED_FAST, HIGH, "phpunit/phpunit detected")];
1367
+ }
1368
+ const cmd = wrappers["php-test"] || "vendor/bin/phpunit";
1369
+ return [
1370
+ new RunnerResult(
1371
+ "php",
1372
+ "phpunit",
1373
+ cmd,
1374
+ SPEED_FAST,
1375
+ MEDIUM,
1376
+ "composer.json present, no explicit runner"
1377
+ )
1378
+ ];
1379
+ }
1380
+ function _js_runners(pkg, wrappers, manager) {
1381
+ const deps = _all_dependencies(
1382
+ pkg,
1383
+ "dependencies",
1384
+ "devDependencies",
1385
+ "peerDependencies",
1386
+ "optionalDependencies"
1387
+ );
1388
+ const scripts = _isDict(pkg.scripts) ? pkg.scripts : {};
1389
+ const pm_test = wrappers["js-test"];
1390
+ const out = [];
1391
+ if ("vitest" in deps) {
1392
+ const cmd = pm_test && _script_uses(scripts, "test", "vitest") ? pm_test : "npx vitest run";
1393
+ out.push(new RunnerResult("js", "vitest", cmd, SPEED_FAST, HIGH, "vitest in package deps"));
1394
+ } else if ("jest" in deps) {
1395
+ const cmd = pm_test && _script_uses(scripts, "test", "jest") ? pm_test : "npx jest";
1396
+ out.push(new RunnerResult("js", "jest", cmd, SPEED_FAST, HIGH, "jest in package deps"));
1397
+ } else if (pm_test) {
1398
+ out.push(
1399
+ new RunnerResult(
1400
+ "js",
1401
+ "jest",
1402
+ pm_test,
1403
+ SPEED_FAST,
1404
+ MEDIUM,
1405
+ "package.json test script, runner unclear"
1406
+ )
1407
+ );
1408
+ }
1409
+ if ("@playwright/test" in deps || "playwright" in deps) {
1410
+ const cmd = _script_command(scripts, ["test:e2e", "e2e", "playwright"], manager) || "npx playwright test";
1411
+ out.push(
1412
+ new RunnerResult("js", "playwright", cmd, SPEED_E2E, HIGH, "@playwright/test in package deps")
1413
+ );
1414
+ } else if ("cypress" in deps) {
1415
+ const cmd = _script_command(scripts, ["test:e2e", "e2e", "cypress"], manager) || "npx cypress run";
1416
+ out.push(new RunnerResult("js", "cypress", cmd, SPEED_E2E, HIGH, "cypress in package deps"));
1417
+ }
1418
+ const slow_cmd = _script_command(scripts, ["test:slow", "test:integration"], manager);
1419
+ if (slow_cmd) {
1420
+ out.push(
1421
+ new RunnerResult(
1422
+ "js",
1423
+ "vitest" in deps ? "vitest" : "jest",
1424
+ slow_cmd,
1425
+ SPEED_SLOW,
1426
+ MEDIUM,
1427
+ "test:slow/integration script"
1428
+ )
1429
+ );
1430
+ }
1431
+ return out;
1432
+ }
1433
+ function _python_runners(pyproject_text) {
1434
+ if (pyproject_text.includes("pytest") || pyproject_text === "") {
1435
+ const conf = pyproject_text.includes("pytest") ? HIGH : MEDIUM;
1436
+ const basis = pyproject_text.includes("pytest") ? "pytest in pyproject" : "python project, no explicit runner";
1437
+ return [new RunnerResult("python", "pytest", "pytest", SPEED_FAST, conf, basis)];
1438
+ }
1439
+ return [
1440
+ new RunnerResult("python", "pytest", "pytest", SPEED_FAST, MEDIUM, "python project, no explicit runner")
1441
+ ];
1442
+ }
1443
+ function _ruby_gemfile_text(dir) {
1444
+ let present = false;
1445
+ for (const name of ["Gemfile", "gems.rb"]) {
1446
+ const full = path3.join(dir, name);
1447
+ present = present || _is_file(full);
1448
+ const text = _read_text(full);
1449
+ if (text !== "") {
1450
+ return { text, file: name, present: true };
1451
+ }
1452
+ }
1453
+ return { text: "", file: "", present };
1454
+ }
1455
+ function _gem_declared(text, gem) {
1456
+ return new RegExp(`^[ \\t]*gem[ \\t]+["']${gem}(-[A-Za-z0-9_-]+)?["']`, "m").test(text);
1457
+ }
1458
+ function _ruby_runners(root, gemfile) {
1459
+ const dot_rspec = _is_file(path3.join(root, ".rspec"));
1460
+ const spec_helper = _is_file(path3.join(root, "spec", "spec_helper.rb"));
1461
+ const in_gemfile = _gem_declared(gemfile.text, "rspec");
1462
+ if (!in_gemfile && !dot_rspec && !spec_helper) {
1463
+ return [];
1464
+ }
1465
+ const basis = in_gemfile ? `rspec in ${gemfile.file}` : dot_rspec ? ".rspec present" : "spec/spec_helper.rb present";
1466
+ const cmd = gemfile.present ? "bundle exec rspec" : "rspec";
1467
+ return [new RunnerResult("ruby", "rspec", cmd, SPEED_FAST, HIGH, basis)];
1468
+ }
1469
+ function _jvm_build(root) {
1470
+ const pom = _read_text(path3.join(root, "pom.xml"));
1471
+ if (pom !== "") {
1472
+ const wrapper = _is_file(path3.join(root, "mvnw"));
1473
+ return { tool: "maven", text: pom, command: wrapper ? "./mvnw test" : "mvn test" };
1474
+ }
1475
+ const gradleFile = ["build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"].find(
1476
+ (n) => _is_file(path3.join(root, n))
1477
+ );
1478
+ if (gradleFile !== void 0) {
1479
+ const wrapper = _is_file(path3.join(root, "gradlew"));
1480
+ return {
1481
+ tool: "gradle",
1482
+ text: _read_text(path3.join(root, "build.gradle")) + "\n" + _read_text(path3.join(root, "build.gradle.kts")),
1483
+ command: wrapper ? "./gradlew test" : "gradle test"
1484
+ };
1485
+ }
1486
+ return null;
1487
+ }
1488
+ function _jvm_runners(build) {
1489
+ const named = /\bjunit\b/i.test(build.text);
1490
+ const conf = named ? HIGH : MEDIUM;
1491
+ const basis = named ? `junit in the ${build.tool} build file` : `${build.tool} build file present, no explicit runner`;
1492
+ return [new RunnerResult("jvm", "junit", build.command, SPEED_FAST, conf, basis)];
1493
+ }
1494
+ function _dotnet_basis(root) {
1495
+ let names;
1496
+ try {
1497
+ names = fs3.readdirSync(root, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort();
1498
+ } catch {
1499
+ names = [];
1500
+ }
1501
+ const target = names.find((n) => _DOTNET_PROJECT_EXTS.includes(path3.extname(n).toLowerCase()));
1502
+ if (target === void 0) {
1503
+ return null;
1504
+ }
1505
+ return _DOTNET_TEST_STACK.test(_dotnet_project_text(root)) ? { basis: "test stack named in a .NET project file", confidence: HIGH } : { basis: `${target} present, no test stack named`, confidence: MEDIUM };
1506
+ }
1507
+ function _dotnet_runners(found) {
1508
+ return [
1509
+ new RunnerResult(
1510
+ "dotnet",
1511
+ "dotnet-test",
1512
+ "dotnet test",
1513
+ SPEED_FAST,
1514
+ found.confidence,
1515
+ found.basis
1516
+ )
1517
+ ];
1518
+ }
1519
+ function resolve_behavior_runners(project_root, scopes) {
1520
+ const out = [];
1521
+ for (const scope of scopes ?? _behavior_scopes(project_root)) {
1522
+ const dir = scope === "." ? project_root : path3.join(project_root, scope);
1523
+ const rows = _behavior_runners_in_scope(dir, scope);
1524
+ for (const ecosystem of _dictFromKeys(rows.map((r) => r.ecosystem))) {
1525
+ const found = rows.filter((r) => r.ecosystem === ecosystem);
1526
+ const names = [...new Set(found.map((r) => r.runner))].sort();
1527
+ if (names.length <= 1) {
1528
+ out.push(found[0]);
1529
+ continue;
1530
+ }
1531
+ out.push(
1532
+ new BehaviorRunnerResult(
1533
+ ecosystem,
1534
+ BEHAVIOR_UNKNOWN,
1535
+ "",
1536
+ scope,
1537
+ LOW,
1538
+ `two ${ecosystem} behavior runners in one scope (${names.join(", ")}) \u2014 refusing to choose, as the frontend detector refuses between mutually exclusive workspaces`,
1539
+ names
1540
+ )
1541
+ );
1542
+ }
1543
+ }
1544
+ return out;
1545
+ }
1546
+ function _behavior_runners_in_scope(dir, scope) {
1547
+ const out = [];
1548
+ const at = (n) => path3.join(dir, n);
1549
+ const row = (ecosystem, runner, command, basis) => {
1550
+ out.push(new BehaviorRunnerResult(ecosystem, runner, command, scope, HIGH, basis));
1551
+ };
1552
+ const php_deps = _all_dependencies(_read_json(at("composer.json")), "require", "require-dev");
1553
+ const behat_config = ["behat.yml", "behat.yaml", "behat.yml.dist", "behat.dist.yml"].find(
1554
+ (n) => _is_file(at(n))
1555
+ );
1556
+ if ("behat/behat" in php_deps || behat_config !== void 0) {
1557
+ const declared = "behat/behat" in php_deps;
1558
+ const basis = declared ? "behat/behat in the composer manifest" : `${behat_config} present`;
1559
+ row("php", "behat", declared ? "vendor/bin/behat" : "behat", basis);
1560
+ }
1561
+ const js_deps = _all_dependencies(
1562
+ _read_json(at("package.json")),
1563
+ "dependencies",
1564
+ "devDependencies"
1565
+ );
1566
+ const cucumber_config = ["cucumber.js", "cucumber.cjs", "cucumber.mjs", "cucumber.json"].find(
1567
+ (n) => _is_file(at(n))
1568
+ );
1569
+ if ("@cucumber/cucumber" in js_deps || "cucumber" in js_deps || cucumber_config !== void 0) {
1570
+ const dep = "@cucumber/cucumber" in js_deps ? "@cucumber/cucumber" : "cucumber";
1571
+ const declared = dep in js_deps;
1572
+ const basis = declared ? `${dep} in package deps` : `${cucumber_config} present`;
1573
+ row("js", "cucumber-js", declared ? "npx cucumber-js" : "cucumber-js", basis);
1574
+ }
1575
+ const py_text = [_read_text(at("pyproject.toml")), _read_text(at("requirements.txt"))].join("\n");
1576
+ if (_PY_BEHAVE.test(py_text)) {
1577
+ row("python", "behave", "behave", "behave declared as a python dependency");
1578
+ }
1579
+ if (_PY_BDD.test(py_text)) {
1580
+ row("python", "pytest-bdd", "pytest", "pytest-bdd declared as a python dependency");
1581
+ }
1582
+ const gemfile = _ruby_gemfile_text(dir);
1583
+ const cucumber_gem = _gem_declared(gemfile.text, "cucumber");
1584
+ if (cucumber_gem || _is_file(path3.join(dir, "features", "support", "env.rb"))) {
1585
+ const basis = cucumber_gem ? `cucumber in ${gemfile.file}` : "features/support/env.rb present";
1586
+ const cmd = gemfile.present ? "bundle exec cucumber" : "cucumber";
1587
+ row("ruby", "cucumber-ruby", cmd, basis);
1588
+ }
1589
+ const jvm = _jvm_build(dir);
1590
+ if (jvm !== null && /io\.cucumber|cucumber-java|cucumber-junit/i.test(jvm.text)) {
1591
+ row("jvm", "cucumber-jvm", jvm.command, `cucumber in the ${jvm.tool} build file`);
1592
+ }
1593
+ const dotnet_text = _dotnet_project_text(dir);
1594
+ if (/\bReqnroll\b/i.test(dotnet_text)) {
1595
+ row("dotnet", "reqnroll", "dotnet test", "Reqnroll in a project file");
1596
+ }
1597
+ if (/\bSpecFlow\b/i.test(dotnet_text)) {
1598
+ row("dotnet", "specflow", "dotnet test", "SpecFlow in a project file");
1599
+ }
1600
+ return out;
1601
+ }
1602
+ function _dotnet_project_text(dir) {
1603
+ const texts = [];
1604
+ const solution = _has_dotnet_solution(dir);
1605
+ const walk = (at, depth) => {
1606
+ if (texts.length >= _DOTNET_MAX_PROJECTS) {
1607
+ return;
1608
+ }
1609
+ let entries;
1610
+ try {
1611
+ entries = fs3.readdirSync(at, { withFileTypes: true });
1612
+ } catch {
1613
+ return;
1614
+ }
1615
+ entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
1616
+ let hit = false;
1617
+ for (const e of entries) {
1618
+ if (!e.isFile() || !_DOTNET_PROJECT_EXTS.includes(path3.extname(e.name).toLowerCase())) {
1619
+ continue;
1620
+ }
1621
+ if (_DOTNET_SOLUTION_EXTS.includes(path3.extname(e.name).toLowerCase())) {
1622
+ continue;
1623
+ }
1624
+ if (texts.length >= _DOTNET_MAX_PROJECTS) {
1625
+ return;
1626
+ }
1627
+ texts.push(_read_text(path3.join(at, e.name)));
1628
+ hit = true;
1629
+ }
1630
+ if (hit || !solution || depth >= _DOTNET_SCAN_DEPTH) {
1631
+ return;
1632
+ }
1633
+ for (const e of entries) {
1634
+ if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
1635
+ walk(path3.join(at, e.name), depth + 1);
1636
+ }
1637
+ }
1638
+ };
1639
+ walk(dir, 0);
1640
+ return texts.join("\n");
1641
+ }
1642
+ var _MAX_BEHAVIOR_SCOPES = 200;
1643
+ var _PY_BEHAVE = /^[ \t]*["']?behave["']?[ \t]*(?:$|[=<>~!,;[#])/m;
1644
+ var _PY_BDD = /^[ \t]*["']?pytest-bdd["']?[ \t]*(?:$|[=<>~!,;[#])/m;
1645
+ function _pnpm_packages(text) {
1646
+ const out = [];
1647
+ let inPackages = false;
1648
+ const push = (raw) => {
1649
+ const v = raw.replace(/\s+#.*$/, "").trim().replace(/^['"]|['"]$/g, "").trim();
1650
+ if (v !== "") out.push(v);
1651
+ };
1652
+ for (const line of text.split(/\r?\n/)) {
1653
+ if (line.trim() === "" || /^\s*#/.test(line)) {
1654
+ continue;
1655
+ }
1656
+ const key = /^([A-Za-z0-9_-]+)\s*:(.*)$/.exec(line);
1657
+ if (key) {
1658
+ inPackages = key[1] === "packages";
1659
+ const rest = (key[2] ?? "").trim();
1660
+ if (inPackages && rest.startsWith("[")) {
1661
+ for (const part of rest.replace(/^\[|\]$/g, "").split(",")) {
1662
+ push(part);
1663
+ }
1664
+ inPackages = false;
1665
+ }
1666
+ continue;
1667
+ }
1668
+ if (!inPackages) {
1669
+ continue;
1670
+ }
1671
+ const item = /^\s*-\s*(.+?)\s*$/.exec(line);
1672
+ if (item && item[1] !== void 0) {
1673
+ push(item[1]);
1674
+ }
1675
+ }
1676
+ return out;
1677
+ }
1678
+ function _excluded_scope(name) {
1679
+ return name.split("/").some((seg) => seg.startsWith(".") || seg === "node_modules");
1680
+ }
1681
+ function _behavior_scopes(project_root) {
1682
+ const globs = [];
1683
+ const pkg = _read_json(path3.join(project_root, "package.json"));
1684
+ const ws = pkg["workspaces"];
1685
+ if (Array.isArray(ws)) {
1686
+ globs.push(...ws.filter((g) => typeof g === "string"));
1687
+ } else if (_isDict(ws) && Array.isArray(ws["packages"])) {
1688
+ globs.push(...ws["packages"].filter((g) => typeof g === "string"));
1689
+ }
1690
+ globs.push(..._pnpm_packages(_read_text(path3.join(project_root, "pnpm-workspace.yaml"))));
1691
+ const scopes = ["."];
1692
+ for (const raw of globs) {
1693
+ if (scopes.length >= _MAX_BEHAVIOR_SCOPES) {
1694
+ break;
1695
+ }
1696
+ const g = raw.replace(/^\.\//, "").replace(/\/+$/, "");
1697
+ if (g === "" || g.startsWith("!") || g.startsWith("/") || g.includes("..")) {
1698
+ continue;
1699
+ }
1700
+ if (_excluded_scope(g)) {
1701
+ continue;
1702
+ }
1703
+ if (g.endsWith("/*")) {
1704
+ const parent = g.slice(0, -2);
1705
+ let children;
1706
+ try {
1707
+ children = fs3.readdirSync(path3.join(project_root, parent), { withFileTypes: true });
1708
+ } catch {
1709
+ continue;
1710
+ }
1711
+ children.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
1712
+ for (const c of children) {
1713
+ if (scopes.length >= _MAX_BEHAVIOR_SCOPES) {
1714
+ break;
1715
+ }
1716
+ if (c.isDirectory() && !_excluded_scope(c.name)) {
1717
+ scopes.push(`${parent}/${c.name}`);
1718
+ }
1719
+ }
1720
+ } else if (!g.includes("*")) {
1721
+ try {
1722
+ if (fs3.lstatSync(path3.join(project_root, g)).isDirectory()) {
1723
+ scopes.push(g);
1724
+ }
1725
+ } catch {
1726
+ }
1727
+ }
1728
+ }
1729
+ return _dictFromKeys(scopes);
1730
+ }
1731
+ function _php_quality(root, composer, _wrappers) {
1732
+ const deps = _all_dependencies(composer, "require", "require-dev");
1733
+ const out = [];
1734
+ if ("phpstan/phpstan" in deps || _is_file(path3.join(root, "vendor", "bin", "phpstan"))) {
1735
+ out.push("vendor/bin/phpstan analyse");
1736
+ }
1737
+ if ("laravel/pint" in deps || _is_file(path3.join(root, "vendor", "bin", "pint"))) {
1738
+ out.push("vendor/bin/pint");
1739
+ }
1740
+ return out;
1741
+ }
1742
+ function _js_quality(pkg, _wrappers) {
1743
+ const deps = _all_dependencies(
1744
+ pkg,
1745
+ "dependencies",
1746
+ "devDependencies",
1747
+ "peerDependencies",
1748
+ "optionalDependencies"
1749
+ );
1750
+ const out = [];
1751
+ if ("typescript" in deps) {
1752
+ out.push("npx tsc --noEmit");
1753
+ }
1754
+ if ("eslint" in deps) {
1755
+ out.push("npx eslint .");
1756
+ }
1757
+ return out;
1758
+ }
1759
+ function _python_quality(pyproject_text) {
1760
+ const out = [];
1761
+ if (pyproject_text.includes("ruff")) {
1762
+ out.push("ruff check");
1763
+ }
1764
+ if (pyproject_text.includes("mypy")) {
1765
+ out.push("mypy .");
1766
+ }
1767
+ return out;
1768
+ }
1769
+ function _task_runner_wrappers(root, pkg) {
1770
+ const out = {};
1771
+ const makefile = _read_text(path3.join(root, "Makefile"));
1772
+ const taskfile = _read_text(path3.join(root, "Taskfile.yml")) || _read_text(path3.join(root, "Taskfile.yaml"));
1773
+ if (makefile && _reSearch(/^test\s*:/m, makefile)) {
1774
+ out["php-test"] = "make test";
1775
+ } else if (taskfile && _reSearch(/^\s*test\s*:/m, taskfile)) {
1776
+ out["php-test"] = "task test";
1777
+ }
1778
+ const scripts = _isDict(pkg.scripts) ? pkg.scripts : {};
1779
+ if (_isDict(scripts) && "test" in scripts) {
1780
+ out["js-test"] = `${_package_manager(root, pkg)} test`;
1781
+ }
1782
+ return out;
1783
+ }
1784
+ var LOCKFILE_MANAGERS = [
1785
+ ["pnpm-lock.yaml", "pnpm"],
1786
+ ["yarn.lock", "yarn"],
1787
+ ["bun.lock", "bun"],
1788
+ ["bun.lockb", "bun"],
1789
+ ["package-lock.json", "npm"]
1790
+ ];
1791
+ function _resolve_package_manager(root, pkg) {
1792
+ const declared = pkg === void 0 ? void 0 : pkg["packageManager"];
1793
+ if (typeof declared === "string" && declared.trim() !== "") {
1794
+ const name = declared.trim().split("@")[0];
1795
+ if (name !== "") {
1796
+ return { manager: name, via: "packageManager", finding: "" };
1797
+ }
1798
+ }
1799
+ const present = LOCKFILE_MANAGERS.filter(([file]) => _is_file(path3.join(root, file)));
1800
+ const distinct = [...new Set(present.map(([, mgr]) => mgr))];
1801
+ if (distinct.length > 1) {
1802
+ return {
1803
+ manager: null,
1804
+ via: "ambiguous",
1805
+ finding: `two package-manager lockfiles present (${present.map(([f]) => f).join(", ")}) and no \`packageManager\` declaration \u2014 report both and stop, per monorepo-workspace SKILL.md \xA7 1`
1806
+ };
1807
+ }
1808
+ if (distinct.length === 1) {
1809
+ const mgr = distinct[0];
1810
+ return { manager: mgr, via: mgr, finding: "" };
1811
+ }
1812
+ return { manager: "npm", via: "npm", finding: "" };
1813
+ }
1814
+ function _package_manager(root, pkg) {
1815
+ return _resolve_package_manager(root, pkg).manager ?? "npm";
1816
+ }
1817
+ function _apply_guard(runners, opts) {
1818
+ const out = [];
1819
+ for (const r of runners) {
1820
+ if (opts.php_only && r.ecosystem !== "php") {
1821
+ continue;
1822
+ }
1823
+ if (r.speed === SPEED_E2E && !opts.include_e2e) {
1824
+ continue;
1825
+ }
1826
+ if (r.speed === SPEED_SLOW && !opts.include_slow) {
1827
+ continue;
1828
+ }
1829
+ out.push(r);
1830
+ }
1831
+ return out;
1832
+ }
1833
+ function _overall_confidence(runners) {
1834
+ if (runners.length === 0) {
1835
+ return LOW;
1836
+ }
1837
+ if (runners.some((r) => r.confidence === HIGH)) {
1838
+ return HIGH;
1839
+ }
1840
+ return MEDIUM;
1841
+ }
1842
+ function _read_json(p) {
1843
+ if (!_is_file(p)) {
1844
+ return {};
1845
+ }
1846
+ let payload;
1847
+ try {
1848
+ payload = JSON.parse(fs3.readFileSync(p, { encoding: "utf-8" }));
1849
+ } catch {
1850
+ return {};
1851
+ }
1852
+ return _isDict(payload) ? payload : {};
1853
+ }
1854
+ function _read_text(p) {
1855
+ if (!_is_file(p)) {
1856
+ return "";
1857
+ }
1858
+ try {
1859
+ return fs3.readFileSync(p, { encoding: "utf-8" });
1860
+ } catch {
1861
+ return "";
1862
+ }
1863
+ }
1864
+ function _all_dependencies(manifest, ...keys) {
1865
+ const merged = {};
1866
+ for (const key of keys) {
1867
+ const section = manifest[key];
1868
+ if (_isDict(section)) {
1869
+ Object.assign(merged, section);
1870
+ }
1871
+ }
1872
+ return merged;
1873
+ }
1874
+ function _script_uses(scripts, name, tool) {
1875
+ const value = scripts[name];
1876
+ return typeof value === "string" && value.includes(tool);
1877
+ }
1878
+ function _script_command(scripts, names, manager) {
1879
+ for (const name of names) {
1880
+ if (typeof scripts[name] === "string") {
1881
+ return `${manager} run ${name}`;
1882
+ }
1883
+ }
1884
+ return "";
1885
+ }
1886
+ function _is_file(p) {
1887
+ try {
1888
+ return fs3.statSync(p).isFile();
1889
+ } catch {
1890
+ return false;
1891
+ }
1892
+ }
1893
+ function _stat_mtime(p) {
1894
+ try {
1895
+ return Number(fs3.statSync(p, { bigint: true }).mtimeNs) / 1e9;
1896
+ } catch {
1897
+ return 0;
1898
+ }
1899
+ }
1900
+ function _isDict(v) {
1901
+ return typeof v === "object" && v !== null && !Array.isArray(v);
1902
+ }
1903
+ function _reSearch(pattern, text) {
1904
+ return pattern.test(text);
1905
+ }
1906
+ function _dictFromKeys(seq) {
1907
+ return [...new Set(seq)];
1908
+ }
1909
+ var _PyFloat = class {
1910
+ value;
1911
+ constructor(value) {
1912
+ this.value = value;
1913
+ }
1914
+ };
1915
+ function pyFloat(value) {
1916
+ return new _PyFloat(value);
1917
+ }
1918
+
1032
1919
  // src/scripts/before_complete_hook.ts
1033
- var STATE_FILE = path.join("agents", "state", "verify-before-complete.json");
1034
- var STATE_DIR = path.join("agents", "state", "verify-before-complete");
1920
+ var STATE_FILE = path4.join("agents", "state", "verify-before-complete.json");
1921
+ var STATE_DIR = path4.join("agents", "state", "verify-before-complete");
1035
1922
  var STATE_RETENTION_DAYS = 7;
1036
1923
  function statePathFor(session_id) {
1037
1924
  return session_state_file(STATE_DIR, session_id);
@@ -1080,9 +1967,55 @@ function _empty_state() {
1080
1967
  // a reader must never have to distinguish "no runs yet" from "key absent".
1081
1968
  verification_runs: [],
1082
1969
  edits_this_turn: 0,
1970
+ // NOTE — `quality_runs` and `quality_files_source` are deliberately ABSENT
1971
+ // here, against the convention `verification_runs` above follows.
1972
+ //
1973
+ // That convention ("present in the empty state so a reader never has to tell
1974
+ // 'no runs yet' from 'key absent'") is the right default and it loses to a
1975
+ // stronger one: the roadmap's first acceptance criterion is that with
1976
+ // `touched_file_quality: off` the record is BYTE-IDENTICAL to the base ref.
1977
+ // Seeding two keys would break that for every install that never opted in,
1978
+ // and "the default-off feature changed my state file" is exactly the kind of
1979
+ // silent shape drift a consumer cannot audit. The convention's cost here is
1980
+ // zero, because the only reader of these keys is 2.1's analysis, which counts
1981
+ // stops — and a stop with no key is a stop where the pass did not run, which
1982
+ // is a distinction it needs rather than one it must not make.
1083
1983
  checked_at: _now()
1084
1984
  };
1085
1985
  }
1986
+ function readTouchedFileQualityMode(consumer_root) {
1987
+ const raw = hookSectionValue(consumer_root, "verify_before_complete", "touched_file_quality");
1988
+ return raw === "shadow" || raw === "warn" ? raw : "off";
1989
+ }
1990
+ function collectTouchedFileQuality(consumer_root, session_id) {
1991
+ let touched;
1992
+ try {
1993
+ touched = touchedFilesAtStop(consumer_root, session_id);
1994
+ } catch {
1995
+ return null;
1996
+ }
1997
+ if (touched.files.length === 0) {
1998
+ return { runs: [], source: touched.source };
1999
+ }
2000
+ let commands = [];
2001
+ try {
2002
+ commands = resolve_toolchain(consumer_root).quality;
2003
+ } catch {
2004
+ commands = [];
2005
+ }
2006
+ try {
2007
+ return {
2008
+ runs: runTouchedFileQuality({
2009
+ root: consumer_root,
2010
+ commands,
2011
+ files: touched.files
2012
+ }),
2013
+ source: touched.source
2014
+ };
2015
+ } catch {
2016
+ return null;
2017
+ }
2018
+ }
1086
2019
  function _extract_command(payload) {
1087
2020
  const toolRaw = payload["tool_name"] || payload["toolName"] || payload["tool"];
1088
2021
  if (!(typeof toolRaw === "string" && COMMAND_TOOLS.has(toolRaw))) {
@@ -1195,6 +2128,7 @@ function _cap_runs(runs) {
1195
2128
  return [head, ...tail];
1196
2129
  }
1197
2130
  var _ERROR_EXIT_PREFIX = /^Error:\s*Exit code\s*(\d+)/i;
2131
+ var _BARE_EXIT_PREFIX = /^\s*Exit code\s*(\d+)/i;
1198
2132
  var _NO_EXIT = { code: null, source: null, interrupted: false };
1199
2133
  function _numeric_exit_field(obj) {
1200
2134
  for (const key of ["exit_code", "exitCode", "returncode", "returnCode", "status_code"]) {
@@ -1206,26 +2140,46 @@ function _numeric_exit_field(obj) {
1206
2140
  }
1207
2141
  return null;
1208
2142
  }
1209
- function _extract_exit_reading(payload) {
2143
+ var FAILURE_EVENT_NAMES = /* @__PURE__ */ new Set(["PostToolUseFailure"]);
2144
+ function _is_failure_event(payload, envelope) {
2145
+ for (const v of [payload["hook_event_name"], envelope["native_event"]]) {
2146
+ if (typeof v !== "string" || v === "") continue;
2147
+ if (FAILURE_EVENT_NAMES.has(v)) return true;
2148
+ if (/failure$/i.test(v)) return true;
2149
+ }
2150
+ return false;
2151
+ }
2152
+ function _extract_exit_reading(payload, failure_event = false) {
2153
+ const seal = (r) => failure_event && r.code === 0 ? { code: null, source: null, interrupted: r.interrupted } : r;
1210
2154
  const direct = _numeric_exit_field(payload);
1211
- if (direct !== null) return { code: direct, source: "field", interrupted: false };
2155
+ if (direct !== null) return seal({ code: direct, source: "field", interrupted: false });
2156
+ const interrupted_top = payload["is_interrupt"] === true;
2157
+ const err = payload["error"];
2158
+ if (typeof err === "string") {
2159
+ if (interrupted_top) return { code: null, source: null, interrupted: true };
2160
+ const bare = _BARE_EXIT_PREFIX.exec(err) ?? _ERROR_EXIT_PREFIX.exec(err);
2161
+ if (bare?.[1] !== void 0) {
2162
+ return seal({ code: Number(bare[1]), source: "error_prefix", interrupted: false });
2163
+ }
2164
+ }
2165
+ if (interrupted_top) return { code: null, source: null, interrupted: true };
1212
2166
  for (const key of ["tool_response", "toolResponse", "result", "output"]) {
1213
2167
  const v = payload[key];
1214
2168
  if (typeof v === "string") {
1215
2169
  const m = _ERROR_EXIT_PREFIX.exec(v);
1216
2170
  if (m?.[1] !== void 0) {
1217
- return { code: Number(m[1]), source: "error_prefix", interrupted: false };
2171
+ return seal({ code: Number(m[1]), source: "error_prefix", interrupted: false });
1218
2172
  }
1219
2173
  continue;
1220
2174
  }
1221
2175
  if (v === null || typeof v !== "object" || Array.isArray(v)) continue;
1222
2176
  const obj = v;
1223
2177
  const nested = _numeric_exit_field(obj);
1224
- if (nested !== null) return { code: nested, source: "field", interrupted: false };
2178
+ if (nested !== null) return seal({ code: nested, source: "field", interrupted: false });
1225
2179
  if (obj["interrupted"] === true) return { code: null, source: null, interrupted: true };
1226
2180
  const hasStream = typeof obj["stdout"] === "string" || typeof obj["stderr"] === "string";
1227
2181
  if (obj["interrupted"] === false && hasStream) {
1228
- return { code: 0, source: "response_shape", interrupted: false };
2182
+ return seal({ code: 0, source: "response_shape", interrupted: false });
1229
2183
  }
1230
2184
  }
1231
2185
  return _NO_EXIT;
@@ -1247,10 +2201,15 @@ function _extract_run_streams(payload) {
1247
2201
  }
1248
2202
  const topOut = payload["stdout"] ?? payload["output"];
1249
2203
  const topErr = payload["stderr"];
1250
- return {
1251
- stdout: typeof topOut === "string" ? topOut : "",
1252
- stderr: typeof topErr === "string" ? topErr : ""
1253
- };
2204
+ if (typeof topOut === "string" || typeof topErr === "string") {
2205
+ return {
2206
+ stdout: typeof topOut === "string" ? topOut : "",
2207
+ stderr: typeof topErr === "string" ? topErr : ""
2208
+ };
2209
+ }
2210
+ const errText = payload["error"];
2211
+ if (typeof errText === "string") return { stdout: errText, stderr: "" };
2212
+ return { stdout: "", stderr: "" };
1254
2213
  }
1255
2214
  function _reset_turn(state, session_id) {
1256
2215
  state["session_id"] = session_id || state["session_id"] || "";
@@ -1259,6 +2218,8 @@ function _reset_turn(state, session_id) {
1259
2218
  state["verified_this_turn"] = false;
1260
2219
  state["verification_runs"] = [];
1261
2220
  state["edits_this_turn"] = 0;
2221
+ delete state["quality_runs"];
2222
+ delete state["quality_files_source"];
1262
2223
  state["ci_saw_pending"] = false;
1263
2224
  state["nonevidence_this_turn"] = 0;
1264
2225
  return state;
@@ -1273,7 +2234,7 @@ function _asInt(v) {
1273
2234
  const n = Number(v);
1274
2235
  return Number.isFinite(n) ? Math.trunc(n) : 0;
1275
2236
  }
1276
- function _update(state, event, envelope) {
2237
+ function _update(state, event, envelope, quality = null) {
1277
2238
  const session_id = envelope["session_id"] || state["session_id"] || "";
1278
2239
  if (session_id && session_id !== state["session_id"]) {
1279
2240
  state["session_id"] = session_id;
@@ -1281,6 +2242,8 @@ function _update(state, event, envelope) {
1281
2242
  state["ci_last"] = null;
1282
2243
  state = _reset_turn(state, session_id);
1283
2244
  }
2245
+ const platform = envelope["platform"];
2246
+ if (typeof platform === "string" && platform) state["platform"] = platform;
1284
2247
  let payload = envelope["payload"];
1285
2248
  if (!(typeof payload === "object" && payload !== null && !Array.isArray(payload))) {
1286
2249
  payload = {};
@@ -1332,7 +2295,7 @@ function _update(state, event, envelope) {
1332
2295
  }
1333
2296
  if (cmd && mightBeVerification(cmd)) {
1334
2297
  const streams = _extract_run_streams(pl);
1335
- const exit = _extract_exit_reading(pl);
2298
+ const exit = _extract_exit_reading(pl, _is_failure_event(pl, envelope));
1336
2299
  const runs = Array.isArray(state["verification_runs"]) ? [...state["verification_runs"]] : [];
1337
2300
  runs.push({
1338
2301
  command: cmd.slice(0, 512),
@@ -1356,6 +2319,10 @@ function _update(state, event, envelope) {
1356
2319
  }
1357
2320
  } else if (event === "stop") {
1358
2321
  state["last_stop_at"] = _now();
2322
+ if (quality !== null) {
2323
+ state["quality_runs"] = quality.runs;
2324
+ state["quality_files_source"] = quality.source;
2325
+ }
1359
2326
  }
1360
2327
  state["checked_at"] = _now();
1361
2328
  return state;
@@ -1384,10 +2351,12 @@ function run(stdin_text, options) {
1384
2351
  }
1385
2352
  return 0;
1386
2353
  }
1387
- const target = path.join(consumer_root, statePathFor(session_id));
2354
+ const target = path4.join(consumer_root, statePathFor(session_id));
2355
+ const quality_mode = event === "stop" ? readTouchedFileQualityMode(consumer_root) : "off";
2356
+ const quality = quality_mode === "off" ? null : collectTouchedFileQuality(consumer_root, session_id);
1388
2357
  let state = {};
1389
2358
  const outcome = update_json_under_lock(target, (loaded) => {
1390
- state = _update({ ..._empty_state(), ...loaded }, event, envelope);
2359
+ state = _update({ ..._empty_state(), ...loaded }, event, envelope, quality);
1391
2360
  return state;
1392
2361
  });
1393
2362
  if (outcome === "failed") {
@@ -1397,13 +2366,20 @@ function run(stdin_text, options) {
1397
2366
  return 0;
1398
2367
  }
1399
2368
  if (event === "session_start" || event === "user_prompt_submit") {
1400
- prune_legacy_state_file(path.join(consumer_root, STATE_FILE));
2369
+ prune_legacy_state_file(path4.join(consumer_root, STATE_FILE));
1401
2370
  prune_stale_session_states(
1402
- path.join(consumer_root, STATE_DIR),
2371
+ path4.join(consumer_root, STATE_DIR),
1403
2372
  Date.now(),
1404
2373
  STATE_RETENTION_DAYS
1405
2374
  );
1406
2375
  }
2376
+ if (quality_mode === "warn" && quality !== null) {
2377
+ const line = advisoryLine(quality.runs);
2378
+ if (line !== null) {
2379
+ process.stderr.write(`${line}
2380
+ `);
2381
+ }
2382
+ }
1407
2383
  if (verbose) {
1408
2384
  process.stderr.write(
1409
2385
  `verify-before-complete-hook: event=${event} verified_this_turn=${pyRepr(state["verified_this_turn"])} verifications_this_turn=${pyRepr(state["verifications_this_turn"])}
@@ -1447,7 +2423,7 @@ function main(argv) {
1447
2423
  function readTurnRunState(workspaceRoot, session_id) {
1448
2424
  if (!has_stable_session_id(session_id)) return null;
1449
2425
  try {
1450
- const raw = fs.readFileSync(path.join(workspaceRoot, statePathFor(session_id)), "utf-8");
2426
+ const raw = fs4.readFileSync(path4.join(workspaceRoot, statePathFor(session_id)), "utf-8");
1451
2427
  const decoded = JSON.parse(raw);
1452
2428
  if (!owns_session_state(decoded, session_id)) return null;
1453
2429
  if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) return null;
@@ -1468,13 +2444,13 @@ function _isCliEntry() {
1468
2444
  if (process.argv[1] === void 0) {
1469
2445
  return false;
1470
2446
  }
1471
- const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
2447
+ const argvUrl = pathToFileURL(path4.resolve(process.argv[1])).href;
1472
2448
  if (import.meta.url === argvUrl) {
1473
2449
  return true;
1474
2450
  }
1475
2451
  try {
1476
- const here = fs.realpathSync(fileURLToPath(import.meta.url));
1477
- const argv = fs.realpathSync(path.resolve(process.argv[1]));
2452
+ const here = fs4.realpathSync(fileURLToPath(import.meta.url));
2453
+ const argv = fs4.realpathSync(path4.resolve(process.argv[1]));
1478
2454
  return here === argv;
1479
2455
  } catch {
1480
2456
  return false;
@@ -1487,8 +2463,8 @@ if (isCliEntry) {
1487
2463
 
1488
2464
  // src/scripts/_lib/turn_end_refusals.ts
1489
2465
  import * as crypto from "node:crypto";
1490
- import * as fs2 from "node:fs";
1491
- import * as path2 from "node:path";
2466
+ import * as fs5 from "node:fs";
2467
+ import * as path5 from "node:path";
1492
2468
  var DETECTOR_IDS = [
1493
2469
  "promissory",
1494
2470
  "language",
@@ -1497,19 +2473,19 @@ var DETECTOR_IDS = [
1497
2473
  "pending-decision",
1498
2474
  "untested"
1499
2475
  ];
1500
- var REFUSAL_STATE_REL = path2.join("agents", "runtime", "state", "turn-end-gate");
2476
+ var REFUSAL_STATE_REL = path5.join("agents", "runtime", "state", "turn-end-gate");
1501
2477
  function refusalStateDir(workspaceRoot) {
1502
- return path2.join(workspaceRoot, REFUSAL_STATE_REL);
2478
+ return path5.join(workspaceRoot, REFUSAL_STATE_REL);
1503
2479
  }
1504
2480
  function deriveSessionKey(sessionId) {
1505
2481
  return crypto.createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
1506
2482
  }
1507
2483
  function sessionRefusalFile(workspaceRoot, sessionKey) {
1508
- return path2.join(refusalStateDir(workspaceRoot), `${sessionKey}.json`);
2484
+ return path5.join(refusalStateDir(workspaceRoot), `${sessionKey}.json`);
1509
2485
  }
1510
2486
  function readSessionCounts(workspaceRoot, sessionId) {
1511
2487
  try {
1512
- const raw = fs2.readFileSync(
2488
+ const raw = fs5.readFileSync(
1513
2489
  sessionRefusalFile(workspaceRoot, deriveSessionKey(sessionId)),
1514
2490
  "utf-8"
1515
2491
  );
@@ -1620,7 +2596,7 @@ function emptyRetryCounts() {
1620
2596
  var SHADOW_MAX_ROWS = 200;
1621
2597
  var SHADOW_SUFFIX = ".shadow.json";
1622
2598
  function sessionShadowFile(workspaceRoot, sessionKey) {
1623
- return path2.join(refusalStateDir(workspaceRoot), `${sessionKey}${SHADOW_SUFFIX}`);
2599
+ return path5.join(refusalStateDir(workspaceRoot), `${sessionKey}${SHADOW_SUFFIX}`);
1624
2600
  }
1625
2601
  function parseShadowRecord(raw) {
1626
2602
  let decoded;
@@ -1645,7 +2621,9 @@ function parseShadowRecord(raw) {
1645
2621
  if (typeof turn !== "number" || !Number.isFinite(turn)) continue;
1646
2622
  if (typeof at !== "string") continue;
1647
2623
  if (layer !== "stop_hook_active" && layer !== "refused_turn") continue;
1648
- rows.push({ detector: det, turn, at, layer });
2624
+ const setByRaw = ro["set_by"];
2625
+ const set_by = setByRaw === "ours" || setByRaw === "foreign" || setByRaw === "unknown" ? setByRaw : "unknown";
2626
+ rows.push({ detector: det, turn, at, layer, set_by });
1649
2627
  }
1650
2628
  const num = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
1651
2629
  const observed = o["retries_observed"];
@@ -1665,8 +2643,15 @@ function parseShadowRecord(raw) {
1665
2643
  }
1666
2644
  function foldShadow(prev, input) {
1667
2645
  const rows = [...prev?.would_refuse_again ?? []];
2646
+ const set_by = input.setBy ?? "unknown";
1668
2647
  for (const d of input.detectors) {
1669
- rows.push({ detector: d, turn: input.turnOrdinal, at: input.at, layer: input.layer });
2648
+ rows.push({
2649
+ detector: d,
2650
+ turn: input.turnOrdinal,
2651
+ at: input.at,
2652
+ layer: input.layer,
2653
+ set_by
2654
+ });
1670
2655
  }
1671
2656
  let dropped = prev?.dropped ?? 0;
1672
2657
  if (rows.length > SHADOW_MAX_ROWS) {
@@ -1684,27 +2669,37 @@ function foldShadow(prev, input) {
1684
2669
  last_at: input.at
1685
2670
  };
1686
2671
  }
2672
+ function shadowSetBy(workspaceRoot, sessionKey) {
2673
+ let raw;
2674
+ try {
2675
+ raw = fs5.readFileSync(sessionRefusalFile(workspaceRoot, sessionKey), "utf-8");
2676
+ } catch (err) {
2677
+ const code = err?.code;
2678
+ return code === "ENOENT" ? "foreign" : "unknown";
2679
+ }
2680
+ return parseRecord(raw) === null ? "unknown" : "ours";
2681
+ }
1687
2682
  var REFUSAL_STATE_MAX_AGE_DAYS = 90;
1688
2683
  function pruneAgedRefusalState(workspaceRoot, opts = {}) {
1689
2684
  const maxAgeDays = opts.maxAgeDays ?? REFUSAL_STATE_MAX_AGE_DAYS;
1690
- const now = opts.now ?? /* @__PURE__ */ new Date();
2685
+ const now2 = opts.now ?? /* @__PURE__ */ new Date();
1691
2686
  const dir = refusalStateDir(workspaceRoot);
1692
2687
  const result = { scanned: 0, pruned: 0, kept: 0 };
1693
2688
  let entries;
1694
2689
  try {
1695
- entries = fs2.readdirSync(dir);
2690
+ entries = fs5.readdirSync(dir);
1696
2691
  } catch {
1697
2692
  return result;
1698
2693
  }
1699
- const cutoffMs = now.getTime() - maxAgeDays * 24 * 60 * 60 * 1e3;
2694
+ const cutoffMs = now2.getTime() - maxAgeDays * 24 * 60 * 60 * 1e3;
1700
2695
  for (const name of entries) {
1701
2696
  if (!name.endsWith(".json")) continue;
1702
2697
  result.scanned += 1;
1703
- const file = path2.join(dir, name);
2698
+ const file = path5.join(dir, name);
1704
2699
  if (name.endsWith(SHADOW_SUFFIX)) {
1705
2700
  let shadow = null;
1706
2701
  try {
1707
- shadow = parseShadowRecord(fs2.readFileSync(file, "utf-8"));
2702
+ shadow = parseShadowRecord(fs5.readFileSync(file, "utf-8"));
1708
2703
  } catch {
1709
2704
  shadow = null;
1710
2705
  }
@@ -1712,7 +2707,7 @@ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
1712
2707
  if (!Number.isFinite(shadowAt)) {
1713
2708
  let mtime = NaN;
1714
2709
  try {
1715
- mtime = fs2.statSync(file).mtimeMs;
2710
+ mtime = fs5.statSync(file).mtimeMs;
1716
2711
  } catch {
1717
2712
  mtime = NaN;
1718
2713
  }
@@ -1725,7 +2720,7 @@ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
1725
2720
  continue;
1726
2721
  }
1727
2722
  try {
1728
- fs2.unlinkSync(file);
2723
+ fs5.unlinkSync(file);
1729
2724
  result.pruned += 1;
1730
2725
  } catch {
1731
2726
  result.kept += 1;
@@ -1734,7 +2729,7 @@ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
1734
2729
  }
1735
2730
  let rec = null;
1736
2731
  try {
1737
- rec = parseRecord(fs2.readFileSync(file, "utf-8"));
2732
+ rec = parseRecord(fs5.readFileSync(file, "utf-8"));
1738
2733
  } catch {
1739
2734
  rec = null;
1740
2735
  }
@@ -1748,7 +2743,7 @@ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
1748
2743
  continue;
1749
2744
  }
1750
2745
  try {
1751
- fs2.unlinkSync(file);
2746
+ fs5.unlinkSync(file);
1752
2747
  result.pruned += 1;
1753
2748
  } catch {
1754
2749
  result.kept += 1;
@@ -1783,5 +2778,6 @@ export {
1783
2778
  sessionShadowFile,
1784
2779
  parseShadowRecord,
1785
2780
  foldShadow,
2781
+ shadowSetBy,
1786
2782
  pruneAgedRefusalState
1787
2783
  };