@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
@@ -0,0 +1,141 @@
1
+ # Release-gate locality registry
2
+ #
3
+ # WHY THIS FILE EXISTS
4
+ #
5
+ # `release-validation.yml` is the last gate a release passes and the first one
6
+ # that costs something to fail: by the time it runs, a branch, a pull request
7
+ # and a CI run already exist. Until 2026-09-05 nothing related its jobs to the
8
+ # commands a maintainer can run BEFORE any of that leaves the machine, and the
9
+ # result was a recurring failure shape rather than a one-off — 14.14.0 (PR
10
+ # #1812) on the curated head, 14.17.0 (PR #1856) on the governance-mix
11
+ # response. Both were discoverable locally in under two seconds. Neither was
12
+ # discovered locally, because nothing asked.
13
+ #
14
+ # The registry is the relation the tree was missing. It is read by two
15
+ # consumers, which is what keeps it from rotting into a comment:
16
+ #
17
+ # 1. `tests/scripts/release_gate_locality.test.ts` — every job in the
18
+ # workflow has a row, every row names a live job, every declared script
19
+ # exists, and every `local: ~` carries a reason with an approved prefix.
20
+ # A new release-validation job therefore cannot land without answering
21
+ # "and how does a maintainer run this before pushing?".
22
+ # 2. `task release:verify` — runs every row marked `verify: true` against the
23
+ # working tree. That is the command the release pipeline points at, and
24
+ # the one to run before `task release`.
25
+ #
26
+ # WHAT A ROW MEANS
27
+ #
28
+ # local: the shell command that reproduces the job's assertion locally.
29
+ # `~` (null) means it genuinely cannot run before the PR exists.
30
+ # reason: REQUIRED when `local` is null. Must start with one of
31
+ # NEEDS_PR: / NEEDS_ARTIFACT: / NEEDS_NETWORK: / NEEDS_TOKEN:
32
+ # so the cause is classified rather than merely narrated.
33
+ # verify: whether `task release:verify` runs it. Cheap and deterministic
34
+ # rows are true; anything that packs or installs is false and stays
35
+ # opt-in.
36
+ # network: marks a row that leaves the machine. Excluded from `--cheap`,
37
+ # which is the set the release pipeline runs on its own, so a
38
+ # registry outage cannot read as a release defect.
39
+ # release_branch_only:
40
+ # the assertion only means anything on a `release/X.Y.Z` head.
41
+ # Skipped with a printed reason elsewhere — never silently, and
42
+ # never reported as a pass.
43
+ # script: the implementing script, when the assertion lives in one. `~`
44
+ # marks an assertion that exists only as inline workflow YAML —
45
+ # which is itself the defect this column makes countable.
46
+ # guard: the local guard site that ALSO refuses this, when one exists.
47
+ # A row with a guard fails before the push, not merely before CI.
48
+ # partial: REQUIRED when the `local` command reproduces only part of what the
49
+ # job asserts. Names the uncovered part. A row silently claiming
50
+ # full coverage it does not have is the defect this file exists to
51
+ # end, so the gap is written down rather than inferred.
52
+
53
+ version: 1
54
+ workflow: .github/workflows/release-validation.yml
55
+
56
+ jobs:
57
+ release-shape:
58
+ local: 'git diff --name-only origin/main...HEAD | ./scripts-run src/scripts/check_release_pr_shape --files -'
59
+ verify: true
60
+ # The assertion is about a RELEASE PR's diff — every ordinary branch fails
61
+ # it by construction, so running it off a release branch would teach a
62
+ # maintainer to ignore this command's red. Skipped with a printed reason
63
+ # instead, which is the difference between a gate that is not applicable
64
+ # and one that passed.
65
+ release_branch_only: true
66
+ script: src/scripts/check_release_pr_shape.ts
67
+ guard: ~
68
+
69
+ changelog-entry:
70
+ # The job asserts three things: a heading for the head version, a non-empty
71
+ # body, and a `Tests: N (+M since PREV)` footer.
72
+ #
73
+ # `check_release_highlights` covers the first two — it exits 2 with
74
+ # "CHANGELOG carries no section for <v>". It does NOT read the footer, and
75
+ # saying otherwise here would be the exact defect this registry exists to
76
+ # end: a row asserting a coverage it does not have. The footer half is
77
+ # covered locally by the guard named below, not by the `local` command, so
78
+ # `partial` states that rather than leaving a reader to grep for it.
79
+ local: './scripts-run src/scripts/check_release_highlights --version ${VERSION}'
80
+ verify: true
81
+ partial: 'heading + non-empty body only; the `Tests: N` footer half is covered by the guard, not by this command'
82
+ script: ~
83
+ guard: guard_release_curation
84
+
85
+ surface-equality:
86
+ local: ~
87
+ reason: 'NEEDS_PR: compares the live PR body against the CHANGELOG section; there is no PR body before `gh pr create`. The body is DERIVED from that same section (`pr_body_from_section`), so the local half is the section check above.'
88
+ verify: false
89
+ script: src/scripts/check_release_surface_equality.ts
90
+ guard: ~
91
+
92
+ highlight-plausibility:
93
+ # The job that failed 14.17.0. All four sub-checks (authoring instruction,
94
+ # unrewritten derived marker, `_none_` contradiction, governance-mix
95
+ # response) run locally from local tags alone — no origin, no HEAD_REF.
96
+ local: './scripts-run src/scripts/check_release_highlights --version ${VERSION}'
97
+ verify: true
98
+ script: src/scripts/check_release_highlights.ts
99
+ guard: guard_release_branch_push
100
+
101
+ version-consistency:
102
+ local: './scripts-run src/scripts/check_template_pin_drift'
103
+ verify: true
104
+ script: src/scripts/check_template_pin_drift.ts
105
+ guard: ~
106
+
107
+ finding-dispositions:
108
+ local: './scripts-run src/scripts/check_finding_dispositions --release ${VERSION}'
109
+ verify: false
110
+ reason: 'NEEDS_PR: the unrecorded-findings half reads live PR review comments. The missing-dispositions half does run locally and is what the `local` command covers.'
111
+ script: src/scripts/check_finding_dispositions.ts
112
+ guard: ~
113
+
114
+ audit-gate:
115
+ local: 'npm audit --omit=dev --audit-level=high'
116
+ verify: true
117
+ # Reaches the registry, so it is excluded from the `--cheap` set the
118
+ # release pipeline runs unattended: a registry outage must not read as a
119
+ # release defect. `task release:verify` (no flag) still runs it.
120
+ network: true
121
+ script: ~
122
+ guard: ~
123
+
124
+ release-install-e2e:
125
+ local: ~
126
+ reason: 'NEEDS_ARTIFACT: needs a packed tarball plus a BASELINE_TARBALL of the upgrade-from version; runnable by hand as `bash tests/test_release_install_e2e.sh`, too slow for a pre-push gate.'
127
+ verify: false
128
+ script: ~
129
+ guard: ~
130
+
131
+ activation-census:
132
+ # The one row where the local command asserts MORE than the job it mirrors,
133
+ # not less, so `partial` does not apply: the transcript store the census
134
+ # reads is keyed on the directory it runs in and does not exist on a runner,
135
+ # so CI can only re-take an empty reading and check the published figure
136
+ # against its record. Run locally, the same command reads the real store,
137
+ # which is the only place the census means anything.
138
+ local: './scripts-run src/scripts/report_skill_activation && ./scripts-run src/scripts/check_skill_activation_claim'
139
+ verify: true
140
+ script: src/scripts/report_skill_activation.ts
141
+ guard: ~
@@ -2881,7 +2881,7 @@ function _run_checks(
2881
2881
  'claude-command-wrappers': _check_claude_command_wrappers,
2882
2882
  'surface-state': _check_surface_state,
2883
2883
  'hook-wiring': _check_hook_wiring,
2884
- ...(runtime_wiring.wiringRunners({ packageRoot: _package_root(), iterOverrides: () => iter_setting_overrides({ cwd: project_root }) }) as unknown as Record<string, CheckRunner>),
2884
+ ...(runtime_wiring.wiringRunners({ packageRoot: _package_root(), projectRoot: project_root, iterOverrides: () => iter_setting_overrides({ cwd: project_root }) }) as unknown as Record<string, CheckRunner>),
2885
2885
  ...(install_reach.reachRunners({ projectRoot: project_root, resolvableVersion: _current_package_version() }) as unknown as Record<string, CheckRunner>),
2886
2886
  'stale-orphans': _check_stale_orphans,
2887
2887
  overrides: () => _check_overrides(project_root),
@@ -2967,7 +2967,7 @@ function _run_checks_no_manifest(
2967
2967
  'claude-command-wrappers': _check_claude_command_wrappers,
2968
2968
  'surface-state': _check_surface_state,
2969
2969
  'hook-wiring': _check_hook_wiring,
2970
- ...(runtime_wiring.wiringRunners({ packageRoot: _package_root(), iterOverrides: () => iter_setting_overrides({ cwd: project_root }) }) as unknown as Record<string, CheckRunner>),
2970
+ ...(runtime_wiring.wiringRunners({ packageRoot: _package_root(), projectRoot: project_root, iterOverrides: () => iter_setting_overrides({ cwd: project_root }) }) as unknown as Record<string, CheckRunner>),
2971
2971
  ...(install_reach.reachRunners({ projectRoot: project_root, resolvableVersion: _current_package_version() }) as unknown as Record<string, CheckRunner>),
2972
2972
  'stale-orphans': _check_stale_orphans,
2973
2973
  // BOTH registries need the id. Registering only the first one crashed with
@@ -17,7 +17,7 @@
17
17
  * file (legacy copy-paste mode)
18
18
  * --launch <host> spawn a fresh interactive host session after
19
19
  * generating (claude: hook-seeded; codex: handoff as
20
- * initial prompt — no hook surface there)
20
+ * initial prompt — this package binds no hook there)
21
21
  * --llm narrative-polish seam; v1: clear not-implemented error
22
22
  * --root <path> repo root override (default: cwd)
23
23
  *
@@ -446,7 +446,7 @@ export async function main(argv: string[] | null = null, options: MainOptions =
446
446
  const installed_version = options.installed_version || _detect_installed_version();
447
447
  const state_path = options.state_path || update_check.DEFAULT_STATE_PATH;
448
448
 
449
- // AGENT_CONFIG_OFFLINE=1 (set by `install.py --offline`) is honored as an
449
+ // AGENT_CONFIG_OFFLINE=1 (set by `agent-config install --offline`) is honored as an
450
450
  // env-level kill-switch. Mirrors cmd_versions.py.
451
451
  const offline = args.offline || process.env['AGENT_CONFIG_OFFLINE'] === '1';
452
452
 
@@ -426,7 +426,7 @@ export function main(argv: string[] | null = null, options: MainOptions = {}): n
426
426
  const err = options.err ?? _stderrSink();
427
427
  const opts = _parse(argv ?? process.argv.slice(2), out, err);
428
428
 
429
- // AGENT_CONFIG_OFFLINE=1 (set by `install.py --offline`) is honored as a
429
+ // AGENT_CONFIG_OFFLINE=1 (set by `agent-config install --offline`) is honored as a
430
430
  // global kill-switch even when the per-command --offline flag is absent.
431
431
  const offline = opts.offline || process.env['AGENT_CONFIG_OFFLINE'] === '1';
432
432
 
@@ -483,3 +483,47 @@ export function readAdrAxisCells(text: string): AdrAxisCells {
483
483
  : `${strength} (${discovery})`,
484
484
  };
485
485
  }
486
+
487
+ /**
488
+ * The one shape this tool decides: a trigger whose condition IS a leading date.
489
+ *
490
+ * Deliberately anchored at the start of the string and deliberately narrow. The
491
+ * boundary is the whole safety property — the moment a trigger is matched on a
492
+ * date found anywhere in its prose, "reopens when N exceeds M" is one edit away
493
+ * and the machine-readable grammar two council seats rejected on 2026-08-19 has
494
+ * been built by increments.
495
+ */
496
+ export const DATED_TRIGGER_RE = /^\s*(?:expir(?:y|es)\b\s*[:—-]?\s*)?(\d{4})-(\d{2})-(\d{2})(?!\d)/iu;
497
+
498
+ /**
499
+ * A trigger that ANNOUNCES itself as dated, whether or not the date parses.
500
+ *
501
+ * The gap between this and `DATED_TRIGGER_RE` is the `dated-unparsed` state.
502
+ * Exactly one ADR in the corpus carries a dated expiry, so a parse tuned to its
503
+ * phrasing can fail on the second one — and the failure mode is silence,
504
+ * because the old answer for every trigger was `indeterminate`. This probe is
505
+ * what turns that silence into a reported defect.
506
+ */
507
+ export const DATED_TRIGGER_PROBE = /^\s*(?:expir(?:y|es)\b|\d{4}-\d{1,2}-\d{1,2}\b)/iu;
508
+
509
+ /** The UTC calendar day of an instant, as `YYYY-MM-DD`. */
510
+ export function iso_day(d: Date): string {
511
+ return d.toISOString().slice(0, 10);
512
+ }
513
+
514
+ /**
515
+ * The calendar day a dated trigger names, or null when there is none to read.
516
+ *
517
+ * Round-tripping through `Date.UTC` rejects `2026-13-45`, which matches the
518
+ * shape and is not a date.
519
+ */
520
+ export function dated_trigger_day(trigger: string): string | null {
521
+ const m = DATED_TRIGGER_RE.exec(trigger);
522
+ if (m === null) return null;
523
+ const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];
524
+ const at = new Date(Date.UTC(y, mo - 1, d));
525
+ if (at.getUTCFullYear() !== y || at.getUTCMonth() !== mo - 1 || at.getUTCDate() !== d) {
526
+ return null;
527
+ }
528
+ return iso_day(at);
529
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * consequence_objects — the consequence vocabulary's prose half.
3
+ *
4
+ * Two things live here: the phrase rows that recognise a consequence
5
+ * authorization, and the extraction of the OBJECT such an authorization names.
6
+ * An authorization for one table, one environment or one recipient class is not
7
+ * an authorization for another, and until this existed the ledger could express
8
+ * the difference for nothing but pull-request numbers.
9
+ *
10
+ * IT IS RECOGNITION AND EXTRACTION, NOT MATCHING. Nothing here decides
11
+ * anything; the one consumer that acts on the vocabulary is `conformance_scan`,
12
+ * which counts after the fact. Making the object a matching condition is the
13
+ * ledger item ADR-254 left to the owner.
14
+ *
15
+ * It sits beside `git_authorization_hook` rather than inside it because that
16
+ * file is against the per-file source-size budget, which charges every line
17
+ * above 1500. The split is along a real seam: the hook owns the machinery —
18
+ * sentence bounds, negation, fenced-paste handling — and this module owns the
19
+ * consequence vocabulary that machinery is applied to.
20
+ */
21
+ import type { GitOp } from "../git_authorization_hook.js";
22
+
23
+ /**
24
+ * Prose phrases, German and English, that authorize a consequence operation.
25
+ *
26
+ * Spliced into `PHRASES` at its definition, so every row inherits the
27
+ * interrogative filter, the clause-scoped negation check and the fenced-paste
28
+ * split rather than reimplementing them.
29
+ *
30
+ * Every row REQUIRES ITS OBJECT NOUN, and that is why the patterns are long. A
31
+ * bare verb cannot tell an authorization from a mention, and for this class the
32
+ * mention is the common case: "deploy" appears in a hundred sentences that
33
+ * authorize nothing, while "deploy nach production" appears only when someone
34
+ * means it.
35
+ *
36
+ * German verb endings are `(e|en)?` because the imperative drops its `-e`, and
37
+ * both word orders are covered — the house style stated above `PHRASES`. The
38
+ * leading boundary is an explicit character class rather than `\b`: JavaScript's
39
+ * `\b` is ASCII-only, so a word STARTING with an umlaut ("Ändere") has no
40
+ * boundary before it and the row silently never fires.
41
+ */
42
+ export const CONSEQUENCE_PHRASES: ReadonlyArray<{ op: GitOp; re: RegExp }> = [
43
+ {
44
+ op: "prod-deploy",
45
+ 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,
46
+ },
47
+ {
48
+ op: "prod-data-destroy",
49
+ 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,
50
+ },
51
+ {
52
+ op: "prod-infra-change",
53
+ 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,
54
+ },
55
+ {
56
+ op: "external-send",
57
+ 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,
58
+ },
59
+ {
60
+ op: "money-movement",
61
+ 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,
62
+ },
63
+ {
64
+ op: "trunk-force-push",
65
+ 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,
66
+ },
67
+ {
68
+ op: "out-of-scope-destruction",
69
+ 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,
70
+ },
71
+ ];
72
+
73
+ /**
74
+ * Operations whose authorization is meaningless without its object.
75
+ *
76
+ * "Delete the table" is not an authorization to delete a table; it is an
77
+ * authorization to delete THAT table. The same holds for a recipient class and
78
+ * for a payment. The other four consequence operations are environment-scoped
79
+ * rather than object-scoped, so an object is recorded for them when the prose
80
+ * names one and never required.
81
+ */
82
+ export const OBJECT_REQUIRED_OPS: ReadonlySet<GitOp> = new Set<GitOp>([
83
+ "prod-data-destroy",
84
+ "external-send",
85
+ "money-movement",
86
+ ]);
87
+
88
+ /**
89
+ * Words that can follow an object noun and are never the object's NAME.
90
+ *
91
+ * "Lösch die Tabelle bitte" names no table; the extractor's next-token rule
92
+ * captures `bitte` unless something says otherwise. A stop list is the honest
93
+ * shape for that — the same reviewable-keyword-list discipline the negation and
94
+ * pasted-output vocabularies above use, and for the same reason: a parser that
95
+ * guessed here would record a fabricated object, and a fabricated object is
96
+ * worse than a missing one because the conformance count treats a recorded
97
+ * object as an authorization the turn actually gave.
98
+ */
99
+ const OBJECT_STOP_WORDS: ReadonlySet<string> = new Set([
100
+ "bitte", "doch", "mal", "nicht", "jetzt", "gleich", "auch", "noch", "wieder",
101
+ "in", "im", "aus", "von", "vom", ": ", "der", "die", "das", "den", "dem",
102
+ "und", "oder", "mit", "auf", "zu", "komplett", "ganz", "alle", "allen",
103
+ "please", "now", "again", "too", "also", "from", "and", "or", "the", "a",
104
+ "an", "there", "here", "completely", "all", "with", "on", "to", "in",
105
+ ]);
106
+
107
+ /** Lower-case, strip surrounding quotes and trailing punctuation. */
108
+ function _normalizeObject(raw: string): string {
109
+ return raw.trim().replace(/^["'`]+|["'`]+$/g, "").replace(/[.,;:]+$/, "").toLowerCase();
110
+ }
111
+
112
+ /** Per-operation object patterns, each with the capture group that holds it. */
113
+ const OBJECT_PATTERNS: Readonly<Record<string, readonly RegExp[]>> = {
114
+ "prod-data-destroy": [
115
+ /\b(?:tabelle|table|datenbank|database|schema|collection|index)\s+["'`]?([A-Za-z_][\w.$-]*)/gi,
116
+ /\bs3:\/\/([\w.-]+)/gi,
117
+ /\b(?:bucket)\s+["'`]?([A-Za-z_][\w.-]*)/gi,
118
+ ],
119
+ "external-send": [
120
+ /([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/g,
121
+ /\bhttps?:\/\/([^\s/"'`]+)/gi,
122
+ /\b(?:an\s+(?:die|den|alle)|to\s+(?:the|all))\s+([A-Za-z][\w-]*)/gi,
123
+ ],
124
+ "money-movement": [
125
+ /(\d[\d.,]*)\s*(?:€|eur\b|euro\b|\$|usd\b)/gi,
126
+ /(?:€|\$)\s*(\d[\d.,]*)/g,
127
+ /\b(?:kunde|kunden|customer|account|konto)\s+["'`]?([A-Za-z0-9][\w.-]*)/gi,
128
+ ],
129
+ };
130
+
131
+ /**
132
+ * The objects a sentence names for one operation.
133
+ *
134
+ * Returns `[]` when the prose names none — which is a real answer and the one
135
+ * `conformance_scan` reads as "the record does not name this object". An empty
136
+ * list is never widened into a wildcard.
137
+ */
138
+ export function extractObjects(op: GitOp, text: string): string[] {
139
+ const patterns = OBJECT_PATTERNS[op];
140
+ if (!patterns) return [];
141
+ const out: string[] = [];
142
+ for (const re of patterns) {
143
+ const local = new RegExp(re.source, re.flags.includes("g") ? re.flags : `${re.flags}g`);
144
+ let m: RegExpExecArray | null;
145
+ while ((m = local.exec(text)) !== null) {
146
+ const value = _normalizeObject(m[1] ?? "");
147
+ if (value && !OBJECT_STOP_WORDS.has(value) && !out.includes(value)) out.push(value);
148
+ if (m.index === local.lastIndex) local.lastIndex++;
149
+ }
150
+ }
151
+ return out;
152
+ }
@@ -68,6 +68,13 @@ export const ALLOWLIST_PREFIXES: readonly (readonly string[])[] = [
68
68
  // a name. Read-only, deterministic, model-free, and already run in CI for its
69
69
  // own reasons — the same class as its neighbours here.
70
70
  ['check_no_external_sources'],
71
+ // Compares two COMMITTED files — the skill-activation census record against
72
+ // the sentence `docs/CLAIMS.md` publishes — so its exit code IS the verdict
73
+ // for that claim rather than evidence that some report exists. The census
74
+ // itself is deliberately NOT here: it reads a transcript store under `$HOME`
75
+ // that is absent on a runner, which would make the verdict an artifact of
76
+ // where it ran.
77
+ ['check_skill_activation_claim'],
71
78
  ['check_token_regression'],
72
79
  ['lint_agent_security'],
73
80
  ['measure_lexical_ranking'],
@@ -356,7 +356,7 @@ function as_posix(p: string): string {
356
356
  *
357
357
  * `dry_run=true` computes and returns the would-delete set (only paths that
358
358
  * currently exist on disk) WITHOUT unlinking anything or pruning empty
359
- * directories — the preview surface for `install.py --dry-run`. The selection
359
+ * directories — the preview surface for `agent-config install --dry-run`. The selection
360
360
  * logic (orphan diff, containment proof, directory guard) is identical to the
361
361
  * live path, so the preview is exact.
362
362
  */
@@ -456,7 +456,7 @@ export function reap_stale(
456
456
  *
457
457
  * `dry_run=true` returns the would-delete set (tagged orphans actually present
458
458
  * on disk) WITHOUT unlinking or pruning — the preview surface for
459
- * `install.py --dry-run`. Selection logic is identical to the live path.
459
+ * `agent-config install --dry-run`. Selection logic is identical to the live path.
460
460
  */
461
461
  export function reap_tagged_orphans(
462
462
  anchor: string,