@event4u/agent-config 17.0.0 → 17.2.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 (164) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +157 -468
  5. package/README.md +4 -3
  6. package/dist/agent-src/commands/project/analyze.md +9 -6
  7. package/dist/agent-src/commands/project/map.md +58 -0
  8. package/dist/agent-src/commands/project.md +5 -3
  9. package/dist/agent-src/contexts/execution/host-capability-manifest.md +5 -3
  10. package/dist/agent-src/skills/code-intelligence/SKILL.md +6 -0
  11. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  12. package/dist/agent-src/templates/roadmaps.md +5 -1
  13. package/dist/cli/registry.js +1 -1
  14. package/dist/cli-delegate/{chunk-IROAQ77A.js → chunk-3PK3VZHB.js} +107 -9
  15. package/dist/cli-delegate/{chunk-2SFOOFJJ.js → chunk-BZJ4Y3CI.js} +1 -1
  16. package/dist/cli-delegate/{chunk-CAL76ADI.js → chunk-CMZEKSNN.js} +52 -10
  17. package/dist/cli-delegate/{chunk-ESUNQ5XR.js → chunk-E3CFULNX.js} +22 -4
  18. package/dist/cli-delegate/{chunk-GOMILC3O.js → chunk-QBHEI5N4.js} +4 -3
  19. package/dist/cli-delegate/{chunk-SIOMDAKL.js → chunk-VYZGJLCA.js} +29 -39
  20. package/dist/cli-delegate/chunk-W5JXMF2T.js +77 -0
  21. package/dist/cli-delegate/{chunk-4SHNERBL.js → chunk-XIAPY44N.js} +1 -1
  22. package/dist/cli-delegate/{chunk-JHMMWNJ2.js → chunk-YHAPONWV.js} +1 -1
  23. package/dist/cli-delegate/cmd_conformance.js +32 -47
  24. package/dist/cli-delegate/cmd_doctor.js +4 -4
  25. package/dist/cli-delegate/cmd_doctor_neighbours.js +2 -2
  26. package/dist/cli-delegate/cmd_explain.js +1 -1
  27. package/dist/cli-delegate/cmd_export.js +6 -6
  28. package/dist/cli-delegate/cmd_fleet.js +3 -3
  29. package/dist/cli-delegate/cmd_preflight.js +3 -3
  30. package/dist/cli-delegate/cmd_prune.js +6 -6
  31. package/dist/cli-delegate/cmd_settings_migrate.js +6 -6
  32. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  33. package/dist/cli-delegate/cmd_sync.js +6 -6
  34. package/dist/cli-delegate/cmd_uninstall.js +6 -6
  35. package/dist/cli-delegate/cmd_update.js +9 -13
  36. package/dist/cli-delegate/cmd_validate.js +6 -6
  37. package/dist/discovery/deprecation-report.md +1 -1
  38. package/dist/discovery/discovery-manifest.json +41 -15
  39. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  40. package/dist/discovery/discovery-manifest.summary.md +3 -3
  41. package/dist/discovery/dormancy-report.md +1 -1
  42. package/dist/discovery/orphan-report.md +1 -1
  43. package/dist/discovery/packs.json +6 -5
  44. package/dist/discovery/trust-report.md +2 -2
  45. package/dist/discovery/workspaces.json +5 -4
  46. package/dist/hooks/dispatch.js +163 -148
  47. package/dist/hooks/dispatch.sha256 +1 -1
  48. package/dist/hooks/instructions_observer.chunk.cjs +301 -0
  49. package/dist/hooks/instructions_observer.chunk.cjs.sha256 +1 -0
  50. package/dist/install/claudeRuleRewrite.js +10 -3
  51. package/dist/install/conflict.js +24 -2
  52. package/dist/install/conflictTracker.js +14 -3
  53. package/dist/install/install.mjs +222 -117
  54. package/dist/install/installThinLayer.js +1 -0
  55. package/dist/install/loggedWrites.js +142 -0
  56. package/dist/install/paths.js +12 -2
  57. package/dist/install/preserve.js +25 -4
  58. package/dist/install/txlog.js +10 -6
  59. package/dist/mcp/registry-manifest.json +3 -3
  60. package/dist/mcp/server.json +2 -2
  61. package/dist/mcp/server.mjs +33595 -22916
  62. package/dist/scripts/_lib/egress_rules.js +4 -1
  63. package/dist/scripts/_lib/identifier_redaction.js +35 -2
  64. package/dist/scripts/ai_council/seat_policy.js +7 -1
  65. package/dist/server/schemas/settings.js +1 -1
  66. package/package.json +4 -1
  67. package/src/config/ci-local-parity.yml +1 -1
  68. package/src/config/continuity-surface.json +7 -0
  69. package/src/config/evaluator-budgets.json +4 -4
  70. package/src/config/gate-coverage.yml +27 -7
  71. package/src/config/gate-violation-baselines.json +9 -3
  72. package/src/config/gitignore-block.txt +6 -0
  73. package/src/config/hook-bundle-budget.json +19 -4
  74. package/src/config/hook-token-budget.json +1 -1
  75. package/src/config/packed-binary-manifest.json +27 -0
  76. package/src/config/production-entries.json +18 -0
  77. package/src/config/release-gate-locality.yml +0 -7
  78. package/src/config/skill-body-budget.json +7 -1
  79. package/src/install/claudeRuleRewrite.ts +10 -1
  80. package/src/install/conflict.ts +24 -1
  81. package/src/install/conflictTracker.ts +18 -2
  82. package/src/install/installThinLayer.ts +6 -0
  83. package/src/install/loggedWrites.ts +148 -0
  84. package/src/install/paths.ts +11 -2
  85. package/src/install/preserve.ts +40 -4
  86. package/src/install/txlog.ts +10 -6
  87. package/src/scripts/_cli/cmd_conformance.ts +3 -2
  88. package/src/scripts/_cli/cmd_update.ts +15 -17
  89. package/src/scripts/_lib/bench_ab_complexity.ts +14 -2
  90. package/src/scripts/_lib/collector_record.ts +1 -0
  91. package/src/scripts/_lib/council_settings_block.ts +9 -2
  92. package/src/scripts/_lib/egress_rules.ts +4 -1
  93. package/src/scripts/_lib/host_capability.ts +10 -5
  94. package/src/scripts/_lib/identifier_redaction.ts +38 -2
  95. package/src/scripts/_lib/price_freshness.ts +112 -0
  96. package/src/scripts/_lib/production_reach.ts +194 -0
  97. package/src/scripts/_lib/quota_parked.ts +17 -28
  98. package/src/scripts/_lib/release_findings_ingest.ts +13 -335
  99. package/src/scripts/_lib/runtime_journal.ts +7 -7
  100. package/src/scripts/_lib/tier_downshift_verdict.ts +81 -35
  101. package/src/scripts/ai_council/chairman.ts +5 -2
  102. package/src/scripts/ai_council/clients.ts +4 -3
  103. package/src/scripts/ai_council/free_hand_shadow.ts +24 -12
  104. package/src/scripts/ai_council/free_route_catalog.ts +142 -5
  105. package/src/scripts/ai_council/free_route_client.ts +4 -0
  106. package/src/scripts/ai_council/hand_payload.ts +115 -7
  107. package/src/scripts/ai_council/orchestrator.ts +7 -10
  108. package/src/scripts/ai_council/route_chooser.ts +1 -1
  109. package/src/scripts/ai_council/route_member.ts +66 -4
  110. package/src/scripts/ai_council/seat_policy.ts +8 -1
  111. package/src/scripts/ai_council/self_hosted_probe.ts +327 -0
  112. package/src/scripts/ai_council/status_surface.ts +2 -0
  113. package/src/scripts/ask_transport.ts +137 -15
  114. package/src/scripts/check_finding_dispositions.ts +25 -71
  115. package/src/scripts/check_release_pr_shape.ts +3 -3
  116. package/src/scripts/check_step_production_reach.ts +257 -0
  117. package/src/scripts/code_graph/build.ts +210 -10
  118. package/src/scripts/code_graph/cli.ts +168 -46
  119. package/src/scripts/code_graph/detect.ts +5 -4
  120. package/src/scripts/code_graph/docs_layer.ts +57 -0
  121. package/src/scripts/code_graph/extract.ts +58 -13
  122. package/src/scripts/code_graph/extract_more.ts +314 -0
  123. package/src/scripts/code_graph/laravel_overlay.ts +454 -0
  124. package/src/scripts/code_graph/map.ts +846 -0
  125. package/src/scripts/code_graph/map_model.ts +977 -0
  126. package/src/scripts/code_graph/map_verify.ts +140 -0
  127. package/src/scripts/code_graph/map_viewer/viewer.css +278 -0
  128. package/src/scripts/code_graph/map_viewer/viewer.js +756 -0
  129. package/src/scripts/code_graph/scip_reader.ts +201 -0
  130. package/src/scripts/code_graph/sqlite_store.ts +8 -1
  131. package/src/scripts/code_graph/types.ts +81 -15
  132. package/src/scripts/code_graph/validate.ts +16 -26
  133. package/src/scripts/code_graph/verbs.ts +7 -0
  134. package/src/scripts/council_cli.ts +10 -8
  135. package/src/scripts/hook_chunks.mjs +7 -0
  136. package/src/scripts/hook_manifest.json +1 -1
  137. package/src/scripts/hook_manifest.yaml +23 -0
  138. package/src/scripts/hooks/dispatch_hook.ts +4 -4
  139. package/src/scripts/hooks/instructions_observer_hook.ts +25 -6
  140. package/src/scripts/hooks/lazy_concern.ts +4 -0
  141. package/src/scripts/hooks/payload_stub.ts +27 -0
  142. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +29 -70
  143. package/src/scripts/hooks/subagent_ledger_hook.ts +80 -0
  144. package/src/scripts/install-hooks.sh +5 -0
  145. package/src/scripts/install.ts +2 -2
  146. package/src/scripts/lint_agents_layout.ts +2 -0
  147. package/src/scripts/lint_decision_classes.ts +34 -0
  148. package/src/scripts/lint_hook_manifest.ts +25 -0
  149. package/src/scripts/lint_roadmap_blockers.ts +52 -3
  150. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -6
  151. package/src/scripts/mcp_server/graph_tools.ts +73 -1
  152. package/src/scripts/mcp_server/tools.ts +19 -36
  153. package/src/scripts/release.ts +5 -9
  154. package/src/scripts/release_drill.ts +11 -165
  155. package/src/scripts/release_publication.ts +0 -9
  156. package/src/scripts/report_production_reach.ts +35 -0
  157. package/src/scripts/worker_reuse_shadow_report.ts +399 -1
  158. package/src/server/schemas/settings.ts +1 -1
  159. package/src/vendor/grammars/README.md +10 -5
  160. package/src/vendor/grammars/tree-sitter-go.wasm +0 -0
  161. package/src/vendor/grammars/tree-sitter-java.wasm +0 -0
  162. package/src/vendor/grammars/tree-sitter-python.wasm +0 -0
  163. package/dist/cli-delegate/chunk-A4QJ6ENT.js +0 -50
  164. /package/dist/cli-delegate/{chunk-E6XNBVE4.js → chunk-KR5ZIFPO.js} +0 -0
@@ -34,6 +34,7 @@ import process from 'node:process';
34
34
  import { fileURLToPath } from 'node:url';
35
35
 
36
36
  import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
37
+ import { OWNER_LOCKED_CLASSES } from './ai_council/ownership_classes.js';
37
38
 
38
39
  const _HERE = fileURLToPath(import.meta.url);
39
40
  const _PROG = 'lint_decision_classes';
@@ -65,6 +66,19 @@ export const RESOLVER_FORMS: readonly string[] = [
65
66
  'owner',
66
67
  ];
67
68
 
69
+ /**
70
+ * Locked-class rows resolved by the council before the owner lock reached this
71
+ * lint (road-to-a-tick-the-shipped-path-earns 2.2, decision A6). Listed rather
72
+ * than rewritten, so a fifth cannot land; each waits on the owner's ruling on
73
+ * blocker `delegated-owner-defaults`.
74
+ */
75
+ export const LOCKED_RESOLVER_BASELINE: ReadonlySet<string> = new Set([
76
+ 'agents/roadmaps/road-to-an-update-that-installs-itself-only-when-it-is-safe.md#D7',
77
+ 'agents/roadmaps/road-to-an-update-that-installs-itself-only-when-it-is-safe.md#D8',
78
+ 'agents/roadmaps/road-to-authority-routing-mechanism.md#D11',
79
+ 'agents/roadmaps/road-to-placeholders-on-the-wire.md#D7',
80
+ ]);
81
+
68
82
  /**
69
83
  * Unresolved-decision markers. Deliberately narrow: every pattern is a phrase
70
84
  * an author writes when they KNOW a decision is open, never a word that shows
@@ -206,6 +220,21 @@ export function checkOwnershipColumn(rel: string, lines: readonly string[]): Vio
206
220
  `team:<record>, owner`,
207
221
  });
208
222
  }
223
+ const id = (cells[0] ?? '').replace(/`/g, '').trim();
224
+ if (
225
+ OWNER_LOCKED_CLASSES.has(owner) &&
226
+ resolved !== 'owner' &&
227
+ !LOCKED_RESOLVER_BASELINE.has(`${rel}#${id}`)
228
+ ) {
229
+ out.push({
230
+ file: rel,
231
+ line: i + 1,
232
+ reason:
233
+ `ownership=\`${owner}\` is locked to the owner (ownership_classes.ts), ` +
234
+ `so resolved by=\`${resolved}\` cannot close it. Record a council answer ` +
235
+ `as \`owner\` with a decision cell starting "proposed".`,
236
+ });
237
+ }
209
238
  }
210
239
  }
211
240
  return out;
@@ -438,6 +467,11 @@ function _selfTest(): number {
438
467
  if (checkFile('m2.md', resolved).length !== 0) {
439
468
  failures.push('a marker naming its own closure row red');
440
469
  }
470
+ const locked = good.replace('critical-technical | council:rec-1', 'product-owned | council:rec-1');
471
+ if (checkFile('l.md', locked).length === 0) failures.push('a locked class closed by the council did not red');
472
+ if (checkFile('l2.md', locked.replace('council:rec-1', 'owner')).length !== 0) {
473
+ failures.push('a locked class closed by the owner red');
474
+ }
441
475
  const dangling = 'Pick the codec: TBD — closed as D9.\n';
442
476
  if (checkFile('m3.md', dangling).length === 0) {
443
477
  failures.push('a marker naming a row that does not exist did not red');
@@ -93,6 +93,7 @@ const EVENT_VOCABULARY: ReadonlySet<string> = new Set([
93
93
  "agent_error",
94
94
  "subagent_start",
95
95
  "subagent_stop",
96
+ "instructions_loaded",
96
97
  ]);
97
98
 
98
99
  // Known platform identifiers.
@@ -276,6 +277,30 @@ function _check_concerns(manifest: YamlObject, errors: string[]): Set<string> {
276
277
  }
277
278
  }
278
279
  }
280
+ // `payload_fields:` — field-level projection of the declared bodies. A
281
+ // non-list is ignored at run time, which would silently hand the concern
282
+ // whole bodies; a projection on a guard is ignored by design, so declaring
283
+ // one there states a narrowing that never happens.
284
+ if ("payload_fields" in spec) {
285
+ const raw = spec["payload_fields"];
286
+ if (!Array.isArray(raw) || raw.length === 0 || raw.some((f) => typeof f !== "string" || f === "")) {
287
+ errors.push(
288
+ `concerns.${name}: 'payload_fields' must be a non-empty list of key names`,
289
+ );
290
+ }
291
+ if (!("needs_payload_bodies" in spec)) {
292
+ errors.push(
293
+ `concerns.${name}: 'payload_fields' without 'needs_payload_bodies' ` +
294
+ `narrows bodies the concern never receives — declare the body classes`,
295
+ );
296
+ }
297
+ if (spec["fail_closed"] === true || spec["severity"] === "blocking") {
298
+ errors.push(
299
+ `concerns.${name}: 'payload_fields' has no effect on a blocking or ` +
300
+ `fail-closed concern, which always receives whole bodies — remove it`,
301
+ );
302
+ }
303
+ }
279
304
  // `re_arm:` — the named boundary at which a rebuilt prompt prefix is
280
305
  // expected and paid for once (`road-to-runtime-context-floors` step 1.2).
281
306
  // `check_prefix_stable_mutation` reads this key to decide whether a
@@ -281,6 +281,27 @@ const TECHNICAL_CLASSES: ReadonlySet<string> = new Set([
281
281
  * parses as `1`: the taxonomy name is documentation for the reader and must
282
282
  * not have to be spelled identically in every entry to stay valid.
283
283
  */
284
+ /** A `## Decisions` row resolved by `owner`, dated, that names blocker `id`. */
285
+ function _ownerDecisionNames(text: string, id: string): boolean {
286
+ const lines = text.split('\n');
287
+ const start = lines.findIndex((l) => /^##\s+Decisions\s*$/.test(l));
288
+ if (start < 0) return false;
289
+ let resolvedCol = -1;
290
+ for (let i = start + 1; i < lines.length && !/^##\s/.test(lines[i] as string); i += 1) {
291
+ const line = (lines[i] as string).trim();
292
+ if (!line.startsWith('|')) continue;
293
+ const cells = line.replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim());
294
+ if (resolvedCol < 0) {
295
+ resolvedCol = cells.findIndex((c) => /^resolved[ _]by$/i.test(c));
296
+ continue;
297
+ }
298
+ if (cells[resolvedCol]?.replace(/`/g, '') !== 'owner') continue;
299
+ if (!line.includes(id) || !/\b\d{4}-\d{2}-\d{2}\b/.test(line)) continue;
300
+ return true;
301
+ }
302
+ return false;
303
+ }
304
+
284
305
  function _blockerClass(body: string): string {
285
306
  const m = CLASS_FIELD_RE.exec(body);
286
307
  if (!m) {
@@ -341,6 +362,8 @@ interface ScanResult {
341
362
  decidability: Violation[];
342
363
  /** Open human-owned entries with no `Routed:` field — counted against its own ratchet. */
343
364
  routing: Violation[];
365
+ /** Resolved human-only entries no owner `## Decisions` row names — ratcheted (K1). */
366
+ ownerClose: Violation[];
344
367
  }
345
368
 
346
369
  /** What a scan needs to resolve a qualified reference: who it is, and where it may point. */
@@ -354,6 +377,7 @@ function _scanBoth(rawText: string, ctx: ScanContext = {}): ScanResult {
354
377
  const violations: Violation[] = [];
355
378
  const decidability: Violation[] = [];
356
379
  const routing: Violation[] = [];
380
+ const ownerClose: Violation[] = [];
357
381
  const text = _stripFencedCode(rawText);
358
382
  const declaredIds = new Set<string>();
359
383
  const userDecisionIds = new Set<string>();
@@ -416,6 +440,15 @@ function _scanBoth(rawText: string, ctx: ScanContext = {}): ScanResult {
416
440
  // Resolved entries are history — re-litigating a decision already
417
441
  // made would be churn, and the ratchet would never reach zero.
418
442
  const isResolved = /^-[ \t]*\*\*Status:\*\*[ \t]*resolved/im.test(body);
443
+ if (isResolved && ['', '3'].includes(_blockerClass(body)) && !_ownerDecisionNames(text, cur.id)) {
444
+ ownerClose.push({
445
+ line: _lineAt(text, sectionStart + cur.start),
446
+ message:
447
+ `blocker '${cur.id}' is human-only and resolved, but no \`## Decisions\` row ` +
448
+ 'resolved by `owner` names it with a date — a class-3 blocker closes only on an ' +
449
+ 'owner decision (road-to-a-tick-the-shipped-path-earns 2.3)',
450
+ });
451
+ }
419
452
  if (!isResolved) {
420
453
  openIds.add(cur.id);
421
454
  // The class/run contract is HARD rather than ratcheted, and it
@@ -552,7 +585,7 @@ function _scanBoth(rawText: string, ctx: ScanContext = {}): ScanResult {
552
585
  violations.sort((a, b) => a.line - b.line);
553
586
  decidability.sort((a, b) => a.line - b.line);
554
587
  routing.sort((a, b) => a.line - b.line);
555
- return { hard: violations, decidability, routing };
588
+ return { hard: violations, decidability, routing, ownerClose };
556
589
  }
557
590
 
558
591
  interface LocalBlockers {
@@ -920,10 +953,11 @@ function main(): number {
920
953
  let failed = 0;
921
954
  const undecidable: Array<{ rel: string; v: Violation }> = [];
922
955
  const unrouted: Array<{ rel: string; v: Violation }> = [];
956
+ const unowned: Array<{ rel: string; v: Violation }> = [];
923
957
  for (const roadmap of roadmaps) {
924
958
  const rel = _relPosix(roadmap, REPO_ROOT);
925
959
  const text = fs.readFileSync(roadmap, 'utf-8');
926
- const { hard, decidability, routing } = _scanBoth(text, {
960
+ const { hard, decidability, routing, ownerClose } = _scanBoth(text, {
927
961
  selfStem: path.basename(roadmap, '.md'),
928
962
  index,
929
963
  });
@@ -933,6 +967,9 @@ function main(): number {
933
967
  for (const v of routing) {
934
968
  unrouted.push({ rel, v });
935
969
  }
970
+ for (const v of ownerClose) {
971
+ unowned.push({ rel, v });
972
+ }
936
973
  if (hard.length) {
937
974
  failed += 1;
938
975
  process.stderr.write(`❌ ${rel}\n`);
@@ -1021,12 +1058,24 @@ function main(): number {
1021
1058
  ' See templates/roadmaps.md rule 20 "Route before you file".\n',
1022
1059
  );
1023
1060
  }
1024
- if (!verdict.ok || !routingVerdict.ok) {
1061
+ const ownerVerdict = checkRatchet({
1062
+ gate: 'lint_roadmap_blockers:owner-close',
1063
+ actual: unowned.length,
1064
+ repoRoot: REPO_ROOT,
1065
+ });
1066
+ if (!ownerVerdict.ok) {
1067
+ process.stderr.write(`❌ ${ownerVerdict.message}\n`);
1068
+ for (const { rel, v } of unowned) {
1069
+ process.stderr.write(` ${rel}:${v.line}: ${v.message}\n`);
1070
+ }
1071
+ }
1072
+ if (!verdict.ok || !routingVerdict.ok || !ownerVerdict.ok) {
1025
1073
  return 1;
1026
1074
  }
1027
1075
  if (!QUIET) {
1028
1076
  process.stdout.write(`\n✅ ${roadmaps.length} roadmap(s) blocker-contract-clean\n`);
1029
1077
  process.stdout.write(`✅ ${routingVerdict.message}\n`);
1078
+ process.stdout.write(`✅ ${ownerVerdict.message}\n`);
1030
1079
  process.stdout.write(
1031
1080
  `✅ 0 blocker id(s) open in both an active and an archived roadmap ` +
1032
1081
  `(${_globArchivedRoadmaps().length} archived file(s) read)\n`,
@@ -93,7 +93,7 @@
93
93
  },
94
94
  {
95
95
  "name": "chat_history_read",
96
- "description": "Read recent entries back from the consumer project's chat-history JSONL (`agents/runtime/.agent-chat-history`; `agents/.agent-chat-history` accepted for back-compat). Use to recover context from an earlier session — decisions, notes, phase markers — at the start of a new task. Read-only. Returns the resolved file path plus a list of matching entries (newest last). Combine `session`, `last`, and `entry_type` to narrow the result.",
96
+ "description": "Read recent entries from the chat-history JSONL (`agents/runtime/.agent-chat-history`; legacy `agents/.agent-chat-history` accepted) to recover decisions, notes and phase markers from an earlier session. Returns the resolved path and matching entries, newest last.",
97
97
  "side_effect": "ro",
98
98
  "implemented_on": [
99
99
  "stdio"
@@ -116,7 +116,7 @@
116
116
  },
117
117
  "path": {
118
118
  "type": "string",
119
- "description": "Optional history-file path override; defaults to the standard chat-history location under the project root."
119
+ "description": "History-file override; default is the standard location."
120
120
  },
121
121
  "around": {
122
122
  "type": "integer",
@@ -142,7 +142,7 @@
142
142
  "full"
143
143
  ],
144
144
  "default": "full",
145
- "description": "'index' returns compact rows (ref, t tag, ~100-char preview, tokens_estimate) instead of full entries — scan first, then re-read the refs you need via `around` or a narrowed filter. History entries are large, so index mode pays off here. 'full' (default) returns complete entries."
145
+ "description": "'index' returns compact rows (ref, t tag, preview, tokens_estimate) — scan, then re-read refs via `around` or a filter. 'full' (default) returns whole entries."
146
146
  }
147
147
  },
148
148
  "additionalProperties": false
@@ -222,6 +222,29 @@
222
222
  "readOnlyHint": true
223
223
  }
224
224
  },
225
+ {
226
+ "name": "graph_area",
227
+ "description": "One code-map area by id, exact name or member file: files, hubs, dependencies. Else refused.",
228
+ "side_effect": "ro",
229
+ "implemented_on": [
230
+ "stdio"
231
+ ],
232
+ "input_schema": {
233
+ "type": "object",
234
+ "required": [
235
+ "area"
236
+ ],
237
+ "properties": {
238
+ "area": {
239
+ "type": "string"
240
+ }
241
+ },
242
+ "additionalProperties": false
243
+ },
244
+ "annotations": {
245
+ "readOnlyHint": true
246
+ }
247
+ },
225
248
  {
226
249
  "name": "graph_dead",
227
250
  "description": "Symbols with no accepted reference that no declared entry point names. REFUSES (status `refused`) while a source is unreadable: with no exportedness recorded, an exported-but-unimported symbol is indistinguishable from a dead one. Never a deletion recommendation.",
@@ -283,6 +306,22 @@
283
306
  "readOnlyHint": false
284
307
  }
285
308
  },
309
+ {
310
+ "name": "graph_map_status",
311
+ "description": "Is there a code map (agents/map/)? Its freshness, counts and MAP.md path.",
312
+ "side_effect": "ro",
313
+ "implemented_on": [
314
+ "stdio"
315
+ ],
316
+ "input_schema": {
317
+ "type": "object",
318
+ "properties": {},
319
+ "additionalProperties": false
320
+ },
321
+ "annotations": {
322
+ "readOnlyHint": true
323
+ }
324
+ },
286
325
  {
287
326
  "name": "graph_node",
288
327
  "description": "One symbol in full: where it is declared, what reaches it, what it reaches, and how connected it is. Resolves EXACTLY (node id, then label) and refuses anything it cannot — it never scores free text, so an answer is always about the node you named.",
@@ -325,7 +364,15 @@
325
364
  "uses",
326
365
  "inherits",
327
366
  "member",
328
- "tests"
367
+ "tests",
368
+ "routes-to",
369
+ "renders",
370
+ "includes",
371
+ "reads",
372
+ "migrates",
373
+ "persists",
374
+ "relates",
375
+ "documents"
329
376
  ],
330
377
  "description": "Restrict to one relation. Omit for all of them."
331
378
  }
@@ -527,7 +574,7 @@
527
574
  },
528
575
  {
529
576
  "name": "memory_lookup",
530
- "description": "Retrieve engineering-memory entries for one or more memory types, optionally narrowed to specific anchor paths. Use before editing a security-sensitive or historically buggy file to surface prior incidents, ownership, and patterns tied to it. WORKFLOW: call with detail:\"index\" FIRST — each row carries id, title and tokens_estimate (the cost of fetching it) — then fetch full bodies via memory_get ONLY for the ids you will actually use, batching multiple ids into one call. Reads `agents/memory/<type>/*.yml` plus the `agents/memory/intake/*.jsonl` signal log. Read-only. Returns the v1 retrieval envelope: a `status` field plus per-type `slices` carrying the matched entries.",
577
+ "description": "Engineering-memory entries for one or more types, optionally narrowed to anchor paths — use before editing a security-sensitive or historically buggy file. Call with detail:\"index\" FIRST (rows carry id, title, tokens_estimate), then memory_get only the ids you will use, batched. Reads `agents/memory/<type>/*.yml` and `agents/memory/intake/*.jsonl`. Returns a `status` plus per-type `slices`.",
531
578
  "side_effect": "ro",
532
579
  "implemented_on": [
533
580
  "stdio"
@@ -568,7 +615,7 @@
568
615
  "token_budget": {
569
616
  "type": "integer",
570
617
  "minimum": 1,
571
- "description": "Optional token budget. When set, entries are rendered as one-line compact rows (id, type, confidence, `line`) and the row set is hard-cut at token_budget × 4 chars; omitted hits appear as a top-level `truncation` hint naming a concrete next step. Absent → the envelope is unchanged."
618
+ "description": "Optional: render compact rows (id, type, confidence, `line`) hard-cut at token_budget × 4 chars; omitted hits yield a top-level `truncation` hint."
572
619
  }
573
620
  },
574
621
  "required": [
@@ -62,6 +62,7 @@ import {
62
62
  testsFor,
63
63
  untested,
64
64
  } from '../code_graph/verbs.js';
65
+ import { areaReport, readMap } from '../code_graph/map.js';
65
66
  import type { BuiltinTool } from './tools.js';
66
67
 
67
68
  /** The `no graph here` answer, in one place so all five agree on its shape. */
@@ -119,7 +120,22 @@ function str(args: Record<string, unknown>, key: string): string {
119
120
  * drift. An enum that silently lost a value would turn a legal filter into a
120
121
  * rejected argument.
121
122
  */
122
- const RELATIONS = ['calls', 'imports', 'uses', 'inherits', 'member', 'tests'] as const;
123
+ const RELATIONS = [
124
+ 'calls',
125
+ 'imports',
126
+ 'uses',
127
+ 'inherits',
128
+ 'member',
129
+ 'tests',
130
+ 'routes-to',
131
+ 'renders',
132
+ 'includes',
133
+ 'reads',
134
+ 'migrates',
135
+ 'persists',
136
+ 'relates',
137
+ 'documents',
138
+ ] as const;
123
139
 
124
140
  function num(args: Record<string, unknown>, key: string, dflt: number): number {
125
141
  const v = args[key];
@@ -360,4 +376,60 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
360
376
  }));
361
377
  },
362
378
  },
379
+ graph_map_status: {
380
+ name: 'graph_map_status',
381
+ side_effect: 'ro',
382
+ description: 'Is there a code map (agents/map/)? Its freshness, counts and MAP.md path.',
383
+ input_schema: { type: 'object', properties: {}, additionalProperties: false },
384
+ handler: async (_args, root) => {
385
+ const staleness = graphState(root);
386
+ const map = readMap(root);
387
+ if (!map) {
388
+ return {
389
+ status: 'unavailable',
390
+ staleness,
391
+ map: 'absent',
392
+ hint: 'run `agent-config code-graph map` to draw it into agents/map/',
393
+ };
394
+ }
395
+ const m = map.model;
396
+ return {
397
+ status: 'ok',
398
+ staleness,
399
+ map: 'present',
400
+ drawn_at_commit: map.meta.commit,
401
+ graph_state_when_drawn: map.meta.state,
402
+ files: m.files.length,
403
+ areas: m.areas.length,
404
+ trusted_links: m.analyses.trustedEdges,
405
+ guess_share: m.analyses.guessShare,
406
+ request_paths: m.analyses.requestPathsTotal,
407
+ largest_areas: m.areas.slice(0, 5).map((a) => `${a.id} ${a.name} (${a.files.length})`),
408
+ map_md: 'agents/map/MAP.md',
409
+ index_html: 'agents/map/index.html',
410
+ };
411
+ },
412
+ },
413
+ graph_area: {
414
+ name: 'graph_area',
415
+ side_effect: 'ro',
416
+ description: 'One code-map area by id, exact name or member file: files, hubs, dependencies. Else refused.',
417
+ input_schema: {
418
+ type: 'object',
419
+ required: ['area'],
420
+ properties: {
421
+ area: { type: 'string' },
422
+ },
423
+ additionalProperties: false,
424
+ },
425
+ handler: async (args, root) => {
426
+ const q = str(args, 'area');
427
+ if (q === '') return { status: 'error', error: 'area must be a non-empty string' };
428
+ const map = readMap(root);
429
+ if (!map) return { status: 'unavailable', error: 'no map — run `agent-config code-graph map`' };
430
+ const r = areaReport(map.model, q);
431
+ if ('refusal' in r) return { status: 'refused', refusal: r.refusal, candidates: r.candidates };
432
+ return { status: 'ok', drawn_at_commit: map.meta.commit, ...r };
433
+ },
434
+ },
363
435
  };
@@ -1298,15 +1298,11 @@ export const ALLOWLIST: Record<string, BuiltinTool> = {
1298
1298
  name: 'chat_history_read',
1299
1299
  side_effect: 'ro',
1300
1300
  description:
1301
- "Read recent entries back from the consumer project's " +
1302
- 'chat-history JSONL ' +
1303
- '(`agents/runtime/.agent-chat-history`; ' +
1304
- '`agents/.agent-chat-history` accepted for back-compat). Use ' +
1305
- 'to recover context from an earlier session — decisions, ' +
1306
- 'notes, phase markers — at the start of a new task. ' +
1307
- 'Read-only. Returns the resolved file path plus a list of ' +
1308
- 'matching entries (newest last). Combine `session`, `last`, ' +
1309
- 'and `entry_type` to narrow the result.',
1301
+ 'Read recent entries from the chat-history JSONL ' +
1302
+ '(`agents/runtime/.agent-chat-history`; legacy ' +
1303
+ '`agents/.agent-chat-history` accepted) to recover decisions, ' +
1304
+ 'notes and phase markers from an earlier session. Returns the ' +
1305
+ 'resolved path and matching entries, newest last.',
1310
1306
  input_schema: {
1311
1307
  type: 'object',
1312
1308
  properties: {
@@ -1328,9 +1324,7 @@ export const ALLOWLIST: Record<string, BuiltinTool> = {
1328
1324
  path: {
1329
1325
  type: 'string',
1330
1326
  description:
1331
- 'Optional history-file path override; defaults to ' +
1332
- 'the standard chat-history location under the ' +
1333
- 'project root.',
1327
+ 'History-file override; default is the standard location.',
1334
1328
  },
1335
1329
  around: {
1336
1330
  type: 'integer',
@@ -1359,12 +1353,9 @@ export const ALLOWLIST: Record<string, BuiltinTool> = {
1359
1353
  enum: ['index', 'full'],
1360
1354
  default: 'full',
1361
1355
  description:
1362
- "'index' returns compact rows (ref, t tag, ~100-char " +
1363
- 'preview, tokens_estimate) instead of full entries — ' +
1364
- 'scan first, then re-read the refs you need via ' +
1365
- '`around` or a narrowed filter. History entries are ' +
1366
- "large, so index mode pays off here. 'full' " +
1367
- '(default) returns complete entries.',
1356
+ "'index' returns compact rows (ref, t tag, preview, " +
1357
+ 'tokens_estimate) — scan, then re-read refs via ' +
1358
+ "`around` or a filter. 'full' (default) returns whole entries.",
1368
1359
  },
1369
1360
  },
1370
1361
  additionalProperties: false,
@@ -1375,18 +1366,13 @@ export const ALLOWLIST: Record<string, BuiltinTool> = {
1375
1366
  name: 'memory_lookup',
1376
1367
  side_effect: 'ro',
1377
1368
  description:
1378
- 'Retrieve engineering-memory entries for one or more memory ' +
1379
- 'types, optionally narrowed to specific anchor paths. Use ' +
1380
- 'before editing a security-sensitive or historically buggy ' +
1381
- 'file to surface prior incidents, ownership, and patterns ' +
1382
- 'tied to it. WORKFLOW: call with detail:"index" FIRST — each ' +
1383
- 'row carries id, title and tokens_estimate (the cost of ' +
1384
- 'fetching it) — then fetch full bodies via memory_get ONLY ' +
1385
- 'for the ids you will actually use, batching multiple ids ' +
1386
- 'into one call. Reads `agents/memory/<type>/*.yml` plus the ' +
1387
- '`agents/memory/intake/*.jsonl` signal log. Read-only. ' +
1388
- 'Returns the v1 retrieval envelope: a `status` field plus ' +
1389
- 'per-type `slices` carrying the matched entries.',
1369
+ 'Engineering-memory entries for one or more types, optionally ' +
1370
+ 'narrowed to anchor paths — use before editing a ' +
1371
+ 'security-sensitive or historically buggy file. Call with ' +
1372
+ 'detail:"index" FIRST (rows carry id, title, tokens_estimate), ' +
1373
+ 'then memory_get only the ids you will use, batched. Reads ' +
1374
+ '`agents/memory/<type>/*.yml` and `agents/memory/intake/*.jsonl`. ' +
1375
+ 'Returns a `status` plus per-type `slices`.',
1390
1376
  input_schema: {
1391
1377
  type: 'object',
1392
1378
  properties: {
@@ -1426,12 +1412,9 @@ export const ALLOWLIST: Record<string, BuiltinTool> = {
1426
1412
  type: 'integer',
1427
1413
  minimum: 1,
1428
1414
  description:
1429
- 'Optional token budget. When set, entries are ' +
1430
- 'rendered as one-line compact rows (id, type, ' +
1431
- 'confidence, `line`) and the row set is hard-cut at ' +
1432
- 'token_budget × 4 chars; omitted hits appear as a ' +
1433
- 'top-level `truncation` hint naming a concrete next ' +
1434
- 'step. Absent → the envelope is unchanged.',
1415
+ 'Optional: render compact rows (id, type, confidence, ' +
1416
+ '`line`) hard-cut at token_budget × 4 chars; omitted ' +
1417
+ 'hits yield a top-level `truncation` hint.',
1435
1418
  },
1436
1419
  },
1437
1420
  required: ['types'],
@@ -36,8 +36,9 @@
36
36
  * the PR's own consistency check fails — PR #226).
37
37
  * 5. Commit + push — `release: X.Y.Z`, push branch, open PR.
38
38
  * 6. Wait for CI — `gh pr checks --watch` (skippable with --no-wait).
39
- * 7. Findings ledger — VERIFY it is on the branch and dispositioned, and
40
- * STOP otherwise; `self-review-gate.yml` builds it.
39
+ * 7. No review gate — a release waits for no review ledger: every commit
40
+ * on main is already reviewed and merged (owner,
41
+ * 2026-10-22). The self-review stays advisory.
41
42
  * 8. Merge — `gh pr merge --merge --delete-branch`.
42
43
  * 9. Tag main — fast-forward main, tag the merge commit, push the
43
44
  * tag (triggers publish-npm.yml, except under --ci).
@@ -128,7 +129,6 @@ import { canPrompt, promptLine } from './_lib/tty_prompt.js';
128
129
  import type { CommitRecord, ImpactTrailers } from './_lib/consumer_impact.js';
129
130
  import { COMMIT_LOG_FORMAT, NO_IMPACT_TRAILERS, is_breaking_commit, parse_commit_log, parse_impact_trailers, write_impact_map } from './_lib/consumer_impact.js';
130
131
  import { preflightPosition } from './_lib/release_position.js';
131
- import { settleFindingsLedger } from './_lib/release_findings_ingest.js';
132
132
  import { TAG_TRIGGERED_WORKFLOWS, dispatchTagWorkflows } from './_lib/release_tag_dispatch.js';
133
133
  import {
134
134
  RELEASE_HEAD_DEFAULT,
@@ -1041,12 +1041,8 @@ function execute(
1041
1041
  _step(6, total, 'Skip waiting for checks (--no-wait)');
1042
1042
  }
1043
1043
 
1044
- // Step 7 — the findings ledger: verify, never produce; the module says why.
1045
- settleFindingsLedger({
1046
- run, die, repoRoot: REPO_ROOT, remote: REMOTE, trunk: MAIN_BRANCH,
1047
- step: (m) => { _step(7, total, m); }, write: (s) => process.stdout.write(s),
1048
- resolvePr: () => Number((pr_info ?? _pr_for_branch(branch))?.['number'] ?? NaN) || null,
1049
- }, plan.target, branch, pr_merged);
1044
+ // Step 7 — kept as a numbered line so the step anchors stay stable.
1045
+ _step(7, total, 'No review gate — main is already reviewed and merged');
1050
1046
  // Step 8 — merge
1051
1047
  if (pr_merged) {
1052
1048
  _step(8, total, `PR #${pr_info!['number']} already merged — skip`);