@event4u/agent-config 14.17.0 → 14.19.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 (184) 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 +207 -77
  5. package/README.md +72 -150
  6. package/dist/agent-src/README.md +1 -1
  7. package/dist/agent-src/commands/agent-handoff.md +2 -2
  8. package/dist/agent-src/commands/pr/merge.md +35 -44
  9. package/dist/agent-src/commands/review/changes.md +19 -19
  10. package/dist/agent-src/commands/review.md +2 -2
  11. package/dist/agent-src/contexts/execution/host-capability-manifest.md +30 -3
  12. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  13. package/dist/agent-src/rules/git-history-discipline.md +2 -2
  14. package/dist/agent-src/rules/guidelines.md +1 -1
  15. package/dist/agent-src/rules/session-canary.md +2 -2
  16. package/dist/agent-src/rules/token-efficiency.md +1 -1
  17. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +2 -0
  18. package/dist/agent-src/skills/adversarial-review/SKILL.md +0 -8
  19. package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +27 -1
  20. package/dist/agent-src/skills/canvas-design/SKILL.md +1 -1
  21. package/dist/agent-src/skills/code-intelligence/SKILL.md +60 -12
  22. package/dist/agent-src/skills/complexity-first-planning/SKILL.md +27 -0
  23. package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
  24. package/dist/agent-src/skills/design-intelligence/data/app-interface.csv +1 -1
  25. package/dist/agent-src/skills/design-intelligence/data/motion.csv +17 -17
  26. package/dist/agent-src/skills/design-intelligence/data/ux-guidelines.csv +2 -2
  27. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +17 -0
  28. package/dist/agent-src/skills/design-intelligence/references/design-rules-checklist.md +4 -4
  29. package/dist/agent-src/skills/design-review/SKILL.md +3 -1
  30. package/dist/agent-src/skills/design-variations/SKILL.md +1 -1
  31. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +1 -1
  32. package/dist/agent-src/skills/fe-design/SKILL.md +32 -5
  33. package/dist/agent-src/skills/fe-design/references/craft-floor.md +7 -0
  34. package/dist/agent-src/skills/fe-design/references/design-patterns.md +47 -2
  35. package/dist/agent-src/skills/feature-planning/SKILL.md +34 -0
  36. package/dist/agent-src/skills/git-workflow/SKILL.md +37 -12
  37. package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +176 -0
  38. package/dist/agent-src/skills/laravel-mail/SKILL.md +44 -0
  39. package/dist/agent-src/skills/laravel-scheduling/SKILL.md +3 -1
  40. package/dist/agent-src/skills/project-analyzer/SKILL.md +0 -6
  41. package/dist/agent-src/skills/react-native-setup/SKILL.md +3 -3
  42. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  43. package/dist/agent-src/skills/sequential-thinking/SKILL.md +18 -8
  44. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  45. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +40 -0
  46. package/dist/agent-src/skills/ui-apply-generic/SKILL.md +1 -1
  47. package/dist/agent-src/skills/ui-component-architect/SKILL.md +1 -1
  48. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +29 -2
  49. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +53 -8
  50. package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +3 -1
  51. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  52. package/dist/cli/commands/recordTriggerEval.js +2 -2
  53. package/dist/cli/main.js +1 -1
  54. package/dist/cli-delegate/{chunk-AKJGUQNO.js → chunk-GZTMRYCN.js} +405 -341
  55. package/dist/cli-delegate/{chunk-7RXIKF62.js → chunk-IDHUQIXP.js} +283 -132
  56. package/dist/cli-delegate/cmd_adr_effective.js +318 -81
  57. package/dist/cli-delegate/cmd_conformance.js +749 -669
  58. package/dist/cli-delegate/cmd_doctor.js +1 -1
  59. package/dist/cli-delegate/cmd_export.js +1 -1
  60. package/dist/cli-delegate/cmd_handoff.js +1 -1
  61. package/dist/cli-delegate/cmd_prune.js +1 -1
  62. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  63. package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
  64. package/dist/cli-delegate/cmd_sync.js +1 -1
  65. package/dist/cli-delegate/cmd_uninstall.js +1 -1
  66. package/dist/cli-delegate/cmd_validate.js +1 -1
  67. package/dist/discovery/deprecation-report.md +1 -1
  68. package/dist/discovery/discovery-manifest.json +39 -38
  69. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  70. package/dist/discovery/discovery-manifest.summary.md +1 -1
  71. package/dist/discovery/dormancy-report.md +1 -1
  72. package/dist/discovery/orphan-report.md +1 -1
  73. package/dist/discovery/packs.json +2 -2
  74. package/dist/discovery/trust-report.md +1 -1
  75. package/dist/discovery/workspaces.json +2 -2
  76. package/dist/hooks/dispatch.js +2764 -2439
  77. package/dist/install/install.mjs +588 -527
  78. package/dist/mcp/registry-manifest.json +2 -2
  79. package/dist/mcp/server.json +2 -2
  80. package/dist/mcp/server.mjs +1381 -1138
  81. package/dist/scripts/_lib/model_tier.js +1 -1
  82. package/dist/shared/settingsConsent.js +1 -1
  83. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +1 -1
  84. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  85. package/docs/guidelines/design-antipatterns.md +87 -64
  86. package/package.json +1 -1
  87. package/src/agent-src/README.md +1 -1
  88. package/src/config/ci-local-parity.yml +6 -0
  89. package/src/config/conformance-claim-baseline.json +9 -0
  90. package/src/config/gate-coverage.yml +170 -0
  91. package/src/config/gate-violation-baselines.json +4 -4
  92. package/src/config/release-gate-locality.yml +141 -0
  93. package/src/scripts/_cli/cmd_doctor.ts +2 -2
  94. package/src/scripts/_cli/cmd_handoff.ts +1 -1
  95. package/src/scripts/_cli/cmd_update.ts +1 -1
  96. package/src/scripts/_cli/cmd_versions.ts +1 -1
  97. package/src/scripts/_lib/adr_frontmatter.ts +44 -0
  98. package/src/scripts/_lib/consequence_objects.ts +152 -0
  99. package/src/scripts/_lib/exec_evidence.ts +7 -0
  100. package/src/scripts/_lib/global_deploy_inventory.ts +2 -2
  101. package/src/scripts/_lib/host_permission_checks.ts +251 -0
  102. package/src/scripts/_lib/model_tier.ts +1 -1
  103. package/src/scripts/_lib/obligation_frequency.ts +2 -2
  104. package/src/scripts/_lib/release_highlights.ts +265 -1
  105. package/src/scripts/_lib/release_material.ts +129 -0
  106. package/src/scripts/_lib/repeated_run.ts +77 -0
  107. package/src/scripts/_lib/runtime_journal.ts +26 -3
  108. package/src/scripts/_lib/runtime_wiring_checks.ts +12 -1
  109. package/src/scripts/_lib/session_register.ts +11 -6
  110. package/src/scripts/_lib/sqlite_schema.ts +22 -0
  111. package/src/scripts/_lib/surface.ts +86 -0
  112. package/src/scripts/_lib/tamper_vocabulary.ts +242 -0
  113. package/src/scripts/_lib/ui_authority.ts +116 -0
  114. package/src/scripts/_lib/unified_diff.ts +121 -0
  115. package/src/scripts/adr/regenerate_index.ts +54 -1
  116. package/src/scripts/adr_cite_check.ts +71 -15
  117. package/src/scripts/ai_council/clients.ts +1 -1
  118. package/src/scripts/autonomy_friction_corpus.ts +229 -0
  119. package/src/scripts/build_proof.ts +15 -2
  120. package/src/scripts/check_beta_review_markers.ts +141 -17
  121. package/src/scripts/check_enforcement_coverage.ts +1 -1
  122. package/src/scripts/check_enforcement_denominator.ts +121 -11
  123. package/src/scripts/check_finding_dispositions.ts +239 -6
  124. package/src/scripts/check_gate_coverage.ts +96 -0
  125. package/src/scripts/check_installed_hooks_fresh.ts +381 -0
  126. package/src/scripts/check_release_highlights.ts +99 -14
  127. package/src/scripts/check_skill_activation_claim.ts +179 -0
  128. package/src/scripts/check_suppression_hygiene.ts +11 -0
  129. package/src/scripts/check_trigger_evals.ts +1 -1
  130. package/src/scripts/code_graph/build.ts +230 -33
  131. package/src/scripts/code_graph/extract.ts +168 -33
  132. package/src/scripts/code_graph/types.ts +37 -2
  133. package/src/scripts/code_graph/validate.ts +12 -1
  134. package/src/scripts/conformance_scan.ts +45 -4
  135. package/src/scripts/detect_silent_catch.ts +403 -0
  136. package/src/scripts/detect_verification_tampering.ts +538 -0
  137. package/src/scripts/explain_run.ts +1 -1
  138. package/src/scripts/generate_capability_matrix.ts +1 -1
  139. package/src/scripts/git_authorization_hook.ts +228 -18
  140. package/src/scripts/hook_manifest.json +1 -1
  141. package/src/scripts/hook_manifest.yaml +47 -30
  142. package/src/scripts/hooks/augment-dispatcher.sh +1 -1
  143. package/src/scripts/hooks/block_no_verify.ts +3 -3
  144. package/src/scripts/hooks/category_a.ts +291 -0
  145. package/src/scripts/hooks/cline-dispatcher.sh +1 -1
  146. package/src/scripts/hooks/concern_registry.ts +2 -2
  147. package/src/scripts/hooks/cursor-dispatcher.sh +11 -5
  148. package/src/scripts/hooks/dispatch_hook.ts +17 -22
  149. package/src/scripts/hooks/evidence_independence.ts +21 -2
  150. package/src/scripts/hooks/{block_unauthorized_git.ts → git_command_classifier.ts} +175 -364
  151. package/src/scripts/hooks/host_lowering.ts +215 -0
  152. package/src/scripts/hooks/host_lowering.yaml +185 -0
  153. package/src/scripts/hooks/host_semantics.ts +105 -37
  154. package/src/scripts/hooks/journal_record_hook.ts +10 -0
  155. package/src/scripts/hooks/permission_gate.ts +117 -0
  156. package/src/scripts/hooks/push_settle_hook.ts +250 -0
  157. package/src/scripts/hooks/ship_diff_volume_hook.ts +1 -1
  158. package/src/scripts/hooks_status.ts +12 -10
  159. package/src/scripts/install-hooks.sh +134 -1
  160. package/src/scripts/install.ts +53 -82
  161. package/src/scripts/install_anthropic_key.sh +2 -2
  162. package/src/scripts/language_mirror_hook.ts +27 -5
  163. package/src/scripts/lint_design_antipattern_parity.ts +80 -0
  164. package/src/scripts/lint_evidence_artifacts.ts +45 -1
  165. package/src/scripts/lint_hook_manifest.ts +87 -3
  166. package/src/scripts/lint_motion_authority_drift.ts +731 -0
  167. package/src/scripts/lint_readme_size.ts +158 -12
  168. package/src/scripts/lint_reasoning_fixtures.ts +216 -0
  169. package/src/scripts/lint_skill_trigger_contradiction.ts +161 -0
  170. package/src/scripts/measure_sibling_naming.ts +150 -0
  171. package/src/scripts/redact_hook_capture.ts +1 -1
  172. package/src/scripts/release.ts +31 -143
  173. package/src/scripts/release_drill.ts +71 -1
  174. package/src/scripts/release_publication.ts +243 -3
  175. package/src/scripts/release_verify.ts +220 -0
  176. package/src/scripts/report_skill_activation.ts +319 -7
  177. package/src/scripts/schemas/review-findings.schema.json +6 -1
  178. package/src/scripts/schemas/skill.schema.json +12 -2
  179. package/src/scripts/self_review_gate.ts +10 -1
  180. package/src/scripts/session_canary_hook.ts +1 -1
  181. package/src/scripts/session_register_hook.ts +4 -3
  182. package/src/scripts/trigger_eval_grandfather.json +5 -1
  183. package/src/scripts/validate_agent_settings.ts +1 -1
  184. package/src/shared/settingsConsent.ts +1 -1
@@ -16,6 +16,9 @@ import {
16
16
  import {
17
17
  readHookStdin
18
18
  } from "./chunk-F7DQWYR5.js";
19
+ import {
20
+ reportScanned
21
+ } from "./chunk-I3EIN2YG.js";
19
22
  import {
20
23
  ArgparseExit,
21
24
  STATUS_SYMBOLS,
@@ -33,7 +36,7 @@ import {
33
36
  frontmatterPaths,
34
37
  proseEqual,
35
38
  splitFrontmatter
36
- } from "./chunk-7RXIKF62.js";
39
+ } from "./chunk-IDHUQIXP.js";
37
40
  import "./chunk-FTAISGDQ.js";
38
41
  import "./chunk-M2DPGFZ4.js";
39
42
  import "./chunk-BTWUYDGN.js";
@@ -78,12 +81,12 @@ import {
78
81
 
79
82
  // src/scripts/_cli/cmd_conformance.ts
80
83
  var YAML = __toESM(require_dist(), 1);
81
- import * as fs14 from "node:fs";
84
+ import * as fs13 from "node:fs";
82
85
  import * as os7 from "node:os";
83
- import * as path14 from "node:path";
86
+ import * as path13 from "node:path";
84
87
  import process6 from "node:process";
85
88
  import { spawnSync as spawnSync2 } from "node:child_process";
86
- import { fileURLToPath as fileURLToPath14, pathToFileURL as pathToFileURL14 } from "node:url";
89
+ import { fileURLToPath as fileURLToPath13, pathToFileURL as pathToFileURL13 } from "node:url";
87
90
 
88
91
  // src/install/txlog.ts
89
92
  import { createReadStream, createWriteStream, existsSync, readFileSync, renameSync, statSync, unlinkSync } from "node:fs";
@@ -144,18 +147,18 @@ function routeTargetPathsPosix(target) {
144
147
  }
145
148
 
146
149
  // src/scripts/report_conformance_funnel.ts
147
- import * as fs13 from "node:fs";
150
+ import * as fs12 from "node:fs";
148
151
  import * as os6 from "node:os";
149
- import * as path13 from "node:path";
152
+ import * as path12 from "node:path";
150
153
  import process5 from "node:process";
151
- import { fileURLToPath as fileURLToPath13, pathToFileURL as pathToFileURL13 } from "node:url";
154
+ import { fileURLToPath as fileURLToPath12, pathToFileURL as pathToFileURL12 } from "node:url";
152
155
 
153
156
  // src/scripts/conformance_scan.ts
154
157
  import { createHash as createHash3 } from "node:crypto";
155
- import fs9 from "node:fs";
158
+ import fs8 from "node:fs";
156
159
  import os2 from "node:os";
157
- import path9 from "node:path";
158
- import { fileURLToPath as fileURLToPath9, pathToFileURL as pathToFileURL9 } from "node:url";
160
+ import path8 from "node:path";
161
+ import { fileURLToPath as fileURLToPath8, pathToFileURL as pathToFileURL8 } from "node:url";
159
162
 
160
163
  // src/scripts/_lib/transcript_entry.ts
161
164
  function _isObject(v) {
@@ -432,7 +435,11 @@ function _setPinLost(consumer_root, session_id) {
432
435
  _migrateLegacyMarker(consumer_root);
433
436
  try {
434
437
  atomic_write_text(path.join(consumer_root, pinLostMarker(session_id)), (/* @__PURE__ */ new Date()).toISOString());
435
- } catch {
438
+ } catch (err) {
439
+ process.stderr.write(
440
+ `language-mirror: pin-lost marker write failed (${_errText(err)}) \u2014 the pin will NOT be re-emitted after this compaction. Turn outcome unchanged.
441
+ `
442
+ );
436
443
  }
437
444
  }
438
445
  function _pinLost(consumer_root, session_id) {
@@ -658,7 +665,11 @@ function run(stdin_text, options) {
658
665
  atomic_write_json(target, next);
659
666
  _pruneLegacyState(options.consumer_root);
660
667
  _pruneStaleSessions(options.consumer_root, Date.now());
661
- } catch {
668
+ } catch (err) {
669
+ process.stderr.write(
670
+ `language-mirror: pin-state write failed (${_errText(err)}) \u2014 the language pin did NOT persist and the next turn re-derives it. This turn's notice is unchanged.
671
+ `
672
+ );
662
673
  }
663
674
  }
664
675
  if (effective === "de" || effective === "en") {
@@ -670,6 +681,10 @@ function run(stdin_text, options) {
670
681
  }
671
682
  return EXIT_ALLOW;
672
683
  }
684
+ function _errText(err) {
685
+ const t = err instanceof Error ? err.message : String(err);
686
+ return t.replace(/\s+/g, " ").trim().slice(0, 200);
687
+ }
673
688
  function main(argv) {
674
689
  const args = argv ?? process.argv.slice(2);
675
690
  let consumer_root = process.cwd();
@@ -727,6 +742,128 @@ function humanTypedThisTurn(prompt) {
727
742
  return true;
728
743
  }
729
744
 
745
+ // src/scripts/_lib/consequence_objects.ts
746
+ var CONSEQUENCE_PHRASES = [
747
+ {
748
+ op: "prod-deploy",
749
+ re: /(?<![A-Za-z0-9_À-ɏ])(deploy(e|en|t|st)?|ausroll(e|en)?|ship(pe|pen)?|live\s*schalt(e|en)?|rollout)\b[^.\n]{0,30}\b(produktion|production|prod|live|prod-umgebung)\b|\b(produktion|production|prod|live)\b[^.\n]{0,30}\b(deploy(e|en)?|ausrollen|ausroll(e|en)?|live\s*schalten|rollout)\b/i
750
+ },
751
+ {
752
+ op: "prod-data-destroy",
753
+ re: /(?<![A-Za-z0-9_À-ɏ])(l(ö|oe)sch(e|en)?|entfern(e|en)?|leer(e|en)?|verwerf(e|en)?|drop|truncate|delete|wipe|purge|zerst(ö|oe)r(e|en)?)\b[^.\n]{0,40}\b(tabelle|tabellen|datenbank|daten|bucket|collection|zeilen|rows|table|database|dataset)\b|\b(tabelle|tabellen|datenbank|daten|bucket|collection|table|database)\b[^.\n]{0,40}\b(l(ö|oe)schen|leeren|verwerfen|droppen|truncaten|entfernen|drop|truncate|delete|wipe|purge)\b/i
754
+ },
755
+ {
756
+ op: "prod-infra-change",
757
+ re: /(?<![A-Za-z0-9_À-ɏ])((ver)?(ä|ae)nder(e|n)?|anpass(e|en)?|umbau(e|en)?|apply|destroy|(neu\s*)?provisionier(e|en)?)\b[^.\n]{0,40}\b(infrastruktur|infrastructure|cluster|terraform|iam|dns|netzwerk|network|firewall|policy|policies|security[-\s]group)\b|\b(infrastruktur|infrastructure|cluster|terraform|iam|dns|netzwerk|firewall)\b[^.\n]{0,40}\b((ver)?(ä|ae)ndern|anpassen|umbauen|zerst(ö|oe)ren|apply|destroy)\b/i
758
+ },
759
+ {
760
+ op: "external-send",
761
+ re: /(?<![A-Za-z0-9_À-ɏ])(schick(e|en)?|verschick(e|en)?|send(e|en|et)?|mail(e|en)?|raussend(e|en)?|versend(e|en)?|post|publish)\b[^.\n]{0,40}\b(mail|mails|email|e-mail|nachricht|nachrichten|newsletter|webhook|kunden|kundin|kunde|empf(ä|ae)nger|customers?|recipients?|message)\b|\b(mail|email|e-mail|nachricht|newsletter|kunden|customers?)\b[^.\n]{0,40}\b(schicken|verschicken|senden|versenden|rausschicken|send|deliver)\b/i
762
+ },
763
+ {
764
+ op: "money-movement",
765
+ re: /(?<![A-Za-z0-9_À-ɏ])(erstatt(e|en)?|r(ü|ue)ckerstatt(e|en)?|zahl(e|en)?|(ü|ue)berweis(e|en)?|abbuch(e|en)?|belast(e|en)?|refund|charge|payout|transfer|bill)\b[^.\n]{0,40}\b(betrag|geld|rechnung|zahlung|euro|dollar|kunde|kunden|karte|konto|invoice|payment|customer|card|account)\b|\b(betrag|geld|rechnung|zahlung|invoice|payment)\b[^.\n]{0,40}\b(erstatten|zur(ü|ue)ckzahlen|(ü|ue)berweisen|abbuchen|belasten|refund|charge|transfer)\b/i
766
+ },
767
+ {
768
+ op: "trunk-force-push",
769
+ re: /(?<![A-Za-z0-9_À-ɏ])(force[-\s]?push(e|en|t)?|force[-\s]?pushen|(ü|ue)berschreib(e|en)?)\b[^.\n]{0,40}\b(main|master|trunk|produktions?[-\s]?branch|production|release[-\s]?branch)\b|\b(main|master|trunk|production)\b[^.\n]{0,40}\b(force[-\s]?pushen|force[-\s]?push|(ü|ue)berschreiben)\b/i
770
+ },
771
+ {
772
+ op: "out-of-scope-destruction",
773
+ re: /(?<![A-Za-z0-9_À-ɏ])(l(ö|oe)sch(e|en)?|entfern(e|en)?|r(ä|ae)um(e|en)?\s*auf|aufr(ä|ae)um(e|en)?|delete|remove|wipe)\b[^.\n]{0,40}\b(au(ß|ss)erhalb|ausserhalb|unabh(ä|ae)ngig|nicht\s+zur\s+aufgabe|outside\s+(the\s+)?(task|scope)|unrelated|everything\s+else|alles\s+andere)\b|\b(au(ß|ss)erhalb|outside\s+(the\s+)?(task|scope)|unrelated)\b[^.\n]{0,40}\b(l(ö|oe)schen|entfernen|aufr(ä|ae)umen|delete|remove|wipe)\b/i
774
+ }
775
+ ];
776
+ var OBJECT_REQUIRED_OPS = /* @__PURE__ */ new Set([
777
+ "prod-data-destroy",
778
+ "external-send",
779
+ "money-movement"
780
+ ]);
781
+ var OBJECT_STOP_WORDS = /* @__PURE__ */ new Set([
782
+ "bitte",
783
+ "doch",
784
+ "mal",
785
+ "nicht",
786
+ "jetzt",
787
+ "gleich",
788
+ "auch",
789
+ "noch",
790
+ "wieder",
791
+ "in",
792
+ "im",
793
+ "aus",
794
+ "von",
795
+ "vom",
796
+ ": ",
797
+ "der",
798
+ "die",
799
+ "das",
800
+ "den",
801
+ "dem",
802
+ "und",
803
+ "oder",
804
+ "mit",
805
+ "auf",
806
+ "zu",
807
+ "komplett",
808
+ "ganz",
809
+ "alle",
810
+ "allen",
811
+ "please",
812
+ "now",
813
+ "again",
814
+ "too",
815
+ "also",
816
+ "from",
817
+ "and",
818
+ "or",
819
+ "the",
820
+ "a",
821
+ "an",
822
+ "there",
823
+ "here",
824
+ "completely",
825
+ "all",
826
+ "with",
827
+ "on",
828
+ "to",
829
+ "in"
830
+ ]);
831
+ function _normalizeObject(raw) {
832
+ return raw.trim().replace(/^["'`]+|["'`]+$/g, "").replace(/[.,;:]+$/, "").toLowerCase();
833
+ }
834
+ var OBJECT_PATTERNS = {
835
+ "prod-data-destroy": [
836
+ /\b(?:tabelle|table|datenbank|database|schema|collection|index)\s+["'`]?([A-Za-z_][\w.$-]*)/gi,
837
+ /\bs3:\/\/([\w.-]+)/gi,
838
+ /\b(?:bucket)\s+["'`]?([A-Za-z_][\w.-]*)/gi
839
+ ],
840
+ "external-send": [
841
+ /([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/g,
842
+ /\bhttps?:\/\/([^\s/"'`]+)/gi,
843
+ /\b(?:an\s+(?:die|den|alle)|to\s+(?:the|all))\s+([A-Za-z][\w-]*)/gi
844
+ ],
845
+ "money-movement": [
846
+ /(\d[\d.,]*)\s*(?:€|eur\b|euro\b|\$|usd\b)/gi,
847
+ /(?:€|\$)\s*(\d[\d.,]*)/g,
848
+ /\b(?:kunde|kunden|customer|account|konto)\s+["'`]?([A-Za-z0-9][\w.-]*)/gi
849
+ ]
850
+ };
851
+ function extractObjects(op, text) {
852
+ const patterns = OBJECT_PATTERNS[op];
853
+ if (!patterns) return [];
854
+ const out = [];
855
+ for (const re of patterns) {
856
+ const local = new RegExp(re.source, re.flags.includes("g") ? re.flags : `${re.flags}g`);
857
+ let m;
858
+ while ((m = local.exec(text)) !== null) {
859
+ const value = _normalizeObject(m[1] ?? "");
860
+ if (value && !OBJECT_STOP_WORDS.has(value) && !out.includes(value)) out.push(value);
861
+ if (m.index === local.lastIndex) local.lastIndex++;
862
+ }
863
+ }
864
+ return out;
865
+ }
866
+
730
867
  // src/scripts/git_authorization_hook.ts
731
868
  var EXIT_ALLOW2 = 0;
732
869
  var STATE_FILE2 = path2.join("agents", "state", "git-authorization.json");
@@ -778,7 +915,14 @@ var ALL_OPS = [
778
915
  "clean",
779
916
  "stash-drop",
780
917
  "branch-delete",
781
- "close"
918
+ "close",
919
+ "prod-deploy",
920
+ "prod-data-destroy",
921
+ "prod-infra-change",
922
+ "external-send",
923
+ "money-movement",
924
+ "trunk-force-push",
925
+ "out-of-scope-destruction"
782
926
  ];
783
927
  var PHRASES = [
784
928
  { op: "commit", re: /\b(commit(e|et|te|ten)?|committe|einchecken)\b/i },
@@ -936,7 +1080,8 @@ var PHRASES = [
936
1080
  {
937
1081
  op: "pr-merge-auto",
938
1082
  re: /\bauto[-\s]?merge\b|\bmerge\b[^.\n]{0,20}\bautomatisch\b|\bautomatisch\b[^.\n]{0,20}\bmerg(e|en)\b/i
939
- }
1083
+ },
1084
+ ...CONSEQUENCE_PHRASES
940
1085
  ];
941
1086
  var OUTPUT_LINE = /^\s*(To\s+\S+|remote:|error:|fatal:|hint:|warning:|!\s|\?\?\s|\s*\^|[-+]{3}\s|@@\s|\$\s|>\s|\d+\s+(pass|fail)|npm ERR!|Error:|Traceback|at\s+\S+:\d+)/i;
942
1087
  function isInterrogative(prose) {
@@ -1000,7 +1145,7 @@ function splitFences(prompt) {
1000
1145
  function _commandLines(fence) {
1001
1146
  return fence.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).filter((l) => !/^(remote:|error:|hint:|fatal:|warning:|To |!\s|\s*\^)/i.test(l)).map((l) => l.replace(/^[$>#]\s*/, "")).filter((l) => /^(git|gh|npm|pnpm|yarn|task)\b/.test(l));
1002
1147
  }
1003
- function _errText(err) {
1148
+ function _errText2(err) {
1004
1149
  const t = err instanceof Error ? err.message : String(err);
1005
1150
  return t.replace(/\s+/g, " ").trim().slice(0, 200);
1006
1151
  }
@@ -1017,9 +1162,20 @@ function extractMergeTargets(prompt) {
1017
1162
  }
1018
1163
  return [...out].sort((a, b) => a - b);
1019
1164
  }
1020
- var REVOKE_RE = /\b(stop|stopp|halt|abbrechen|abbruch|cancel|revoke|widerruf(en)?|zur(ü|ue)ck(nehmen|ziehen)|hold\s+(everything|on|the\s+merge)|warte|wait)\b|(?:\b(nicht|kein(e|en)?|no|not|dont|don't|never|niemals|nie)\b[^.!?\n]{0,30}\b(merg(e|en|ing)|zusammenf(ü|ue)hren)\b)/iu;
1165
+ var REVOKE_STOP_WORD = /\b(stop|stopp|halt|abbrechen|abbruch|cancel|revoke|widerruf(en)?|zur(ü|ue)ck(nehmen|ziehen)|hold\s+(everything|on|the\s+merge)|warte|wait)\b/iu;
1166
+ var MERGE_VERB = /\b(merg(e|en|ing)|zusammenf(ü|ue)hren)\b/giu;
1021
1167
  function isRevocation(prompt) {
1022
- return REVOKE_RE.test(prompt);
1168
+ if (REVOKE_STOP_WORD.test(prompt) || isBareRefusal(prompt)) {
1169
+ return true;
1170
+ }
1171
+ MERGE_VERB.lastIndex = 0;
1172
+ let m;
1173
+ while ((m = MERGE_VERB.exec(prompt)) !== null) {
1174
+ if (negatedBefore(prompt, m.index, m[0])) {
1175
+ return true;
1176
+ }
1177
+ }
1178
+ return false;
1023
1179
  }
1024
1180
  function _grantId(session_id, at, targets) {
1025
1181
  return `${sessionSlug(session_id) || "nosession"}:${at}:${targets.join(",")}`;
@@ -1054,8 +1210,12 @@ function foldGrants(prior, prompt, authorized, session_id, now) {
1054
1210
  }
1055
1211
  ];
1056
1212
  }
1057
- var NEGATION_WORD = /\b(nicht|nichts|kein(e|en|em|er|es)?|niemals|nie|no|not|dont|don't|never|without|ohne)\b/i;
1058
- var CONTRAST_CUE = /\b(aber|sondern|jedoch|but|however|instead)\b/gi;
1213
+ var APOSTROPHE = "'\u2019\u2018\u02BC";
1214
+ var NEGATION_ALTERNATION = `nicht|nichts|kein(?:e|en|em|er|es)?|niemals|nie|no|not|don[${APOSTROPHE}]?t|never|without|ohne`;
1215
+ var NEGATION_WORD = new RegExp(`\\b(${NEGATION_ALTERNATION})\\b`, "i");
1216
+ var NEGATION_TOKEN = new RegExp(`^(?:${NEGATION_ALTERNATION})$`, "i");
1217
+ var CONTRAST_ALTERNATION = "aber|sondern|jedoch|but|however|instead";
1218
+ var CONTRAST_CUE = new RegExp(`\\b(${CONTRAST_ALTERNATION})\\b`, "gi");
1059
1219
  var ABBREVIATION_BEFORE_DOT = /(?:^|[\s("'\u2018\u201c])(?:[a-z]|z\.b|bzw|ca|usw|d\.h|u\.a|evtl|ggf|inkl|max|min|nr|abb|e\.g|i\.e|etc|vs|approx|fig|no|st|dr|prof)\.$/i;
1060
1220
  function isSentenceBoundary(text, at) {
1061
1221
  const mark = text[at];
@@ -1142,6 +1302,19 @@ function negatedBefore(text, index, matched = "") {
1142
1302
  const after = afterFrom < end ? text.slice(afterFrom, clauseEnd(text, afterFrom, end)) : "";
1143
1303
  return NEGATION_WORD.test(before) || NEGATION_WORD.test(matched) || NEGATION_WORD.test(after);
1144
1304
  }
1305
+ var WORD_TOKEN = new RegExp(`[\\p{L}\\p{N}${APOSTROPHE}]+`, "gu");
1306
+ var CLAUSE_SPLIT = new RegExp(`[,;:]|\\b(?:${CONTRAST_ALTERNATION})\\b`, "iu");
1307
+ function isBareRefusal(text) {
1308
+ for (const sentence of _sentences(text)) {
1309
+ for (const clause of sentence.split(CLAUSE_SPLIT)) {
1310
+ const tokens = (clause ?? "").match(WORD_TOKEN) ?? [];
1311
+ if (tokens.length > 0 && tokens.every((t) => NEGATION_TOKEN.test(t))) {
1312
+ return true;
1313
+ }
1314
+ }
1315
+ }
1316
+ return false;
1317
+ }
1145
1318
  function classifyAuthorization(prompt) {
1146
1319
  const { prose, fences } = splitFences(prompt);
1147
1320
  const authorized = /* @__PURE__ */ new Set();
@@ -1209,40 +1382,6 @@ function readGrants(consumer_root, session_id) {
1209
1382
  return [];
1210
1383
  }
1211
1384
  }
1212
- function consumeGrantTarget(consumer_root, session_id, target) {
1213
- const file = path2.join(consumer_root, ledgerFileFor(session_id));
1214
- let decoded;
1215
- try {
1216
- decoded = JSON.parse(fs2.readFileSync(file, "utf8"));
1217
- } catch {
1218
- return;
1219
- }
1220
- if (!_isObject3(decoded) || !Array.isArray(decoded["grants"])) {
1221
- return;
1222
- }
1223
- let touched = false;
1224
- for (const g of decoded["grants"]) {
1225
- if (!_isObject3(g) || !Array.isArray(g["targets"]) || !Array.isArray(g["consumed"])) {
1226
- continue;
1227
- }
1228
- const targets = g["targets"];
1229
- const consumed = g["consumed"];
1230
- if (targets.includes(target) && !consumed.includes(target)) {
1231
- consumed.push(target);
1232
- touched = true;
1233
- }
1234
- }
1235
- if (touched) {
1236
- try {
1237
- atomic_write_json(file, decoded);
1238
- } catch (err) {
1239
- process.stderr.write(
1240
- `git-authorization: grant-consumption write failed (${_errText(err)}) \u2014 the target stays UNSPENT, so one further merge of a PR the user named will still be authorized. Authorization outcome unchanged.
1241
- `
1242
- );
1243
- }
1244
- }
1245
- }
1246
1385
  function takePending(consumer_root, session_id, now) {
1247
1386
  const file = path2.join(consumer_root, pendingFileFor(session_id));
1248
1387
  let raw;
@@ -1311,6 +1450,11 @@ function run2(stdin_text, options) {
1311
1450
  authorized.push(pending.op);
1312
1451
  evidence[pending.op] = `confirmation of the refused \`${pending.op}\` (refused ${pending.refused_at}): "${prompt.trim().slice(0, 40)}"`;
1313
1452
  }
1453
+ const objects = {};
1454
+ for (const op of authorized) {
1455
+ const named = extractObjects(op, prompt);
1456
+ if (named.length > 0) objects[op] = named;
1457
+ }
1314
1458
  const now = /* @__PURE__ */ new Date();
1315
1459
  const ledger = {
1316
1460
  session_id: typeof envelope["session_id"] === "string" ? envelope["session_id"] : "",
@@ -1318,6 +1462,7 @@ function run2(stdin_text, options) {
1318
1462
  authorized,
1319
1463
  evidence,
1320
1464
  prompt_chars: prompt.length,
1465
+ objects,
1321
1466
  // Grants survive this write; `authorized` does not. That asymmetry IS the
1322
1467
  // decision. A bare operation name stays one-shot exactly as `commit-policy`
1323
1468
  // requires, and only an authorization that also froze its objects earns a
@@ -1334,7 +1479,7 @@ function run2(stdin_text, options) {
1334
1479
  atomic_write_json(path2.join(options.consumer_root, ledgerFileFor(session)), ledger);
1335
1480
  } catch (err) {
1336
1481
  process.stderr.write(
1337
- `git-authorization: session-ledger write failed (${_errText(err)}) \u2014 the gate degrades to "no ledger" for this session, which denies the irreversible subset and warns on the rest. Authorization outcome unchanged.
1482
+ `git-authorization: session-ledger write failed (${_errText2(err)}) \u2014 the gate degrades to "no ledger" for this session, which denies the irreversible subset and warns on the rest. Authorization outcome unchanged.
1338
1483
  `
1339
1484
  );
1340
1485
  }
@@ -1377,125 +1522,7 @@ if (_isCliEntry2()) {
1377
1522
  process.exit(main2());
1378
1523
  }
1379
1524
 
1380
- // src/scripts/hooks/block_unauthorized_git.ts
1381
- import fs3 from "node:fs";
1382
- import path3 from "node:path";
1383
- import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "node:url";
1384
-
1385
- // src/scripts/hooks/merge_impact.ts
1386
- import { execFileSync } from "node:child_process";
1387
- var DESTRUCTIVE_MARKERS = [
1388
- // Schema removal. Matched on ADDED lines only — a migration that *removes* a
1389
- // dropColumn call is the opposite of destructive, and matching the raw patch
1390
- // would score it the same way.
1391
- { label: "migration drops a column or table", re: /^\+.*\b(dropColumn|dropTable|dropIfExists|removeColumn|drop_column|drop_table)\b/im },
1392
- { label: "raw DROP or TRUNCATE in a migration", re: /^\+.*\b(DROP\s+(TABLE|COLUMN|INDEX|CONSTRAINT)|TRUNCATE\s+TABLE)\b/im },
1393
- // The author said so themselves. Conventional Commits marks a breaking change
1394
- // with a `!` before the colon or a body trailer.
1395
- { label: "author marked a BREAKING CHANGE", re: /^\+.*BREAKING[ -]CHANGE/m },
1396
- // A removed export is the shape most likely to break an importer.
1397
- { label: "an exported symbol was removed", re: /^-\s*export\s+(default\s+|const\s+|function\s+|class\s+|interface\s+|type\s+|async\s+)/m },
1398
- { label: "a public PHP or Python symbol was removed", re: /^-\s*(public\s+function|def\s+[a-z_]+\s*\()/m },
1399
- // Route removal breaks callers that never imported anything.
1400
- { label: "a route or endpoint was removed", re: /^-.*\b(Route::(get|post|put|patch|delete)|@(Get|Post|Put|Patch|Delete)Mapping|app\.(get|post|put|patch|delete)\s*\()/m },
1401
- { label: "a whole file was deleted", re: /^deleted file mode /m }
1402
- ];
1403
- var SCHEMA_REMOVAL_BODY = /\b(dropColumn|dropTable|dropIfExists|removeColumn|drop_column|drop_table|DROP\s+(TABLE|COLUMN|INDEX|CONSTRAINT)|TRUNCATE\s+TABLE)\b/i;
1404
- var RENAME_METADATA = /^rename (?:from|to) /m;
1405
- function fileBlocks(patch) {
1406
- return patch.split(/^(?=diff --git )/m).filter((b) => b.startsWith("diff --git "));
1407
- }
1408
- function renamedSchemaRemoval(patch) {
1409
- for (const block of fileBlocks(patch)) {
1410
- if (!RENAME_METADATA.test(block)) {
1411
- continue;
1412
- }
1413
- for (const line of block.split("\n")) {
1414
- if (line.startsWith("-") && !line.startsWith("---") && SCHEMA_REMOVAL_BODY.test(line)) {
1415
- return true;
1416
- }
1417
- }
1418
- }
1419
- return false;
1420
- }
1421
- var MAJOR_BUMP = /^-\s*"version"\s*:\s*"(\d+)\.[^"]*"[\s\S]{0,200}?^\+\s*"version"\s*:\s*"(\d+)\./m;
1422
- function classifyDiff(patch) {
1423
- if (patch.trim() === "") {
1424
- return { verdict: "undecidable", markers: [], reason: "empty diff" };
1425
- }
1426
- const markers = [];
1427
- for (const { label, re } of DESTRUCTIVE_MARKERS) {
1428
- if (re.test(patch)) {
1429
- markers.push(label);
1430
- }
1431
- }
1432
- if (renamedSchemaRemoval(patch)) {
1433
- markers.push("a renamed file drops a column or table");
1434
- }
1435
- const bump = MAJOR_BUMP.exec(patch);
1436
- if (bump && bump[1] !== bump[2]) {
1437
- markers.push(`major version bump ${bump[1]}.x to ${bump[2]}.x`);
1438
- }
1439
- const filesChanged = (patch.match(/^diff --git /gm) ?? []).length;
1440
- if (markers.length > 0) {
1441
- return { verdict: "destructive", markers, filesChanged };
1442
- }
1443
- return { verdict: "additive", markers: [], filesChanged };
1444
- }
1445
- function fetchPatch(pr, opts = {}) {
1446
- const run5 = opts.exec ?? execFileSync;
1447
- const cwd = opts.cwd ?? process.cwd();
1448
- const capture = (file, args) => run5(file, args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 15e3 });
1449
- for (const ref of [`refs/pull/${pr}/head`, `pull/${pr}/head`]) {
1450
- try {
1451
- const base = capture("git", ["merge-base", "HEAD", ref]).trim();
1452
- if (base) {
1453
- const patch = capture("git", ["diff", `${base}...${ref}`]);
1454
- if (patch.trim() !== "") {
1455
- return { patch, source: "local" };
1456
- }
1457
- }
1458
- } catch {
1459
- }
1460
- }
1461
- if (opts.allowNetwork === false) {
1462
- return { patch: "", source: "none", reason: "no local ref and network reads disabled" };
1463
- }
1464
- try {
1465
- const patch = capture("gh", ["pr", "diff", String(pr), "--patch"]);
1466
- if (patch.trim() !== "") {
1467
- return { patch, source: "gh" };
1468
- }
1469
- return { patch: "", source: "none", reason: "gh returned an empty diff" };
1470
- } catch {
1471
- return { patch: "", source: "none", reason: "no local ref, and gh pr diff failed" };
1472
- }
1473
- }
1474
- function analyseMergeImpact(pr, opts = {}) {
1475
- const got = fetchPatch(pr, opts);
1476
- if (got.source === "none") {
1477
- return { verdict: "undecidable", markers: [], ...got.reason ? { reason: got.reason } : {} };
1478
- }
1479
- return classifyDiff(got.patch);
1480
- }
1481
- function describeImpact(pr, impact) {
1482
- const say = `Say "merge #${pr}" to authorize it.`;
1483
- switch (impact.verdict) {
1484
- case "additive":
1485
- return `Impact scan: PR #${pr} touches ${impact.filesChanged ?? 0} file(s) and trips no destructive marker \u2014 no schema drop, no removed export or route, no major bump, no BREAKING CHANGE. Additive as far as the diff can show, which is not the same as safe to merge: the base has moved since review and merging may trigger a deploy. ${say}`;
1486
- case "destructive":
1487
- return `Impact scan: PR #${pr} is NOT additive \u2014 ${impact.markers.join("; ")}. Read the diff before authorizing. ${say}`;
1488
- default:
1489
- return `Impact scan: could not read the diff for PR #${pr} (${impact.reason ?? "unknown"}), so nothing is known about what it changes. ${say}`;
1490
- }
1491
- }
1492
-
1493
- // src/scripts/hooks/block_unauthorized_git.ts
1494
- var EXIT_ALLOW3 = 0;
1495
- var EXIT_BLOCK = 1;
1496
- function _isObject4(v) {
1497
- return typeof v === "object" && v !== null && !Array.isArray(v);
1498
- }
1525
+ // src/scripts/hooks/git_command_classifier.ts
1499
1526
  var BLOCK_OPS = /* @__PURE__ */ new Set([
1500
1527
  "publish",
1501
1528
  "tag",
@@ -1527,31 +1554,26 @@ var BLOCK_OPS = /* @__PURE__ */ new Set([
1527
1554
  /** Destroys uncommitted work belonging to a session this one cannot see. */
1528
1555
  "worktree-remove",
1529
1556
  /** `-x` is the flag that takes `.env` and local certificates. */
1530
- "clean-ignored"
1531
- ]);
1532
- var WARN_OPS = /* @__PURE__ */ new Set([
1533
- "commit",
1534
- "push",
1535
- "pr-create",
1536
- "branch",
1537
- // ── Added 2026-09-03, deliberately at warn rather than block.
1557
+ "clean-ignored",
1558
+ // ── The consequence vocabulary, added 2026-09-06. Every one is already
1559
+ // never-autonomous in the `non-destructive-by-default` prose.
1538
1560
  //
1539
- // Each is destructive in some reading, and each is also routine enough that
1540
- // blocking it would be the friction that gets the whole guard routed around —
1541
- // the failure mode this estate has already recorded twice. `reset-hard` is the
1542
- // sharpest case and stays here for a decidable reason: the Hard Floor's
1543
- // qualifier is "past unpushed work", and the command text cannot say whether
1544
- // the work was pushed.
1545
- "tag-force",
1546
- "rebase",
1547
- "reset-hard",
1548
- "clean",
1549
- "stash-drop",
1550
- "branch-delete",
1551
- "close"
1561
+ // MEMBERSHIP HERE IS A COUNTING DECISION, NOT A REFUSAL. The set's name
1562
+ // predates ADR-254, which deleted the reader that blocked: the one consumer
1563
+ // that acts on it now is `conformance_scan`'s after-the-fact tally, so
1564
+ // "blocked" describes what the Hard Floor requires of the model, never what
1565
+ // this package does at tool-call time.
1566
+ "prod-deploy",
1567
+ "prod-data-destroy",
1568
+ "prod-infra-change",
1569
+ "external-send",
1570
+ "money-movement",
1571
+ "trunk-force-push",
1572
+ "out-of-scope-destruction"
1552
1573
  ]);
1553
1574
  var G = "(?:-[A-Za-z-]+(?:=\\S+)?(?:\\s+\\S+)?\\s+)*";
1554
1575
  var P = "^(?:\\S*\\/)?";
1576
+ var CONSEQUENCE_TOOL_HEAD = /^(?:\S*\/)?(terraform|pulumi|kubectl|helm|aws|gcloud|az|vercel|netlify|flyctl|fly|wrangler|serverless|sls|eb|heroku|cap|psql|mysql|mariadb|sqlite3|mongosh|mongo|redis-cli|sendmail|msmtp|mail|mailx|curl|stripe|braintree|paypal|rm)\b/i;
1555
1577
  function ghApiWrite(pathRe) {
1556
1578
  return new RegExp(
1557
1579
  `${P}gh\\s+api\\b(?=[^\\n]*${pathRe})(?=[^\\n]*(?:-X|--method)\\s+(?:POST|PATCH|PUT|DELETE)\\b)`,
@@ -1632,6 +1654,13 @@ var COMMAND_OPS = [
1632
1654
  // Above `push`, and both spellings: `--force` and `--force-with-lease`. The
1633
1655
  // "safe force" is in the same class here because what it is safe against is a
1634
1656
  // stale local view, not a collaborator who pushed after your fetch.
1657
+ {
1658
+ op: "trunk-force-push",
1659
+ re: new RegExp(
1660
+ `${P}git\\s+${G}push\\b[^\\n;|&]*(--force\\b|--force-with-lease\\b|\\s-f\\b)[^\\n;|&]*\\b(main|master|trunk|production|prod)\\b`,
1661
+ "i"
1662
+ )
1663
+ },
1635
1664
  {
1636
1665
  op: "force-push",
1637
1666
  re: new RegExp(`${P}git\\s+${G}push\\b[^\\n;|&]*(--force\\b|--force-with-lease\\b|\\s-f\\b)`, "i")
@@ -1660,37 +1689,64 @@ var COMMAND_OPS = [
1660
1689
  )
1661
1690
  },
1662
1691
  { op: "close", re: new RegExp(`${P}gh\\s+(pr|issue)\\s+close\\b`, "i") },
1692
+ // ── Consequence operations, added 2026-09-06
1693
+ // (`road-to-authorization-that-reaches-further` 2.1).
1694
+ //
1695
+ // ORDER: above the generic `push`/`commit` fallbacks, because
1696
+ // `trunk-force-push` is a `git push` and the most specific pattern must win —
1697
+ // the rule this table states at its head.
1698
+ //
1699
+ // These classify commands the count could not see before. They add NOTHING to
1700
+ // any refusal path: `commandOp` and `BLOCK_OPS` have exactly one consumer
1701
+ // that acts on them, the after-the-fact tally in `conformance_scan`, and this
1702
+ // change does not add a second.
1703
+ {
1704
+ op: "prod-data-destroy",
1705
+ re: new RegExp(
1706
+ `${P}(psql|mysql|mariadb|sqlite3|mongosh?)\\b[^\\n]*\\b(drop\\s+(table|database|schema)|truncate|delete\\s+from)\\b|${P}aws\\s+s3\\s+(rm|rb)\\b|${P}aws\\s+(dynamodb|rds)\\s+delete-\\S+|${P}redis-cli\\b[^\\n]*\\bflush(all|db)\\b`,
1707
+ "i"
1708
+ )
1709
+ },
1710
+ {
1711
+ op: "prod-infra-change",
1712
+ re: new RegExp(
1713
+ `${P}terraform\\s+(apply|destroy)\\b|${P}pulumi\\s+(up|destroy)\\b|${P}kubectl\\s+(apply|delete|scale|drain)\\b|${P}helm\\s+(install|upgrade|uninstall|rollback)\\b|${P}aws\\s+(iam|ec2|route53|elbv2)\\s+(create|delete|put|update|modify|attach|detach)-\\S+|${P}(gcloud|az)\\s+\\S+\\s+(create|delete|update)\\b`,
1714
+ "i"
1715
+ )
1716
+ },
1717
+ {
1718
+ op: "prod-deploy",
1719
+ re: new RegExp(
1720
+ `${P}(vercel|netlify|flyctl|fly|wrangler|serverless|sls|eb|heroku)\\s+${G}deploy\\b|${P}(vercel|netlify)\\b[^\\n]*--prod\\b|${P}(npm|pnpm|yarn)\\s+run\\s+deploy\\b|${P}cap\\s+production\\s+deploy\\b`,
1721
+ "i"
1722
+ )
1723
+ },
1724
+ {
1725
+ op: "external-send",
1726
+ re: new RegExp(
1727
+ `${P}(sendmail|msmtp|mailx?)\\b|${P}curl\\b(?=[^\\n]*(?:-X|--request)\\s+(?:POST|PUT|PATCH)\\b)(?=[^\\n]*https?:\\/\\/)`,
1728
+ "i"
1729
+ )
1730
+ },
1731
+ {
1732
+ op: "money-movement",
1733
+ re: new RegExp(
1734
+ `${P}(stripe|braintree|paypal)\\s+\\S*(charge|refund|payout|transfer|payment)\\S*\\b`,
1735
+ "i"
1736
+ )
1737
+ },
1738
+ {
1739
+ // `rm -rf` on an ABSOLUTE path or a home-relative one. Deliberately not
1740
+ // "outside the working tree" — this function receives a command string and
1741
+ // no cwd, so that reading is not decidable here and a classifier that
1742
+ // guesses is worse than one that under-reports.
1743
+ op: "out-of-scope-destruction",
1744
+ re: new RegExp(`${P}rm\\s+(-\\S+\\s+)*-\\S*[rR]\\S*\\s+(-\\S+\\s+)*(~|/)\\S*`, "i")
1745
+ },
1663
1746
  { op: "push", re: new RegExp(`${P}git\\s+${G}push\\b`, "i") },
1664
1747
  { op: "commit", re: new RegExp(`${P}git\\s+${G}commit\\b`, "i") },
1665
1748
  { op: "branch", re: new RegExp(`${P}git\\s+${G}(checkout\\s+-b|switch\\s+-c)\\b`, "i") }
1666
1749
  ];
1667
- var COMMAND_TOOLS = /* @__PURE__ */ new Set([
1668
- "launch-process",
1669
- "launch_process",
1670
- "Bash",
1671
- "BashTool",
1672
- "run-process",
1673
- "runProcess",
1674
- "shell",
1675
- "execute_shell",
1676
- "RunShellCommand"
1677
- ]);
1678
- function extractCommand(envelope) {
1679
- const payload = _isObject4(envelope["payload"]) ? envelope["payload"] : envelope;
1680
- const toolRaw = payload["tool_name"] ?? payload["toolName"] ?? payload["tool"];
1681
- const tool = typeof toolRaw === "string" ? toolRaw : null;
1682
- if (tool !== null && !COMMAND_TOOLS.has(tool)) {
1683
- return null;
1684
- }
1685
- const input = _isObject4(payload["tool_input"]) ? payload["tool_input"] : _isObject4(payload["toolInput"]) ? payload["toolInput"] : payload;
1686
- for (const key of ["command", "cmd", "script"]) {
1687
- const v = input[key];
1688
- if (typeof v === "string" && v.trim()) {
1689
- return v;
1690
- }
1691
- }
1692
- return null;
1693
- }
1694
1750
  function splitOutsideQuotes(command) {
1695
1751
  const parts = [];
1696
1752
  let cur = "";
@@ -1837,7 +1893,7 @@ function invokedSegments(command, depth = 0) {
1837
1893
  function commandOp(command) {
1838
1894
  let found = null;
1839
1895
  for (const seg of invokedSegments(command)) {
1840
- if (!/^(?:\S*\/)?(git|gh|npm|pnpm|yarn)\b/.test(seg)) {
1896
+ if (!/^(?:\S*\/)?(git|gh|npm|pnpm|yarn)\b/.test(seg) && !CONSEQUENCE_TOOL_HEAD.test(seg)) {
1841
1897
  continue;
1842
1898
  }
1843
1899
  for (const { op, re } of COMMAND_OPS) {
@@ -1852,191 +1908,50 @@ function commandOp(command) {
1852
1908
  }
1853
1909
  return found;
1854
1910
  }
1855
- function mergeTargetOf(command) {
1856
- const gh = /\bgh\s+pr\s+merge\s+(?:-{1,2}\S+\s+)*?(\d{1,10})\b/i.exec(command);
1857
- if (gh) {
1858
- return _boundedTarget(gh[1]);
1859
- }
1860
- const api = /\/pulls\/(\d{1,10})\/merge\b/i.exec(command);
1861
- return api ? _boundedTarget(api[1]) : null;
1862
- }
1863
- function _boundedTarget(digits) {
1864
- const n = Number.parseInt(digits, 10);
1865
- return Number.isSafeInteger(n) && n > 0 && n <= PR_NUMBER_MAX ? n : null;
1866
- }
1867
- function grantCovers(grants, op, target) {
1868
- if (op !== "pr-merge" || target === null) {
1869
- return null;
1870
- }
1871
- return grants.find(
1872
- (g) => g.op === "pr-merge" && g.targets.includes(target) && !g.consumed.includes(target)
1873
- ) ?? null;
1874
- }
1875
- var LEDGER_MAX_AGE_MS = 30 * 60 * 1e3;
1876
- function _loadLedger(consumer_root, session_id, now) {
1877
- const grants = readGrants(consumer_root, session_id);
1878
- const candidates = [ledgerFileFor(session_id)];
1879
- if (!candidates.includes(STATE_FILE2)) {
1880
- candidates.push(STATE_FILE2);
1881
- }
1882
- for (const rel of candidates) {
1883
- const found = _readLedgerFile(path3.join(consumer_root, rel), session_id, now);
1884
- if (found !== null) {
1885
- return { ...found, grants };
1886
- }
1887
- }
1888
- return { authorized: /* @__PURE__ */ new Set(), present: false, grants };
1889
- }
1890
- function _readLedgerFile(file, session_id, now) {
1891
- try {
1892
- const raw = fs3.readFileSync(file, "utf8");
1893
- const decoded = JSON.parse(raw);
1894
- if (_isObject4(decoded) && Array.isArray(decoded["authorized"])) {
1895
- const ledgerSession = typeof decoded["session_id"] === "string" ? decoded["session_id"] : "";
1896
- if (session_id && ledgerSession && ledgerSession !== session_id) {
1897
- return null;
1898
- }
1899
- const at = Date.parse(String(decoded["detected_at"] ?? ""));
1900
- if (Number.isFinite(at) && now - at > LEDGER_MAX_AGE_MS) {
1901
- return null;
1902
- }
1903
- return {
1904
- authorized: new Set(decoded["authorized"].filter(Boolean)),
1905
- present: true
1906
- };
1911
+ function commandObject(op, command) {
1912
+ const patterns = COMMAND_OBJECTS[op];
1913
+ if (!patterns) return null;
1914
+ for (const re of patterns) {
1915
+ const m = new RegExp(re.source, re.flags.replace("g", "")).exec(command);
1916
+ const raw = m?.[1];
1917
+ if (raw) {
1918
+ const value = raw.trim().replace(/^["'`]+|["'`]+$/g, "").replace(/[.,;:]+$/, "").toLowerCase();
1919
+ if (value) return value;
1907
1920
  }
1908
- } catch {
1909
1921
  }
1910
1922
  return null;
1911
1923
  }
1912
- function decide(command, ledger, impact) {
1913
- if (command === null) {
1914
- return { exit: EXIT_ALLOW3, stdout: "", stderr: "" };
1915
- }
1916
- const op = commandOp(command);
1917
- if (op === null || ledger.authorized.has(op)) {
1918
- return { exit: EXIT_ALLOW3, stdout: "", stderr: "" };
1919
- }
1920
- const target = mergeTargetOf(command);
1921
- if (grantCovers(ledger.grants ?? [], op, target) && target !== null) {
1922
- return { exit: EXIT_ALLOW3, stdout: "", stderr: "", consumedTarget: target };
1923
- }
1924
- const shown = command.length > 120 ? `${command.slice(0, 117)}\u2026` : command;
1925
- if (BLOCK_OPS.has(op)) {
1926
- const scan = op === "pr-merge" && target !== null && impact ? ` ${impact}` : "";
1927
- const reason = `Blocked: \`${op}\` with no authorization in this turn's prompt. This operation is irreversible and non-destructive-by-default already declares it never-autonomous. Command: ${shown}.${scan} Ask the user, in one numbered-options block, and run it only after they answer this turn.`;
1928
- return { exit: EXIT_BLOCK, stdout: "", stderr: `${reason}
1929
- `, blockedOp: op };
1930
- }
1931
- if (WARN_OPS.has(op)) {
1932
- const reason = `No \`${op}\` authorization found in this turn's prompt (commit-policy: a one-shot authorization is spent on the operation it named). If the user did authorize it this turn, proceed; otherwise stop and ask. Command: ${shown}`;
1933
- return {
1934
- exit: EXIT_ALLOW3,
1935
- stdout: `${JSON.stringify({ decision: "warn", reason })}
1936
- `,
1937
- stderr: ""
1938
- };
1939
- }
1940
- return { exit: EXIT_ALLOW3, stdout: "", stderr: "" };
1941
- }
1942
- function run3(stdin_text, options) {
1943
- let envelope = {};
1944
- if (stdin_text.trim()) {
1945
- try {
1946
- const decoded = JSON.parse(stdin_text);
1947
- if (_isObject4(decoded)) {
1948
- envelope = decoded;
1949
- }
1950
- } catch {
1951
- return EXIT_ALLOW3;
1952
- }
1953
- }
1954
- const session_id = typeof envelope["session_id"] === "string" ? envelope["session_id"] : "";
1955
- const command = extractCommand(envelope);
1956
- const ledger = _loadLedger(options.consumer_root, session_id, Date.now());
1957
- let impact;
1958
- const stage1 = command === null ? null : commandOp(command);
1959
- const stage2Target = command === null ? null : mergeTargetOf(command);
1960
- if (stage1 === "pr-merge" && stage2Target !== null && !ledger.authorized.has("pr-merge") && !grantCovers(ledger.grants ?? [], "pr-merge", stage2Target)) {
1961
- try {
1962
- impact = describeImpact(
1963
- stage2Target,
1964
- analyseMergeImpact(stage2Target, { cwd: options.consumer_root })
1965
- );
1966
- } catch {
1967
- }
1968
- }
1969
- const decision = decide(command, ledger, impact);
1970
- if (decision.consumedTarget !== void 0) {
1971
- consumeGrantTarget(options.consumer_root, session_id, decision.consumedTarget);
1972
- }
1973
- if (decision.blockedOp) {
1974
- try {
1975
- atomic_write_json(path3.join(options.consumer_root, pendingFileFor(session_id)), {
1976
- op: decision.blockedOp,
1977
- session_id,
1978
- refused_at: (/* @__PURE__ */ new Date()).toISOString()
1979
- });
1980
- } catch {
1981
- }
1982
- }
1983
- if (decision.stdout) {
1984
- process.stdout.write(decision.stdout);
1985
- }
1986
- if (decision.stderr) {
1987
- process.stderr.write(decision.stderr);
1988
- }
1989
- return decision.exit;
1990
- }
1991
- function main3(argv) {
1992
- const args = argv ?? process.argv.slice(2);
1993
- let consumer_root = process.cwd();
1994
- for (let i = 0; i < args.length; i += 1) {
1995
- const a = args[i];
1996
- if (a === "--project-dir" && args[i + 1] !== void 0) {
1997
- consumer_root = args[i + 1];
1998
- i += 1;
1999
- } else if (a !== void 0 && a.startsWith("--project-dir=")) {
2000
- consumer_root = a.slice("--project-dir=".length);
2001
- }
2002
- }
2003
- return run3(readHookStdin(), { consumer_root });
2004
- }
2005
- function _isCliEntry3() {
2006
- if (true) {
2007
- return false;
2008
- }
2009
- if (process.argv[1] === void 0) {
2010
- return false;
2011
- }
2012
- const argvUrl = pathToFileURL3(path3.resolve(process.argv[1])).href;
2013
- if (import.meta.url === argvUrl) {
2014
- return true;
2015
- }
2016
- try {
2017
- const here = fs3.realpathSync(fileURLToPath3(import.meta.url));
2018
- const argv = fs3.realpathSync(path3.resolve(process.argv[1]));
2019
- return here === argv;
2020
- } catch {
2021
- return false;
2022
- }
2023
- }
2024
- if (_isCliEntry3()) {
2025
- process.exit(main3());
2026
- }
1924
+ var COMMAND_OBJECTS = {
1925
+ "prod-data-destroy": [
1926
+ /\bdrop\s+(?:table|database|schema|collection)\s+(?:if\s+exists\s+)?["'`]?([\w.$-]+)/i,
1927
+ /\btruncate\s+(?:table\s+)?["'`]?([\w.$-]+)/i,
1928
+ /\bdelete\s+from\s+["'`]?([\w.$-]+)/i,
1929
+ /\bs3:\/\/([\w.-]+)/i,
1930
+ /--table-name[= ]["'`]?([\w.-]+)/i
1931
+ ],
1932
+ "external-send": [
1933
+ /\bhttps?:\/\/([^\s/"'`]+)/i,
1934
+ /([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/
1935
+ ],
1936
+ "money-movement": [
1937
+ /--customer[= ]["'`]?([\w.-]+)/i,
1938
+ /--amount[= ]["'`]?([\d.,]+)/i,
1939
+ /\b(\d[\d.,]*)\s*(?:eur|usd)\b/i
1940
+ ]
1941
+ };
2027
1942
 
2028
1943
  // src/scripts/hooks/turn_end_gate_hook.ts
2029
- import * as fs6 from "node:fs";
2030
- import * as path6 from "node:path";
2031
- import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL6 } from "node:url";
1944
+ import * as fs5 from "node:fs";
1945
+ import * as path5 from "node:path";
1946
+ import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL5 } from "node:url";
2032
1947
 
2033
1948
  // src/scripts/hooks/end_review_nudge_hook.ts
2034
1949
  import { spawnSync } from "node:child_process";
2035
1950
  import * as crypto from "node:crypto";
2036
- import * as fs4 from "node:fs";
1951
+ import * as fs3 from "node:fs";
2037
1952
  import * as os from "node:os";
2038
- import * as path4 from "node:path";
2039
- import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL4 } from "node:url";
1953
+ import * as path3 from "node:path";
1954
+ import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "node:url";
2040
1955
 
2041
1956
  // src/scripts/_lib/review_skipped_record.ts
2042
1957
  var PRODUCER_PRIVACY_CLASS = "ids-only";
@@ -2091,11 +2006,11 @@ var ENVELOPE_KEYS = [
2091
2006
  "event",
2092
2007
  "payload"
2093
2008
  ];
2094
- function _isObject5(obj) {
2009
+ function _isObject4(obj) {
2095
2010
  return typeof obj === "object" && obj !== null && !Array.isArray(obj);
2096
2011
  }
2097
2012
  function looks_like_envelope(obj) {
2098
- if (!_isObject5(obj)) {
2013
+ if (!_isObject4(obj)) {
2099
2014
  return false;
2100
2015
  }
2101
2016
  return ENVELOPE_KEYS.every((key) => key in obj);
@@ -2114,14 +2029,14 @@ function unwrap(stdin_text, default_platform = "generic") {
2114
2029
  if (looks_like_envelope(decoded)) {
2115
2030
  const env = decoded;
2116
2031
  let payload2 = env["payload"];
2117
- if (!payload2 || !_isObject5(payload2)) {
2032
+ if (!payload2 || !_isObject4(payload2)) {
2118
2033
  payload2 = {};
2119
2034
  }
2120
2035
  const platRaw = env["platform"];
2121
2036
  const platform = String(platRaw || default_platform);
2122
2037
  return [env, payload2, platform];
2123
2038
  }
2124
- const payload = _isObject5(decoded) ? decoded : {};
2039
+ const payload = _isObject4(decoded) ? decoded : {};
2125
2040
  return [
2126
2041
  {
2127
2042
  schema_version: 1,
@@ -2147,13 +2062,13 @@ function isSafeTranscriptPath(rawPath, opts = {}) {
2147
2062
  const homeDir = opts.homeDir ?? os.homedir();
2148
2063
  const maxBytes = opts.maxBytes ?? TRANSCRIPT_SCAN_MAX_BYTES;
2149
2064
  try {
2150
- const realHome = fs4.realpathSync(homeDir);
2151
- const realPath = fs4.realpathSync(rawPath);
2152
- const relative2 = path4.relative(realHome, realPath);
2153
- if (relative2 === "" || relative2.startsWith("..") || path4.isAbsolute(relative2)) {
2065
+ const realHome = fs3.realpathSync(homeDir);
2066
+ const realPath = fs3.realpathSync(rawPath);
2067
+ const relative2 = path3.relative(realHome, realPath);
2068
+ if (relative2 === "" || relative2.startsWith("..") || path3.isAbsolute(relative2)) {
2154
2069
  return false;
2155
2070
  }
2156
- const { size } = fs4.statSync(realPath);
2071
+ const { size } = fs3.statSync(realPath);
2157
2072
  return size <= maxBytes;
2158
2073
  } catch {
2159
2074
  return false;
@@ -2212,13 +2127,13 @@ var BINARY_PROBE_BYTES = 8e3;
2212
2127
  var UNTRACKED_FILE_READ_CAP_BYTES = 8 * 1024 * 1024;
2213
2128
  function untrackedFileLineCount(cwd, relPath) {
2214
2129
  try {
2215
- const abs = path4.resolve(cwd, relPath);
2216
- const size = fs4.statSync(abs).size;
2130
+ const abs = path3.resolve(cwd, relPath);
2131
+ const size = fs3.statSync(abs).size;
2217
2132
  if (size === 0) return 0;
2218
2133
  if (size > UNTRACKED_FILE_READ_CAP_BYTES) {
2219
2134
  return MUTATION_LINE_THRESHOLD + 1;
2220
2135
  }
2221
- const buf = fs4.readFileSync(abs);
2136
+ const buf = fs3.readFileSync(abs);
2222
2137
  if (buf.length === 0) return 0;
2223
2138
  const probe = buf.subarray(0, Math.min(buf.length, BINARY_PROBE_BYTES));
2224
2139
  if (probe.includes(0)) return 0;
@@ -2308,19 +2223,19 @@ function appendReviewSkippedTelemetry(workspaceRoot, diffLines, mutationMeasure)
2308
2223
  });
2309
2224
  if (errors.length || line === null) return;
2310
2225
  try {
2311
- const dir = path4.join(workspaceRoot, "agents", "runtime", "state", "audit");
2312
- fs4.mkdirSync(dir, { recursive: true });
2313
- const file = path4.join(dir, `${ts.slice(0, 7)}.jsonl`);
2314
- fs4.appendFileSync(file, `${JSON.stringify(line)}
2226
+ const dir = path3.join(workspaceRoot, "agents", "runtime", "state", "audit");
2227
+ fs3.mkdirSync(dir, { recursive: true });
2228
+ const file = path3.join(dir, `${ts.slice(0, 7)}.jsonl`);
2229
+ fs3.appendFileSync(file, `${JSON.stringify(line)}
2315
2230
  `, "utf8");
2316
2231
  } catch {
2317
2232
  }
2318
2233
  }
2319
2234
  function sessionStateDir(workspaceRoot) {
2320
- return path4.join(workspaceRoot, "agents", "runtime", "state", "end-review-nudge");
2235
+ return path3.join(workspaceRoot, "agents", "runtime", "state", "end-review-nudge");
2321
2236
  }
2322
2237
  function sessionStateFile(workspaceRoot, sessionKey) {
2323
- return path4.join(sessionStateDir(workspaceRoot), `${sessionKey}.json`);
2238
+ return path3.join(sessionStateDir(workspaceRoot), `${sessionKey}.json`);
2324
2239
  }
2325
2240
  function deriveSessionKey2(envelope, payload) {
2326
2241
  const raw = str(envelope["session_id"]) || str(payload["transcript_path"] ?? payload["transcriptPath"]);
@@ -2329,7 +2244,7 @@ function deriveSessionKey2(envelope, payload) {
2329
2244
  }
2330
2245
  function hasFiredThisSession(workspaceRoot, sessionKey) {
2331
2246
  try {
2332
- return fs4.existsSync(sessionStateFile(workspaceRoot, sessionKey));
2247
+ return fs3.existsSync(sessionStateFile(workspaceRoot, sessionKey));
2333
2248
  } catch {
2334
2249
  return false;
2335
2250
  }
@@ -2343,7 +2258,7 @@ function markFiredThisSession(workspaceRoot, sessionKey, ts) {
2343
2258
  function _read_stdin() {
2344
2259
  return readHookStdin();
2345
2260
  }
2346
- function main4() {
2261
+ function main3() {
2347
2262
  const [envelope, payload] = unwrap(_read_stdin(), "claude");
2348
2263
  const event = String(envelope["event"] ?? "");
2349
2264
  if (event !== "" && event !== "stop") {
@@ -2372,7 +2287,7 @@ function main4() {
2372
2287
  let reviewerRan = false;
2373
2288
  if (transcriptPath && isSafeTranscriptPath(transcriptPath)) {
2374
2289
  try {
2375
- reviewerRan = scanTranscriptForReviewer(fs4.readFileSync(transcriptPath, "utf-8"));
2290
+ reviewerRan = scanTranscriptForReviewer(fs3.readFileSync(transcriptPath, "utf-8"));
2376
2291
  } catch {
2377
2292
  reviewerRan = false;
2378
2293
  }
@@ -2395,34 +2310,34 @@ function main4() {
2395
2310
  );
2396
2311
  return EXIT_WARN2;
2397
2312
  }
2398
- function _isCliEntry4() {
2313
+ function _isCliEntry3() {
2399
2314
  if (true) {
2400
2315
  return false;
2401
2316
  }
2402
2317
  if (process.argv[1] === void 0) {
2403
2318
  return false;
2404
2319
  }
2405
- const argvUrl = pathToFileURL4(path4.resolve(process.argv[1])).href;
2320
+ const argvUrl = pathToFileURL3(path3.resolve(process.argv[1])).href;
2406
2321
  if (import.meta.url === argvUrl) {
2407
2322
  return true;
2408
2323
  }
2409
2324
  try {
2410
- const here = fs4.realpathSync(fileURLToPath4(import.meta.url));
2411
- const argv = fs4.realpathSync(path4.resolve(process.argv[1]));
2325
+ const here = fs3.realpathSync(fileURLToPath3(import.meta.url));
2326
+ const argv = fs3.realpathSync(path3.resolve(process.argv[1]));
2412
2327
  return here === argv;
2413
2328
  } catch {
2414
2329
  return false;
2415
2330
  }
2416
2331
  }
2417
- if (_isCliEntry4()) {
2418
- process.exit(main4());
2332
+ if (_isCliEntry3()) {
2333
+ process.exit(main3());
2419
2334
  }
2420
2335
 
2421
2336
  // src/scripts/hooks/subagent_ledger_hook.ts
2422
2337
  import * as crypto2 from "node:crypto";
2423
- import * as fs5 from "node:fs";
2424
- import * as path5 from "node:path";
2425
- import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL5 } from "node:url";
2338
+ import * as fs4 from "node:fs";
2339
+ import * as path4 from "node:path";
2340
+ import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL4 } from "node:url";
2426
2341
 
2427
2342
  // src/scripts/_lib/subagent_response.ts
2428
2343
  var MAX_SUMMARY_CHARS = 2e3;
@@ -2524,11 +2439,11 @@ function validateResponse(input) {
2524
2439
  }
2525
2440
 
2526
2441
  // src/scripts/hooks/subagent_ledger_hook.ts
2527
- var EXIT_ALLOW4 = 0;
2442
+ var EXIT_ALLOW3 = 0;
2528
2443
  function isObject2(v) {
2529
2444
  return typeof v === "object" && v !== null && !Array.isArray(v);
2530
2445
  }
2531
- var LEDGER_DIR = path5.join("agents", "runtime", "state", "subagent-ledger");
2446
+ var LEDGER_DIR = path4.join("agents", "runtime", "state", "subagent-ledger");
2532
2447
  var OPEN_SUBDIR = "open";
2533
2448
  var OPEN_RECORD_TTL_MS = 24 * 60 * 60 * 1e3;
2534
2449
  function refFor(agentId) {
@@ -2618,10 +2533,10 @@ function _jsonObjectCandidates(message) {
2618
2533
  return out;
2619
2534
  }
2620
2535
  function openDir(root) {
2621
- return path5.join(root, LEDGER_DIR, OPEN_SUBDIR);
2536
+ return path4.join(root, LEDGER_DIR, OPEN_SUBDIR);
2622
2537
  }
2623
2538
  function openFile(root, ref) {
2624
- return path5.join(openDir(root), `${ref}.json`);
2539
+ return path4.join(openDir(root), `${ref}.json`);
2625
2540
  }
2626
2541
  var DEPTH_BASES = /* @__PURE__ */ new Set(["observed", "asserted-parent", "assumed-root"]);
2627
2542
  function _asOpenRecord(value, expectedRef) {
@@ -2648,7 +2563,7 @@ function readOpenRecords(root) {
2648
2563
  const out = /* @__PURE__ */ new Map();
2649
2564
  let names;
2650
2565
  try {
2651
- names = fs5.readdirSync(openDir(root));
2566
+ names = fs4.readdirSync(openDir(root));
2652
2567
  } catch {
2653
2568
  return out;
2654
2569
  }
@@ -2656,7 +2571,7 @@ function readOpenRecords(root) {
2656
2571
  if (!name.endsWith(".json")) continue;
2657
2572
  const expectedRef = name.slice(0, -".json".length);
2658
2573
  try {
2659
- const raw = fs5.readFileSync(path5.join(openDir(root), name), "utf8");
2574
+ const raw = fs4.readFileSync(path4.join(openDir(root), name), "utf8");
2660
2575
  const rec = _asOpenRecord(JSON.parse(raw), expectedRef);
2661
2576
  if (rec) out.set(rec.ref, rec);
2662
2577
  } catch {
@@ -2671,13 +2586,13 @@ function resolveDepth(parentRef, open) {
2671
2586
  return { depth: parent.depth + 1, depth_basis: "observed" };
2672
2587
  }
2673
2588
  function ledgerFileFor2(root, ts) {
2674
- return path5.join(root, LEDGER_DIR, `${ts.slice(0, 7)}.jsonl`);
2589
+ return path4.join(root, LEDGER_DIR, `${ts.slice(0, 7)}.jsonl`);
2675
2590
  }
2676
2591
  function appendLedgerLine(root, ts, line) {
2677
2592
  if (is_replay_mode()) return;
2678
2593
  const file = ledgerFileFor2(root, ts);
2679
- fs5.mkdirSync(path5.dirname(file), { recursive: true });
2680
- fs5.appendFileSync(file, `${JSON.stringify(line)}
2594
+ fs4.mkdirSync(path4.dirname(file), { recursive: true });
2595
+ fs4.appendFileSync(file, `${JSON.stringify(line)}
2681
2596
  `, "utf8");
2682
2597
  }
2683
2598
  var WALL_CLOCK_ARMS_MS = [
@@ -2691,7 +2606,7 @@ function armsExceeded(elapsedMs) {
2691
2606
  return WALL_CLOCK_ARMS_MS.filter((a) => elapsedMs >= a.ms).map((a) => a.label);
2692
2607
  }
2693
2608
  function openRecordStats(root, nowMs = Date.now()) {
2694
- const ledgerPresent = fs5.existsSync(path5.join(root, LEDGER_DIR));
2609
+ const ledgerPresent = fs4.existsSync(path4.join(root, LEDGER_DIR));
2695
2610
  const open = readOpenRecords(root);
2696
2611
  let maxDepth = 0;
2697
2612
  let counted = 0;
@@ -2719,13 +2634,13 @@ function writeOpenRecord(root, rec) {
2719
2634
  function removeOpenRecord(root, ref) {
2720
2635
  if (is_replay_mode()) return;
2721
2636
  try {
2722
- fs5.unlinkSync(openFile(root, ref));
2637
+ fs4.unlinkSync(openFile(root, ref));
2723
2638
  } catch {
2724
2639
  }
2725
2640
  }
2726
2641
  function countOpen(root) {
2727
2642
  try {
2728
- return fs5.readdirSync(openDir(root)).filter((n) => n.endsWith(".json")).length;
2643
+ return fs4.readdirSync(openDir(root)).filter((n) => n.endsWith(".json")).length;
2729
2644
  } catch {
2730
2645
  return 0;
2731
2646
  }
@@ -2852,9 +2767,9 @@ function handleStop(root, payload, nowIso, sessionId) {
2852
2767
  }
2853
2768
  function processEnvelope(envelope, consumerRoot) {
2854
2769
  try {
2855
- if (!isObject2(envelope)) return EXIT_ALLOW4;
2770
+ if (!isObject2(envelope)) return EXIT_ALLOW3;
2856
2771
  const event = envelope["event"];
2857
- if (event !== "subagent_start" && event !== "subagent_stop") return EXIT_ALLOW4;
2772
+ if (event !== "subagent_start" && event !== "subagent_stop") return EXIT_ALLOW3;
2858
2773
  const payload = unwrapPayload(envelope);
2859
2774
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
2860
2775
  const sessionId = str2(payload, "session_id", "sessionId") ?? str2(envelope, "session_id", "sessionId");
@@ -2862,9 +2777,9 @@ function processEnvelope(envelope, consumerRoot) {
2862
2777
  if (event === "subagent_start") handleStart(consumerRoot, payload, nowIso, sessionId);
2863
2778
  else handleStop(consumerRoot, payload, nowIso, sessionId);
2864
2779
  } catch {
2865
- return EXIT_ALLOW4;
2780
+ return EXIT_ALLOW3;
2866
2781
  }
2867
- return EXIT_ALLOW4;
2782
+ return EXIT_ALLOW3;
2868
2783
  }
2869
2784
  function resolveConsumerRoot(envelope) {
2870
2785
  if (isObject2(envelope)) {
@@ -2876,34 +2791,34 @@ function resolveConsumerRoot(envelope) {
2876
2791
  }
2877
2792
  return process.cwd();
2878
2793
  }
2879
- function main5() {
2794
+ function main4() {
2880
2795
  const raw = readHookStdin();
2881
2796
  let envelope = {};
2882
2797
  try {
2883
2798
  envelope = raw.trim() ? JSON.parse(raw) : {};
2884
2799
  } catch {
2885
- return EXIT_ALLOW4;
2800
+ return EXIT_ALLOW3;
2886
2801
  }
2887
2802
  return processEnvelope(envelope, resolveConsumerRoot(envelope));
2888
2803
  }
2889
- function _isCliEntry5() {
2804
+ function _isCliEntry4() {
2890
2805
  if (true) {
2891
2806
  return false;
2892
2807
  }
2893
2808
  if (process.argv[1] === void 0) return false;
2894
- const argvUrl = pathToFileURL5(path5.resolve(process.argv[1])).href;
2809
+ const argvUrl = pathToFileURL4(path4.resolve(process.argv[1])).href;
2895
2810
  if (import.meta.url === argvUrl) return true;
2896
2811
  try {
2897
- return fs5.realpathSync(fileURLToPath5(import.meta.url)) === fs5.realpathSync(path5.resolve(process.argv[1]));
2812
+ return fs4.realpathSync(fileURLToPath4(import.meta.url)) === fs4.realpathSync(path4.resolve(process.argv[1]));
2898
2813
  } catch {
2899
2814
  return false;
2900
2815
  }
2901
2816
  }
2902
- if (_isCliEntry5()) process.exit(main5());
2817
+ if (_isCliEntry4()) process.exit(main4());
2903
2818
 
2904
2819
  // src/scripts/hooks/turn_end_gate_hook.ts
2905
- var EXIT_BLOCK2 = 1;
2906
- var EXIT_ALLOW5 = 0;
2820
+ var EXIT_BLOCK = 1;
2821
+ var EXIT_ALLOW4 = 0;
2907
2822
  var TRANSCRIPT_READ_MAX_BYTES = 8 * 1024 * 1024;
2908
2823
  function stripFencedBlocks(text) {
2909
2824
  const out = [];
@@ -3008,7 +2923,7 @@ function detectPromissory(reply) {
3008
2923
  function readCiSettled(workspaceRoot, session_id) {
3009
2924
  if (!hasStableSessionId(session_id)) return { seen: false, settled: false };
3010
2925
  try {
3011
- const raw = fs6.readFileSync(path6.join(workspaceRoot, statePathFor(session_id)), "utf-8");
2926
+ const raw = fs5.readFileSync(path5.join(workspaceRoot, statePathFor(session_id)), "utf-8");
3012
2927
  const decoded = JSON.parse(raw);
3013
2928
  if (!owns_session_state(decoded, session_id)) return { seen: false, settled: false };
3014
2929
  if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
@@ -3044,8 +2959,8 @@ function detectCompletionClaim(reply, ci) {
3044
2959
  function readLanguagePin(workspaceRoot, session_id) {
3045
2960
  if (!hasStableSessionId(session_id)) return "und";
3046
2961
  try {
3047
- const raw = fs6.readFileSync(
3048
- path6.join(workspaceRoot, statePathFor2(session_id)),
2962
+ const raw = fs5.readFileSync(
2963
+ path5.join(workspaceRoot, statePathFor2(session_id)),
3049
2964
  "utf-8"
3050
2965
  );
3051
2966
  const decoded = JSON.parse(raw);
@@ -3101,7 +3016,7 @@ function sessionStateFile2(workspaceRoot, sessionKey) {
3101
3016
  }
3102
3017
  function alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal) {
3103
3018
  try {
3104
- const raw = fs6.readFileSync(sessionStateFile2(workspaceRoot, sessionKey), "utf-8");
3019
+ const raw = fs5.readFileSync(sessionStateFile2(workspaceRoot, sessionKey), "utf-8");
3105
3020
  const decoded = JSON.parse(raw);
3106
3021
  if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
3107
3022
  return decoded["refused_turn"] === turnOrdinal;
@@ -3115,7 +3030,7 @@ function markRefusedTurn(workspaceRoot, sessionKey, turnOrdinal, detectors) {
3115
3030
  const file = sessionStateFile2(workspaceRoot, sessionKey);
3116
3031
  let prev = null;
3117
3032
  try {
3118
- prev = parseRecord(fs6.readFileSync(file, "utf-8"));
3033
+ prev = parseRecord(fs5.readFileSync(file, "utf-8"));
3119
3034
  } catch {
3120
3035
  }
3121
3036
  let version;
@@ -3142,10 +3057,10 @@ function readTranscriptTail(transcriptPath, opts = {}) {
3142
3057
  if (!transcriptPath || !isSafeTranscriptPath(transcriptPath, opts)) return empty;
3143
3058
  let lines;
3144
3059
  try {
3145
- if (opts.maxBytes !== void 0 && fs6.statSync(transcriptPath).size > opts.maxBytes) {
3060
+ if (opts.maxBytes !== void 0 && fs5.statSync(transcriptPath).size > opts.maxBytes) {
3146
3061
  return empty;
3147
3062
  }
3148
- lines = fs6.readFileSync(transcriptPath, "utf-8").split("\n");
3063
+ lines = fs5.readFileSync(transcriptPath, "utf-8").split("\n");
3149
3064
  } catch {
3150
3065
  return empty;
3151
3066
  }
@@ -3221,15 +3136,15 @@ function _messageText(content) {
3221
3136
  function str3(v) {
3222
3137
  return typeof v === "string" ? v : "";
3223
3138
  }
3224
- function main6() {
3139
+ function main5() {
3225
3140
  let envelope;
3226
3141
  let payload;
3227
3142
  try {
3228
3143
  [envelope, payload] = unwrap(readHookStdin(), "claude");
3229
3144
  } catch {
3230
- return EXIT_ALLOW5;
3145
+ return EXIT_ALLOW4;
3231
3146
  }
3232
- if (payload["stop_hook_active"] === true) return EXIT_ALLOW5;
3147
+ if (payload["stop_hook_active"] === true) return EXIT_ALLOW4;
3233
3148
  const workspaceRoot = str3(envelope["workspace_root"]) || process.cwd();
3234
3149
  let dispatchOpen = false;
3235
3150
  try {
@@ -3242,10 +3157,10 @@ function main6() {
3242
3157
  const { lastAssistant, turnOrdinal, toolCalls } = readTranscriptTail(transcriptPath, {
3243
3158
  maxBytes: TRANSCRIPT_READ_MAX_BYTES
3244
3159
  });
3245
- if (!lastAssistant) return EXIT_ALLOW5;
3160
+ if (!lastAssistant) return EXIT_ALLOW4;
3246
3161
  const rawSessionId = str3(envelope["session_id"]) || "";
3247
3162
  const sessionKey = deriveSessionKey(rawSessionId || "unknown-session");
3248
- if (alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal)) return EXIT_ALLOW5;
3163
+ if (alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal)) return EXIT_ALLOW4;
3249
3164
  const findings = [];
3250
3165
  for (const f of [
3251
3166
  // A and D are the completion-adjacent pair a pending dispatch excuses
@@ -3268,7 +3183,7 @@ function main6() {
3268
3183
  ]) {
3269
3184
  if (f) findings.push(f);
3270
3185
  }
3271
- if (findings.length === 0) return EXIT_ALLOW5;
3186
+ if (findings.length === 0) return EXIT_ALLOW4;
3272
3187
  markRefusedTurn(
3273
3188
  workspaceRoot,
3274
3189
  sessionKey,
@@ -3286,40 +3201,40 @@ ${lines.join("\n")}
3286
3201
  This turn will not be refused a second time.
3287
3202
  `
3288
3203
  );
3289
- return EXIT_BLOCK2;
3204
+ return EXIT_BLOCK;
3290
3205
  }
3291
- function _isCliEntry6() {
3206
+ function _isCliEntry5() {
3292
3207
  if (true) {
3293
3208
  return false;
3294
3209
  }
3295
3210
  if (process.argv[1] === void 0) {
3296
3211
  return false;
3297
3212
  }
3298
- const argvUrl = pathToFileURL6(path6.resolve(process.argv[1])).href;
3213
+ const argvUrl = pathToFileURL5(path5.resolve(process.argv[1])).href;
3299
3214
  if (import.meta.url === argvUrl) {
3300
3215
  return true;
3301
3216
  }
3302
3217
  try {
3303
- const here = fs6.realpathSync(fileURLToPath6(import.meta.url));
3304
- const argv = fs6.realpathSync(path6.resolve(process.argv[1]));
3218
+ const here = fs5.realpathSync(fileURLToPath5(import.meta.url));
3219
+ const argv = fs5.realpathSync(path5.resolve(process.argv[1]));
3305
3220
  return here === argv;
3306
3221
  } catch {
3307
3222
  return false;
3308
3223
  }
3309
3224
  }
3310
- if (_isCliEntry6()) {
3311
- process.exit(main6());
3225
+ if (_isCliEntry5()) {
3226
+ process.exit(main5());
3312
3227
  }
3313
3228
 
3314
3229
  // src/scripts/hooks/evidence_independence.ts
3315
3230
  import crypto3 from "node:crypto";
3316
- import fs7 from "node:fs";
3317
- import path7 from "node:path";
3318
- import { fileURLToPath as fileURLToPath7, pathToFileURL as pathToFileURL7 } from "node:url";
3319
- var EXIT_ALLOW6 = 0;
3320
- var EXIT_BLOCK3 = 1;
3321
- var STATE_FILE3 = path7.join("agents", "state", "evidence-dispatch.json");
3322
- function _isObject6(v) {
3231
+ import fs6 from "node:fs";
3232
+ import path6 from "node:path";
3233
+ import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL6 } from "node:url";
3234
+ var EXIT_ALLOW5 = 0;
3235
+ var EXIT_BLOCK2 = 1;
3236
+ var STATE_FILE3 = path6.join("agents", "state", "evidence-dispatch.json");
3237
+ function _isObject5(v) {
3323
3238
  return typeof v === "object" && v !== null && !Array.isArray(v);
3324
3239
  }
3325
3240
  var DISPATCH_TOOLS = /* @__PURE__ */ new Set([
@@ -3372,10 +3287,10 @@ function preloadedVerdict(prompt) {
3372
3287
  return null;
3373
3288
  }
3374
3289
  function extractDispatch(envelope) {
3375
- const payload = _isObject6(envelope["payload"]) ? envelope["payload"] : envelope;
3290
+ const payload = _isObject5(envelope["payload"]) ? envelope["payload"] : envelope;
3376
3291
  const toolRaw = payload["tool_name"] ?? payload["toolName"] ?? payload["tool"];
3377
3292
  const tool = typeof toolRaw === "string" ? toolRaw : null;
3378
- const input = _isObject6(payload["tool_input"]) ? payload["tool_input"] : _isObject6(payload["toolInput"]) ? payload["toolInput"] : payload;
3293
+ const input = _isObject5(payload["tool_input"]) ? payload["tool_input"] : _isObject5(payload["toolInput"]) ? payload["toolInput"] : payload;
3379
3294
  for (const key of ["prompt", "instructions", "task", "description"]) {
3380
3295
  const v = input[key];
3381
3296
  if (typeof v === "string" && v.trim()) {
@@ -3386,8 +3301,8 @@ function extractDispatch(envelope) {
3386
3301
  }
3387
3302
  function _ledgerStamp(consumer_root) {
3388
3303
  try {
3389
- const raw = fs7.readFileSync(
3390
- path7.join(consumer_root, "agents", "state", "git-authorization.json"),
3304
+ const raw = fs6.readFileSync(
3305
+ path6.join(consumer_root, "agents", "state", "git-authorization.json"),
3391
3306
  "utf8"
3392
3307
  );
3393
3308
  const d = JSON.parse(raw);
@@ -3398,8 +3313,8 @@ function _ledgerStamp(consumer_root) {
3398
3313
  }
3399
3314
  function _load(target) {
3400
3315
  try {
3401
- const decoded = JSON.parse(fs7.readFileSync(target, "utf8"));
3402
- if (_isObject6(decoded) && Array.isArray(decoded["evaluations"])) {
3316
+ const decoded = JSON.parse(fs6.readFileSync(target, "utf8"));
3317
+ if (_isObject5(decoded) && Array.isArray(decoded["evaluations"])) {
3403
3318
  return decoded;
3404
3319
  }
3405
3320
  } catch {
@@ -3407,14 +3322,14 @@ function _load(target) {
3407
3322
  return { session_id: "", turn_started_at: "", evaluations: [] };
3408
3323
  }
3409
3324
  var WARN_TEXT = "First evaluation dispatch this turn. Two shapes fabricated a binding honest-null in the audited sessions \u2014 check your prompt for both before it goes out: (1) a verdict pre-loaded into the prompt, and (2) a scope narrowed to files you chose. A review you commissioned on your own work is admissible as gate evidence only when the prompt is recorded alongside the verdict.";
3410
- function decide2(tool, prompt, priorEvaluations) {
3325
+ function decide(tool, prompt, priorEvaluations) {
3411
3326
  if (!isDispatchTool(tool) || !isEvaluationPrompt(prompt)) {
3412
- return { exit: EXIT_ALLOW6, stdout: "", stderr: "", evaluations: priorEvaluations };
3327
+ return { exit: EXIT_ALLOW5, stdout: "", stderr: "", evaluations: priorEvaluations };
3413
3328
  }
3414
3329
  const preloaded = preloadedVerdict(prompt);
3415
3330
  if (preloaded !== null) {
3416
3331
  return {
3417
- exit: EXIT_BLOCK3,
3332
+ exit: EXIT_BLOCK2,
3418
3333
  stdout: "",
3419
3334
  stderr: `Blocked: this evaluation prompt pre-loads its verdict ("${preloaded}"). That is the construct that produced a fabricated NO-FINDINGS committed as binding gate evidence, over a delta an unsteered pass then found a live critical in. Remove the expectation from the prompt and let the evaluator reach its own verdict.
3420
3335
  `,
@@ -3422,11 +3337,11 @@ function decide2(tool, prompt, priorEvaluations) {
3422
3337
  };
3423
3338
  }
3424
3339
  if (!isSelfScoped(prompt)) {
3425
- return { exit: EXIT_ALLOW6, stdout: "", stderr: "", evaluations: priorEvaluations };
3340
+ return { exit: EXIT_ALLOW5, stdout: "", stderr: "", evaluations: priorEvaluations };
3426
3341
  }
3427
3342
  if (priorEvaluations >= 1) {
3428
3343
  return {
3429
- exit: EXIT_ALLOW6,
3344
+ exit: EXIT_ALLOW5,
3430
3345
  stdout: `${JSON.stringify({
3431
3346
  decision: "warn",
3432
3347
  reason: `Second evaluation dispatch of your own work in this turn. If both passes judge the SAME subject, that is verdict shopping \u2014 commissioning another with a different prompt or scope selects the answer instead of measuring it. Report what the first pass returned, then re-plan. If this is an implementation fan-out rather than a second review, carry on: this check reads prose and cannot tell the two apart with certainty.`
@@ -3437,26 +3352,26 @@ function decide2(tool, prompt, priorEvaluations) {
3437
3352
  };
3438
3353
  }
3439
3354
  return {
3440
- exit: EXIT_ALLOW6,
3355
+ exit: EXIT_ALLOW5,
3441
3356
  stdout: `${JSON.stringify({ decision: "warn", reason: WARN_TEXT })}
3442
3357
  `,
3443
3358
  stderr: "",
3444
3359
  evaluations: priorEvaluations + 1
3445
3360
  };
3446
3361
  }
3447
- function run4(stdin_text, options) {
3362
+ function run3(stdin_text, options) {
3448
3363
  let envelope = {};
3449
3364
  if (stdin_text.trim()) {
3450
3365
  try {
3451
3366
  const decoded = JSON.parse(stdin_text);
3452
- if (_isObject6(decoded)) {
3367
+ if (_isObject5(decoded)) {
3453
3368
  envelope = decoded;
3454
3369
  }
3455
3370
  } catch {
3456
- return EXIT_ALLOW6;
3371
+ return EXIT_ALLOW5;
3457
3372
  }
3458
3373
  }
3459
- const target = path7.join(options.consumer_root, STATE_FILE3);
3374
+ const target = path6.join(options.consumer_root, STATE_FILE3);
3460
3375
  const state = _load(target);
3461
3376
  const session_id = typeof envelope["session_id"] === "string" ? envelope["session_id"] : "";
3462
3377
  const turnMarker = `${session_id}:${_ledgerStamp(options.consumer_root)}`;
@@ -3466,7 +3381,7 @@ function run4(stdin_text, options) {
3466
3381
  state.evaluations = [];
3467
3382
  }
3468
3383
  const [tool, prompt] = extractDispatch(envelope);
3469
- const decision = decide2(tool, prompt, state.evaluations.length);
3384
+ const decision = decide(tool, prompt, state.evaluations.length);
3470
3385
  if (decision.evaluations > state.evaluations.length) {
3471
3386
  state.evaluations.push({
3472
3387
  at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -3475,7 +3390,11 @@ function run4(stdin_text, options) {
3475
3390
  });
3476
3391
  try {
3477
3392
  atomic_write_json(target, state);
3478
- } catch {
3393
+ } catch (err) {
3394
+ process.stderr.write(
3395
+ `evidence-independence: evaluation-count write failed (${_errText3(err)}) \u2014 this turn's count did NOT advance, so a second self-scoped evaluation will not be warned about. The pre-loaded-verdict block is unaffected.
3396
+ `
3397
+ );
3479
3398
  }
3480
3399
  }
3481
3400
  if (decision.stdout) {
@@ -3486,7 +3405,11 @@ function run4(stdin_text, options) {
3486
3405
  }
3487
3406
  return decision.exit;
3488
3407
  }
3489
- function main7(argv) {
3408
+ function _errText3(err) {
3409
+ const t = err instanceof Error ? err.message : String(err);
3410
+ return t.replace(/\s+/g, " ").trim().slice(0, 200);
3411
+ }
3412
+ function main6(argv) {
3490
3413
  const args = argv ?? process.argv.slice(2);
3491
3414
  let consumer_root = process.cwd();
3492
3415
  for (let i = 0; i < args.length; i += 1) {
@@ -3498,35 +3421,35 @@ function main7(argv) {
3498
3421
  consumer_root = a.slice("--project-dir=".length);
3499
3422
  }
3500
3423
  }
3501
- return run4(readHookStdin(), { consumer_root });
3424
+ return run3(readHookStdin(), { consumer_root });
3502
3425
  }
3503
- function _isCliEntry7() {
3426
+ function _isCliEntry6() {
3504
3427
  if (true) {
3505
3428
  return false;
3506
3429
  }
3507
3430
  if (process.argv[1] === void 0) {
3508
3431
  return false;
3509
3432
  }
3510
- const argvUrl = pathToFileURL7(path7.resolve(process.argv[1])).href;
3433
+ const argvUrl = pathToFileURL6(path6.resolve(process.argv[1])).href;
3511
3434
  if (import.meta.url === argvUrl) {
3512
3435
  return true;
3513
3436
  }
3514
3437
  try {
3515
- const here = fs7.realpathSync(fileURLToPath7(import.meta.url));
3516
- const argv = fs7.realpathSync(path7.resolve(process.argv[1]));
3438
+ const here = fs6.realpathSync(fileURLToPath6(import.meta.url));
3439
+ const argv = fs6.realpathSync(path6.resolve(process.argv[1]));
3517
3440
  return here === argv;
3518
3441
  } catch {
3519
3442
  return false;
3520
3443
  }
3521
3444
  }
3522
- if (_isCliEntry7()) {
3523
- process.exit(main7());
3445
+ if (_isCliEntry6()) {
3446
+ process.exit(main6());
3524
3447
  }
3525
3448
 
3526
3449
  // src/scripts/hooks/delegation_nudge_hook.ts
3527
- import fs8 from "node:fs";
3528
- import path8 from "node:path";
3529
- import { fileURLToPath as fileURLToPath8, pathToFileURL as pathToFileURL8 } from "node:url";
3450
+ import fs7 from "node:fs";
3451
+ import path7 from "node:path";
3452
+ import { fileURLToPath as fileURLToPath7, pathToFileURL as pathToFileURL7 } from "node:url";
3530
3453
 
3531
3454
  // src/scripts/_lib/host_capability.ts
3532
3455
  import process2 from "node:process";
@@ -3855,9 +3778,9 @@ function resolveSubagentRouting(inp) {
3855
3778
  }
3856
3779
 
3857
3780
  // src/scripts/hooks/delegation_nudge_hook.ts
3858
- var EXIT_ALLOW7 = 0;
3781
+ var EXIT_ALLOW6 = 0;
3859
3782
  var EXIT_WARN3 = 2;
3860
- function _isObject7(v) {
3783
+ function _isObject6(v) {
3861
3784
  return typeof v === "object" && v !== null && !Array.isArray(v);
3862
3785
  }
3863
3786
  var FILE_TOKEN_RE = /`?\b[\w][\w.\-]{0,80}\.(ts|tsx|js|jsx|mjs|cjs|py|php|rb|go|rs|java|kt|swift|md|mdx|json|ya?ml|css|scss|less|html|vue|svelte|sql|sh|bash|c|cpp|h|hpp|cs|toml|xml)\b`?/gi;
@@ -4016,34 +3939,34 @@ function _workspaceRoot(envelope) {
4016
3939
  }
4017
3940
  return process.cwd();
4018
3941
  }
4019
- function main8() {
3942
+ function main7() {
4020
3943
  let envelope = {};
4021
3944
  try {
4022
3945
  const raw = readHookStdin();
4023
3946
  envelope = raw.trim() ? JSON.parse(raw) : {};
4024
3947
  } catch {
4025
- return EXIT_ALLOW7;
3948
+ return EXIT_ALLOW6;
4026
3949
  }
4027
- const env = _isObject7(envelope) ? envelope : {};
3950
+ const env = _isObject6(envelope) ? envelope : {};
4028
3951
  const event = env["event"];
4029
3952
  const slot = typeof event === "string" && event !== "" ? event : "user_prompt_submit";
4030
3953
  if (slot !== "user_prompt_submit") {
4031
- return EXIT_ALLOW7;
3954
+ return EXIT_ALLOW6;
4032
3955
  }
4033
- const payload = _isObject7(env["payload"]) ? env["payload"] : env;
3956
+ const payload = _isObject6(env["payload"]) ? env["payload"] : env;
4034
3957
  const prompt = _extractPrompt2(payload);
4035
3958
  if (!prompt) {
4036
- return EXIT_ALLOW7;
3959
+ return EXIT_ALLOW6;
4037
3960
  }
4038
3961
  if (isSyntheticPrompt(prompt)) {
4039
- return EXIT_ALLOW7;
3962
+ return EXIT_ALLOW6;
4040
3963
  }
4041
3964
  const root = _workspaceRoot(env);
4042
3965
  const platform = env["platform"];
4043
3966
  const hostId = typeof platform === "string" && platform ? platform : null;
4044
3967
  const result = classifyPrompt(prompt, root, hostId);
4045
3968
  if (!result) {
4046
- return EXIT_ALLOW7;
3969
+ return EXIT_ALLOW6;
4047
3970
  }
4048
3971
  process.stdout.write(
4049
3972
  `${JSON.stringify({
@@ -4055,31 +3978,31 @@ function main8() {
4055
3978
  );
4056
3979
  return EXIT_WARN3;
4057
3980
  }
4058
- function _isCliEntry8() {
3981
+ function _isCliEntry7() {
4059
3982
  if (true) {
4060
3983
  return false;
4061
3984
  }
4062
3985
  if (process.argv[1] === void 0) {
4063
3986
  return false;
4064
3987
  }
4065
- const argvUrl = pathToFileURL8(path8.resolve(process.argv[1])).href;
3988
+ const argvUrl = pathToFileURL7(path7.resolve(process.argv[1])).href;
4066
3989
  if (import.meta.url === argvUrl) {
4067
3990
  return true;
4068
3991
  }
4069
3992
  try {
4070
- const here = fs8.realpathSync(fileURLToPath8(import.meta.url));
4071
- const argv = fs8.realpathSync(path8.resolve(process.argv[1]));
3993
+ const here = fs7.realpathSync(fileURLToPath7(import.meta.url));
3994
+ const argv = fs7.realpathSync(path7.resolve(process.argv[1]));
4072
3995
  return here === argv;
4073
3996
  } catch {
4074
3997
  return false;
4075
3998
  }
4076
3999
  }
4077
- if (_isCliEntry8()) {
4078
- process.exit(main8());
4000
+ if (_isCliEntry7()) {
4001
+ process.exit(main7());
4079
4002
  }
4080
4003
 
4081
4004
  // src/scripts/conformance_scan.ts
4082
- var REPO_ROOT = path9.resolve(path9.dirname(fileURLToPath9(import.meta.url)), "..", "..");
4005
+ var REPO_ROOT = path8.resolve(path8.dirname(fileURLToPath8(import.meta.url)), "..", "..");
4083
4006
  var OBSERVED_BAND = { low: 9.1, high: 39.2 };
4084
4007
  var BAND_MIN_TURNS = 1978;
4085
4008
  var CARRIER_CHANGES = [
@@ -4163,7 +4086,7 @@ function renderWhy(report, id) {
4163
4086
  }
4164
4087
  return lines.join("\n");
4165
4088
  }
4166
- function _isObject8(v) {
4089
+ function _isObject7(v) {
4167
4090
  return typeof v === "object" && v !== null && !Array.isArray(v);
4168
4091
  }
4169
4092
  function userText(entry) {
@@ -4187,15 +4110,15 @@ function assistantText(entry) {
4187
4110
  }
4188
4111
  function toolUses2(entry) {
4189
4112
  const msg = entry["message"];
4190
- const content = _isObject8(msg) ? msg["content"] : void 0;
4113
+ const content = _isObject7(msg) ? msg["content"] : void 0;
4191
4114
  if (!Array.isArray(content)) {
4192
4115
  return [];
4193
4116
  }
4194
- return content.filter((b) => _isObject8(b) && b["type"] === "tool_use").map((b) => {
4117
+ return content.filter((b) => _isObject7(b) && b["type"] === "tool_use").map((b) => {
4195
4118
  const o = b;
4196
4119
  return {
4197
4120
  name: String(o["name"] ?? ""),
4198
- input: _isObject8(o["input"]) ? o["input"] : {}
4121
+ input: _isObject7(o["input"]) ? o["input"] : {}
4199
4122
  };
4200
4123
  });
4201
4124
  }
@@ -4238,6 +4161,7 @@ function scanSession(sessionId, lines) {
4238
4161
  let pinned = null;
4239
4162
  let pendingPoll = null;
4240
4163
  let authorized = /* @__PURE__ */ new Set();
4164
+ let authorizedObjects = /* @__PURE__ */ new Map();
4241
4165
  let sawPending = false;
4242
4166
  let evaluationsThisTurn = 0;
4243
4167
  let turnsSincePrompt = 0;
@@ -4331,7 +4255,11 @@ function scanSession(sessionId, lines) {
4331
4255
  if (c.language !== "und") {
4332
4256
  pinned = c.language;
4333
4257
  }
4334
- authorized = new Set(classifyAuthorization(ut).authorized);
4258
+ const classified = classifyAuthorization(ut);
4259
+ authorized = new Set(classified.authorized);
4260
+ authorizedObjects = new Map(
4261
+ classified.authorized.map((op) => [op, new Set(extractObjects(op, ut))])
4262
+ );
4335
4263
  sawPending = false;
4336
4264
  evaluationsThisTurn = 0;
4337
4265
  pendingPoll = null;
@@ -4397,6 +4325,17 @@ function scanSession(sessionId, lines) {
4397
4325
  at,
4398
4326
  detail: `irreversible \`${op}\` with no authorization in the turn's prompt: ${cmd.slice(0, 110)}`
4399
4327
  });
4328
+ } else if (op !== null && authorized.has(op) && OBJECT_REQUIRED_OPS.has(op)) {
4329
+ const object = commandObject(op, cmd);
4330
+ const named = authorizedObjects.get(op);
4331
+ if (object !== null && !(named?.has(object) ?? false)) {
4332
+ report.violations.push({
4333
+ check: "git-authorization",
4334
+ session: sessionId,
4335
+ at,
4336
+ detail: `\`${op}\` authorized, but its object \`${object}\` is not one the turn's prompt named (${named && named.size > 0 ? [...named].join(", ") : "no object named"}): ` + cmd.slice(0, 110)
4337
+ });
4338
+ }
4400
4339
  }
4401
4340
  if (isCiPoll(cmd)) {
4402
4341
  pendingPoll = { at, cmd };
@@ -4429,7 +4368,7 @@ function scanSession(sessionId, lines) {
4429
4368
  closeWindow();
4430
4369
  return report;
4431
4370
  }
4432
- var DEFAULT_RATE_SERIES = path9.join(
4371
+ var DEFAULT_RATE_SERIES = path8.join(
4433
4372
  REPO_ROOT,
4434
4373
  "agents",
4435
4374
  "runtime",
@@ -4438,7 +4377,7 @@ var DEFAULT_RATE_SERIES = path9.join(
4438
4377
  );
4439
4378
  function defaultStore(projectDir) {
4440
4379
  const slug = projectStoreSlug(projectDir);
4441
- return path9.join(process.env["HOME"] ?? "", ".claude", "projects", slug);
4380
+ return path8.join(process.env["HOME"] ?? "", ".claude", "projects", slug);
4442
4381
  }
4443
4382
  function _tokens(chars) {
4444
4383
  return Math.round(chars / 4);
@@ -4449,13 +4388,13 @@ function measureDelivered(projectRulesDir, globalRulesDir) {
4449
4388
  return {
4450
4389
  project: {
4451
4390
  dir: projectRulesDir,
4452
- present: fs9.existsSync(projectRulesDir),
4391
+ present: fs8.existsSync(projectRulesDir),
4453
4392
  files: p.files,
4454
4393
  tokens: _tokens(p.chars)
4455
4394
  },
4456
4395
  global: {
4457
4396
  dir: globalRulesDir,
4458
- present: fs9.existsSync(globalRulesDir),
4397
+ present: fs8.existsSync(globalRulesDir),
4459
4398
  files: g.files,
4460
4399
  tokens: _tokens(g.chars)
4461
4400
  },
@@ -4463,13 +4402,13 @@ function measureDelivered(projectRulesDir, globalRulesDir) {
4463
4402
  };
4464
4403
  }
4465
4404
  function storeKey(store) {
4466
- return createHash3("sha256").update(path9.resolve(store)).digest("hex").slice(0, 12);
4405
+ return createHash3("sha256").update(path8.resolve(store)).digest("hex").slice(0, 12);
4467
4406
  }
4468
4407
  function scanStore(store, limit) {
4469
- const files = fs9.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => ({ f, m: fs9.statSync(path9.join(store, f)).mtimeMs })).sort((a, b) => b.m - a.m).slice(0, limit).map((x) => x.f);
4408
+ const files = fs8.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => ({ f, m: fs8.statSync(path8.join(store, f)).mtimeMs })).sort((a, b) => b.m - a.m).slice(0, limit).map((x) => x.f);
4470
4409
  const per_session = [];
4471
4410
  for (const f of files) {
4472
- const lines = fs9.readFileSync(path9.join(store, f), "utf8").split("\n");
4411
+ const lines = fs8.readFileSync(path8.join(store, f), "utf8").split("\n");
4473
4412
  const r = scanSession(f.replace(/\.jsonl$/, "").slice(0, 8), lines);
4474
4413
  if (r.assistant_turns > 0) {
4475
4414
  per_session.push(r);
@@ -4497,10 +4436,10 @@ function scanStore(store, limit) {
4497
4436
  const task_completeness_rate_pct = completeness_windows === 0 ? 0 : Number((100 * task_completeness / completeness_windows).toFixed(1));
4498
4437
  const era = spansCarrierChange(per_session.map((s) => s.first_at));
4499
4438
  const delivered = measureDelivered(
4500
- path9.join(REPO_ROOT, ".claude", "rules"),
4501
- path9.join(process.env["HOME"] ?? os2.homedir(), ".claude", "rules")
4439
+ path8.join(REPO_ROOT, ".claude", "rules"),
4440
+ path8.join(process.env["HOME"] ?? os2.homedir(), ".claude", "rules")
4502
4441
  );
4503
- const ownStore = path9.resolve(store) === path9.resolve(defaultStore(REPO_ROOT));
4442
+ const ownStore = path8.resolve(store) === path8.resolve(defaultStore(REPO_ROOT));
4504
4443
  return {
4505
4444
  scanned_at: (/* @__PURE__ */ new Date()).toISOString(),
4506
4445
  store,
@@ -4530,8 +4469,8 @@ function scanStore(store, limit) {
4530
4469
  };
4531
4470
  }
4532
4471
  function recordRate(file, scanned_at, rate) {
4533
- fs9.mkdirSync(path9.dirname(file), { recursive: true });
4534
- fs9.appendFileSync(file, `${JSON.stringify({ measured_at: scanned_at, ...rate })}
4472
+ fs8.mkdirSync(path8.dirname(file), { recursive: true });
4473
+ fs8.appendFileSync(file, `${JSON.stringify({ measured_at: scanned_at, ...rate })}
4535
4474
  `, "utf8");
4536
4475
  }
4537
4476
  function render(report) {
@@ -4630,7 +4569,7 @@ function render(report) {
4630
4569
  lines.push(" checkbox batching \u2014 measured NOWHERE. ~5 low-severity findings.");
4631
4570
  return lines.join("\n");
4632
4571
  }
4633
- function main9(argv) {
4572
+ function main8(argv) {
4634
4573
  const args = argv ?? process.argv.slice(2);
4635
4574
  let store = null;
4636
4575
  let limit = 30;
@@ -4662,14 +4601,14 @@ function main9(argv) {
4662
4601
  return 2;
4663
4602
  }
4664
4603
  const resolved = store ?? defaultStore(process.cwd());
4665
- if (!fs9.existsSync(resolved)) {
4604
+ if (!fs8.existsSync(resolved)) {
4666
4605
  process.stderr.write(`conformance:behavior: no transcript store at ${resolved}
4667
4606
  `);
4668
4607
  return 1;
4669
4608
  }
4670
4609
  const report = scanStore(resolved, Number.isFinite(limit) && limit > 0 ? limit : 30);
4671
4610
  if (out) {
4672
- fs9.writeFileSync(out, `${JSON.stringify(report, null, 2)}
4611
+ fs8.writeFileSync(out, `${JSON.stringify(report, null, 2)}
4673
4612
  `, "utf8");
4674
4613
  }
4675
4614
  if (record) {
@@ -4677,7 +4616,7 @@ function main9(argv) {
4677
4616
  }
4678
4617
  if (why !== null) {
4679
4618
  const id = why;
4680
- fs9.writeSync(
4619
+ fs8.writeSync(
4681
4620
  1,
4682
4621
  json ? `${JSON.stringify(
4683
4622
  {
@@ -4697,48 +4636,48 @@ function main9(argv) {
4697
4636
  );
4698
4637
  return 0;
4699
4638
  }
4700
- fs9.writeSync(1, json ? `${JSON.stringify(report, null, 2)}
4639
+ fs8.writeSync(1, json ? `${JSON.stringify(report, null, 2)}
4701
4640
  ` : `${render(report)}
4702
4641
  `);
4703
4642
  return 0;
4704
4643
  }
4705
- function _isCliEntry9() {
4644
+ function _isCliEntry8() {
4706
4645
  if (true) {
4707
4646
  return false;
4708
4647
  }
4709
4648
  if (process.argv[1] === void 0) {
4710
4649
  return false;
4711
4650
  }
4712
- const argvUrl = pathToFileURL9(path9.resolve(process.argv[1])).href;
4651
+ const argvUrl = pathToFileURL8(path8.resolve(process.argv[1])).href;
4713
4652
  if (import.meta.url === argvUrl) {
4714
4653
  return true;
4715
4654
  }
4716
4655
  try {
4717
- const here = fs9.realpathSync(fileURLToPath9(import.meta.url));
4718
- const argv = fs9.realpathSync(path9.resolve(process.argv[1]));
4656
+ const here = fs8.realpathSync(fileURLToPath8(import.meta.url));
4657
+ const argv = fs8.realpathSync(path8.resolve(process.argv[1]));
4719
4658
  return here === argv;
4720
4659
  } catch {
4721
4660
  return false;
4722
4661
  }
4723
4662
  }
4724
- if (_isCliEntry9()) {
4725
- process.exit(main9());
4663
+ if (_isCliEntry8()) {
4664
+ process.exit(main8());
4726
4665
  }
4727
4666
 
4728
4667
  // src/scripts/report_carrier_divergence.ts
4729
- import * as fs10 from "node:fs";
4668
+ import * as fs9 from "node:fs";
4730
4669
  import * as os3 from "node:os";
4731
- import * as path10 from "node:path";
4670
+ import * as path9 from "node:path";
4732
4671
  import process3 from "node:process";
4733
- import { fileURLToPath as fileURLToPath10, pathToFileURL as pathToFileURL10 } from "node:url";
4734
- var _HERE = fileURLToPath10(import.meta.url);
4735
- var REPO_ROOT2 = path10.resolve(path10.dirname(_HERE), "..", "..");
4736
- var PROJECT_RULES = path10.join(".claude", "rules");
4737
- var PROJECTION_SOURCE = path10.join("dist", "agent-src", "rules");
4738
- var GLOBAL_RULES = path10.join(".claude", "rules");
4672
+ import { fileURLToPath as fileURLToPath9, pathToFileURL as pathToFileURL9 } from "node:url";
4673
+ var _HERE = fileURLToPath9(import.meta.url);
4674
+ var REPO_ROOT2 = path9.resolve(path9.dirname(_HERE), "..", "..");
4675
+ var PROJECT_RULES = path9.join(".claude", "rules");
4676
+ var PROJECTION_SOURCE = path9.join("dist", "agent-src", "rules");
4677
+ var GLOBAL_RULES = path9.join(".claude", "rules");
4739
4678
  function _listRules(dir) {
4740
4679
  try {
4741
- return fs10.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
4680
+ return fs9.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
4742
4681
  } catch {
4743
4682
  return [];
4744
4683
  }
@@ -4746,7 +4685,7 @@ function _listRules(dir) {
4746
4685
  function _ruleType(file) {
4747
4686
  let text;
4748
4687
  try {
4749
- text = fs10.readFileSync(file, "utf-8");
4688
+ text = fs9.readFileSync(file, "utf-8");
4750
4689
  } catch {
4751
4690
  return "";
4752
4691
  }
@@ -4757,8 +4696,8 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
4757
4696
  const out = {
4758
4697
  projectDir,
4759
4698
  globalDir,
4760
- globalPresent: fs10.existsSync(globalDir),
4761
- projectPresent: fs10.existsSync(projectDir),
4699
+ globalPresent: fs9.existsSync(globalDir),
4700
+ projectPresent: fs9.existsSync(projectDir),
4762
4701
  shared: 0,
4763
4702
  identical: [],
4764
4703
  provenanceOnly: [],
@@ -4781,7 +4720,7 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
4781
4720
  }
4782
4721
  if (!inP && inG) {
4783
4722
  out.globalOnly.push(rule);
4784
- if (sourceDir !== void 0 && _ruleType(path10.join(sourceDir, rule)) === "manual") {
4723
+ if (sourceDir !== void 0 && _ruleType(path9.join(sourceDir, rule)) === "manual") {
4785
4724
  out.manualOnlyGlobal.push(rule);
4786
4725
  }
4787
4726
  continue;
@@ -4791,8 +4730,8 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
4791
4730
  let projectCopy;
4792
4731
  let globalCopy;
4793
4732
  try {
4794
- projectCopy = fs10.readFileSync(path10.join(projectDir, rule));
4795
- globalCopy = fs10.readFileSync(path10.join(globalDir, rule));
4733
+ projectCopy = fs9.readFileSync(path9.join(projectDir, rule));
4734
+ globalCopy = fs9.readFileSync(path9.join(globalDir, rule));
4796
4735
  verdict = comparePair(projectCopy, globalCopy);
4797
4736
  } catch {
4798
4737
  out.shared -= 1;
@@ -4937,11 +4876,11 @@ function render2(d) {
4937
4876
  }
4938
4877
  return lines.join("\n");
4939
4878
  }
4940
- function main10(argv) {
4879
+ function main9(argv) {
4941
4880
  const args = argv ?? process3.argv.slice(2);
4942
- let projectDir = path10.join(REPO_ROOT2, PROJECT_RULES);
4943
- let globalDir = path10.join(os3.homedir(), GLOBAL_RULES);
4944
- let sourceDir = path10.join(REPO_ROOT2, PROJECTION_SOURCE);
4881
+ let projectDir = path9.join(REPO_ROOT2, PROJECT_RULES);
4882
+ let globalDir = path9.join(os3.homedir(), GLOBAL_RULES);
4883
+ let sourceDir = path9.join(REPO_ROOT2, PROJECTION_SOURCE);
4945
4884
  const value = (i) => {
4946
4885
  const v = args[i + 1];
4947
4886
  return v === void 0 || v.startsWith("-") ? null : v;
@@ -4955,7 +4894,7 @@ function main10(argv) {
4955
4894
  `);
4956
4895
  return 1;
4957
4896
  }
4958
- const resolved = path10.resolve(v);
4897
+ const resolved = path9.resolve(v);
4959
4898
  if (a === "--project") projectDir = resolved;
4960
4899
  else if (a === "--global") globalDir = resolved;
4961
4900
  else sourceDir = resolved;
@@ -4975,49 +4914,57 @@ function main10(argv) {
4975
4914
  `);
4976
4915
  return 0;
4977
4916
  }
4978
- function _isCliEntry10() {
4917
+ function _isCliEntry9() {
4979
4918
  if (process3.argv[1] === void 0) return false;
4980
- if (import.meta.url === pathToFileURL10(path10.resolve(process3.argv[1])).href) return true;
4919
+ if (import.meta.url === pathToFileURL9(path9.resolve(process3.argv[1])).href) return true;
4981
4920
  try {
4982
- return fs10.realpathSync(_HERE) === fs10.realpathSync(path10.resolve(process3.argv[1]));
4921
+ return fs9.realpathSync(_HERE) === fs9.realpathSync(path9.resolve(process3.argv[1]));
4983
4922
  } catch {
4984
4923
  return false;
4985
4924
  }
4986
4925
  }
4987
- if (_isCliEntry10()) {
4988
- process3.exit(main10());
4926
+ if (_isCliEntry9()) {
4927
+ process3.exit(main9());
4989
4928
  }
4990
4929
 
4991
4930
  // src/scripts/report_skill_activation.ts
4992
- import * as fs11 from "node:fs";
4931
+ import * as fs10 from "node:fs";
4993
4932
  import * as os4 from "node:os";
4994
- import * as path11 from "node:path";
4995
- import { fileURLToPath as fileURLToPath11, pathToFileURL as pathToFileURL11 } from "node:url";
4996
- var _HERE2 = fileURLToPath11(import.meta.url);
4997
- var REPO_ROOT3 = path11.resolve(path11.dirname(_HERE2), "..", "..");
4933
+ import * as path10 from "node:path";
4934
+ import { fileURLToPath as fileURLToPath10, pathToFileURL as pathToFileURL10 } from "node:url";
4935
+ var _HERE2 = fileURLToPath10(import.meta.url);
4936
+ var REPO_ROOT3 = path10.resolve(path10.dirname(_HERE2), "..", "..");
4998
4937
  var SKILLS_ROOT = "src/skills";
4999
4938
  var TRIGGER_KEYS = ["triggers:", "trigger_description:", "file_pattern:", "path_prefix:"];
5000
4939
  var DETERMINISTIC_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?(MUST|NEVER|ALWAYS)\b/m;
5001
4940
  function censusSkills(root) {
5002
- const out = { total: 0, withTriggerKey: [], withDeterministicObligation: [] };
5003
- if (!fs11.existsSync(root)) {
4941
+ const out = {
4942
+ total: 0,
4943
+ withTriggerKey: [],
4944
+ withTriggerCorpus: [],
4945
+ withDeterministicObligation: []
4946
+ };
4947
+ if (!fs10.existsSync(root)) {
5004
4948
  return out;
5005
4949
  }
5006
- for (const entry of fs11.readdirSync(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
4950
+ for (const entry of fs10.readdirSync(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
5007
4951
  if (!entry.isDirectory()) {
5008
4952
  continue;
5009
4953
  }
5010
- const file = path11.join(root, entry.name, "SKILL.md");
5011
- if (!fs11.existsSync(file)) {
4954
+ const file = path10.join(root, entry.name, "SKILL.md");
4955
+ if (!fs10.existsSync(file)) {
5012
4956
  continue;
5013
4957
  }
5014
4958
  out.total += 1;
5015
- const text = fs11.readFileSync(file, "utf8");
4959
+ const text = fs10.readFileSync(file, "utf8");
5016
4960
  const fmEnd = text.startsWith("---") ? text.indexOf("\n---", 3) : -1;
5017
4961
  const fm = fmEnd === -1 ? "" : text.slice(0, fmEnd);
5018
4962
  if (TRIGGER_KEYS.some((k) => new RegExp(`^${k}`, "m").test(fm))) {
5019
4963
  out.withTriggerKey.push(entry.name);
5020
4964
  }
4965
+ if (fs10.existsSync(path10.join(root, entry.name, "evals", "triggers.json"))) {
4966
+ out.withTriggerCorpus.push(entry.name);
4967
+ }
5021
4968
  const body = fmEnd === -1 ? text : text.slice(fmEnd + 4);
5022
4969
  if (DETERMINISTIC_RE.test(body)) {
5023
4970
  out.withDeterministicObligation.push(entry.name);
@@ -5026,17 +4973,17 @@ function censusSkills(root) {
5026
4973
  return out;
5027
4974
  }
5028
4975
  function defaultStore2(cwd) {
5029
- return path11.join(os4.homedir(), ".claude", "projects", projectStoreSlug(cwd));
4976
+ return path10.join(os4.homedir(), ".claude", "projects", projectStoreSlug(cwd));
5030
4977
  }
5031
4978
  function measureUsage(store, limit) {
5032
4979
  const rep = { store, sessions: 0, assistantTurns: 0, invocations: 0, bySkill: {} };
5033
- if (!fs11.existsSync(store)) {
4980
+ if (!fs10.existsSync(store)) {
5034
4981
  return rep;
5035
4982
  }
5036
- const files = fs11.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => path11.join(store, f)).sort((a, b) => fs11.statSync(b).mtimeMs - fs11.statSync(a).mtimeMs).slice(0, limit);
4983
+ const files = fs10.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => path10.join(store, f)).sort((a, b) => fs10.statSync(b).mtimeMs - fs10.statSync(a).mtimeMs).slice(0, limit);
5037
4984
  rep.sessions = files.length;
5038
4985
  for (const file of files) {
5039
- for (const line of fs11.readFileSync(file, "utf8").split("\n")) {
4986
+ for (const line of fs10.readFileSync(file, "utf8").split("\n")) {
5040
4987
  if (!line.trim()) {
5041
4988
  continue;
5042
4989
  }
@@ -5076,7 +5023,50 @@ function measureUsage(store, limit) {
5076
5023
  function _pct(n, d) {
5077
5024
  return d === 0 ? "n/a" : `${(100 * n / d).toFixed(1)}%`;
5078
5025
  }
5079
- function render3(census, usage) {
5026
+ var RECORD_PATH = "agents/evidence/metrics/skill-activation-census.json";
5027
+ function populations(census) {
5028
+ const corpus = new Set(census.withTriggerCorpus);
5029
+ const both = census.withTriggerKey.filter((s) => corpus.has(s));
5030
+ const eitherOne = /* @__PURE__ */ new Set([...census.withTriggerKey, ...census.withTriggerCorpus]);
5031
+ return {
5032
+ shipped: census.total,
5033
+ triggerKey: census.withTriggerKey.length,
5034
+ triggerCorpus: census.withTriggerCorpus.length,
5035
+ bothKeyAndCorpus: both.length,
5036
+ humanNamedOnly: census.total - eitherOne.size
5037
+ };
5038
+ }
5039
+ function buildRecord(census, usage, today) {
5040
+ const pop = populations(census);
5041
+ const distinct = /* @__PURE__ */ new Set();
5042
+ for (const u of usage) {
5043
+ for (const k of Object.keys(u.bySkill)) {
5044
+ distinct.add(k);
5045
+ }
5046
+ }
5047
+ return {
5048
+ schema_version: 1,
5049
+ measured_at: today,
5050
+ skills_shipped: pop.shipped,
5051
+ with_trigger_key: pop.triggerKey,
5052
+ with_trigger_corpus: pop.triggerCorpus,
5053
+ trigger_key_and_corpus: pop.bothKeyAndCorpus,
5054
+ human_named_only: pop.humanNamedOnly,
5055
+ sessions: usage.reduce((a, u) => a + u.sessions, 0),
5056
+ assistant_turns: usage.reduce((a, u) => a + u.assistantTurns, 0),
5057
+ invocations: usage.reduce((a, u) => a + u.invocations, 0),
5058
+ distinct_skills_invoked: distinct.size,
5059
+ stores: usage.map((u) => path10.basename(u.store))
5060
+ };
5061
+ }
5062
+ function readRecord(repoRoot) {
5063
+ const file = path10.join(repoRoot, RECORD_PATH);
5064
+ if (!fs10.existsSync(file)) {
5065
+ return null;
5066
+ }
5067
+ return JSON.parse(fs10.readFileSync(file, "utf8"));
5068
+ }
5069
+ function render3(census, usage, published = null) {
5080
5070
  const lines = [];
5081
5071
  lines.push("skill activation census \u2014 advisory, gates on nothing");
5082
5072
  lines.push("");
@@ -5091,6 +5081,16 @@ function render3(census, usage) {
5091
5081
  lines.push(` ${census.withDeterministicObligation.join(", ")}`);
5092
5082
  }
5093
5083
  lines.push("");
5084
+ const pop = populations(census);
5085
+ lines.push(" populations \u2014 the three ways a skill can be reached, reconciled");
5086
+ lines.push(` machine-matchable trigger key ${pop.triggerKey}`);
5087
+ lines.push(` evals/triggers.json corpus ${pop.triggerCorpus} (a test fixture; no host reads it at routing time)`);
5088
+ lines.push(` both of the above ${pop.bothKeyAndCorpus}`);
5089
+ lines.push(` human-named only (remainder) ${pop.humanNamedOnly}`);
5090
+ lines.push(
5091
+ ` reconciles ${pop.triggerKey} + ${pop.triggerCorpus} - ${pop.bothKeyAndCorpus} + ${pop.humanNamedOnly} = ${String(pop.triggerKey + pop.triggerCorpus - pop.bothKeyAndCorpus + pop.humanNamedOnly)} of ${pop.shipped}`
5092
+ );
5093
+ lines.push("");
5094
5094
  let totalInv = 0;
5095
5095
  const distinct = /* @__PURE__ */ new Set();
5096
5096
  for (const u of usage) {
@@ -5099,7 +5099,7 @@ function render3(census, usage) {
5099
5099
  }
5100
5100
  totalInv += u.invocations;
5101
5101
  lines.push(
5102
- ` ${path11.basename(u.store).slice(-38).padEnd(38)} sessions=${String(u.sessions).padStart(3)} asst=${String(u.assistantTurns).padStart(6)} Skill calls=${String(u.invocations).padStart(3)}`
5102
+ ` ${path10.basename(u.store).slice(-38).padEnd(38)} sessions=${String(u.sessions).padStart(3)} asst=${String(u.assistantTurns).padStart(6)} Skill calls=${String(u.invocations).padStart(3)}`
5103
5103
  );
5104
5104
  }
5105
5105
  lines.push("");
@@ -5111,6 +5111,51 @@ function render3(census, usage) {
5111
5111
  lines.push(` ${[...distinct].sort().join(", ")}`);
5112
5112
  }
5113
5113
  lines.push("");
5114
+ const sessionsRead = usage.reduce((a, u) => a + u.sessions, 0);
5115
+ if (sessionsRead === 0) {
5116
+ lines.push(' EMPTY STORE: 0 sessions were read, so the zero above is "no transcripts at');
5117
+ lines.push(' this path", NOT "no activations". It is not a measurement of the surface and');
5118
+ lines.push(" must not be published as one. Run this where the store lives, or pass --store.");
5119
+ lines.push("");
5120
+ }
5121
+ if (published !== null) {
5122
+ const live = buildRecord(census, usage, published.measured_at);
5123
+ const REPO_FIELDS = [
5124
+ "skills_shipped",
5125
+ "with_trigger_key",
5126
+ "with_trigger_corpus",
5127
+ "trigger_key_and_corpus",
5128
+ "human_named_only"
5129
+ ];
5130
+ const STORE_FIELDS = [
5131
+ "sessions",
5132
+ "assistant_turns",
5133
+ "invocations",
5134
+ "distinct_skills_invoked"
5135
+ ];
5136
+ const repoDrift = REPO_FIELDS.filter((k) => live[k] !== published[k]);
5137
+ const storeDrift = STORE_FIELDS.filter((k) => live[k] !== published[k]);
5138
+ lines.push(` PUBLISHED RECORD (${RECORD_PATH}), measured ${published.measured_at}:`);
5139
+ lines.push(
5140
+ ` sessions=${String(published.sessions)} asst=${String(published.assistant_turns)} invocations=${String(published.invocations)} distinct=${String(published.distinct_skills_invoked)}`
5141
+ );
5142
+ if (repoDrift.length > 0) {
5143
+ lines.push(` STALE: the published population split no longer describes this tree \u2014`);
5144
+ lines.push(` ${repoDrift.join(", ")} moved. Re-take with --emit.`);
5145
+ } else {
5146
+ lines.push(" population split still describes this tree.");
5147
+ }
5148
+ if (sessionsRead === 0) {
5149
+ lines.push(" Store reading: UNDECIDED \u2014 this run read no store, so it cannot say whether");
5150
+ lines.push(" the published figures still hold. Re-run where the store lives.");
5151
+ } else if (storeDrift.length === 0) {
5152
+ lines.push(" Store reading: reproduces the published figures exactly.");
5153
+ } else {
5154
+ lines.push(` Store reading: moved since the record (${storeDrift.join(", ")}). Expected \u2014`);
5155
+ lines.push(" the store is rolling, not append-only, and a later reading can be lower.");
5156
+ }
5157
+ lines.push("");
5158
+ }
5114
5159
  if (census.withTriggerKey.length === 0) {
5115
5160
  lines.push(" VERDICT: activation is not measurable as a rate. No skill declares a");
5116
5161
  lines.push(" machine-matchable trigger, so a missed-activation detector has nothing to");
@@ -5124,19 +5169,30 @@ function render3(census, usage) {
5124
5169
  lines.push(" catalogue once per session, then count descriptions against bare names.");
5125
5170
  return lines.join("\n");
5126
5171
  }
5127
- function main11(argv) {
5172
+ var USAGE = "usage: report_skill_activation [--root DIR] [--limit N] [--store PATH]... [--emit] [--allow-empty-store]\n";
5173
+ function main10(argv) {
5128
5174
  let limit = 30;
5175
+ let emit = false;
5176
+ let allowEmpty = false;
5177
+ const rootIdx = argv.indexOf("--root");
5178
+ const root = rootIdx === -1 ? REPO_ROOT3 : argv[rootIdx + 1] ?? REPO_ROOT3;
5129
5179
  const stores = [];
5130
5180
  for (let i = 0; i < argv.length; i += 1) {
5131
5181
  const a = argv[i];
5132
- if (a === "--limit" && argv[i + 1] !== void 0) {
5182
+ if (a === "--root" && argv[i + 1] !== void 0) {
5183
+ i += 1;
5184
+ } else if (a === "--limit" && argv[i + 1] !== void 0) {
5133
5185
  limit = Number.parseInt(argv[i + 1], 10);
5134
5186
  i += 1;
5135
5187
  } else if (a === "--store" && argv[i + 1] !== void 0) {
5136
5188
  stores.push(argv[i + 1]);
5137
5189
  i += 1;
5190
+ } else if (a === "--emit") {
5191
+ emit = true;
5192
+ } else if (a === "--allow-empty-store") {
5193
+ allowEmpty = true;
5138
5194
  } else if (a === "--help" || a === "-h") {
5139
- process.stdout.write("usage: report_skill_activation [--limit N] [--store PATH]...\n");
5195
+ process.stdout.write(USAGE);
5140
5196
  return 0;
5141
5197
  }
5142
5198
  }
@@ -5147,24 +5203,48 @@ function main11(argv) {
5147
5203
  if (stores.length === 0) {
5148
5204
  stores.push(defaultStore2(REPO_ROOT3));
5149
5205
  }
5150
- const census = censusSkills(path11.join(REPO_ROOT3, SKILLS_ROOT));
5206
+ const census = censusSkills(path10.join(root, SKILLS_ROOT));
5151
5207
  const usage = stores.map((s) => measureUsage(s, limit));
5152
- process.stdout.write(`${render3(census, usage)}
5208
+ process.stdout.write(`${render3(census, usage, readRecord(root))}
5209
+ `);
5210
+ reportScanned({
5211
+ gate: "report_skill_activation",
5212
+ scanned: census.total,
5213
+ units: "SKILL.md file(s)",
5214
+ roots: [SKILLS_ROOT]
5215
+ });
5216
+ if (!emit) {
5217
+ return 0;
5218
+ }
5219
+ const sessionsRead = usage.reduce((a, u) => a + u.sessions, 0);
5220
+ if (sessionsRead === 0 && !allowEmpty) {
5221
+ process.stderr.write(
5222
+ 'report_skill_activation --emit: refusing to write a record from 0 sessions \u2014 that zero\nis "no transcripts at this path", not an activation reading. Run where the store lives,\npass --store, or pass --allow-empty-store if an empty store is genuinely the finding.\n'
5223
+ );
5224
+ return 1;
5225
+ }
5226
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5227
+ const file = path10.join(root, RECORD_PATH);
5228
+ fs10.mkdirSync(path10.dirname(file), { recursive: true });
5229
+ fs10.writeFileSync(file, `${JSON.stringify(buildRecord(census, usage, today), null, 2)}
5230
+ `);
5231
+ process.stdout.write(`
5232
+ wrote ${RECORD_PATH} (measured ${today})
5153
5233
  `);
5154
5234
  return 0;
5155
5235
  }
5156
- if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL11(path11.resolve(process.argv[1])).href) {
5157
- process.exit(main11(process.argv.slice(2)));
5236
+ if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL10(path10.resolve(process.argv[1])).href) {
5237
+ process.exit(main10(process.argv.slice(2)));
5158
5238
  }
5159
5239
 
5160
5240
  // src/scripts/report_skill_obligation_violations.ts
5161
- import * as fs12 from "node:fs";
5241
+ import * as fs11 from "node:fs";
5162
5242
  import * as os5 from "node:os";
5163
- import * as path12 from "node:path";
5243
+ import * as path11 from "node:path";
5164
5244
  import process4 from "node:process";
5165
- import { fileURLToPath as fileURLToPath12, pathToFileURL as pathToFileURL12 } from "node:url";
5166
- var _HERE3 = fileURLToPath12(import.meta.url);
5167
- var REPO_ROOT4 = path12.resolve(path12.dirname(_HERE3), "..", "..");
5245
+ import { fileURLToPath as fileURLToPath11, pathToFileURL as pathToFileURL11 } from "node:url";
5246
+ var _HERE3 = fileURLToPath11(import.meta.url);
5247
+ var REPO_ROOT4 = path11.resolve(path11.dirname(_HERE3), "..", "..");
5168
5248
  var OBLIGATION_LINE_RE = new RegExp(`${DETERMINISTIC_RE.source}.*`, "gm");
5169
5249
  var PATH_RE = /`([A-Za-z0-9_./-]*\/[A-Za-z0-9_.-]+\.[a-z]{2,6})`/g;
5170
5250
  var CMD_RE = /`([a-z][a-z0-9-]*(?: [a-z][a-z0-9:._-]*){1,3})`/g;
@@ -5202,7 +5282,7 @@ function _artefacts(line) {
5202
5282
  return out;
5203
5283
  }
5204
5284
  function extractObligations(repoRoot) {
5205
- const skillsRoot = path12.join(repoRoot, SKILLS_ROOT);
5285
+ const skillsRoot = path11.join(repoRoot, SKILLS_ROOT);
5206
5286
  const census = censusSkills(skillsRoot);
5207
5287
  const out = {
5208
5288
  skills: census.withDeterministicObligation,
@@ -5216,7 +5296,7 @@ function extractObligations(repoRoot) {
5216
5296
  for (const skill of census.withDeterministicObligation) {
5217
5297
  let text;
5218
5298
  try {
5219
- text = fs12.readFileSync(path12.join(skillsRoot, skill, "SKILL.md"), "utf-8");
5299
+ text = fs11.readFileSync(path11.join(skillsRoot, skill, "SKILL.md"), "utf-8");
5220
5300
  } catch {
5221
5301
  continue;
5222
5302
  }
@@ -5332,7 +5412,7 @@ function scanSessionForViolations(session, lines, forbidden, parsed) {
5332
5412
  return flags;
5333
5413
  }
5334
5414
  function defaultStore3(cwd) {
5335
- return path12.join(os5.homedir(), ".claude", "projects", projectStoreSlug(cwd));
5415
+ return path11.join(os5.homedir(), ".claude", "projects", projectStoreSlug(cwd));
5336
5416
  }
5337
5417
  function scanStore2(repoRoot, store, limit) {
5338
5418
  const census = extractObligations(repoRoot);
@@ -5345,14 +5425,14 @@ function scanStore2(repoRoot, store, limit) {
5345
5425
  };
5346
5426
  let files;
5347
5427
  try {
5348
- files = fs12.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => ({ p: path12.join(store, f), m: fs12.statSync(path12.join(store, f)).mtimeMs })).sort((a, b) => b.m - a.m).slice(0, limit).map((x) => x.p);
5428
+ files = fs11.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => ({ p: path11.join(store, f), m: fs11.statSync(path11.join(store, f)).mtimeMs })).sort((a, b) => b.m - a.m).slice(0, limit).map((x) => x.p);
5349
5429
  } catch {
5350
5430
  return report;
5351
5431
  }
5352
5432
  report.sessions = files.length;
5353
5433
  for (const file of files) {
5354
- const lines = fs12.readFileSync(file, "utf-8").split("\n");
5355
- const id = path12.basename(file).replace(/\.jsonl$/, "").slice(0, 8);
5434
+ const lines = fs11.readFileSync(file, "utf-8").split("\n");
5435
+ const id = path11.basename(file).replace(/\.jsonl$/, "").slice(0, 8);
5356
5436
  const entries = _parseSession(lines);
5357
5437
  if (loadedSkillsFrom(entries).size > 0) report.sessionsWithASkill += 1;
5358
5438
  report.flags.push(...scanSessionForViolations(id, lines, census.forbidden, entries));
@@ -5422,7 +5502,7 @@ function render4(r) {
5422
5502
  lines.push(" ships as detection-only, and this one says so.");
5423
5503
  return lines.join("\n");
5424
5504
  }
5425
- function main12(argv) {
5505
+ function main11(argv) {
5426
5506
  const args = argv ?? process4.argv.slice(2);
5427
5507
  let limit = 30;
5428
5508
  let json = false;
@@ -5475,35 +5555,35 @@ function main12(argv) {
5475
5555
  }
5476
5556
  return 0;
5477
5557
  }
5478
- function _isCliEntry11() {
5558
+ function _isCliEntry10() {
5479
5559
  if (process4.argv[1] === void 0) return false;
5480
- if (import.meta.url === pathToFileURL12(path12.resolve(process4.argv[1])).href) return true;
5560
+ if (import.meta.url === pathToFileURL11(path11.resolve(process4.argv[1])).href) return true;
5481
5561
  try {
5482
- return fs12.realpathSync(_HERE3) === fs12.realpathSync(path12.resolve(process4.argv[1]));
5562
+ return fs11.realpathSync(_HERE3) === fs11.realpathSync(path11.resolve(process4.argv[1]));
5483
5563
  } catch {
5484
5564
  return false;
5485
5565
  }
5486
5566
  }
5487
- if (_isCliEntry11()) {
5488
- process4.exit(main12());
5567
+ if (_isCliEntry10()) {
5568
+ process4.exit(main11());
5489
5569
  }
5490
5570
 
5491
5571
  // src/scripts/report_conformance_funnel.ts
5492
- var _HERE4 = fileURLToPath13(import.meta.url);
5493
- var REPO_ROOT5 = path13.resolve(path13.dirname(_HERE4), "..", "..");
5572
+ var _HERE4 = fileURLToPath12(import.meta.url);
5573
+ var REPO_ROOT5 = path12.resolve(path12.dirname(_HERE4), "..", "..");
5494
5574
  function buildFunnel(opts = {}) {
5495
5575
  const repoRoot = opts.repoRoot ?? REPO_ROOT5;
5496
- const projectRulesDir = opts.projectRulesDir ?? path13.join(repoRoot, PROJECT_RULES);
5497
- const globalRulesDir = opts.globalRulesDir ?? path13.join(os6.homedir(), GLOBAL_RULES);
5498
- const sourceDir = opts.sourceDir ?? path13.join(repoRoot, PROJECTION_SOURCE);
5576
+ const projectRulesDir = opts.projectRulesDir ?? path12.join(repoRoot, PROJECT_RULES);
5577
+ const globalRulesDir = opts.globalRulesDir ?? path12.join(os6.homedir(), GLOBAL_RULES);
5578
+ const sourceDir = opts.sourceDir ?? path12.join(repoRoot, PROJECTION_SOURCE);
5499
5579
  const store = opts.store ?? defaultStore(repoRoot);
5500
5580
  const limit = opts.limit ?? 30;
5501
5581
  return {
5502
5582
  delivered: measureDelivered(projectRulesDir, globalRulesDir),
5503
5583
  divergence: compareCarriers(projectRulesDir, globalRulesDir, sourceDir),
5504
- skills: censusSkills(path13.join(repoRoot, SKILLS_ROOT)),
5584
+ skills: censusSkills(path12.join(repoRoot, SKILLS_ROOT)),
5505
5585
  usage: measureUsage(store, limit),
5506
- storePresent: fs13.existsSync(store),
5586
+ storePresent: fs12.existsSync(store),
5507
5587
  sk2: scanStore2(repoRoot, store, limit)
5508
5588
  };
5509
5589
  }
@@ -5628,7 +5708,7 @@ function render5(r) {
5628
5708
  lines.push("workflow without a measured false-positive rate first (locked policy).");
5629
5709
  return lines.join("\n");
5630
5710
  }
5631
- function main13(argv) {
5711
+ function main12(argv) {
5632
5712
  const args = argv ?? process5.argv.slice(2);
5633
5713
  const opts = {};
5634
5714
  const value = (i) => {
@@ -5644,7 +5724,7 @@ function main13(argv) {
5644
5724
  `);
5645
5725
  return 1;
5646
5726
  }
5647
- const resolved = path13.resolve(v);
5727
+ const resolved = path12.resolve(v);
5648
5728
  if (a === "--store") opts.store = resolved;
5649
5729
  else if (a === "--project") opts.projectRulesDir = resolved;
5650
5730
  else if (a === "--global") opts.globalRulesDir = resolved;
@@ -5674,21 +5754,21 @@ function main13(argv) {
5674
5754
  `);
5675
5755
  return 0;
5676
5756
  }
5677
- function _isCliEntry12() {
5757
+ function _isCliEntry11() {
5678
5758
  if (process5.argv[1] === void 0) return false;
5679
- if (import.meta.url === pathToFileURL13(path13.resolve(process5.argv[1])).href) return true;
5759
+ if (import.meta.url === pathToFileURL12(path12.resolve(process5.argv[1])).href) return true;
5680
5760
  try {
5681
- return fs13.realpathSync(_HERE4) === fs13.realpathSync(path13.resolve(process5.argv[1]));
5761
+ return fs12.realpathSync(_HERE4) === fs12.realpathSync(path12.resolve(process5.argv[1]));
5682
5762
  } catch {
5683
5763
  return false;
5684
5764
  }
5685
5765
  }
5686
- if (_isCliEntry12()) {
5687
- process5.exit(main13());
5766
+ if (_isCliEntry11()) {
5767
+ process5.exit(main12());
5688
5768
  }
5689
5769
 
5690
5770
  // src/scripts/_cli/cmd_conformance.ts
5691
- var _HERE5 = fileURLToPath14(import.meta.url);
5771
+ var _HERE5 = fileURLToPath13(import.meta.url);
5692
5772
  var PACKAGE_ROOT = resolvePackageRoot(import.meta.url);
5693
5773
  var CONFORMANCE_CHECK_IDS = [
5694
5774
  "txlog-clean",
@@ -5711,17 +5791,17 @@ function conformanceLogPath() {
5711
5791
  if (override && override.trim().length > 0) {
5712
5792
  return override;
5713
5793
  }
5714
- return path14.join(os7.homedir(), ".event4u", "agent-config", "conformance-log.jsonl");
5794
+ return path13.join(os7.homedir(), ".event4u", "agent-config", "conformance-log.jsonl");
5715
5795
  }
5716
5796
  function installLogPath() {
5717
5797
  const override = process6.env["AGENT_CONFIG_INSTALL_LOG"];
5718
5798
  if (override && override.trim().length > 0) {
5719
5799
  return override;
5720
5800
  }
5721
- return path14.join(os7.homedir(), ".event4u", "agent-config", "install-log.jsonl");
5801
+ return path13.join(os7.homedir(), ".event4u", "agent-config", "install-log.jsonl");
5722
5802
  }
5723
5803
  function _check_txlog_clean(logPath = installLogPath()) {
5724
- if (!fs14.existsSync(logPath)) {
5804
+ if (!fs13.existsSync(logPath)) {
5725
5805
  return {
5726
5806
  id: "txlog-clean",
5727
5807
  status: "ok",
@@ -5755,11 +5835,11 @@ function _check_txlog_clean(logPath = installLogPath()) {
5755
5835
  };
5756
5836
  }
5757
5837
  function routeTargetPaths(target) {
5758
- return routeTargetPathsPosix(target).map((rel) => path14.join(...rel.split("/")));
5838
+ return routeTargetPathsPosix(target).map((rel) => path13.join(...rel.split("/")));
5759
5839
  }
5760
5840
  function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
5761
- const routerPath = path14.join(packageRoot, "dist", "router.json");
5762
- if (!fs14.existsSync(routerPath)) {
5841
+ const routerPath = path13.join(packageRoot, "dist", "router.json");
5842
+ if (!fs13.existsSync(routerPath)) {
5763
5843
  return {
5764
5844
  id: "router-pointers",
5765
5845
  status: "fail",
@@ -5769,7 +5849,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
5769
5849
  }
5770
5850
  let router;
5771
5851
  try {
5772
- router = JSON.parse(fs14.readFileSync(routerPath, "utf8"));
5852
+ router = JSON.parse(fs13.readFileSync(routerPath, "utf8"));
5773
5853
  } catch (exc) {
5774
5854
  return {
5775
5855
  id: "router-pointers",
@@ -5778,13 +5858,13 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
5778
5858
  remedy: "reinstall the package \u2014 dist/router.json is corrupt"
5779
5859
  };
5780
5860
  }
5781
- const rulesDir = path14.join(packageRoot, "dist", "agent-src", "rules");
5861
+ const rulesDir = path13.join(packageRoot, "dist", "agent-src", "rules");
5782
5862
  const broken = [];
5783
5863
  let ids = 0;
5784
5864
  let targets = 0;
5785
5865
  for (const kernelId of router["kernel"] ?? []) {
5786
5866
  ids += 1;
5787
- if (!fs14.existsSync(path14.join(rulesDir, `${kernelId}.md`))) {
5867
+ if (!fs13.existsSync(path13.join(rulesDir, `${kernelId}.md`))) {
5788
5868
  broken.push(`kernel:${kernelId} \u2192 rules/${kernelId}.md missing`);
5789
5869
  }
5790
5870
  }
@@ -5792,7 +5872,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
5792
5872
  for (const entry of router[tier] ?? []) {
5793
5873
  const ruleId = String(entry["id"] ?? "");
5794
5874
  ids += 1;
5795
- if (!fs14.existsSync(path14.join(rulesDir, `${ruleId}.md`))) {
5875
+ if (!fs13.existsSync(path13.join(rulesDir, `${ruleId}.md`))) {
5796
5876
  broken.push(`${tier}:${ruleId} \u2192 rules/${ruleId}.md missing`);
5797
5877
  }
5798
5878
  for (const target of entry["routes_to"] ?? []) {
@@ -5802,7 +5882,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
5802
5882
  broken.push(`${ruleId} \u2192 unparseable routes_to ${JSON.stringify(target)}`);
5803
5883
  continue;
5804
5884
  }
5805
- if (!candidates.some((rel) => fs14.existsSync(path14.join(packageRoot, rel)))) {
5885
+ if (!candidates.some((rel) => fs13.existsSync(path13.join(packageRoot, rel)))) {
5806
5886
  broken.push(`${ruleId} \u2192 ${target} (${candidates[0]} missing)`);
5807
5887
  }
5808
5888
  }
@@ -5834,9 +5914,9 @@ function detectSmokePlatform(projectRoot) {
5834
5914
  return "claude";
5835
5915
  }
5836
5916
  function dispatcherRunner(packageRoot) {
5837
- const localTsx = path14.join(packageRoot, "node_modules", ".bin", "tsx");
5838
- const script = path14.join(packageRoot, "src", "scripts", "hooks", "dispatch_hook.ts");
5839
- if (fs14.existsSync(localTsx)) {
5917
+ const localTsx = path13.join(packageRoot, "node_modules", ".bin", "tsx");
5918
+ const script = path13.join(packageRoot, "src", "scripts", "hooks", "dispatch_hook.ts");
5919
+ if (fs13.existsSync(localTsx)) {
5840
5920
  return [localTsx, script];
5841
5921
  }
5842
5922
  return ["npx", "tsx", script];
@@ -5886,12 +5966,12 @@ function _check_hook_dispatcher(projectRoot, packageRoot = PACKAGE_ROOT, opts =
5886
5966
  };
5887
5967
  }
5888
5968
  function readSettingsMode(projectRoot) {
5889
- const settingsPath = path14.join(projectRoot, ".agent-settings.yml");
5890
- if (!fs14.existsSync(settingsPath)) {
5969
+ const settingsPath = path13.join(projectRoot, ".agent-settings.yml");
5970
+ if (!fs13.existsSync(settingsPath)) {
5891
5971
  return "eager-all";
5892
5972
  }
5893
5973
  try {
5894
- const parsed = YAML.parse(fs14.readFileSync(settingsPath, "utf8"));
5974
+ const parsed = YAML.parse(fs13.readFileSync(settingsPath, "utf8"));
5895
5975
  const lean = parsed?.["lean_projection"] ?? null;
5896
5976
  const mode = lean?.["mode"];
5897
5977
  return typeof mode === "string" && mode.length > 0 ? mode : "eager-all";
@@ -5902,7 +5982,7 @@ function readSettingsMode(projectRoot) {
5902
5982
  function kernelIdsFromRouter(packageRoot) {
5903
5983
  try {
5904
5984
  const router = JSON.parse(
5905
- fs14.readFileSync(path14.join(packageRoot, "dist", "router.json"), "utf8")
5985
+ fs13.readFileSync(path13.join(packageRoot, "dist", "router.json"), "utf8")
5906
5986
  );
5907
5987
  return new Set((router["kernel"] ?? []).map(String));
5908
5988
  } catch {
@@ -5919,8 +5999,8 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
5919
5999
  remedy: "set lean_projection.mode to 'eager-all' or 'thin' in .agent-settings.yml"
5920
6000
  };
5921
6001
  }
5922
- const rulesDir = projectedRulesDir ?? path14.join(projectRoot, ".augment", "rules");
5923
- if (!fs14.existsSync(rulesDir)) {
6002
+ const rulesDir = projectedRulesDir ?? path13.join(projectRoot, ".augment", "rules");
6003
+ if (!fs13.existsSync(rulesDir)) {
5924
6004
  return {
5925
6005
  id: "lean-projection",
5926
6006
  status: "skipped",
@@ -5929,7 +6009,7 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
5929
6009
  };
5930
6010
  }
5931
6011
  const kernel = kernelIdsFromRouter(packageRoot);
5932
- const files = fs14.readdirSync(rulesDir).filter((f) => f.endsWith(".md") && f !== "INDEX.md").sort();
6012
+ const files = fs13.readdirSync(rulesDir).filter((f) => f.endsWith(".md") && f !== "INDEX.md").sort();
5933
6013
  const nonKernel = files.filter((f) => !kernel.has(f.replace(/\.md$/, "")));
5934
6014
  if (nonKernel.length === 0) {
5935
6015
  return {
@@ -5943,7 +6023,7 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
5943
6023
  for (const f of nonKernel) {
5944
6024
  let text = "";
5945
6025
  try {
5946
- text = fs14.readFileSync(path14.join(rulesDir, f), "utf8");
6026
+ text = fs13.readFileSync(path13.join(rulesDir, f), "utf8");
5947
6027
  } catch {
5948
6028
  continue;
5949
6029
  }
@@ -5974,8 +6054,8 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
5974
6054
  function _check_host_manifest(projectRoot) {
5975
6055
  const detected = Object.entries(detectToolPresence(projectRoot)).filter(([, v]) => v === true).map(([k]) => k);
5976
6056
  const detectedNote = detected.length > 0 ? `detected surfaces: ${detected.join(", ")}` : "no surfaces detected";
5977
- const settingsPath = path14.join(projectRoot, ".agent-settings.yml");
5978
- if (!fs14.existsSync(settingsPath)) {
6057
+ const settingsPath = path13.join(projectRoot, ".agent-settings.yml");
6058
+ if (!fs13.existsSync(settingsPath)) {
5979
6059
  return {
5980
6060
  id: "host-manifest",
5981
6061
  status: "ok",
@@ -5985,7 +6065,7 @@ function _check_host_manifest(projectRoot) {
5985
6065
  }
5986
6066
  let parsed = null;
5987
6067
  try {
5988
- parsed = YAML.parse(fs14.readFileSync(settingsPath, "utf8"));
6068
+ parsed = YAML.parse(fs13.readFileSync(settingsPath, "utf8"));
5989
6069
  } catch (exc) {
5990
6070
  return {
5991
6071
  id: "host-manifest",
@@ -6046,14 +6126,14 @@ function appendConformanceReport(checks, projectRoot, logPath = conformanceLogPa
6046
6126
  } catch {
6047
6127
  }
6048
6128
  }
6049
- var USAGE = "usage: agent-config conformance [-h] [--project PROJECT] [--json]\n [--skip-dispatcher] [--funnel]\n";
6129
+ var USAGE2 = "usage: agent-config conformance [-h] [--project PROJECT] [--json]\n [--skip-dispatcher] [--funnel]\n";
6050
6130
  function _parse(argv) {
6051
6131
  const opts = { project: null, json: false, skip_dispatcher: false, funnel: false };
6052
6132
  let i = 0;
6053
6133
  while (i < argv.length) {
6054
6134
  const a = argv[i];
6055
6135
  if (a === "-h" || a === "--help") {
6056
- process6.stdout.write(USAGE);
6136
+ process6.stdout.write(USAGE2);
6057
6137
  throw new ArgparseExit(0);
6058
6138
  }
6059
6139
  if (a === "--json") {
@@ -6067,7 +6147,7 @@ function _parse(argv) {
6067
6147
  opts.project = a.slice(a.indexOf("=") + 1);
6068
6148
  } else {
6069
6149
  if (i + 1 >= argv.length) {
6070
- process6.stderr.write(USAGE);
6150
+ process6.stderr.write(USAGE2);
6071
6151
  process6.stderr.write("agent-config conformance: error: --project expects a value\n");
6072
6152
  throw new ArgparseExit(2);
6073
6153
  }
@@ -6075,7 +6155,7 @@ function _parse(argv) {
6075
6155
  i += 1;
6076
6156
  }
6077
6157
  } else {
6078
- process6.stderr.write(USAGE);
6158
+ process6.stderr.write(USAGE2);
6079
6159
  process6.stderr.write(`agent-config conformance: error: unrecognized argument ${a}
6080
6160
  `);
6081
6161
  throw new ArgparseExit(2);
@@ -6084,7 +6164,7 @@ function _parse(argv) {
6084
6164
  }
6085
6165
  return opts;
6086
6166
  }
6087
- function main14(argv = null) {
6167
+ function main13(argv = null) {
6088
6168
  const opts = _parse(argv !== null ? Array.from(argv) : process6.argv.slice(2));
6089
6169
  if (opts.funnel) {
6090
6170
  print(render5(buildFunnel()));
@@ -6155,25 +6235,25 @@ function main14(argv = null) {
6155
6235
  }
6156
6236
  return failed ? 1 : 0;
6157
6237
  }
6158
- function _isCliEntry13() {
6238
+ function _isCliEntry12() {
6159
6239
  if (process6.argv[1] === void 0) {
6160
6240
  return false;
6161
6241
  }
6162
- const argvUrl = pathToFileURL14(path14.resolve(process6.argv[1])).href;
6242
+ const argvUrl = pathToFileURL13(path13.resolve(process6.argv[1])).href;
6163
6243
  if (import.meta.url === argvUrl) {
6164
6244
  return true;
6165
6245
  }
6166
6246
  try {
6167
- const here = fs14.realpathSync(fileURLToPath14(import.meta.url));
6168
- const argv = fs14.realpathSync(path14.resolve(process6.argv[1]));
6247
+ const here = fs13.realpathSync(fileURLToPath13(import.meta.url));
6248
+ const argv = fs13.realpathSync(path13.resolve(process6.argv[1]));
6169
6249
  return here === argv;
6170
6250
  } catch {
6171
6251
  return false;
6172
6252
  }
6173
6253
  }
6174
- if (_isCliEntry13() || process6.argv[1] === _HERE5) {
6254
+ if (_isCliEntry12() || process6.argv[1] === _HERE5) {
6175
6255
  try {
6176
- process6.exitCode = main14(process6.argv.slice(2));
6256
+ process6.exitCode = main13(process6.argv.slice(2));
6177
6257
  } catch (e) {
6178
6258
  if (e instanceof ArgparseExit) {
6179
6259
  process6.exitCode = e.code;
@@ -6196,7 +6276,7 @@ export {
6196
6276
  conformanceLogPath,
6197
6277
  detectSmokePlatform,
6198
6278
  installLogPath,
6199
- main14 as main,
6279
+ main13 as main,
6200
6280
  routeTargetPaths,
6201
6281
  runConformanceChecks
6202
6282
  };