@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,216 @@
1
+ /**
2
+ * Reading a Claude JSONL transcript tail, for the turn-end gate's detectors.
3
+ *
4
+ * Extracted from `hooks/turn_end_gate_hook.ts` for the reason its own detector
5
+ * E carries one line above its import: `_lib` is where this prose costs no
6
+ * ratchet debt. The gate reached 1,593 lines against a 1,500-line source
7
+ * budget when the shadow read landed, and `check_source_size_budget` only
8
+ * turns one way — so the choice was to extract or to delete comments that
9
+ * record why the reader is shaped as it is. This module is the extraction.
10
+ *
11
+ * Nothing about the behaviour moves: every function below is byte-identical to
12
+ * the version that lived in the gate, and the gate re-exports all three public
13
+ * names so no importer changes. The two guards this reader depends on stay
14
+ * where they are — `isSafeTranscriptPath` is the security-relevant
15
+ * home-confinement predicate and `isSyntheticPrompt` is the ordinal filter, and
16
+ * relocating either as a side effect of a size budget is exactly the drive-by
17
+ * this tree's diff discipline forbids.
18
+ */
19
+ import * as fs from 'node:fs';
20
+
21
+ import { isSyntheticPrompt } from '../language_mirror_hook.js';
22
+ import { isSafeTranscriptPath } from '../hooks/end_review_nudge_hook.js';
23
+
24
+ /**
25
+ * One tool call the assistant made, reduced to what a detector can reason
26
+ * about: the tool's name, the shell command when it is a shell call, and the
27
+ * target path when it is a file write.
28
+ *
29
+ * Nothing else is kept. A tool input can hold a whole file body, and this
30
+ * struct is what a refusal quotes back — so it is shaped to be INCAPABLE of
31
+ * carrying one, the same PII-exclusion-by-construction discipline
32
+ * `domain-safety-pii` § Surface 2 asks for in a log line.
33
+ */
34
+ export interface ToolCall {
35
+ name: string;
36
+ /** `Bash` only — the command line, so "did anything verify" is answerable. */
37
+ command?: string;
38
+ /** Edit/Write only — the file the turn changed, used as the evidence span. */
39
+ path?: string;
40
+ }
41
+
42
+ export interface TranscriptTail {
43
+ lastAssistant: string;
44
+ /**
45
+ * Every assistant text of the CURRENT turn, in order — reset at each genuine
46
+ * user prompt exactly like `toolCalls`, and for the same reason: a question
47
+ * asked three turns ago was answered, a question asked earlier in THIS turn
48
+ * was not. Detector E reads it; the other four read `lastAssistant`, which is
49
+ * always the last element when this is non-empty.
50
+ *
51
+ * A tool-only assistant entry contributes NOTHING here. `_messageText`
52
+ * returns null for it and the collector skips it, so the ordinary shape of a
53
+ * working turn — prose, tool call, prose — produces two elements and not
54
+ * three. That is the whole false-positive surface of detector E, and it is
55
+ * closed here rather than in the detector.
56
+ */
57
+ assistantTurnTexts: string[];
58
+ /**
59
+ * How many GENUINE user prompts the transcript carries — harness-injected
60
+ * user-role entries excluded via `isSyntheticPrompt`. This is the turn's
61
+ * identity; see `alreadyRefusedTurn` for why the prompt's text is not.
62
+ */
63
+ turnOrdinal: number;
64
+ /**
65
+ * The tool calls of the CURRENT turn, in order — reset at every genuine user
66
+ * prompt, so a verification from three turns ago cannot vouch for an edit
67
+ * made now. `_messageText` keeps only `type === 'text'` blocks, which is why
68
+ * this needed its own extraction rather than a reading of `lastAssistant`:
69
+ * tool activity is not in the prose at all.
70
+ */
71
+ toolCalls: ToolCall[];
72
+ }
73
+
74
+ /**
75
+ * The last assistant text, plus the turn ordinal, from a Claude JSONL
76
+ * transcript. Mirrors `chat_history._extract_claude_transcript_response` for
77
+ * the assistant half.
78
+ *
79
+ * `isSyntheticPrompt` is applied to every user-role entry — the same filter
80
+ * `language_mirror_hook` uses, and for the same reason it was added there
81
+ * (round-5 § 6.5): a background-task notification and a `<system-reminder>`
82
+ * both occupy the user role without being chat messages. Counting them would
83
+ * move the turn ordinal mid-turn, which is R2 finding 3.
84
+ */
85
+ export function readTranscriptTail(
86
+ transcriptPath: string,
87
+ opts: { homeDir?: string; maxBytes?: number } = {},
88
+ ): TranscriptTail {
89
+ const empty: TranscriptTail = {
90
+ lastAssistant: '',
91
+ turnOrdinal: 0,
92
+ toolCalls: [],
93
+ assistantTurnTexts: [],
94
+ };
95
+ if (!transcriptPath || !isSafeTranscriptPath(transcriptPath, opts)) return empty;
96
+ let lines: string[];
97
+ try {
98
+ // R2 finding 12: `maxBytes` was accepted in the options type and never
99
+ // used, so the declared cap was decoration. It is enforced here as well
100
+ // as inside `isSafeTranscriptPath`, because this is the read it bounds.
101
+ // The whole file still has to be walked — the turn ordinal is a count
102
+ // over all entries, not something a tail can answer — so the cap is the
103
+ // guard, not an optimisation.
104
+ if (opts.maxBytes !== undefined && fs.statSync(transcriptPath).size > opts.maxBytes) {
105
+ return empty;
106
+ }
107
+ lines = fs.readFileSync(transcriptPath, 'utf-8').split('\n');
108
+ } catch {
109
+ return empty;
110
+ }
111
+ let lastAssistant = '';
112
+ let turnOrdinal = 0;
113
+ let toolCalls: ToolCall[] = [];
114
+ let assistantTurnTexts: string[] = [];
115
+ for (const rawLine of lines) {
116
+ const line = rawLine.trim();
117
+ if (!line) continue;
118
+ let obj: unknown;
119
+ try {
120
+ obj = JSON.parse(line);
121
+ } catch {
122
+ continue;
123
+ }
124
+ if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) continue;
125
+ const entry = obj as Record<string, unknown>;
126
+ // Sidechain entries are a SUBAGENT's conversation recorded in the same
127
+ // JSONL. A subagent prompt is a genuine-looking user-role text entry
128
+ // appended mid-turn, so counting it moves the ordinal within the turn —
129
+ // finding 3's failure class in a new shape (R2 round 2, finding 3).
130
+ if (entry['isSidechain'] === true) continue;
131
+ const role = entry['type'];
132
+ if (role !== 'assistant' && role !== 'user') continue;
133
+ const msg = entry['message'];
134
+ if (typeof msg !== 'object' || msg === null || Array.isArray(msg)) continue;
135
+ const content = (msg as Record<string, unknown>)['content'];
136
+ // Tool calls are read BEFORE the text guard: an assistant entry that is
137
+ // only a tool_use block has no text at all, so `continue`-ing on a null
138
+ // text would drop exactly the entries this detector exists to see.
139
+ if (role === 'assistant') {
140
+ toolCalls.push(...extractToolCalls(content));
141
+ }
142
+ const text = _messageText(content);
143
+ if (text === null) continue;
144
+ if (role === 'assistant') {
145
+ lastAssistant = text;
146
+ assistantTurnTexts.push(text);
147
+ } else if (!isSyntheticPrompt(text)) {
148
+ turnOrdinal += 1;
149
+ // A genuine user prompt starts a new turn, so the previous turn's
150
+ // tool activity stops counting. Without this reset, a verification
151
+ // run three turns ago would vouch for an edit made now — the
152
+ // "fresh" in edit-without-FRESH-verification is this line.
153
+ toolCalls = [];
154
+ // Same boundary, same reason, for detector E: an options block the
155
+ // user has since replied to is answered, not dropped. Resetting on
156
+ // the identical line is what makes "in the SAME user turn" true of
157
+ // the array rather than merely intended.
158
+ assistantTurnTexts = [];
159
+ }
160
+ }
161
+ return {
162
+ lastAssistant: lastAssistant.trim(),
163
+ turnOrdinal,
164
+ toolCalls,
165
+ assistantTurnTexts,
166
+ };
167
+ }
168
+
169
+ /**
170
+ * Extract this entry's tool calls, keeping only name, shell command and target
171
+ * path. A tool input can hold a whole file body; nothing but those three fields
172
+ * is carried forward.
173
+ *
174
+ * Exported because `measure_turn_end_gate` scores detectors C and E over a real
175
+ * transcript corpus and therefore has to rebuild the same `ToolCall[]` the gate
176
+ * sees. A second extractor there would be a second dialect of "what the turn
177
+ * did", and the measurement would then be of that dialect rather than of the
178
+ * shipped gate — the exact population-parity defect that script's own header
179
+ * documents twice.
180
+ */
181
+ export function extractToolCalls(content: unknown): ToolCall[] {
182
+ if (!Array.isArray(content)) return [];
183
+ const out: ToolCall[] = [];
184
+ for (const blk of content) {
185
+ if (typeof blk !== 'object' || blk === null || Array.isArray(blk)) continue;
186
+ const b = blk as Record<string, unknown>;
187
+ if (b['type'] !== 'tool_use') continue;
188
+ const name = b['name'];
189
+ if (typeof name !== 'string') continue;
190
+ const input = b['input'];
191
+ const call: ToolCall = { name };
192
+ if (typeof input === 'object' && input !== null && !Array.isArray(input)) {
193
+ const inp = input as Record<string, unknown>;
194
+ const cmd = inp['command'];
195
+ if (typeof cmd === 'string') call.command = cmd;
196
+ const p = inp['file_path'] ?? inp['path'] ?? inp['notebook_path'];
197
+ if (typeof p === 'string') call.path = p;
198
+ }
199
+ out.push(call);
200
+ }
201
+ return out;
202
+ }
203
+
204
+ function _messageText(content: unknown): string | null {
205
+ if (typeof content === 'string') return content;
206
+ if (!Array.isArray(content)) return null;
207
+ const parts: string[] = [];
208
+ for (const blk of content) {
209
+ if (typeof blk !== 'object' || blk === null || Array.isArray(blk)) continue;
210
+ const b = blk as Record<string, unknown>;
211
+ if (b['type'] !== 'text') continue;
212
+ const t = b['text'];
213
+ if (typeof t === 'string') parts.push(t);
214
+ }
215
+ return parts.length > 0 ? parts.join('\n') : null;
216
+ }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Who may authorise a typed op — `road-to-adversarial-verification-and-long-runs`
3
+ * 7.3.
4
+ *
5
+ * Under a mission the sequence is: a council check that the op belongs to the
6
+ * mission → a native ask naming the exact object → execute. The council sits
7
+ * BEFORE the ask and can only subtract.
8
+ *
9
+ * ```
10
+ * THE COUNCIL MAY VETO A TYPED OP. IT MAY NEVER GRANT ONE.
11
+ * A COUNCIL VERDICT ALONE NEVER PRODUCES THE GRANT, HOWEVER UNANIMOUS.
12
+ * THE GRANT COMES FROM THE ASK, AND THE ASK NAMES THE EXACT OBJECT.
13
+ * THE ASK ALSO NAMES THE VERB AND THE TURN. A CONFIRMATION THAT CANNOT SAY
14
+ * WHICH TURN IT CAME FROM IS NOT A THIS-TURN CONFIRMATION.
15
+ * ```
16
+ *
17
+ * **Why the asymmetry is the whole design.** ADR-257: an unpaid route may
18
+ * propose and score, never decide. A council that could grant would be a second
19
+ * authorisation path around `non-destructive-by-default`'s this-turn
20
+ * confirmation — and it would be the cheaper one, so it would become the only
21
+ * one. Letting it VETO adds a refusal without adding an authority, which is the
22
+ * only direction that is safe to add for free.
23
+ *
24
+ * **An UNAVAILABLE council does not block the op**, and that is deliberate
25
+ * rather than lax: the council is a filter, so its absence removes a filter and
26
+ * leaves the ask exactly where it was. Treating unavailability as a veto would
27
+ * make an unconfigured council a silent kill switch on every typed op — the
28
+ * same-shaped wrong guess `council-availability` exists over.
29
+ *
30
+ * **What the exactness check is, and why it is two tiers.** It used to be a
31
+ * shape-free heuristic — eight characters, one of them not a letter — which
32
+ * accepted `!!!!!!!!` and `all-branches` alike, so the "exact object" guarantee
33
+ * the Hard Floor rests on was not real. It is now a universal floor that every
34
+ * op passes through, plus a per-op shape where one is declared. The floor is
35
+ * deliberately the weakest claim that cannot be wrong (an object with no
36
+ * alphanumeric run names nothing); the shape is where a category is actually
37
+ * refused. An op with NO declared shape keeps the old heuristic above the floor
38
+ * rather than being refused by default, because a Hard-Floor ask that can never
39
+ * be answered is worse than a loose one. The residual that leaves is real and
40
+ * bounded: a category still grants for an op OUTSIDE the declared set, and the
41
+ * declared set is drawn from the Hard Floor's own list of gated operations.
42
+ *
43
+ * Pure.
44
+ */
45
+
46
+ /** What the council said about this op. `null` when it was not consulted. */
47
+ export type CouncilVerdict = 'in-mission' | 'out-of-mission' | 'unavailable' | null;
48
+
49
+ /** The ask, as `non-destructive-by-default` requires it: an exact object. */
50
+ export interface ExactObjectAsk {
51
+ /** e.g. `force-push`. Blank is not a verb, and a blank verb never grants. */
52
+ readonly op: string;
53
+ /**
54
+ * The object, named concretely — a branch and SHA, a recipient and subject,
55
+ * an amount and card-last4. A CATEGORY is not an object.
56
+ */
57
+ readonly object: string;
58
+ /** True once a human answered this ask. */
59
+ readonly confirmed: boolean;
60
+ /**
61
+ * The turn the human answered in, compared against the caller's current
62
+ * turn. A bare boolean cannot express the Hard Floor's THIS-TURN wording:
63
+ * an ask persisted into a mission record and read back after a restart is
64
+ * otherwise indistinguishable from a confirmation given a second ago. That
65
+ * replay-across-restart case is the concrete threat, and the sibling module
66
+ * `mission_record` is what makes it concrete.
67
+ */
68
+ readonly confirmed_turn: string;
69
+ }
70
+
71
+ export type GrantState = 'vetoed' | 'ask-required' | 'granted';
72
+
73
+ export interface GrantDecision {
74
+ readonly state: GrantState;
75
+ readonly reason: string;
76
+ }
77
+
78
+ /** Whitespace-separated parts of an object, empties dropped. */
79
+ function parts(object: string): string[] {
80
+ return object.trim().split(/\s+/).filter((p) => p.length > 0);
81
+ }
82
+
83
+ /** A part with its surrounding punctuation removed, so `@abc123,` reads as `abc123`. */
84
+ function bare(part: string): string {
85
+ return part.replace(/^[^0-9a-z]+/i, '').replace(/[^0-9a-z]+$/i, '');
86
+ }
87
+
88
+ /** Does this text name anything at all? The universal floor, and the weakest claim made here. */
89
+ function namesSomething(object: string): boolean {
90
+ return /[0-9a-z]{2,}/i.test(object);
91
+ }
92
+
93
+ /** A 7-to-40 character hex run — the token that pins a push to one commit. */
94
+ function isSha(part: string): boolean {
95
+ return /^[0-9a-f]{7,40}$/i.test(bare(part));
96
+ }
97
+
98
+ /** An address-shaped part — the token that pins a send to one recipient. */
99
+ function isRecipient(part: string): boolean {
100
+ return /^[^\s@]+@[^\s@]+\.[a-z]{2,}$/i.test(bare(part));
101
+ }
102
+
103
+ /** A money-shaped part — a decimal amount, or digits carrying a currency marker. */
104
+ function isAmount(part: string): boolean {
105
+ return /\d[.,]\d{2}\b/.test(part) || (/\d/.test(part) && /[€$£]|\b(?:EUR|USD|GBP)\b/i.test(part));
106
+ }
107
+
108
+ /** A four-digit run — the card suffix that pins a purchase to one instrument. */
109
+ function isCardSuffix(part: string): boolean {
110
+ return /(?:^|\D)\d{4}$/.test(bare(part)) && !isAmount(part);
111
+ }
112
+
113
+ /** Is there a part that carries a name, other than the one already accounted for? */
114
+ function hasQualifier(ps: string[], accountedFor: (p: string) => boolean): boolean {
115
+ return ps.some((p) => !accountedFor(p) && /[0-9a-z]{2,}/i.test(bare(p)));
116
+ }
117
+
118
+ /**
119
+ * The declared per-op object shapes.
120
+ *
121
+ * Keyed by the typed-op vocabulary, looked up case-insensitively on a trimmed
122
+ * op. Only the ops whose exact object this repository has actually seen or that
123
+ * the roadmap specifies are declared — inventing a shape for an op with no
124
+ * example risks refusing the first real one, which is the Risk-1 direction.
125
+ *
126
+ * `tag-or-release` is deliberately absent: a release object may legitimately be
127
+ * a bare version, so a SHA requirement would refuse a real one.
128
+ */
129
+ const OBJECT_SHAPES: ReadonlyMap<string, (object: string) => boolean> = new Map([
130
+ // A push names WHERE and WHICH COMMIT. The remote is optional — the shipped
131
+ // object in the unit fixture names a ref and a SHA and no remote, so
132
+ // requiring one would refuse an object this repository already ships.
133
+ ...(['push', 'force-push', 'merge-to-trunk', 'history-rewrite'] as const).map(
134
+ (op) =>
135
+ [
136
+ op,
137
+ (object: string): boolean => {
138
+ const ps = parts(object);
139
+ return ps.some(isSha) && hasQualifier(ps, isSha);
140
+ },
141
+ ] as const,
142
+ ),
143
+ [
144
+ 'send',
145
+ (object: string): boolean => {
146
+ const ps = parts(object);
147
+ return ps.some(isRecipient) && hasQualifier(ps, isRecipient);
148
+ },
149
+ ] as const,
150
+ [
151
+ 'purchase',
152
+ (object: string): boolean => {
153
+ const ps = parts(object);
154
+ return ps.some(isAmount) && ps.some(isCardSuffix);
155
+ },
156
+ ] as const,
157
+ ]);
158
+
159
+ /**
160
+ * A named object must be specific enough to be checkable.
161
+ *
162
+ * Exported so the two tiers are testable separately: a caller can ask whether an
163
+ * object would survive the floor without also asking which op it belongs to.
164
+ */
165
+ export function objectIsExact(op: string, object: string): boolean {
166
+ if (!namesSomething(object)) return false;
167
+ const shape = OBJECT_SHAPES.get(op.trim().toLowerCase());
168
+ if (shape !== undefined) return shape(object);
169
+ // No declared shape: the pre-existing heuristic, above the floor.
170
+ return object.trim().length >= 8 && /[^a-z ]/i.test(object);
171
+ }
172
+
173
+ /** An op names what is about to happen. Blank names nothing. */
174
+ function opIsNamed(op: string): boolean {
175
+ return op.trim().length > 0;
176
+ }
177
+
178
+ /**
179
+ * Decide whether a typed op may proceed.
180
+ *
181
+ * The order is fixed: veto first, then the verb, then the confirmation, then
182
+ * the turn, then the object. A confirmed ask after a veto is still a veto —
183
+ * otherwise the veto would be advisory, and an advisory veto is not one. The
184
+ * verb is read before the object because it decides which object shape applies.
185
+ *
186
+ * `currentTurn` is REQUIRED rather than optional. An optional turn would make
187
+ * the this-turn check something a caller opts into, and a security check that
188
+ * is off by default is not one.
189
+ */
190
+ export function grantFor(
191
+ verdict: CouncilVerdict,
192
+ ask: ExactObjectAsk,
193
+ currentTurn: string,
194
+ ): GrantDecision {
195
+ if (verdict === 'out-of-mission') {
196
+ return {
197
+ state: 'vetoed',
198
+ reason: `the council found ${ask.op} outside this mission; a veto is not overridden by a later confirmation`,
199
+ };
200
+ }
201
+ if (!opIsNamed(ask.op)) {
202
+ return {
203
+ state: 'ask-required',
204
+ reason: 'the ask names no operation — the verb that says what is about to happen is blank',
205
+ };
206
+ }
207
+ if (!ask.confirmed) {
208
+ return {
209
+ state: 'ask-required',
210
+ reason:
211
+ verdict === 'in-mission'
212
+ ? `the council cleared ${ask.op}, which is not a grant — the ask still has to happen`
213
+ : `${ask.op} needs a this-turn confirmation naming the exact object`,
214
+ };
215
+ }
216
+ if (currentTurn.trim().length === 0 || ask.confirmed_turn.trim().length === 0) {
217
+ return {
218
+ state: 'ask-required',
219
+ reason: `the confirmation for ${ask.op} carries no turn, so it cannot be shown to be this turn`,
220
+ };
221
+ }
222
+ if (ask.confirmed_turn !== currentTurn) {
223
+ return {
224
+ state: 'ask-required',
225
+ reason: `the confirmation for ${ask.op} was given in turn ${ask.confirmed_turn}, not this turn (${currentTurn})`,
226
+ };
227
+ }
228
+ if (!objectIsExact(ask.op, ask.object)) {
229
+ return {
230
+ state: 'ask-required',
231
+ reason: `the confirmation named "${ask.object}", which is a category rather than an object — an approval must name the exact thing`,
232
+ };
233
+ }
234
+ return { state: 'granted', reason: `confirmed this turn for ${ask.op} on ${ask.object}` };
235
+ }
236
+
237
+ /**
238
+ * Could this verdict, on its own, produce a grant?
239
+ *
240
+ * Exists as its own function so the Iron Law is CHECKABLE rather than merely
241
+ * stated: it returns false for every verdict, including a unanimous one, and a
242
+ * test asserts that over the whole domain.
243
+ */
244
+ export function verdictAloneGrants(_verdict: CouncilVerdict): boolean {
245
+ return false;
246
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * The guardrail watch over typed ops — `road-to-adversarial-verification-and-long-runs`
3
+ * 7.1, observation-only floor.
4
+ *
5
+ * The eleven typed ops are the Hard Floor's own list: the irreversible and
6
+ * externally-visible actions `non-destructive-by-default` gates. The layering is
7
+ * forge protection → the host hook where one is bound → this watch → model
8
+ * policy, and the watch exists because 7.2 measured the second layer and found
9
+ * SEVEN of eight hosts carry no refusing `pre_tool_use` binding at all.
10
+ *
11
+ * **This mode observes and takes no action. That is a design decision with a
12
+ * measured precondition, not caution.** K5 of the roadmap kills daemon
13
+ * enforcement before the observation floor, and the roadmap's own Risk 3 states
14
+ * the reason: a watchdog reading the reflog and shell history for typed ops can
15
+ * misclassify, and its enforcing action is stopping the host process — one false
16
+ * stop costs a twelve-hour run. So enforcement waits behind a measured
17
+ * false-positive rate under 1 % on the 30-session corpus, and
18
+ * {@link enforcementAllowed} refuses to unlock without that artefact.
19
+ *
20
+ * **What it can and cannot see, said plainly.** It reads lines — reflog entries,
21
+ * exposed shell history, forge event payloads. A line is not an intention: an op
22
+ * typed into a comment, a heredoc, or a string literal looks identical to one
23
+ * about to run. That is precisely why the first mode only writes down what it
24
+ * saw, and why the false-positive rate is the gate on doing anything more.
25
+ *
26
+ * Pure. The I/O — tailing a reflog, reading history, polling an event stream —
27
+ * belongs to the caller, so every classification is testable offline.
28
+ */
29
+
30
+ /** The typed-op classes this watch recognises. */
31
+ export const TYPED_OPS = [
32
+ 'push',
33
+ 'force-push',
34
+ 'merge-to-trunk',
35
+ 'tag-or-release',
36
+ 'deploy',
37
+ 'bulk-delete',
38
+ 'history-rewrite',
39
+ 'send',
40
+ 'publish',
41
+ 'purchase',
42
+ 'submit',
43
+ ] as const;
44
+ export type TypedOp = (typeof TYPED_OPS)[number];
45
+
46
+ /** Where a line came from. Kept because the sources have different reliability. */
47
+ export type WatchSource = 'reflog' | 'shell-history' | 'forge-event';
48
+
49
+ export interface Observation {
50
+ readonly op: TypedOp;
51
+ readonly source: WatchSource;
52
+ /** The line, truncated. Evidence, never re-executed. */
53
+ readonly evidence: string;
54
+ }
55
+
56
+ /**
57
+ * The recognisers, most specific first.
58
+ *
59
+ * ORDER MATTERS and is asserted by a test: `git push --force` is a force-push,
60
+ * not a push, and a table walked in declaration order gives the specific class
61
+ * only if it is declared before the general one. A misordered table would report
62
+ * the milder op for the more dangerous line, which is the wrong direction for
63
+ * every downstream reader.
64
+ */
65
+ const RECOGNISERS: readonly (readonly [TypedOp, RegExp])[] = [
66
+ ['force-push', /\bgit\b[^\n]*\bpush\b[^\n]*(--force\b|--force-with-lease\b|\s-f\b)/],
67
+ ['history-rewrite', /\bgit\b[^\n]*\b(rebase|filter-repo|filter-branch)\b|\bgit\b[^\n]*reset\s+--hard\b/],
68
+ ['merge-to-trunk', /\bgit\b[^\n]*\bmerge\b[^\n]*\b(main|master|trunk|production)\b|\bgh\b[^\n]*\bpr\b[^\n]*\bmerge\b/],
69
+ ['tag-or-release', /\bgit\b[^\n]*\bpush\b[^\n]*\b--tags?\b|\bgh\b[^\n]*\brelease\b[^\n]*\bcreate\b/],
70
+ ['deploy', /\b(terraform\s+apply|kubectl\s+apply|helm\s+(install|upgrade)|serverless\s+deploy)\b/],
71
+ ['bulk-delete', /\brm\s+-[a-z]*r[a-z]*f\b|\bgit\b[^\n]*\brm\s+-r\b|\b(DROP\s+TABLE|TRUNCATE)\b/i],
72
+ ['push', /\bgit\b[^\n]*\bpush\b/],
73
+ ['purchase', /\b(stripe|checkout|charge|payment)\b[^\n]*\b(create|confirm|capture)\b/i],
74
+ ['publish', /\bnpm\s+publish\b|\bgh\b[^\n]*\brelease\b[^\n]*\bupload\b/],
75
+ ['send', /\b(sendmail|mail\s+-s|curl\b[^\n]*\/messages\b)/],
76
+ ['submit', /\bcurl\b[^\n]*\s-X\s*POST\b[^\n]*\b(submit|apply|order)\b/i],
77
+ ];
78
+
79
+ const EVIDENCE_MAX = 200;
80
+
81
+ /**
82
+ * Classify one line, or `null` when nothing matched.
83
+ *
84
+ * Deliberately returns at most ONE op. A line is one action, and reporting two
85
+ * classes for it would double-count in the false-positive denominator the
86
+ * enforcement gate depends on.
87
+ */
88
+ export function classifyLine(line: string, source: WatchSource): Observation | null {
89
+ for (const [op, re] of RECOGNISERS) {
90
+ if (re.test(line)) {
91
+ return { op, source, evidence: line.trim().slice(0, EVIDENCE_MAX) };
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+
97
+ /** Classify a batch, preserving order and dropping non-matches. */
98
+ export function observe(
99
+ lines: readonly string[],
100
+ source: WatchSource,
101
+ ): Observation[] {
102
+ return lines
103
+ .map((l) => classifyLine(l, source))
104
+ .filter((o): o is Observation => o !== null);
105
+ }
106
+
107
+ /** The two modes. There is no third, and `enforce` is gated. */
108
+ export type WatchMode = 'observe' | 'enforce';
109
+
110
+ /** The false-positive measurement the enforcing mode waits behind. */
111
+ export interface FalsePositiveMeasurement {
112
+ /** Sessions the corpus covered. The roadmap names 30. */
113
+ readonly sessions: number;
114
+ readonly observations: number;
115
+ readonly falsePositives: number;
116
+ /** Where the artefact lives, so the claim is checkable. */
117
+ readonly artefact: string;
118
+ }
119
+
120
+ /** The corpus size the roadmap fixed before any measurement was taken. */
121
+ export const REQUIRED_SESSIONS = 30;
122
+ /** The rate enforcement waits behind. */
123
+ export const MAX_FALSE_POSITIVE_RATE = 0.01;
124
+
125
+ export interface EnforcementVerdict {
126
+ readonly allowed: boolean;
127
+ readonly reason: string;
128
+ }
129
+
130
+ /**
131
+ * May the enforcing mode be enabled?
132
+ *
133
+ * `null` — no measurement at all — is a refusal with its own reason, never a
134
+ * pass. A gate that unlocks when it cannot measure is the shape this whole
135
+ * roadmap is written against, and it is the one an absent-artefact check gets
136
+ * wrong by default.
137
+ */
138
+ export function enforcementAllowed(
139
+ m: FalsePositiveMeasurement | null,
140
+ ): EnforcementVerdict {
141
+ if (m === null) {
142
+ return {
143
+ allowed: false,
144
+ reason:
145
+ 'no false-positive measurement exists. Enforcement stops the host process; a ' +
146
+ 'rate nobody measured is not a control (K5).',
147
+ };
148
+ }
149
+ if (m.sessions < REQUIRED_SESSIONS) {
150
+ return {
151
+ allowed: false,
152
+ reason: `measured over ${String(m.sessions)} session(s), below the ${String(REQUIRED_SESSIONS)} the corpus was fixed at`,
153
+ };
154
+ }
155
+ if (m.observations === 0) {
156
+ return {
157
+ allowed: false,
158
+ reason:
159
+ 'zero observations: a rate over an empty denominator is not a measurement, ' +
160
+ 'and it is the reading a broken recogniser produces',
161
+ };
162
+ }
163
+ const rate = m.falsePositives / m.observations;
164
+ if (rate >= MAX_FALSE_POSITIVE_RATE) {
165
+ return {
166
+ allowed: false,
167
+ reason: `false-positive rate ${rate.toFixed(4)} is not below ${String(MAX_FALSE_POSITIVE_RATE)}`,
168
+ };
169
+ }
170
+ return { allowed: true, reason: `rate ${rate.toFixed(4)} over ${String(m.observations)} observations in ${m.artefact}` };
171
+ }
172
+
173
+ /**
174
+ * What the watch does with an observation, per mode.
175
+ *
176
+ * In `observe` the action is always `record` — there is no branch that acts, so
177
+ * no configuration flips this mode into one that does.
178
+ */
179
+ export function actionFor(
180
+ mode: WatchMode,
181
+ measurement: FalsePositiveMeasurement | null,
182
+ ): 'record' | 'record-and-ask' {
183
+ if (mode === 'observe') return 'record';
184
+ return enforcementAllowed(measurement).allowed ? 'record-and-ask' : 'record';
185
+ }