@event4u/agent-config 16.1.0 → 16.3.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 (467) 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 +98 -257
  5. package/README.md +14 -12
  6. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  7. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  8. package/dist/agent-src/commands/bug/fix.md +17 -2
  9. package/dist/agent-src/commands/feature/plan.md +1 -1
  10. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  11. package/dist/agent-src/commands/fix/ci.md +1 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/jira-ticket.md +1 -1
  14. package/dist/agent-src/commands/roadmap/create.md +1 -1
  15. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  16. package/dist/agent-src/commands/roadmap/process-full.md +5 -5
  17. package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
  18. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  19. package/dist/agent-src/commands/roadmap.md +18 -4
  20. package/dist/agent-src/commands/tests/create.md +17 -3
  21. package/dist/agent-src/commands/tests/execute.md +22 -5
  22. package/dist/agent-src/commands/work.md +4 -0
  23. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  25. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
  27. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  28. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  29. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  30. package/dist/agent-src/rules/active-remediation.md +1 -1
  31. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  32. package/dist/agent-src/rules/broken-access-control.md +1 -0
  33. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  34. package/dist/agent-src/rules/context-hygiene.md +1 -1
  35. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  36. package/dist/agent-src/rules/design-fidelity.md +13 -16
  37. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  39. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  40. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  41. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  42. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  43. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  44. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  45. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  46. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  47. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  48. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  49. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  50. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  51. package/dist/agent-src/rules/persona-governance.md +2 -2
  52. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  53. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  54. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  55. package/dist/agent-src/rules/runtime-safety.md +1 -0
  56. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  57. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  58. package/dist/agent-src/rules/session-canary.md +8 -62
  59. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  60. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  61. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  62. package/dist/agent-src/rules/tool-safety.md +1 -0
  63. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  64. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  65. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  66. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  67. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  68. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  69. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  70. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
  71. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  72. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  73. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  74. package/dist/agent-src/skills/design-review/SKILL.md +30 -4
  75. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  76. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
  78. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  79. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  80. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  81. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  82. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  83. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  84. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  85. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  86. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  87. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  88. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  89. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  90. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  91. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  92. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  93. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  94. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  95. package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
  96. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  97. package/dist/agent-src/templates/roadmaps.md +30 -0
  98. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  99. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  100. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  101. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  102. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  103. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  104. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  105. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  106. package/dist/cli/python/knowledge_ingest.js +1 -1
  107. package/dist/cli/python/workspace_drive.js +1 -1
  108. package/dist/cli/registry.js +1 -1
  109. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  110. package/dist/cli-delegate/check_update_banner.js +12 -1
  111. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  112. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  113. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  114. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  115. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  116. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  117. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  118. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  119. package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
  120. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  121. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  122. package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
  123. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  124. package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
  125. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  126. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  127. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  128. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  129. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  130. package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
  131. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
  132. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  133. package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
  134. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  135. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  136. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  137. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  138. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  139. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  140. package/dist/cli-delegate/cmd_conformance.js +1862 -809
  141. package/dist/cli-delegate/cmd_doctor.js +13 -6
  142. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  143. package/dist/cli-delegate/cmd_explain.js +5 -5
  144. package/dist/cli-delegate/cmd_export.js +11 -8
  145. package/dist/cli-delegate/cmd_fleet.js +3 -3
  146. package/dist/cli-delegate/cmd_handoff.js +10 -7
  147. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  148. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  149. package/dist/cli-delegate/cmd_migrate.js +2 -2
  150. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  151. package/dist/cli-delegate/cmd_preflight.js +6 -6
  152. package/dist/cli-delegate/cmd_prune.js +11 -8
  153. package/dist/cli-delegate/cmd_refresh.js +2 -1
  154. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  155. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  156. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  157. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  158. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  159. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  160. package/dist/cli-delegate/cmd_sync.js +11 -8
  161. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  162. package/dist/cli-delegate/cmd_update.js +1 -1
  163. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  164. package/dist/cli-delegate/cmd_validate.js +11 -8
  165. package/dist/cli-delegate/cmd_versions.js +1 -1
  166. package/dist/cli-delegate/deferral_carry.js +16 -0
  167. package/dist/cli-delegate/gate_execute.js +2 -2
  168. package/dist/cli-delegate/roadmap_gates.js +5 -5
  169. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  170. package/dist/discovery/deprecation-report.md +1 -1
  171. package/dist/discovery/discovery-manifest.json +132 -80
  172. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  173. package/dist/discovery/discovery-manifest.summary.md +3 -3
  174. package/dist/discovery/dormancy-report.md +1 -1
  175. package/dist/discovery/orphan-report.md +1 -1
  176. package/dist/discovery/packs.json +7 -5
  177. package/dist/discovery/trust-report.md +2 -2
  178. package/dist/discovery/workspaces.json +6 -4
  179. package/dist/hooks/dispatch.js +7972 -5186
  180. package/dist/hooks/dispatch.sha256 +1 -0
  181. package/dist/install/claudePathsPlan.js +47 -6
  182. package/dist/install/claudeRuleRewrite.js +7 -0
  183. package/dist/install/emit_host_rules_cli.js +39 -36
  184. package/dist/install/globalRuleScope.js +87 -0
  185. package/dist/install/host_rule_ownership.js +160 -0
  186. package/dist/install/install.mjs +2226 -1055
  187. package/dist/install/installThinLayer.js +261 -0
  188. package/dist/install/installedRuleLinks.js +176 -0
  189. package/dist/install/wizard-plan.js +24 -0
  190. package/dist/mcp/registry-manifest.json +3 -3
  191. package/dist/mcp/server.json +2 -2
  192. package/dist/mcp/server.mjs +40913 -34865
  193. package/dist/scripts/_lib/agent_settings.js +11 -0
  194. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  195. package/dist/scripts/_lib/installed_layer.js +378 -0
  196. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  197. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  198. package/dist/scripts/_lib/rule_injection.js +67 -10
  199. package/dist/scripts/_lib/rule_law_section.js +122 -0
  200. package/dist/scripts/_lib/thin_rules.js +458 -0
  201. package/dist/scripts/_lib/windsurf_render.js +60 -0
  202. package/dist/scripts/condense.js +14 -26
  203. package/dist/scripts/project_thin_rules.js +14 -294
  204. package/dist/server/schemas/settings.js +29 -0
  205. package/dist/shared/skillRanking.js +74 -13
  206. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  207. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  208. package/dist/ui/index.html +2 -2
  209. package/docs/contracts/settings-classes.md +10 -6
  210. package/package.json +4 -4
  211. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  212. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  213. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  214. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  215. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  216. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  217. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  218. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  219. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  220. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  221. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  222. package/src/config/agent-settings.template.yml +91 -22
  223. package/src/config/ci-local-parity.yml +18 -0
  224. package/src/config/continuity-surface.json +9 -2
  225. package/src/config/evaluator-budgets.json +4 -4
  226. package/src/config/gate-coverage.yml +327 -3
  227. package/src/config/gate-violation-baselines.json +21 -9
  228. package/src/config/hook-bundle-budget.json +25 -0
  229. package/src/config/hook-latency-budget.json +21 -0
  230. package/src/config/hook-token-budget.json +8 -3
  231. package/src/config/host-instruction-limits.json +33 -0
  232. package/src/config/metric-registry.yml +114 -0
  233. package/src/config/placeholder-drift-budget.json +10 -0
  234. package/src/config/routing-coverage-seed.json +38 -3
  235. package/src/config/rule-activation-census.json +7 -3
  236. package/src/config/rule-consequence-class.json +161 -0
  237. package/src/config/rule-law-ceilings.json +99 -0
  238. package/src/install/claudePathsPlan.ts +46 -4
  239. package/src/install/claudeRuleRewrite.ts +10 -1
  240. package/src/install/emit_host_rules_cli.ts +43 -34
  241. package/src/install/globalRuleScope.ts +109 -0
  242. package/src/install/host_rule_ownership.ts +170 -0
  243. package/src/install/installThinLayer.ts +312 -0
  244. package/src/install/installedRuleLinks.ts +220 -0
  245. package/src/install/wizard-plan.ts +24 -0
  246. package/src/scripts/_cli/cmd_doctor.ts +28 -90
  247. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  248. package/src/scripts/_cli/doctor_execution.ts +100 -17
  249. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  250. package/src/scripts/_dispatch.bash +16 -2
  251. package/src/scripts/_lib/agent_settings.ts +11 -0
  252. package/src/scripts/_lib/body_portable.ts +102 -0
  253. package/src/scripts/_lib/cc_transcript.ts +132 -1
  254. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  255. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  256. package/src/scripts/_lib/estate_offsets.ts +241 -0
  257. package/src/scripts/_lib/exemption_shape.ts +143 -0
  258. package/src/scripts/_lib/fnv.ts +23 -0
  259. package/src/scripts/_lib/foreign_scan.ts +106 -0
  260. package/src/scripts/_lib/forge_protection.ts +114 -0
  261. package/src/scripts/_lib/forge_reader.ts +558 -0
  262. package/src/scripts/_lib/gitignore_block.ts +314 -0
  263. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  264. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  265. package/src/scripts/_lib/hook_settings.ts +73 -0
  266. package/src/scripts/_lib/host_capability.ts +40 -19
  267. package/src/scripts/_lib/host_env_write.ts +111 -0
  268. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  269. package/src/scripts/_lib/installed_layer.ts +538 -0
  270. package/src/scripts/_lib/json_merge.ts +82 -0
  271. package/src/scripts/_lib/json_pointers.ts +93 -11
  272. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  273. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  274. package/src/scripts/_lib/machine_wake.ts +16 -3
  275. package/src/scripts/_lib/neighbour_census.ts +760 -0
  276. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  277. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  278. package/src/scripts/_lib/obligations.ts +119 -8
  279. package/src/scripts/_lib/probe_inputs.ts +271 -0
  280. package/src/scripts/_lib/prompt_shape.ts +46 -0
  281. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  282. package/src/scripts/_lib/release_highlights.ts +2 -1
  283. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  284. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  285. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  286. package/src/scripts/_lib/review_coverage.ts +134 -0
  287. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  288. package/src/scripts/_lib/rule_injection.ts +130 -10
  289. package/src/scripts/_lib/rule_law_section.ts +147 -0
  290. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  291. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  292. package/src/scripts/_lib/skill_origin.ts +254 -0
  293. package/src/scripts/_lib/structural_hiding.ts +622 -0
  294. package/src/scripts/_lib/structured_ask.ts +64 -21
  295. package/src/scripts/_lib/thin_rules.ts +540 -0
  296. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  297. package/src/scripts/_lib/turn_end_refusals.ts +600 -4
  298. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  299. package/src/scripts/_lib/verification_command.ts +324 -0
  300. package/src/scripts/_lib/verification_evidence.ts +647 -0
  301. package/src/scripts/_lib/verify_clause.ts +280 -0
  302. package/src/scripts/_lib/windsurf_render.ts +63 -0
  303. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  304. package/src/scripts/ai_council/clients.ts +26 -57
  305. package/src/scripts/ai_council/config.ts +1 -1
  306. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  307. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  308. package/src/scripts/audit_skill_overlap.ts +261 -0
  309. package/src/scripts/before_complete_hook.ts +573 -25
  310. package/src/scripts/bench_hook_latency.ts +412 -5
  311. package/src/scripts/bench_touched_file_quality.ts +238 -0
  312. package/src/scripts/cache_realization_report.ts +61 -1
  313. package/src/scripts/check_argument_hint.ts +287 -0
  314. package/src/scripts/check_claims.ts +125 -1
  315. package/src/scripts/check_enforcement_coverage.ts +38 -9
  316. package/src/scripts/check_enforcement_matrix.ts +621 -22
  317. package/src/scripts/check_estate_count.ts +130 -153
  318. package/src/scripts/check_finding_dispositions.ts +138 -7
  319. package/src/scripts/check_generator_sync.ts +71 -0
  320. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  321. package/src/scripts/check_host_docs_digest.ts +786 -0
  322. package/src/scripts/check_host_format_column.ts +559 -0
  323. package/src/scripts/check_input_references.ts +228 -0
  324. package/src/scripts/check_inputs_parity.ts +235 -0
  325. package/src/scripts/check_kernel_edit_ratified.ts +89 -9
  326. package/src/scripts/check_kill_switch_table.ts +294 -0
  327. package/src/scripts/check_memory.ts +7 -1
  328. package/src/scripts/check_placeholder_drift.ts +221 -0
  329. package/src/scripts/check_read_surface_coverage.ts +515 -0
  330. package/src/scripts/check_release_highlights.ts +10 -0
  331. package/src/scripts/check_release_holds.ts +2 -1
  332. package/src/scripts/check_release_pr_shape.ts +38 -15
  333. package/src/scripts/check_routing_coverage.ts +378 -11
  334. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  335. package/src/scripts/check_static_parity.ts +17 -0
  336. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  337. package/src/scripts/ci_settle.ts +145 -19
  338. package/src/scripts/closure_scan.ts +115 -1
  339. package/src/scripts/code_graph/detect.ts +127 -7
  340. package/src/scripts/code_graph/foreign.ts +194 -0
  341. package/src/scripts/code_graph/query.ts +217 -4
  342. package/src/scripts/code_graph/types.ts +26 -2
  343. package/src/scripts/code_graph/validate.ts +3 -1
  344. package/src/scripts/code_graph/verbs.ts +103 -0
  345. package/src/scripts/compile_hook_manifest.ts +90 -14
  346. package/src/scripts/condense.ts +14 -27
  347. package/src/scripts/estate_exemption_shape.ts +202 -0
  348. package/src/scripts/external_sources_denylist.json +29 -4
  349. package/src/scripts/generate_host_cost_table.ts +1 -1
  350. package/src/scripts/hook_manifest.json +1 -1
  351. package/src/scripts/hook_manifest.yaml +192 -14
  352. package/src/scripts/hooks/block_config_weakening.ts +294 -4
  353. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  354. package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
  355. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  356. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  357. package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
  358. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  359. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  360. package/src/scripts/hooks/concern_registry.ts +2 -0
  361. package/src/scripts/hooks/concern_timings.ts +94 -0
  362. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  363. package/src/scripts/hooks/design_pass_hook.ts +100 -9
  364. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  365. package/src/scripts/hooks/dispatch_hook.ts +77 -71
  366. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  367. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  368. package/src/scripts/hooks/evidence_independence.ts +1 -2
  369. package/src/scripts/hooks/exit_codes.ts +129 -0
  370. package/src/scripts/hooks/host_lowering.json +1 -0
  371. package/src/scripts/hooks/host_lowering.ts +146 -3
  372. package/src/scripts/hooks/host_lowering.yaml +262 -46
  373. package/src/scripts/hooks/host_semantics.ts +30 -2
  374. package/src/scripts/hooks/injection_budget.ts +9 -2
  375. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  376. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  377. package/src/scripts/hooks/memory_recall_hook.ts +1 -2
  378. package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
  379. package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
  380. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  381. package/src/scripts/hooks/payload_stub.ts +10 -1
  382. package/src/scripts/hooks/permission_gate.ts +1 -2
  383. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  384. package/src/scripts/hooks/replay_hook.ts +2 -1
  385. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  386. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  387. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  388. package/src/scripts/hooks/rule_inject_hook.ts +765 -68
  389. package/src/scripts/hooks/run_continuation_hook.ts +13 -15
  390. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  391. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  392. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  393. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  394. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  395. package/src/scripts/hooks/table_fingerprint.ts +70 -0
  396. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  397. package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
  398. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  399. package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
  400. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  401. package/src/scripts/hooks_status.ts +112 -0
  402. package/src/scripts/injection_scan_hook.ts +7 -0
  403. package/src/scripts/install.ts +75 -189
  404. package/src/scripts/installed_layer_report.ts +103 -0
  405. package/src/scripts/language_mirror_hook.ts +15 -6
  406. package/src/scripts/learning_sidecar.ts +119 -8
  407. package/src/scripts/lint_deny_text.ts +419 -0
  408. package/src/scripts/lint_exit_codes.ts +262 -0
  409. package/src/scripts/lint_hook_manifest.ts +108 -0
  410. package/src/scripts/lint_major_migration_sections.ts +156 -26
  411. package/src/scripts/lint_pack_boundaries.ts +63 -5
  412. package/src/scripts/lint_roadmap_blockers.ts +78 -12
  413. package/src/scripts/lint_rule_law_section.ts +517 -0
  414. package/src/scripts/lint_skill_descriptions.ts +1 -1
  415. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  416. package/src/scripts/lint_workflow_security.ts +161 -5
  417. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  418. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  419. package/src/scripts/mcp_server/prompts.ts +142 -1
  420. package/src/scripts/measure_routing_signal.ts +54 -16
  421. package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
  422. package/src/scripts/measure_turn_end_gate.ts +193 -0
  423. package/src/scripts/memory_learn_hook.ts +91 -6
  424. package/src/scripts/memory_lookup.ts +1 -1
  425. package/src/scripts/model_rule_injection.ts +52 -5
  426. package/src/scripts/prepack-check.mjs +53 -0
  427. package/src/scripts/project_thin_rules.ts +34 -327
  428. package/src/scripts/release_publication.ts +13 -1
  429. package/src/scripts/report_evidence_temperature.ts +657 -0
  430. package/src/scripts/report_installed_rule_links.ts +157 -0
  431. package/src/scripts/report_invocation_surface.ts +383 -0
  432. package/src/scripts/report_label_agreement.ts +233 -0
  433. package/src/scripts/report_obligation_carriers.ts +699 -0
  434. package/src/scripts/report_obligation_mechanism.ts +230 -0
  435. package/src/scripts/report_obligation_writer_split.ts +236 -0
  436. package/src/scripts/report_single_token_triggers.ts +277 -0
  437. package/src/scripts/report_skill_menu_census.ts +208 -0
  438. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  439. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  440. package/src/scripts/report_verification_record_coverage.ts +306 -0
  441. package/src/scripts/roadmap_verify_share.ts +213 -0
  442. package/src/scripts/rule_activation_census.ts +5 -2
  443. package/src/scripts/rule_trigger_eval.ts +9 -9
  444. package/src/scripts/schemas/command.schema.json +44 -0
  445. package/src/scripts/schemas/review-findings.schema.json +7 -2
  446. package/src/scripts/schemas/rule.schema.json +5 -0
  447. package/src/scripts/schemas/skill.schema.json +44 -0
  448. package/src/scripts/self_review_gate.ts +421 -17
  449. package/src/scripts/semantic_parity_census.ts +314 -0
  450. package/src/scripts/session_eol_report.ts +54 -1
  451. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  452. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  453. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  454. package/src/scripts/sync_gitignore.ts +29 -235
  455. package/src/scripts/trigger_eval_grandfather.json +0 -7
  456. package/src/scripts/trigger_eval_rotation.ts +122 -27
  457. package/src/scripts/ui_conformance_probe.ts +156 -4
  458. package/src/scripts/update_prices.ts +85 -3
  459. package/src/scripts/write_bundle_digest.mjs +74 -0
  460. package/src/server/schemas/settings.ts +41 -0
  461. package/src/shared/skillRanking.ts +117 -11
  462. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  463. package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
  464. package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
  465. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  466. package/dist/ui/assets/index-DH1xc62v.js +0 -29
  467. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
@@ -0,0 +1,241 @@
1
+ /**
2
+ * How a CHANGE touches the roadmap estate — the diff half, read once.
3
+ *
4
+ * `check_estate_count` answers two different questions with two different
5
+ * inputs. The count half reads the TREE and ratchets it against the base ref's
6
+ * tree. This module is the other half: it reads the DIFF and says what the
7
+ * change did — which roadmaps entered the active top level, which left it, which
8
+ * of the arrivals declared an exemption, and what that exemption says.
9
+ *
10
+ * WHY IT LIVES HERE AND NOT IN THE GATE. `check_source_size_budget` scores
11
+ * `Σ max(0, lines(f) − 1500)` over `src/**` and the gate crossed the ceiling
12
+ * when the exemption-shape work landed. That gate does not object to new code;
13
+ * it objects to new code in the file that is already too big to read, and the
14
+ * ratchet was right about which file that was. The seam it forced is the one a
15
+ * reader would draw anyway — tree-reading on one side of it, diff-reading on the
16
+ * other — so the extraction is a correction rather than a concession.
17
+ *
18
+ * `exemptionReason` sits here rather than beside the vocabulary it feeds
19
+ * (`exemption_shape.ts`) because this is the module that READS a change; that
20
+ * one is the module that JUDGES a reason. The split is read-then-judge, and
21
+ * keeping it means the vocabulary module imports no filesystem and no git.
22
+ */
23
+ import * as path from 'node:path';
24
+
25
+ import {
26
+ is_roadmap_candidate as isRoadmapCandidate,
27
+ parse_frontmatter as parseFrontmatter,
28
+ } from '../../agent-src/scripts/update_roadmap_progress.js';
29
+
30
+ /** How a change touched the active roadmap tree, as git reports it. */
31
+ export interface OffsetLedger {
32
+ /** Files that entered the active top level: new files, and un-parked ones. */
33
+ added: string[];
34
+ /** Files that left it: deleted, archived, parked or merged away. */
35
+ offsets: string[];
36
+ /** Added files carrying an `estate_offset_exempt:` reason, with the reason. */
37
+ exempt: Array<{ file: string; reason: string }>;
38
+ /**
39
+ * The subset of `offsets` that went to `later/`.
40
+ *
41
+ * Tracked separately because parking is the one offset that RAISES another
42
+ * gated count: active falls by one and later rises by one, which under an
43
+ * exact floor is growth in `later_roadmaps` unless this allowance exists.
44
+ * Kept out of a general "any offset raises any allowance" rule on purpose —
45
+ * an archived roadmap must not buy a new `later/` file.
46
+ */
47
+ parked: string[];
48
+ }
49
+
50
+ /** An `estate_growth_exempt:` reason ADDED to a roadmap in this change. */
51
+ export interface GrowthClaim {
52
+ /** The roadmap the claim was added to, as git reports the path. */
53
+ file: string;
54
+ reason: string;
55
+ }
56
+
57
+ /** `agents/roadmaps/<name>.md` — the active top level. code-comment-allow provenance-comment -- operand, not provenance */
58
+ function isActiveTopLevel(rel: string): boolean {
59
+ const norm = rel.split(path.sep).join('/');
60
+ if (!norm.startsWith('agents/roadmaps/') || !norm.endsWith('.md')) {
61
+ return false;
62
+ }
63
+ const tail = norm.slice('agents/roadmaps/'.length);
64
+ return !tail.includes('/') && isRoadmapCandidate(norm);
65
+ }
66
+
67
+ /**
68
+ * A disposition directory — where an offset sends a roadmap.
69
+ *
70
+ * `stubs/` is in the set, and it was missing from the first version. Un-stubbing
71
+ * is the documented promotion path, so a stub moved to the top level is an
72
+ * ADDITION that T3 must charge, and a roadmap demoted to a stub is an offset.
73
+ * With `stubs/` unrecognised, a promotion was classified as neither and the lint
74
+ * could never charge it — the one hole that let an active roadmap arrive for free.
75
+ */
76
+ function isDisposed(rel: string): boolean {
77
+ const norm = rel.split(path.sep).join('/');
78
+ return /^agents\/roadmaps\/(archive|later|skipped|stubs)\//.test(norm);
79
+ }
80
+
81
+ /** A roadmap parked for later — the one disposition that grows another count. */
82
+ function isParked(rel: string): boolean {
83
+ return rel.split(path.sep).join('/').startsWith('agents/roadmaps/later/');
84
+ }
85
+
86
+ /**
87
+ * Read the exemption reason a newly added roadmap declares, if any.
88
+ *
89
+ * The key lives in the file's own frontmatter rather than in a config or a
90
+ * commit trailer, for the reason `RATCHET_RESET_KEY` gives for living inside the
91
+ * baseline JSON: the claim then shows up in the diff of the change that makes
92
+ * it, and a reviewer sees it without being told to look.
93
+ *
94
+ * THE BLOCK SCALAR IS READ HERE AND NOT BY `parseFrontmatter`, which is a flat
95
+ * line parser with no YAML block support: it returns the value as everything
96
+ * after the first colon. Measured 2026-10-01 across the three roadmap trees,
97
+ * **38 of 221 exemptions are written `estate_offset_exempt: >-` with the reason
98
+ * indented beneath**, and for every one of them this function used to return
99
+ * the literal two-character string `>-`. That is non-empty, so the gate
100
+ * accepted it — a key whose whole purpose is that a reviewer sees the claim was
101
+ * being checked against a YAML punctuation mark on one file in six. The fix
102
+ * belongs here rather than in the shared parser because every other caller of
103
+ * that parser reads single-token values (`status`, `complexity`) where block
104
+ * support buys nothing and a rewrite risks the dashboard.
105
+ */
106
+ export function exemptionReason(text: string): string | null {
107
+ const block = blockScalar(text, 'estate_offset_exempt');
108
+ if (block !== null) return block === '' ? null : block;
109
+ const fm = parseFrontmatter(text);
110
+ const raw = (fm as Record<string, unknown>)['estate_offset_exempt'];
111
+ if (typeof raw !== 'string') {
112
+ return null;
113
+ }
114
+ const reason = raw.trim().replace(/^["']|["']$/g, '').trim();
115
+ return reason === '' ? null : reason;
116
+ }
117
+
118
+ /**
119
+ * The folded body of `<key>: >-` / `<key>: |`, or `null` when not that form.
120
+ *
121
+ * Returns `''` — not `null` — for a block header with no body, so the caller can
122
+ * tell "declared and empty" from "not a block scalar". An empty block is the
123
+ * blank-reason case the key already refuses, and collapsing it into `null` here
124
+ * would send it back to the flat parser, which would hand back `>-` and accept it.
125
+ */
126
+ function blockScalar(text: string, key: string): string | null {
127
+ const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
128
+ if (m === null) return null;
129
+ const lines = (m[1] as string).split(/\r?\n/);
130
+ const head = new RegExp(`^${key}:[ \\t]*(.*)$`);
131
+ for (let i = 0; i < lines.length; i += 1) {
132
+ const hm = head.exec(lines[i] as string);
133
+ if (hm === null) continue;
134
+ // Only the block indicators. A quoted or bare value is the flat form and
135
+ // stays with the flat parser, which already handles it.
136
+ //
137
+ // THE TRAILING COMMENT IS PART OF THE HEADER, and omitting it was a
138
+ // BYPASS rather than a parse gap. `estate_offset_exempt: >- # offset`
139
+ // failed this test, fell through to the flat parser, and came back as
140
+ // the string `">- # offset"` — which the shape rule then searched for a
141
+ // disposition word and found one, IN THE COMMENT, over a body it never
142
+ // read. The indicator may also be written `>2-` as well as `>-2`, so
143
+ // both orders are accepted; an indentation indicator is never `0`.
144
+ if (!/^[>|](?:[1-9][-+]?|[-+]?[1-9]?)(?:[ \t]+#.*)?$/.test((hm[1] as string).trim())) return null;
145
+ const body: string[] = [];
146
+ for (let j = i + 1; j < lines.length; j += 1) {
147
+ const line = lines[j] as string;
148
+ if (line.trim() === '') continue;
149
+ // The first unindented line ends the block — the next key.
150
+ if (!/^[ \t]/.test(line)) break;
151
+ body.push(line.trim());
152
+ }
153
+ return body.join(' ').replace(/\s+/g, ' ').trim();
154
+ }
155
+ return null;
156
+ }
157
+
158
+ /**
159
+ * Classify the change's effect on the active roadmap tree.
160
+ *
161
+ * Renames carry information a name-only diff loses: `road-to-x.md` →
162
+ * `archive/road-to-x.md` is the wanted direction and counts as an offset, while
163
+ * `later/road-to-x.md` → `road-to-x.md` is an un-parking and counts as an
164
+ * addition. A top-level-to-top-level rename is neither.
165
+ */
166
+ export function classifyDiff(
167
+ nameStatus: string,
168
+ readFile: (rel: string) => string | null,
169
+ ): OffsetLedger {
170
+ const added: string[] = [];
171
+ const offsets: string[] = [];
172
+ const exempt: Array<{ file: string; reason: string }> = [];
173
+ const parked: string[] = [];
174
+ for (const line of nameStatus.split('\n')) {
175
+ if (line.trim() === '') continue;
176
+ const cols = line.split('\t');
177
+ const status = (cols[0] ?? '').trim();
178
+ if (status.startsWith('R') || status.startsWith('C')) {
179
+ const from = cols[1] ?? '';
180
+ const to = cols[2] ?? '';
181
+ if (isActiveTopLevel(from) && isDisposed(to)) {
182
+ offsets.push(from);
183
+ if (isParked(to)) parked.push(to);
184
+ } else if (isDisposed(from) && isActiveTopLevel(to)) {
185
+ added.push(to);
186
+ }
187
+ continue;
188
+ }
189
+ const file = cols[1] ?? '';
190
+ if (!isActiveTopLevel(file)) continue;
191
+ if (status === 'A') {
192
+ added.push(file);
193
+ } else if (status === 'D') {
194
+ offsets.push(file);
195
+ }
196
+ }
197
+ for (const file of added) {
198
+ const text = readFile(file);
199
+ if (text === null) continue;
200
+ const reason = exemptionReason(text);
201
+ if (reason !== null) {
202
+ exempt.push({ file, reason });
203
+ }
204
+ }
205
+ return { added, offsets, exempt, parked };
206
+ }
207
+
208
+ /**
209
+ * The `estate_growth_exempt:` reasons this change ADDS, read from the patch.
210
+ *
211
+ * Read from the diff rather than from the file, and that is the point: a claim
212
+ * sitting in a roadmap authorises nothing on a later change, so an exemption
213
+ * cannot be banked the way surplus in a stored baseline could. It also means a
214
+ * newly added roadmap and an edited one need no separate handling — in a
215
+ * `base...HEAD` patch both arrive as `+` lines.
216
+ *
217
+ * `--unified=0` keeps context lines out, so a claim that merely sits NEAR an
218
+ * edited line is not read as added. Deliberately tolerant of leading whitespace
219
+ * and of quoted values, matching `exemptionReason`; deliberately NOT tolerant of
220
+ * an empty reason, because an exemption whose reason is blank is the silent
221
+ * exception the key exists to replace.
222
+ */
223
+ export function growthClaims(patch: string): GrowthClaim[] {
224
+ const out: GrowthClaim[] = [];
225
+ let file = '';
226
+ for (const line of patch.split('\n')) {
227
+ // `+++ b/<path>` names the file the following `+` lines belong to. The
228
+ // `/dev/null` form is a deletion, which cannot carry a claim.
229
+ const head = /^\+\+\+ b\/(.+)$/.exec(line);
230
+ if (head !== null) {
231
+ file = (head[1] ?? '').trim();
232
+ continue;
233
+ }
234
+ if (!line.startsWith('+') || line.startsWith('+++')) continue;
235
+ const m = /^\+\s*estate_growth_exempt:\s*(.+?)\s*$/.exec(line);
236
+ if (m === null) continue;
237
+ const reason = (m[1] ?? '').trim().replace(/^["']|["']$/g, '').trim();
238
+ if (reason !== '') out.push({ file, reason });
239
+ }
240
+ return out;
241
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * What an `estate_offset_exempt` reason has to SAY — one vocabulary, two readers.
3
+ *
4
+ * THE DEFECT THIS EXISTS TO NAME. The one-in-one-out rule charges every added
5
+ * active roadmap against a disposed one, and `estate_offset_exempt: <reason>`
6
+ * is the escape hatch that costs "one reviewable line instead of a silent
7
+ * exception". `exemptionReason` accepted any non-empty string, so the line was
8
+ * reviewable in the sense that it existed. Measured 2026-10-01 over the active,
9
+ * `later/` and `archive/` trees: 221 files carry the key, and 43 of them name
10
+ * no alternative disposition at all under the three-lemma set the roadmap named
11
+ * from memory, and 6 under the measured one below — including three that say only
12
+ * `lane N of <parent>`. The escape hatch was free.
13
+ *
14
+ * WHAT A SHAPED REASON SAYS. It names at least one disposition the author
15
+ * considered instead of adding a file: archiving something, parking it in
16
+ * `later/`, merging into an existing roadmap, or any of the words this tree
17
+ * actually uses for those three. The requirement is deliberately NOT "explain
18
+ * yourself well" — that is unjudgeable — but "name what you rejected", which is
19
+ * a word that is either present or absent.
20
+ *
21
+ * WHY THE VOCABULARY IS MEASURED AND NOT CHOSEN. The commissioning roadmap
22
+ * named three lemmas, archive / park / merge, from memory. Over the real
23
+ * population those three cover 178 of 221; the measured ten below cover 215.
24
+ * The 37-file gap is not boilerplate — "Offsetting it individually is not
25
+ * possible without splitting the set" and "no completed roadmap to retire
26
+ * against this addition" both name a rejected alternative in words the
27
+ * three-lemma set does not carry. A refusal vocabulary that reds one added
28
+ * roadmap in five on the house style gets weakened until it finds nothing, so
29
+ * the set here is the one the tree writes. Every number above carries its
30
+ * producing command: `./scripts-run src/scripts/estate_exemption_shape`.
31
+ *
32
+ * `close` is excluded on purpose, and it is the most common verb in these
33
+ * reasons: it almost always refers to closing WORK, not to disposing of a file,
34
+ * so accepting it would make the check pass on a sentence that named nothing.
35
+ *
36
+ * WHAT THIS DOES NOT DO, stated because a reviewer called it HIGH and the
37
+ * disagreement is worth recording rather than resolving silently. The cheapest
38
+ * string that passes is the single word `park`, and `park 1` / `park 2` are two
39
+ * distinct reasons to the duplicate rule. So this verifies that a disposition
40
+ * word APPEARS; it does not verify that the author thought about one, and no
41
+ * check can. The claim the gate's message makes is therefore "name what you
42
+ * rejected", which is what a word either does or does not do — not "explain
43
+ * yourself well", which is unjudgeable. A reason gamed down to one word is
44
+ * visible to a human reviewer in the diff, which is where the key's own stated
45
+ * purpose puts the real control; what this removes is the case where there was
46
+ * nothing to look at because the reason was free text nobody had read.
47
+ *
48
+ * WHY ONE MODULE. Two readers consume this — the estate gate, which refuses,
49
+ * and the measurement script, which reports. A second copy of the lemma list is
50
+ * how a gate and its own evidence drift apart, and a drifted vocabulary fails
51
+ * silently: the report says the tree conforms while the gate refuses it.
52
+ */
53
+
54
+ /**
55
+ * The dispositions a reason may name, as source strings.
56
+ *
57
+ * Exported individually rather than as one baked regex so the measurement
58
+ * script can report per-lemma counts against the same list the gate refuses on.
59
+ */
60
+ export const DISPOSITION_LEMMAS: readonly { readonly name: string; readonly source: string }[] = [
61
+ { name: 'archive', source: String.raw`archiv\w*` },
62
+ // `park` is the one stem with a common false friend, and a reviewer named
63
+ // it: `park\w*` matches `parkour` at a word boundary, so a reason saying
64
+ // nothing about dispositions passes on a word that is not one. Enumerated
65
+ // instead of stemmed — the inflections this tree writes, and no more. Every
66
+ // other lemma keeps its stem; none of them has a false friend in English
67
+ // that could plausibly appear in a sentence about roadmap dispositions.
68
+ { name: 'park', source: String.raw`unpark(?:ed|ing|s)?\b|park(?:ed|ing|s)?\b` },
69
+ { name: 'merge', source: String.raw`merg\w*` },
70
+ { name: 'later', source: String.raw`later\/` },
71
+ { name: 'offset', source: String.raw`offset\w*` },
72
+ { name: 'defer', source: String.raw`defer\w*` },
73
+ { name: 'fold', source: String.raw`fold\w*` },
74
+ { name: 'consolidate', source: String.raw`consolidat\w*` },
75
+ { name: 'absorb', source: String.raw`absorb\w*` },
76
+ { name: 'retire', source: String.raw`retir\w*` },
77
+ ];
78
+
79
+ /** The whole vocabulary as one alternation — the gate's own test. */
80
+ export const DISPOSITION_SOURCE = `\\b(?:${DISPOSITION_LEMMAS.map((l) => l.source).join('|')})`;
81
+
82
+ const DISPOSITION_RE = new RegExp(DISPOSITION_SOURCE, 'i');
83
+
84
+ /** Does this reason name at least one rejected alternative? */
85
+ export function namesDisposition(reason: string): boolean {
86
+ return DISPOSITION_RE.test(reason);
87
+ }
88
+
89
+ /** Which lemmas a reason names, for the reader's per-file column. */
90
+ export function lemmasNamed(reason: string): string[] {
91
+ return DISPOSITION_LEMMAS.filter((l) => new RegExp(`\\b(?:${l.source})`, 'i').test(reason)).map((l) => l.name);
92
+ }
93
+
94
+ /**
95
+ * The comparison key for "two added files say the same thing".
96
+ *
97
+ * Case and whitespace are folded because a reason pasted into a second file and
98
+ * re-wrapped by an editor is the same reason. Nothing else is folded: a reason
99
+ * that differs by one clause is a different reason, and a normaliser clever
100
+ * enough to call those equal would start refusing reasons that genuinely differ.
101
+ */
102
+ export function reasonKey(reason: string): string {
103
+ return reason.toLowerCase().replace(/\s+/g, ' ').trim();
104
+ }
105
+
106
+ /** One added file whose exemption reason does not hold up. */
107
+ export interface ExemptionFinding {
108
+ readonly file: string;
109
+ readonly kind: 'shapeless' | 'duplicate';
110
+ /** For `duplicate`, the other added file carrying the same reason. */
111
+ readonly twin: string | null;
112
+ readonly reason: string;
113
+ }
114
+
115
+ /**
116
+ * Judge the exemptions a single change ADDS — never the ones already in the tree.
117
+ *
118
+ * Grandfathering is load-bearing rather than polite. 43 of the 221 existing
119
+ * reasons name no disposition; re-reading them would land this check red on a
120
+ * fifth of the corpus, which is a backlog wearing a control's clothes. The
121
+ * input is `classifyDiff`'s exempt ledger, which by construction lists only the
122
+ * files this change added.
123
+ */
124
+ export function exemptionFindings(
125
+ exempt: readonly { readonly file: string; readonly reason: string }[],
126
+ ): ExemptionFinding[] {
127
+ const out: ExemptionFinding[] = [];
128
+ const seen = new Map<string, string>();
129
+ for (const e of exempt) {
130
+ if (!namesDisposition(e.reason)) {
131
+ out.push({ file: e.file, kind: 'shapeless', twin: null, reason: e.reason });
132
+ continue;
133
+ }
134
+ const key = reasonKey(e.reason);
135
+ const twin = seen.get(key);
136
+ if (twin === undefined) {
137
+ seen.set(key, e.file);
138
+ continue;
139
+ }
140
+ out.push({ file: e.file, kind: 'duplicate', twin, reason: e.reason });
141
+ }
142
+ return out;
143
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * FNV-1a, 32-bit — the one definition in this tree.
3
+ *
4
+ * Extracted from `rule_trigger_eval.ts`, which still re-exports it under its
5
+ * own name so its suite keeps importing the symbol it always did. The reason
6
+ * for the extraction is the reason this file carries a doc block at all: a
7
+ * second consumer arrived (`measure_skill_ranker_baseline`'s held-out
8
+ * partition), and two copies of a hash that decides which rows a measurement
9
+ * reads is the shape in which a partition quietly stops being the same
10
+ * partition. One definition, two callers.
11
+ *
12
+ * Pure — no imports, no I/O, no clock.
13
+ */
14
+
15
+ /** FNV-1a over UTF-16 code units, returned as an unsigned 32-bit integer. */
16
+ export function fnv1a(s: string): number {
17
+ let h = 0x811c9dc5;
18
+ for (let i = 0; i < s.length; i += 1) {
19
+ h ^= s.charCodeAt(i);
20
+ h = Math.imul(h, 0x01000193) >>> 0;
21
+ }
22
+ return h >>> 0;
23
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The unclaimed-file scan under this package's own deploy roots.
3
+ *
4
+ * Moved verbatim out of `src/scripts/_cli/cmd_doctor.ts`
5
+ * (road-to-a-tree-that-keeps-its-neighbours Phase 2.6). The behaviour is
6
+ * unchanged — this is a module boundary, not a rewrite.
7
+ *
8
+ * Two reasons it earns its own file. `cmd_doctor.ts` is 2,100 lines above the
9
+ * `check_source_size_budget` ceiling, so the census line that now links this
10
+ * listing is paid for by a MOVE rather than by raising the tree-wide excess.
11
+ * And the neighbour census answers a strictly larger question than this scan
12
+ * does — this one lists FILES under paths this package deploys to, the census
13
+ * lists ARTEFACTS wherever a host keeps them — so the two belong beside each
14
+ * other rather than nested inside a doctor renderer.
15
+ *
16
+ * What this scan does NOT do, stated because the two listings look alike: it
17
+ * never leaves a deploy root, so a neighbour's skill in `~/.claude/skills` or
18
+ * hook entry in `.cursor/hooks.json` is invisible to it. That gap is the
19
+ * census's subject, and the census links back here rather than repeating the
20
+ * paths this one already prints.
21
+ */
22
+
23
+ import * as fs from 'node:fs';
24
+
25
+ /**
26
+ * `Path.rglob("*")` — every descendant path (dirs + files), recursively.
27
+ *
28
+ * Order is not relied upon by callers (they sort the matched set), but the
29
+ * walk is deterministic (sorted per directory) to keep behaviour stable.
30
+ */
31
+ export function rglob(root: string, isDir: (p: string) => boolean, join: (a: string, b: string) => string): string[] {
32
+ const out: string[] = [];
33
+ const walk = (dir: string): void => {
34
+ let entries: fs.Dirent[];
35
+ try {
36
+ entries = fs.readdirSync(dir, { withFileTypes: true });
37
+ } catch {
38
+ return;
39
+ }
40
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
41
+ for (const ent of entries) {
42
+ const full = join(dir, ent.name);
43
+ out.push(full);
44
+ let dirLike = ent.isDirectory();
45
+ if (ent.isSymbolicLink()) {
46
+ // pathlib rglob follows into symlinked dirs.
47
+ dirLike = isDir(full);
48
+ }
49
+ if (dirLike) {
50
+ walk(full);
51
+ }
52
+ }
53
+ };
54
+ walk(root);
55
+ return out;
56
+ }
57
+
58
+ export interface ForeignScanDeps {
59
+ pathExists: (p: string) => boolean;
60
+ isDir: (p: string) => boolean;
61
+ isFile: (p: string) => boolean;
62
+ resolvePath: (p: string) => string;
63
+ join: (a: string, b: string) => string;
64
+ /** Resolve one declared deploy root against the consumer root. */
65
+ resolveRoot: (projectRoot: string, rel: string) => string;
66
+ defaultRoots: readonly string[];
67
+ }
68
+
69
+ /**
70
+ * Walk every declared deploy root and surface unclaimed files. Only regular
71
+ * files under `deploy_roots` count; bookkeeping is on the resolved final path.
72
+ */
73
+ export function scanForeign(
74
+ projectRoot: string,
75
+ manifest: Record<string, unknown>,
76
+ known: Set<string>,
77
+ deps: ForeignScanDeps,
78
+ ): string[] {
79
+ const roots = (manifest['deploy_roots'] as unknown[] | undefined) || Array.from(deps.defaultRoots);
80
+ const foreign: string[] = [];
81
+ const seen = new Set<string>();
82
+ for (const root_rel of roots) {
83
+ const root = deps.resolveRoot(projectRoot, String(root_rel));
84
+ if (!deps.pathExists(root) || !deps.isDir(root)) {
85
+ continue;
86
+ }
87
+ for (const child of rglob(root, deps.isDir, deps.join)) {
88
+ if (!deps.isFile(child)) {
89
+ continue;
90
+ }
91
+ let resolved: string;
92
+ try {
93
+ resolved = deps.resolvePath(child);
94
+ } catch {
95
+ resolved = child;
96
+ }
97
+ if (known.has(resolved) || seen.has(resolved)) {
98
+ continue;
99
+ }
100
+ seen.add(resolved);
101
+ foreign.push(child);
102
+ }
103
+ }
104
+ foreign.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
105
+ return foreign;
106
+ }
@@ -73,6 +73,120 @@ export interface ForgeReading {
73
73
  readonly deployRestricted: boolean | null;
74
74
  }
75
75
 
76
+ /**
77
+ * The all-null reading: nothing was queried.
78
+ *
79
+ * Lives here, beside {@link ForgeReading}, rather than in the `doctor` caller
80
+ * that first needed it — `_lib/forge_reader.ts` returns it on every failure
81
+ * path, and a second hand-written all-null literal is exactly the drift that
82
+ * makes "offline output is unchanged" an assertion instead of a fact.
83
+ *
84
+ * It MOVED from `_cli/doctor_execution.ts`, which no longer exports it in any
85
+ * form. Nothing broke, because nothing outside that file imported it — but an
86
+ * earlier draft of this comment claimed a re-export kept every caller in place,
87
+ * and no such shim was ever written. Corrected rather than quietly deleted: a
88
+ * comment describing a compatibility layer that does not exist is the next
89
+ * reader's false premise.
90
+ */
91
+ export const UNREAD_FORGE: ForgeReading = {
92
+ rulesets: null,
93
+ defaultBranch: null,
94
+ allowAutoMerge: null,
95
+ deployRestricted: null,
96
+ };
97
+
98
+ /**
99
+ * The two fields of an environment this row reads, as
100
+ * `GET repos/{owner}/{repo}/environments` returns them.
101
+ *
102
+ * A NARROW PROJECTION, not the full payload: the real response also carries
103
+ * `id`, `protection_rules`, `can_admins_bypass`, timestamps and more. Declared
104
+ * this way on purpose — a caller passes the real object and structural typing
105
+ * accepts the superset, while the interface states exactly which fields this
106
+ * decision depends on. Calling it "the shape" would overstate it.
107
+ *
108
+ * `null` is GitHub's encoding for *no restriction at all* — every branch may
109
+ * deploy. It is therefore the one shape that refutes the row.
110
+ */
111
+ export interface EnvironmentPolicy {
112
+ readonly name: string;
113
+ readonly deployment_branch_policy: {
114
+ readonly protected_branches?: boolean;
115
+ readonly custom_branch_policies?: boolean;
116
+ } | null;
117
+ }
118
+
119
+ /**
120
+ * Derive `deployRestricted` from the environments payload.
121
+ *
122
+ * **Both restriction mechanisms count, and reading only one is the defect this
123
+ * function exists to close.** GitHub restricts deployment branches two ways:
124
+ * `protected_branches` (whatever the forge currently treats as protected) and
125
+ * `custom_branch_policies` (an explicit named list). A checker that reads only
126
+ * the first reports "accepts a deployment from any branch" for an environment
127
+ * pinned to exactly one named branch — which is the opposite of what the
128
+ * payload says, and is precisely the misreading recorded against this
129
+ * repository on 2026-09-13: `github-pages` carried
130
+ * `custom_branch_policies: true` with a single policy, `main`, and was written
131
+ * up as unrestricted because only `protected_branches` was consulted.
132
+ *
133
+ * The named-list mechanism is also the STRICTER of the two here, which is why
134
+ * the row is satisfied without changing the forge: `protected_branches` widens
135
+ * with the protected set, while a custom list names one branch and stays there.
136
+ * On a ruleset-protected repository it is additionally the safer mechanism —
137
+ * `protected_branches` resolves against a notion of protection whose classic
138
+ * endpoint 404s here (see the module header), so switching to it would trade a
139
+ * precise restriction for one whose evaluation is exactly the ambiguity the
140
+ * `forge-protection-settings` re-scope forbids relying on.
141
+ *
142
+ * **An empty environment set returns `null`, not `true`.** The first draft
143
+ * returned `true` on the reasoning that with no environment there is no
144
+ * deployment surface to leave open. That is sound about a *confirmed* empty
145
+ * set and wrong about the value actually reaching this function: an empty
146
+ * array is what a failed fetch, a truncated page, or a caller that never
147
+ * queried also produces, and collapsing those into `satisfied` is exactly the
148
+ * quiet false-positive the three-state row exists to prevent. `null` maps to
149
+ * `unread`, which is the state this module already reserves for a value nobody
150
+ * established. A caller that has genuinely confirmed zero environments can say
151
+ * so by passing `true` itself; it cannot be inferred from the array alone.
152
+ *
153
+ * **Wildcard patterns.** A custom policy's NAMES live behind a second call
154
+ * (`GET .../environments/{name}/deployment-branch-policies`) and accept globs,
155
+ * so `custom_branch_policies: true` alone cannot distinguish a policy pinned to
156
+ * `main` from one whose pattern is `*`. Pass `patternsByEnv` when those names
157
+ * have been read: an environment whose patterns admit everything is reported
158
+ * unrestricted. Omit it and the flag is trusted — the narrower guarantee was
159
+ * not checked, which is stated here rather than left for a reader to discover.
160
+ *
161
+ * **A confirmed-EMPTY pattern list also yields `false`, and it is NOT the same
162
+ * case as a wildcard.** With `custom_branch_policies: true` and zero named
163
+ * patterns nothing matches, so nothing can deploy — strictly the most
164
+ * restrictive posture there is, and the exact opposite of `*`. Both land on
165
+ * `false` deliberately, because this row feeds an ACTION line rather than a
166
+ * permission: an environment configured to accept no deployment at all is a
167
+ * misconfiguration worth a human look, and `false` surfaces it where `true`
168
+ * would silently bless it. The direction is the safe one — it can under-report
169
+ * satisfaction, never over-report it. Written down because the two states are
170
+ * genuinely opposite, and a reader deriving the behaviour from the expression
171
+ * alone would reasonably take it for a bug.
172
+ */
173
+ export function deployRestrictedFrom(
174
+ environments: readonly EnvironmentPolicy[],
175
+ patternsByEnv?: Readonly<Record<string, readonly string[]>>,
176
+ ): boolean | null {
177
+ if (environments.length === 0) return null;
178
+ return environments.every((env) => {
179
+ const policy = env.deployment_branch_policy;
180
+ if (policy === null || policy === undefined) return false;
181
+ if (policy.protected_branches === true) return true;
182
+ if (policy.custom_branch_policies !== true) return false;
183
+ const patterns = patternsByEnv?.[env.name];
184
+ if (patterns === undefined) return true;
185
+ // A policy admitting every ref restricts nothing, whatever the flag says.
186
+ return patterns.length > 0 && !patterns.some((p) => p === '*' || p === '**');
187
+ });
188
+ }
189
+
76
190
  const SOURCE = {
77
191
  rulesets: 'GET repos/{owner}/{repo}/rulesets + GET .../rulesets/{id}',
78
192
  repo: 'GET repos/{owner}/{repo} (allow_auto_merge)',