@event4u/agent-config 14.6.0 → 14.7.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 (188) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +260 -7
  3. package/README.md +1 -1
  4. package/dist/agent-src/commands/analyze/inbox.md +264 -10
  5. package/dist/agent-src/commands/analyze.md +1 -1
  6. package/dist/agent-src/commands/roadmap/process-full.md +116 -30
  7. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +31 -0
  8. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +135 -104
  9. package/dist/agent-src/rules/context-hygiene.md +2 -0
  10. package/dist/agent-src/rules/decision-revisit-gate.md +3 -0
  11. package/dist/agent-src/rules/fix-what-you-see.md +108 -0
  12. package/dist/agent-src/rules/no-roadmap-references.md +0 -5
  13. package/dist/agent-src/rules/recurring-criticism.md +110 -0
  14. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  15. package/dist/agent-src/rules/rule-type-governance.md +0 -2
  16. package/dist/agent-src/rules/self-repair-loop.md +17 -0
  17. package/dist/agent-src/rules/skill-quality.md +0 -2
  18. package/dist/agent-src/rules/source-confidentiality.md +37 -7
  19. package/dist/agent-src/scripts/archival_sweep.ts +117 -0
  20. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +19 -3
  21. package/dist/agent-src/scripts/gate_budget.ts +282 -0
  22. package/dist/agent-src/scripts/gate_execute.ts +72 -31
  23. package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
  24. package/dist/agent-src/scripts/update_roadmap_progress.ts +251 -7
  25. package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
  26. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
  27. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
  28. package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
  29. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
  30. package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
  31. package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
  32. package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
  33. package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
  34. package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
  35. package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
  36. package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
  37. package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
  38. package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
  39. package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
  40. package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
  41. package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
  42. package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
  43. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  44. package/dist/agent-src/templates/roadmaps.md +45 -2
  45. package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  46. package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  47. package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  48. package/dist/cli/registry.js +1 -1
  49. package/dist/cli/registry.js.map +1 -1
  50. package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
  51. package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-4VR4ORQ6.js} +10 -7
  52. package/dist/cli-delegate/chunk-6EOCNQAM.js +402 -0
  53. package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-74E3TKWV.js} +381 -280
  54. package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-7F3VYSM2.js} +4 -4
  55. package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
  56. package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
  57. package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-GJ5BVHXM.js} +72 -165
  58. package/dist/cli-delegate/{chunk-P6LQDIWK.js → chunk-JZ4NR2TW.js} +338 -0
  59. package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
  60. package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
  61. package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
  62. package/dist/cli-delegate/cmd_conformance.js +459 -709
  63. package/dist/cli-delegate/cmd_doctor.js +6 -6
  64. package/dist/cli-delegate/cmd_explain.js +1 -1
  65. package/dist/cli-delegate/cmd_export.js +7 -6
  66. package/dist/cli-delegate/cmd_handoff.js +6 -6
  67. package/dist/cli-delegate/cmd_packs_active.js +1 -1
  68. package/dist/cli-delegate/cmd_preflight.js +2 -2
  69. package/dist/cli-delegate/cmd_prune.js +7 -6
  70. package/dist/cli-delegate/cmd_session_recycle.js +840 -30
  71. package/dist/cli-delegate/cmd_settings_migrate.js +7 -6
  72. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  73. package/dist/cli-delegate/cmd_sync.js +7 -6
  74. package/dist/cli-delegate/cmd_uninstall.js +9 -7
  75. package/dist/cli-delegate/cmd_update.js +1 -1
  76. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  77. package/dist/cli-delegate/cmd_validate.js +7 -6
  78. package/dist/discovery/deprecation-report.md +1 -1
  79. package/dist/discovery/discovery-manifest.json +68 -25
  80. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  81. package/dist/discovery/discovery-manifest.summary.md +4 -4
  82. package/dist/discovery/dormancy-report.md +1 -1
  83. package/dist/discovery/orphan-report.md +1 -1
  84. package/dist/discovery/packs.json +10 -8
  85. package/dist/discovery/trust-report.md +3 -3
  86. package/dist/discovery/workspaces.json +8 -6
  87. package/dist/hooks/dispatch.js +2901 -2053
  88. package/dist/install/claudePathsPlan.js +187 -0
  89. package/dist/install/claudePathsPlan.js.map +1 -0
  90. package/dist/install/hostLayerFingerprint.js +136 -0
  91. package/dist/install/hostLayerFingerprint.js.map +1 -0
  92. package/dist/install/install.mjs +553 -425
  93. package/dist/install/partitionEligibility.js +276 -0
  94. package/dist/install/partitionEligibility.js.map +1 -0
  95. package/dist/mcp/registry-manifest.json +3 -3
  96. package/dist/mcp/server.json +2 -2
  97. package/dist/mcp/server.mjs +1744 -1191
  98. package/dist/router.json +1 -1
  99. package/dist/scripts/_lib/agent_src.js +41 -0
  100. package/dist/scripts/_lib/agent_src.js.map +1 -1
  101. package/dist/scripts/_lib/install_layout.js +83 -0
  102. package/dist/scripts/_lib/install_layout.js.map +1 -0
  103. package/dist/scripts/_lib/installed_lock.js +444 -0
  104. package/dist/scripts/_lib/installed_lock.js.map +1 -0
  105. package/dist/scripts/condense.js +13 -164
  106. package/dist/scripts/condense.js.map +1 -1
  107. package/dist/server/schemas/settings.js +4 -0
  108. package/dist/server/schemas/settings.js.map +1 -1
  109. package/docs/contracts/settings-classes.md +12 -6
  110. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
  111. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
  112. package/docs/guidelines/agent-infra/memory-access.md +17 -0
  113. package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
  114. package/package.json +1 -1
  115. package/src/agent-src/scripts/archival_sweep.ts +117 -0
  116. package/src/agent-src/scripts/archive_completed_roadmaps.ts +19 -3
  117. package/src/agent-src/scripts/gate_budget.ts +282 -0
  118. package/src/agent-src/scripts/gate_execute.ts +72 -31
  119. package/src/agent-src/scripts/roadmap_gates.ts +74 -3
  120. package/src/agent-src/scripts/update_roadmap_progress.ts +251 -7
  121. package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  122. package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  123. package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  124. package/src/config/agent-settings.template.yml +27 -2
  125. package/src/config/ci-local-parity.yml +18 -0
  126. package/src/config/estate-count-budget.json +339 -3
  127. package/src/config/gate-violation-baselines.json +41 -41
  128. package/src/config/rule-activation-census.json +14 -6
  129. package/src/config/rule-stub-ceilings.json +19 -6
  130. package/src/install/claudePathsPlan.ts +196 -0
  131. package/src/install/hostLayerFingerprint.ts +145 -0
  132. package/src/install/partitionEligibility.ts +325 -0
  133. package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
  134. package/src/scripts/_dispatch.bash +6 -1
  135. package/src/scripts/_lib/ac_heading.ts +30 -0
  136. package/src/scripts/_lib/agent_src.ts +42 -0
  137. package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
  138. package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
  139. package/src/scripts/_lib/envelope_grounding.ts +38 -8
  140. package/src/scripts/_lib/installed_lock.ts +50 -5
  141. package/src/scripts/_lib/md_section.ts +129 -0
  142. package/src/scripts/_lib/scope_guard.sh +26 -3
  143. package/src/scripts/_lib/self_repair.ts +19 -0
  144. package/src/scripts/_lib/self_repair_class_b.ts +171 -0
  145. package/src/scripts/ai_council/cli_help.ts +55 -0
  146. package/src/scripts/before_complete_hook.ts +153 -28
  147. package/src/scripts/bench_ab_v2_complexity.ts +27 -7
  148. package/src/scripts/check_references.ts +60 -0
  149. package/src/scripts/check_release_published.ts +32 -11
  150. package/src/scripts/check_single_delivery.ts +59 -15
  151. package/src/scripts/ci_settle.ts +170 -0
  152. package/src/scripts/condense.ts +20 -170
  153. package/src/scripts/council_cli.ts +3 -3
  154. package/src/scripts/dispatch_r2_reviewer.ts +6 -14
  155. package/src/scripts/hook_manifest.json +1 -1
  156. package/src/scripts/hook_manifest.yaml +77 -14
  157. package/src/scripts/hooks/block_no_verify.ts +61 -10
  158. package/src/scripts/hooks/block_unauthorized_git.ts +1 -1
  159. package/src/scripts/hooks/concern_registry.ts +4 -0
  160. package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
  161. package/src/scripts/hooks/run_continuation_hook.ts +8 -5
  162. package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
  163. package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
  164. package/src/scripts/hooks/state_io.ts +782 -20
  165. package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
  166. package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
  167. package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
  168. package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
  169. package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
  170. package/src/scripts/install.ts +16 -11
  171. package/src/scripts/language_mirror_hook.ts +561 -25
  172. package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
  173. package/src/scripts/lint_plan_risk_register.ts +4 -3
  174. package/src/scripts/lint_skill_router_head.ts +4 -4
  175. package/src/scripts/mcp_server/tools.ts +2 -2
  176. package/src/scripts/new_roadmap.ts +136 -0
  177. package/src/scripts/pack_mcp_content.ts +20 -5
  178. package/src/scripts/release.ts +37 -38
  179. package/src/scripts/report_layer_overlap.ts +6 -0
  180. package/src/scripts/self_repair_hook.ts +37 -5
  181. package/src/scripts/session_register_hook.ts +11 -0
  182. package/src/scripts/skill_usage_collect.ts +11 -1
  183. package/src/scripts/skill_usage_report.ts +278 -8
  184. package/src/scripts/sync_pr_branch.ts +7 -0
  185. package/src/server/schemas/settings.ts +8 -0
  186. package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
  187. package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
  188. /package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
@@ -88,9 +88,9 @@
88
88
  "reason": "Quoted anti-example ('❌ Describing how Laravel middleware works') — the line teaches what NOT to write."
89
89
  },
90
90
  {
91
- "file": "src/skills/skill-writing/SKILL.md",
91
+ "file": "src/skills/skill-writing/references/procedure.md",
92
92
  "anchor": "Use when creating Laravel middleware for request filtering",
93
- "reason": "Quoted description examples + the 'too-broad skill (Laravel skill)' anti-pattern — meta content."
93
+ "reason": "Quoted description examples + the 'too-broad skill (Laravel skill)' anti-pattern — meta content. Repointed 2026-08-20: the K6 router-head retrofit moved the quoted description examples out of the head into references/procedure.md, so the anchor no longer matched its file and the entry exempted nothing. The content is unchanged and still meta."
94
94
  },
95
95
  {
96
96
  "file": "src/skills/supply-chain-intake/SKILL.md",
@@ -39,6 +39,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
39
39
 
40
40
  import { parse as parseYaml } from 'yaml';
41
41
 
42
+ import { isAcceptanceCriteriaHeading } from './_lib/ac_heading.js';
42
43
  import { workspaceIdentity } from './_lib/git_common_dir.js';
43
44
  import { gitEnv } from './_lib/git_env.js';
44
45
  import { splitMarkdownRow } from './_lib/md_table.js';
@@ -52,7 +53,7 @@ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
52
53
  */
53
54
  export const RISK_REGISTER_GATE_ACTIVATION = '2026-08-04';
54
55
 
55
- const MARKER_RE =
56
+ export const MARKER_RE =
56
57
  /^<!--\s*risk-review:\s*v1\s*\|\s*reviewed:\s*(\d{4}-\d{2}-\d{2})\s*\|\s*reviewer:\s*(.+?)\s*-->\s*$/;
57
58
  const HONEST_NULL_RE = /^\*\*Honest-null:\*\*(.*)$/;
58
59
  const CHECKBOX_RE = /^\s*- \[( |x|~|-)\]/;
@@ -72,7 +73,7 @@ export interface PlanFeatures {
72
73
  phaseHeadings: string[];
73
74
  /** Total count of deliverable checkbox lines (state-insensitive). */
74
75
  checkboxCount: number;
75
- /** sha256 of the `## Acceptance Criteria` section body (checkbox state normalized). */
76
+ /** sha256 of the acceptance-criteria section body (heading matched per `_lib/ac_heading`; checkbox state normalized). */
76
77
  acHash: string;
77
78
  }
78
79
 
@@ -115,7 +116,7 @@ export function extractFeatures(text: string): PlanFeatures {
115
116
  if (CHECKBOX_RE.test(entry.line)) {
116
117
  checkboxCount += 1;
117
118
  }
118
- if (/^##\s+Acceptance Criteria\s*$/.test(entry.line)) {
119
+ if (isAcceptanceCriteriaHeading(entry.line)) {
119
120
  acStart = i;
120
121
  }
121
122
  }
@@ -59,10 +59,10 @@ export const MODE_BODY_DIRS: readonly string[] = ['tasks', 'references'];
59
59
  * Measured, not guessed. REMOVE entries as skills are restructured; never add.
60
60
  */
61
61
  export const GRANDFATHERED: readonly string[] = [
62
- 'ai-council', // 1055
63
- 'skill-writing', // 767
64
- 'roadmap-management', // 552
65
- 'quality-tools', // 445
62
+ // Emptied 2026-08-20: all four seeded entries were restructured into router
63
+ // heads (entry head + `references/` mode bodies) under the published K6 cap.
64
+ // Shrink-only means this list may never grow again — a new oversized
65
+ // monolith fails the gate instead of joining it.
66
66
  ];
67
67
 
68
68
  export interface Finding {
@@ -669,7 +669,7 @@ async function _roadmapProgressHandler(
669
669
  };
670
670
  }
671
671
  const roadmaps = urp.collect(roadmapRoot);
672
- const content = urp.render(roadmaps, urp.collect_bundles(root));
672
+ const content = urp.render(roadmaps, urp.collect_bundles(root), roadmapRoot);
673
673
  const stepsDone = roadmaps.reduce((s, r) => s + r.done, 0);
674
674
  const stepsTotal = roadmaps.reduce((s, r) => s + r.total_active, 0);
675
675
  if (!dryRun) {
@@ -719,7 +719,7 @@ async function _roadmapArchiveHandler(
719
719
  const roadmaps = urp.collect(roadmapRoot);
720
720
  fs.writeFileSync(
721
721
  path.join(root, 'agents', 'roadmaps-progress.md'),
722
- urp.render(roadmaps, urp.collect_bundles(root)),
722
+ urp.render(roadmaps, urp.collect_bundles(root), roadmapRoot),
723
723
  'utf-8',
724
724
  );
725
725
  dashboardRegenerated = true;
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Emit a roadmap skeleton that passes every roadmap gate unedited.
4
+ *
5
+ * WHY THIS EXISTS. Four authoring conventions are enforced by four different
6
+ * gates and documented in prose an author has to already know to look for:
7
+ *
8
+ * 1. `complexity:` is a two-value enum — `lightweight` | `structural`.
9
+ * Measured 2026-08-20 across 88 roadmaps: six DIFFERENT invented values
10
+ * were in the tree (`bounded`, `medium`, `moderate`, `small`, `standard`),
11
+ * one of them on the active side and reddening the trunk, seven more in
12
+ * `later/` where the gate does not scan and which would red on the day
13
+ * they are reactivated.
14
+ * 2. The acceptance heading is matched CASE-INSENSITIVELY and without an end
15
+ * anchor — one predicate, `_lib/ac_heading.ts`, shared by the R1 gate and
16
+ * the R2 dispatcher. It was not always: an end-anchored, case-sensitive
17
+ * copy made 10 of 22 roadmaps writing `Acceptance criteria` invisible to
18
+ * the extractor, and a third copy in `lint_plan_risk_register` kept both
19
+ * faults until 2026-08-20, hashing the empty string for 8 of the 32 ready
20
+ * roadmaps while the gate exited 0. The skeleton still emits the
21
+ * capitalised form: it is the shape every reader already recognises, and
22
+ * nothing now depends on the case.
23
+ * 3. The Risk Register needs its `<!-- risk-review: … -->` marker as the
24
+ * first non-blank line of the section.
25
+ * 4. `Risk type` is a two-value enum — `product` | `implementation`. The
26
+ * natural third word (`process`) hard-fails at pre-push.
27
+ *
28
+ * Every one of those is cheap to satisfy up front and costs a full gate round
29
+ * to discover. This command satisfies them by construction.
30
+ *
31
+ * The skeleton is deliberately MINIMAL: one phase, one step, one risk row. It
32
+ * is a starting point that is green, not a template to fill in blindly — the
33
+ * content is the author's, the shape is not worth rediscovering.
34
+ *
35
+ * Usage: ./scripts-run src/scripts/new_roadmap <slug> [--structural] [--stdout]
36
+ */
37
+
38
+ import * as fs from 'node:fs';
39
+ import * as path from 'node:path';
40
+
41
+ /** Today, as the risk-review marker wants it. */
42
+ function today(): string {
43
+ return new Date().toISOString().slice(0, 10);
44
+ }
45
+
46
+ /** `road to X` from a kebab slug, for the H1. */
47
+ function titleOf(slug: string): string {
48
+ return slug.replace(/-/g, ' ');
49
+ }
50
+
51
+ export function skeleton(slug: string, complexity: 'lightweight' | 'structural', date: string): string {
52
+ return `---
53
+ complexity: ${complexity}
54
+ status: draft
55
+ execution:
56
+ mode: phase-checkpoints
57
+ ---
58
+ # Road to ${titleOf(slug)}
59
+
60
+ > **Source:** replace this with where the finding came from — a measurement, a
61
+ > review, an incident. A roadmap whose origin is not stated cannot be checked
62
+ > against it later.
63
+
64
+ ## Goal
65
+
66
+ One paragraph: what is true when this is finished, phrased so someone else
67
+ could tell whether it happened.
68
+
69
+ ## Phase 1 — <name the first coherent slice>
70
+
71
+ - [ ] **1.1 <the step, as an imperative>.** What changes, and why it is not
72
+ already the case.
73
+ verify: the check that proves it — a command, a test, an observable
74
+ state. Never "looks right".
75
+
76
+ ## Risk Register
77
+ <!-- risk-review: v1 | reviewed: ${date} | reviewer: claude/host -->
78
+
79
+ | Rank | Item | Risk type | Description | Mitigation | Anchored under |
80
+ |------|------|-----------|-------------|------------|----------------|
81
+ | 1 | <the way this roadmap most plausibly fails> | implementation | What goes wrong, concretely | What makes it not happen, or what catches it | Phase 1 — <name the first coherent slice> |
82
+
83
+ ## Acceptance Criteria
84
+
85
+ - [ ] AC-1 — <the residual state, not the activity>. Phrase it on what is true
86
+ afterwards, so a step that ran but achieved nothing cannot satisfy it.
87
+ `;
88
+ }
89
+
90
+ function main(argv: readonly string[]): number {
91
+ const args = argv.filter((a) => !a.startsWith('--'));
92
+ const structural = argv.includes('--structural');
93
+ const toStdout = argv.includes('--stdout');
94
+ const slug = args[0];
95
+
96
+ if (slug === undefined || slug.length === 0) {
97
+ process.stderr.write(
98
+ 'usage: new_roadmap <slug> [--structural] [--stdout]\n' +
99
+ ' <slug> kebab-case, without the road-to- prefix\n' +
100
+ ' --structural tag structural instead of lightweight (no line/phase cap)\n' +
101
+ ' --stdout print instead of writing the file\n',
102
+ );
103
+ return 2;
104
+ }
105
+ if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) {
106
+ process.stderr.write(`new_roadmap: slug must be kebab-case, got '${slug}'\n`);
107
+ return 2;
108
+ }
109
+
110
+ const body = skeleton(slug, structural ? 'structural' : 'lightweight', today());
111
+ if (toStdout) {
112
+ process.stdout.write(body);
113
+ return 0;
114
+ }
115
+
116
+ const rel = path.join('agents', 'roadmaps', `road-to-${slug}.md`);
117
+ if (fs.existsSync(rel)) {
118
+ process.stderr.write(`new_roadmap: ${rel} already exists — refusing to overwrite\n`);
119
+ return 1;
120
+ }
121
+ fs.mkdirSync(path.dirname(rel), { recursive: true });
122
+ fs.writeFileSync(rel, body, 'utf-8');
123
+ process.stdout.write(
124
+ `✅ wrote ${rel}\n` +
125
+ ' It passes the roadmap gates as emitted. Adding a `### blocker:` entry or\n' +
126
+ ' flipping status to `ready` grows the estate — check_estate_count wants the\n' +
127
+ ' baseline raised with a reason in the same commit.\n',
128
+ );
129
+ return 0;
130
+ }
131
+
132
+ if (process.argv[1] !== undefined && import.meta.url.endsWith(path.basename(process.argv[1]))) {
133
+ process.exitCode = main(process.argv.slice(2));
134
+ }
135
+
136
+ export { main, titleOf, today };
@@ -41,11 +41,26 @@ import { scan_contexts, scan_guidelines, scan_rules } from './mcp_server/resourc
41
41
 
42
42
  const SCHEMA_VERSION = 1;
43
43
  const PACKER_VERSION = '1.0.0';
44
- // Worker bundle is the compact JSON; gzipped copy lives in R2. Cloudflare's
45
- // condensed-bundle limit is 3 MB (free) / 10 MB (paid); 778 KB gz today
46
- // (438 entries) leaves ample headroom. Hard-fail at 5 MB uncondensed so
47
- // the build dies before the Worker upload does.
48
- const MAX_UNCONDENSED_BYTES = 5 * 1024 * 1024;
44
+ // Worker bundle is the compact JSON; gzipped copy lives in R2. This constant is
45
+ // a PROXY for Cloudflare's condensed-bundle limit (3 MB free / 10 MB paid) — it
46
+ // hard-fails early so the build dies before the Worker upload does. Because it is
47
+ // a proxy, the number that matters is the gz one, and both are restated here as
48
+ // MEASURED values rather than remembered ones.
49
+ //
50
+ // Measured 2026-08-20 on origin/main (770 entries): uncondensed 5,230,537 B,
51
+ // gz 1,793,879 B. So the real constraint sits at 57 % of the free tier with
52
+ // ~1.2 MB of room, while the old 5 MiB proxy had 12,343 B of headroom — a 0.24 %
53
+ // margin on trunk, which made ANY addition above ~12 KB to rules, guidelines or
54
+ // contexts fail here regardless of merit. Raised to 6 MiB, deliberately well
55
+ // above the measurement: a cap pinned just over the current figure reds on the
56
+ // next commit and teaches people to raise it again.
57
+ //
58
+ // The prior comment claimed "778 KB gz today (438 entries)". That was stale by
59
+ // 2.3x on size and 332 entries — the reason the proxy drifted into a blocker
60
+ // without anyone noticing was that its own justification was never re-measured.
61
+ // If this needs raising again, re-measure BOTH numbers and replace them here;
62
+ // the gz figure against 3 MB is the one that decides whether there is a problem.
63
+ const MAX_UNCONDENSED_BYTES = 6 * 1024 * 1024;
49
64
 
50
65
  /** Sentinel for the early-exit paths (`raise SystemExit(n)` in Python). */
51
66
  class SystemExit extends Error {
@@ -2475,55 +2475,54 @@ export function nothing_to_release_ci(
2475
2475
 
2476
2476
  const _RELEASE_BRANCH_RE = /^release\/(\d+\.\d+\.\d+)$/;
2477
2477
 
2478
+ /** `package.json`'s `version`, or null when unreadable, not JSON, or key-less. */
2479
+ function _package_version_or_null(): string | null {
2480
+ try {
2481
+ const data = JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf-8')) as Record<string, unknown>;
2482
+ return 'version' in data ? (data['version'] as string) : null;
2483
+ } catch {
2484
+ return null;
2485
+ }
2486
+ }
2487
+
2488
+ /** Observations `_detect_in_flight_target` reads; injected by its tests. */
2489
+ export interface InFlightProbes {
2490
+ head_branch?: () => string;
2491
+ package_version?: () => string | null;
2492
+ /** True when the tag is ON THE REMOTE — never merely local. */
2493
+ tag_published?: (tag: string) => boolean;
2494
+ }
2495
+
2478
2496
  /**
2479
- * Find the in-flight release target — the SOURCE OF TRUTH is package.json.
2480
- *
2481
- * An "in-flight" release is one whose version was already bumped into `main`'s
2482
- * `package.json` (and possibly merged) but whose tag has not yet been pushed —
2483
- * i.e. the publish step never completed. The canonical anchor is therefore
2484
- * `package.json` version `V` **with no matching tag `V`**, NOT the set of
2485
- * `release/X.Y.Z` branches.
2486
- *
2487
- * Why not the branch set: merged release branches are frequently left
2488
- * undeleted on the remote, so "highest existing release/* branch" can resolve
2489
- * to an OLD, already-published version (e.g. picking 5.4.0 while 5.8.0 is the
2490
- * real in-flight target) and tag a downgrade. The package.json version cannot
2491
- * lie that way — it is the version main currently claims to be, and an
2492
- * untagged claim is exactly an incomplete release.
2493
- *
2494
- * Resolution order:
2495
- * 1. If HEAD is on a `release/X.Y.Z` branch, that explicit checkout wins.
2496
- * 2. Else: read `package.json` version `V`. If tag `V` does not exist
2497
- * (local or remote), `V` is the in-flight target. If it is already
2498
- * tagged, the release is complete → return null (regular bump path).
2499
- *
2500
- * Stale `release/*` branches are never used for version detection.
2497
+ * In-flight release target: `package.json` claims `V` while no PUBLISHED tag
2498
+ * `V` exists, so the publish step never completed. HEAD on `release/X.Y.Z`
2499
+ * wins first; stale `release/*` branches are never consulted (a merged one
2500
+ * left undeleted can tag a downgrade). PUBLISHED means on the REMOTE, because
2501
+ * publish-npm.yml triggers on `push: tags:`. Why not a local tag, and the
2502
+ * 14.6.0 release that stranded on the old form: see
2503
+ * `tests/scripts/release.test.ts` § _detect_in_flight_target.
2501
2504
  */
2502
- function _detect_in_flight_target(): string | null {
2503
- const head = git(['rev-parse', '--abbrev-ref', 'HEAD'], { capture: true });
2504
- const m = _RELEASE_BRANCH_RE.exec(head);
2505
+ function _detect_in_flight_target(probes: InFlightProbes = {}): string | null {
2506
+ const head_branch =
2507
+ probes.head_branch ?? (() => git(['rev-parse', '--abbrev-ref', 'HEAD'], { capture: true }));
2508
+ const package_version = probes.package_version ?? _package_version_or_null;
2509
+ const tag_published = probes.tag_published ?? _tag_exists_remote;
2510
+
2511
+ const m = _RELEASE_BRANCH_RE.exec(head_branch());
2505
2512
  if (m) {
2506
2513
  return m[1] as string;
2507
2514
  }
2508
-
2509
- let version: string;
2510
- try {
2511
- const data = JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf-8')) as Record<string, unknown>;
2512
- if (!('version' in data)) {
2513
- return null; // KeyError analogue.
2514
- }
2515
- version = data['version'] as string;
2516
- } catch {
2517
- return null; // OSError / JSONDecodeError analogue.
2515
+ const version = package_version();
2516
+ if (version === null) {
2517
+ return null;
2518
2518
  }
2519
2519
  try {
2520
2520
  parse_version(version);
2521
2521
  } catch {
2522
2522
  return null;
2523
2523
  }
2524
-
2525
- // An already-tagged version is a completed release, not in-flight.
2526
- if (_tag_exists_local(version) || _tag_exists_remote(version)) {
2524
+ // A PUBLISHED tag is a completed release; a local-only tag is not.
2525
+ if (tag_published(version)) {
2527
2526
  return null;
2528
2527
  }
2529
2528
  return version;
@@ -26,6 +26,12 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
26
26
 
27
27
  import { warnLayerOverlap } from './_lib/layer_overlap_notice.js';
28
28
 
29
+ // ledger-exempt: this is a REPORTER, not a gate — it always exits 0 by construction
30
+ // (see the docstring above), so there is no verdict for a per-target ledger to
31
+ // account for. `_lib/gate_ledger` records why a gate passed over a target; a script
32
+ // that cannot fail passes over nothing. The advisory-only property is asserted by the
33
+ // task step that calls it, not by this marker.
34
+
29
35
  const _HERE = fileURLToPath(import.meta.url);
30
36
  const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
31
37
 
@@ -38,6 +38,7 @@ import {
38
38
  } from './_lib/self_repair.js';
39
39
  import { openRecords, upsertFinding } from './_lib/self_repair_store.js';
40
40
  import { readHookStdin } from './hooks/hook_stdin.js';
41
+ import { emitDefectShadow } from './hooks/telemetry_self_repair.js';
41
42
 
42
43
  const EXIT_ALLOW = 0;
43
44
 
@@ -130,11 +131,28 @@ export function readTurn(transcriptPath: string, pinned: 'de' | 'en' | null): Tu
130
131
  return { prompt, reply, toolCommands, pinnedLanguage: pinned };
131
132
  }
132
133
 
133
- /** The queue line injected at prompt time when records are waiting. */
134
- export function buildQueueLine(count: number, newest: string): string {
134
+ /**
135
+ * The queue line injected at prompt time when records are waiting.
136
+ *
137
+ * `occurrences` is the newest record's own repeat counter, and it is in the line
138
+ * because the store has always counted while the model never saw the number: a
139
+ * recurring defect increments one record instead of adding a queue entry, so
140
+ * `count` stays flat and the recurrence is invisible. Above 1 it is the only
141
+ * deterministic "this came back" signal an agent gets, which is what makes
142
+ * `decision-revisit-gate` § Recurrence executable rather than aspirational.
143
+ * Omitted at 1 — a first sighting has nothing to say.
144
+ */
145
+ export function buildQueueLine(count: number, newest: string, occurrences = 1): string {
146
+ const repeat =
147
+ occurrences > 1
148
+ ? ` The newest record has recurred ${occurrences} time(s): the earlier ` +
149
+ `disposition did not hold, so per decision-revisit-gate § Recurrence ` +
150
+ `reopen it and name which assumption broke — resolve on evidence, ` +
151
+ `never on the repetition count.`
152
+ : '';
135
153
  return (
136
154
  `<self-repair-queue>${count} open agent-config defect record(s) ` +
137
- `(newest: ${newest}). These are defects in the AGENT CONFIG, not in this ` +
155
+ `(newest: ${newest}).${repeat} These are defects in the AGENT CONFIG, not in this ` +
138
156
  `project. Per the self-repair rule: analyse the newest record, author the ` +
139
157
  `fix against agent-config, and prepare the release — the outward step ` +
140
158
  `(push + PR, or an issue) still needs the user's word this turn. ` +
@@ -185,7 +203,17 @@ export function main(): number {
185
203
 
186
204
  for (const f of findings) {
187
205
  try {
188
- upsertFinding(root, f, now);
206
+ const rec = upsertFinding(root, f, now);
207
+ // The Class-A shadow (road-to-org-telemetry Phase 5, step 5.1).
208
+ // Structural fields only — the record type has no member able to
209
+ // hold `f.evidence` or `f.suggested_surface`, which are the
210
+ // Class-B payload and ship only on per-case approval. Inert unless
211
+ // the org pack activated telemetry; a refused record (`null` from
212
+ // the creation cap) emits nothing, because there is no queued
213
+ // defect to shadow.
214
+ if (rec !== null) {
215
+ emitDefectShadow(root, rec, env);
216
+ }
189
217
  } catch {
190
218
  // an unwritable store must never cost the user their turn
191
219
  }
@@ -208,7 +236,11 @@ export function main(): number {
208
236
  `${JSON.stringify({
209
237
  decision: 'allow',
210
238
  reason: `self-repair: ${open.length} open record(s)`,
211
- context: buildQueueLine(open.length, open[0]!.defect_class),
239
+ context: buildQueueLine(
240
+ open.length,
241
+ open[0]!.defect_class,
242
+ open[0]!.occurrences,
243
+ ),
212
244
  })}\n`,
213
245
  );
214
246
  return EXIT_ALLOW;
@@ -715,6 +715,17 @@ export function foreign_sessions_block(
715
715
  '',
716
716
  `COLLISION: another live session is on branch \`${here}\` in THIS SAME worktree`,
717
717
  `(${branch_hit.record.worktree}) — the same files and the same git index.`,
718
+ 'What "join anyway" actually costs, because option 1 is chosen routinely and',
719
+ 'the price was not in this text until 2026-08-20 (road-to-session-closeout 6.3):',
720
+ ' · SHARED INDEX — `git add -A` stages the peer\'s work too. Measured: a peer',
721
+ ' session\'s untracked roadmap was swept into an unrelated merge commit and',
722
+ ' surfaced three commits later at an estate gate. Use pathspecs, never -A.',
723
+ ' · SHARED STASH STACK — `git stash` is repo-wide, so your stash is visible and',
724
+ ' poppable by the peer, and `stash@{0}` means a different thing to each of you.',
725
+ ' · PRE-PUSH runs over the shared tree — it sees the peer\'s files, not only',
726
+ ' yours. `task consistency` is now scoped to the paths it regenerates, so a',
727
+ ' peer\'s unrelated edit no longer fails it; anything else in that hook still',
728
+ ' reads one tree written by two sessions.',
718
729
  'Ask the user ONCE PER SESSION, as numbered options, before writing anything:',
719
730
  ' 1. Join anyway and coordinate manually (this register is advisory, not a lock)',
720
731
  ' 2. Spawn a separate worktree for this session',
@@ -31,7 +31,17 @@ const _HERE = fileURLToPath(import.meta.url);
31
31
 
32
32
  // src/scripts/skill_usage_collect.ts → parents[2] of the .py file is repo root.
33
33
  export const REPO = path.resolve(path.dirname(_HERE), '..', '..');
34
- const OUT = path.join(REPO, 'agents', 'metrics', 'skill-usage.jsonl');
34
+ // `agents/metrics/` was retired by the agents/ taxonomy consolidation (the
35
+ // same finding `skill_overlap.ts` records), so this collector was appending to
36
+ // a directory that does not exist while the real record set sat untouched at
37
+ // `agents/runtime/metrics/skill-usage.jsonl` — last written 2026-05-16. The
38
+ // report script read the same dead path, so the pair agreed with each other
39
+ // and with nothing else. road-to-org-telemetry Phase 0 measured the
40
+ // consequence (spike 3: 0 of 89 invocations detected on the set the collector
41
+ // reads) and named the repair a Phase 4 prerequisite. `runtime/` is the typed,
42
+ // gitignored home for a record set nothing tracks, per the agents/ layout
43
+ // contract.
44
+ const OUT = path.join(REPO, 'agents', 'runtime', 'metrics', 'skill-usage.jsonl');
35
45
 
36
46
  // LISTING_LINE_RE = re.compile(r"^-\s+([a-z0-9][a-z0-9_-]+):\s", re.MULTILINE)
37
47
  const LISTING_LINE_RE = /^-\s+([a-z0-9][a-z0-9_-]+):\s/gm;