@event4u/agent-config 9.14.0 → 9.16.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 (287) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +118 -99
  3. package/README.md +8 -0
  4. package/dist/agent-src/commands/agents/audit.md +7 -7
  5. package/dist/agent-src/commands/agents/optimize.md +1 -1
  6. package/dist/agent-src/commands/analyze/reference-repo.md +1 -1
  7. package/dist/agent-src/commands/chat-history.md +1 -0
  8. package/dist/agent-src/commands/context.md +1 -0
  9. package/dist/agent-src/commands/council.md +1 -1
  10. package/dist/agent-src/commands/ghostwriter.md +1 -1
  11. package/dist/agent-src/commands/memory.md +2 -1
  12. package/dist/agent-src/commands/module.md +1 -0
  13. package/dist/agent-src/commands/optimize/agents-dir.md +1 -1
  14. package/dist/agent-src/commands/optimize/deep.md +5 -2
  15. package/dist/agent-src/commands/optimize/skills.md +5 -5
  16. package/dist/agent-src/commands/override.md +1 -0
  17. package/dist/agent-src/commands/prediction-pool.md +4 -4
  18. package/dist/agent-src/commands/research/deep.md +1 -1
  19. package/dist/agent-src/commands/roadmap.md +2 -1
  20. package/dist/agent-src/commands/rule-compliance-audit.md +1 -1
  21. package/dist/agent-src/commands/tdd.md +1 -0
  22. package/dist/agent-src/commands/tests.md +1 -0
  23. package/dist/agent-src/commands/upstream-contribute.md +5 -5
  24. package/dist/agent-src/commands/video/from-script.md +1 -1
  25. package/dist/agent-src/commands/video/from-song.md +1 -1
  26. package/dist/agent-src/commands/video/scene.md +1 -1
  27. package/dist/agent-src/commands/video/stitch.md +1 -1
  28. package/dist/agent-src/commands/video/storyboard.md +1 -1
  29. package/dist/agent-src/commands/worktree/create.md +2 -1
  30. package/dist/agent-src/contexts/augment-infrastructure.md +1 -1
  31. package/dist/agent-src/contexts/contracts/artifact-engagement-flow.md +5 -5
  32. package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +3 -3
  33. package/dist/agent-src/contexts/contracts/emergency-triage-block.md +2 -2
  34. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +4 -3
  35. package/dist/agent-src/contexts/execution/subagent-routing.md +19 -0
  36. package/dist/agent-src/contexts/override-system.md +1 -1
  37. package/dist/agent-src/ghostwriter/fictional-fixture-v1.md +1 -1
  38. package/dist/agent-src/rules/augment-edit-discipline.md +1 -1
  39. package/dist/agent-src/rules/autonomous-execution.md +2 -1
  40. package/dist/agent-src/rules/brand-consistency.md +1 -0
  41. package/dist/agent-src/rules/brand-source-of-truth.md +1 -0
  42. package/dist/agent-src/rules/code-comment-discipline.md +2 -0
  43. package/dist/agent-src/rules/communication-through-line.md +1 -0
  44. package/dist/agent-src/rules/content-quoting-floor.md +1 -0
  45. package/dist/agent-src/rules/cross-source-consistency.md +0 -1
  46. package/dist/agent-src/rules/downstream-changes.md +1 -0
  47. package/dist/agent-src/rules/external-code-graph-interop.md +1 -0
  48. package/dist/agent-src/rules/external-reference-deep-dive.md +1 -0
  49. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  50. package/dist/agent-src/rules/finance-safety-floor.md +0 -1
  51. package/dist/agent-src/rules/icon-consistency.md +1 -0
  52. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  53. package/dist/agent-src/rules/improve-before-implement.md +1 -1
  54. package/dist/agent-src/rules/invite-challenge.md +1 -0
  55. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  56. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  57. package/dist/agent-src/rules/markdown-safe-codeblocks.md +1 -0
  58. package/dist/agent-src/rules/media-governance-routing.md +4 -3
  59. package/dist/agent-src/rules/media-sync-ground-truth.md +1 -0
  60. package/dist/agent-src/rules/no-attribution-footers.md +1 -0
  61. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +1 -0
  62. package/dist/agent-src/rules/no-pr-progress-comments.md +1 -0
  63. package/dist/agent-src/rules/onboarding-gate.md +1 -0
  64. package/dist/agent-src/rules/output-discipline.md +1 -0
  65. package/dist/agent-src/rules/prefer-enums-over-literals.md +1 -0
  66. package/dist/agent-src/rules/preservation-guard.md +1 -0
  67. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  68. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +1 -0
  69. package/dist/agent-src/rules/role-mode-adherence.md +1 -0
  70. package/dist/agent-src/rules/runtime-safety.md +1 -0
  71. package/dist/agent-src/rules/security-sensitive-stop.md +1 -0
  72. package/dist/agent-src/rules/session-canary.md +9 -2
  73. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  74. package/dist/agent-src/rules/source-of-truth.md +1 -0
  75. package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -0
  76. package/dist/agent-src/rules/think-before-action.md +1 -0
  77. package/dist/agent-src/rules/token-budget-discipline.md +1 -0
  78. package/dist/agent-src/rules/token-efficiency.md +1 -0
  79. package/dist/agent-src/rules/token-optimizer-maintenance.md +5 -5
  80. package/dist/agent-src/rules/tool-safety.md +1 -0
  81. package/dist/agent-src/rules/untrusted-input-defense.md +2 -0
  82. package/dist/agent-src/rules/user-interaction.md +1 -0
  83. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  84. package/dist/agent-src/skills/adr-create/SKILL.md +1 -1
  85. package/dist/agent-src/skills/check-refs/SKILL.md +1 -1
  86. package/dist/agent-src/skills/competitive-positioning/SKILL.md +1 -1
  87. package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
  88. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  89. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +1 -1
  90. package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
  91. package/dist/agent-src/skills/github-ci/SKILL.md +1 -0
  92. package/dist/agent-src/skills/guideline-writing/SKILL.md +1 -1
  93. package/dist/agent-src/skills/lint-skills/SKILL.md +1 -1
  94. package/dist/agent-src/skills/readme-reviewer/SKILL.md +1 -1
  95. package/dist/agent-src/skills/roadmap-management/SKILL.md +8 -1
  96. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  97. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  98. package/dist/agent-src/skills/skill-writing/SKILL.md +4 -4
  99. package/dist/agent-src/skills/token-optimizer/SKILL.md +4 -4
  100. package/dist/agent-src/skills/upstream-contribute/SKILL.md +6 -6
  101. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +25 -0
  102. package/dist/agent-src/templates/AGENTS.md +1 -1
  103. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  104. package/dist/agent-src/templates/command.md +1 -1
  105. package/dist/agent-src/templates/copilot-instructions.md +3 -3
  106. package/dist/agent-src/templates/rule.md +2 -2
  107. package/dist/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
  108. package/dist/agent-src/templates/skill-archive-note.md +5 -5
  109. package/dist/agent-src/templates/skill.md +1 -1
  110. package/dist/cli/agent-config.js +66 -19
  111. package/dist/cli/agent-config.js.map +1 -1
  112. package/dist/cli/main.js +9 -4
  113. package/dist/cli/main.js.map +1 -1
  114. package/dist/cli/mcp/content.js +1 -1
  115. package/dist/cli/mcp/content.js.map +1 -1
  116. package/dist/cli/python/workspace_drive.js +2 -0
  117. package/dist/cli/python/workspace_drive.js.map +1 -1
  118. package/dist/cli/python/workspace_drive_health.js +2 -0
  119. package/dist/cli/python/workspace_drive_health.js.map +1 -1
  120. package/dist/cli/python/workspace_hosts.js +2 -0
  121. package/dist/cli/python/workspace_hosts.js.map +1 -1
  122. package/dist/cli/python/workspace_sessions.js +2 -0
  123. package/dist/cli/python/workspace_sessions.js.map +1 -1
  124. package/dist/cli/registry.js +1 -0
  125. package/dist/cli/registry.js.map +1 -1
  126. package/dist/cli-delegate/{chunk-A3HJ6NTI.js → chunk-63WSP6YZ.js} +2 -1
  127. package/dist/cli-delegate/{chunk-EJYIGZ3T.js → chunk-BKO2AZ7Z.js} +1 -1
  128. package/dist/cli-delegate/{chunk-GCVFLBP2.js → chunk-H574KOUH.js} +338 -494
  129. package/dist/cli-delegate/chunk-NXFWFAYV.js +339 -0
  130. package/dist/cli-delegate/{chunk-TEGJU2JX.js → chunk-ZXXJLL5K.js} +2 -2
  131. package/dist/cli-delegate/cmd_conformance.js +29 -23
  132. package/dist/cli-delegate/cmd_doctor.js +3 -3
  133. package/dist/cli-delegate/cmd_export.js +4 -4
  134. package/dist/cli-delegate/cmd_preflight.js +2 -2
  135. package/dist/cli-delegate/cmd_prune.js +4 -4
  136. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  137. package/dist/cli-delegate/cmd_sync.js +4 -4
  138. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  139. package/dist/cli-delegate/cmd_validate.js +4 -4
  140. package/dist/discovery/deprecation-report.md +1 -1
  141. package/dist/discovery/discovery-manifest.json +127 -95
  142. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  143. package/dist/discovery/discovery-manifest.summary.md +1 -1
  144. package/dist/discovery/orphan-report.md +1 -1
  145. package/dist/discovery/packs.json +2 -2
  146. package/dist/discovery/trust-report.md +1 -1
  147. package/dist/discovery/workspaces.json +2 -2
  148. package/dist/hooks/dispatch.js +729 -430
  149. package/dist/install/install.mjs +52 -11
  150. package/dist/install/rule_scope.js +18 -2
  151. package/dist/install/rule_scope.js.map +1 -1
  152. package/dist/mcp/registry-manifest.json +2 -2
  153. package/dist/mcp/server.json +2 -2
  154. package/dist/mcp/server.mjs +518 -411
  155. package/dist/router.json +1 -1
  156. package/dist/scripts/_lib/claude_settings_hooks.js +30 -5
  157. package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
  158. package/dist/scripts/_lib/scoped_projection.js +223 -0
  159. package/dist/scripts/_lib/scoped_projection.js.map +1 -0
  160. package/dist/scripts/_lib/surface_tiers.js +125 -0
  161. package/dist/scripts/_lib/surface_tiers.js.map +1 -0
  162. package/dist/scripts/_lib/token_count.js +166 -0
  163. package/dist/scripts/_lib/token_count.js.map +1 -0
  164. package/dist/scripts/_lib/trigger_eval_floors.js +8 -0
  165. package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -1
  166. package/dist/scripts/condense.js +139 -9
  167. package/dist/scripts/condense.js.map +1 -1
  168. package/dist/scripts/project_thin_rules.js +450 -0
  169. package/dist/scripts/project_thin_rules.js.map +1 -0
  170. package/dist/server/routes/workspace.js +3 -1
  171. package/dist/server/routes/workspace.js.map +1 -1
  172. package/dist/server/schemas/settings.js +9 -3
  173. package/dist/server/schemas/settings.js.map +1 -1
  174. package/dist/shared/userMd/utils.js +1 -1
  175. package/dist/shared/userMd/utils.js.map +1 -1
  176. package/dist/ui/assets/index-DmR0mbk4.js +26 -0
  177. package/dist/ui/assets/index-DmR0mbk4.js.map +1 -0
  178. package/dist/ui/index.html +1 -1
  179. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  180. package/package.json +14 -14
  181. package/src/agent-src/templates/AGENTS.md +1 -1
  182. package/src/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
  183. package/src/config/agent-settings.template.yml +61 -11
  184. package/src/config/budget-routing.json +7 -0
  185. package/src/config/ci-local-parity.yml +8 -0
  186. package/src/config/discovery/unassigned-artefacts.yml +23 -25
  187. package/src/config/evaluator-budgets.json +35 -5
  188. package/src/config/gate-coverage.yml +147 -0
  189. package/src/config/gate-violation-baselines.json +25 -0
  190. package/src/install/rule_scope.ts +27 -2
  191. package/src/scripts/_cli/cmd_conformance.ts +13 -24
  192. package/src/scripts/_dispatch.bash +10 -0
  193. package/src/scripts/_lib/claude_settings_hooks.ts +30 -4
  194. package/src/scripts/_lib/gate_baseline.ts +213 -0
  195. package/src/scripts/_lib/scoped_projection.ts +50 -0
  196. package/src/scripts/_lib/tier_budget_routing.ts +360 -0
  197. package/src/scripts/_lib/tool_adapter_registry.ts +8 -1
  198. package/src/scripts/_lib/trigger_eval_floors.ts +9 -0
  199. package/src/scripts/_lib/trigger_routers.ts +17 -5
  200. package/src/scripts/adr/regenerate_index.ts +4 -3
  201. package/src/scripts/ai-video/lib/parse-blueprint.sh +1 -1
  202. package/src/scripts/ai_council/compile_corpus.ts +1 -1
  203. package/src/scripts/anchor_scoring_run.ts +1 -1
  204. package/src/scripts/audit_cloud_compatibility.ts +37 -2
  205. package/src/scripts/audit_initial_context.ts +53 -19
  206. package/src/scripts/audit_likelihood.ts +31 -2
  207. package/src/scripts/audit_user_type_axis.ts +58 -11
  208. package/src/scripts/bench_hook_latency.ts +172 -41
  209. package/src/scripts/bench_persona_placebo.ts +1 -1
  210. package/src/scripts/bench_quality_run.ts +1 -1
  211. package/src/scripts/build_mcp_catalog.ts +12 -1
  212. package/src/scripts/check_always_budget.ts +122 -11
  213. package/src/scripts/check_augment_description_cap.ts +24 -3
  214. package/src/scripts/check_ci_strict_superset.ts +150 -0
  215. package/src/scripts/check_cli_registry_budget_sync.ts +168 -0
  216. package/src/scripts/check_cluster_patterns.ts +100 -0
  217. package/src/scripts/check_consumer_scope_flip.ts +26 -4
  218. package/src/scripts/check_evaluator_budgets.ts +198 -19
  219. package/src/scripts/check_gate_coverage.ts +338 -2
  220. package/src/scripts/check_generator_output_coverage.ts +2 -1
  221. package/src/scripts/check_iron_law_prominence.ts +1 -1
  222. package/src/scripts/check_kernel_rule_bundle.ts +1 -1
  223. package/src/scripts/check_no_new_legacy_path.ts +167 -3
  224. package/src/scripts/check_pack_size.ts +18 -4
  225. package/src/scripts/check_portability.ts +37 -7
  226. package/src/scripts/check_references.ts +19 -3
  227. package/src/scripts/check_release_pr_shape.ts +13 -0
  228. package/src/scripts/check_token_quality_golden.ts +1 -1
  229. package/src/scripts/check_token_regression.ts +9 -2
  230. package/src/scripts/condense.ts +155 -12
  231. package/src/scripts/cost/budget.mjs +77 -2
  232. package/src/scripts/evaluator_umbrella.sh +34 -1
  233. package/src/scripts/generate_capability_matrix.ts +8 -0
  234. package/src/scripts/generate_router_coverage_from_matrix.ts +155 -0
  235. package/src/scripts/hook_manifest.yaml +17 -3
  236. package/src/scripts/hooks/block_config_weakening.ts +337 -0
  237. package/src/scripts/hooks/concern_registry.ts +2 -0
  238. package/src/scripts/hooks/probe_throttle.ts +75 -0
  239. package/src/scripts/install-hooks.sh +24 -2
  240. package/src/scripts/install.ts +2 -2
  241. package/src/scripts/lint_agents_md.ts +9 -3
  242. package/src/scripts/lint_archived_skills.ts +1 -1
  243. package/src/scripts/lint_artefact_frontmatter.ts +185 -37
  244. package/src/scripts/lint_command_routing.ts +21 -52
  245. package/src/scripts/lint_command_verbs.ts +47 -28
  246. package/src/scripts/lint_context_spine_usage.ts +38 -12
  247. package/src/scripts/lint_framework_leakage.ts +25 -6
  248. package/src/scripts/lint_handoffs.ts +49 -2
  249. package/src/scripts/lint_media_policy_linkage.ts +62 -30
  250. package/src/scripts/lint_namespace.ts +92 -7
  251. package/src/scripts/lint_new_skill_gate.ts +39 -31
  252. package/src/scripts/lint_originality_shingles.ts +7 -1
  253. package/src/scripts/lint_override_kernel_guard.ts +21 -3
  254. package/src/scripts/lint_pack_boundaries.ts +461 -91
  255. package/src/scripts/lint_pack_dependencies.ts +24 -57
  256. package/src/scripts/lint_persona_governance.ts +1 -1
  257. package/src/scripts/lint_role_experiences.ts +34 -5
  258. package/src/scripts/lint_trigger_precision.ts +142 -0
  259. package/src/scripts/mcp_server/consumer_tool_catalog.json +12 -201
  260. package/src/scripts/mcp_server/tools.ts +1 -1
  261. package/src/scripts/prepack-check.mjs +40 -1
  262. package/src/scripts/prepack_router_targets.d.mts +7 -0
  263. package/src/scripts/prepack_router_targets.mjs +93 -0
  264. package/src/scripts/print_required_checks.ts +72 -18
  265. package/src/scripts/profile_staleness_hook.ts +12 -0
  266. package/src/scripts/record_evaluator_measurements.mjs +105 -0
  267. package/src/scripts/recruit_preflight.sh +1 -1
  268. package/src/scripts/release.ts +214 -7
  269. package/src/scripts/router_target_paths.d.mts +4 -0
  270. package/src/scripts/router_target_paths.mjs +69 -0
  271. package/src/scripts/router_telemetry.ts +52 -1
  272. package/src/scripts/routing_doctor.ts +560 -0
  273. package/src/scripts/rule_trigger_eval.ts +403 -0
  274. package/src/scripts/schemas/rule.schema.json +5 -1
  275. package/src/scripts/session_canary_hook.ts +96 -5
  276. package/src/scripts/skill_linter.ts +9 -28
  277. package/src/scripts/smoke/skills.sh +1 -1
  278. package/src/scripts/sweep_dead_scan_roots.ts +356 -10
  279. package/src/scripts/trigger_coverage.ts +13 -2
  280. package/src/scripts/update_counts.ts +1 -1
  281. package/src/scripts/validate_telegraph_carveouts.ts +1 -1
  282. package/src/scripts/wrapper_freshness_hook.ts +14 -0
  283. package/src/server/schemas/settings.ts +19 -5
  284. package/src/shared/userMd/utils.ts +1 -1
  285. package/dist/cli-delegate/chunk-TJIAM5FW.js +0 -133
  286. package/dist/ui/assets/index-BpSQt5vm.js +0 -40
  287. package/dist/ui/assets/index-BpSQt5vm.js.map +0 -1
@@ -32,6 +32,7 @@
32
32
  import * as fs from 'node:fs';
33
33
  import * as path from 'node:path';
34
34
 
35
+ import { resolve_rule_pack_scope } from '../scripts/_lib/scoped_projection.js';
35
36
  import { rule_in_scope } from './ruleInScope.js';
36
37
 
37
38
  /**
@@ -67,19 +68,43 @@ function _list(value: unknown): readonly string[] | null {
67
68
  * already-parsed settings object (same key contract as condense's
68
69
  * `_read_projection_list` — absent / empty = legacy-all).
69
70
  */
70
- export function ruleScopeFromSettings(settings: Record<string, unknown>): RuleScope {
71
+ export function ruleScopeFromSettings(
72
+ settings: Record<string, unknown>,
73
+ packageRoot?: string,
74
+ ): RuleScope {
71
75
  const proj = settings['projection'];
72
76
  if (typeof proj !== 'object' || proj === null || Array.isArray(proj)) {
73
77
  return LEGACY_ALL;
74
78
  }
75
79
  const p = proj as Record<string, unknown>;
80
+ // `rule_packs: auto` derives the active-pack set — the SAME set the
81
+ // skill/command prune uses — so the rule axis cannot drift from the
82
+ // artefact axis. Resolving it needs the package root (packs.yml lives
83
+ // there); without one the sentinel degrades to the inactive axis rather
84
+ // than to an empty set, which would prune every pack-tagged rule.
85
+ const packs =
86
+ packageRoot === undefined
87
+ ? _list(p['rule_packs'])
88
+ : resolve_rule_pack_scope(
89
+ p['rule_packs'],
90
+ packageRoot,
91
+ _list(_runtimeActivePacks(settings)) ?? [],
92
+ );
76
93
  return {
77
94
  workspaces: _list(p['rule_workspaces']),
78
- packs: _list(p['rule_packs']),
95
+ packs,
79
96
  roles: _list(p['rule_roles']),
80
97
  };
81
98
  }
82
99
 
100
+ /** `runtime.active_packs` overlay from an already-parsed settings object. */
101
+ function _runtimeActivePacks(settings: Record<string, unknown>): unknown {
102
+ const rt = settings['runtime'];
103
+ return typeof rt === 'object' && rt !== null && !Array.isArray(rt)
104
+ ? (rt as Record<string, unknown>)['active_packs']
105
+ : null;
106
+ }
107
+
83
108
  /**
84
109
  * Whether a rule FILE (path into the shipped `dist/agent-src/rules` tree)
85
110
  * arrives in a consumer install under `scope`.
@@ -38,6 +38,7 @@ import { atomicAppendLine } from '../../install/atomic.js';
38
38
  import { readRecentEntries } from '../../install/txlog.js';
39
39
  import { detectToolPresence } from '../../install/detect.js';
40
40
  import { resolvePackageRoot } from '../_lib/package_root.js';
41
+ import { routeTargetPathsPosix } from '../router_target_paths.mjs';
41
42
  import {
42
43
  _classify,
43
44
  _collect_manifest_entries,
@@ -146,32 +147,20 @@ export function _check_txlog_clean(logPath: string = installLogPath()): Dict {
146
147
  * Map one `routes_to` target (`<kind>:<id>`) to its candidate
147
148
  * package-relative paths. The FIRST existing candidate wins.
148
149
  *
149
- * `contract:` has two legitimate homes in the shipped tree: the public
150
- * contract pages under `docs/contracts/` (per the rule-router contract)
151
- * and the agent-facing contract contexts under
152
- * `dist/agent-src/contexts/contracts/` (e.g. artifact-engagement-flow).
150
+ * The kind→path table itself lives in `../router_target_paths.mjs` and is
151
+ * SHARED with `prepack-check.mjs` gate 4, which asserts every one of these
152
+ * targets lands inside the `files[]` whitelist at pack time. A second copy of
153
+ * the table would let the pack gate and this runtime resolver disagree about
154
+ * where a pointer lives — the packaging↔runtime gap Phase 4 of
155
+ * `road-to-gates-that-can-fail` closes. `contract:`'s two legitimate homes
156
+ * (public `docs/contracts/` pages and agent-facing
157
+ * `dist/agent-src/contexts/contracts/` contexts) are documented there.
153
158
  */
154
159
  export function routeTargetPaths(target: string): string[] {
155
- const sep = target.indexOf(':');
156
- if (sep <= 0) return [];
157
- const kind = target.slice(0, sep);
158
- const id = target.slice(sep + 1);
159
- if (!id) return [];
160
- switch (kind) {
161
- case 'skill':
162
- return [path.join('dist', 'agent-src', 'skills', id, 'SKILL.md')];
163
- case 'command':
164
- return [path.join('dist', 'agent-src', 'commands', `${id}.md`)];
165
- case 'guideline':
166
- return [path.join('docs', 'guidelines', `${id}.md`)];
167
- case 'contract':
168
- return [
169
- path.join('docs', 'contracts', `${id}.md`),
170
- path.join('dist', 'agent-src', 'contexts', 'contracts', `${id}.md`),
171
- ];
172
- default:
173
- return [];
174
- }
160
+ // POSIX templates → platform paths. `path.join` over the split segments
161
+ // reproduces the previous per-kind `path.join(...)` calls exactly,
162
+ // including a slash-bearing id such as `guideline:a/b`.
163
+ return routeTargetPathsPosix(target).map((rel) => path.join(...rel.split('/')));
175
164
  }
176
165
 
177
166
  export function _check_router_pointers(packageRoot: string = PACKAGE_ROOT): Dict {
@@ -203,6 +203,11 @@ Tier 2 — maintenance / internal (hooks, MCP, memory, telemetry):
203
203
  last dispatcher feedback per concern, missing trampolines.
204
204
  Wraps hooks:status. Read-only.
205
205
  Flags: --format json|table, --strict (CI), --project-root <path>
206
+ routing:doctor Live routing diagnosis: per-gate ACTIVE/INACTIVE with the
207
+ concern's own reason (read-only probes), session_start chain,
208
+ router + projection freshness, host bridge status.
209
+ Flags: --platform <p>, --workspace <path>, --json, --strict,
210
+ --no-freshness
206
211
  hooks:replay Replay a fixture through the universal dispatcher with
207
212
  AGENT_CONFIG_REPLAY=1 (no writes under agents/runtime/state/).
208
213
  Usage: hooks:replay --platform <name> --event <event>
@@ -755,6 +760,10 @@ cmd_hooks_doctor() {
755
760
  exec_hook "src/scripts/hooks_doctor" "$@"
756
761
  }
757
762
 
763
+ cmd_routing_doctor() {
764
+ exec_hook "src/scripts/routing_doctor" "$@"
765
+ }
766
+
758
767
  cmd_hooks_replay() {
759
768
  exec_hook "src/scripts/hooks/replay_hook" "$@"
760
769
  }
@@ -1217,6 +1226,7 @@ main() {
1217
1226
  dispatch:hook) cmd_dispatch_hook "$@" ;;
1218
1227
  hooks:status) cmd_hooks_status "$@" ;;
1219
1228
  hooks:doctor) cmd_hooks_doctor "$@" ;;
1229
+ routing:doctor) cmd_routing_doctor "$@" ;;
1220
1230
  hooks:replay) cmd_hooks_replay "$@" ;;
1221
1231
  reach:doctor) cmd_reach_doctor "$@" ;;
1222
1232
  telemetry:record) cmd_telemetry_record "$@" ;;
@@ -108,19 +108,45 @@ export function build_claude_hook_matrix(manifest_path: string): ClaudeHookMatri
108
108
  if (!concerns || (Array.isArray(concerns) && concerns.length === 0)) continue;
109
109
  const native = ac_to_native[ac_event];
110
110
  if (native === undefined) continue;
111
- // Hook-resilience shim (road-to-opt-subagent-harvest P1.4):
111
+ // Bundle fast path (road-to-hook-latency-repair Phase 2): invoke the
112
+ // precompiled dispatcher bundle directly — no bash shim, no CLI boot
113
+ // (measured ~450-500 ms CLI vs ~110 ms bundle per event on a 1-vCPU
114
+ // container; bench_hook_latency --via-cli pins it). Candidates:
115
+ // 1. project npm install (node_modules/@event4u/agent-config/…)
116
+ // 2. a source checkout — guarded by hook_manifest.yaml existing
117
+ // next to it, so an UNRELATED consumer file that happens to sit
118
+ // at dist/hooks/dispatch.js can never be executed as a hook
119
+ // (council 2026-08-03).
120
+ // `exec node` replaces the shell; stdin/exit codes flow through
121
+ // unchanged (a fail_closed concern still denies). Global installs
122
+ // have no project-relative bundle path and take the CLI fallback
123
+ // below, which boots the same bundle in-process via the launcher
124
+ // fast path (src/cli/agent-config.ts).
125
+ //
126
+ // Hook-resilience fallback (road-to-opt-subagent-harvest P1.4):
112
127
  // local binary → PATH fallback → SILENT exit 0 when neither exists
113
128
  // (a hook must never error-spam the loop on an uninstalled repo).
114
129
  // The dispatcher's own exit code PROPAGATES when it runs — a
115
130
  // fail_closed concern (e.g. block-no-verify) still denies; only the
116
131
  // missing-binary case degrades open, which is the honest degrade
117
132
  // (an absent dispatcher cannot evaluate anything).
133
+ // NOTE: the CLI fallback carries MANAGED_SIGNATURE — keep it, or
134
+ // previously installed managed entries orphan on refresh.
135
+ const dispatchArgs =
136
+ `--platform claude --event ${ac_event} --native-event ${native} ` +
137
+ `--project-dir "$CLAUDE_PROJECT_DIR" --min-version ${String(hook_spec)}`;
118
138
  matrix[native] =
139
+ 'B=""; ' +
140
+ '[ -f "$CLAUDE_PROJECT_DIR/node_modules/@event4u/agent-config/dist/hooks/dispatch.js" ] && ' +
141
+ 'B="$CLAUDE_PROJECT_DIR/node_modules/@event4u/agent-config/dist/hooks/dispatch.js"; ' +
142
+ '[ -z "$B" ] && [ -f "$CLAUDE_PROJECT_DIR/dist/hooks/dispatch.js" ] && ' +
143
+ '[ -f "$CLAUDE_PROJECT_DIR/src/scripts/hook_manifest.yaml" ] && ' +
144
+ 'B="$CLAUDE_PROJECT_DIR/dist/hooks/dispatch.js"; ' +
145
+ 'if [ -n "$B" ] && command -v node >/dev/null 2>&1; then ' +
146
+ `exec node "$B" ${dispatchArgs}; fi; ` +
119
147
  'BIN="$CLAUDE_PROJECT_DIR/agent-config"; [ -x "$BIN" ] || BIN=agent-config; ' +
120
148
  'command -v "$BIN" >/dev/null 2>&1 || exit 0; ' +
121
- `"$BIN" dispatch:hook --platform claude --event ${ac_event} ` +
122
- `--native-event ${native} --project-dir "$CLAUDE_PROJECT_DIR" ` +
123
- `--min-version ${String(hook_spec)}`;
149
+ `"$BIN" dispatch:hook ${dispatchArgs}`;
124
150
  }
125
151
  return matrix;
126
152
  }
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Violation ratchet for gates whose scan root was repaired.
3
+ *
4
+ * WHY THIS EXISTS (council 2026-08-02, roadmap `road-to-gates-that-can-fail`
5
+ * blocker `dead-gate-finding-triage`): repairing a dead scan root does not
6
+ * create violations, it REVEALS violations that were invisible while the gate
7
+ * scanned nothing. Landing the repair with a hard fail would turn `main` red on
8
+ * debt the repairing change did not cause, blocking every unrelated merge
9
+ * behind unrelated copy-editing. Suppressing the findings would be the same
10
+ * green-on-nothing failure the repair exists to end.
11
+ *
12
+ * The adopted shape is a ratchet:
13
+ *
14
+ * The repair records the revealed count as a per-gate baseline in
15
+ * `src/config/gate-violation-baselines.json`. The gate fails only when its
16
+ * count rises ABOVE the baseline. Lowering a baseline is a normal commit;
17
+ * raising one is a defect.
18
+ *
19
+ * This is not an allowlist: no individual violation is named or excused, the
20
+ * number is committed, and every change to it shows as a delta in the diff.
21
+ *
22
+ * ## The anti-fossilization clause — why entries expire
23
+ *
24
+ * The council's own strongest objection to the ratchet was that a baseline
25
+ * which never moves hardens from "debt" into "configuration", at which point it
26
+ * IS suppression with extra steps. So an entry is not permanent: it carries the
27
+ * date its repair landed, and after {@link STALE_AFTER_DAYS} days a baseline
28
+ * that has not been lowered fails the gate with an escalation message. Clearing
29
+ * it means one of three visible acts — fix the findings, lower the count, or
30
+ * write a `reaffirmed` block whose non-empty reason lands in the diff.
31
+ *
32
+ * Dates are plain `YYYY-MM-DD`; ages are whole days in UTC, so the verdict does
33
+ * not depend on the machine's timezone.
34
+ */
35
+ import * as fs from 'node:fs';
36
+ import * as path from 'node:path';
37
+
38
+ /** Days a baseline may sit unchanged before it must be fixed or reaffirmed. */
39
+ export const STALE_AFTER_DAYS = 56;
40
+
41
+ /** Repo-relative location of the committed baseline file. */
42
+ export const BASELINE_REL = 'src/config/gate-violation-baselines.json';
43
+
44
+ /** A single gate's recorded debt. */
45
+ export interface BaselineEntry {
46
+ /** Violation count revealed when the scan root was repaired. */
47
+ count: number;
48
+ /** `YYYY-MM-DD` the repair (or the last lowering) landed. */
49
+ landed: string;
50
+ /** What the counted violations are, so a reader need not re-derive them. */
51
+ note: string;
52
+ /** Optional clock reset — requires a reason, and the reason lands in the diff. */
53
+ reaffirmed?: { date: string; reason: string };
54
+ }
55
+
56
+ export interface BaselineFile {
57
+ /** Human-facing pointer back to the rule that governs this file. */
58
+ $comment?: string;
59
+ gates: Record<string, BaselineEntry>;
60
+ }
61
+
62
+ export type BaselineStatus =
63
+ /** Count is at or under the baseline and the entry is still fresh. */
64
+ | 'within'
65
+ /** Count is below the baseline — the ratchet should be tightened. */
66
+ | 'improved'
67
+ /** Count rose above the baseline. This is the defect the ratchet catches. */
68
+ | 'regressed'
69
+ /** Count is within, but the entry has sat unchanged past the expiry. */
70
+ | 'stale'
71
+ /** No baseline recorded — zero violations is the only passing state. */
72
+ | 'unbaselined';
73
+
74
+ export interface BaselineVerdict {
75
+ status: BaselineStatus;
76
+ /** True when the gate may exit 0. */
77
+ ok: boolean;
78
+ /** The effective baseline (0 when no entry exists). */
79
+ baseline: number;
80
+ actual: number;
81
+ /** Whole days since `landed` (or since `reaffirmed.date` when present). */
82
+ ageDays: number | null;
83
+ /** One line, ready for stdout (ok) or stderr (not ok). */
84
+ message: string;
85
+ }
86
+
87
+ function parseIsoDate(value: string): number | null {
88
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
89
+ return null;
90
+ }
91
+ const ms = Date.parse(`${value}T00:00:00Z`);
92
+ return Number.isNaN(ms) ? null : ms;
93
+ }
94
+
95
+ function wholeDaysBetween(fromMs: number, toMs: number): number {
96
+ return Math.floor((toMs - fromMs) / 86_400_000);
97
+ }
98
+
99
+ /** Read the committed baseline file. A missing file is an empty ratchet, not an error. */
100
+ export function loadBaselines(repoRoot: string, relPath: string = BASELINE_REL): BaselineFile {
101
+ const abs = path.join(repoRoot, relPath);
102
+ let raw: string;
103
+ try {
104
+ raw = fs.readFileSync(abs, 'utf8');
105
+ } catch {
106
+ return { gates: {} };
107
+ }
108
+ const parsed = JSON.parse(raw) as Partial<BaselineFile>;
109
+ const out: BaselineFile = { gates: parsed.gates ?? {} };
110
+ if (parsed.$comment !== undefined) out.$comment = parsed.$comment;
111
+ return out;
112
+ }
113
+
114
+ export interface RatchetOptions {
115
+ /** Gate name — the key in the baseline file. */
116
+ gate: string;
117
+ /** How many violations this run found. */
118
+ actual: number;
119
+ /** Absolute repo root. */
120
+ repoRoot: string;
121
+ /** Override the baseline file location (tests). */
122
+ baselineRel?: string;
123
+ /** Override "now" as `YYYY-MM-DD` (tests); defaults to the current UTC date. */
124
+ today?: string;
125
+ }
126
+
127
+ /**
128
+ * Judge a violation count against its recorded baseline.
129
+ *
130
+ * Callers keep their own reporting and exit codes — this returns a verdict, it
131
+ * does not print or exit. A gate with no entry behaves exactly as it did before
132
+ * the ratchet existed: any violation fails.
133
+ */
134
+ export function checkRatchet(opts: RatchetOptions): BaselineVerdict {
135
+ const { gate, actual } = opts;
136
+ const file = loadBaselines(opts.repoRoot, opts.baselineRel ?? BASELINE_REL);
137
+ const entry = file.gates[gate];
138
+
139
+ if (entry === undefined) {
140
+ return {
141
+ status: 'unbaselined',
142
+ ok: actual === 0,
143
+ baseline: 0,
144
+ actual,
145
+ ageDays: null,
146
+ message:
147
+ actual === 0
148
+ ? `${gate}: 0 violation(s), no baseline needed.`
149
+ : `${gate}: ${actual} violation(s) and no recorded baseline — fix them, or ` +
150
+ `record the count in ${BASELINE_REL} if this is a scan-root repair ` +
151
+ 'revealing pre-existing debt.',
152
+ };
153
+ }
154
+
155
+ const anchor = entry.reaffirmed?.date ?? entry.landed;
156
+ const anchorMs = parseIsoDate(anchor);
157
+ const todayMs = parseIsoDate(opts.today ?? new Date().toISOString().slice(0, 10));
158
+ const ageDays = anchorMs !== null && todayMs !== null ? wholeDaysBetween(anchorMs, todayMs) : null;
159
+
160
+ if (actual > entry.count) {
161
+ return {
162
+ status: 'regressed',
163
+ ok: false,
164
+ baseline: entry.count,
165
+ actual,
166
+ ageDays,
167
+ message:
168
+ `${gate}: ${actual} violation(s) against a baseline of ${entry.count} — ` +
169
+ `${actual - entry.count} new. A ratchet only turns one way: fix the new ` +
170
+ `violation(s). Raising the baseline in ${BASELINE_REL} is a defect, not a fix.`,
171
+ };
172
+ }
173
+
174
+ if (actual < entry.count) {
175
+ return {
176
+ status: 'improved',
177
+ ok: true,
178
+ baseline: entry.count,
179
+ actual,
180
+ ageDays,
181
+ message:
182
+ `${gate}: ${actual} violation(s), baseline ${entry.count} — ratchet is loose. ` +
183
+ `Lower it to ${actual} in ${BASELINE_REL} (and refresh \`landed\`) so the ` +
184
+ 'gain cannot be silently given back.',
185
+ };
186
+ }
187
+
188
+ if (ageDays !== null && ageDays > STALE_AFTER_DAYS) {
189
+ return {
190
+ status: 'stale',
191
+ ok: false,
192
+ baseline: entry.count,
193
+ actual,
194
+ ageDays,
195
+ message:
196
+ `${gate}: baseline of ${entry.count} has not moved in ${ageDays} days ` +
197
+ `(limit ${STALE_AFTER_DAYS}). A baseline that never drops is suppression with ` +
198
+ 'extra steps. Fix the findings, lower the count, or add a `reaffirmed` block ' +
199
+ `with a reason in ${BASELINE_REL}. Debt: ${entry.note}`,
200
+ };
201
+ }
202
+
203
+ return {
204
+ status: 'within',
205
+ ok: true,
206
+ baseline: entry.count,
207
+ actual,
208
+ ageDays,
209
+ message:
210
+ `${gate}: ${actual} violation(s) at baseline${ageDays === null ? '' : `, age ${ageDays}d`} ` +
211
+ `— known debt, not new breakage. ${entry.note}`,
212
+ };
213
+ }
@@ -160,6 +160,56 @@ export function is_pruned_under_scoped(md_path: string, active_ids: ReadonlySet<
160
160
  return true;
161
161
  }
162
162
 
163
+ /**
164
+ * Sentinel value for `projection.rule_packs`: derive the pack scope from the
165
+ * active-pack set instead of listing ids by hand.
166
+ *
167
+ * The rule layer's pack axis was wired end-to-end but shipped inactive
168
+ * (`rule_packs: []`), so a rule whose own body says "auto-activates when
169
+ * pack-X is installed" projected into installs that do not have pack-X,
170
+ * where it guards nothing. Listing the active ids by hand is not a fix — the
171
+ * list would need re-typing whenever a pack is added, which is the
172
+ * two-counting-paths failure this module exists to prevent. `auto` reuses
173
+ * `compute_active_pack_ids`, the SAME set the skill/command prune uses, so
174
+ * the rule axis cannot drift from the artefact axis.
175
+ */
176
+ export const RULE_PACKS_AUTO = 'auto';
177
+
178
+ /**
179
+ * Resolve a raw `projection.rule_packs` setting into the pack scope
180
+ * `rule_in_scope` consumes.
181
+ *
182
+ * - `'auto'` (scalar or single-element list) → the derived active-pack set.
183
+ * - a non-empty list → that list, verbatim.
184
+ * - anything else (absent, `[]`, wrong type) → `null`, axis inactive.
185
+ *
186
+ * A derivation failure (unreadable / malformed `packs.yml`) returns `null`,
187
+ * not an empty set: an empty set would prune every pack-tagged rule, the
188
+ * opposite of the fail-safe contract `rule_in_scope` documents.
189
+ */
190
+ export function resolve_rule_pack_scope(
191
+ raw: unknown,
192
+ package_root: string,
193
+ runtime_active_packs: readonly string[] = [],
194
+ ): string[] | null {
195
+ const is_auto =
196
+ raw === RULE_PACKS_AUTO ||
197
+ (Array.isArray(raw) && raw.length === 1 && raw[0] === RULE_PACKS_AUTO);
198
+ if (is_auto) {
199
+ try {
200
+ return [
201
+ ...compute_active_pack_ids(load_packs_registry(package_root), runtime_active_packs),
202
+ ].sort();
203
+ } catch {
204
+ return null;
205
+ }
206
+ }
207
+ if (Array.isArray(raw) && raw.length > 0) {
208
+ return raw.map((v) => String(v));
209
+ }
210
+ return null;
211
+ }
212
+
163
213
  export interface ScopedProjectionStats {
164
214
  /** Skills a default `projection.mode: scoped` install deploys. */
165
215
  projected: number;