@event4u/agent-config 8.7.0 → 8.9.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 (171) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +109 -222
  3. package/CONTRIBUTING.md +25 -0
  4. package/README.md +7 -4
  5. package/dist/agent-src/commands/agent-handoff.md +39 -2
  6. package/dist/agent-src/commands/research/deep.md +8 -0
  7. package/dist/agent-src/commands/research/report.md +7 -0
  8. package/dist/agent-src/commands/roadmap/create.md +12 -0
  9. package/dist/agent-src/commands/roadmap/process-full.md +31 -5
  10. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +30 -1
  11. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +1 -1
  12. package/dist/agent-src/contexts/execution/orchestration-benchmark-gate.md +28 -16
  13. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +36 -5
  14. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +37 -0
  15. package/dist/agent-src/contexts/execution/subagent-topologies.md +34 -0
  16. package/dist/agent-src/contexts/execution/verification-mechanics.md +9 -9
  17. package/dist/agent-src/rules/active-remediation.md +92 -0
  18. package/dist/agent-src/rules/agent-authority.md +1 -1
  19. package/dist/agent-src/rules/broken-access-control.md +106 -0
  20. package/dist/agent-src/rules/communication-through-line.md +42 -0
  21. package/dist/agent-src/rules/decision-revisit-gate.md +8 -0
  22. package/dist/agent-src/rules/delegation-policy.md +5 -0
  23. package/dist/agent-src/rules/direct-answers.md +1 -1
  24. package/dist/agent-src/rules/downstream-changes.md +1 -0
  25. package/dist/agent-src/rules/external-code-graph-interop.md +65 -0
  26. package/dist/agent-src/rules/git-history-discipline.md +18 -0
  27. package/dist/agent-src/rules/lethal-trifecta-guard.md +6 -1
  28. package/dist/agent-src/rules/minimal-safe-diff.md +66 -1
  29. package/dist/agent-src/rules/non-destructive-by-default.md +3 -0
  30. package/dist/agent-src/rules/prefer-enums-over-literals.md +65 -0
  31. package/dist/agent-src/rules/question-not-instruction.md +76 -0
  32. package/dist/agent-src/rules/senior-engineering-discipline.md +82 -0
  33. package/dist/agent-src/rules/think-before-action.md +1 -0
  34. package/dist/agent-src/rules/tool-safety.md +8 -0
  35. package/dist/agent-src/rules/untrusted-input-defense.md +87 -22
  36. package/dist/agent-src/scripts/update_roadmap_progress.ts +2 -1
  37. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  38. package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +112 -0
  39. package/dist/agent-src/skills/ai-code-blindspots/evals/triggers.json +52 -0
  40. package/dist/agent-src/skills/authz-review/SKILL.md +21 -4
  41. package/dist/agent-src/skills/code-review/SKILL.md +2 -0
  42. package/dist/agent-src/skills/command-writing/SKILL.md +53 -40
  43. package/dist/agent-src/skills/dcf-modeling/evals/domain-truth.json +29 -0
  44. package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +1 -0
  45. package/dist/agent-src/skills/dependency-upgrade/SKILL.md +20 -0
  46. package/dist/agent-src/skills/design-intelligence/SKILL.md +49 -12
  47. package/dist/agent-src/skills/design-review/SKILL.md +29 -15
  48. package/dist/agent-src/skills/design-system-capture/SKILL.md +63 -24
  49. package/dist/agent-src/skills/forecasting/evals/domain-truth.json +29 -0
  50. package/dist/agent-src/skills/frontend-render-security/SKILL.md +104 -0
  51. package/dist/agent-src/skills/frontend-render-security/evals/triggers.json +52 -0
  52. package/dist/agent-src/skills/markitdown/SKILL.md +1 -0
  53. package/dist/agent-src/skills/mcp/SKILL.md +24 -0
  54. package/dist/agent-src/skills/mcp-builder/SKILL.md +9 -1
  55. package/dist/agent-src/skills/memory-consolidation/SKILL.md +55 -4
  56. package/dist/agent-src/skills/roadmap-writing/SKILL.md +46 -5
  57. package/dist/agent-src/skills/runway-cognition/evals/domain-truth.json +29 -0
  58. package/dist/agent-src/skills/scenario-modeling/evals/domain-truth.json +29 -0
  59. package/dist/agent-src/skills/secrets-management/SKILL.md +7 -0
  60. package/dist/agent-src/skills/skill-writing/SKILL.md +12 -3
  61. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +27 -33
  62. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +112 -0
  63. package/dist/agent-src/skills/supply-chain-intake/evals/triggers.json +52 -0
  64. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +72 -2
  65. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -0
  66. package/dist/agent-src/skills/unit-economics-modeling/evals/domain-truth.json +40 -0
  67. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  68. package/dist/agent-src/templates/roadmaps.md +60 -0
  69. package/dist/cli/registry.js +3 -0
  70. package/dist/cli/registry.js.map +1 -1
  71. package/dist/discovery/deprecation-report.md +1 -1
  72. package/dist/discovery/discovery-manifest.json +268 -46
  73. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  74. package/dist/discovery/discovery-manifest.summary.md +4 -4
  75. package/dist/discovery/orphan-report.md +1 -1
  76. package/dist/discovery/packs.json +18 -8
  77. package/dist/discovery/trust-report.md +11 -11
  78. package/dist/discovery/workspaces.json +16 -6
  79. package/dist/install/install.mjs +141 -3
  80. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  81. package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
  82. package/dist/mcp/registry-manifest.json +4 -4
  83. package/dist/router.json +1 -1
  84. package/dist/scripts/_lib/claude_builtin_names.js +145 -0
  85. package/dist/scripts/_lib/claude_builtin_names.js.map +1 -0
  86. package/dist/scripts/condense.js +14 -0
  87. package/dist/scripts/condense.js.map +1 -1
  88. package/dist/server/schemas/settings.js +2 -1
  89. package/dist/server/schemas/settings.js.map +1 -1
  90. package/docs/CLAIMS.md +58 -2
  91. package/docs/architecture.md +3 -3
  92. package/docs/archive/CHANGELOG-pre-8.9.0.md +306 -0
  93. package/docs/benchmark.md +113 -0
  94. package/docs/catalog.md +32 -5
  95. package/docs/contracts/command-clusters.md +18 -0
  96. package/docs/contracts/design-artifact-verification.md +124 -0
  97. package/docs/contracts/injected-block-authenticity-model.md +66 -0
  98. package/docs/contracts/mcp-tool-inventory.md +31 -30
  99. package/docs/decisions/ADR-061-corpus-grounding-layer.md +13 -0
  100. package/docs/decisions/ADR-105-automatic-subagent-orchestration.md +6 -0
  101. package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +11 -2
  102. package/docs/decisions/ADR-117-subagents-auto-default-on.md +88 -0
  103. package/docs/decisions/INDEX.md +1 -0
  104. package/docs/experiences/content_creator.md +2 -0
  105. package/docs/experiences/finance.md +2 -0
  106. package/docs/experiences/founder.md +2 -0
  107. package/docs/experiences/ops.md +2 -0
  108. package/docs/featured-skills.md +2 -2
  109. package/docs/getting-started-by-role.md +1 -1
  110. package/docs/getting-started.md +1 -1
  111. package/docs/governance-advantage.md +1 -1
  112. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +27 -0
  113. package/docs/guidelines/agent-infra/emphasis-budget.md +45 -0
  114. package/docs/guidelines/agent-infra/memory-access.md +12 -0
  115. package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +151 -0
  116. package/docs/guidelines/agent-infra/tool-description-as-policy.md +35 -0
  117. package/docs/guidelines/design-canon.md +1 -0
  118. package/docs/proof.md +60 -3
  119. package/docs/release-runbook.md +138 -0
  120. package/docs/second-brain-scope.md +116 -0
  121. package/docs/succession.md +80 -0
  122. package/package.json +2 -2
  123. package/src/agent-src/scripts/update_roadmap_progress.ts +2 -1
  124. package/src/config/agent-settings.template.yml +25 -6
  125. package/src/scripts/_dispatch.bash +27 -0
  126. package/src/scripts/_lib/claude_builtin_names.ts +148 -0
  127. package/src/scripts/_lib/file_slicer.ts +117 -0
  128. package/src/scripts/_lib/lexical_index.ts +155 -0
  129. package/src/scripts/_lib/orchestration_gate.ts +12 -8
  130. package/src/scripts/_lib/orchestration_record.ts +170 -0
  131. package/src/scripts/_lib/orchestration_savings.ts +180 -0
  132. package/src/scripts/_lib/retrieval_sanitize.ts +71 -0
  133. package/src/scripts/_lib/security_lint.ts +1 -0
  134. package/src/scripts/_lib/stat_index.ts +98 -0
  135. package/src/scripts/benchmark.ts +122 -0
  136. package/src/scripts/build_proof.ts +51 -0
  137. package/src/scripts/chat_history.ts +79 -7
  138. package/src/scripts/check_memory.ts +2 -0
  139. package/src/scripts/check_quality_regression.ts +51 -0
  140. package/src/scripts/check_roadmap_trackable.ts +3 -2
  141. package/src/scripts/condense.ts +14 -0
  142. package/src/scripts/discovery_graph.ts +0 -0
  143. package/src/scripts/domain_soundness_status.ts +236 -0
  144. package/src/scripts/external_sources_denylist.json +4 -1
  145. package/src/scripts/generate_index.ts +17 -0
  146. package/src/scripts/hot_context_hook.ts +47 -2
  147. package/src/scripts/install-hooks.sh +25 -0
  148. package/src/scripts/install.ts +35 -2
  149. package/src/scripts/learning_sidecar.ts +0 -0
  150. package/src/scripts/lint_agent_skill_names.ts +29 -0
  151. package/src/scripts/lint_behavioural_eval_freshness.ts +140 -0
  152. package/src/scripts/lint_knowledge_scale.ts +4 -3
  153. package/src/scripts/lint_roadmap_complexity.ts +124 -0
  154. package/src/scripts/lint_versioned_cache.ts +195 -0
  155. package/src/scripts/mcp_server/consumer_tool_catalog.json +23 -3
  156. package/src/scripts/mcp_server/tools.ts +182 -1
  157. package/src/scripts/measure_lexical_ranking.ts +173 -0
  158. package/src/scripts/memory_lookup.ts +0 -0
  159. package/src/scripts/memory_replay.ts +285 -0
  160. package/src/scripts/orchestration_record.ts +140 -0
  161. package/src/scripts/orchestration_savings_report.ts +124 -0
  162. package/src/scripts/schemas/domain-truth.schema.json +94 -0
  163. package/src/scripts/schemas/evals.schema.json +113 -0
  164. package/src/scripts/schemas/skill.schema.json +4 -0
  165. package/src/scripts/score_domain_truth.ts +192 -0
  166. package/src/scripts/second_brain_retrieval.ts +278 -0
  167. package/src/scripts/second_brain_run.ts +341 -0
  168. package/src/scripts/second_brain_score.ts +186 -0
  169. package/src/scripts/session_memory_index.ts +94 -0
  170. package/src/scripts/skill_eval_coverage.ts +318 -0
  171. package/src/server/schemas/settings.ts +5 -2
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Memory-retrieval replay rig (road-to-memory-retrieval-economy Phase 0).
4
+ *
5
+ * Replays the corpus-derived query set (tests/fixtures/memory-replay/ —
6
+ * provenance note in queries.yml; council 2026-07-08 Option-A verdict)
7
+ * against the fixture memory root and measures, with the REAL tokenizer
8
+ * (token_count.ts, cl100k_base — never chars/4, roadmap D2):
9
+ *
10
+ * --baseline full-envelope payload per query → pins
11
+ * internal/bench/reports/memory-retrieval-baseline.json
12
+ * --paired index-mode envelope + memory_get(needed) payload vs the
13
+ * full baseline → run report + the three falsification
14
+ * checks from Phase 1b (token saving ≥30%, zero missed
15
+ * needed-fetches; the judge arm is reported as
16
+ * out-of-band — see report field `judge`)
17
+ * --check CI regression: index-mode payload for the replay set must
18
+ * not exceed the recorded baseline's index payload by >5%.
19
+ * INERT (exit 0) while no baseline file exists.
20
+ *
21
+ * Exit codes: 0 ok/inert · 1 check failed or paired falsification red ·
22
+ * 2 usage/fixture error.
23
+ */
24
+ import * as fs from 'node:fs';
25
+ import * as path from 'node:path';
26
+ import process from 'node:process';
27
+ import { fileURLToPath, pathToFileURL } from 'node:url';
28
+
29
+ import * as YAML from 'yaml';
30
+
31
+ import {
32
+ _setIntakeRoot,
33
+ _setKnowledgeRoot,
34
+ _setMemoryRoot,
35
+ retrieve_v1,
36
+ } from './memory_lookup.js';
37
+ import { gpt_tokens, TIKTOKEN_AVAILABLE } from './_lib/token_count.js';
38
+
39
+ const _HERE = fileURLToPath(import.meta.url);
40
+ export const ROOT = path.resolve(path.dirname(_HERE), '..', '..');
41
+ export const FIXTURE_DIR = path.join(ROOT, 'tests', 'fixtures', 'memory-replay');
42
+ export const BASELINE_PATH = path.join(
43
+ ROOT,
44
+ 'internal',
45
+ 'bench',
46
+ 'reports',
47
+ 'memory-retrieval-baseline.json',
48
+ );
49
+
50
+ export interface ReplayQuery {
51
+ readonly id: string;
52
+ readonly types: string[];
53
+ readonly keys: string[];
54
+ readonly limit: number;
55
+ readonly needed: string[];
56
+ }
57
+
58
+ export function load_queries(): ReplayQuery[] {
59
+ const raw = YAML.parse(fs.readFileSync(path.join(FIXTURE_DIR, 'queries.yml'), 'utf-8')) as {
60
+ queries: ReplayQuery[];
61
+ };
62
+ return raw.queries;
63
+ }
64
+
65
+ /** Point the lookup module at the fixture memory tree. */
66
+ export function bind_fixture_root(): void {
67
+ const memRoot = path.join(FIXTURE_DIR, 'memory-root');
68
+ _setMemoryRoot(memRoot);
69
+ _setKnowledgeRoot(path.join(memRoot, 'knowledge'));
70
+ _setIntakeRoot(path.join(memRoot, 'intake-does-not-exist'));
71
+ }
72
+
73
+ /** Real-tokenizer size of an envelope, serialized the way a host sees it. */
74
+ export function envelope_tokens(envelope: unknown): number {
75
+ return gpt_tokens(JSON.stringify(envelope)).tokens;
76
+ }
77
+
78
+ interface PerQuery {
79
+ id: string;
80
+ full_tokens: number;
81
+ needed_found: number;
82
+ needed_total: number;
83
+ index_tokens?: number;
84
+ fetch_tokens?: number;
85
+ index_plus_fetch_tokens?: number;
86
+ saving_pct?: number;
87
+ missed_needed?: string[];
88
+ }
89
+
90
+ /** IDs present in a v1 envelope's entries. */
91
+ function _entry_ids(envelope: Record<string, unknown>): Set<string> {
92
+ const out = new Set<string>();
93
+ for (const e of (envelope['entries'] as Array<Record<string, unknown>>) ?? []) {
94
+ out.add(String(e['id'] ?? ''));
95
+ }
96
+ return out;
97
+ }
98
+
99
+ type RetrieveV1 = (
100
+ types: string[],
101
+ keys: string[],
102
+ limit?: number,
103
+ options?: { detail?: 'index' | 'full' },
104
+ ) => Record<string, unknown>;
105
+
106
+ /** Optional Phase-1 surfaces, absent before P1 lands. */
107
+ async function _phase1(): Promise<{
108
+ retrieve_v1_detail: RetrieveV1 | null;
109
+ memory_get: ((ids: string[]) => Record<string, unknown>) | null;
110
+ }> {
111
+ const mod = (await import('./memory_lookup.js')) as Record<string, unknown>;
112
+ const hasDetail = typeof mod['retrieve_v1'] === 'function' && (mod['retrieve_v1'] as { length: number }).length >= 0;
113
+ const memoryGet = typeof mod['memory_get_v1'] === 'function'
114
+ ? (mod['memory_get_v1'] as (ids: string[]) => Record<string, unknown>)
115
+ : null;
116
+ // Detail support is feature-detected by probing the options seam: before
117
+ // Phase 1, passing options is harmless (extra arg ignored) but index mode
118
+ // is only REAL when memory_get exists too.
119
+ return { retrieve_v1_detail: hasDetail ? (retrieve_v1 as unknown as RetrieveV1) : null, memory_get: memoryGet };
120
+ }
121
+
122
+ export async function run(mode: 'baseline' | 'paired' | 'check'): Promise<number> {
123
+ bind_fixture_root();
124
+ const queries = load_queries();
125
+ if (queries.length < 20) {
126
+ process.stderr.write(`memory_replay: replay set too small (${queries.length} < 20)\n`);
127
+ return 2;
128
+ }
129
+ const p1 = await _phase1();
130
+ const perQuery: PerQuery[] = [];
131
+
132
+ for (const q of queries) {
133
+ const full = retrieve_v1(q.types, q.keys, q.limit);
134
+ const fullIds = _entry_ids(full);
135
+ const foundNeeded = q.needed.filter((n) => fullIds.has(n));
136
+ const row: PerQuery = {
137
+ id: q.id,
138
+ full_tokens: envelope_tokens(full),
139
+ needed_found: foundNeeded.length,
140
+ needed_total: q.needed.length,
141
+ };
142
+
143
+ if (mode !== 'baseline' && p1.retrieve_v1_detail !== null && p1.memory_get !== null) {
144
+ const index = p1.retrieve_v1_detail(q.types, q.keys, q.limit, { detail: 'index' });
145
+ const indexIds = _entry_ids(index);
146
+ const missed = q.needed.filter((n) => !indexIds.has(n));
147
+ const fetched = p1.memory_get(q.needed);
148
+ row.index_tokens = envelope_tokens(index);
149
+ row.fetch_tokens = envelope_tokens(fetched);
150
+ row.index_plus_fetch_tokens = row.index_tokens + row.fetch_tokens;
151
+ row.saving_pct =
152
+ Math.round((1 - row.index_plus_fetch_tokens / row.full_tokens) * 1000) / 10;
153
+ row.missed_needed = missed;
154
+ }
155
+ perQuery.push(row);
156
+ }
157
+
158
+ const agg = {
159
+ queries: perQuery.length,
160
+ full_tokens: perQuery.reduce((s, r) => s + r.full_tokens, 0),
161
+ needed_recall: perQuery.reduce((s, r) => s + r.needed_found, 0) /
162
+ perQuery.reduce((s, r) => s + r.needed_total, 0),
163
+ index_plus_fetch_tokens: perQuery.every((r) => r.index_plus_fetch_tokens !== undefined)
164
+ ? perQuery.reduce((s, r) => s + (r.index_plus_fetch_tokens ?? 0), 0)
165
+ : null,
166
+ };
167
+ const saving_pct = agg.index_plus_fetch_tokens === null
168
+ ? null
169
+ : Math.round((1 - agg.index_plus_fetch_tokens / agg.full_tokens) * 1000) / 10;
170
+
171
+ const report = {
172
+ schema_version: 1,
173
+ kind: `memory-retrieval-${mode}`,
174
+ roadmap: 'road-to-memory-retrieval-economy',
175
+ corpus_provenance:
176
+ 'corpus-derived (council 2026-07-08 Option A) — see tests/fixtures/memory-replay/queries.yml header; claims scoped to corpus-derived replay queries',
177
+ tokenizer: TIKTOKEN_AVAILABLE ? 'cl100k_base (tiktoken)' : 'PROXY chars/4 — NOT evidence (D2)',
178
+ aggregate: { ...agg, saving_pct },
179
+ per_query: perQuery,
180
+ };
181
+
182
+ if (mode === 'baseline') {
183
+ fs.mkdirSync(path.dirname(BASELINE_PATH), { recursive: true });
184
+ fs.writeFileSync(BASELINE_PATH, JSON.stringify(report, null, 2) + '\n');
185
+ process.stdout.write(
186
+ `✅ baseline pinned: ${agg.queries} queries · full=${agg.full_tokens} tok · ` +
187
+ `needed-recall=${(agg.needed_recall * 100).toFixed(1)}%\n`,
188
+ );
189
+ return agg.needed_recall === 1 ? 0 : 1;
190
+ }
191
+
192
+ if (mode === 'paired') {
193
+ if (agg.index_plus_fetch_tokens === null) {
194
+ process.stderr.write(
195
+ 'memory_replay: paired mode needs Phase 1 (detail + memory_get_v1) — not available\n',
196
+ );
197
+ return 2;
198
+ }
199
+ const missedTotal = perQuery.reduce((s, r) => s + (r.missed_needed?.length ?? 0), 0);
200
+ const out = path.join(
201
+ path.dirname(BASELINE_PATH),
202
+ 'memory-retrieval-run.json',
203
+ );
204
+ const checks = {
205
+ 'a-token-saving-ge-30pct': (saving_pct ?? 0) >= 30,
206
+ 'b-judge-win-rate': 'OUT-OF-BAND — run check_quality_regression paired judge before flip sign-off',
207
+ 'c-zero-missed-needed': missedTotal === 0,
208
+ };
209
+ fs.writeFileSync(out, JSON.stringify({ ...report, falsification: checks }, null, 2) + '\n');
210
+ process.stdout.write(
211
+ `paired: full=${agg.full_tokens} vs index+fetch=${agg.index_plus_fetch_tokens} ` +
212
+ `(saving ${saving_pct}%) · missed-needed=${missedTotal} → ${out}\n`,
213
+ );
214
+ return checks['a-token-saving-ge-30pct'] && checks['c-zero-missed-needed'] ? 0 : 1;
215
+ }
216
+
217
+ // --check: inert without a baseline; else compare index payload drift.
218
+ if (!fs.existsSync(BASELINE_PATH)) {
219
+ process.stdout.write('memory_replay --check: no baseline pinned — inert.\n');
220
+ return 0;
221
+ }
222
+ if (agg.index_plus_fetch_tokens === null) {
223
+ // Phase 1 not landed: guard the FULL payload against >5% growth
224
+ // instead, so the baseline still means something pre-split.
225
+ const pinned = JSON.parse(fs.readFileSync(BASELINE_PATH, 'utf-8')) as {
226
+ aggregate: { full_tokens: number };
227
+ };
228
+ const limitTok = pinned.aggregate.full_tokens * 1.05;
229
+ if (agg.full_tokens > limitTok) {
230
+ process.stdout.write(
231
+ `❌ memory_replay --check: full payload ${agg.full_tokens} exceeds baseline ` +
232
+ `${pinned.aggregate.full_tokens} by >5%\n`,
233
+ );
234
+ return 1;
235
+ }
236
+ process.stdout.write('✅ memory_replay --check: within baseline (+5%).\n');
237
+ return 0;
238
+ }
239
+ const pinned = JSON.parse(fs.readFileSync(BASELINE_PATH, 'utf-8')) as {
240
+ aggregate: { full_tokens: number };
241
+ };
242
+ const limitTok = pinned.aggregate.full_tokens * 1.05;
243
+ if ((agg.index_plus_fetch_tokens ?? 0) > limitTok) {
244
+ process.stdout.write(
245
+ `❌ memory_replay --check: index payload ${agg.index_plus_fetch_tokens} exceeds ` +
246
+ `baseline full ${pinned.aggregate.full_tokens} by >5%\n`,
247
+ );
248
+ return 1;
249
+ }
250
+ process.stdout.write('✅ memory_replay --check: index payload within baseline (+5%).\n');
251
+ return 0;
252
+ }
253
+
254
+ export async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {
255
+ const mode = argv.includes('--baseline')
256
+ ? 'baseline'
257
+ : argv.includes('--paired')
258
+ ? 'paired'
259
+ : argv.includes('--check')
260
+ ? 'check'
261
+ : null;
262
+ if (mode === null) {
263
+ process.stderr.write('usage: memory_replay.ts --baseline | --paired | --check\n');
264
+ return 2;
265
+ }
266
+ return run(mode);
267
+ }
268
+
269
+ function _isCliEntry(): boolean {
270
+ if (process.argv[1] === undefined) return false;
271
+ const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
272
+ if (import.meta.url === argvUrl) return true;
273
+ try {
274
+ return (
275
+ fs.realpathSync(fileURLToPath(import.meta.url)) ===
276
+ fs.realpathSync(path.resolve(process.argv[1]))
277
+ );
278
+ } catch {
279
+ return false;
280
+ }
281
+ }
282
+
283
+ if (_isCliEntry()) {
284
+ main().then((code) => process.exit(code));
285
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Orchestration telemetry recorder — CLI.
3
+ *
4
+ * Appends ONE validated audit-log-v1 line (with an `orchestration` object) to
5
+ * the monthly audit log, from the counts the orchestrating agent has after an
6
+ * auto-dispatch. One command instead of hand-authored JSON — the reliable,
7
+ * lock-respecting (2026-06-30 no-hook decision) capture path.
8
+ *
9
+ * Usage:
10
+ * ./scripts-run src/scripts/orchestration_record \
11
+ * --spawn-count 1 --token-delta -72000 --provenance measured \
12
+ * --tier-chosen lite --tier-source inferred --task-class read-only-fanout \
13
+ * [--tiers sonnet,opus] [--wall-clock-ms 18500] [--dispatch-outcome DONE] \
14
+ * [--dir <audit-dir>] [--dry-run]
15
+ *
16
+ * Read by `src/scripts/orchestration_savings_report.ts`.
17
+ */
18
+
19
+ import * as crypto from 'node:crypto';
20
+ import * as fs from 'node:fs';
21
+ import * as path from 'node:path';
22
+ import { pathToFileURL } from 'node:url';
23
+
24
+ import {
25
+ buildOrchestrationLine,
26
+ type Provenance,
27
+ type TierChosen,
28
+ type TierSource,
29
+ type DispatchOutcome,
30
+ type VerifyMode,
31
+ type Band,
32
+ type LinePhase,
33
+ type LineOutcome,
34
+ type RecordInput,
35
+ } from './_lib/orchestration_record.js';
36
+
37
+ const DEFAULT_DIR = 'agents/runtime/state/audit';
38
+
39
+ type Flags = Record<string, string | boolean>;
40
+
41
+ function parseFlags(argv: string[]): Flags {
42
+ const flags: Flags = {};
43
+ for (let i = 0; i < argv.length; i++) {
44
+ const a = argv[i];
45
+ if (a === undefined || !a.startsWith('--')) continue;
46
+ const eq = a.indexOf('=');
47
+ if (eq !== -1) {
48
+ flags[a.slice(2, eq)] = a.slice(eq + 1);
49
+ continue;
50
+ }
51
+ const key = a.slice(2);
52
+ const next = argv[i + 1];
53
+ if (next !== undefined && !next.startsWith('--')) {
54
+ flags[key] = next;
55
+ i++;
56
+ } else {
57
+ flags[key] = true; // boolean flag (e.g. --dry-run)
58
+ }
59
+ }
60
+ return flags;
61
+ }
62
+
63
+ function str(flags: Flags, key: string): string | undefined {
64
+ const v = flags[key];
65
+ return typeof v === 'string' ? v : undefined;
66
+ }
67
+
68
+ function int(flags: Flags, key: string): number | undefined {
69
+ const v = str(flags, key);
70
+ if (v === undefined) return undefined;
71
+ const n = Number(v);
72
+ return Number.isFinite(n) ? n : NaN; // NaN → caught by the lib's isInt validation
73
+ }
74
+
75
+ /** Deterministic content-hash id (contract allows "ULID or content hash"). */
76
+ function contentHashId(input: RecordInput): string {
77
+ const canonical = JSON.stringify({
78
+ ts: input.ts,
79
+ spawn_count: input.spawn_count,
80
+ token_delta: input.token_delta,
81
+ tier_chosen: input.tier_chosen ?? null,
82
+ task_class: input.task_class ?? null,
83
+ });
84
+ return crypto.createHash('sha256').update(canonical).digest('hex').slice(0, 26).toUpperCase();
85
+ }
86
+
87
+ export function main(argv: string[] = process.argv.slice(2)): number {
88
+ const flags = parseFlags(argv);
89
+ const dir = str(flags, 'dir') ?? DEFAULT_DIR;
90
+ const dryRun = flags['dry-run'] === true;
91
+ const ts = new Date().toISOString();
92
+
93
+ const partial: Omit<RecordInput, 'id'> = {
94
+ spawn_count: int(flags, 'spawn-count') ?? NaN,
95
+ token_delta: int(flags, 'token-delta') ?? NaN,
96
+ token_delta_provenance: str(flags, 'provenance') as Provenance | undefined,
97
+ tiers: str(flags, 'tiers')?.split(',').map((s) => s.trim()).filter(Boolean),
98
+ tier_chosen: str(flags, 'tier-chosen') as TierChosen | undefined,
99
+ tier_source: str(flags, 'tier-source') as TierSource | undefined,
100
+ task_class: str(flags, 'task-class'),
101
+ task_size_estimate: int(flags, 'task-size-estimate'),
102
+ wall_clock_ms: int(flags, 'wall-clock-ms'),
103
+ dispatch_tokens: int(flags, 'dispatch-tokens'),
104
+ session_tier: str(flags, 'session-tier'),
105
+ dispatch_outcome: str(flags, 'dispatch-outcome') as DispatchOutcome | undefined,
106
+ verify_mode: str(flags, 'verify-mode') as VerifyMode | undefined,
107
+ phase: str(flags, 'phase') as LinePhase | undefined,
108
+ outcome: str(flags, 'outcome') as LineOutcome | undefined,
109
+ confidence_band: str(flags, 'confidence-band') as Band | undefined,
110
+ risk_class: str(flags, 'risk-class') as Band | undefined,
111
+ persona: str(flags, 'persona'),
112
+ work_id: str(flags, 'work-id'),
113
+ ts,
114
+ };
115
+ const input: RecordInput = { ...partial, id: contentHashId({ ...partial, id: '' }) };
116
+
117
+ const { line, errors } = buildOrchestrationLine(input);
118
+ if (errors.length || line === null) {
119
+ process.stderr.write('orchestration_record: refused — invalid input:\n');
120
+ for (const e of errors) process.stderr.write(` - ${e}\n`);
121
+ return 1;
122
+ }
123
+
124
+ const serialized = JSON.stringify(line);
125
+ if (dryRun) {
126
+ process.stdout.write(serialized + '\n');
127
+ return 0;
128
+ }
129
+
130
+ fs.mkdirSync(dir, { recursive: true });
131
+ const file = path.join(dir, `${ts.slice(0, 7)}.jsonl`);
132
+ fs.appendFileSync(file, serialized + '\n', 'utf8');
133
+ process.stdout.write(`orchestration telemetry recorded → ${file} (id ${line.id})\n`);
134
+ return 0;
135
+ }
136
+
137
+ if (process.argv[1] !== undefined) {
138
+ const invokedUrl = pathToFileURL(path.resolve(process.argv[1])).href;
139
+ if (import.meta.url === invokedUrl) process.exit(main());
140
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Orchestration savings report — CLI.
3
+ *
4
+ * Reads accumulated orchestration telemetry from the audit-log-v1 JSONL files
5
+ * and prints an aggregate token-savings report. Read-only; no network, no
6
+ * mutation. Handles a missing/empty audit dir gracefully.
7
+ *
8
+ * Usage:
9
+ * ./scripts-run src/scripts/orchestration_savings_report \
10
+ * [--dir <path>] [--format text|json] [--weights lite=1,medium=5,high=15]
11
+ *
12
+ * Default dir: agents/runtime/state/audit (the audit-log-v1 path where the
13
+ * orchestration layer writes telemetry lines). `--weights` tunes the
14
+ * provider-neutral tier weights behind the MODELED cost-%. See
15
+ * `src/agent-src/contexts/execution/orchestration-telemetry.md`.
16
+ */
17
+
18
+ import * as fs from 'node:fs';
19
+ import * as path from 'node:path';
20
+ import { pathToFileURL } from 'node:url';
21
+
22
+ import { aggregateOrchestrationSavings, DEFAULT_TIER_WEIGHTS, type AuditLine, type SavingsReport, type TierWeights } from './_lib/orchestration_savings.js';
23
+
24
+ const DEFAULT_DIR = 'agents/runtime/state/audit';
25
+
26
+ interface Options {
27
+ dir: string;
28
+ format: 'text' | 'json';
29
+ weights: TierWeights;
30
+ }
31
+
32
+ /** Parse `lite=1,medium=5,high=15` → weight map. Returns undefined if nothing valid parsed. */
33
+ function parseWeights(spec: string | undefined): TierWeights | undefined {
34
+ if (!spec) return undefined;
35
+ const w: TierWeights = {};
36
+ for (const pair of spec.split(',')) {
37
+ const [k, v] = pair.split('=');
38
+ const n = Number(v);
39
+ if (k && Number.isFinite(n)) w[k.trim()] = n;
40
+ }
41
+ return Object.keys(w).length ? w : undefined;
42
+ }
43
+
44
+ function parseArgs(argv: string[]): Options {
45
+ const opts: Options = { dir: DEFAULT_DIR, format: 'text', weights: DEFAULT_TIER_WEIGHTS };
46
+ for (let i = 0; i < argv.length; i++) {
47
+ const a = argv[i];
48
+ if (a === undefined) continue;
49
+ if (a === '--dir') opts.dir = argv[++i] ?? opts.dir;
50
+ else if (a.startsWith('--dir=')) opts.dir = a.slice('--dir='.length);
51
+ else if (a === '--format') opts.format = (argv[++i] as Options['format']) ?? opts.format;
52
+ else if (a.startsWith('--format=')) opts.format = a.slice('--format='.length) as Options['format'];
53
+ else if (a === '--weights') opts.weights = parseWeights(argv[++i]) ?? opts.weights;
54
+ else if (a.startsWith('--weights=')) opts.weights = parseWeights(a.slice('--weights='.length)) ?? opts.weights;
55
+ }
56
+ if (opts.format !== 'text' && opts.format !== 'json') opts.format = 'text';
57
+ return opts;
58
+ }
59
+
60
+ /** Read + parse every `.jsonl` line in `dir`. Malformed lines are skipped. */
61
+ export function readAuditLines(dir: string): AuditLine[] {
62
+ if (!fs.existsSync(dir)) return [];
63
+ const lines: AuditLine[] = [];
64
+ for (const entry of fs.readdirSync(dir).sort()) {
65
+ if (!entry.endsWith('.jsonl')) continue;
66
+ const raw = fs.readFileSync(path.join(dir, entry), 'utf8');
67
+ for (const ln of raw.split('\n')) {
68
+ const trimmed = ln.trim();
69
+ if (!trimmed) continue;
70
+ try {
71
+ lines.push(JSON.parse(trimmed) as AuditLine);
72
+ } catch {
73
+ // Skip malformed line — a partial write must not crash the report.
74
+ }
75
+ }
76
+ }
77
+ return lines;
78
+ }
79
+
80
+ function renderText(r: SavingsReport, dir: string): string {
81
+ const out: string[] = [];
82
+ out.push('Orchestration savings report');
83
+ out.push(` source: ${dir}`);
84
+ out.push(` dispatches: ${r.dispatches} (total spawns: ${r.total_spawns})`);
85
+ out.push(` net token_delta: ${r.net_token_delta} (negative = net saved)`);
86
+ out.push(` tokens saved: ${r.tokens_saved} tokens added: ${r.tokens_added}`);
87
+ out.push(` provenance: measured ${r.by_provenance.measured.dispatches} (Δ ${r.by_provenance.measured.net_token_delta}) · estimated ${r.by_provenance.estimated.dispatches} (Δ ${r.by_provenance.estimated.net_token_delta})`);
88
+ out.push(` measured share: ${(r.measured_share * 100).toFixed(0)}%`);
89
+ if (Object.keys(r.by_tier).length) {
90
+ out.push(' by tier (Δ tokens):');
91
+ for (const [tier, d] of Object.entries(r.by_tier).sort()) out.push(` ${tier}: ${d}`);
92
+ }
93
+ if (Object.keys(r.by_task_class).length) {
94
+ out.push(' by task_class (Δ tokens):');
95
+ for (const [cls, d] of Object.entries(r.by_task_class).sort()) out.push(` ${cls}: ${d}`);
96
+ }
97
+ const mc = r.modeled_cost;
98
+ if (mc.cost_reduction_pct !== null) {
99
+ out.push(` MODELED cost reduction: ${(mc.cost_reduction_pct * 100).toFixed(0)}% (over ${mc.covered_dispatches}/${r.dispatches} dispatch(es) with tier data)`);
100
+ out.push(` weights ${JSON.stringify(mc.weights)} · baseline ${mc.baseline_cost_units} → delegated ${mc.delegated_cost_units} cost-units · MODEL, not measured $`);
101
+ } else {
102
+ out.push(' MODELED cost reduction: n/a (needs dispatch_tokens + session_tier + tier_chosen on a dispatch)');
103
+ }
104
+ out.push(' notes:');
105
+ for (const n of r.notes) out.push(` - ${n}`);
106
+ return out.join('\n');
107
+ }
108
+
109
+ export function main(argv: string[] = process.argv.slice(2)): number {
110
+ const opts = parseArgs(argv);
111
+ const lines = readAuditLines(opts.dir);
112
+ const report = aggregateOrchestrationSavings(lines, opts.weights);
113
+ if (opts.format === 'json') {
114
+ process.stdout.write(JSON.stringify({ ...report, source: opts.dir }, null, 2) + '\n');
115
+ } else {
116
+ process.stdout.write(renderText(report, opts.dir) + '\n');
117
+ }
118
+ return 0;
119
+ }
120
+
121
+ if (process.argv[1] !== undefined) {
122
+ const invokedUrl = pathToFileURL(path.resolve(process.argv[1])).href;
123
+ if (import.meta.url === invokedUrl) process.exit(main());
124
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/event4u-app/agent-config/scripts/schemas/domain-truth.schema.json",
4
+ "x-schemaVersion": "1.0.0",
5
+ "title": "Domain-truth fixtures (domain-truth.json)",
6
+ "$comment": "Domain-SOUNDNESS fixtures for non-coding default-surface skills (road-to-domain-soundness Phase 1). DISTINCT from evals.json (behavioural output quality) and triggers.json (activation): a domain-truth case asserts the embedded domain heuristic is actually CORRECT off the forged TS/PHP stacks. Every case's answer key must be traceable to a CITED external method/standard — never the skill's own output (that validates nothing). A skill lacking this file is `unvalidated`; presence + a passing run makes it `validated`.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": ["skill", "domain", "cases"],
10
+ "properties": {
11
+ "skill": {
12
+ "type": "string",
13
+ "pattern": "^[a-z][a-z0-9-]*$",
14
+ "description": "Skill slug; must match the parent skill directory name."
15
+ },
16
+ "domain": {
17
+ "type": "string",
18
+ "enum": ["finance", "founder", "ops", "content"],
19
+ "description": "The non-coding domain whose ground truth this fixture checks."
20
+ },
21
+ "_note": {
22
+ "type": "string",
23
+ "description": "Optional free-text note (calibration / provenance status). Advisory."
24
+ },
25
+ "cases": {
26
+ "type": "array",
27
+ "minItems": 1,
28
+ "description": "One or more domain-truth cases: a scenario + a domain-correct answer key with a SOURCED rationale.",
29
+ "items": {
30
+ "type": "object",
31
+ "additionalProperties": false,
32
+ "required": ["id", "scenario", "check", "source"],
33
+ "properties": {
34
+ "id": {
35
+ "type": "string",
36
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
37
+ "description": "Stable case slug, unique within this file."
38
+ },
39
+ "scenario": {
40
+ "type": "string",
41
+ "minLength": 1,
42
+ "description": "The input scenario handed to the skill under test."
43
+ },
44
+ "source": {
45
+ "type": "string",
46
+ "minLength": 1,
47
+ "description": "The CITED external method/standard the answer key is traceable to (a finance text, an IC framework, a named practice). A case whose key cannot cite an external basis is not a domain-truth case — it is rejected. NEVER the skill's own output."
48
+ },
49
+ "check": {
50
+ "description": "How soundness is judged. Deterministic where the domain has a right number; rubric where it does not (the rubric criterion must name the cited practice, graded by a pinned judge, recorded as a known-limit — never a hidden LLM-judge).",
51
+ "oneOf": [
52
+ {
53
+ "type": "object",
54
+ "additionalProperties": false,
55
+ "required": ["kind", "expected"],
56
+ "properties": {
57
+ "kind": { "const": "deterministic" },
58
+ "expected": {
59
+ "type": ["number", "string"],
60
+ "description": "The computed-correct answer (e.g. a runway in months, an NPV) with the working shown in `rationale`."
61
+ },
62
+ "tolerance": {
63
+ "type": "number",
64
+ "minimum": 0,
65
+ "description": "Absolute tolerance for a numeric expected value (0 = exact)."
66
+ },
67
+ "rationale": {
68
+ "type": "string",
69
+ "minLength": 1,
70
+ "description": "The working that derives `expected` from the cited method, so a maintainer without the domain can check the key against the source, not against the skill."
71
+ }
72
+ },
73
+ "required": ["kind", "expected", "rationale"]
74
+ },
75
+ {
76
+ "type": "object",
77
+ "additionalProperties": false,
78
+ "required": ["kind", "criterion"],
79
+ "properties": {
80
+ "kind": { "const": "rubric" },
81
+ "criterion": {
82
+ "type": "string",
83
+ "minLength": 1,
84
+ "description": "Pass criterion phrased as \"matches <named practice>\", graded by a pinned judge. NON-deterministic — recorded as a known-limit with a witness."
85
+ }
86
+ }
87
+ }
88
+ ]
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }