@event4u/agent-config 9.14.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 (276) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +110 -112
  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/python/workspace_drive.js +2 -0
  115. package/dist/cli/python/workspace_drive.js.map +1 -1
  116. package/dist/cli/python/workspace_drive_health.js +2 -0
  117. package/dist/cli/python/workspace_drive_health.js.map +1 -1
  118. package/dist/cli/python/workspace_hosts.js +2 -0
  119. package/dist/cli/python/workspace_hosts.js.map +1 -1
  120. package/dist/cli/python/workspace_sessions.js +2 -0
  121. package/dist/cli/python/workspace_sessions.js.map +1 -1
  122. package/dist/cli/registry.js +1 -0
  123. package/dist/cli/registry.js.map +1 -1
  124. package/dist/cli-delegate/{chunk-A3HJ6NTI.js → chunk-63WSP6YZ.js} +2 -1
  125. package/dist/cli-delegate/{chunk-EJYIGZ3T.js → chunk-BKO2AZ7Z.js} +1 -1
  126. package/dist/cli-delegate/{chunk-GCVFLBP2.js → chunk-H574KOUH.js} +338 -494
  127. package/dist/cli-delegate/chunk-NXFWFAYV.js +339 -0
  128. package/dist/cli-delegate/{chunk-TEGJU2JX.js → chunk-ZXXJLL5K.js} +2 -2
  129. package/dist/cli-delegate/cmd_conformance.js +29 -23
  130. package/dist/cli-delegate/cmd_doctor.js +3 -3
  131. package/dist/cli-delegate/cmd_export.js +4 -4
  132. package/dist/cli-delegate/cmd_preflight.js +2 -2
  133. package/dist/cli-delegate/cmd_prune.js +4 -4
  134. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  135. package/dist/cli-delegate/cmd_sync.js +4 -4
  136. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  137. package/dist/cli-delegate/cmd_validate.js +4 -4
  138. package/dist/discovery/deprecation-report.md +1 -1
  139. package/dist/discovery/discovery-manifest.json +127 -95
  140. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  141. package/dist/discovery/discovery-manifest.summary.md +1 -1
  142. package/dist/discovery/orphan-report.md +1 -1
  143. package/dist/discovery/packs.json +2 -2
  144. package/dist/discovery/trust-report.md +1 -1
  145. package/dist/discovery/workspaces.json +2 -2
  146. package/dist/hooks/dispatch.js +729 -430
  147. package/dist/install/install.mjs +52 -11
  148. package/dist/install/rule_scope.js +18 -2
  149. package/dist/install/rule_scope.js.map +1 -1
  150. package/dist/mcp/registry-manifest.json +2 -2
  151. package/dist/mcp/server.json +2 -2
  152. package/dist/mcp/server.mjs +518 -411
  153. package/dist/router.json +1 -1
  154. package/dist/scripts/_lib/claude_settings_hooks.js +30 -5
  155. package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
  156. package/dist/scripts/_lib/scoped_projection.js +223 -0
  157. package/dist/scripts/_lib/scoped_projection.js.map +1 -0
  158. package/dist/scripts/_lib/surface_tiers.js +125 -0
  159. package/dist/scripts/_lib/surface_tiers.js.map +1 -0
  160. package/dist/scripts/_lib/token_count.js +166 -0
  161. package/dist/scripts/_lib/token_count.js.map +1 -0
  162. package/dist/scripts/_lib/trigger_eval_floors.js +8 -0
  163. package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -1
  164. package/dist/scripts/condense.js +139 -9
  165. package/dist/scripts/condense.js.map +1 -1
  166. package/dist/scripts/project_thin_rules.js +450 -0
  167. package/dist/scripts/project_thin_rules.js.map +1 -0
  168. package/dist/server/routes/workspace.js +2 -0
  169. package/dist/server/routes/workspace.js.map +1 -1
  170. package/dist/server/schemas/settings.js +9 -3
  171. package/dist/server/schemas/settings.js.map +1 -1
  172. package/dist/ui/assets/index-DJY_vQVn.js +40 -0
  173. package/dist/ui/assets/index-DJY_vQVn.js.map +1 -0
  174. package/dist/ui/index.html +1 -1
  175. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  176. package/package.json +11 -11
  177. package/src/agent-src/templates/AGENTS.md +1 -1
  178. package/src/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
  179. package/src/config/agent-settings.template.yml +59 -11
  180. package/src/config/ci-local-parity.yml +8 -0
  181. package/src/config/discovery/unassigned-artefacts.yml +23 -25
  182. package/src/config/evaluator-budgets.json +35 -5
  183. package/src/config/gate-coverage.yml +147 -0
  184. package/src/config/gate-violation-baselines.json +25 -0
  185. package/src/install/rule_scope.ts +27 -2
  186. package/src/scripts/_cli/cmd_conformance.ts +13 -24
  187. package/src/scripts/_dispatch.bash +10 -0
  188. package/src/scripts/_lib/claude_settings_hooks.ts +30 -4
  189. package/src/scripts/_lib/gate_baseline.ts +213 -0
  190. package/src/scripts/_lib/scoped_projection.ts +50 -0
  191. package/src/scripts/_lib/tier_budget_routing.ts +234 -0
  192. package/src/scripts/_lib/tool_adapter_registry.ts +8 -1
  193. package/src/scripts/_lib/trigger_eval_floors.ts +9 -0
  194. package/src/scripts/_lib/trigger_routers.ts +17 -5
  195. package/src/scripts/adr/regenerate_index.ts +4 -3
  196. package/src/scripts/ai-video/lib/parse-blueprint.sh +1 -1
  197. package/src/scripts/ai_council/compile_corpus.ts +1 -1
  198. package/src/scripts/audit_cloud_compatibility.ts +37 -2
  199. package/src/scripts/audit_initial_context.ts +53 -19
  200. package/src/scripts/audit_likelihood.ts +31 -2
  201. package/src/scripts/audit_user_type_axis.ts +58 -11
  202. package/src/scripts/bench_hook_latency.ts +172 -41
  203. package/src/scripts/build_mcp_catalog.ts +12 -1
  204. package/src/scripts/check_always_budget.ts +122 -11
  205. package/src/scripts/check_augment_description_cap.ts +24 -3
  206. package/src/scripts/check_ci_strict_superset.ts +150 -0
  207. package/src/scripts/check_cluster_patterns.ts +100 -0
  208. package/src/scripts/check_consumer_scope_flip.ts +26 -4
  209. package/src/scripts/check_evaluator_budgets.ts +198 -19
  210. package/src/scripts/check_gate_coverage.ts +337 -1
  211. package/src/scripts/check_generator_output_coverage.ts +2 -1
  212. package/src/scripts/check_iron_law_prominence.ts +1 -1
  213. package/src/scripts/check_kernel_rule_bundle.ts +1 -1
  214. package/src/scripts/check_no_new_legacy_path.ts +167 -3
  215. package/src/scripts/check_pack_size.ts +18 -4
  216. package/src/scripts/check_portability.ts +37 -7
  217. package/src/scripts/check_references.ts +19 -3
  218. package/src/scripts/check_release_pr_shape.ts +13 -0
  219. package/src/scripts/check_token_regression.ts +9 -2
  220. package/src/scripts/condense.ts +155 -12
  221. package/src/scripts/cost/budget.mjs +63 -2
  222. package/src/scripts/evaluator_umbrella.sh +34 -1
  223. package/src/scripts/generate_capability_matrix.ts +8 -0
  224. package/src/scripts/generate_router_coverage_from_matrix.ts +155 -0
  225. package/src/scripts/hook_manifest.yaml +17 -3
  226. package/src/scripts/hooks/block_config_weakening.ts +337 -0
  227. package/src/scripts/hooks/concern_registry.ts +2 -0
  228. package/src/scripts/hooks/probe_throttle.ts +75 -0
  229. package/src/scripts/install-hooks.sh +24 -2
  230. package/src/scripts/install.ts +2 -2
  231. package/src/scripts/lint_agents_md.ts +9 -3
  232. package/src/scripts/lint_archived_skills.ts +1 -1
  233. package/src/scripts/lint_artefact_frontmatter.ts +185 -37
  234. package/src/scripts/lint_command_routing.ts +21 -52
  235. package/src/scripts/lint_command_verbs.ts +47 -28
  236. package/src/scripts/lint_context_spine_usage.ts +38 -12
  237. package/src/scripts/lint_framework_leakage.ts +25 -6
  238. package/src/scripts/lint_handoffs.ts +49 -2
  239. package/src/scripts/lint_media_policy_linkage.ts +62 -30
  240. package/src/scripts/lint_namespace.ts +92 -7
  241. package/src/scripts/lint_new_skill_gate.ts +39 -31
  242. package/src/scripts/lint_originality_shingles.ts +7 -1
  243. package/src/scripts/lint_override_kernel_guard.ts +21 -3
  244. package/src/scripts/lint_pack_boundaries.ts +461 -91
  245. package/src/scripts/lint_pack_dependencies.ts +24 -57
  246. package/src/scripts/lint_persona_governance.ts +1 -1
  247. package/src/scripts/lint_role_experiences.ts +34 -5
  248. package/src/scripts/lint_trigger_precision.ts +142 -0
  249. package/src/scripts/mcp_server/consumer_tool_catalog.json +12 -201
  250. package/src/scripts/mcp_server/tools.ts +1 -1
  251. package/src/scripts/prepack-check.mjs +40 -1
  252. package/src/scripts/prepack_router_targets.d.mts +7 -0
  253. package/src/scripts/prepack_router_targets.mjs +93 -0
  254. package/src/scripts/print_required_checks.ts +72 -18
  255. package/src/scripts/profile_staleness_hook.ts +12 -0
  256. package/src/scripts/record_evaluator_measurements.mjs +105 -0
  257. package/src/scripts/recruit_preflight.sh +1 -1
  258. package/src/scripts/release.ts +105 -1
  259. package/src/scripts/router_target_paths.d.mts +4 -0
  260. package/src/scripts/router_target_paths.mjs +69 -0
  261. package/src/scripts/router_telemetry.ts +52 -1
  262. package/src/scripts/routing_doctor.ts +485 -0
  263. package/src/scripts/rule_trigger_eval.ts +403 -0
  264. package/src/scripts/schemas/rule.schema.json +5 -1
  265. package/src/scripts/session_canary_hook.ts +96 -5
  266. package/src/scripts/skill_linter.ts +9 -28
  267. package/src/scripts/smoke/skills.sh +1 -1
  268. package/src/scripts/sweep_dead_scan_roots.ts +356 -10
  269. package/src/scripts/trigger_coverage.ts +13 -2
  270. package/src/scripts/update_counts.ts +1 -1
  271. package/src/scripts/validate_telegraph_carveouts.ts +1 -1
  272. package/src/scripts/wrapper_freshness_hook.ts +14 -0
  273. package/src/server/schemas/settings.ts +19 -5
  274. package/dist/cli-delegate/chunk-TJIAM5FW.js +0 -133
  275. package/dist/ui/assets/index-BpSQt5vm.js +0 -40
  276. package/dist/ui/assets/index-BpSQt5vm.js.map +0 -1
@@ -3,7 +3,7 @@
3
3
  * prepack-check.mjs — guard the published artifact.
4
4
  *
5
5
  * Runs during `npm pack` / `npm publish` AND as a dry-run on every PR
6
- * (Static Checks workflow). Two gates:
6
+ * (Static Checks workflow). Four gates:
7
7
  *
8
8
  * 1. Compiled-bin shape — the TS CLI binary exists, is executable, and
9
9
  * carries the Node shebang before the tarball is built. Otherwise a
@@ -12,6 +12,11 @@
12
12
  * 2. Shipped-import completeness — every relative import reachable from
13
13
  * the shipped `src/` trees resolves to a file the `files` whitelist
14
14
  * actually ships (the 8.3.0 ERR_MODULE_NOT_FOUND class).
15
+ * 3. Lifecycle-target shape — every consumer-side lifecycle script points
16
+ * at a target that exists and ships (the 9.8.0 dead-postinstall class).
17
+ * 4. Router-target shipping — every `routes_to` target in dist/router.json
18
+ * resolves inside the shipped `files[]` set, so a `files[]` narrowing
19
+ * cannot ship a dead router pointer.
15
20
  *
16
21
  * Folded from external council pass 2026-05-18 (Phase 1.3 acceptance).
17
22
  *
@@ -176,6 +181,40 @@ if (lifecycleErrors.length > 0) {
176
181
  }
177
182
  process.stderr.write('prepack-check: lifecycle script targets OK\n');
178
183
 
184
+ // ---------------------------------------------------------------------------
185
+ // Router-pointer shipping guard (gate 4): every `routes_to` target in
186
+ // dist/router.json must resolve to a file that exists AND is inside the
187
+ // shipped `files[]` set. A `files[]` narrowing that drops a routed target was
188
+ // caught once by a working gate; this makes that catch structural rather than
189
+ // incidental, and moves it to pack time on the causing PR
190
+ // (road-to-gates-that-can-fail Phase 4). The kind→path table is single-sourced
191
+ // from ./router_target_paths.mjs, shared with
192
+ // cmd_conformance.ts::routeTargetPaths — two copies could disagree about where
193
+ // a pointer lives, which is the packaging↔runtime gap this closes.
194
+ //
195
+ // PREPACK_ROUTER_JSON points the gate at a different index (repo-relative or
196
+ // absolute) — used by tests/scripts/prepack_router_targets.test.ts and by the
197
+ // planted-target proof, so dist/router.json itself is never mutated.
198
+ // ---------------------------------------------------------------------------
199
+ import { checkRouterTargetsShipped } from './prepack_router_targets.mjs';
200
+
201
+ const routerRel = env.PREPACK_ROUTER_JSON ?? 'dist/router.json';
202
+ const routerResult = checkRouterTargetsShipped({
203
+ routerPath: resolve(routerRel),
204
+ isShipped,
205
+ exists: (relPath) => existsSync(resolve(relPath)),
206
+ readFile: (p) => readFileSync(p, 'utf8'),
207
+ });
208
+ if (routerResult.errors.length > 0) {
209
+ for (const e of routerResult.errors) {
210
+ process.stderr.write(`prepack-check: ${e}\n`);
211
+ }
212
+ die(`${routerResult.errors.length} router target(s) do not resolve inside the shipped set.`);
213
+ }
214
+ process.stderr.write(
215
+ `prepack-check: router targets OK (${routerResult.scanned} routes_to target(s) in ${routerRel})\n`,
216
+ );
217
+
179
218
  // Optional: invoked with --verbose dumps the size for tarball-budget bookkeeping.
180
219
  if (argv.includes('--verbose')) {
181
220
  process.stderr.write(`prepack-check: size=${st.size} bytes\n`);
@@ -0,0 +1,7 @@
1
+ /** Type declarations for prepack_router_targets.mjs (prepack gate 4). */
2
+ export declare function checkRouterTargetsShipped(opts: {
3
+ routerPath: string;
4
+ isShipped: (relPath: string) => boolean;
5
+ exists: (relPath: string) => boolean;
6
+ readFile: (absPath: string) => string;
7
+ }): { errors: string[]; scanned: number };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * prepack_router_targets.mjs — pure core of prepack gate 4.
3
+ *
4
+ * Every `routes_to` target in `dist/router.json` must resolve to a file that
5
+ * (a) exists and (b) is inside the `files[]` whitelist. A pointer that
6
+ * resolves in the dev tree but is not in the tarball is dead the moment a
7
+ * consumer installs it — `agent-config conformance`'s `router-pointers` check
8
+ * then fails on the consumer's machine, for a defect introduced here.
9
+ *
10
+ * The kind→path table is NOT defined here: it is imported from
11
+ * `router_target_paths.mjs`, which `cmd_conformance.ts::routeTargetPaths` also
12
+ * consumes. One table, two readers — see that file's header for why.
13
+ *
14
+ * Extracted from `prepack-check.mjs` for the same reason
15
+ * `prepack_lifecycle_check.mjs` was: the entry script has top-level side
16
+ * effects and `process.exit`, so it cannot be imported by a test.
17
+ * road-to-gates-that-can-fail Phase 4.
18
+ */
19
+ import { routeTargetPathsPosix } from './router_target_paths.mjs';
20
+
21
+ /**
22
+ * @typedef {object} RouterTargetOptions
23
+ * @property {string} routerPath Absolute path to the router index.
24
+ * @property {(relPath: string) => boolean} isShipped `files[]` membership test.
25
+ * @property {(relPath: string) => boolean} exists Existence test, repo-relative.
26
+ * @property {(absPath: string) => string} readFile Reader for `routerPath`.
27
+ */
28
+
29
+ /**
30
+ * @param {RouterTargetOptions} opts
31
+ * @returns {{ errors: string[], scanned: number }}
32
+ */
33
+ export function checkRouterTargetsShipped(opts) {
34
+ const { routerPath, isShipped, exists, readFile } = opts;
35
+ const errors = [];
36
+ let scanned = 0;
37
+
38
+ let router;
39
+ try {
40
+ router = JSON.parse(readFile(routerPath));
41
+ } catch (e) {
42
+ return {
43
+ errors: [`router index ${routerPath} unreadable or not JSON: ${e.message}`],
44
+ scanned: 0,
45
+ };
46
+ }
47
+
48
+ for (const tier of ['tier_1', 'tier_2']) {
49
+ for (const entry of router[tier] ?? []) {
50
+ const ruleId = String(entry?.id ?? '<unnamed rule>');
51
+ for (const raw of entry?.routes_to ?? []) {
52
+ const target = String(raw);
53
+ scanned += 1;
54
+ const candidates = routeTargetPathsPosix(target);
55
+ if (candidates.length === 0) {
56
+ errors.push(
57
+ `${tier}:${ruleId} routes_to ${JSON.stringify(target)} — unparseable ` +
58
+ 'target or unknown kind (router_target_paths.mjs has no template ' +
59
+ 'for it), so nothing can be verified as shipped',
60
+ );
61
+ continue;
62
+ }
63
+ const present = candidates.filter((rel) => exists(rel));
64
+ if (present.length === 0) {
65
+ errors.push(
66
+ `${tier}:${ruleId} routes_to '${target}' → none of ` +
67
+ `[${candidates.join(', ')}] exists in the tree`,
68
+ );
69
+ continue;
70
+ }
71
+ if (!present.some((rel) => isShipped(rel))) {
72
+ errors.push(
73
+ `${tier}:${ruleId} routes_to '${target}' → ${present[0]} exists but is ` +
74
+ 'NOT in the package.json `files` whitelist — the pointer would ' +
75
+ 'ship dead and fail `agent-config conformance` on the consumer',
76
+ );
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ // Zero-scope is a failure (Phase 1 of the same roadmap): a router index
83
+ // that yields no targets means the shape changed under the gate, not that
84
+ // the package is clean.
85
+ if (errors.length === 0 && scanned === 0) {
86
+ errors.push(
87
+ `scanned 0 routes_to target(s) in ${routerPath} — the scan scope is dead or the ` +
88
+ 'router schema moved; this gate cannot report clean on nothing',
89
+ );
90
+ }
91
+
92
+ return { errors, scanned };
93
+ }
@@ -61,28 +61,75 @@ const DOCS_ONLY_ALLOWED_TOP: ReadonlySet<string> = new Set([
61
61
  'llms.txt',
62
62
  ]);
63
63
 
64
+ // Check names below are the strings GitHub actually REPORTS, verified against
65
+ // `gh pr checks 1108` on 2026-08-02 (road-to-renewal-foundation Phase 1).
66
+ //
67
+ // They previously named a matrix that had drifted into fiction — `Consistency`,
68
+ // `Skill Lint`, `Tests / install-tests`, `Tests / node-tests`,
69
+ // `Public Install Smoke / smoke`, `Migration Dry-Run`,
70
+ // `Release Validation / release-shape`, `Release Guard (post-tag)` and, most
71
+ // clearly, `Tests / python-tests`, which cannot exist at all: the Python→TS
72
+ // migration completed and `no-python-in-src.yml` now enforces its absence. A
73
+ // preview tool that prints names no check will ever report is worse than no
74
+ // preview — it reads as a verified list.
75
+ //
76
+ // Matrix families are printed as ONE line with the axis spelled out rather than
77
+ // as N pinned contexts. Shard counts and runner labels are part of the reported
78
+ // name, so pinning them here would re-create the same drift on the next matrix
79
+ // change — the exact failure this reconciliation removed.
64
80
  const FEATURE_CHECKS = [
65
- 'Consistency',
66
- 'Smoke Contracts',
67
- 'Skill Lint',
68
- 'Tests / install-tests',
69
- 'Tests / install-aux-tests',
70
- 'Tests / python-tests',
71
- 'Tests / node-tests',
72
- 'Public Install Smoke / smoke',
81
+ 'Sync + Generate Tools Consistency',
82
+ 'Smoke — kernel',
83
+ 'Smoke — router',
84
+ 'Smoke — schema',
85
+ 'Smoke — skills',
86
+ 'skill-lint',
87
+ 'Static Checks (ESLint · typecheck · prepack)',
88
+ 'Install Script Tests (matrix: {ubuntu,macos}-latest × shard 1-4/4)',
89
+ 'Install Aux Tests (matrix: {ubuntu,macos}-latest)',
90
+ 'Node Tests (matrix: {ubuntu,macos}-latest × shard 1-4/4)',
91
+ 'Golden Tests (matrix: {ubuntu,macos}-latest)',
92
+ 'Workspace Tests (matrix: {ubuntu,macos}-latest)',
93
+ 'Public Install Smoke (matrix: {ubuntu,macos,windows}-latest × node {20,22})',
94
+ 'Rule backstops',
95
+ 'no-python-in-src',
96
+ 'lint commit subjects',
73
97
  ] as const;
74
98
 
75
99
  const RELEASE_CHECKS = [
76
- 'Consistency',
77
- 'Smoke Contracts',
78
- 'Migration Dry-Run',
79
- 'Release Validation / release-shape',
80
- 'Release Validation / changelog-entry',
81
- 'Release Validation / version-consistency',
82
- 'Release Guard (post-tag)',
100
+ 'Sync + Generate Tools Consistency',
101
+ 'Smoke — kernel',
102
+ 'Smoke — router',
103
+ 'Smoke — schema',
104
+ 'Smoke — skills',
105
+ 'Release-PR shape detector',
106
+ 'CHANGELOG entry exists for head version',
107
+ 'package.json / marketplace.json / pack manifests agree',
108
+ 'Release install E2E (pack → install → upgrade → boot)',
109
+ 'npm audit (runtime deps, high+)',
110
+ 'skill-lint-strict',
83
111
  ] as const;
84
112
 
85
- const DOCS_ONLY_CHECKS = ['Consistency', 'Smoke Contracts'] as const;
113
+ const DOCS_ONLY_CHECKS = [
114
+ 'Sync + Generate Tools Consistency',
115
+ 'Smoke — kernel',
116
+ 'Smoke — router',
117
+ 'Smoke — schema',
118
+ 'Smoke — skills',
119
+ ] as const;
120
+
121
+ /**
122
+ * The checks that mechanically BLOCK a merge, per the repository ruleset
123
+ * `main protection` (id 17749383). Everything else above runs and is visible
124
+ * on the PR but does not gate the merge button.
125
+ *
126
+ * Deliberately a constant, not an API read: this script is contractually
127
+ * offline ("never invokes `gh`, never touches the network"). Re-verify with
128
+ * `gh api repos/event4u-app/agent-config/rulesets/17749383` when the ruleset
129
+ * changes — `docs/contracts/branch-protection-policy.md` § Change discipline
130
+ * owns that procedure.
131
+ */
132
+ const ENFORCED_CHECKS = ['Sync + Generate Tools Consistency'] as const;
86
133
 
87
134
  /** `git rev-parse --abbrev-ref HEAD`, mirroring `_git(check=True)`. */
88
135
  function current_branch(): string {
@@ -161,12 +208,19 @@ function print_set(shape_label: Shape, files: string[]): void {
161
208
  'docs-only': DOCS_ONLY_CHECKS,
162
209
  };
163
210
  const checks = table[shape_label];
211
+ const enforced: ReadonlySet<string> = new Set(ENFORCED_CHECKS);
164
212
  process.stdout.write(`PR shape: ${shape_label} (${files.length} file(s) in diff)\n`);
165
- process.stdout.write(`Required checks (${checks.length}):\n`);
213
+ process.stdout.write(`Checks this PR will face (${checks.length}):\n`);
166
214
  for (const name of checks) {
167
- process.stdout.write(` - ${name}\n`);
215
+ // `!` marks the checks that actually block the merge button; the rest
216
+ // run and are visible but are advisory at the branch-protection layer.
217
+ process.stdout.write(` ${enforced.has(name) ? '!' : '-'} ${name}\n`);
168
218
  }
169
219
  process.stdout.write('\n');
220
+ process.stdout.write(
221
+ `! = blocks merge (${ENFORCED_CHECKS.length} of ${checks.length}); ` +
222
+ '- = runs, visible, advisory\n',
223
+ );
170
224
  process.stdout.write(
171
225
  'Contract: docs/contracts/branch-protection-policy.md ' +
172
226
  '(per-PR-shape matrix)\n',
@@ -37,6 +37,9 @@ import {
37
37
  LOCAL_PROJECT_SUBDIR,
38
38
  } from "./_lib/agent_settings.js";
39
39
  import { readHookStdin } from "./hooks/hook_stdin.js";
40
+ import { probeRanToday, stampProbeRun } from "./hooks/probe_throttle.js";
41
+
42
+ const THROTTLE_CONCERN = "profile-staleness";
40
43
 
41
44
  // --- inlined session_profiles.stale_notice slice (unported dep) ----------
42
45
 
@@ -143,6 +146,15 @@ export function main(argv?: string[]): number {
143
146
  _readStdinIfNotTty();
144
147
 
145
148
  const root = rootArg !== null ? rootArg : _project_root();
149
+
150
+ // Daily throttle (road-to-hook-latency-repair): the overlay does not
151
+ // change within a day more often than a session ends — one attempt per
152
+ // local calendar day is enough. Skipped probe re-runs tomorrow.
153
+ if (probeRanToday(root, THROTTLE_CONCERN)) {
154
+ return 0;
155
+ }
156
+ stampProbeRun(root, THROTTLE_CONCERN);
157
+
146
158
  let notice: string | null;
147
159
  try {
148
160
  notice = _stale_notice(root);
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * record_evaluator_measurements.mjs — write the committed measurement record.
4
+ *
5
+ * `cli_help_command_count` drifted 74 → 80 against a value frozen at 79 with
6
+ * nobody seeing it, because the only place the real number existed was a
7
+ * transient CI log. The fix (road-to-gates-that-can-fail Phase 5; council route
8
+ * 2026-08-02) is a COMMITTED artifact: the nightly writes the measurement set
9
+ * here, and `check_evaluator_budgets --recorded` compares a later fresh run
10
+ * against it — a contradiction warns on `main` and fails on `release/*`. The
11
+ * review surface that already exists carries the signal; no notification
12
+ * channel a single maintainer would mute.
13
+ *
14
+ * Lives under `agents/evidence/` (the repo's committed-evidence tree), NOT
15
+ * under `src/config/` which ships: this is maintainer evidence with nightly
16
+ * churn, not consumer runtime config, and keeping it out of the tarball also
17
+ * keeps `evaluator-budgets.json` — the policy — reviewable on its own.
18
+ *
19
+ * Sorted keys + 4-space indent + trailing newline: a moved number must be a
20
+ * one-line diff, or the artifact is not reviewable.
21
+ *
22
+ * Usage:
23
+ * node src/scripts/record_evaluator_measurements.mjs \
24
+ * --measurements <file.json> [--out <file.json>]
25
+ * [--sha <git sha>] [--run-url <url>] [--conditions <text>]
26
+ */
27
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
28
+ import { dirname, resolve } from 'node:path';
29
+ import { argv, exit, stdout } from 'node:process';
30
+
31
+ /** Default (repo-relative) home of the record. */
32
+ export const RECORD_REL = 'agents/evidence/metrics/evaluator-measurements.json';
33
+
34
+ const RECORD_COMMENT =
35
+ 'Written by src/scripts/record_evaluator_measurements.mjs, invoked from ' +
36
+ 'src/scripts/evaluator_umbrella.sh when UMBRELLA_RECORD=1 (the nightly). ' +
37
+ 'check_evaluator_budgets.ts --recorded compares a fresh measurement set against ' +
38
+ 'this file; a contradiction on a DETERMINISTIC metric warns on main and fails on ' +
39
+ 'release. Do not hand-edit — refresh it from a real run.';
40
+
41
+ /**
42
+ * Build the record document. Pure — the caller owns the filesystem.
43
+ *
44
+ * @param {Record<string, unknown>} measurements
45
+ * @param {{ recordedAt?: string, sha?: string, runUrl?: string, conditions?: string }} [meta]
46
+ */
47
+ export function buildRecord(measurements, meta = {}) {
48
+ const sorted = {};
49
+ for (const key of Object.keys(measurements).sort()) {
50
+ sorted[key] = measurements[key];
51
+ }
52
+ return {
53
+ schema_version: 1,
54
+ _comment: RECORD_COMMENT,
55
+ recorded_at: meta.recordedAt ?? new Date().toISOString(),
56
+ git_sha: meta.sha || null,
57
+ run_url: meta.runUrl || null,
58
+ conditions: meta.conditions || null,
59
+ measurements: sorted,
60
+ };
61
+ }
62
+
63
+ /** Canonical serialization — stable so an unchanged run produces no diff. */
64
+ export function serializeRecord(doc) {
65
+ return `${JSON.stringify(doc, null, 4)}\n`;
66
+ }
67
+
68
+ function argValue(args, flag) {
69
+ const i = args.indexOf(flag);
70
+ return i >= 0 ? args[i + 1] : undefined;
71
+ }
72
+
73
+ export function main(args = argv.slice(2)) {
74
+ const measurementsPath = argValue(args, '--measurements');
75
+ if (measurementsPath === undefined) {
76
+ process.stderr.write(
77
+ 'usage: record_evaluator_measurements --measurements <file.json> ' +
78
+ '[--out <file.json>] [--sha <sha>] [--run-url <url>] [--conditions <text>]\n',
79
+ );
80
+ return 2;
81
+ }
82
+ const out = resolve(argValue(args, '--out') ?? RECORD_REL);
83
+ let measurements;
84
+ try {
85
+ measurements = JSON.parse(readFileSync(resolve(measurementsPath), 'utf8'));
86
+ } catch (e) {
87
+ process.stderr.write(`record_evaluator_measurements: ${e.message}\n`);
88
+ return 2;
89
+ }
90
+ const doc = buildRecord(measurements, {
91
+ sha: argValue(args, '--sha'),
92
+ runUrl: argValue(args, '--run-url'),
93
+ conditions: argValue(args, '--conditions'),
94
+ });
95
+ mkdirSync(dirname(out), { recursive: true });
96
+ writeFileSync(out, serializeRecord(doc));
97
+ stdout.write(
98
+ `recorded ${Object.keys(doc.measurements).length} measurement(s) → ${out}\n`,
99
+ );
100
+ return 0;
101
+ }
102
+
103
+ if (process.argv[1] !== undefined && resolve(process.argv[1]).endsWith('record_evaluator_measurements.mjs')) {
104
+ exit(main());
105
+ }
@@ -107,7 +107,7 @@ fi
107
107
  # exists. /tmp/agent-config-ci-start.txt is written by _ci-start
108
108
  # at the start of every 'task ci' run; /tmp/agent-config-ci-end.txt
109
109
  # by _ci-end on success. Pre-flight passes when both exist AND
110
- # end > start AND no .agent-src.uncondensed file is newer than end.
110
+ # end > start AND no src/ file is newer than end.
111
111
  START_MARKER="/tmp/agent-config-ci-start.txt"
112
112
  END_MARKER="/tmp/agent-config-ci-end.txt"
113
113
  if [ ! -f "$START_MARKER" ] || [ ! -f "$END_MARKER" ]; then
@@ -169,6 +169,8 @@ class CalledProcessError extends Error {
169
169
  // _lib/ and install.ts's REPO_ROOT computation).
170
170
  const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
171
171
  const PACKAGE_JSON = path.join(REPO_ROOT, 'package.json');
172
+ // Bumped alongside package.json — see set_lockfile_version for why.
173
+ const PACKAGE_LOCK_JSON = path.join(REPO_ROOT, 'package-lock.json');
172
174
  const MARKETPLACE_JSON = path.join(REPO_ROOT, '.claude-plugin', 'marketplace.json');
173
175
  // Source-of-truth project-settings template. Carries an `agent_config_version`
174
176
  // pin that check_template_pin_drift requires to equal package.json.version. We
@@ -562,6 +564,65 @@ function git(args: readonly string[], opts: { capture?: boolean } = {}): string
562
564
  return capture ? r.stdout.trim() : '';
563
565
  }
564
566
 
567
+ /**
568
+ * One name per failing check, from `gh pr checks --json name,bucket` output.
569
+ *
570
+ * Pure over the JSON text so the shape is testable without gh. Returns []
571
+ * when the payload is unparseable or nothing sits in the `fail` bucket —
572
+ * the caller then falls back to the raw watch output alone.
573
+ */
574
+ export function _failed_check_names(jsonText: string): string[] {
575
+ let parsed: unknown;
576
+ try {
577
+ parsed = JSON.parse(jsonText);
578
+ } catch {
579
+ return [];
580
+ }
581
+ if (!Array.isArray(parsed)) {
582
+ return [];
583
+ }
584
+ const names: string[] = [];
585
+ for (const entry of parsed) {
586
+ if (typeof entry !== 'object' || entry === null) {
587
+ continue;
588
+ }
589
+ const e = entry as Record<string, unknown>;
590
+ if (e['bucket'] === 'fail' && typeof e['name'] === 'string') {
591
+ names.push(e['name']);
592
+ }
593
+ }
594
+ return names;
595
+ }
596
+
597
+ /**
598
+ * Remediation lines for a set of failing check names.
599
+ *
600
+ * The watch table scrolls failures out of view on a busy PR, so the operator
601
+ * repeatedly saw only `pass`/`skipping` rows plus "PR checks failed" — the
602
+ * 9.14.0 failure mode, twice. Name the red checks explicitly and, for the
603
+ * shape detector, say where the fix belongs (main, never the release branch).
604
+ */
605
+ export function _failed_checks_report(names: readonly string[]): string {
606
+ if (names.length === 0) {
607
+ return '';
608
+ }
609
+ const lines: string[] = ['', 'Failing check(s):'];
610
+ for (const name of names) {
611
+ lines.push(` ❌ ${name}`);
612
+ }
613
+ if (names.includes('Release-PR shape detector')) {
614
+ lines.push(
615
+ '',
616
+ 'Release-PR shape: a release PR may only contain the version-bump',
617
+ 'allowlist. Land the out-of-shape files on main via their own PR, then',
618
+ 'merge main into the release branch — their release-PR diff becomes',
619
+ 'empty (docs/contracts/release-pr-gating.md § Mid-release fixes).',
620
+ );
621
+ }
622
+ lines.push('', 'After fixing, resume with: task release -- --resume --yes', '');
623
+ return lines.join('\n');
624
+ }
625
+
565
626
  /**
566
627
  * Watch PR checks and tolerate the 'no checks' case.
567
628
  *
@@ -606,6 +667,15 @@ function watch_pr_checks(): void {
606
667
  if (output) {
607
668
  process.stderr.write(output + '\n');
608
669
  }
670
+ const summary = spawnSync('gh', ['pr', 'checks', '--json', 'name,bucket'], {
671
+ cwd: REPO_ROOT,
672
+ encoding: 'utf-8',
673
+ stdio: ['ignore', 'pipe', 'pipe'],
674
+ });
675
+ const report = _failed_checks_report(_failed_check_names(summary.stdout ?? ''));
676
+ if (report) {
677
+ process.stderr.write(report + '\n');
678
+ }
609
679
  die(`PR checks failed (exit ${returncode})`);
610
680
  }
611
681
 
@@ -1064,6 +1134,34 @@ function set_package_version(p: string, version: string): void {
1064
1134
  fs.writeFileSync(p, jsonDumpsIndent(data, 4) + '\n', 'utf-8');
1065
1135
  }
1066
1136
 
1137
+ /**
1138
+ * Update BOTH version fields npm keeps in a lockfile — the top-level one and
1139
+ * the root package entry (`packages[""]`).
1140
+ *
1141
+ * The bump previously touched package.json and left the lock behind, so every
1142
+ * release shipped a `main` whose two files disagreed and every subsequent
1143
+ * local `npm install` produced a spurious modification (measured 2026-08-02:
1144
+ * package.json 9.13.0 vs package-lock.json 9.12.0). Rewriting the two fields
1145
+ * rather than shelling out to `npm install --package-lock-only` keeps the bump
1146
+ * offline and cannot re-resolve a dependency mid-release.
1147
+ * road-to-gates-that-can-fail Phase 5.
1148
+ */
1149
+ function set_lockfile_version(p: string, version: string): void {
1150
+ if (!fs.existsSync(p)) {
1151
+ return;
1152
+ }
1153
+ const data = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>;
1154
+ data['version'] = version;
1155
+ const packages = data['packages'];
1156
+ if (typeof packages === 'object' && packages !== null && !Array.isArray(packages)) {
1157
+ const root = (packages as Record<string, unknown>)[''];
1158
+ if (typeof root === 'object' && root !== null && !Array.isArray(root)) {
1159
+ (root as Record<string, unknown>)['version'] = version;
1160
+ }
1161
+ }
1162
+ fs.writeFileSync(p, jsonDumpsIndent(data, 4) + '\n', 'utf-8');
1163
+ }
1164
+
1067
1165
  /** Update `metadata.version`; preserve 2-space indentation + UTF-8. */
1068
1166
  function set_marketplace_version(p: string, version: string): void {
1069
1167
  const data = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>;
@@ -1442,8 +1540,13 @@ function execute(
1442
1540
  if (resume && current_pkg === plan.target) {
1443
1541
  _step(2, total, `Files already at ${plan.target} — skip bump`);
1444
1542
  } else {
1445
- _step(2, total, 'Bump package.json + marketplace.json + template pin, prepend CHANGELOG');
1543
+ _step(
1544
+ 2,
1545
+ total,
1546
+ 'Bump package.json + package-lock.json + marketplace.json + template pin, prepend CHANGELOG',
1547
+ );
1446
1548
  set_package_version(PACKAGE_JSON, plan.target);
1549
+ set_lockfile_version(PACKAGE_LOCK_JSON, plan.target);
1447
1550
  set_marketplace_version(MARKETPLACE_JSON, plan.target);
1448
1551
  set_template_pin(PROJECT_TEMPLATE, plan.target);
1449
1552
  prepend_changelog(CHANGELOG, plan.changelog_entry);
@@ -2135,6 +2238,7 @@ export {
2135
2238
  _cap_body,
2136
2239
  prepend_changelog,
2137
2240
  set_package_version,
2241
+ set_lockfile_version,
2138
2242
  set_marketplace_version,
2139
2243
  set_template_pin,
2140
2244
  resolve_bump,
@@ -0,0 +1,4 @@
1
+ /** Type declarations for router_target_paths.mjs (the shared kind→path table). */
2
+ export declare const ROUTE_TARGET_TEMPLATES: Readonly<Record<string, readonly string[]>>;
3
+ export declare function parseRouteTarget(target: string): { kind: string; id: string } | null;
4
+ export declare function routeTargetPathsPosix(target: string): string[];
@@ -0,0 +1,69 @@
1
+ /**
2
+ * router_target_paths.mjs — the ONE kind→path table for `dist/router.json`
3
+ * `routes_to` targets.
4
+ *
5
+ * Two consumers read it and they must not disagree:
6
+ * * `src/scripts/_cli/cmd_conformance.ts::routeTargetPaths` — resolves a
7
+ * pointer against the INSTALLED package tree at runtime.
8
+ * * `src/scripts/prepack-check.mjs` gate 4 — asserts the same pointer lands
9
+ * inside the `files[]` whitelist BEFORE the tarball is built.
10
+ *
11
+ * A copied table would let one side ship a target the other cannot see, which
12
+ * is exactly the packaging↔runtime gap the pack gate exists to close
13
+ * (road-to-gates-that-can-fail Phase 4). Plain `.mjs` because `prepack-check`
14
+ * runs as raw Node during the npm lifecycle and cannot load TypeScript; the
15
+ * TS side reads the sibling `router_target_paths.d.mts` declarations. Same
16
+ * shape as `prepack_lifecycle_check.mjs`.
17
+ *
18
+ * Templates are POSIX-relative with a single `{id}` placeholder. Callers that
19
+ * need platform separators split on `/` and `path.join` the segments.
20
+ */
21
+
22
+ /**
23
+ * Candidate homes per target kind, in resolution order — the FIRST existing
24
+ * candidate wins.
25
+ *
26
+ * `contract:` has two legitimate homes in the shipped tree: the public
27
+ * contract pages under `docs/contracts/` (per the rule-router contract) and
28
+ * the agent-facing contract contexts under
29
+ * `dist/agent-src/contexts/contracts/` (e.g. artifact-engagement-flow).
30
+ */
31
+ export const ROUTE_TARGET_TEMPLATES = Object.freeze({
32
+ skill: Object.freeze(['dist/agent-src/skills/{id}/SKILL.md']),
33
+ command: Object.freeze(['dist/agent-src/commands/{id}.md']),
34
+ guideline: Object.freeze(['docs/guidelines/{id}.md']),
35
+ contract: Object.freeze([
36
+ 'docs/contracts/{id}.md',
37
+ 'dist/agent-src/contexts/contracts/{id}.md',
38
+ ]),
39
+ });
40
+
41
+ /**
42
+ * Split a `<kind>:<id>` target. Returns `null` for a malformed target (no
43
+ * colon, empty kind, empty id, or an unknown kind).
44
+ *
45
+ * @param {string} target
46
+ * @returns {{ kind: string, id: string } | null}
47
+ */
48
+ export function parseRouteTarget(target) {
49
+ const sep = target.indexOf(':');
50
+ if (sep <= 0) return null;
51
+ const kind = target.slice(0, sep);
52
+ const id = target.slice(sep + 1);
53
+ if (!id) return null;
54
+ if (!Object.prototype.hasOwnProperty.call(ROUTE_TARGET_TEMPLATES, kind)) return null;
55
+ return { kind, id };
56
+ }
57
+
58
+ /**
59
+ * POSIX package-relative candidate paths for one `routes_to` target.
60
+ * Empty array = the target is unparseable or its kind is unknown.
61
+ *
62
+ * @param {string} target
63
+ * @returns {string[]}
64
+ */
65
+ export function routeTargetPathsPosix(target) {
66
+ const parsed = parseRouteTarget(target);
67
+ if (parsed === null) return [];
68
+ return ROUTE_TARGET_TEMPLATES[parsed.kind].map((tpl) => tpl.replace('{id}', parsed.id));
69
+ }