@event4u/agent-config 12.0.0 → 12.1.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 (89) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +122 -0
  3. package/agents/templates/.ai-council.yml.example +46 -3
  4. package/dist/agent-src/contexts/model-recommendations.md +6 -4
  5. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  6. package/dist/agent-src/scripts/roadmap_gates.ts +30 -2
  7. package/dist/agent-src/scripts/update_roadmap_progress.ts +32 -1
  8. package/dist/agent-src/skills/roadmap-writing/SKILL.md +7 -7
  9. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  10. package/dist/agent-src/templates/roadmaps.md +41 -3
  11. package/dist/cli/registry.js +1 -0
  12. package/dist/cli/registry.js.map +1 -1
  13. package/dist/cli-delegate/chunk-K34QTSD5.js +3895 -0
  14. package/dist/cli-delegate/{chunk-L565YFXH.js → chunk-LKZ5DOYW.js} +557 -297
  15. package/dist/cli-delegate/{chunk-NN4KW4HZ.js → chunk-O52B4F5S.js} +123 -2338
  16. package/dist/cli-delegate/cmd_conformance.js +4 -4
  17. package/dist/cli-delegate/cmd_doctor.js +2 -2
  18. package/dist/cli-delegate/cmd_export.js +1 -1
  19. package/dist/cli-delegate/cmd_handoff.js +1 -1
  20. package/dist/cli-delegate/cmd_prune.js +1 -1
  21. package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
  22. package/dist/cli-delegate/cmd_sync.js +1 -1
  23. package/dist/cli-delegate/cmd_uninstall.js +1 -1
  24. package/dist/cli-delegate/cmd_validate.js +1 -1
  25. package/dist/discovery/deprecation-report.md +1 -1
  26. package/dist/discovery/discovery-manifest.json +4 -4
  27. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  28. package/dist/discovery/discovery-manifest.summary.md +1 -1
  29. package/dist/discovery/dormancy-report.md +1 -1
  30. package/dist/discovery/orphan-report.md +1 -1
  31. package/dist/discovery/packs.json +2 -2
  32. package/dist/discovery/trust-report.md +1 -1
  33. package/dist/discovery/workspaces.json +2 -2
  34. package/dist/hooks/dispatch.js +2574 -1080
  35. package/dist/install/install.mjs +744 -482
  36. package/dist/mcp/registry-manifest.json +2 -2
  37. package/dist/mcp/server.json +2 -2
  38. package/dist/mcp/server.mjs +113 -10
  39. package/dist/router.json +1 -1
  40. package/dist/scripts/_lib/model_tier.js +9 -0
  41. package/dist/scripts/_lib/model_tier.js.map +1 -1
  42. package/dist/scripts/condense.js +50 -0
  43. package/dist/scripts/condense.js.map +1 -1
  44. package/docs/MIGRATION.md +20 -1
  45. package/package.json +1 -1
  46. package/src/agent-src/scripts/roadmap_gates.ts +30 -2
  47. package/src/agent-src/scripts/update_roadmap_progress.ts +32 -1
  48. package/src/config/evaluator-budgets.json +4 -4
  49. package/src/config/gate-coverage.yml +39 -9
  50. package/src/config/gate-violation-baselines.json +6 -0
  51. package/src/config/hook-token-budget.json +7 -1
  52. package/src/config/preamble-payload-budget.json +28 -0
  53. package/src/scripts/_dispatch.bash +10 -0
  54. package/src/scripts/_lib/git_common_dir.ts +173 -0
  55. package/src/scripts/_lib/model_tier.ts +9 -0
  56. package/src/scripts/_lib/release_highlights.ts +108 -7
  57. package/src/scripts/_lib/skill_catalogue.ts +862 -0
  58. package/src/scripts/ai_council/clients.ts +182 -3
  59. package/src/scripts/ai_council/config.ts +16 -1
  60. package/src/scripts/ai_council/low_impact.ts +14 -1
  61. package/src/scripts/ai_council/orchestrator.ts +14 -1
  62. package/src/scripts/ai_council/transport_resolver.ts +32 -2
  63. package/src/scripts/capture_skill_catalogue.ts +185 -328
  64. package/src/scripts/check_branch_freshness.ts +7 -1
  65. package/src/scripts/check_release_published.ts +6 -2
  66. package/src/scripts/check_release_trunk_sync.ts +18 -2
  67. package/src/scripts/condense.ts +50 -1
  68. package/src/scripts/dispatch_r2_reviewer.ts +15 -5
  69. package/src/scripts/evidence_report.ts +15 -4
  70. package/src/scripts/hook_manifest.yaml +47 -13
  71. package/src/scripts/hooks/concern_registry.ts +2 -0
  72. package/src/scripts/hooks/orchestration_record_hook.ts +21 -0
  73. package/src/scripts/hooks/tool_result_bytes_hook.ts +219 -0
  74. package/src/scripts/hot_context_hook.ts +13 -1
  75. package/src/scripts/install.ts +129 -0
  76. package/src/scripts/janitor.ts +77 -2
  77. package/src/scripts/lint_plan_risk_register.ts +22 -9
  78. package/src/scripts/lint_roadmap_blockers.ts +109 -6
  79. package/src/scripts/lint_scheduled_deprecations.ts +738 -0
  80. package/src/scripts/migration_status.ts +17 -1
  81. package/src/scripts/probe_review_binding_drift.ts +731 -0
  82. package/src/scripts/release.ts +95 -1
  83. package/src/scripts/schemas/command.schema.json +2 -2
  84. package/src/scripts/schemas/skill.schema.json +2 -1
  85. package/src/scripts/schemas/subagent.schema.json +2 -2
  86. package/src/scripts/session_eol_report.ts +81 -0
  87. package/src/scripts/workspace_doctor.ts +464 -0
  88. package/src/scripts/worktree_cleanup_check.ts +2 -1
  89. package/dist/cli-delegate/chunk-AYKQGIBG.js +0 -1594
@@ -536,7 +536,7 @@ function die(msg: string, code = 2): never {
536
536
  throw new SystemExitError(code);
537
537
  }
538
538
 
539
- interface RunResult {
539
+ export interface RunResult {
540
540
  returncode: number;
541
541
  stdout: string;
542
542
  stderr: string;
@@ -1623,6 +1623,93 @@ function set_template_pin(p: string, version: string): void {
1623
1623
  fs.writeFileSync(p, lines.join('\n'), 'utf-8');
1624
1624
  }
1625
1625
 
1626
+ /**
1627
+ * Refuse a MAJOR cut that carries a scheduled deprecation due at or before it.
1628
+ *
1629
+ * **The trigger is the shape of the TARGET, not a comparison with the current
1630
+ * version.** A major target is `X.0.0` — and that one predicate covers every
1631
+ * path to a major: the `--as major` flag, an explicit `--version 13.0.0`,
1632
+ * auto-detection from a `feat!:` commit, AND `--resume`, where
1633
+ * `_detect_in_flight_target()` returns the already-bumped `package.json`
1634
+ * version so `target === current` and any current-vs-target comparison
1635
+ * silently returns. Resume was the fourth path an earlier version of this
1636
+ * guard missed while its own comment claimed three paths converged.
1637
+ *
1638
+ * Refusing a resumed release can strand a partially-completed one, and that is
1639
+ * the deliberate trade: a stranded release is recoverable by fixing the table
1640
+ * and resuming again, whereas a major shipped over a missed commitment is the
1641
+ * failure this whole surface exists to prevent.
1642
+ *
1643
+ * The target version is PASSED to the gate. Without it the gate falls back to
1644
+ * `package.json`, which at the cut to N still reads N-1 — so a row committed
1645
+ * to N reads as one major early and passes, and only rows already a major late
1646
+ * could ever be refused. That is the lateness being prevented, so measuring
1647
+ * against the shipped version would have made the refusal fire exactly one
1648
+ * major too late, forever.
1649
+ *
1650
+ * Runs under `--dry-run` too, unlike the rest of preflight: this check is one
1651
+ * subprocess reading two files, so the "keep a preview fast" rationale that
1652
+ * excludes the ~15s test-trend collection does not apply — and a preview that
1653
+ * reports green for the single condition that will refuse the real run is the
1654
+ * case an operator runs a preview to discover. It REPORTS there rather than
1655
+ * dying: `--dry-run` exiting 0 before `execute()` and before `preflight()` is a
1656
+ * contract this file's own tests assert, and an earlier revision of this guard
1657
+ * broke it by sitting above the dry-run branch with no preview mode.
1658
+ *
1659
+ * @param runner Seam for the gate invocation. Production passes nothing and
1660
+ * gets the real `run`; tests inject a stub, because the alternative — reaching
1661
+ * this branch only by mutating `docs/MIGRATION.md` — would make the refusal
1662
+ * path testable exclusively through a tracked-file edit.
1663
+ */
1664
+ export function assert_scheduled_deprecations_clear(
1665
+ target: string,
1666
+ runner: (args: readonly string[]) => RunResult = (args) => run(args, { check: false, capture: true }),
1667
+ opts: { previewOnly?: boolean } = {},
1668
+ ): void {
1669
+ const [, minor, patch] = parse_version(target);
1670
+ if (minor !== 0 || patch !== 0) {
1671
+ return;
1672
+ }
1673
+ const res = runner([
1674
+ './scripts-run',
1675
+ 'src/scripts/lint_scheduled_deprecations',
1676
+ '--cutting',
1677
+ target,
1678
+ ]);
1679
+ if (res.returncode === 0) {
1680
+ return;
1681
+ }
1682
+ process.stderr.write(res.stdout);
1683
+ process.stderr.write(res.stderr);
1684
+ if (res.returncode !== 1) {
1685
+ // The gate reserves 1 for a finding and 2 for a usage/environment
1686
+ // failure. Diagnosing the latter as "the table has an overdue row"
1687
+ // sends the releaser to edit a file that is not the problem.
1688
+ die(
1689
+ `refusing the ${target} cut: the scheduled-deprecations check could not run ` +
1690
+ `(exit ${String(res.returncode)}). That is an environment or usage failure, not a ` +
1691
+ 'finding in docs/MIGRATION.md — fix the invocation or the checkout, then re-run.',
1692
+ );
1693
+ }
1694
+ if (opts.previewOnly === true) {
1695
+ // The preview's job is to SHOW what the real run will refuse. Dying
1696
+ // here would break the `--dry-run exits 0` contract asserted elsewhere
1697
+ // in this file's tests — trading a documented exit code for a message
1698
+ // that has already been printed above.
1699
+ process.stderr.write(
1700
+ `\n(dry-run) the ${target} cut WOULD BE REFUSED for the reason above. ` +
1701
+ 'Previewing only; exit code unchanged.\n',
1702
+ );
1703
+ return;
1704
+ }
1705
+ die(
1706
+ `refusing the ${target} cut: the scheduled-deprecations table in docs/MIGRATION.md ` +
1707
+ 'has a row due at or before this major, or one that cannot be resolved. Act on ' +
1708
+ "it — perform the removal in its own change, or revise the row's commitment and " +
1709
+ 'record why the surface stays — then re-run.',
1710
+ );
1711
+ }
1712
+
1626
1713
  // ─── preflight ────────────────────────────────────────────────────────────────
1627
1714
 
1628
1715
  /**
@@ -2578,6 +2665,13 @@ function main(argv: readonly string[] | null = null): number {
2578
2665
  }
2579
2666
  parse_version(target);
2580
2667
 
2668
+ // Runs in dry-run too — a preview that hides the one condition which will
2669
+ // refuse the real run is the case previews are for — but REPORTS there
2670
+ // instead of dying: `--dry-run returns 0` is a contract this file's tests
2671
+ // assert, and breaking it to surface a warning would trade a documented
2672
+ // exit code for a message.
2673
+ assert_scheduled_deprecations_clear(target, undefined, { previewOnly: args.dry_run });
2674
+
2581
2675
  if (!args.dry_run) {
2582
2676
  preflight(target, { resume: args.resume, ci: args.ci });
2583
2677
  }
@@ -158,8 +158,8 @@
158
158
  },
159
159
  "model_tier": {
160
160
  "type": "string",
161
- "enum": ["lite", "medium", "high", "inherit"],
162
- "description": "Vendor-neutral capability band the command needs (ADR-035). Commands project as Claude skills, so high/medium/lite produce a native Claude `model:` (high→opus, medium→sonnet, lite→haiku) when model.auto_switch=auto; the generator owns that mapping. inherit keeps the session model. Non-Claude agents are suggestion-only. No vendor model names here."
161
+ "enum": ["lite", "medium", "high", "frontier", "inherit"],
162
+ "description": "Vendor-neutral capability band the command needs (ADR-035, amended by ADR-232). Commands project as Claude skills, so frontier/high/medium/lite produce a native Claude `model:` when model.auto_switch=auto; the generator owns that mapping. inherit keeps the session model. `frontier` is the band above the strongest generally-recommended one - opt-in and expected to stay rare. Non-Claude agents are suggestion-only. No vendor model names here."
163
163
  },
164
164
  "context": {
165
165
  "type": "string",
@@ -168,9 +168,10 @@
168
168
  "lite",
169
169
  "medium",
170
170
  "high",
171
+ "frontier",
171
172
  "inherit"
172
173
  ],
173
- "description": "Vendor-neutral capability band the skill needs (ADR-035). high/medium/lite project to a native Claude `model:` (high→opus, medium→sonnet, lite→haiku) when model.auto_switch=auto; the generator owns that mapping exclusively. inherit keeps the session model. Non-Claude agents are suggestion-only — the model-recommendation rule names the tier and the agent resolves its own model. No vendor model names here."
174
+ "description": "Vendor-neutral capability band the skill needs (ADR-035, amended by ADR-232). frontier/high/medium/lite project to a native Claude `model:` when model.auto_switch=auto; the generator owns that mapping exclusively. inherit keeps the session model. `frontier` means reasoning capability above the strongest generally-recommended band - opt-in and expected to stay rare, so work that genuinely needs it says so instead of inheriting it silently. Non-Claude agents are suggestion-only - the model-recommendation rule names the tier and the agent resolves its own model. No vendor model names here."
174
175
  },
175
176
  "context": {
176
177
  "type": "string",
@@ -34,8 +34,8 @@
34
34
  },
35
35
  "model_tier": {
36
36
  "type": "string",
37
- "enum": ["lite", "medium", "high", "inherit"],
38
- "description": "Vendor-neutral capability band (ADR-034/035). high/medium/lite project to a native Claude `model:` (high→opus, medium→sonnet, lite→haiku) via model_tier.ts; `inherit` is a documented passthrough sentinel (→ CC `model: inherit`), not a new mapping."
37
+ "enum": ["lite", "medium", "high", "frontier", "inherit"],
38
+ "description": "Vendor-neutral capability band (ADR-034/035, amended by ADR-232). frontier/high/medium/lite project to a native Claude `model:` via model_tier.ts; `inherit` is a documented passthrough sentinel (→ CC `model: inherit`), not a new mapping. `frontier` is the band above the strongest generally-recommended one - opt-in, expected to stay rare, and never the resolution of `inherit`."
39
39
  },
40
40
  "effort": {
41
41
  "type": "string",
@@ -61,6 +61,33 @@ export interface EolReport {
61
61
  compact_summary_records: number;
62
62
  marker_drift: boolean;
63
63
  };
64
+ /**
65
+ * State-destroyed vs state-captured, as ONE reading
66
+ * (road-to-inbox-harvest-2026-08-d-context-ledger Step 2.2).
67
+ *
68
+ * Before this, a compaction count and an advisory count sat in two places
69
+ * and nobody correlated them, so "how often was state destroyed with no
70
+ * capture in front of it" had no answer at all.
71
+ *
72
+ * HONEST LIMIT, stated in the shape rather than in prose only: the two
73
+ * sides come from different corpora — compactions are scanned out of
74
+ * transcripts, the advisory stamps live in per-session state files under a
75
+ * hashed key — and no field joins a transcript to its state file. So this
76
+ * is an AGGREGATE comparison over the same store, not a per-session join,
77
+ * and `join_basis` says so on every emitted report. A reader may compare
78
+ * the totals; a reader may NOT conclude that a specific compaction lacked
79
+ * a capture.
80
+ */
81
+ capture: {
82
+ /** Session-eol state files found. `null` ⇒ the directory is absent. */
83
+ state_files: number | null;
84
+ /** State files carrying an `advisory_fired_at` stamp. */
85
+ advisory_fired: number;
86
+ /** State files that additionally recorded "advised, but no envelope". */
87
+ advised_without_envelope: number;
88
+ /** Always `"aggregate"` today. See the doc comment. */
89
+ join_basis: 'aggregate';
90
+ };
64
91
  /** Pearson r of file bytes vs final context tokens over the readable set. */
65
92
  bytes_tokens_pearson_r: number | null;
66
93
  threshold_crossings: Record<string, number>;
@@ -130,6 +157,47 @@ export function scanSessionFile(file: string): SessionSummary | null {
130
157
  return { file, bytes, counters, last_ts };
131
158
  }
132
159
 
160
+ /** Where `session_eol_hook` persists its per-session state (gitignored). */
161
+ export const SESSION_EOL_STATE_REL = path.join('agents', 'runtime', 'state', 'session-eol');
162
+
163
+ /**
164
+ * The capture side of the Step-2.2 reading.
165
+ *
166
+ * `state_files: null` distinguishes "the directory is not there" from "it is
167
+ * there and empty" — an instrument that reports 0 for both cannot tell a quiet
168
+ * estate from one it cannot see, which is the failure the sibling ledger fixed
169
+ * for its own open-record set.
170
+ */
171
+ export function scanCaptureState(root: string): EolReport['capture'] {
172
+ const dir = path.join(root, SESSION_EOL_STATE_REL);
173
+ let names: string[];
174
+ try {
175
+ names = fs.readdirSync(dir).filter((n) => n.endsWith('.json'));
176
+ } catch {
177
+ return { state_files: null, advisory_fired: 0, advised_without_envelope: 0, join_basis: 'aggregate' };
178
+ }
179
+
180
+ let advisoryFired = 0;
181
+ let advisedWithoutEnvelope = 0;
182
+ for (const name of names) {
183
+ try {
184
+ const parsed: unknown = JSON.parse(fs.readFileSync(path.join(dir, name), 'utf-8'));
185
+ if (typeof parsed !== 'object' || parsed === null) continue;
186
+ const rec = parsed as Record<string, unknown>;
187
+ if (typeof rec['advisory_fired_at'] === 'string') advisoryFired++;
188
+ if (typeof rec['missing_envelope_warned_at'] === 'string') advisedWithoutEnvelope++;
189
+ } catch {
190
+ // A torn or hand-edited state file is data we do not have.
191
+ }
192
+ }
193
+ return {
194
+ state_files: names.length,
195
+ advisory_fired: advisoryFired,
196
+ advised_without_envelope: advisedWithoutEnvelope,
197
+ join_basis: 'aggregate',
198
+ };
199
+ }
200
+
133
201
  export function buildReport(root: string, now: Date = new Date()): EolReport {
134
202
  const files = listTranscriptFiles({ root });
135
203
  const sessions: SessionSummary[] = [];
@@ -176,6 +244,7 @@ export function buildReport(root: string, now: Date = new Date()): EolReport {
176
244
  compact_summary_records: summaryRecords,
177
245
  marker_drift: summaryRecords !== allEvents.length,
178
246
  },
247
+ capture: scanCaptureState(root),
179
248
  bytes_tokens_pearson_r: pearson(
180
249
  withUsage
181
250
  .filter((s) => (s.counters.final_context_tokens as number) > 1_000)
@@ -216,6 +285,18 @@ export function renderText(r: EolReport): string {
216
285
  ` trigger pre-tokens: min=${fmt(c.pre_tokens.min)} median=${fmt(c.pre_tokens.median)} max=${fmt(c.pre_tokens.max)}`,
217
286
  );
218
287
  }
288
+ // Step 2.2: the capture side, printed directly under the destruction side
289
+ // so the two are read together. `join_basis` is printed every time — a
290
+ // reader must not mistake an aggregate comparison for a per-session join.
291
+ const cap = r.capture;
292
+ if (cap.state_files === null) {
293
+ lines.push(` capture: no session-eol state directory — capture side UNOBSERVED (not zero)`);
294
+ } else {
295
+ lines.push(
296
+ ` capture: ${cap.advisory_fired} of ${cap.state_files} state file(s) advised, ` +
297
+ `${cap.advised_without_envelope} advised with no envelope [join: ${cap.join_basis}]`,
298
+ );
299
+ }
219
300
  if (c.post_tokens) {
220
301
  lines.push(` post-tokens median=${fmt(c.post_tokens.median)}`);
221
302
  }