knosky 0.6.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +149 -93
  2. package/CREDITS.md +14 -14
  3. package/LICENSE.md +76 -76
  4. package/LIMITATIONS.md +33 -23
  5. package/PRIVACY.md +30 -30
  6. package/README.md +170 -117
  7. package/SECURITY.md +78 -46
  8. package/action/post-comment.mjs +94 -89
  9. package/action.yml +62 -62
  10. package/bin/knosky.mjs +279 -105
  11. package/core/CONTRACT.md +70 -70
  12. package/core/append-only-checkpoint.mjs +215 -0
  13. package/core/audit-writer.mjs +317 -0
  14. package/core/benchmark-results.mjs +225 -225
  15. package/core/bundle.mjs +178 -178
  16. package/core/churn.mjs +23 -23
  17. package/core/ci.mjs +268 -268
  18. package/core/comparison.mjs +189 -189
  19. package/core/config.mjs +189 -189
  20. package/core/constants.mjs +13 -13
  21. package/core/contract.mjs +123 -123
  22. package/core/cross-repo.mjs +111 -111
  23. package/core/decision-codes.mjs +92 -0
  24. package/core/destination.mjs +161 -161
  25. package/core/district-classification.mjs +111 -0
  26. package/core/doctor-scorecard.mjs +369 -0
  27. package/core/domain-store.mjs +347 -0
  28. package/core/edges.mjs +43 -43
  29. package/core/escalate.mjs +68 -68
  30. package/core/freshness.mjs +198 -194
  31. package/core/fs-indexer.mjs +218 -218
  32. package/core/key-store.mjs +348 -348
  33. package/core/layout.mjs +46 -46
  34. package/core/ledger.mjs +176 -141
  35. package/core/local-ipc-identity.mjs +500 -0
  36. package/core/lod.mjs +155 -155
  37. package/core/mode-b.mjs +410 -0
  38. package/core/multi-model-benchmark.mjs +405 -405
  39. package/core/net-lockdown.mjs +421 -0
  40. package/core/onboarding.mjs +223 -223
  41. package/core/operator-auth.mjs +317 -0
  42. package/core/overlays.mjs +45 -45
  43. package/core/policy-lattice.mjs +142 -0
  44. package/core/pr-comment.mjs +198 -198
  45. package/core/protocol-spec.mjs +460 -460
  46. package/core/provenance.mjs +320 -0
  47. package/core/retrieve.mjs +63 -63
  48. package/core/route.mjs +304 -304
  49. package/core/schema.mjs +275 -275
  50. package/core/signing-tiers.mjs +1265 -0
  51. package/core/swarm-bench.mjs +106 -0
  52. package/core/swarm-coordinator.mjs +867 -0
  53. package/core/trust-root-rekey.mjs +410 -0
  54. package/mcp/server.mjs +264 -108
  55. package/package.json +56 -46
  56. package/renderer/art/kenney/buildingTiles_sheet.xml +130 -130
  57. package/renderer/art/kenney/cityDetails_sheet.xml +12 -12
  58. package/renderer/art/kenney/landscapeTiles_sheet.xml +129 -129
  59. package/renderer/art/kenney/sheet_allCars.xml +545 -545
  60. package/renderer/build-rich.mjs +43 -43
  61. package/renderer/city.template.html +808 -808
  62. package/ssot/decision-codes.json +133 -0
  63. package/ssot/ladder-l0-l3.md +232 -0
  64. package/ssot/tool-menu.json +130 -0
@@ -1,225 +1,225 @@
1
- // KnoSky token-efficiency benchmark results (SAT-439 / SAT-469).
2
- //
3
- // Source: five tasks run head-to-head — naive agent (no KnoSky guidance) vs.
4
- // KnoSky-guided agent — measured with the `comparison-run` protocol defined in
5
- // core/comparison.mjs. Summaries exported here are the single source of truth
6
- // for all public copy (README.md, wiki, etc.); update the raw runs first and
7
- // re-derive the summary constants rather than editing the constants directly.
8
- //
9
- // The runs are stored in the COMPARISON_RUNS array for traceability and to
10
- // allow tests to re-derive summary statistics from the raw data.
11
-
12
- import { makeComparisonRun, validateComparisonRun } from './comparison.mjs';
13
-
14
- // ---------------------------------------------------------------------------
15
- // Raw comparison runs (SAT-439)
16
- // ---------------------------------------------------------------------------
17
-
18
- /**
19
- * Five tasks measured head-to-head. Each entry is a valid `comparison-run`
20
- * artifact per core/comparison.mjs. Fields:
21
- * tokens_in / tokens_out : LLM tokens consumed / produced
22
- * tool_calls : total tool-call round-trips
23
- * time_to_relevant_file_ms : ms until the agent first cited the target file,
24
- * or null when the naive agent never found it
25
- * correct : whether the agent reached the correct answer
26
- */
27
- export const COMPARISON_RUNS = [
28
- makeComparisonRun({
29
- task_id: 'sat439-t01',
30
- task_description: 'Locate the authentication module and explain its entry point',
31
- target_files: ['core/key-store.mjs'],
32
- naive: { tokens_in: 7400, tokens_out: 780, tool_calls: 13, time_to_relevant_file_ms: 10800, correct: false },
33
- guided: { tokens_in: 2250, tokens_out: 197, tool_calls: 4, time_to_relevant_file_ms: 1800, correct: true },
34
- }),
35
- makeComparisonRun({
36
- task_id: 'sat439-t02',
37
- task_description: 'Find every file that imports the ledger module',
38
- target_files: ['core/ledger.mjs'],
39
- naive: { tokens_in: 11600, tokens_out: 1040, tool_calls: 20, time_to_relevant_file_ms: null, correct: false },
40
- guided: { tokens_in: 4000, tokens_out: 305, tool_calls: 5, time_to_relevant_file_ms: 2050, correct: true },
41
- }),
42
- makeComparisonRun({
43
- task_id: 'sat439-t03',
44
- task_description: 'Trace which modules contribute to the MCP server entrypoint',
45
- target_files: ['mcp/server.mjs'],
46
- naive: { tokens_in: 5800, tokens_out: 640, tool_calls: 11, time_to_relevant_file_ms: 8400, correct: false },
47
- guided: { tokens_in: 1900, tokens_out: 185, tool_calls: 4, time_to_relevant_file_ms: 1400, correct: true },
48
- }),
49
- makeComparisonRun({
50
- task_id: 'sat439-t04',
51
- task_description: 'List all test files that cover the route engine',
52
- target_files: ['core/route.mjs', 'test/route.test.mjs'],
53
- naive: { tokens_in: 9200, tokens_out: 880, tool_calls: 16, time_to_relevant_file_ms: null, correct: false },
54
- guided: { tokens_in: 3000, tokens_out: 220, tool_calls: 5, time_to_relevant_file_ms: 1900, correct: true },
55
- }),
56
- makeComparisonRun({
57
- task_id: 'sat439-t05',
58
- task_description: 'Identify the freshness / churn modules and their relationship',
59
- target_files: ['core/freshness.mjs', 'core/churn.mjs'],
60
- naive: { tokens_in: 8000, tokens_out: 760, tool_calls: 14, time_to_relevant_file_ms: 10200, correct: false },
61
- guided: { tokens_in: 2500, tokens_out: 200, tool_calls: 4, time_to_relevant_file_ms: 1700, correct: true },
62
- }),
63
- ];
64
-
65
- // ---------------------------------------------------------------------------
66
- // Derived summary constants
67
- // Computed once at module load from COMPARISON_RUNS — never hand-tuned.
68
- // ---------------------------------------------------------------------------
69
-
70
- /**
71
- * Aggregate token counts across all runs.
72
- * @type {{ naive: number, guided: number }}
73
- */
74
- export const TOTAL_TOKENS = COMPARISON_RUNS.reduce(
75
- (acc, run) => {
76
- acc.naive += run.naive.tokens_in + run.naive.tokens_out;
77
- acc.guided += run.guided.tokens_in + run.guided.tokens_out;
78
- return acc;
79
- },
80
- { naive: 0, guided: 0 },
81
- );
82
-
83
- /**
84
- * Aggregate tool-call counts across all runs.
85
- * @type {{ naive: number, guided: number }}
86
- */
87
- export const TOTAL_TOOL_CALLS = COMPARISON_RUNS.reduce(
88
- (acc, run) => {
89
- acc.naive += run.naive.tool_calls;
90
- acc.guided += run.guided.tool_calls;
91
- return acc;
92
- },
93
- { naive: 0, guided: 0 },
94
- );
95
-
96
- /**
97
- * Token reduction percentage (rounded to nearest integer).
98
- * "X% fewer tokens" claim derived from TOTAL_TOKENS.
99
- * @type {number}
100
- */
101
- export const TOKEN_REDUCTION_PCT = Math.round(
102
- 100 * (TOTAL_TOKENS.naive - TOTAL_TOKENS.guided) / TOTAL_TOKENS.naive,
103
- );
104
-
105
- /**
106
- * Tool-call reduction percentage (rounded to nearest integer).
107
- * @type {number}
108
- */
109
- export const TOOL_CALL_REDUCTION_PCT = Math.round(
110
- 100 * (TOTAL_TOOL_CALLS.naive - TOTAL_TOOL_CALLS.guided) / TOTAL_TOOL_CALLS.naive,
111
- );
112
-
113
- /**
114
- * Mean time-to-relevant-file speedup (guided vs. naive), restricted to runs
115
- * where both agents found the file (time_to_relevant_file_ms !== null).
116
- * Rounded to one decimal place.
117
- * @type {number}
118
- */
119
- export const TTRF_SPEEDUP_X = (() => {
120
- const both = COMPARISON_RUNS.filter(
121
- r => r.naive.time_to_relevant_file_ms !== null &&
122
- r.guided.time_to_relevant_file_ms !== null,
123
- );
124
- if (both.length === 0) return 0;
125
- const naiveMean = both.reduce((s, r) => s + r.naive.time_to_relevant_file_ms, 0) / both.length;
126
- const guidedMean = both.reduce((s, r) => s + r.guided.time_to_relevant_file_ms, 0) / both.length;
127
- return Math.round(10 * naiveMean / guidedMean) / 10;
128
- })();
129
-
130
- /**
131
- * Human-readable headline in the form used in README.md.
132
- * Built from the derived constants so README claims stay consistent.
133
- * @type {string}
134
- */
135
- export const HEADLINE_CLAIM =
136
- `${TOKEN_REDUCTION_PCT}% fewer tokens, ` +
137
- `${TOOL_CALL_REDUCTION_PCT}% fewer tool calls, ` +
138
- `${TTRF_SPEEDUP_X}× faster to the right file`;
139
-
140
- /**
141
- * Schema version for the benchmark-results module.
142
- * Bump this when the run schema or summary fields change.
143
- * @type {string}
144
- */
145
- export const BENCHMARK_RESULTS_VERSION = '1.0';
146
-
147
- // ---------------------------------------------------------------------------
148
- // formatBenchmarkReport (SAT-460)
149
- // ---------------------------------------------------------------------------
150
-
151
- /**
152
- * Produce a human-readable Markdown benchmark report from the comparison runs
153
- * stored in this module. The report is deterministic — same input → same
154
- * output — and safe to embed in wikis, PR comments, or printed to stdout.
155
- *
156
- * Layout:
157
- * 1. Headline summary block (the three key numbers)
158
- * 2. Per-run table (task id, tokens naive/guided, tool calls, TTRF, correct)
159
- * 3. Footer with provenance note
160
- *
161
- * @param {object} [opts]
162
- * @param {object[]} [opts.runs=COMPARISON_RUNS] — override for testing.
163
- * @returns {string} Markdown string, no trailing newline.
164
- */
165
- export function formatBenchmarkReport({ runs = COMPARISON_RUNS } = {}) {
166
- // --- summary numbers (re-derived from the provided runs so the function is
167
- // self-contained and testable with custom run sets) -------------------
168
- const totalNaiveTok = runs.reduce((s, r) => s + r.naive.tokens_in + r.naive.tokens_out, 0);
169
- const totalGuidedTok = runs.reduce((s, r) => s + r.guided.tokens_in + r.guided.tokens_out, 0);
170
- const totalNaiveCalls = runs.reduce((s, r) => s + r.naive.tool_calls, 0);
171
- const totalGuidedCalls = runs.reduce((s, r) => s + r.guided.tool_calls, 0);
172
-
173
- const tokenPct = totalNaiveTok > 0
174
- ? Math.round(100 * (totalNaiveTok - totalGuidedTok) / totalNaiveTok)
175
- : 0;
176
- const callPct = totalNaiveCalls > 0
177
- ? Math.round(100 * (totalNaiveCalls - totalGuidedCalls) / totalNaiveCalls)
178
- : 0;
179
-
180
- const both = runs.filter(
181
- r => r.naive.time_to_relevant_file_ms !== null &&
182
- r.guided.time_to_relevant_file_ms !== null,
183
- );
184
- const speedup = both.length > 0
185
- ? Math.round(
186
- 10 * (both.reduce((s, r) => s + r.naive.time_to_relevant_file_ms, 0) / both.length) /
187
- (both.reduce((s, r) => s + r.guided.time_to_relevant_file_ms, 0) / both.length),
188
- ) / 10
189
- : 0;
190
- const speedupStr = Number.isInteger(speedup) ? String(speedup) : speedup.toFixed(1);
191
-
192
- const guidedCorrect = runs.filter(r => r.guided.correct).length;
193
- const naiveCorrect = runs.filter(r => r.naive.correct).length;
194
-
195
- // --- headline block -------------------------------------------------------
196
- const lines = [
197
- '## KnoSky token-efficiency benchmark (SAT-439)',
198
- '',
199
- `**${tokenPct}% fewer tokens · ${callPct}% fewer tool calls · ${speedupStr}× faster to the right file**`,
200
- '',
201
- `_${runs.length} tasks, naive agent vs. KnoSky-guided agent._`,
202
- `_Guided: ${guidedCorrect}/${runs.length} correct. Naive: ${naiveCorrect}/${runs.length} correct._`,
203
- '',
204
- '### Per-task results',
205
- '',
206
- '| Task | Tokens (naive) | Tokens (guided) | Tool calls (naive) | Tool calls (guided) | TTRF naive (ms) | TTRF guided (ms) | Guided correct |',
207
- '|------|---------------:|----------------:|-------------------:|--------------------:|----------------:|-----------------:|:--------------:|',
208
- ];
209
-
210
- for (const run of runs) {
211
- const tnaive = run.naive.tokens_in + run.naive.tokens_out;
212
- const tguided = run.guided.tokens_in + run.guided.tokens_out;
213
- const ttrfN = run.naive.time_to_relevant_file_ms === null ? '—' : String(run.naive.time_to_relevant_file_ms);
214
- const ttrfG = run.guided.time_to_relevant_file_ms === null ? '—' : String(run.guided.time_to_relevant_file_ms);
215
- const correct = run.guided.correct ? '✓' : '✗';
216
- lines.push(
217
- `| ${run.task_id} | ${tnaive} | ${tguided} | ${run.naive.tool_calls} | ${run.guided.tool_calls} | ${ttrfN} | ${ttrfG} | ${correct} |`,
218
- );
219
- }
220
-
221
- lines.push('');
222
- lines.push(`_Data source: \`core/benchmark-results.mjs\` (BENCHMARK_RESULTS_VERSION ${BENCHMARK_RESULTS_VERSION})._`);
223
-
224
- return lines.join('\n');
225
- }
1
+ // KnoSky token-efficiency benchmark results (SAT-439 / SAT-469).
2
+ //
3
+ // Source: five tasks run head-to-head — naive agent (no KnoSky guidance) vs.
4
+ // KnoSky-guided agent — measured with the `comparison-run` protocol defined in
5
+ // core/comparison.mjs. Summaries exported here are the single source of truth
6
+ // for all public copy (README.md, wiki, etc.); update the raw runs first and
7
+ // re-derive the summary constants rather than editing the constants directly.
8
+ //
9
+ // The runs are stored in the COMPARISON_RUNS array for traceability and to
10
+ // allow tests to re-derive summary statistics from the raw data.
11
+
12
+ import { makeComparisonRun, validateComparisonRun } from './comparison.mjs';
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Raw comparison runs (SAT-439)
16
+ // ---------------------------------------------------------------------------
17
+
18
+ /**
19
+ * Five tasks measured head-to-head. Each entry is a valid `comparison-run`
20
+ * artifact per core/comparison.mjs. Fields:
21
+ * tokens_in / tokens_out : LLM tokens consumed / produced
22
+ * tool_calls : total tool-call round-trips
23
+ * time_to_relevant_file_ms : ms until the agent first cited the target file,
24
+ * or null when the naive agent never found it
25
+ * correct : whether the agent reached the correct answer
26
+ */
27
+ export const COMPARISON_RUNS = [
28
+ makeComparisonRun({
29
+ task_id: 'sat439-t01',
30
+ task_description: 'Locate the authentication module and explain its entry point',
31
+ target_files: ['core/key-store.mjs'],
32
+ naive: { tokens_in: 7400, tokens_out: 780, tool_calls: 13, time_to_relevant_file_ms: 10800, correct: false },
33
+ guided: { tokens_in: 2250, tokens_out: 197, tool_calls: 4, time_to_relevant_file_ms: 1800, correct: true },
34
+ }),
35
+ makeComparisonRun({
36
+ task_id: 'sat439-t02',
37
+ task_description: 'Find every file that imports the ledger module',
38
+ target_files: ['core/ledger.mjs'],
39
+ naive: { tokens_in: 11600, tokens_out: 1040, tool_calls: 20, time_to_relevant_file_ms: null, correct: false },
40
+ guided: { tokens_in: 4000, tokens_out: 305, tool_calls: 5, time_to_relevant_file_ms: 2050, correct: true },
41
+ }),
42
+ makeComparisonRun({
43
+ task_id: 'sat439-t03',
44
+ task_description: 'Trace which modules contribute to the MCP server entrypoint',
45
+ target_files: ['mcp/server.mjs'],
46
+ naive: { tokens_in: 5800, tokens_out: 640, tool_calls: 11, time_to_relevant_file_ms: 8400, correct: false },
47
+ guided: { tokens_in: 1900, tokens_out: 185, tool_calls: 4, time_to_relevant_file_ms: 1400, correct: true },
48
+ }),
49
+ makeComparisonRun({
50
+ task_id: 'sat439-t04',
51
+ task_description: 'List all test files that cover the route engine',
52
+ target_files: ['core/route.mjs', 'test/route.test.mjs'],
53
+ naive: { tokens_in: 9200, tokens_out: 880, tool_calls: 16, time_to_relevant_file_ms: null, correct: false },
54
+ guided: { tokens_in: 3000, tokens_out: 220, tool_calls: 5, time_to_relevant_file_ms: 1900, correct: true },
55
+ }),
56
+ makeComparisonRun({
57
+ task_id: 'sat439-t05',
58
+ task_description: 'Identify the freshness / churn modules and their relationship',
59
+ target_files: ['core/freshness.mjs', 'core/churn.mjs'],
60
+ naive: { tokens_in: 8000, tokens_out: 760, tool_calls: 14, time_to_relevant_file_ms: 10200, correct: false },
61
+ guided: { tokens_in: 2500, tokens_out: 200, tool_calls: 4, time_to_relevant_file_ms: 1700, correct: true },
62
+ }),
63
+ ];
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Derived summary constants
67
+ // Computed once at module load from COMPARISON_RUNS — never hand-tuned.
68
+ // ---------------------------------------------------------------------------
69
+
70
+ /**
71
+ * Aggregate token counts across all runs.
72
+ * @type {{ naive: number, guided: number }}
73
+ */
74
+ export const TOTAL_TOKENS = COMPARISON_RUNS.reduce(
75
+ (acc, run) => {
76
+ acc.naive += run.naive.tokens_in + run.naive.tokens_out;
77
+ acc.guided += run.guided.tokens_in + run.guided.tokens_out;
78
+ return acc;
79
+ },
80
+ { naive: 0, guided: 0 },
81
+ );
82
+
83
+ /**
84
+ * Aggregate tool-call counts across all runs.
85
+ * @type {{ naive: number, guided: number }}
86
+ */
87
+ export const TOTAL_TOOL_CALLS = COMPARISON_RUNS.reduce(
88
+ (acc, run) => {
89
+ acc.naive += run.naive.tool_calls;
90
+ acc.guided += run.guided.tool_calls;
91
+ return acc;
92
+ },
93
+ { naive: 0, guided: 0 },
94
+ );
95
+
96
+ /**
97
+ * Token reduction percentage (rounded to nearest integer).
98
+ * "X% fewer tokens" claim derived from TOTAL_TOKENS.
99
+ * @type {number}
100
+ */
101
+ export const TOKEN_REDUCTION_PCT = Math.round(
102
+ 100 * (TOTAL_TOKENS.naive - TOTAL_TOKENS.guided) / TOTAL_TOKENS.naive,
103
+ );
104
+
105
+ /**
106
+ * Tool-call reduction percentage (rounded to nearest integer).
107
+ * @type {number}
108
+ */
109
+ export const TOOL_CALL_REDUCTION_PCT = Math.round(
110
+ 100 * (TOTAL_TOOL_CALLS.naive - TOTAL_TOOL_CALLS.guided) / TOTAL_TOOL_CALLS.naive,
111
+ );
112
+
113
+ /**
114
+ * Mean time-to-relevant-file speedup (guided vs. naive), restricted to runs
115
+ * where both agents found the file (time_to_relevant_file_ms !== null).
116
+ * Rounded to one decimal place.
117
+ * @type {number}
118
+ */
119
+ export const TTRF_SPEEDUP_X = (() => {
120
+ const both = COMPARISON_RUNS.filter(
121
+ r => r.naive.time_to_relevant_file_ms !== null &&
122
+ r.guided.time_to_relevant_file_ms !== null,
123
+ );
124
+ if (both.length === 0) return 0;
125
+ const naiveMean = both.reduce((s, r) => s + r.naive.time_to_relevant_file_ms, 0) / both.length;
126
+ const guidedMean = both.reduce((s, r) => s + r.guided.time_to_relevant_file_ms, 0) / both.length;
127
+ return Math.round(10 * naiveMean / guidedMean) / 10;
128
+ })();
129
+
130
+ /**
131
+ * Human-readable headline in the form used in README.md.
132
+ * Built from the derived constants so README claims stay consistent.
133
+ * @type {string}
134
+ */
135
+ export const HEADLINE_CLAIM =
136
+ `${TOKEN_REDUCTION_PCT}% fewer tokens, ` +
137
+ `${TOOL_CALL_REDUCTION_PCT}% fewer tool calls, ` +
138
+ `${TTRF_SPEEDUP_X}× faster to the right file`;
139
+
140
+ /**
141
+ * Schema version for the benchmark-results module.
142
+ * Bump this when the run schema or summary fields change.
143
+ * @type {string}
144
+ */
145
+ export const BENCHMARK_RESULTS_VERSION = '1.0';
146
+
147
+ // ---------------------------------------------------------------------------
148
+ // formatBenchmarkReport (SAT-460)
149
+ // ---------------------------------------------------------------------------
150
+
151
+ /**
152
+ * Produce a human-readable Markdown benchmark report from the comparison runs
153
+ * stored in this module. The report is deterministic — same input → same
154
+ * output — and safe to embed in wikis, PR comments, or printed to stdout.
155
+ *
156
+ * Layout:
157
+ * 1. Headline summary block (the three key numbers)
158
+ * 2. Per-run table (task id, tokens naive/guided, tool calls, TTRF, correct)
159
+ * 3. Footer with provenance note
160
+ *
161
+ * @param {object} [opts]
162
+ * @param {object[]} [opts.runs=COMPARISON_RUNS] — override for testing.
163
+ * @returns {string} Markdown string, no trailing newline.
164
+ */
165
+ export function formatBenchmarkReport({ runs = COMPARISON_RUNS } = {}) {
166
+ // --- summary numbers (re-derived from the provided runs so the function is
167
+ // self-contained and testable with custom run sets) -------------------
168
+ const totalNaiveTok = runs.reduce((s, r) => s + r.naive.tokens_in + r.naive.tokens_out, 0);
169
+ const totalGuidedTok = runs.reduce((s, r) => s + r.guided.tokens_in + r.guided.tokens_out, 0);
170
+ const totalNaiveCalls = runs.reduce((s, r) => s + r.naive.tool_calls, 0);
171
+ const totalGuidedCalls = runs.reduce((s, r) => s + r.guided.tool_calls, 0);
172
+
173
+ const tokenPct = totalNaiveTok > 0
174
+ ? Math.round(100 * (totalNaiveTok - totalGuidedTok) / totalNaiveTok)
175
+ : 0;
176
+ const callPct = totalNaiveCalls > 0
177
+ ? Math.round(100 * (totalNaiveCalls - totalGuidedCalls) / totalNaiveCalls)
178
+ : 0;
179
+
180
+ const both = runs.filter(
181
+ r => r.naive.time_to_relevant_file_ms !== null &&
182
+ r.guided.time_to_relevant_file_ms !== null,
183
+ );
184
+ const speedup = both.length > 0
185
+ ? Math.round(
186
+ 10 * (both.reduce((s, r) => s + r.naive.time_to_relevant_file_ms, 0) / both.length) /
187
+ (both.reduce((s, r) => s + r.guided.time_to_relevant_file_ms, 0) / both.length),
188
+ ) / 10
189
+ : 0;
190
+ const speedupStr = Number.isInteger(speedup) ? String(speedup) : speedup.toFixed(1);
191
+
192
+ const guidedCorrect = runs.filter(r => r.guided.correct).length;
193
+ const naiveCorrect = runs.filter(r => r.naive.correct).length;
194
+
195
+ // --- headline block -------------------------------------------------------
196
+ const lines = [
197
+ '## KnoSky token-efficiency benchmark (SAT-439)',
198
+ '',
199
+ `**${tokenPct}% fewer tokens · ${callPct}% fewer tool calls · ${speedupStr}× faster to the right file**`,
200
+ '',
201
+ `_${runs.length} tasks, naive agent vs. KnoSky-guided agent._`,
202
+ `_Guided: ${guidedCorrect}/${runs.length} correct. Naive: ${naiveCorrect}/${runs.length} correct._`,
203
+ '',
204
+ '### Per-task results',
205
+ '',
206
+ '| Task | Tokens (naive) | Tokens (guided) | Tool calls (naive) | Tool calls (guided) | TTRF naive (ms) | TTRF guided (ms) | Guided correct |',
207
+ '|------|---------------:|----------------:|-------------------:|--------------------:|----------------:|-----------------:|:--------------:|',
208
+ ];
209
+
210
+ for (const run of runs) {
211
+ const tnaive = run.naive.tokens_in + run.naive.tokens_out;
212
+ const tguided = run.guided.tokens_in + run.guided.tokens_out;
213
+ const ttrfN = run.naive.time_to_relevant_file_ms === null ? '—' : String(run.naive.time_to_relevant_file_ms);
214
+ const ttrfG = run.guided.time_to_relevant_file_ms === null ? '—' : String(run.guided.time_to_relevant_file_ms);
215
+ const correct = run.guided.correct ? '✓' : '✗';
216
+ lines.push(
217
+ `| ${run.task_id} | ${tnaive} | ${tguided} | ${run.naive.tool_calls} | ${run.guided.tool_calls} | ${ttrfN} | ${ttrfG} | ${correct} |`,
218
+ );
219
+ }
220
+
221
+ lines.push('');
222
+ lines.push(`_Data source: \`core/benchmark-results.mjs\` (BENCHMARK_RESULTS_VERSION ${BENCHMARK_RESULTS_VERSION})._`);
223
+
224
+ return lines.join('\n');
225
+ }