@event4u/agent-config 16.3.0 → 16.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (532) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +178 -78
  5. package/README.md +2 -2
  6. package/agents/templates/.ai-council.yml.example +26 -8
  7. package/dist/agent-src/commands/analyze/inbox.md +10 -7
  8. package/dist/agent-src/commands/bug/investigate.md +1 -1
  9. package/dist/agent-src/commands/commit/in-chunks.md +40 -31
  10. package/dist/agent-src/commands/commit.md +72 -33
  11. package/dist/agent-src/commands/condense.md +2 -8
  12. package/dist/agent-src/commands/council/debate.md +11 -7
  13. package/dist/agent-src/commands/council/default.md +10 -9
  14. package/dist/agent-src/commands/council.md +1 -1
  15. package/dist/agent-src/commands/design.md +1 -1
  16. package/dist/agent-src/commands/estimate-ticket.md +1 -1
  17. package/dist/agent-src/commands/feature/explore.md +3 -3
  18. package/dist/agent-src/commands/feature/plan.md +1 -1
  19. package/dist/agent-src/commands/fix/ci.md +17 -3
  20. package/dist/agent-src/commands/fix/commit-messages.md +48 -23
  21. package/dist/agent-src/commands/fix/pr-comments.md +3 -6
  22. package/dist/agent-src/commands/implement-ticket.md +3 -3
  23. package/dist/agent-src/commands/memory/add.md +4 -16
  24. package/dist/agent-src/commands/memory/load.md +5 -6
  25. package/dist/agent-src/commands/memory/promote.md +3 -16
  26. package/dist/agent-src/commands/mission/upgrade.md +3 -2
  27. package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
  28. package/dist/agent-src/commands/optimize/deep.md +4 -2
  29. package/dist/agent-src/commands/optimize/rtk.md +4 -26
  30. package/dist/agent-src/commands/pr/create/description-only.md +18 -15
  31. package/dist/agent-src/commands/pr/create.md +42 -34
  32. package/dist/agent-src/commands/pr/merge.md +136 -9
  33. package/dist/agent-src/commands/prepare-for-review.md +13 -11
  34. package/dist/agent-src/commands/project/analyze.md +7 -35
  35. package/dist/agent-src/commands/refine-ticket.md +1 -1
  36. package/dist/agent-src/commands/review/changes.md +14 -19
  37. package/dist/agent-src/commands/roadmap/next.md +10 -1
  38. package/dist/agent-src/commands/roadmap/process-full.md +52 -38
  39. package/dist/agent-src/commands/roadmap/resolve-blockers.md +64 -14
  40. package/dist/agent-src/commands/worktree/create.md +7 -3
  41. package/dist/agent-src/commands/worktree/verify.md +2 -1
  42. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
  43. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
  44. package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
  45. package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
  46. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +1 -1
  47. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
  48. package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
  49. package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
  50. package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
  51. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
  52. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +67 -52
  53. package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
  54. package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
  55. package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
  56. package/dist/agent-src/contexts/execution/toolchain-resolver.md +4 -10
  57. package/dist/agent-src/contexts/subagent-configuration.md +1 -1
  58. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  59. package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  60. package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  61. package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  62. package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
  63. package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  64. package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  65. package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
  66. package/dist/agent-src/guidelines/design-antipatterns.md +1 -1
  67. package/dist/agent-src/guidelines/php/git.md +15 -2
  68. package/dist/agent-src/rules/active-remediation.md +0 -1
  69. package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
  70. package/dist/agent-src/rules/architecture.md +0 -1
  71. package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
  72. package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
  73. package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
  74. package/dist/agent-src/rules/autonomous-execution.md +0 -1
  75. package/dist/agent-src/rules/brand-consistency.md +0 -1
  76. package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
  77. package/dist/agent-src/rules/broken-access-control.md +0 -1
  78. package/dist/agent-src/rules/cli-output-handling.md +0 -1
  79. package/dist/agent-src/rules/code-comment-discipline.md +0 -1
  80. package/dist/agent-src/rules/code-provenance.md +0 -1
  81. package/dist/agent-src/rules/command-suggestion-policy.md +0 -1
  82. package/dist/agent-src/rules/commit-conventions.md +1 -2
  83. package/dist/agent-src/rules/communication-through-line.md +1 -2
  84. package/dist/agent-src/rules/content-quoting-floor.md +1 -2
  85. package/dist/agent-src/rules/context-hygiene.md +5 -5
  86. package/dist/agent-src/rules/copilot-routing.md +1 -2
  87. package/dist/agent-src/rules/council-availability.md +4 -4
  88. package/dist/agent-src/rules/cross-source-consistency.md +4 -6
  89. package/dist/agent-src/rules/decision-revisit-gate.md +2 -3
  90. package/dist/agent-src/rules/delegation-policy.md +2 -3
  91. package/dist/agent-src/rules/design-fidelity.md +0 -1
  92. package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
  93. package/dist/agent-src/rules/devcontainer-routing.md +1 -2
  94. package/dist/agent-src/rules/doc-screenshot-hygiene.md +0 -1
  95. package/dist/agent-src/rules/docker-commands.md +0 -1
  96. package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
  97. package/dist/agent-src/rules/domain-safety-disclaimer.md +0 -1
  98. package/dist/agent-src/rules/domain-safety-pii.md +0 -1
  99. package/dist/agent-src/rules/domain-safety-retention.md +0 -1
  100. package/dist/agent-src/rules/downstream-changes.md +0 -1
  101. package/dist/agent-src/rules/engineering-safety-floor.md +0 -1
  102. package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
  103. package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
  104. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -2
  105. package/dist/agent-src/rules/finance-safety-floor.md +0 -1
  106. package/dist/agent-src/rules/fix-what-you-see.md +6 -32
  107. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
  108. package/dist/agent-src/rules/git-history-discipline.md +1 -2
  109. package/dist/agent-src/rules/guidelines.md +0 -1
  110. package/dist/agent-src/rules/history-discipline.md +0 -1
  111. package/dist/agent-src/rules/icon-consistency.md +0 -1
  112. package/dist/agent-src/rules/image-likeness-and-rights.md +0 -1
  113. package/dist/agent-src/rules/improve-before-implement.md +0 -1
  114. package/dist/agent-src/rules/invite-challenge.md +0 -1
  115. package/dist/agent-src/rules/laravel-routing.md +1 -2
  116. package/dist/agent-src/rules/laravel-translations.md +0 -1
  117. package/dist/agent-src/rules/legal-safety-floor.md +0 -1
  118. package/dist/agent-src/rules/lethal-trifecta-guard.md +0 -1
  119. package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
  120. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +0 -1
  121. package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
  122. package/dist/agent-src/rules/media-governance-routing.md +1 -2
  123. package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
  124. package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
  125. package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
  126. package/dist/agent-src/rules/missing-tool-handling.md +0 -1
  127. package/dist/agent-src/rules/model-recommendation.md +0 -1
  128. package/dist/agent-src/rules/neighbour-precedence.md +64 -0
  129. package/dist/agent-src/rules/no-attribution-footers.md +0 -1
  130. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
  131. package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
  132. package/dist/agent-src/rules/no-roadmap-references.md +0 -1
  133. package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
  134. package/dist/agent-src/rules/onboarding-gate.md +0 -1
  135. package/dist/agent-src/rules/output-discipline.md +0 -1
  136. package/dist/agent-src/rules/package-ci-checks.md +0 -1
  137. package/dist/agent-src/rules/persona-governance.md +2 -3
  138. package/dist/agent-src/rules/php-coding.md +0 -1
  139. package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
  140. package/dist/agent-src/rules/preservation-guard.md +0 -1
  141. package/dist/agent-src/rules/provider-lifecycle-discipline.md +2 -3
  142. package/dist/agent-src/rules/question-not-instruction.md +0 -1
  143. package/dist/agent-src/rules/reviewer-awareness.md +0 -1
  144. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
  145. package/dist/agent-src/rules/roadmap-progress-sync.md +3 -6
  146. package/dist/agent-src/rules/role-mode-adherence.md +0 -1
  147. package/dist/agent-src/rules/rule-type-governance.md +0 -1
  148. package/dist/agent-src/rules/runtime-safety.md +0 -1
  149. package/dist/agent-src/rules/scale-discipline.md +0 -1
  150. package/dist/agent-src/rules/secret-vcs-guard.md +0 -1
  151. package/dist/agent-src/rules/security-sensitive-stop.md +0 -1
  152. package/dist/agent-src/rules/self-repair-loop.md +0 -1
  153. package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
  154. package/dist/agent-src/rules/session-canary.md +0 -1
  155. package/dist/agent-src/rules/settings-ask-protocol.md +5 -6
  156. package/dist/agent-src/rules/size-enforcement.md +0 -1
  157. package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
  158. package/dist/agent-src/rules/skill-quality.md +0 -1
  159. package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
  160. package/dist/agent-src/rules/source-confidentiality.md +0 -1
  161. package/dist/agent-src/rules/source-discovery-gate.md +0 -1
  162. package/dist/agent-src/rules/source-of-truth.md +0 -1
  163. package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
  164. package/dist/agent-src/rules/strategy-safety-floor.md +0 -1
  165. package/dist/agent-src/rules/symfony-routing.md +1 -2
  166. package/dist/agent-src/rules/think-before-action.md +0 -1
  167. package/dist/agent-src/rules/token-budget-discipline.md +14 -48
  168. package/dist/agent-src/rules/token-efficiency.md +0 -1
  169. package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
  170. package/dist/agent-src/rules/tool-safety.md +0 -1
  171. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  172. package/dist/agent-src/rules/untrusted-input-defense.md +0 -1
  173. package/dist/agent-src/rules/upstream-proposal.md +0 -1
  174. package/dist/agent-src/rules/user-interaction.md +0 -1
  175. package/dist/agent-src/rules/user-interrupt-priority.md +0 -1
  176. package/dist/agent-src/scripts/gate_budget.ts +47 -23
  177. package/dist/agent-src/scripts/gate_execute.ts +12 -4
  178. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +2 -0
  179. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  180. package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
  181. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
  182. package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
  183. package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
  184. package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
  185. package/dist/agent-src/skills/canvas-design/SKILL.md +4 -2
  186. package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
  187. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
  188. package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
  189. package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
  190. package/dist/agent-src/skills/database/data/manifest.json +2 -2
  191. package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
  192. package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +13 -10
  193. package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
  194. package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
  195. package/dist/agent-src/skills/fe-design/references/design-patterns.md +49 -2
  196. package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
  197. package/dist/agent-src/skills/frontend-render-security/SKILL.md +9 -0
  198. package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
  199. package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
  200. package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
  201. package/dist/agent-src/skills/iconography/SKILL.md +4 -2
  202. package/dist/agent-src/skills/image-provider-routing/SKILL.md +16 -5
  203. package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
  204. package/dist/agent-src/skills/logo-generation/SKILL.md +12 -8
  205. package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
  206. package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
  207. package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
  208. package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
  209. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +4 -2
  210. package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
  211. package/dist/agent-src/skills/svg-authoring/SKILL.md +318 -0
  212. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
  213. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
  214. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  215. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
  216. package/dist/agent-src/skills/wireframe/SKILL.md +5 -2
  217. package/dist/agent-src/templates/AGENTS.md +1 -1
  218. package/dist/agent-src/templates/agent-settings.md +46 -129
  219. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
  220. package/dist/agent-src/templates/command.md +2 -2
  221. package/dist/agent-src/templates/roadmaps.md +6 -1
  222. package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  223. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  224. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  225. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  226. package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  227. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  228. package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  229. package/dist/agent-src/templates/skill.md +1 -1
  230. package/dist/cli/commands/uiRender.js +201 -4
  231. package/dist/cli/commands/uiRenderAsset.js +1308 -0
  232. package/dist/cli/commands/uiRenderAssetCompare.js +752 -0
  233. package/dist/cli/commands/uiRenderAssetGrid.js +265 -0
  234. package/dist/cli/commands/uiRenderAssetGrounds.js +323 -0
  235. package/dist/cli/commands/uiRenderAssetIsolation.js +85 -0
  236. package/dist/cli/commands/uiRenderAssetTypes.js +145 -0
  237. package/dist/cli/main.js +47 -1
  238. package/dist/cli/registry.js +2 -1
  239. package/dist/cli-delegate/check_update_banner.js +195 -119
  240. package/dist/cli-delegate/{chunk-S4UMZNES.js → chunk-4DBG5K7M.js} +5 -5
  241. package/dist/cli-delegate/{chunk-TRI6DQZ6.js → chunk-5GPULQBE.js} +60 -42
  242. package/dist/cli-delegate/{chunk-JQVYKIZA.js → chunk-6SRJTYJS.js} +33 -177
  243. package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
  244. package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
  245. package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
  246. package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
  247. package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
  248. package/dist/cli-delegate/{chunk-NXEILF2K.js → chunk-F3QDG2GT.js} +481 -72
  249. package/dist/cli-delegate/{chunk-CQWX336L.js → chunk-G5VVIZRC.js} +3 -3
  250. package/dist/cli-delegate/{chunk-JY2I2DXV.js → chunk-H76PHZBI.js} +137 -60
  251. package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
  252. package/dist/cli-delegate/{chunk-LIYTLH6O.js → chunk-KXXWJTDO.js} +5 -10
  253. package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
  254. package/dist/cli-delegate/{chunk-S4YWNRKO.js → chunk-MDPPKQCQ.js} +34 -1
  255. package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
  256. package/dist/cli-delegate/{chunk-GKE7ZVFA.js → chunk-PWX7CKMD.js} +1 -1
  257. package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
  258. package/dist/cli-delegate/{chunk-RQNNW7IF.js → chunk-T7XSJSUQ.js} +433 -632
  259. package/dist/cli-delegate/{chunk-NQ7WT43P.js → chunk-TMWYUOK4.js} +75 -12
  260. package/dist/cli-delegate/{chunk-EEHDXSYD.js → chunk-WQWXGIWB.js} +12 -273
  261. package/dist/cli-delegate/{chunk-QLDHIPWR.js → chunk-XQTPXKFA.js} +1 -1
  262. package/dist/cli-delegate/cmd_analyze_session.js +223 -43
  263. package/dist/cli-delegate/cmd_brand_status.js +3 -2
  264. package/dist/cli-delegate/cmd_conformance.js +489 -238
  265. package/dist/cli-delegate/cmd_converge.js +3 -3
  266. package/dist/cli-delegate/cmd_doctor.js +15 -12
  267. package/dist/cli-delegate/cmd_doctor_neighbours.js +11 -4
  268. package/dist/cli-delegate/cmd_explain.js +43 -19
  269. package/dist/cli-delegate/cmd_export.js +10 -10
  270. package/dist/cli-delegate/cmd_git_convention.js +1851 -0
  271. package/dist/cli-delegate/cmd_handoff.js +40 -21
  272. package/dist/cli-delegate/cmd_mcp_available.js +3 -2
  273. package/dist/cli-delegate/cmd_migrate.js +3 -3
  274. package/dist/cli-delegate/cmd_packs_active.js +4 -3
  275. package/dist/cli-delegate/cmd_prune.js +10 -10
  276. package/dist/cli-delegate/cmd_route_audit.js +7 -7
  277. package/dist/cli-delegate/cmd_route_explain.js +5 -5
  278. package/dist/cli-delegate/cmd_session_recycle.js +9 -7
  279. package/dist/cli-delegate/cmd_settings_check.js +54 -7
  280. package/dist/cli-delegate/cmd_settings_get.js +18 -8
  281. package/dist/cli-delegate/cmd_settings_migrate.js +10 -10
  282. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  283. package/dist/cli-delegate/cmd_sync.js +10 -10
  284. package/dist/cli-delegate/cmd_uninstall.js +10 -10
  285. package/dist/cli-delegate/cmd_update.js +7 -7
  286. package/dist/cli-delegate/cmd_upgrade.js +7 -7
  287. package/dist/cli-delegate/cmd_validate.js +10 -10
  288. package/dist/cli-delegate/cmd_versions.js +5 -5
  289. package/dist/cli-delegate/gate_budget.js +1 -1
  290. package/dist/cli-delegate/gate_execute.js +2 -2
  291. package/dist/cli-delegate/roadmap_gates.js +2 -2
  292. package/dist/discovery/deprecation-report.md +1 -1
  293. package/dist/discovery/discovery-manifest.json +171 -107
  294. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  295. package/dist/discovery/discovery-manifest.summary.md +7 -7
  296. package/dist/discovery/dormancy-report.md +1 -1
  297. package/dist/discovery/orphan-report.md +1 -1
  298. package/dist/discovery/packs.json +41 -22
  299. package/dist/discovery/trust-report.md +3 -3
  300. package/dist/discovery/workspaces.json +16 -11
  301. package/dist/hooks/dispatch.js +2520 -1924
  302. package/dist/hooks/dispatch.sha256 +1 -1
  303. package/dist/hooks/svg_rules.chunk.cjs +1626 -0
  304. package/dist/hooks/svg_rules.chunk.cjs.sha256 +1 -0
  305. package/dist/install/install.mjs +462 -431
  306. package/dist/install/installThinLayer.js +40 -12
  307. package/dist/install/installedRuleLinks.js +105 -19
  308. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  309. package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
  310. package/dist/mcp/registry-manifest.json +5 -5
  311. package/dist/mcp/server.json +2 -2
  312. package/dist/mcp/server.mjs +3063 -2160
  313. package/dist/router.json +1 -1
  314. package/dist/scripts/_lib/agent_settings.js +15 -120
  315. package/dist/scripts/_lib/compile_time_toggles.js +12 -21
  316. package/dist/scripts/_lib/git_convention.js +232 -0
  317. package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
  318. package/dist/scripts/_lib/installed_layer.js +344 -18
  319. package/dist/scripts/_lib/package_root.js +68 -0
  320. package/dist/scripts/_lib/rule_law_section.js +1 -1
  321. package/dist/scripts/_lib/scan_scope.js +105 -0
  322. package/dist/scripts/_lib/settings_removed_keys.js +168 -0
  323. package/dist/scripts/_lib/svg_dom.js +284 -0
  324. package/dist/scripts/_lib/svg_geometry.js +477 -0
  325. package/dist/scripts/_lib/svg_motion.js +179 -0
  326. package/dist/scripts/_lib/svg_rules.js +1337 -0
  327. package/dist/scripts/_lib/svg_urls.js +67 -0
  328. package/dist/scripts/_lib/thin_rules.js +80 -3
  329. package/dist/scripts/lint_svg_asset.js +324 -0
  330. package/dist/server/app.js +2 -1
  331. package/dist/server/gitKeysGate.js +105 -0
  332. package/dist/server/routes/settings.js +67 -8
  333. package/dist/server/routes/wizard.js +4 -0
  334. package/dist/server/schemas/settings.js +23 -81
  335. package/dist/shared/skillRanking.js +30 -4
  336. package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
  337. package/dist/ui/index.html +1 -1
  338. package/docs/MIGRATION.md +72 -1
  339. package/docs/contracts/settings-classes.md +35 -65
  340. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
  341. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
  342. package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
  343. package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
  344. package/docs/guidelines/agent-infra/layered-settings.md +13 -1
  345. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  346. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
  347. package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
  348. package/docs/guidelines/design-antipatterns.md +1 -1
  349. package/docs/guidelines/php/git.md +15 -2
  350. package/package.json +4 -3
  351. package/src/agent-src/scripts/gate_budget.ts +47 -23
  352. package/src/agent-src/scripts/gate_execute.ts +12 -4
  353. package/src/agent-src/templates/AGENTS.md +1 -1
  354. package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
  355. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
  356. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
  357. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
  358. package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
  359. package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
  360. package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
  361. package/src/config/agent-settings.template.yml +79 -312
  362. package/src/config/agents-paths.yml +16 -0
  363. package/src/config/assurance-capability-registry.json +7 -3
  364. package/src/config/ci-local-parity.yml +3 -0
  365. package/src/config/continuity-surface.json +3 -3
  366. package/src/config/discovery/packs.yml +17 -1
  367. package/src/config/evaluator-budgets.json +19 -6
  368. package/src/config/gate-coverage.yml +65 -23
  369. package/src/config/gate-violation-baselines.json +56 -16
  370. package/src/config/gitignore-block.txt +19 -0
  371. package/src/config/hook-bundle-budget.json +1 -1
  372. package/src/config/release-gate-locality.yml +2 -1
  373. package/src/config/routing-coverage-seed.json +55 -6
  374. package/src/config/rule-law-ceilings.json +12 -6
  375. package/src/config/rule-obligations.json +833 -0
  376. package/src/install/installThinLayer.ts +76 -10
  377. package/src/install/installedRuleLinks.ts +105 -17
  378. package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
  379. package/src/scripts/_cli/cmd_conformance.ts +13 -2
  380. package/src/scripts/_cli/cmd_doctor.ts +10 -10
  381. package/src/scripts/_cli/cmd_doctor_neighbours.ts +133 -0
  382. package/src/scripts/_cli/cmd_explain.ts +72 -14
  383. package/src/scripts/_cli/cmd_git_convention.ts +637 -0
  384. package/src/scripts/_cli/cmd_settings_check.ts +66 -8
  385. package/src/scripts/_cli/cmd_settings_get.ts +18 -3
  386. package/src/scripts/_cli/doctor_execution.ts +72 -1
  387. package/src/scripts/_dispatch.bash +37 -2
  388. package/src/scripts/_lib/agent_settings.ts +15 -124
  389. package/src/scripts/_lib/blocked_by_marker.ts +1 -1
  390. package/src/scripts/_lib/compile_time_toggles.ts +12 -21
  391. package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
  392. package/src/scripts/_lib/git_base_ref.ts +92 -0
  393. package/src/scripts/_lib/git_convention.ts +291 -0
  394. package/src/scripts/_lib/git_convention_carrier.ts +389 -0
  395. package/src/scripts/_lib/git_convention_grammar.ts +246 -0
  396. package/src/scripts/_lib/git_convention_measure.ts +344 -0
  397. package/src/scripts/_lib/global_deploy_inventory.ts +74 -0
  398. package/src/scripts/_lib/graph_feeder_record.ts +236 -13
  399. package/src/scripts/_lib/installed_layer.ts +448 -23
  400. package/src/scripts/_lib/knowledge_global.ts +14 -1
  401. package/src/scripts/_lib/module_reach.ts +456 -0
  402. package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
  403. package/src/scripts/_lib/neighbour_tool_use.ts +8 -10
  404. package/src/scripts/_lib/ratification_artifact.ts +220 -1
  405. package/src/scripts/_lib/ratification_subject.ts +92 -0
  406. package/src/scripts/_lib/release_findings_ingest.ts +15 -7
  407. package/src/scripts/_lib/release_highlights.ts +5 -0
  408. package/src/scripts/_lib/rule_law_section.ts +1 -1
  409. package/src/scripts/_lib/rule_obligations.ts +205 -0
  410. package/src/scripts/_lib/self_repair.ts +1 -1
  411. package/src/scripts/_lib/settings_removed_keys.ts +173 -0
  412. package/src/scripts/_lib/svg_dom.ts +318 -0
  413. package/src/scripts/_lib/svg_geometry.ts +480 -0
  414. package/src/scripts/_lib/svg_motion.ts +208 -0
  415. package/src/scripts/_lib/svg_rules.ts +1453 -0
  416. package/src/scripts/_lib/svg_urls.ts +72 -0
  417. package/src/scripts/_lib/test_red_state.ts +5 -0
  418. package/src/scripts/_lib/thin_rules.ts +83 -3
  419. package/src/scripts/_lib/touched_file_quality.ts +142 -6
  420. package/src/scripts/_lib/verification_command.ts +59 -0
  421. package/src/scripts/_lib/verify_clause.ts +27 -17
  422. package/src/scripts/_lib/worker_budget.ts +1 -1
  423. package/src/scripts/adr/regenerate_index.ts +16 -1
  424. package/src/scripts/ai_council/config.ts +7 -7
  425. package/src/scripts/ai_council/modification_review.ts +100 -0
  426. package/src/scripts/ai_council/modification_review_questions.ts +90 -0
  427. package/src/scripts/ai_council/orchestrator.ts +5 -5
  428. package/src/scripts/ai_council/spend_gate.ts +18 -7
  429. package/src/scripts/ai_team/config.ts +11 -16
  430. package/src/scripts/audit_user_type_axis.ts +20 -8
  431. package/src/scripts/bench_ab_clone.ts +1 -2
  432. package/src/scripts/bench_concern_spawn_path.ts +129 -0
  433. package/src/scripts/bench_graph_feeder_latency.ts +584 -0
  434. package/src/scripts/check_agent_artifact_location.ts +36 -0
  435. package/src/scripts/check_branch_freshness.ts +147 -24
  436. package/src/scripts/check_enforcement_coverage.ts +130 -0
  437. package/src/scripts/check_evaluator_budgets.ts +4 -0
  438. package/src/scripts/check_finding_dispositions.ts +88 -25
  439. package/src/scripts/check_gate_coverage.ts +4 -3
  440. package/src/scripts/check_gate_reachability.ts +48 -1
  441. package/src/scripts/check_generator_sync.ts +48 -0
  442. package/src/scripts/check_hook_bundle_composition.ts +107 -0
  443. package/src/scripts/check_kernel_edit_ratified.ts +86 -6
  444. package/src/scripts/check_pack_size.ts +1 -1
  445. package/src/scripts/check_release_manifest_content.ts +189 -0
  446. package/src/scripts/check_standing_rule_delivery.ts +96 -0
  447. package/src/scripts/check_suppression_hygiene.ts +2 -5
  448. package/src/scripts/ci_settle.ts +42 -5
  449. package/src/scripts/classify_merge_risk.ts +320 -0
  450. package/src/scripts/closure_scan.ts +91 -1
  451. package/src/scripts/command_suggester/settings.ts +8 -108
  452. package/src/scripts/command_suggester/types.ts +4 -5
  453. package/src/scripts/consumer_matrix.ts +102 -0
  454. package/src/scripts/context_hygiene_hook.ts +13 -23
  455. package/src/scripts/cost/preflight.mjs +12 -1
  456. package/src/scripts/council_cli.ts +7 -7
  457. package/src/scripts/dispatch_r2_reviewer.ts +24 -11
  458. package/src/scripts/explain_run.ts +8 -13
  459. package/src/scripts/generate_git_convention_grammar.ts +127 -0
  460. package/src/scripts/generate_host_cost_table.ts +20 -3
  461. package/src/scripts/handoff_context_hook.ts +86 -37
  462. package/src/scripts/hook_manifest.json +1 -1
  463. package/src/scripts/hook_manifest.yaml +25 -6
  464. package/src/scripts/hooks/block_config_weakening.ts +48 -5
  465. package/src/scripts/hooks/block_plumbing_writes.ts +8 -4
  466. package/src/scripts/hooks/block_speaking_inbox_dir.ts +16 -1
  467. package/src/scripts/hooks/concern_registry.ts +2 -0
  468. package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
  469. package/src/scripts/hooks/design_pass_hook.ts +157 -3
  470. package/src/scripts/hooks/evidence_independence.ts +12 -4
  471. package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
  472. package/src/scripts/hooks/push_settle_hook.ts +4 -3
  473. package/src/scripts/hooks/rule_inject_hook.ts +44 -3
  474. package/src/scripts/hooks/state_io.ts +8 -4
  475. package/src/scripts/hooks/svg_obligation.ts +497 -0
  476. package/src/scripts/hooks/svg_rules_loader.ts +133 -0
  477. package/src/scripts/hooks/telemetry_usage_hook.ts +7 -46
  478. package/src/scripts/hooks/turn_end_gate_hook.ts +6 -3
  479. package/src/scripts/install-hooks.sh +42 -14
  480. package/src/scripts/install.ts +7 -31
  481. package/src/scripts/lint_blocker_citations.ts +198 -0
  482. package/src/scripts/lint_consolidation_lineage.ts +6 -3
  483. package/src/scripts/lint_design_quality.ts +60 -12
  484. package/src/scripts/lint_evidence_artifacts.ts +9 -0
  485. package/src/scripts/lint_hook_concern_budget.ts +10 -21
  486. package/src/scripts/lint_motion_authority_drift.ts +27 -0
  487. package/src/scripts/lint_pack_boundaries.ts +406 -28
  488. package/src/scripts/lint_roadmap_blockers.ts +282 -29
  489. package/src/scripts/lint_roadmap_complexity.ts +4 -2
  490. package/src/scripts/lint_rule_law_section.ts +151 -21
  491. package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
  492. package/src/scripts/lint_settings_classes.ts +1 -1
  493. package/src/scripts/lint_svg_asset.ts +365 -0
  494. package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
  495. package/src/scripts/lint_workflow_security.ts +12 -1
  496. package/src/scripts/measure_skill_ranker_baseline.ts +5 -0
  497. package/src/scripts/model_rule_injection.ts +3 -3
  498. package/src/scripts/onboarding_gate_hook.ts +7 -6
  499. package/src/scripts/print_required_checks.ts +15 -3
  500. package/src/scripts/probe_host_compliance.ts +33 -5
  501. package/src/scripts/probe_turnaround.ts +183 -3
  502. package/src/scripts/ratification_header.ts +77 -0
  503. package/src/scripts/refusal_envelope.ts +138 -0
  504. package/src/scripts/release_publication.ts +29 -3
  505. package/src/scripts/report_dispatch_import_closure.ts +76 -0
  506. package/src/scripts/report_installed_rule_links.ts +118 -1
  507. package/src/scripts/report_module_reach.ts +212 -0
  508. package/src/scripts/report_obligation_mechanism.ts +16 -18
  509. package/src/scripts/report_parked_blockers.ts +222 -0
  510. package/src/scripts/report_required_checks_drift.ts +196 -0
  511. package/src/scripts/report_skill_ranker_confusion.ts +103 -2
  512. package/src/scripts/report_standing_payload_by_host.ts +1 -1
  513. package/src/scripts/routing_doctor.ts +2 -2
  514. package/src/scripts/schemas/agent-settings.schema.json +17 -31
  515. package/src/scripts/schemas/command.schema.json +1 -1
  516. package/src/scripts/schemas/skill.schema.json +1 -1
  517. package/src/scripts/self_review_gate.ts +39 -2
  518. package/src/scripts/skill_tools/score_skill_relevance.ts +28 -19
  519. package/src/scripts/sync_pr_branch.ts +403 -132
  520. package/src/scripts/trigger_eval_grandfather.json +0 -16
  521. package/src/server/schemas/settings.ts +33 -177
  522. package/src/shared/skillRanking.ts +53 -4
  523. package/dist/cli-delegate/chunk-NYX3L2PZ.js +0 -99
  524. package/src/scripts/_lib/config_chain.ts +0 -477
  525. package/src/scripts/_lib/conformance_report.ts +0 -403
  526. package/src/scripts/_lib/eval_discrimination.ts +0 -95
  527. package/src/scripts/_lib/evaluator_promotion.ts +0 -225
  528. package/src/scripts/_lib/file_slicer.ts +0 -117
  529. package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
  530. package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
  531. package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
  532. package/src/scripts/_lib/role_split.ts +0 -338
@@ -40,11 +40,14 @@
40
40
  * {@link InstalledLayerReport.manifest_present} is in the output.
41
41
  */
42
42
  import * as fs from 'node:fs';
43
+ import * as os from 'node:os';
43
44
  import * as path from 'node:path';
44
45
  import { fileURLToPath } from 'node:url';
45
46
  import { GLOBAL_RULE_DIRS, PROJECT_RULE_DIRS, globalRuleLayerPath, } from '../../install/globalRuleLayers.js';
46
47
  import { NO_RECORDED_HASHES, readRecordedHashes } from '../../install/recordedOwnership.js';
47
- import { ruleBody } from './rule_law_section.js';
48
+ import { INVENTORY_BASENAME, inventory_path, load_inventory, recorded_absolute_files, } from './global_deploy_inventory.js';
49
+ import { DEFAULT_EVENT4U_ROOT_RELATIVE } from './user_global_paths.js';
50
+ import { ruleBody, splitFrontmatter } from './rule_law_section.js';
48
51
  /** How many files the per-directory ranking carries. Step 0.1 fixes it at 20. */
49
52
  export const TOP_FILES = 20;
50
53
  /**
@@ -58,12 +61,33 @@ export const LIMIT_WARN_FRACTION = 0.8;
58
61
  /**
59
62
  * Whether the host loads this file on every session rather than on a path match.
60
63
  *
61
- * `paths:` at the start of a line is the whole test, and it is the host's own
62
- * convention rather than this package's: `claudeRuleRewrite.ts` states it from
63
- * the emitting side — "with it a rule loads on a path match, without it the
64
- * rule loads unconditionally". `rule_activation_census.projection_reading` asks
65
- * the installed tree the same question with the same regex, so a file counted
66
- * unconditional here is counted unconditional there.
64
+ * `paths:` at the start of a line IN THE FRONTMATTER is the whole test, and it
65
+ * is the host's own convention rather than this package's: `claudeRuleRewrite`
66
+ * states it from the emitting side — "with it a rule loads on a path match,
67
+ * without it the rule loads unconditionally".
68
+ *
69
+ * SCOPED TO THE FRONTMATTER, which it was not until 2026-10-06. The predicate
70
+ * tested the whole unstripped file while the characters it partitions come
71
+ * from `ruleBody` — predicate and measure read different strings. That was
72
+ * survivable while it only moved a file COUNT; since the when-loaded character
73
+ * split it decides which bucket a whole file's characters land in, and
74
+ * `unconditional_chars` is the figure the ceiling argument is read in. One body
75
+ * line beginning `paths:` — a YAML trigger example quoted inside a kernel or
76
+ * `no_stub` rule, which is ordinary prose — would have moved that rule's entire
77
+ * character count from standing to path-scoped, silently.
78
+ *
79
+ * Measured before the change: of the 105 files on a real opted-in install, two
80
+ * are path-scoped by either reading and none carries a body-only mention, so
81
+ * the fix moves no published figure. It is hardening against a case that does
82
+ * not exist yet rather than a correction of one that does, and it is worth
83
+ * making precisely because the failure would be silent.
84
+ *
85
+ * `check_single_delivery` already slices the frontmatter before this test, so
86
+ * this now AGREES with it. `rule_activation_census.projection_reading` still
87
+ * scans the whole file; that reader counts files rather than characters, so the
88
+ * divergence costs it nothing today, and changing a census's reading belongs to
89
+ * that census rather than here. Named so the next reader finds a known
90
+ * difference instead of discovering an unexplained one.
67
91
  *
68
92
  * SOURCE-SIDE CLASSIFICATION IS A DIFFERENT QUESTION and deliberately not used.
69
93
  * `classify_rule` reads `alwaysApply` and `type: always` to tell `always` from
@@ -73,7 +97,48 @@ export const LIMIT_WARN_FRACTION = 0.8;
73
97
  * both load every session, which is the only property the host acts on.
74
98
  */
75
99
  export function isUnconditional(text) {
76
- return !/^paths:/m.test(text);
100
+ // THE SAME FUNCTION that produces the characters being partitioned, not the
101
+ // same arithmetic re-implemented. An earlier version scanned for
102
+ // `'\n---'` while `ruleBody` -> `splitFrontmatter` scans for `'\n---\n'`,
103
+ // so the two disagreed about where the frontmatter stops, and the
104
+ // predicate's early stop could hide a later `paths:` key.
105
+ const [frontmatter] = splitFrontmatter(text);
106
+ // AND THE BLIND SPOT THAT SCOPING OPENS, CLOSED HERE. `splitFrontmatter`
107
+ // returns `''` for anything that does not begin exactly `---\n` — a BOM, a
108
+ // leading blank line, CRLF, a block that never closes — and scoping to `''`
109
+ // reads every such file as unconditional even when its frontmatter carries
110
+ // `paths:`. `readLayer` measures every `.md` in a host directory, foreign
111
+ // user-authored rules included, so that would inflate `unconditional_chars`
112
+ // silently, which is the figure the whole ceiling argument is read in.
113
+ //
114
+ // A file that CLAIMS a block `splitFrontmatter` could not parse falls back
115
+ // to the whole-text test. The claim test is the one `installThinLayer`
116
+ // already uses for the same blind spot, and is a regex rather than
117
+ // `startsWith('---')` for the reason stated there.
118
+ //
119
+ // WHICH WAY THE FALLBACK ERRS, SAID PLAINLY RATHER THAN CALLED
120
+ // "CONSERVATIVE". It can only move characters OUT of the standing bucket,
121
+ // never into it. That is conservative about the claim "this rule stands
122
+ // every session" — it never asserts one it cannot support. It is the
123
+ // FLATTERING direction for the ceiling argument, which reads the standing
124
+ // figure against a limit: a misfire here makes the layer look smaller than
125
+ // it is. Both halves are true and the second is the one a reader checking
126
+ // the overage needs, so it is stated rather than left to the word.
127
+ //
128
+ // AND THE STATE THAT IS NOT AN UNPARSEABLE BLOCK. `splitFrontmatter`
129
+ // returns '' for a WELL-FORMED EMPTY block too — `---\n---\n` has no
130
+ // `\n---\n` after index 4, so its own parse fails — and such a file has no
131
+ // `paths:` key by construction. Without the second test it fell to the
132
+ // whole-text scan and a body line beginning `paths:` (a quoted trigger
133
+ // example, which is exactly what a rule about triggers contains) moved the
134
+ // file's entire character count out of the standing figure. Measured
135
+ // before the exclusion: `---\n---\npaths: not a key, just prose\n` read
136
+ // path-scoped.
137
+ const emptyBlock = /^---\n---(\n|$)/.test(text);
138
+ if (frontmatter === '' && !emptyBlock && /^\uFEFF?[\s]*---/.test(text)) {
139
+ return !/^paths:/m.test(text);
140
+ }
141
+ return !/^paths:/m.test(frontmatter);
77
142
  }
78
143
  /**
79
144
  * Measure one directory.
@@ -94,6 +159,8 @@ export function readLayer(host, scope, dir, recorded) {
94
159
  foreign: 0,
95
160
  package_owned_chars: 0,
96
161
  foreign_chars: 0,
162
+ unconditional_chars: 0,
163
+ scoped_chars: 0,
97
164
  top: [],
98
165
  };
99
166
  let names;
@@ -153,6 +220,8 @@ export function readLayer(host, scope, dir, recorded) {
153
220
  foreign: readings.filter((r) => !r.package_owned).length,
154
221
  package_owned_chars: readings.reduce((n, r) => n + (r.package_owned ? r.chars : 0), 0),
155
222
  foreign_chars: readings.reduce((n, r) => n + (r.package_owned ? 0 : r.chars), 0),
223
+ unconditional_chars: readings.reduce((n, r) => n + (r.unconditional ? r.chars : 0), 0),
224
+ scoped_chars: readings.reduce((n, r) => n + (r.unconditional ? 0 : r.chars), 0),
156
225
  top,
157
226
  };
158
227
  }
@@ -266,6 +335,227 @@ export function buildHostLimitRows(layers, limits) {
266
335
  }
267
336
  return rows;
268
337
  }
338
+ /**
339
+ * Where `deployed-files.json` lives FOR A STATED HOME.
340
+ *
341
+ * `global_deploy_inventory.inventory_path()` answers for the process asking,
342
+ * honouring `AGENT_CONFIG_DEPLOY_INVENTORY` and `EVENT4U_CONFIG_HOME`. That is
343
+ * right for the installer, which is running the install it is recording, and
344
+ * WRONG for a reader measuring some other home: a report about a staged install
345
+ * under a temporary HOME would read the maintainer's own inventory and claim
346
+ * ownership of files that install never wrote.
347
+ *
348
+ * SO AN EXPLICITLY SUPPLIED HOME WINS, AND THE ENV OVERRIDES DECIDE ONLY THE
349
+ * HOME-LESS CASE. A caller that says which home it is measuring has already
350
+ * answered the question those variables would answer for it, and letting the
351
+ * asking process's environment override that is exactly the hazard named above.
352
+ * An intermediate draft of this function had the precedence the other way round
353
+ * while this paragraph said what it says now; the code is what changed.
354
+ *
355
+ * THE CONSEQUENCE, STATED RATHER THAN IMPLIED: `ReportOptions.home` is
356
+ * REQUIRED, so every caller of {@link buildInstalledLayerReport} supplies one
357
+ * and neither env override reaches this reader through that path. That is
358
+ * correct — those variables describe the asking process's own install, which is
359
+ * not what a report about another home is measuring — but it means "honours the
360
+ * env override" is not a true sentence about the report, and saying so here is
361
+ * cheaper than leaving the next reader to discover it.
362
+ *
363
+ * The layout is COMPOSED from the modules that own it rather than re-spelled,
364
+ * so a layout change moves this reader with it instead of leaving it looking in
365
+ * a directory nobody writes.
366
+ */
367
+ export function defaultInventoryPath(home) {
368
+ // NO HOME STATED — the asking process is the subject, so the env-aware
369
+ // resolver is the right answer.
370
+ if (home === undefined || home === null || home === '')
371
+ return inventory_path();
372
+ // THE STATED HOME IS THE ASKING PROCESS'S OWN. `ReportOptions.home` is
373
+ // required, so `buildInstalledLayerReport` ALWAYS states one — including
374
+ // when it is simply measuring the machine it is running on. Treating that
375
+ // as "a different home" sent it to the hardcoded layout, found nothing
376
+ // under `EVENT4U_CONFIG_HOME` or `AGENT_CONFIG_DEPLOY_INVENTORY`, and
377
+ // printed "NO manifest and NO deploy inventory resolved" — the
378
+ // `0 package-owned` reading step 1.2 exists to remove, and it put the
379
+ // report and the receipt in disagreement about one install.
380
+ //
381
+ // The precedence argument only ever covered a home that is NOT this
382
+ // process's, so that is the case the branch now tests for.
383
+ try {
384
+ // BOTH SPELLINGS, for the reason `recorded_absolute_files` states at
385
+ // length: a lexical compare alone calls a symlinked or alternate
386
+ // spelling of the SAME home "a different home", takes the hardcoded
387
+ // layout, and reproduces the 0 package-owned reading this branch
388
+ // exists to prevent. macOS `/var` -> `/private/var` is the standing
389
+ // example.
390
+ const real = (q) => {
391
+ try {
392
+ return fs.realpathSync(q);
393
+ }
394
+ catch {
395
+ return path.resolve(q);
396
+ }
397
+ };
398
+ const mine = os.homedir();
399
+ if (path.resolve(home) === path.resolve(mine) || real(home) === real(mine)) {
400
+ return inventory_path();
401
+ }
402
+ }
403
+ catch {
404
+ // `homedir()` can throw on an exotic environment; fall through to the
405
+ // layout, which is the answer for a home that is not ours anyway.
406
+ }
407
+ return path.join(home, DEFAULT_EVENT4U_ROOT_RELATIVE, INVENTORY_BASENAME);
408
+ }
409
+ /**
410
+ * The ONE place that answers "which of these installed files are ours".
411
+ *
412
+ * Two readers ask it — the installed-layer report and the install receipt's
413
+ * budget lines — and until step 1.2 they asked different things. Both called
414
+ * `manifest_path(...)` and read a project-scoped `agents/installed-tools.lock`.
415
+ * A global-only install writes nothing into a project, so on exactly the
416
+ * install shape the thinned-layer roadmap's AC-1 is about, both reported
417
+ * `0 package-owned / N foreign` and AC-1's figure could not be produced at all.
418
+ *
419
+ * The global install DOES record what it wrote: `deployed-files.json` lists,
420
+ * per tool, the anchor and every file the installer maintains there —
421
+ * "the ownership proof that makes reaping safe". This reads it.
422
+ *
423
+ * IT IS A UNION, AND THE PATH SPACES ARE WHY. An earlier shape let the project
424
+ * manifest win OUTRIGHT whenever its file existed, on the reasoning that it
425
+ * carries hashes an inventory does not. That reasoning does not survive the
426
+ * observation that the two sources describe DISJOINT sets of paths: the
427
+ * manifest records project-relative entries resolved against `projectRoot`,
428
+ * the inventory records paths under the user's home. Exclusivity therefore
429
+ * bought nothing and cost everything — any `agents/installed-tools.lock` on
430
+ * disk, a gitignored file a single project-scoped install creates, restored
431
+ * the exact `0 package-owned` reading for the global layer that step 1.2
432
+ * exists to fix, while the report printed "read from the installed-tools
433
+ * manifest" with no evidence behind it for those paths.
434
+ *
435
+ * So every source contributes, and the MANIFEST IS MERGED LAST: on the paths
436
+ * it names its hash overwrites a hashless entry, which preserves the
437
+ * `recorded-unchanged` / `recorded-modified` distinction that motivated the
438
+ * precedence in the first place, without discarding evidence about paths it
439
+ * says nothing about.
440
+ *
441
+ * {@link OwnershipResolution.sources} lists every source that contributed at
442
+ * least one path, because one word cannot describe two path spaces — a report
443
+ * reading global and project layers together may have manifest evidence for
444
+ * one and inventory evidence for the other, and saying only one of them is
445
+ * wrong about half the files it is describing.
446
+ *
447
+ * A FILE THE USER EDITED AND THE INSTALLER PRESERVED STILL COUNTS AS
448
+ * MAINTAINED. The inventory records what the installer MAINTAINS at a path, not
449
+ * what the bytes currently are, so a preserved rule stays package-owned. That
450
+ * is the correct reading — the package is still responsible for the path — and
451
+ * it is why this function returns `null` hashes rather than fabricating one.
452
+ */
453
+ export function resolveLayerOwnership(opts) {
454
+ const recorded = new Map();
455
+ const sources = [];
456
+ // HASHLESS SOURCES FIRST, so the manifest's hashes overwrite a `null` on
457
+ // any path both happen to name rather than the other way round.
458
+ if (opts.inventoryPath !== null) {
459
+ let n = 0;
460
+ try {
461
+ const inv = load_inventory(opts.inventoryPath ?? defaultInventoryPath(opts.home));
462
+ for (const abs of recorded_absolute_files(inv, opts.home ?? null)) {
463
+ // COUNT PATHS NEWLY CONTRIBUTED, not entries read, so a
464
+ // source that added nothing is not named as evidence.
465
+ //
466
+ // WHAT THIS GUARD DOES NOT DO, since an earlier comment here
467
+ // claimed it did: it does not collapse the two spellings
468
+ // `recorded_absolute_files` emits per file. The lexical and the
469
+ // realpath spelling are distinct Map keys, so wherever they
470
+ // differ — every path under macOS `/var` — each is newly
471
+ // contributed and `n` still rises twice for one file. Harmless
472
+ // only because `n` is read as `> 0` and never as a count; a
473
+ // future reader wanting a count must not take it from here.
474
+ if (!recorded.has(abs))
475
+ n += 1;
476
+ recorded.set(abs, null);
477
+ }
478
+ }
479
+ catch {
480
+ // Defence in depth, and NOT where the documented behaviour lives.
481
+ // "A corrupt inventory is no evidence, never a failure" is real but
482
+ // is carried one module down: `load_inventory` swallows its own
483
+ // read and parse failures and returns an empty shell, and
484
+ // `recorded_absolute_files` type-guards every field. So this catch
485
+ // is unreachable for that reason and the fixture case named for it
486
+ // exercises `load_inventory`, not this guard. Kept because a future
487
+ // change to either could make it reachable; labelled so it is not
488
+ // mistaken for tested coverage.
489
+ }
490
+ if (n > 0)
491
+ sources.push('global-inventory');
492
+ }
493
+ let fromDeploy = 0;
494
+ for (const [anchor, rels] of opts.thisDeploy ?? new Map()) {
495
+ for (const rel of rels) {
496
+ const abs = path.resolve(anchor, rel);
497
+ // Same rule as the inventory branch: a source is named only when it
498
+ // contributes a path nothing else had. On every install after the
499
+ // first, `thisDeploy`'s paths are already in the inventory, and
500
+ // naming it there printed "the file set this install just wrote"
501
+ // for a source that added nothing.
502
+ if (!recorded.has(abs))
503
+ fromDeploy += 1;
504
+ recorded.set(abs, null);
505
+ // Both spellings, for the reason `recorded_absolute_files` states:
506
+ // the reader compares against a lexically joined path, and a HOME
507
+ // behind a symlink makes the two disagree.
508
+ try {
509
+ recorded.set(fs.realpathSync(abs), null);
510
+ }
511
+ catch {
512
+ // Not on disk — the lexical spelling is all there is.
513
+ }
514
+ }
515
+ }
516
+ if (fromDeploy > 0)
517
+ sources.push('this-deploy');
518
+ const manifestPath = opts.manifestPath ?? null;
519
+ if (manifestPath !== null && fs.existsSync(manifestPath)) {
520
+ let n = 0;
521
+ // DEGRADE LIKE THE INVENTORY BRANCH, which this function documents
522
+ // twice. The code this replaced wrapped exactly this call, so a corrupt
523
+ // `installed-tools.lock` used to read as "no manifest evidence" and the
524
+ // budget block still printed. Unguarded it threw into the receipt's
525
+ // outer catch and suppressed the WHOLE block — ownership line, per-host
526
+ // rows and the 80 % warning — and out of `buildInstalledLayerReport`,
527
+ // which has no outer catch at all. An unreadable manifest is no
528
+ // evidence, never a failure; the asymmetry was undeclared.
529
+ let recordedHashes;
530
+ try {
531
+ recordedHashes = readRecordedHashes(manifestPath, opts.projectRoot);
532
+ }
533
+ catch {
534
+ recordedHashes = NO_RECORDED_HASHES;
535
+ }
536
+ for (const [abs, hash] of recordedHashes) {
537
+ // The manifest contributes whenever it names a path at all: it
538
+ // supplies the HASH even where another source already had the path,
539
+ // which is evidence the others cannot give.
540
+ n += 1;
541
+ recorded.set(abs, hash);
542
+ }
543
+ if (n > 0)
544
+ sources.push('manifest');
545
+ }
546
+ if (recorded.size === 0) {
547
+ return { recorded: NO_RECORDED_HASHES, sources: [], source: 'none' };
548
+ }
549
+ // `source` keeps a single word for readers that want one. The manifest is
550
+ // the most specific evidence, so it leads when it contributed; otherwise
551
+ // the inventory, otherwise this deploy's own set.
552
+ const primary = sources.includes('manifest')
553
+ ? 'manifest'
554
+ : sources.includes('global-inventory')
555
+ ? 'global-inventory'
556
+ : 'this-deploy';
557
+ return { recorded, sources, source: primary };
558
+ }
269
559
  /**
270
560
  * Read every host rule directory in both scopes.
271
561
  *
@@ -276,9 +566,12 @@ export function buildHostLimitRows(layers, limits) {
276
566
  */
277
567
  export function buildInstalledLayerReport(opts) {
278
568
  const manifestPath = opts.manifestPath ?? null;
279
- const recorded = manifestPath === null
280
- ? NO_RECORDED_HASHES
281
- : readRecordedHashes(manifestPath, opts.projectRoot);
569
+ const { recorded, source: ownership_source, sources: ownership_sources } = resolveLayerOwnership({
570
+ manifestPath,
571
+ projectRoot: opts.projectRoot,
572
+ home: opts.home,
573
+ inventoryPath: opts.inventoryPath,
574
+ });
282
575
  const layers = [];
283
576
  for (const host of Object.keys(GLOBAL_RULE_DIRS).sort()) {
284
577
  const dir = globalRuleLayerPath(host, opts.home);
@@ -298,6 +591,8 @@ export function buildInstalledLayerReport(opts) {
298
591
  foreign: acc.foreign + l.foreign,
299
592
  package_owned_chars: acc.package_owned_chars + l.package_owned_chars,
300
593
  foreign_chars: acc.foreign_chars + l.foreign_chars,
594
+ unconditional_chars: acc.unconditional_chars + l.unconditional_chars,
595
+ scoped_chars: acc.scoped_chars + l.scoped_chars,
301
596
  }), {
302
597
  files: 0,
303
598
  unconditional: 0,
@@ -306,6 +601,8 @@ export function buildInstalledLayerReport(opts) {
306
601
  foreign: 0,
307
602
  package_owned_chars: 0,
308
603
  foreign_chars: 0,
604
+ unconditional_chars: 0,
605
+ scoped_chars: 0,
309
606
  });
310
607
  const limits = buildHostLimitRows(layers, loadHostInstructionLimits(opts.hostLimitsPath ?? defaultHostLimitsPath()));
311
608
  return {
@@ -313,34 +610,63 @@ export function buildInstalledLayerReport(opts) {
313
610
  project_root: opts.projectRoot,
314
611
  host_version: opts.hostVersion ?? 'unknown',
315
612
  manifest_present: manifestPath !== null && fs.existsSync(manifestPath),
613
+ ownership_source,
614
+ ownership_sources,
316
615
  layers,
317
616
  limits,
318
617
  totals,
319
618
  };
320
619
  }
620
+ /**
621
+ * One line naming WHICH evidence answered ownership.
622
+ *
623
+ * Exported so the report and the install receipt say the same sentence about
624
+ * the same source — a consumer comparing a receipt against a later report must
625
+ * not have to decide whether a wording difference means a different reading.
626
+ */
627
+ export function ownershipLine(sources) {
628
+ const list = (Array.isArray(sources) ? sources : [sources]).filter((s) => s !== 'none');
629
+ if (list.length === 0) {
630
+ return 'ownership: NO manifest and NO deploy inventory resolved — every file reads foreign, which is no evidence rather than a finding';
631
+ }
632
+ // EVERY contributor is named, never just one. The sources cover disjoint
633
+ // path spaces, so a line naming one of two is a true sentence about half
634
+ // the files and a false one about the rest.
635
+ const phrases = {
636
+ manifest: 'the installed-tools manifest',
637
+ 'global-inventory': 'the global deploy inventory (deployed-files.json)',
638
+ 'this-deploy': 'the file set this install just wrote',
639
+ };
640
+ const named = list.map((s) => phrases[s]);
641
+ const joined = named.length === 1
642
+ ? named[0]
643
+ : `${named.slice(0, -1).join(', ')} and ${named[named.length - 1]}`;
644
+ return `ownership: read from ${joined}`;
645
+ }
321
646
  /** The report as lines, for a terminal. One directory per block. */
322
647
  export function renderInstalledLayerReport(report) {
323
648
  const out = [];
324
649
  out.push(`installed layer — host ${report.host_version}, HOME ${report.home}`);
325
650
  out.push(`project ${report.project_root}`);
326
- out.push(report.manifest_present
327
- ? 'ownership: read from the installed-tools manifest'
328
- : 'ownership: NO manifest resolved — every file reads foreign, which is no evidence rather than a finding');
651
+ out.push(ownershipLine(report.ownership_sources));
329
652
  for (const l of report.layers) {
330
653
  if (!l.present) {
331
654
  out.push(` ${l.host} (${l.scope}) — absent: ${l.dir}`);
332
655
  continue;
333
656
  }
334
- out.push(` ${l.host} (${l.scope}) — ${String(l.files)} files, ` +
335
- `${String(l.unconditional)} unconditional, ${String(l.chars)} chars, ` +
657
+ out.push(` ${l.host} (${l.scope}) — ${String(l.files)} files ` +
658
+ `(${String(l.unconditional)} unconditional), ${String(l.chars)} chars ` +
659
+ `(${String(l.unconditional_chars)} standing + ${String(l.scoped_chars)} path-scoped), ` +
336
660
  `${String(l.package_owned)} package-owned / ${String(l.foreign)} foreign`);
337
661
  for (const f of l.top) {
338
662
  out.push(` ${String(f.chars).padStart(7)} ${f.unconditional ? 'uncond' : 'scoped'} ${f.file}`);
339
663
  }
340
664
  }
341
665
  const t = report.totals;
342
- out.push(` TOTAL — ${String(t.files)} files, ${String(t.unconditional)} unconditional, ` +
343
- `${String(t.chars)} chars, ${String(t.package_owned)} package-owned / ${String(t.foreign)} foreign`);
666
+ out.push(` TOTAL — ${String(t.files)} files (${String(t.unconditional)} unconditional), ` +
667
+ `${String(t.chars)} chars ` +
668
+ `(${String(t.unconditional_chars)} standing + ${String(t.scoped_chars)} path-scoped), ` +
669
+ `${String(t.package_owned)} package-owned / ${String(t.foreign)} foreign`);
344
670
  out.push(...renderHostLimitRows(report.limits));
345
671
  return out;
346
672
  }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Layout-independent package-root resolution for the `_cli` delegate commands.
3
+ *
4
+ * `src/scripts/_cli/*.ts` sits three levels below the package root, so every
5
+ * delegate command derived the root with three hard-coded parent hops. Then the
6
+ * delegate precompile (`fa51c5a54`) started bundling those same modules into
7
+ * `dist/cli-delegate/*.js` — **two** levels below the root. Fixed hop counts are
8
+ * correct in exactly one of the two layouts, and nothing failed loudly: three
9
+ * hops from the bundle lands on `<pkg>/..`, which for a scoped install is
10
+ * `node_modules/@event4u`. `conformance` then reported `dist/router.json` and
11
+ * `src/scripts/hook_manifest.yaml` as missing — files that ship fine — and the
12
+ * 9.11.0 release PR's `tarball E2E` went red on both Node majors.
13
+ *
14
+ * The fix is to stop counting hops. Anchor on the marker that actually defines
15
+ * the root: the `package.json` whose `name` is this package. That answer holds
16
+ * for the source tree, for any bundle depth, for npm-global, npx, and
17
+ * vendor/bin symlink invocations — and it keeps holding if either directory
18
+ * ever moves.
19
+ */
20
+ import * as fs from 'node:fs';
21
+ import * as path from 'node:path';
22
+ import { fileURLToPath } from 'node:url';
23
+ /** `name` in this package's own package.json — the root marker. */
24
+ export const PACKAGE_NAME = '@event4u/agent-config';
25
+ /**
26
+ * Bound on the upward walk. Deep enough for any real install path, finite so a
27
+ * malformed start value can never spin.
28
+ */
29
+ const MAX_ASCENT = 16;
30
+ /**
31
+ * Absolute package root for the module at `fromUrlOrPath` (pass
32
+ * `import.meta.url`).
33
+ *
34
+ * Walks up looking for the package.json whose `name` is {@link PACKAGE_NAME}.
35
+ * A consumer's own package.json encountered on the way is skipped by that name
36
+ * check, so an install nested under another project resolves correctly.
37
+ *
38
+ * `legacyHops` is the pre-existing hop count for the *source* layout and is
39
+ * used only if no marker is found. Falling back beats throwing during CLI
40
+ * startup: a wrong path yields one actionable "missing file" message, while an
41
+ * exception takes down every command in the delegate.
42
+ */
43
+ export function resolvePackageRoot(fromUrlOrPath, legacyHops = 3) {
44
+ const start = fromUrlOrPath.startsWith('file:') ? fileURLToPath(fromUrlOrPath) : fromUrlOrPath;
45
+ const startDir = path.dirname(start);
46
+ let dir = startDir;
47
+ for (let i = 0; i < MAX_ASCENT; i++) {
48
+ const manifest = path.join(dir, 'package.json');
49
+ if (fs.existsSync(manifest)) {
50
+ try {
51
+ const parsed = JSON.parse(fs.readFileSync(manifest, 'utf8'));
52
+ if (parsed.name === PACKAGE_NAME) {
53
+ return dir;
54
+ }
55
+ }
56
+ catch {
57
+ // Unreadable / unparseable manifest — not our marker; keep ascending.
58
+ }
59
+ }
60
+ const parent = path.dirname(dir);
61
+ if (parent === dir) {
62
+ break; // filesystem root
63
+ }
64
+ dir = parent;
65
+ }
66
+ return path.resolve(startDir, ...Array.from({ length: legacyHops }, () => '..'));
67
+ }
68
+ //# sourceMappingURL=package_root.js.map
@@ -25,7 +25,7 @@
25
25
  * law in bold prose or a bare fence under no heading at all — several of the 35
26
26
  * routed rules without a heading match do exactly that. A heading match is a
27
27
  * STRUCTURAL property, checkable; "this sentence is the obligation" is not, and
28
- * `report_obligation_carriers` plus the `# obligation: line N` marker is where
28
+ * `report_obligation_carriers` plus the ids in `src/config/rule-obligations.json` is where
29
29
  * that question already lives.
30
30
  */
31
31
  import * as fs from 'node:fs';
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Scan-scope assertions — "a gate that read nothing did not pass".
3
+ *
4
+ * WHY THIS EXISTS (audit 2026-07-29, `agents/settings/contexts/gates-that-cannot-fail.md`):
5
+ * ADR-051 moved the source container and a later commit deleted `packages/`.
6
+ * Fourteen `lint_*`/`check_*` gates kept a hardcoded literal path, and every
7
+ * one of them treats a missing directory as "nothing to check" — so they exit 0
8
+ * and print a green checkmark while scanning zero files. Their own output said
9
+ * so (`0 file(s) scanned`, `0 name(s) checked`, `0 declarer(s)`); nothing ever
10
+ * asserted on the number.
11
+ *
12
+ * Two shapes cover the whole class:
13
+ *
14
+ * - **Corpus gates** walk a root and check N units → {@link assertScanned}.
15
+ * Zero units is a failure unless the gate declares a justified `allowEmpty`.
16
+ * - **Watch-list gates** (diff-based, no corpus to count) name specific files
17
+ * they guard → {@link assertWatchlistResolves}. A watch list that resolves to
18
+ * nothing on disk means the guard is watching phantoms.
19
+ *
20
+ * Both throw {@link DeadScopeError}; callers map it to a non-zero exit. The
21
+ * point is that the failure is LOUD and names the root, so the next path
22
+ * migration cannot silently disarm a gate again.
23
+ */
24
+ import * as fs from 'node:fs';
25
+ import * as path from 'node:path';
26
+ import process from 'node:process';
27
+ /** Raised when a gate's scope is empty — it cannot have checked anything. */
28
+ export class DeadScopeError extends Error {
29
+ gate;
30
+ constructor(gate, message) {
31
+ super(message);
32
+ this.name = 'DeadScopeError';
33
+ this.gate = gate;
34
+ }
35
+ }
36
+ /**
37
+ * Assert a corpus gate examined at least one unit.
38
+ *
39
+ * @throws {DeadScopeError} when `scanned === 0` and no `allowEmpty` reason is given.
40
+ */
41
+ export function assertScanned(opts) {
42
+ if (opts.scanned > 0) {
43
+ return;
44
+ }
45
+ if (opts.allowEmpty !== undefined && opts.allowEmpty.trim() !== '') {
46
+ return;
47
+ }
48
+ const roots = opts.roots.length > 0 ? opts.roots.join(', ') : '(no root declared)';
49
+ throw new DeadScopeError(opts.gate, `${opts.gate}: scanned 0 ${opts.units} under ${roots} — the scan scope is ` +
50
+ 'dead or the root moved. A gate that read nothing has not passed. ' +
51
+ 'Repoint the root (prefer the shared resolver in _lib/agent_src.ts), ' +
52
+ 'or declare a justified `allowEmpty` reason if an empty corpus is genuinely expected.');
53
+ }
54
+ /**
55
+ * Assert a corpus gate's scope AND publish the count it just validated.
56
+ *
57
+ * The two halves of scope hardening drifted apart in practice: 14 gates emit
58
+ * the machine-readable `scanned:` line the coverage guard reads, 12 assert
59
+ * their scope, and only 8 do both. A gate that publishes a number it never
60
+ * asserted can print `scanned: 0` and exit green; a gate that asserts without
61
+ * publishing is invisible to `check_gate_coverage`. Splitting the two also
62
+ * lets the published number drift from the number that was validated — the
63
+ * invented-count failure that is mechanically undetectable downstream.
64
+ *
65
+ * One call closes both: the emitted number is, by construction, the number the
66
+ * assertion just accepted. Use this for any gate that should also carry a floor
67
+ * in `src/config/gate-coverage.yml`; plain {@link assertScanned} stays correct
68
+ * for a gate that only needs its scope guarded.
69
+ *
70
+ * The line goes to stdout unconditionally — a count only visible without
71
+ * `--quiet` is not a count, since CI passes `--quiet` (the `lint_handoffs`
72
+ * lesson, preserved here so no caller has to rediscover it).
73
+ */
74
+ export function reportScanned(opts, write = process.stdout.write.bind(process.stdout)) {
75
+ assertScanned(opts);
76
+ write(`scanned: ${String(opts.scanned)}\n`);
77
+ }
78
+ /**
79
+ * Assert a watch-list gate's targets exist on disk, and return the ones that do.
80
+ *
81
+ * A diff-based guard has no corpus to count: it compares changed paths against
82
+ * a fixed list. If that list resolves to nothing, the guard reports "clean"
83
+ * forever — the exact shape of the `check_safety_floor_untouched` defect, which
84
+ * announced `4 rules guarded` while guarding none.
85
+ *
86
+ * @returns the subset of `candidates` that exist.
87
+ * @throws {DeadScopeError} when none of them exist.
88
+ */
89
+ export function assertWatchlistResolves(opts) {
90
+ const resolved = opts.candidates.filter((rel) => {
91
+ try {
92
+ return fs.statSync(path.join(opts.repoRoot, rel)).isFile();
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ });
98
+ if (resolved.length === 0) {
99
+ throw new DeadScopeError(opts.gate, `${opts.gate}: none of its ${opts.candidates.length} guarded path(s) exist ` +
100
+ `under ${opts.repoRoot} — the guard is watching phantoms and can never fire. ` +
101
+ `Checked: ${opts.candidates.join(', ')}`);
102
+ }
103
+ return resolved;
104
+ }
105
+ //# sourceMappingURL=scan_scope.js.map