@event4u/agent-config 9.13.0 → 9.15.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 (280) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +113 -67
  3. package/README.md +9 -1
  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 +150 -0
  15. package/dist/agent-src/commands/optimize/skills.md +5 -5
  16. package/dist/agent-src/commands/optimize.md +12 -6
  17. package/dist/agent-src/commands/override.md +1 -0
  18. package/dist/agent-src/commands/prediction-pool.md +4 -4
  19. package/dist/agent-src/commands/research/deep.md +1 -1
  20. package/dist/agent-src/commands/roadmap.md +2 -1
  21. package/dist/agent-src/commands/rule-compliance-audit.md +1 -1
  22. package/dist/agent-src/commands/tdd.md +1 -0
  23. package/dist/agent-src/commands/tests.md +1 -0
  24. package/dist/agent-src/commands/upstream-contribute.md +5 -5
  25. package/dist/agent-src/commands/video/from-script.md +1 -1
  26. package/dist/agent-src/commands/video/from-song.md +1 -1
  27. package/dist/agent-src/commands/video/scene.md +1 -1
  28. package/dist/agent-src/commands/video/stitch.md +1 -1
  29. package/dist/agent-src/commands/video/storyboard.md +1 -1
  30. package/dist/agent-src/commands/worktree/create.md +2 -1
  31. package/dist/agent-src/contexts/augment-infrastructure.md +1 -1
  32. package/dist/agent-src/contexts/contracts/artifact-engagement-flow.md +5 -5
  33. package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +3 -3
  34. package/dist/agent-src/contexts/contracts/emergency-triage-block.md +2 -2
  35. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +4 -3
  36. package/dist/agent-src/contexts/execution/subagent-routing.md +19 -0
  37. package/dist/agent-src/contexts/override-system.md +1 -1
  38. package/dist/agent-src/ghostwriter/fictional-fixture-v1.md +1 -1
  39. package/dist/agent-src/rules/augment-edit-discipline.md +1 -1
  40. package/dist/agent-src/rules/autonomous-execution.md +2 -1
  41. package/dist/agent-src/rules/brand-consistency.md +1 -0
  42. package/dist/agent-src/rules/brand-source-of-truth.md +1 -0
  43. package/dist/agent-src/rules/code-comment-discipline.md +2 -0
  44. package/dist/agent-src/rules/communication-through-line.md +1 -0
  45. package/dist/agent-src/rules/content-quoting-floor.md +1 -0
  46. package/dist/agent-src/rules/cross-source-consistency.md +0 -1
  47. package/dist/agent-src/rules/downstream-changes.md +1 -0
  48. package/dist/agent-src/rules/external-code-graph-interop.md +1 -0
  49. package/dist/agent-src/rules/external-reference-deep-dive.md +1 -0
  50. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  51. package/dist/agent-src/rules/finance-safety-floor.md +0 -1
  52. package/dist/agent-src/rules/icon-consistency.md +1 -0
  53. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  54. package/dist/agent-src/rules/improve-before-implement.md +1 -1
  55. package/dist/agent-src/rules/invite-challenge.md +1 -0
  56. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  57. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  58. package/dist/agent-src/rules/markdown-safe-codeblocks.md +1 -0
  59. package/dist/agent-src/rules/media-governance-routing.md +4 -3
  60. package/dist/agent-src/rules/media-sync-ground-truth.md +1 -0
  61. package/dist/agent-src/rules/no-attribution-footers.md +1 -0
  62. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +1 -0
  63. package/dist/agent-src/rules/no-pr-progress-comments.md +1 -0
  64. package/dist/agent-src/rules/onboarding-gate.md +1 -0
  65. package/dist/agent-src/rules/output-discipline.md +1 -0
  66. package/dist/agent-src/rules/prefer-enums-over-literals.md +1 -0
  67. package/dist/agent-src/rules/preservation-guard.md +1 -0
  68. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  69. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +1 -0
  70. package/dist/agent-src/rules/role-mode-adherence.md +1 -0
  71. package/dist/agent-src/rules/runtime-safety.md +1 -0
  72. package/dist/agent-src/rules/security-sensitive-stop.md +1 -0
  73. package/dist/agent-src/rules/session-canary.md +9 -2
  74. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  75. package/dist/agent-src/rules/source-of-truth.md +1 -0
  76. package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -0
  77. package/dist/agent-src/rules/think-before-action.md +1 -0
  78. package/dist/agent-src/rules/token-budget-discipline.md +1 -0
  79. package/dist/agent-src/rules/token-efficiency.md +1 -0
  80. package/dist/agent-src/rules/token-optimizer-maintenance.md +5 -5
  81. package/dist/agent-src/rules/tool-safety.md +1 -0
  82. package/dist/agent-src/rules/untrusted-input-defense.md +2 -0
  83. package/dist/agent-src/rules/user-interaction.md +1 -0
  84. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  85. package/dist/agent-src/skills/adr-create/SKILL.md +1 -1
  86. package/dist/agent-src/skills/check-refs/SKILL.md +1 -1
  87. package/dist/agent-src/skills/competitive-positioning/SKILL.md +1 -1
  88. package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
  89. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  90. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +1 -1
  91. package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
  92. package/dist/agent-src/skills/github-ci/SKILL.md +1 -0
  93. package/dist/agent-src/skills/guideline-writing/SKILL.md +1 -1
  94. package/dist/agent-src/skills/lint-skills/SKILL.md +1 -1
  95. package/dist/agent-src/skills/readme-reviewer/SKILL.md +1 -1
  96. package/dist/agent-src/skills/roadmap-management/SKILL.md +8 -1
  97. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  98. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  99. package/dist/agent-src/skills/skill-writing/SKILL.md +4 -4
  100. package/dist/agent-src/skills/token-optimizer/SKILL.md +4 -4
  101. package/dist/agent-src/skills/upstream-contribute/SKILL.md +6 -6
  102. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +25 -0
  103. package/dist/agent-src/templates/AGENTS.md +1 -1
  104. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  105. package/dist/agent-src/templates/command.md +1 -1
  106. package/dist/agent-src/templates/copilot-instructions.md +3 -3
  107. package/dist/agent-src/templates/rule.md +2 -2
  108. package/dist/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
  109. package/dist/agent-src/templates/skill-archive-note.md +5 -5
  110. package/dist/agent-src/templates/skill.md +1 -1
  111. package/dist/cli/agent-config.js +66 -19
  112. package/dist/cli/agent-config.js.map +1 -1
  113. package/dist/cli/main.js +9 -4
  114. package/dist/cli/main.js.map +1 -1
  115. package/dist/cli/python/workspace_drive.js +2 -0
  116. package/dist/cli/python/workspace_drive.js.map +1 -1
  117. package/dist/cli/python/workspace_drive_health.js +2 -0
  118. package/dist/cli/python/workspace_drive_health.js.map +1 -1
  119. package/dist/cli/python/workspace_hosts.js +2 -0
  120. package/dist/cli/python/workspace_hosts.js.map +1 -1
  121. package/dist/cli/python/workspace_sessions.js +2 -0
  122. package/dist/cli/python/workspace_sessions.js.map +1 -1
  123. package/dist/cli/registry.js +1 -0
  124. package/dist/cli/registry.js.map +1 -1
  125. package/dist/cli-delegate/{chunk-A3HJ6NTI.js → chunk-63WSP6YZ.js} +2 -1
  126. package/dist/cli-delegate/{chunk-EJYIGZ3T.js → chunk-BKO2AZ7Z.js} +1 -1
  127. package/dist/cli-delegate/{chunk-QFKQLQ7X.js → chunk-H574KOUH.js} +340 -462
  128. package/dist/cli-delegate/chunk-NXFWFAYV.js +339 -0
  129. package/dist/cli-delegate/{chunk-TEGJU2JX.js → chunk-ZXXJLL5K.js} +2 -2
  130. package/dist/cli-delegate/cmd_conformance.js +29 -23
  131. package/dist/cli-delegate/cmd_doctor.js +3 -3
  132. package/dist/cli-delegate/cmd_export.js +4 -4
  133. package/dist/cli-delegate/cmd_preflight.js +2 -2
  134. package/dist/cli-delegate/cmd_prune.js +4 -4
  135. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  136. package/dist/cli-delegate/cmd_sync.js +4 -4
  137. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  138. package/dist/cli-delegate/cmd_validate.js +4 -4
  139. package/dist/discovery/deprecation-report.md +1 -1
  140. package/dist/discovery/discovery-manifest.json +163 -103
  141. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  142. package/dist/discovery/discovery-manifest.summary.md +3 -3
  143. package/dist/discovery/orphan-report.md +1 -1
  144. package/dist/discovery/packs.json +6 -5
  145. package/dist/discovery/trust-report.md +3 -3
  146. package/dist/discovery/workspaces.json +5 -4
  147. package/dist/hooks/dispatch.js +729 -430
  148. package/dist/install/install.mjs +700 -625
  149. package/dist/install/rule_scope.js +18 -2
  150. package/dist/install/rule_scope.js.map +1 -1
  151. package/dist/mcp/registry-manifest.json +3 -3
  152. package/dist/mcp/server.json +2 -2
  153. package/dist/mcp/server.mjs +564 -424
  154. package/dist/router.json +1 -1
  155. package/dist/scripts/_lib/claude_settings_hooks.js +30 -5
  156. package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
  157. package/dist/scripts/_lib/scoped_projection.js +223 -0
  158. package/dist/scripts/_lib/scoped_projection.js.map +1 -0
  159. package/dist/scripts/_lib/surface_tiers.js +125 -0
  160. package/dist/scripts/_lib/surface_tiers.js.map +1 -0
  161. package/dist/scripts/_lib/token_count.js +166 -0
  162. package/dist/scripts/_lib/token_count.js.map +1 -0
  163. package/dist/scripts/_lib/trigger_eval_floors.js +8 -0
  164. package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -1
  165. package/dist/scripts/condense.js +139 -9
  166. package/dist/scripts/condense.js.map +1 -1
  167. package/dist/scripts/project_thin_rules.js +450 -0
  168. package/dist/scripts/project_thin_rules.js.map +1 -0
  169. package/dist/server/routes/workspace.js +2 -0
  170. package/dist/server/routes/workspace.js.map +1 -1
  171. package/dist/server/schemas/settings.js +9 -3
  172. package/dist/server/schemas/settings.js.map +1 -1
  173. package/dist/ui/assets/index-DJY_vQVn.js +40 -0
  174. package/dist/ui/assets/index-DJY_vQVn.js.map +1 -0
  175. package/dist/ui/index.html +1 -1
  176. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  177. package/package.json +11 -11
  178. package/src/agent-src/templates/AGENTS.md +1 -1
  179. package/src/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
  180. package/src/config/agent-settings.template.yml +59 -11
  181. package/src/config/ci-local-parity.yml +8 -0
  182. package/src/config/discovery/unassigned-artefacts.yml +23 -25
  183. package/src/config/evaluator-budgets.json +35 -5
  184. package/src/config/gate-coverage.yml +147 -0
  185. package/src/config/gate-violation-baselines.json +25 -0
  186. package/src/install/rule_scope.ts +27 -2
  187. package/src/scripts/_cli/cmd_conformance.ts +13 -24
  188. package/src/scripts/_dispatch.bash +10 -0
  189. package/src/scripts/_lib/claude_settings_hooks.ts +30 -4
  190. package/src/scripts/_lib/exec_evidence.ts +5 -0
  191. package/src/scripts/_lib/gate_baseline.ts +213 -0
  192. package/src/scripts/_lib/scoped_projection.ts +262 -0
  193. package/src/scripts/_lib/tier_budget_routing.ts +234 -0
  194. package/src/scripts/_lib/tool_adapter_registry.ts +8 -1
  195. package/src/scripts/_lib/trigger_eval_floors.ts +9 -0
  196. package/src/scripts/_lib/trigger_routers.ts +17 -5
  197. package/src/scripts/adr/regenerate_index.ts +4 -3
  198. package/src/scripts/ai-video/lib/parse-blueprint.sh +1 -1
  199. package/src/scripts/ai_council/compile_corpus.ts +1 -1
  200. package/src/scripts/audit_cloud_compatibility.ts +37 -2
  201. package/src/scripts/audit_initial_context.ts +53 -19
  202. package/src/scripts/audit_likelihood.ts +31 -2
  203. package/src/scripts/audit_user_type_axis.ts +58 -11
  204. package/src/scripts/bench_hook_latency.ts +172 -41
  205. package/src/scripts/build_mcp_catalog.ts +12 -1
  206. package/src/scripts/check_always_budget.ts +122 -11
  207. package/src/scripts/check_artefact_count_messaging.ts +128 -15
  208. package/src/scripts/check_augment_description_cap.ts +24 -3
  209. package/src/scripts/check_ci_strict_superset.ts +150 -0
  210. package/src/scripts/check_cluster_patterns.ts +100 -0
  211. package/src/scripts/check_consumer_scope_flip.ts +26 -4
  212. package/src/scripts/check_evaluator_budgets.ts +198 -19
  213. package/src/scripts/check_gate_coverage.ts +337 -1
  214. package/src/scripts/check_generator_output_coverage.ts +2 -1
  215. package/src/scripts/check_iron_law_prominence.ts +1 -1
  216. package/src/scripts/check_kernel_rule_bundle.ts +1 -1
  217. package/src/scripts/check_no_new_legacy_path.ts +167 -3
  218. package/src/scripts/check_pack_size.ts +18 -4
  219. package/src/scripts/check_portability.ts +37 -7
  220. package/src/scripts/check_references.ts +19 -3
  221. package/src/scripts/check_release_pr_shape.ts +13 -0
  222. package/src/scripts/check_token_regression.ts +9 -2
  223. package/src/scripts/condense.ts +155 -12
  224. package/src/scripts/cost/budget.mjs +63 -2
  225. package/src/scripts/count_scoped_projection.ts +104 -0
  226. package/src/scripts/evaluator_umbrella.sh +34 -1
  227. package/src/scripts/generate_capability_matrix.ts +8 -0
  228. package/src/scripts/generate_router_coverage_from_matrix.ts +155 -0
  229. package/src/scripts/hook_manifest.yaml +17 -3
  230. package/src/scripts/hooks/block_config_weakening.ts +337 -0
  231. package/src/scripts/hooks/concern_registry.ts +2 -0
  232. package/src/scripts/hooks/probe_throttle.ts +75 -0
  233. package/src/scripts/install-hooks.sh +24 -2
  234. package/src/scripts/install.ts +15 -92
  235. package/src/scripts/lint_agents_md.ts +9 -3
  236. package/src/scripts/lint_archived_skills.ts +1 -1
  237. package/src/scripts/lint_artefact_frontmatter.ts +185 -37
  238. package/src/scripts/lint_command_routing.ts +21 -52
  239. package/src/scripts/lint_command_verbs.ts +47 -28
  240. package/src/scripts/lint_context_spine_usage.ts +38 -12
  241. package/src/scripts/lint_framework_leakage.ts +25 -6
  242. package/src/scripts/lint_handoffs.ts +49 -2
  243. package/src/scripts/lint_media_policy_linkage.ts +62 -30
  244. package/src/scripts/lint_namespace.ts +92 -7
  245. package/src/scripts/lint_new_skill_gate.ts +39 -31
  246. package/src/scripts/lint_originality_shingles.ts +7 -1
  247. package/src/scripts/lint_override_kernel_guard.ts +21 -3
  248. package/src/scripts/lint_pack_boundaries.ts +461 -91
  249. package/src/scripts/lint_pack_dependencies.ts +24 -57
  250. package/src/scripts/lint_persona_governance.ts +1 -1
  251. package/src/scripts/lint_role_experiences.ts +34 -5
  252. package/src/scripts/lint_trigger_precision.ts +142 -0
  253. package/src/scripts/mcp_server/consumer_tool_catalog.json +12 -201
  254. package/src/scripts/mcp_server/tools.ts +1 -1
  255. package/src/scripts/prepack-check.mjs +40 -1
  256. package/src/scripts/prepack_router_targets.d.mts +7 -0
  257. package/src/scripts/prepack_router_targets.mjs +93 -0
  258. package/src/scripts/print_required_checks.ts +72 -18
  259. package/src/scripts/profile_staleness_hook.ts +12 -0
  260. package/src/scripts/record_evaluator_measurements.mjs +105 -0
  261. package/src/scripts/recruit_preflight.sh +1 -1
  262. package/src/scripts/release.ts +105 -1
  263. package/src/scripts/router_target_paths.d.mts +4 -0
  264. package/src/scripts/router_target_paths.mjs +69 -0
  265. package/src/scripts/router_telemetry.ts +52 -1
  266. package/src/scripts/routing_doctor.ts +485 -0
  267. package/src/scripts/rule_trigger_eval.ts +403 -0
  268. package/src/scripts/schemas/rule.schema.json +5 -1
  269. package/src/scripts/session_canary_hook.ts +96 -5
  270. package/src/scripts/skill_linter.ts +90 -42
  271. package/src/scripts/smoke/skills.sh +1 -1
  272. package/src/scripts/sweep_dead_scan_roots.ts +356 -10
  273. package/src/scripts/trigger_coverage.ts +13 -2
  274. package/src/scripts/update_counts.ts +91 -12
  275. package/src/scripts/validate_telegraph_carveouts.ts +1 -1
  276. package/src/scripts/wrapper_freshness_hook.ts +14 -0
  277. package/src/server/schemas/settings.ts +19 -5
  278. package/dist/cli-delegate/chunk-TJIAM5FW.js +0 -133
  279. package/dist/ui/assets/index-BpSQt5vm.js +0 -40
  280. package/dist/ui/assets/index-BpSQt5vm.js.map +0 -1
@@ -25,10 +25,10 @@ import path from "node:path";
25
25
  import { fileURLToPath, pathToFileURL } from "node:url";
26
26
  import { parse as parseYaml } from "yaml";
27
27
  import { SRC_AGENT, iter_all_sources } from "./_lib/agent_src.js";
28
+ import { DeadScopeError, assertScanned } from "./_lib/scan_scope.js";
28
29
 
29
30
  // src/scripts/lint_pack_dependencies.ts → two levels up is the repo root.
30
31
  const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
31
- const PACKAGES = path.join(REPO, "packages");
32
32
  const SRC_DOMAINS = path.join(REPO, "src", "domains");
33
33
  const SRC_PACKS = path.join(REPO, "src", "packs");
34
34
  const PACKAGE_JSON = path.join(REPO, "package.json");
@@ -102,16 +102,7 @@ function _gpm_read_frontmatter(p: string): YamlObject {
102
102
  return isObject(data) ? data : {};
103
103
  }
104
104
 
105
- function _gpm_pack_id_from_dir(pkgDir: string): string {
106
- const name = path.basename(pkgDir);
107
- return name === "core" ? "core" : removePrefix(name, "pack-");
108
- }
109
-
110
- function removePrefix(s: string, prefix: string): string {
111
- return s.startsWith(prefix) ? s.slice(prefix.length) : s;
112
- }
113
-
114
- type PackHome = [string, string, "core" | "flat" | "physical"];
105
+ type PackHome = [string, string, "core" | "flat"];
115
106
 
116
107
  function _gpm_pack_homes(): PackHome[] {
117
108
  const homes: PackHome[] = [];
@@ -137,20 +128,6 @@ function _gpm_pack_homes(): PackHome[] {
137
128
  seen.add(pid);
138
129
  }
139
130
  }
140
- if (_exists(PACKAGES)) {
141
- for (const pkg of sortedDirEntries(PACKAGES)) {
142
- const full = path.join(PACKAGES, pkg);
143
- if (!_isDir(full)) {
144
- continue;
145
- }
146
- const pid = _gpm_pack_id_from_dir(full);
147
- if (seen.has(pid)) {
148
- continue;
149
- }
150
- homes.push([pid, full, "physical"]);
151
- seen.add(pid);
152
- }
153
- }
154
131
  return homes;
155
132
  }
156
133
 
@@ -246,25 +223,6 @@ function asStringList(v: YamlValue | undefined): string[] {
246
223
  return v.filter((x): x is string => typeof x === "string");
247
224
  }
248
225
 
249
- function _gpm_collect_physical(pkgDir: string): ArtefactRecord[] {
250
- const srcRoot = path.join(pkgDir, ".agent-src.uncondensed");
251
- if (!_isDir(srcRoot)) {
252
- return [];
253
- }
254
- const items: ArtefactRecord[] = [];
255
- for (const p of rglobMd(srcRoot)) {
256
- if (!_isFile(p)) {
257
- continue;
258
- }
259
- const fm = _gpm_read_frontmatter(p);
260
- if (Object.keys(fm).length === 0) {
261
- continue;
262
- }
263
- items.push(_gpm_artefact_record(p, relPosix(p, srcRoot), fm));
264
- }
265
- return items;
266
- }
267
-
268
226
  function _gpm_collect_core(): ArtefactRecord[] {
269
227
  const items: ArtefactRecord[] = [];
270
228
  const seen = new Set<string>();
@@ -314,15 +272,11 @@ function _gpm_collect_flat(pid: string): ArtefactRecord[] {
314
272
 
315
273
  function _gpm_collect_artefacts(
316
274
  pid: string,
317
- homeDir: string,
318
- mode: "core" | "flat" | "physical",
275
+ mode: "core" | "flat",
319
276
  ): ArtefactRecord[] {
320
277
  if (mode === "core") {
321
278
  return _gpm_collect_core();
322
279
  }
323
- if (mode === "physical") {
324
- return _gpm_collect_physical(homeDir);
325
- }
326
280
  return _gpm_collect_flat(pid);
327
281
  }
328
282
 
@@ -379,7 +333,7 @@ function _dependency_drift(): string[] {
379
333
  if (!_exists(manifest)) {
380
334
  continue;
381
335
  }
382
- const artefacts = _gpm_collect_artefacts(pid, homeDir, mode);
336
+ const artefacts = _gpm_collect_artefacts(pid, mode);
383
337
  const expected = _gpm_build_dependencies(artefacts);
384
338
  let onDisk: YamlObject;
385
339
  try {
@@ -489,12 +443,23 @@ function _find_cycle(graph: Map<string, Set<string>>): string[] | null {
489
443
  }
490
444
 
491
445
  export function main(): number {
492
- // Gate on the resolved home set, not on packages/ existing.
493
- if (_gpm_pack_homes().length === 0) {
494
- process.stdout.write(
495
- "no pack homes (src/packs, src/domains, packages/) — nothing to lint\n",
496
- );
497
- return 0;
446
+ // Scope assertion: zero resolved pack homes means both roots moved, not that
447
+ // every pack is in sync. The previous soft skip printed a green line in
448
+ // exactly that state — the defect class this gate is being hardened against.
449
+ const homes = _gpm_pack_homes();
450
+ try {
451
+ assertScanned({
452
+ gate: "lint_pack_dependencies",
453
+ scanned: homes.length,
454
+ units: "pack home(s)",
455
+ roots: ["src/packs", "src/domains"],
456
+ });
457
+ } catch (exc) {
458
+ if (!(exc instanceof DeadScopeError)) {
459
+ throw exc;
460
+ }
461
+ process.stderr.write(`❌ ${exc.message}\n`);
462
+ return 2;
498
463
  }
499
464
  const errors = _dependency_drift();
500
465
  const cycle = _find_cycle(_pack_requires_graph());
@@ -510,7 +475,9 @@ export function main(): number {
510
475
  process.stderr.write(`\n${errors.length} pack-dependency violation(s).\n`);
511
476
  return 1;
512
477
  }
513
- process.stdout.write("✅ pack dependencies in sync; pack-graph is acyclic.\n");
478
+ process.stdout.write(
479
+ `✅ pack dependencies in sync; pack-graph is acyclic (${homes.length} pack home(s) scanned).\n`,
480
+ );
514
481
  return 0;
515
482
  }
516
483
 
@@ -9,7 +9,7 @@
9
9
  * stderr), exit codes. Historical quirks are preserved deliberately — tests and downstream consumers pin the exact behaviour.
10
10
  *
11
11
  * Enforces the mechanical checks in
12
- * `.agent-src.uncondensed/rules/persona-governance.md`:
12
+ * `src/rules/persona-governance.md`:
13
13
  *
14
14
  * 1. Per-domain cap (HARD) — ≤ 2 active specialist personas per domain.
15
15
  * 2. Skill citation floor (WARN) — every active specialist persona SHOULD
@@ -18,14 +18,24 @@ import * as path from 'node:path';
18
18
  import { fileURLToPath, pathToFileURL } from 'node:url';
19
19
  import { parse as parseYaml } from 'yaml';
20
20
 
21
+ import { SRC_SKILLS } from './_lib/agent_src.js';
22
+ import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
23
+
21
24
  const _HERE = fileURLToPath(import.meta.url);
22
25
 
23
26
  const ROOT = path.resolve(path.dirname(_HERE), '..', '..');
24
27
  const ROLES_DIR = path.join(ROOT, 'agents', 'roles');
25
- const SKILL_SOURCES = [
26
- path.join(ROOT, '.agent-src.uncondensed', 'skills'),
27
- path.join(ROOT, 'dist/agent-src', 'skills'),
28
- ];
28
+ /**
29
+ * Where a referenced skill slug may be resolved, in precedence order.
30
+ *
31
+ * The first entry used to be the pre-ADR-051 source container, which has not
32
+ * existed since the move — so on a checkout with no built `dist/`,
33
+ * `all_skills()` returned an EMPTY set and every `skills.yml` reference
34
+ * resolved against nothing. `src/skills` is the source of truth; the `dist/`
35
+ * projection stays as a second source so the gate also works from an
36
+ * installed tree.
37
+ */
38
+ const SKILL_SOURCES = [SRC_SKILLS(), path.join(ROOT, 'dist/agent-src', 'skills')];
29
39
 
30
40
  const REQUIRED_INDEX_KEYS = new Set([
31
41
  'role',
@@ -260,6 +270,23 @@ function main(argv?: readonly string[]): number {
260
270
  }
261
271
 
262
272
  const knownSkills = all_skills();
273
+ // Scope assertion: an empty skill index is not "no unknown skills" — it
274
+ // means both sources moved, and every `skills.yml` reference is then
275
+ // checked against nothing.
276
+ try {
277
+ assertScanned({
278
+ gate: 'lint_role_experiences',
279
+ scanned: knownSkills.size,
280
+ units: 'known skill(s)',
281
+ roots: SKILL_SOURCES.map((s) => path.relative(ROOT, s).split(path.sep).join('/')),
282
+ });
283
+ } catch (exc) {
284
+ if (!(exc instanceof DeadScopeError)) {
285
+ throw exc;
286
+ }
287
+ process.stderr.write(`❌ ${exc.message}\n`);
288
+ return 2;
289
+ }
263
290
  const failures: string[] = [];
264
291
  for (const name of _iterdir(ROLES_DIR).sort()) {
265
292
  const roleDir = path.join(ROLES_DIR, name);
@@ -279,7 +306,9 @@ function main(argv?: readonly string[]): number {
279
306
  process.stdout.write(`\nlint_role_experiences: ${failures.length} failure(s)\n`);
280
307
  return 1;
281
308
  }
282
- process.stdout.write('✅ lint_role_experiences: all role experiences pass\n');
309
+ process.stdout.write(
310
+ `✅ lint_role_experiences: all role experiences pass (${knownSkills.size} known skill(s) indexed)\n`,
311
+ );
283
312
  return 0;
284
313
  }
285
314
 
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Trigger-precision budget (road-to-renewal-foundation Phase 3, step 2).
4
+ *
5
+ * ## The defect this measures
6
+ *
7
+ * Every `keyword` trigger is matched as an UNANCHORED, case-insensitive
8
+ * substring of the prompt (`router_telemetry.ts::trigger_matches`). A short
9
+ * keyword therefore fires on words that merely contain it: `AC` fired
10
+ * `cross-source-consistency` on "black", "back", "contact"; `CAC` fired the
11
+ * finance floor on "cache". The shorter the keyword, the more of the English
12
+ * language it claims.
13
+ *
14
+ * ## Why a COUNT ratchet and not a rewrite
15
+ *
16
+ * The obvious repair — "promote the noisy keywords to `phrase:`" — is a
17
+ * NO-OP: `keyword` and `phrase` run the same substring comparison
18
+ * (`router_telemetry.ts` lines for both branches are identical), so the two
19
+ * kinds differ in documentation only. The other obvious repair — anchoring
20
+ * `keyword` on word boundaries — is the real fix, but it changes shipped
21
+ * activation semantics for all 316 single-token keywords at once and belongs
22
+ * in its own change with its own before/after (recorded as the reopen term in
23
+ * the Phase 3 pre-registration).
24
+ *
25
+ * So this gate does the one thing that is both safe and durable: it fixes the
26
+ * population in place. Short keywords that exist today are recorded; a NEW one
27
+ * cannot land. Every removal is a normal commit that lowers the number.
28
+ *
29
+ * ## Scope of "short"
30
+ *
31
+ * ASCII keywords of ≤ 3 characters. Non-ASCII short keywords are excluded by
32
+ * construction: the eight emoji triggers on
33
+ * `no-decorative-emojis-in-git-surfaces` are one code point each and cannot
34
+ * collide with prose substrings, so counting them would be noise.
35
+ *
36
+ * A pure length rule is deliberate. A collision count against a system word
37
+ * list would be sharper but is not portable (no dictionary on a CI runner),
38
+ * and an embedded word list is a second source of truth to maintain.
39
+ *
40
+ * Exit: 0 within budget · 1 usage/IO error · 2 over budget.
41
+ */
42
+ import * as fs from 'node:fs';
43
+ import * as path from 'node:path';
44
+ import { fileURLToPath, pathToFileURL } from 'node:url';
45
+
46
+ const _HERE = fileURLToPath(import.meta.url);
47
+ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
48
+ const ROUTER = path.join(REPO_ROOT, 'dist', 'router.json');
49
+
50
+ /**
51
+ * Ratchet, seeded 2026-08-02 at the measured population after removing the
52
+ * two provably redundant entries (`AC`, already covered by the
53
+ * `acceptance criteria` keyword on the same rule; `CAC`, which collided with
54
+ * "cache" while `LTV` and `payback` already carry the unit-economics signal).
55
+ * MAY ONLY MOVE DOWN.
56
+ */
57
+ export const SHORT_KEYWORD_BUDGET = 22;
58
+
59
+ export interface ShortKeyword {
60
+ keyword: string;
61
+ rule: string;
62
+ tier: string;
63
+ }
64
+
65
+ interface RouterRule {
66
+ id: string;
67
+ triggers?: Array<Record<string, unknown>>;
68
+ }
69
+
70
+ /** Every ASCII `keyword` trigger of ≤ 3 characters, sorted for stable output. */
71
+ export function short_keywords(router: Record<string, unknown>): ShortKeyword[] {
72
+ const out: ShortKeyword[] = [];
73
+ for (const tier of ['tier_1', 'tier_2']) {
74
+ const entries = router[tier];
75
+ if (!Array.isArray(entries)) continue;
76
+ for (const raw of entries) {
77
+ const rule = raw as RouterRule;
78
+ for (const trigger of rule.triggers ?? []) {
79
+ if (!('keyword' in trigger)) continue;
80
+ const keyword = String(trigger['keyword']);
81
+ const ascii = [...keyword].every((c) => c.codePointAt(0)! < 128);
82
+ if (keyword.length <= 3 && ascii) {
83
+ out.push({ keyword, rule: rule.id, tier });
84
+ }
85
+ }
86
+ }
87
+ }
88
+ out.sort(
89
+ (a, b) =>
90
+ (a.keyword < b.keyword ? -1 : a.keyword > b.keyword ? 1 : 0) ||
91
+ (a.rule < b.rule ? -1 : a.rule > b.rule ? 1 : 0),
92
+ );
93
+ return out;
94
+ }
95
+
96
+ export function main(): number {
97
+ if (!fs.existsSync(ROUTER)) {
98
+ process.stderr.write(
99
+ `error: ${path.relative(REPO_ROOT, ROUTER)} not found — run \`task sync\` first\n`,
100
+ );
101
+ return 1;
102
+ }
103
+ let router: Record<string, unknown>;
104
+ try {
105
+ router = JSON.parse(fs.readFileSync(ROUTER, 'utf-8')) as Record<string, unknown>;
106
+ } catch (e) {
107
+ process.stderr.write(`error: cannot parse router.json (${String(e)})\n`);
108
+ return 1;
109
+ }
110
+
111
+ const found = short_keywords(router);
112
+ const over = found.length > SHORT_KEYWORD_BUDGET;
113
+
114
+ if (over) {
115
+ process.stdout.write(
116
+ `❌ trigger precision: ${found.length} short (≤3 char) keyword triggers > budget ${SHORT_KEYWORD_BUDGET}\n`,
117
+ );
118
+ for (const s of found) {
119
+ process.stdout.write(` ${s.keyword.padEnd(6)} ${s.rule} [${s.tier}]\n`);
120
+ }
121
+ process.stdout.write(
122
+ '\n A short keyword is matched as an unanchored substring, so it fires on\n' +
123
+ ' every word that merely contains it. Lengthen it into an unambiguous\n' +
124
+ ' string, or drop it when a longer trigger on the same rule already\n' +
125
+ ' carries the signal. Raising SHORT_KEYWORD_BUDGET is not the fix.\n',
126
+ );
127
+ return 2;
128
+ }
129
+
130
+ process.stdout.write(
131
+ `✅ trigger precision: ${found.length} short (≤3 char) keyword triggers ` +
132
+ `/ budget ${SHORT_KEYWORD_BUDGET}` +
133
+ (found.length < SHORT_KEYWORD_BUDGET
134
+ ? ` — lower the budget to ${found.length} to lock the gain in\n`
135
+ : '\n'),
136
+ );
137
+ return 0;
138
+ }
139
+
140
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
141
+ process.exit(main());
142
+ }
@@ -30,81 +30,21 @@
30
30
  "description": "[stub — implemented on demand] Return the metadata for one catalog entry by id (id, class, name, description, tags, path). Body is returned ONLY when include_body is true. Use after catalog_search to look at a candidate before loading it. Read-only. Activation deferred (see catalog_search).",
31
31
  "side_effect": "ro",
32
32
  "implemented_on": [],
33
- "input_schema": {
34
- "type": "object",
35
- "properties": {
36
- "id": {
37
- "type": "string",
38
- "description": "Catalog id, e.g. `skill:api-design` or `persona:backend-architect`."
39
- },
40
- "include_body": {
41
- "type": "boolean",
42
- "default": false,
43
- "description": "When true, include the full body. Defaults to false (metadata only)."
44
- }
45
- },
46
- "required": [
47
- "id"
48
- ],
49
- "additionalProperties": false
50
- }
33
+ "input_schema": {}
51
34
  },
52
35
  {
53
36
  "name": "catalog_load",
54
37
  "description": "[stub — implemented on demand] Load the full body of one catalog entry by id, wrapped in a neutral reference preamble that subordinates it to the user's request and system instructions. Use to bring a skill/persona/command body into context on demand. Read-only, path-confined to the package root. Never injects or acts — the host decides what to do with the body. Activation deferred (see catalog_search).",
55
38
  "side_effect": "ro",
56
39
  "implemented_on": [],
57
- "input_schema": {
58
- "type": "object",
59
- "properties": {
60
- "id": {
61
- "type": "string",
62
- "description": "Catalog id to load the body for."
63
- }
64
- },
65
- "required": [
66
- "id"
67
- ],
68
- "additionalProperties": false
69
- }
40
+ "input_schema": {}
70
41
  },
71
42
  {
72
43
  "name": "catalog_search",
73
44
  "description": "[stub — implemented on demand] Search the package's own artifact catalog (skills, personas, commands) by free-text query and return ranked summaries — name, description, tags, score — WITHOUT loading any body. Use to discover which skill or persona fits a task when the full catalog is not in context. Read-only, deterministic (BM25 over an on-disk index). Activation deferred behind a task-quality A/B (see catalog_tools.ts).",
74
45
  "side_effect": "ro",
75
46
  "implemented_on": [],
76
- "input_schema": {
77
- "type": "object",
78
- "properties": {
79
- "query": {
80
- "type": "string",
81
- "description": "Free-text search query."
82
- },
83
- "class": {
84
- "type": "string",
85
- "enum": [
86
- "skill",
87
- "persona",
88
- "command"
89
- ],
90
- "description": "Optional: restrict to one artifact class."
91
- },
92
- "pack": {
93
- "type": "string",
94
- "description": "Optional: restrict to entries tagged with this pack/domain."
95
- },
96
- "limit": {
97
- "type": "integer",
98
- "minimum": 1,
99
- "default": 8,
100
- "description": "Maximum results. Defaults to 8."
101
- }
102
- },
103
- "required": [
104
- "query"
105
- ],
106
- "additionalProperties": false
107
- }
47
+ "input_schema": {}
108
48
  },
109
49
  {
110
50
  "name": "chat_history_append",
@@ -216,17 +156,7 @@
216
156
  "description": "[stub — implemented on demand] Regenerate the compiled router (`router.compiled.json`) from the current rule, skill, and command frontmatter, so trigger-based routing reflects the latest artifacts. Use after adding or editing a rule / skill / command's trigger metadata. Shell-bound — wraps `scripts/compile_router.py` — and returns the regenerated router summary. Set `dry_run: true` to compute the result without writing the file.",
217
157
  "side_effect": "shell",
218
158
  "implemented_on": [],
219
- "input_schema": {
220
- "type": "object",
221
- "properties": {
222
- "dry_run": {
223
- "type": "boolean",
224
- "default": false,
225
- "description": "When true, compute the router without writing `router.compiled.json` to disk."
226
- }
227
- },
228
- "additionalProperties": false
229
- }
159
+ "input_schema": {}
230
160
  },
231
161
  {
232
162
  "name": "conformance_check",
@@ -502,20 +432,7 @@
502
432
  "description": "[stub — implemented on demand] Extract reusable patterns, decisions, and learnings from a single chat-history session, as candidate memory entries. Use to distill a finished session into durable memory before it ages out. Read-only. Returns the mined candidates grouped by kind.",
503
433
  "side_effect": "ro",
504
434
  "implemented_on": [],
505
- "input_schema": {
506
- "type": "object",
507
- "properties": {
508
- "session": {
509
- "type": "string",
510
- "description": "Session id to mine."
511
- },
512
- "path": {
513
- "type": "string",
514
- "description": "Optional path to a chat-history JSONL; defaults to the standard location."
515
- }
516
- },
517
- "additionalProperties": false
518
- }
435
+ "input_schema": {}
519
436
  },
520
437
  {
521
438
  "name": "read_resource_body",
@@ -584,16 +501,7 @@
584
501
  "description": "[stub — implemented on demand] Run the consumer project's configured quality gate (PHPStan / Rector / ECS or the stack equivalent) and return a structured per-tool result. Use before committing to confirm static-analysis and style gates pass. Shell-bound. Pass `tool` to run a single gate; omit to run all.",
585
502
  "side_effect": "shell",
586
503
  "implemented_on": [],
587
- "input_schema": {
588
- "type": "object",
589
- "properties": {
590
- "tool": {
591
- "type": "string",
592
- "description": "Restrict to a single quality tool by name (e.g. phpstan, rector, ecs)."
593
- }
594
- },
595
- "additionalProperties": false
596
- }
504
+ "input_schema": {}
597
505
  },
598
506
  {
599
507
  "name": "run_tests",
@@ -625,115 +533,35 @@
625
533
  "description": "[stub — implemented on demand] Score a user message against the compiled router and return the skills whose triggers match, each with the reason it fired. Use to predict which skills a given message would activate, for debugging trigger coverage. Read-only and deterministic. Returns a ranked list of matching skills with their trigger reasons.",
626
534
  "side_effect": "ro",
627
535
  "implemented_on": [],
628
- "input_schema": {
629
- "type": "object",
630
- "properties": {
631
- "message": {
632
- "type": "string",
633
- "description": "The user message to score against skill triggers."
634
- },
635
- "context": {
636
- "type": "string",
637
- "description": "Optional recent-turn context to sharpen the match."
638
- },
639
- "limit": {
640
- "type": "integer",
641
- "minimum": 1,
642
- "default": 5,
643
- "description": "Maximum number of matching skills to return. Defaults to 5."
644
- }
645
- },
646
- "required": [
647
- "message"
648
- ],
649
- "additionalProperties": false
650
- }
536
+ "input_schema": {}
651
537
  },
652
538
  {
653
539
  "name": "suggest_command",
654
540
  "description": "[stub — implemented on demand] Run the command-suggestion engine: score the available slash commands against a user message and context, and return a ranked, numbered-options block of the best matches. Use to recommend a command for a free-form request without invoking anything. Read-only and deterministic. Returns the formatted suggestion block plus the scored candidates.",
655
541
  "side_effect": "ro",
656
542
  "implemented_on": [],
657
- "input_schema": {
658
- "type": "object",
659
- "properties": {
660
- "message": {
661
- "type": "string",
662
- "description": "The user message to match commands against."
663
- },
664
- "context": {
665
- "type": "string",
666
- "description": "Optional recent-turn context to sharpen the match."
667
- }
668
- },
669
- "required": [
670
- "message"
671
- ],
672
- "additionalProperties": false
673
- }
543
+ "input_schema": {}
674
544
  },
675
545
  {
676
546
  "name": "suggest_skill_for_task",
677
547
  "description": "[stub — implemented on demand] Match a free-form task description to the most relevant skills, ranked by their frontmatter triggers. Use to pick a skill for a described task before starting work. Read-only. Returns a ranked list of skills with name, description, and match reason.",
678
548
  "side_effect": "ro",
679
549
  "implemented_on": [],
680
- "input_schema": {
681
- "type": "object",
682
- "properties": {
683
- "task": {
684
- "type": "string",
685
- "description": "Free-form description of the task to find a skill for."
686
- },
687
- "limit": {
688
- "type": "integer",
689
- "minimum": 1,
690
- "default": 5,
691
- "description": "Maximum number of skills to return. Defaults to 5."
692
- }
693
- },
694
- "required": [
695
- "task"
696
- ],
697
- "additionalProperties": false
698
- }
550
+ "input_schema": {}
699
551
  },
700
552
  {
701
553
  "name": "sync_agent_settings",
702
554
  "description": "[stub — implemented on demand] Synchronise `.agent-settings.yml` against the current template and active profile — add new sections and keys while preserving existing user values. Use after a package upgrade to pick up newly introduced settings. Edits the settings file and returns the keys that were (or would be) added. Set `dry_run: true` to preview.",
703
555
  "side_effect": "fs-write",
704
556
  "implemented_on": [],
705
- "input_schema": {
706
- "type": "object",
707
- "properties": {
708
- "profile": {
709
- "type": "string",
710
- "description": "Override the profile pinned in the settings file."
711
- },
712
- "dry_run": {
713
- "type": "boolean",
714
- "default": false,
715
- "description": "When true, return the planned settings changes without writing the file."
716
- }
717
- },
718
- "additionalProperties": false
719
- }
557
+ "input_schema": {}
720
558
  },
721
559
  {
722
560
  "name": "sync_gitignore",
723
561
  "description": "[stub — implemented on demand] Synchronise the managed `event4u/agent-config` block in the consumer project's `.gitignore` — add missing canonical entries while preserving user-added lines. Use after upgrading the package or when generated paths are being tracked by mistake. Edits `.gitignore` and returns the added / removed entries. Set `dry_run: true` to preview the changes.",
724
562
  "side_effect": "fs-write",
725
563
  "implemented_on": [],
726
- "input_schema": {
727
- "type": "object",
728
- "properties": {
729
- "dry_run": {
730
- "type": "boolean",
731
- "default": false,
732
- "description": "When true, return the planned `.gitignore` changes without writing the file."
733
- }
734
- },
735
- "additionalProperties": false
736
- }
564
+ "input_schema": {}
737
565
  },
738
566
  {
739
567
  "name": "telemetry_report",
@@ -763,24 +591,7 @@
763
591
  "description": "[stub — implemented on demand] Synchronise the `messages()` method of a Laravel FormRequest class: add entries for validation rules that lack a custom message, link them to translation keys, and drop stale ones. Use after changing a FormRequest's `rules()`. Edits the class file on disk and returns the planned or applied message changes. Set `dry_run: true` to preview the diff without writing.",
764
592
  "side_effect": "fs-write",
765
593
  "implemented_on": [],
766
- "input_schema": {
767
- "type": "object",
768
- "properties": {
769
- "request_class": {
770
- "type": "string",
771
- "description": "Fully-qualified FormRequest class FQN or repo-relative file path."
772
- },
773
- "dry_run": {
774
- "type": "boolean",
775
- "default": false,
776
- "description": "When true, return the planned message changes without editing the file."
777
- }
778
- },
779
- "required": [
780
- "request_class"
781
- ],
782
- "additionalProperties": false
783
- }
594
+ "input_schema": {}
784
595
  }
785
596
  ]
786
597
  }
@@ -233,7 +233,7 @@ function _chReadEntries(
233
233
  *
234
234
  * Arguments:
235
235
  * paths: optional list of repo-relative paths to lint. Empty /
236
- * missing → lint the full `.agent-src.uncondensed/` tree
236
+ * missing → lint the full `src/` tree
237
237
  * via `gather_all_candidate_files`.
238
238
  *
239
239
  * Never spawns `git` (no `--changed` mode); never writes; mirrors