@event4u/agent-config 16.0.0 → 16.2.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 (382) 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 +289 -140
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +74 -27
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/commands/work.md +4 -0
  24. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  25. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
  26. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  27. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
  28. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  29. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  30. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  31. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  32. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  33. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  34. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  35. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  36. package/dist/agent-src/rules/delegation-policy.md +23 -45
  37. package/dist/agent-src/rules/design-fidelity.md +32 -37
  38. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  39. package/dist/agent-src/rules/test-first.md +41 -0
  40. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  41. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  42. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  44. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  45. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  46. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  47. package/dist/agent-src/skills/design-review/SKILL.md +80 -4
  48. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  49. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
  50. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  51. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  52. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  53. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  54. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  55. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  56. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  57. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  58. package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
  59. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  60. package/dist/agent-src/templates/agent-settings.md +1 -1
  61. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  62. package/dist/agent-src/templates/roadmaps.md +218 -0
  63. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  64. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  65. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  66. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  67. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  68. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  69. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  70. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  71. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  72. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  73. package/dist/cli/python/knowledge_ingest.js +1 -1
  74. package/dist/cli/python/workspace_drive.js +1 -1
  75. package/dist/cli-delegate/check_update_banner.js +40 -2
  76. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  77. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
  78. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  79. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  80. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  81. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
  82. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  83. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
  84. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
  85. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
  86. package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
  87. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  88. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  89. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  90. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  91. package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
  92. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  93. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  94. package/dist/cli-delegate/cmd_conformance.js +985 -831
  95. package/dist/cli-delegate/cmd_doctor.js +7 -5
  96. package/dist/cli-delegate/cmd_explain.js +2 -2
  97. package/dist/cli-delegate/cmd_export.js +9 -7
  98. package/dist/cli-delegate/cmd_fleet.js +4 -2
  99. package/dist/cli-delegate/cmd_handoff.js +7 -7
  100. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  101. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  102. package/dist/cli-delegate/cmd_migrate.js +2 -2
  103. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  104. package/dist/cli-delegate/cmd_preflight.js +4 -2
  105. package/dist/cli-delegate/cmd_prune.js +10 -8
  106. package/dist/cli-delegate/cmd_route_audit.js +3 -3
  107. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  108. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  109. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  110. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  111. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  112. package/dist/cli-delegate/cmd_sync.js +10 -8
  113. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  114. package/dist/cli-delegate/cmd_update.js +1 -1
  115. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  116. package/dist/cli-delegate/cmd_validate.js +12 -10
  117. package/dist/cli-delegate/cmd_versions.js +1 -1
  118. package/dist/discovery/deprecation-report.md +1 -1
  119. package/dist/discovery/discovery-manifest.json +90 -43
  120. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  121. package/dist/discovery/discovery-manifest.summary.md +4 -4
  122. package/dist/discovery/dormancy-report.md +1 -1
  123. package/dist/discovery/orphan-report.md +1 -1
  124. package/dist/discovery/packs.json +10 -8
  125. package/dist/discovery/trust-report.md +3 -3
  126. package/dist/discovery/workspaces.json +8 -6
  127. package/dist/hooks/dispatch.js +7682 -2772
  128. package/dist/hooks/dispatch.sha256 +1 -0
  129. package/dist/install/claudeRuleRewrite.js +14 -3
  130. package/dist/install/conflict.js +60 -10
  131. package/dist/install/conflictTracker.js +218 -0
  132. package/dist/install/fsPrimitives.js +54 -0
  133. package/dist/install/install.mjs +2200 -1559
  134. package/dist/install/manifestFiles.js +259 -0
  135. package/dist/install/preflight.js +14 -2
  136. package/dist/install/preserve.js +157 -0
  137. package/dist/install/recordedOwnership.js +132 -0
  138. package/dist/install/txlog.js +11 -2
  139. package/dist/mcp/registry-manifest.json +3 -3
  140. package/dist/mcp/server.json +2 -2
  141. package/dist/mcp/server.mjs +3066 -1470
  142. package/dist/router.json +1 -1
  143. package/dist/scripts/_lib/agent_settings.js +3 -2
  144. package/dist/scripts/_lib/fs_atomic.js +167 -0
  145. package/dist/scripts/_lib/installed_tools.js +460 -0
  146. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  147. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  148. package/dist/server/routes/install.js +8 -0
  149. package/dist/server/routes/wizard.js +9 -3
  150. package/dist/server/schemas/settings.js +40 -5
  151. package/dist/shared/missionExecution.js +97 -0
  152. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  153. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  154. package/dist/ui/index.html +2 -2
  155. package/docs/MIGRATION.md +103 -0
  156. package/docs/contracts/settings-classes.md +28 -7
  157. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  158. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  159. package/docs/guidelines/design-fidelity-routing.md +29 -0
  160. package/package.json +4 -4
  161. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  162. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  163. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  164. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  165. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  166. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  167. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  168. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  169. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  170. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  171. package/src/config/agent-settings.template.yml +168 -27
  172. package/src/config/auto-rule-budget.json +16 -0
  173. package/src/config/ci-local-parity.yml +36 -5
  174. package/src/config/continuity-surface.json +23 -2
  175. package/src/config/gate-coverage.yml +377 -13
  176. package/src/config/gate-violation-baselines.json +10 -6
  177. package/src/config/hook-latency-budget.json +21 -0
  178. package/src/config/hook-token-budget.json +4 -2
  179. package/src/config/loop-surfaces.yaml +215 -0
  180. package/src/config/metric-registry.yml +114 -0
  181. package/src/config/placeholder-drift-budget.json +10 -0
  182. package/src/config/platform-anchor.json +3 -2
  183. package/src/config/release-gate-locality.yml +11 -0
  184. package/src/config/routing-coverage-seed.json +33 -5
  185. package/src/config/rule-enforcement-baseline.json +0 -1
  186. package/src/install/claudeRuleRewrite.ts +16 -2
  187. package/src/install/conflict.ts +65 -9
  188. package/src/install/conflictTracker.ts +299 -0
  189. package/src/install/fsPrimitives.ts +54 -0
  190. package/src/install/manifestFiles.ts +312 -0
  191. package/src/install/preflight.ts +16 -2
  192. package/src/install/preserve.ts +205 -0
  193. package/src/install/recordedOwnership.ts +149 -0
  194. package/src/install/txlog.ts +11 -2
  195. package/src/install/types.ts +19 -4
  196. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  197. package/src/scripts/_cli/cmd_doctor.ts +7 -20
  198. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  199. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  200. package/src/scripts/_cli/doctor_execution.ts +173 -0
  201. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  202. package/src/scripts/_lib/activation_payload.ts +247 -0
  203. package/src/scripts/_lib/agent_settings.ts +4 -2
  204. package/src/scripts/_lib/authority_path.ts +102 -0
  205. package/src/scripts/_lib/body_portable.ts +102 -0
  206. package/src/scripts/_lib/cascade_base.ts +94 -0
  207. package/src/scripts/_lib/cc_transcript.ts +132 -1
  208. package/src/scripts/_lib/changelog_eras.ts +7 -2
  209. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  210. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  211. package/src/scripts/_lib/council_transport.ts +129 -0
  212. package/src/scripts/_lib/delivery_ready.ts +112 -0
  213. package/src/scripts/_lib/forge_protection.ts +381 -0
  214. package/src/scripts/_lib/forge_reader.ts +558 -0
  215. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  216. package/src/scripts/_lib/host_env_write.ts +111 -0
  217. package/src/scripts/_lib/json_python_parity.ts +127 -0
  218. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  219. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  220. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  221. package/src/scripts/_lib/mission_record.ts +190 -0
  222. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  223. package/src/scripts/_lib/obligations.ts +513 -0
  224. package/src/scripts/_lib/orchestration_record.ts +11 -20
  225. package/src/scripts/_lib/probe_inputs.ts +271 -0
  226. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  227. package/src/scripts/_lib/release_highlights.ts +201 -11
  228. package/src/scripts/_lib/release_holds.ts +493 -0
  229. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  230. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  231. package/src/scripts/_lib/rides_along.ts +102 -0
  232. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  233. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  234. package/src/scripts/_lib/structural_hiding.ts +622 -0
  235. package/src/scripts/_lib/test_delta.ts +166 -0
  236. package/src/scripts/_lib/test_provenance.ts +171 -0
  237. package/src/scripts/_lib/turn_end_refusals.ts +525 -4
  238. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  239. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  240. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  241. package/src/scripts/_lib/verification_command.ts +324 -0
  242. package/src/scripts/_lib/verification_evidence.ts +647 -0
  243. package/src/scripts/_lib/verify_clause.ts +280 -0
  244. package/src/scripts/ai_council/config.ts +8 -11
  245. package/src/scripts/ai_council/fallback_config.ts +5 -3
  246. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  247. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  248. package/src/scripts/ask_block_census.ts +285 -15
  249. package/src/scripts/before_complete_hook.ts +340 -23
  250. package/src/scripts/bench_hook_latency.ts +412 -5
  251. package/src/scripts/cache_realization_report.ts +61 -1
  252. package/src/scripts/check_argument_hint.ts +287 -0
  253. package/src/scripts/check_claims.ts +125 -1
  254. package/src/scripts/check_enforcement_coverage.ts +38 -9
  255. package/src/scripts/check_enforcement_matrix.ts +866 -0
  256. package/src/scripts/check_finding_dispositions.ts +22 -3
  257. package/src/scripts/check_gate_reachability.ts +173 -10
  258. package/src/scripts/check_generator_sync.ts +1025 -0
  259. package/src/scripts/check_host_docs_digest.ts +786 -0
  260. package/src/scripts/check_host_format_column.ts +559 -0
  261. package/src/scripts/check_input_references.ts +228 -0
  262. package/src/scripts/check_inputs_parity.ts +235 -0
  263. package/src/scripts/check_kernel_edit_ratified.ts +47 -5
  264. package/src/scripts/check_kill_switch_table.ts +294 -0
  265. package/src/scripts/check_placeholder_drift.ts +221 -0
  266. package/src/scripts/check_preamble_payload_budget.ts +75 -2
  267. package/src/scripts/check_read_surface_coverage.ts +515 -0
  268. package/src/scripts/check_release_highlights.ts +33 -1
  269. package/src/scripts/check_release_holds.ts +568 -0
  270. package/src/scripts/check_routing_coverage.ts +378 -11
  271. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  272. package/src/scripts/check_static_parity.ts +17 -0
  273. package/src/scripts/check_test_delta.ts +294 -0
  274. package/src/scripts/check_test_weakening.ts +299 -0
  275. package/src/scripts/ci_settle.ts +145 -19
  276. package/src/scripts/closure_scan.ts +447 -0
  277. package/src/scripts/compile_hook_manifest.ts +102 -18
  278. package/src/scripts/council_record_shape.ts +209 -0
  279. package/src/scripts/external_sources_denylist.json +29 -4
  280. package/src/scripts/hook_manifest.json +1 -1
  281. package/src/scripts/hook_manifest.yaml +127 -11
  282. package/src/scripts/hooks/block_config_weakening.ts +229 -4
  283. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  284. package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
  285. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  286. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  287. package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
  288. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  289. package/src/scripts/hooks/concern_registry.ts +6 -0
  290. package/src/scripts/hooks/concern_timings.ts +94 -0
  291. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  292. package/src/scripts/hooks/design_pass_hook.ts +221 -5
  293. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  294. package/src/scripts/hooks/dispatch_hook.ts +25 -19
  295. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  296. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  297. package/src/scripts/hooks/evidence_independence.ts +97 -2
  298. package/src/scripts/hooks/exit_codes.ts +128 -0
  299. package/src/scripts/hooks/host_lowering.json +1 -0
  300. package/src/scripts/hooks/host_lowering.ts +128 -3
  301. package/src/scripts/hooks/host_lowering.yaml +262 -46
  302. package/src/scripts/hooks/host_semantics.ts +30 -2
  303. package/src/scripts/hooks/injection_budget.ts +9 -2
  304. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  305. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  306. package/src/scripts/hooks/memory_recall_hook.ts +539 -0
  307. package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
  308. package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
  309. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  310. package/src/scripts/hooks/permission_gate.ts +1 -2
  311. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  312. package/src/scripts/hooks/replay_hook.ts +2 -1
  313. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  314. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  315. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  316. package/src/scripts/hooks/rule_inject_hook.ts +55 -4
  317. package/src/scripts/hooks/run_continuation_hook.ts +30 -35
  318. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  319. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  320. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  321. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  322. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  323. package/src/scripts/hooks/table_fingerprint.ts +49 -0
  324. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  325. package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
  326. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  327. package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
  328. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  329. package/src/scripts/hooks_doctor.ts +85 -0
  330. package/src/scripts/hooks_status.ts +19 -0
  331. package/src/scripts/install.ts +109 -210
  332. package/src/scripts/janitor.ts +7 -0
  333. package/src/scripts/lint_decision_classes.ts +464 -0
  334. package/src/scripts/lint_deny_text.ts +419 -0
  335. package/src/scripts/lint_exit_codes.ts +262 -0
  336. package/src/scripts/lint_hook_manifest.ts +66 -0
  337. package/src/scripts/lint_major_migration_sections.ts +450 -0
  338. package/src/scripts/lint_plan_risk_register.ts +47 -3
  339. package/src/scripts/lint_roadmap_blockers.ts +139 -12
  340. package/src/scripts/lint_roadmap_producers.ts +202 -0
  341. package/src/scripts/lint_skill_descriptions.ts +1 -1
  342. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  343. package/src/scripts/mcp_server/prompts.ts +142 -1
  344. package/src/scripts/measure_rule_budget.ts +101 -5
  345. package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
  346. package/src/scripts/measure_turn_end_gate.ts +193 -0
  347. package/src/scripts/memory_lookup.ts +1 -1
  348. package/src/scripts/new_roadmap.ts +37 -0
  349. package/src/scripts/orchestration_record.ts +0 -5
  350. package/src/scripts/prepack-check.mjs +53 -0
  351. package/src/scripts/release.ts +14 -98
  352. package/src/scripts/release_drill.ts +267 -1
  353. package/src/scripts/release_publication.ts +64 -1
  354. package/src/scripts/report_enforcement_drift.ts +566 -0
  355. package/src/scripts/report_host_injection_effect.ts +3 -11
  356. package/src/scripts/report_invocation_surface.ts +383 -0
  357. package/src/scripts/report_obligation_carriers.ts +699 -0
  358. package/src/scripts/report_obligation_writer_split.ts +236 -0
  359. package/src/scripts/report_skill_menu_census.ts +208 -0
  360. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  361. package/src/scripts/roadmap_verify_share.ts +213 -0
  362. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  363. package/src/scripts/schemas/command.schema.json +48 -0
  364. package/src/scripts/schemas/review-findings.schema.json +4 -0
  365. package/src/scripts/schemas/skill.schema.json +44 -0
  366. package/src/scripts/scope_growth.ts +219 -0
  367. package/src/scripts/self_review_gate.ts +359 -10
  368. package/src/scripts/semantic_parity_census.ts +314 -0
  369. package/src/scripts/session_eol_report.ts +54 -1
  370. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  371. package/src/scripts/trigger_eval_grandfather.json +0 -3
  372. package/src/scripts/trigger_eval_rotation.ts +122 -27
  373. package/src/scripts/ui_conformance_probe.ts +774 -0
  374. package/src/scripts/update_prices.ts +85 -3
  375. package/src/scripts/write_bundle_digest.mjs +74 -0
  376. package/src/server/schemas/settings.ts +62 -7
  377. package/src/shared/missionExecution.ts +126 -0
  378. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  379. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
  380. package/dist/ui/assets/index-D0jaCRPM.js +0 -29
  381. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
  382. /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
@@ -0,0 +1,568 @@
1
+ #!/usr/bin/env -S npx tsx
2
+ /**
3
+ * check_release_holds — the one evaluator every release boundary reads.
4
+ *
5
+ * Template rule 28. Four modes, and the split matters because two of them run
6
+ * on completely different paths:
7
+ *
8
+ * --lint a malformed declaration reddens NORMAL CI, so a broken hold
9
+ * cannot fail open by being unparseable.
10
+ * --status list every declared hold and its state. Read-only.
11
+ * --require-safe the RELEASE-path question: may a cut be published now?
12
+ * --selftest every row of rule 28's state table, including the
13
+ * not-evaluable row, asserted against fixtures.
14
+ *
15
+ * Exit codes: 0 = clean / safe · 1 = a violation or a refusal · 2 = usage.
16
+ *
17
+ * The load-bearing behaviour, and the one worth re-reading before changing
18
+ * anything here: `not-evaluable` REFUSES, on every channel. An evaluator that
19
+ * cannot read a declaration has learned nothing about whether the tree is safe,
20
+ * and "learned nothing" is not "safe". There is deliberately no flag, label or
21
+ * trailer anywhere in this file that turns a refusal into a pass.
22
+ */
23
+
24
+ import * as fs from 'node:fs';
25
+ import * as os from 'node:os';
26
+ import * as path from 'node:path';
27
+ import { fileURLToPath, pathToFileURL } from 'node:url';
28
+
29
+ import {
30
+ evaluateFile,
31
+ evaluateHolds,
32
+ lifecycleViolations,
33
+ refuses,
34
+ refusalReport,
35
+ REFUSING_DIRS,
36
+ type CutChannel,
37
+ type Hold,
38
+ } from './_lib/release_holds.js';
39
+ import { REPO_ROOT } from './lint_roadmap_blockers.js';
40
+ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
41
+ import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
42
+
43
+ const _HERE = fileURLToPath(import.meta.url);
44
+
45
+ /**
46
+ * The folders a release question must read.
47
+ *
48
+ * Wider than `lint_roadmap_blockers`' glob on purpose — that gate reads the
49
+ * active tree plus `stubs/` and stops there (2026-09-28), while rule 28's
50
+ * per-folder lifecycle says a window does not disappear by moving the file, and
51
+ * a `later/` roadmap still refuses. `archive/` and `skipped/` are included
52
+ * because the move INTO them is refused while a window is open — a file that
53
+ * reached them with an open window is a defect this must still see, not one it
54
+ * should be blind to.
55
+ */
56
+ const HOLD_DIRS = ['', 'later', 'archive', 'skipped', 'stubs'] as const;
57
+
58
+ function globRoadmaps(root = REPO_ROOT): string[] {
59
+ const out: string[] = [];
60
+ for (const sub of HOLD_DIRS) {
61
+ const dir = path.join(root, 'agents', 'roadmaps', sub);
62
+ let entries: fs.Dirent[];
63
+ try {
64
+ entries = fs.readdirSync(dir, { withFileTypes: true });
65
+ } catch {
66
+ continue;
67
+ }
68
+ for (const e of entries) {
69
+ if (e.isFile() && e.name.endsWith('.md')) {
70
+ out.push(path.join(dir, e.name));
71
+ }
72
+ }
73
+ }
74
+ return out.sort();
75
+ }
76
+
77
+ function collect(root = REPO_ROOT): Hold[] {
78
+ return globRoadmaps(root).flatMap((f) => evaluateFile(f));
79
+ }
80
+
81
+ function rel(p: string): string {
82
+ return path.relative(REPO_ROOT, p).split(path.sep).join('/');
83
+ }
84
+
85
+ function cmdLint(root: string): number {
86
+ // A broken glob is this gate's one silent failure mode: with zero files it
87
+ // would print "0 declared hold(s), all well-formed" and exit 0, certifying
88
+ // a coverage that does not exist. "Zero holds" is a real and normal state;
89
+ // "zero FILES under agents/roadmaps" never is.
90
+ try {
91
+ assertScanned({
92
+ gate: 'check_release_holds',
93
+ scanned: globRoadmaps(root).length,
94
+ units: 'roadmap file(s)',
95
+ roots: ['agents/roadmaps'],
96
+ });
97
+ } catch (e) {
98
+ if (e instanceof DeadScopeError) {
99
+ process.stderr.write(`❌ ${e.message}\n`);
100
+ return 1;
101
+ }
102
+ throw e;
103
+ }
104
+
105
+ const files = globRoadmaps(root);
106
+ const holds = collect(root);
107
+ const bad = holds.filter((h) => h.state === 'not-evaluable');
108
+
109
+ // The lifecycle half. A move already made is what a gate can see: if a file
110
+ // with a live window is sitting in `archive/` or `skipped/`, the refused
111
+ // move happened, and reddening CI is how the move is refused in practice.
112
+ const lifecycle = files.flatMap((f) => {
113
+ const hs = evaluateFile(f);
114
+ if (hs.length === 0) {
115
+ return [];
116
+ }
117
+ let text: string;
118
+ try {
119
+ text = fs.readFileSync(f, 'utf8');
120
+ } catch {
121
+ return [];
122
+ }
123
+ return lifecycleViolations(f, hs, text);
124
+ });
125
+
126
+ process.stdout.write(`scanned: ${files.length}\n`);
127
+ if (bad.length === 0 && lifecycle.length === 0) {
128
+ process.stdout.write(
129
+ `✅ check-release-holds: ${holds.length} declared hold(s), all well-formed, ` +
130
+ `lifecycle clean\n`,
131
+ );
132
+ return 0;
133
+ }
134
+ if (bad.length > 0) {
135
+ process.stderr.write('❌ check-release-holds: malformed declaration(s):\n');
136
+ for (const h of bad) {
137
+ for (const why of h.malformed) {
138
+ process.stderr.write(` - ${rel(h.file)} · hold \`${h.id}\`: ${why}\n`);
139
+ }
140
+ }
141
+ }
142
+ if (lifecycle.length > 0) {
143
+ process.stderr.write('❌ check-release-holds: lifecycle violation(s):\n');
144
+ for (const v of lifecycle) {
145
+ process.stderr.write(` - ${rel(v.file)} · hold \`${v.holdId}\`: ${v.reason}\n`);
146
+ }
147
+ }
148
+ return 1;
149
+ }
150
+
151
+ /**
152
+ * Answer the move BEFORE it happens, so the loop asks rather than discovers.
153
+ *
154
+ * `--can-move <file> <archive|skipped|later>`; exit 0 permits, 1 refuses. The
155
+ * `--lint` gate above is the backstop that catches a move made without asking.
156
+ */
157
+ function cmdCanMove(file: string, dest: string): number {
158
+ if (!(REFUSING_DIRS as readonly string[]).includes(dest) && dest !== 'later') {
159
+ process.stderr.write(`❌ --can-move destination must be archive, skipped or later\n`);
160
+ return 2;
161
+ }
162
+ const holds = evaluateFile(file);
163
+ const live = holds.filter((h) => h.state === 'open' || h.state === 'not-evaluable');
164
+ if (live.length === 0) {
165
+ process.stdout.write(`✅ no live window — the move to \`${dest}/\` is permitted\n`);
166
+ return 0;
167
+ }
168
+ if (dest === 'later') {
169
+ let text = '';
170
+ try {
171
+ text = fs.readFileSync(file, 'utf8');
172
+ } catch {
173
+ /* an unreadable file is already not-evaluable above */
174
+ }
175
+ const problems = lifecycleViolations(
176
+ path.join(REPO_ROOT, 'agents', 'roadmaps', 'later', path.basename(file)),
177
+ holds,
178
+ text,
179
+ );
180
+ if (problems.length === 0) {
181
+ process.stdout.write(
182
+ `✅ the move to \`later/\` is permitted — the window stays listed and still refuses\n`,
183
+ );
184
+ return 0;
185
+ }
186
+ for (const v of problems) {
187
+ process.stderr.write(`❌ hold \`${v.holdId}\`: ${v.reason}\n`);
188
+ }
189
+ return 1;
190
+ }
191
+ for (const h of live) {
192
+ process.stderr.write(
193
+ `❌ REFUSED: hold \`${h.id}\` is ${h.state} — a roadmap with a live window may not ` +
194
+ `move to \`${dest}/\`. Finish \`${h.clearedBy || '?'}\`, or move it to \`later/\`.\n`,
195
+ );
196
+ }
197
+ return 1;
198
+ }
199
+
200
+ function cmdStatus(root: string): number {
201
+ const holds = collect(root);
202
+ if (holds.length === 0) {
203
+ process.stdout.write('No release holds declared. Every cut is permitted.\n');
204
+ return 0;
205
+ }
206
+ for (const h of holds) {
207
+ process.stdout.write(
208
+ `${h.state.padEnd(14)} ${h.id.padEnd(28)} channel=${h.channel.padEnd(6)} ` +
209
+ `opens=${h.openedBy || '-'} clears=${h.clearedBy || '-'} ${rel(h.file)}\n`,
210
+ );
211
+ }
212
+ return 0;
213
+ }
214
+
215
+ function cmdRequireSafe(root: string, cut: CutChannel): number {
216
+ const report = refusalReport(collect(root), cut);
217
+ if (report === null) {
218
+ process.stdout.write(`✅ release-holds: safe to cut (channel ${cut})\n`);
219
+ return 0;
220
+ }
221
+ process.stderr.write(`❌ ${report}\n`);
222
+ return 1;
223
+ }
224
+
225
+ /** Rule 28's state table, one fixture per row, plus the two channel cases. */
226
+ function cmdSelftest(): number {
227
+ // Built line-by-line rather than as one template literal on purpose: a
228
+ // heredoc whose lines begin with `##` reads to `lint_code_comments` as a
229
+ // markdown heading left in source, and the fixture would redden a gate it
230
+ // has nothing to do with.
231
+ const base = (open: string, clear: string, channel = 'all', extra = '') =>
232
+ [
233
+ '',
234
+ '## Phase 1',
235
+ `- [${open}] **1.1 opener** <!-- opens-hold: probe -->`,
236
+ " verify: the opener's own check",
237
+ `- [${clear}] **1.2 clearer** <!-- clears-hold: probe -->`,
238
+ ' verify: the check that proves the state is repaired',
239
+ '',
240
+ '## Release holds',
241
+ '',
242
+ '### hold: probe',
243
+ `- **Channel:** ${channel}`,
244
+ '- **Opened by:** 1.1',
245
+ '- **Cleared by:** 1.2',
246
+ '- **State:** the surface is half wired while this is open.',
247
+ '- **Why not a guard:** the entry point is reachable and cannot be made inert.',
248
+ extra,
249
+ ].join('\n');
250
+
251
+ type Case = { name: string; text: string; state: string; all: boolean; latest: boolean };
252
+ const cases: Case[] = [
253
+ { name: 'unopened permits', text: base(' ', ' '), state: 'unopened', all: false, latest: false },
254
+ { name: 'open/all refuses every cut', text: base('x', ' '), state: 'open', all: true, latest: true },
255
+ { name: 'cleared permits', text: base('x', 'x'), state: 'cleared', all: false, latest: false },
256
+ {
257
+ name: 'open/latest refuses stable, permits prerelease',
258
+ text: base('x', ' ', 'latest'),
259
+ state: 'open',
260
+ all: true,
261
+ latest: false,
262
+ },
263
+ // The load-bearing row. Each of these is a DIFFERENT way to be
264
+ // unreadable, and every one of them must refuse on BOTH channels.
265
+ {
266
+ name: 'not-evaluable: missing `Why not a guard:`',
267
+ text: base('x', ' ').replace(/- \*\*Why not a guard:\*\*.*\n/, ''),
268
+ state: 'not-evaluable',
269
+ all: true,
270
+ latest: true,
271
+ },
272
+ {
273
+ name: 'not-evaluable: unknown channel',
274
+ text: base('x', ' ', 'someday'),
275
+ state: 'not-evaluable',
276
+ all: true,
277
+ latest: true,
278
+ },
279
+ {
280
+ name: 'not-evaluable: clearer carries no `verify:`',
281
+ text: base('x', ' ').replace(
282
+ ' verify: the check that proves the state is repaired\n',
283
+ '',
284
+ ),
285
+ state: 'not-evaluable',
286
+ all: true,
287
+ latest: true,
288
+ },
289
+ {
290
+ name: 'not-evaluable: marker is not on a checkbox',
291
+ text: base('x', ' ').replace(
292
+ '- [ ] **1.2 clearer** <!-- clears-hold: probe -->',
293
+ 'The clearer is step 1.2 <!-- clears-hold: probe -->',
294
+ ),
295
+ state: 'not-evaluable',
296
+ all: true,
297
+ latest: true,
298
+ },
299
+ {
300
+ name: 'not-evaluable: hold entry names a version (rule 13)',
301
+ text: base('x', ' ', 'all', '- **Note:** ships in 15.1.0\n'),
302
+ state: 'not-evaluable',
303
+ all: true,
304
+ latest: true,
305
+ },
306
+ {
307
+ name: 'not-evaluable: duplicate hold id',
308
+ text: `${base('x', ' ')}\n### hold: probe\n- **Channel:** all\n`,
309
+ state: 'not-evaluable',
310
+ all: true,
311
+ latest: true,
312
+ },
313
+ {
314
+ name: 'not-evaluable: marker with no entry',
315
+ text: '## Phase 1\n- [x] **1.1 opener** <!-- opens-hold: orphan -->\n',
316
+ state: 'not-evaluable',
317
+ all: true,
318
+ latest: true,
319
+ },
320
+ ];
321
+
322
+ // The two NEGATIVE cases. Without them a check that refused everything
323
+ // would pass every row above, which is the tautology this guards against.
324
+ const negatives: { name: string; text: string }[] = [
325
+ { name: 'a roadmap with no holds section declares nothing', text: '## Phase 1\n- [ ] **1.1 x**\n' },
326
+ {
327
+ name: 'a fenced documentation example is not a declaration',
328
+ text: '```markdown\n## Release holds\n\n### hold: doc-example\n- **Channel:** all\n```\n',
329
+ },
330
+ ];
331
+
332
+ // Lifecycle, rule 28's per-folder half. Asserted on the SAME fixture text
333
+ // under three different paths, so the only variable is the folder.
334
+ const openText = base('x', ' ');
335
+ const lifecycle: { name: string; file: string; text: string; want: number }[] = [
336
+ {
337
+ name: 'archive/ refuses a live window',
338
+ file: 'agents/roadmaps/archive/probe.md',
339
+ text: openText,
340
+ want: 1,
341
+ },
342
+ {
343
+ name: 'skipped/ refuses a live window',
344
+ file: 'agents/roadmaps/skipped/probe.md',
345
+ text: openText,
346
+ want: 1,
347
+ },
348
+ {
349
+ name: 'later/ refuses when entry_condition.what does not name the hold',
350
+ file: 'agents/roadmaps/later/probe.md',
351
+ text: `---\nstatus: later\nentry_condition:\n what: something else entirely\n when: later\n who: maintainer\n---\n${openText}`,
352
+ want: 1,
353
+ },
354
+ {
355
+ name: 'later/ permits when entry_condition.what names the hold',
356
+ file: 'agents/roadmaps/later/probe.md',
357
+ text: `---\nstatus: later\nentry_condition:\n what: the probe hold stays open until 1.2 lands\n when: later\n who: maintainer\n---\n${openText}`,
358
+ want: 0,
359
+ },
360
+ {
361
+ name: 'the active root permits a live window',
362
+ file: 'agents/roadmaps/probe.md',
363
+ text: openText,
364
+ want: 0,
365
+ },
366
+ {
367
+ name: 'archive/ permits a CLEARED window',
368
+ file: 'agents/roadmaps/archive/probe.md',
369
+ text: base('x', 'x'),
370
+ want: 0,
371
+ },
372
+ ];
373
+
374
+ let failed = 0;
375
+ for (const c of cases) {
376
+ const holds = evaluateHolds(c.text, 'selftest.md');
377
+ const h = holds[0];
378
+ const ok =
379
+ h !== undefined &&
380
+ h.state === c.state &&
381
+ refuses(h, 'all') === c.all &&
382
+ refuses(h, 'latest') === c.latest;
383
+ if (!ok) {
384
+ failed += 1;
385
+ process.stderr.write(
386
+ ` ❌ ${c.name}: got state=${h?.state ?? '<none>'} ` +
387
+ `all=${h ? refuses(h, 'all') : '-'} latest=${h ? refuses(h, 'latest') : '-'}; ` +
388
+ `want state=${c.state} all=${c.all} latest=${c.latest}\n`,
389
+ );
390
+ }
391
+ }
392
+ for (const n of negatives) {
393
+ const holds = evaluateHolds(n.text, 'selftest.md');
394
+ if (holds.length !== 0) {
395
+ failed += 1;
396
+ process.stderr.write(` ❌ ${n.name}: got ${holds.length} hold(s), want 0\n`);
397
+ }
398
+ }
399
+
400
+ for (const l of lifecycle) {
401
+ const got = lifecycleViolations(l.file, evaluateHolds(l.text, l.file), l.text).length;
402
+ if (got !== l.want) {
403
+ failed += 1;
404
+ process.stderr.write(` ❌ ${l.name}: got ${got} violation(s), want ${l.want}\n`);
405
+ }
406
+ }
407
+
408
+ const total = cases.length + negatives.length + lifecycle.length;
409
+ if (failed > 0) {
410
+ process.stderr.write(`❌ check-release-holds --selftest: ${failed}/${total} failed\n`);
411
+ return 1;
412
+ }
413
+ process.stdout.write(
414
+ `✅ check-release-holds --selftest: ${total}/${total} — every state-table row ` +
415
+ `(including not-evaluable, 7 ways), both negatives, and all six lifecycle cases\n`,
416
+ );
417
+ return 0;
418
+ }
419
+
420
+ /**
421
+ * The house self-test: fixture trees on disk, this gate's real CLI, exit codes.
422
+ *
423
+ * Two accept cases are not padding. A gate that refused everything would pass
424
+ * every reject case, and the one that matters most is the VALID OPEN WINDOW:
425
+ * normal CI must stay green on exactly the tree state the release path refuses,
426
+ * and nothing else in this suite would notice if `--lint` started reddening it.
427
+ */
428
+ function cmdConventionSelfTest(): number {
429
+ const script = 'src/scripts/check_release_holds.ts';
430
+ const mk = (name: string, files: Record<string, string>): string => {
431
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), `crh-${name}-`));
432
+ for (const [rel, body] of Object.entries(files)) {
433
+ const abs = path.join(root, rel);
434
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
435
+ fs.writeFileSync(abs, body, 'utf8');
436
+ }
437
+ return root;
438
+ };
439
+ const hold = (open: string, clear: string, extra = ''): string =>
440
+ [
441
+ '## Phase 1',
442
+ `- [${open}] **1.1 opener** <!-- opens-hold: probe -->`,
443
+ ' verify: the opener check',
444
+ `- [${clear}] **1.2 clearer** <!-- clears-hold: probe -->`,
445
+ ' verify: ./scripts-run src/scripts/check_surface_whole',
446
+ '',
447
+ '## Release holds',
448
+ '',
449
+ '### hold: probe',
450
+ '- **Channel:** all',
451
+ '- **Opened by:** 1.1',
452
+ '- **Cleared by:** 1.2',
453
+ '- **State:** the surface is half wired while this is open.',
454
+ `- **Why not a guard:** the entry point cannot be made inert.${extra}`,
455
+ '',
456
+ ].join('\n');
457
+ const at = (root: string, args: string[]): number =>
458
+ runGateCli(REPO_ROOT, script, [...args, '--root', root], REPO_ROOT);
459
+
460
+ const cases: SelfTestCase[] = [
461
+ {
462
+ name: 'a malformed declaration reddens --lint',
463
+ expect: 'reject',
464
+ run: () =>
465
+ at(
466
+ mk('bad', {
467
+ 'agents/roadmaps/r.md': hold('x', ' ').replace(
468
+ /- \*\*Why not a guard:\*\*.*\n/,
469
+ '',
470
+ ),
471
+ }),
472
+ ['--lint'],
473
+ ),
474
+ },
475
+ {
476
+ name: 'an open window refuses --require-safe',
477
+ expect: 'reject',
478
+ run: () => at(mk('open', { 'agents/roadmaps/r.md': hold('x', ' ') }), ['--require-safe']),
479
+ },
480
+ {
481
+ name: 'a live window sitting in archive/ reddens --lint',
482
+ expect: 'reject',
483
+ run: () => at(mk('arch', { 'agents/roadmaps/archive/r.md': hold('x', ' ') }), ['--lint']),
484
+ },
485
+ {
486
+ name: 'a dead scan scope reddens rather than certifying an empty corpus',
487
+ expect: 'reject',
488
+ run: () => at(mk('dead', { 'README.md': 'no roadmaps here\n' }), ['--lint']),
489
+ },
490
+ {
491
+ name: 'a clean corpus passes --lint',
492
+ expect: 'accept',
493
+ run: () =>
494
+ at(mk('clean', { 'agents/roadmaps/r.md': '## Phase 1\n- [ ] **1.1 x**\n' }), ['--lint']),
495
+ },
496
+ {
497
+ name: 'a VALID OPEN window passes --lint — normal CI stays green on it',
498
+ expect: 'accept',
499
+ run: () => at(mk('valid', { 'agents/roadmaps/r.md': hold('x', ' ') }), ['--lint']),
500
+ },
501
+ ];
502
+ return runSelfTest({ gate: 'check_release_holds', cases, minCases: 6, minRejectCases: 4 });
503
+ }
504
+
505
+ function usage(): number {
506
+ process.stderr.write(
507
+ 'usage: check_release_holds (--lint | --status | --require-safe [--channel latest|all]\n' +
508
+ ' | --can-move <file> <archive|skipped|later>\n' +
509
+ ' | --selftest | --self-test) [--root <dir>]\n' +
510
+ ' --channel all (default) a stable X.Y.Z cut — every open hold refuses\n' +
511
+ ' --channel latest a -next.N prerelease — only `Channel: all` holds refuse\n',
512
+ );
513
+ return 2;
514
+ }
515
+
516
+ function main(argv: string[] = process.argv.slice(2)): number {
517
+ const ri = argv.indexOf('--root');
518
+ const root = ri === -1 ? REPO_ROOT : path.resolve(argv[ri + 1] ?? '.');
519
+ // `--self-test` is the house convention (`_lib/gate_self_test`): it drives
520
+ // this CLI against fixture trees on disk and asserts the exit code, so what
521
+ // it proves is that the BINARY still rejects. `--selftest` is the unit-level
522
+ // state-table suite. They are complementary, not duplicates -- one can pass
523
+ // while the other fails, which is the whole reason both exist.
524
+ if (argv.includes('--self-test') && process.env['GATE_SELF_TEST_CHILD'] !== '1') {
525
+ return cmdConventionSelfTest();
526
+ }
527
+ if (argv.includes('--selftest')) {
528
+ return cmdSelftest();
529
+ }
530
+ if (argv.includes('--lint')) {
531
+ return cmdLint(root);
532
+ }
533
+ if (argv.includes('--status')) {
534
+ return cmdStatus(root);
535
+ }
536
+ if (argv.includes('--require-safe')) {
537
+ const i = argv.indexOf('--channel');
538
+ const raw = i === -1 ? 'all' : argv[i + 1];
539
+ if (raw !== 'all' && raw !== 'latest') {
540
+ process.stderr.write(`❌ --channel must be \`all\` or \`latest\`, got \`${raw ?? ''}\`\n`);
541
+ return usage();
542
+ }
543
+ return cmdRequireSafe(root, raw);
544
+ }
545
+ const mv = argv.indexOf('--can-move');
546
+ if (mv !== -1) {
547
+ const file = argv[mv + 1];
548
+ const dest = argv[mv + 2];
549
+ if (file === undefined || dest === undefined) {
550
+ return usage();
551
+ }
552
+ return cmdCanMove(path.resolve(file), dest);
553
+ }
554
+ return usage();
555
+ }
556
+
557
+ function _isCliEntry(): boolean {
558
+ if (process.argv[1] === undefined) {
559
+ return false;
560
+ }
561
+ return import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href;
562
+ }
563
+
564
+ if (_isCliEntry() || process.argv[1] === _HERE) {
565
+ process.exit(main());
566
+ }
567
+
568
+ export { collect, globRoadmaps, main, HOLD_DIRS };