@holmes-lab/holmes-kit 0.2.0 → 0.3.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 (134) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/.build-id +1 -1
  3. package/dist/holmes/assoc/assoc-arm.d.ts +94 -0
  4. package/dist/holmes/assoc/assoc-arm.js +187 -0
  5. package/dist/holmes/assoc/explore.d.ts +21 -0
  6. package/dist/holmes/assoc/explore.js +160 -0
  7. package/dist/holmes/assoc/impact-baseline.d.ts +13 -0
  8. package/dist/holmes/assoc/impact-baseline.js +48 -0
  9. package/dist/holmes/assoc/ppr.d.ts +64 -0
  10. package/dist/holmes/assoc/ppr.js +110 -0
  11. package/dist/holmes/cli/agents.d.ts +24 -11
  12. package/dist/holmes/cli/agents.js +93 -17
  13. package/dist/holmes/cli/codex-toml.d.ts +26 -0
  14. package/dist/holmes/cli/codex-toml.js +282 -0
  15. package/dist/holmes/cli/doctor.d.ts +56 -0
  16. package/dist/holmes/cli/doctor.js +348 -18
  17. package/dist/holmes/cli/index.js +16 -2
  18. package/dist/holmes/cli/init.js +78 -0
  19. package/dist/holmes/cli/interactive-prompt.js +4 -4
  20. package/dist/holmes/cli/mcp-launcher.d.ts +2 -2
  21. package/dist/holmes/cli/semantic-key.d.ts +19 -0
  22. package/dist/holmes/cli/semantic-key.js +93 -0
  23. package/dist/holmes/config/config.d.ts +9 -0
  24. package/dist/holmes/config/config.js +8 -1
  25. package/dist/holmes/cpg/language-capability.d.ts +65 -0
  26. package/dist/holmes/cpg/language-capability.js +145 -0
  27. package/dist/holmes/cpg/language-parser-walk.js +179 -34
  28. package/dist/holmes/cpg/language-parser.d.ts +1 -1
  29. package/dist/holmes/governance/ledger-rechain.d.ts +12 -0
  30. package/dist/holmes/governance/ledger-rechain.js +17 -2
  31. package/dist/holmes/governance/provenance-ledger.js +21 -0
  32. package/dist/holmes/guardrail/blind-spots.js +12 -1
  33. package/dist/holmes/guardrail/impact-gate.d.ts +77 -0
  34. package/dist/holmes/guardrail/impact-gate.js +263 -0
  35. package/dist/holmes/guardrail/write-target.d.ts +38 -1
  36. package/dist/holmes/guardrail/write-target.js +48 -4
  37. package/dist/holmes/hooks/adapters/antigravity.js +12 -1
  38. package/dist/holmes/hooks/corrupt-state-run.d.ts +33 -0
  39. package/dist/holmes/hooks/corrupt-state-run.js +16 -0
  40. package/dist/holmes/hooks/pre-tool-use.d.ts +26 -0
  41. package/dist/holmes/hooks/pre-tool-use.js +203 -11
  42. package/dist/holmes/hooks/stop.d.ts +65 -0
  43. package/dist/holmes/hooks/stop.js +200 -2
  44. package/dist/holmes/mcp/handlers.d.ts +88 -0
  45. package/dist/holmes/mcp/handlers.js +597 -16
  46. package/dist/holmes/mcp/maintenance-analyze.d.ts +435 -0
  47. package/dist/holmes/mcp/maintenance-analyze.js +994 -0
  48. package/dist/holmes/mcp/maintenance-evidence.d.ts +140 -0
  49. package/dist/holmes/mcp/maintenance-evidence.js +253 -0
  50. package/dist/holmes/mcp/tool-schemas.js +71 -0
  51. package/dist/holmes/project/root.js +3 -1
  52. package/dist/holmes/review/baseline-arm.d.ts +37 -0
  53. package/dist/holmes/review/baseline-arm.js +51 -0
  54. package/dist/holmes/review/captured-stdin-guard.d.ts +8 -0
  55. package/dist/holmes/review/captured-stdin-guard.js +48 -0
  56. package/dist/holmes/review/coherence-verify.d.ts +31 -0
  57. package/dist/holmes/review/coherence-verify.js +144 -0
  58. package/dist/holmes/review/commit-text.d.ts +50 -0
  59. package/dist/holmes/review/commit-text.js +76 -0
  60. package/dist/holmes/review/confidence-calibration.d.ts +39 -0
  61. package/dist/holmes/review/confidence-calibration.js +39 -0
  62. package/dist/holmes/review/content-baseline.d.ts +38 -0
  63. package/dist/holmes/review/content-baseline.js +103 -0
  64. package/dist/holmes/review/content-verify.d.ts +20 -0
  65. package/dist/holmes/review/content-verify.js +73 -0
  66. package/dist/holmes/review/dense-retrieval.d.ts +66 -0
  67. package/dist/holmes/review/dense-retrieval.js +97 -0
  68. package/dist/holmes/review/edge-quality.d.ts +44 -0
  69. package/dist/holmes/review/edge-quality.js +117 -0
  70. package/dist/holmes/review/evaluation-metrics.d.ts +138 -0
  71. package/dist/holmes/review/evaluation-metrics.js +175 -0
  72. package/dist/holmes/review/graph-verifier.d.ts +34 -0
  73. package/dist/holmes/review/graph-verifier.js +62 -0
  74. package/dist/holmes/review/hop-ablation.d.ts +100 -0
  75. package/dist/holmes/review/hop-ablation.js +89 -0
  76. package/dist/holmes/review/manual-baseline.d.ts +209 -0
  77. package/dist/holmes/review/manual-baseline.js +2846 -0
  78. package/dist/holmes/review/oracle-gap.d.ts +32 -0
  79. package/dist/holmes/review/oracle-gap.js +102 -0
  80. package/dist/holmes/review/point-in-time-replay.d.ts +41 -0
  81. package/dist/holmes/review/point-in-time-replay.js +161 -0
  82. package/dist/holmes/review/rank-diagnosis.d.ts +43 -0
  83. package/dist/holmes/review/rank-diagnosis.js +163 -0
  84. package/dist/holmes/review/replay-calibration.d.ts +62 -0
  85. package/dist/holmes/review/replay-calibration.js +83 -0
  86. package/dist/holmes/review/replay-corpus.d.ts +135 -0
  87. package/dist/holmes/review/replay-corpus.js +210 -0
  88. package/dist/holmes/review/run-replay.d.ts +260 -0
  89. package/dist/holmes/review/run-replay.js +729 -0
  90. package/dist/holmes/review/semantic-arm.d.ts +271 -0
  91. package/dist/holmes/review/semantic-arm.js +717 -0
  92. package/dist/holmes/review/semantic-retrieval.d.ts +55 -0
  93. package/dist/holmes/review/semantic-retrieval.js +156 -0
  94. package/dist/holmes/review/spec-layer-stats.d.ts +38 -0
  95. package/dist/holmes/review/spec-layer-stats.js +52 -0
  96. package/dist/holmes/review/temporal-prior.d.ts +33 -0
  97. package/dist/holmes/review/temporal-prior.js +53 -0
  98. package/dist/holmes/review/test-runner.d.ts +15 -0
  99. package/dist/holmes/review/test-runner.js +41 -4
  100. package/dist/holmes/review/test-selection-breadth.d.ts +75 -0
  101. package/dist/holmes/review/test-selection-breadth.js +57 -0
  102. package/dist/holmes/review/traceability-benchmark.d.ts +81 -0
  103. package/dist/holmes/review/traceability-benchmark.js +135 -0
  104. package/dist/holmes/review/union-verify.d.ts +12 -0
  105. package/dist/holmes/review/union-verify.js +70 -0
  106. package/dist/holmes/rtm/graph-store.d.ts +51 -0
  107. package/dist/holmes/rtm/graph-store.js +122 -0
  108. package/dist/holmes/rtm/incremental.d.ts +25 -1
  109. package/dist/holmes/rtm/incremental.js +18 -1
  110. package/dist/holmes/rtm/localize.d.ts +28 -0
  111. package/dist/holmes/rtm/localize.js +272 -14
  112. package/dist/holmes/rtm/rtm-builder.d.ts +118 -3
  113. package/dist/holmes/rtm/rtm-builder.js +265 -28
  114. package/dist/holmes/rtm/rtm-graph.d.ts +117 -2
  115. package/dist/holmes/rtm/rtm-graph.js +194 -31
  116. package/dist/holmes/rtm/taint-benchmark.d.ts +97 -0
  117. package/dist/holmes/rtm/taint-benchmark.js +141 -0
  118. package/dist/holmes/rtm/test-scope.js +24 -1
  119. package/dist/holmes/semantic/credentials.d.ts +15 -0
  120. package/dist/holmes/semantic/credentials.js +134 -0
  121. package/dist/holmes/semantic/embedder.d.ts +44 -0
  122. package/dist/holmes/semantic/embedder.js +185 -0
  123. package/dist/holmes/semantic/hit-rerank.d.ts +4 -0
  124. package/dist/holmes/semantic/hit-rerank.js +38 -0
  125. package/dist/holmes/semantic/tier.d.ts +37 -0
  126. package/dist/holmes/semantic/tier.js +54 -0
  127. package/dist/holmes/semantic/vector-cache.d.ts +11 -0
  128. package/dist/holmes/semantic/vector-cache.js +91 -0
  129. package/dist/holmes/spec/acceptance-quality.d.ts +81 -0
  130. package/dist/holmes/spec/acceptance-quality.js +169 -0
  131. package/dist/holmes/spec/validator.js +33 -1
  132. package/dist/holmes/spec/yaml-scalar.d.ts +1 -0
  133. package/dist/holmes/spec/yaml-scalar.js +43 -0
  134. package/package.json +1 -1
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.corroborated = corroborated;
4
+ exports.verifyByGraph = verifyByGraph;
5
+ /** Files reachable from `seed` by a call edge in either direction, or by a shared spec anchor. */
6
+ function corroborated(seed, scanned) {
7
+ const out = new Set([seed]);
8
+ const byPath = new Map(scanned.map((f) => [f.sourcePath, f]));
9
+ const seedFile = byPath.get(seed);
10
+ if (!seedFile)
11
+ return out;
12
+ const declaredIn = new Map();
13
+ for (const f of scanned) {
14
+ for (const sym of f.symbols) {
15
+ const bare = sym.qualifiedName.includes('.')
16
+ ? sym.qualifiedName.slice(sym.qualifiedName.lastIndexOf('.') + 1) : sym.qualifiedName;
17
+ for (const key of [sym.qualifiedName, bare])
18
+ if (!declaredIn.has(key))
19
+ declaredIn.set(key, f.sourcePath);
20
+ }
21
+ }
22
+ // Outgoing calls from the seed.
23
+ for (const e of seedFile.edges ?? []) {
24
+ if (e.rel !== 'calls')
25
+ continue;
26
+ const target = declaredIn.get(e.to);
27
+ if (target)
28
+ out.add(target);
29
+ }
30
+ // Incoming calls to the seed's symbols, and files sharing one of its spec anchors.
31
+ const seedSymbols = new Set(seedFile.symbols.flatMap((s) => {
32
+ const bare = s.qualifiedName.includes('.') ? s.qualifiedName.slice(s.qualifiedName.lastIndexOf('.') + 1) : s.qualifiedName;
33
+ return [s.qualifiedName, bare];
34
+ }));
35
+ const seedAnchors = new Set(seedFile.implementsSpecs ?? []);
36
+ for (const f of scanned) {
37
+ if (f.sourcePath === seed)
38
+ continue;
39
+ if ((f.edges ?? []).some((e) => e.rel === 'calls' && seedSymbols.has(e.to))) {
40
+ out.add(f.sourcePath);
41
+ continue;
42
+ }
43
+ if (seedAnchors.size > 0 && (f.implementsSpecs ?? []).some((id) => seedAnchors.has(id)))
44
+ out.add(f.sourcePath);
45
+ }
46
+ return out;
47
+ }
48
+ /**
49
+ * Demote candidates the graph cannot connect to the top-ranked one.
50
+ *
51
+ * The top candidate is taken as the anchor of the answer, which is the assumption that makes this
52
+ * cheap and also the one that can sink it: if the first hit is wrong, corroboration is measured
53
+ * against the wrong place. That is why the demotion is partial and the list is never truncated.
54
+ */
55
+ function verifyByGraph(ranked, scanned, opts) {
56
+ if (ranked.length === 0 || opts.demotion >= 1)
57
+ return [...ranked];
58
+ const ok = corroborated(ranked[0].file, scanned);
59
+ return ranked
60
+ .map((h) => ({ file: h.file, score: ok.has(h.file) ? h.score : h.score * opts.demotion }))
61
+ .sort((a, b) => b.score - a.score || a.file.localeCompare(b.file));
62
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * What the knowledge-graph hop actually contributes, isolated.
3
+ *
4
+ * A-SPEC-407 put a one-hop proximity list into the rank fusion and was adopted because Top-5 recall
5
+ * rose on the clean slice of both corpora. Two things were wrong with that as evidence. It was
6
+ * measured on a hand-built arm rather than through `analyzeMaintenance`, and it was never checked
7
+ * against a different set of cases — the exact mistake this repository's own record warns about,
8
+ * twice.
9
+ *
10
+ * So this is the ablation done properly: the SHIPPED path, `HOP_SEEDS` at 5 and at 0, over three
11
+ * slices. The result is that the contribution flips sign. It is worth +0.061 and +0.040 of Top-5
12
+ * recall on the two slices the adoption was based on, and −0.047 on a third that nobody had
13
+ * measured. Weighted over all 92 cases it is +0.017 — positive, and small enough that "the graph
14
+ * improves the ranking" is only conditionally true.
15
+ *
16
+ * The hop is kept: net positive, and two of three slices favour it. What is NOT kept is the
17
+ * impression that it reliably helps. A later change to this axis has to be measured on all three.
18
+ */
19
+ export interface HopArm {
20
+ top1: number;
21
+ top5Recall: number;
22
+ top10Recall: number;
23
+ top10Precision: number;
24
+ /** Truth files inside the forty exposed to the caller — the admission axis, as opposed to order. */
25
+ truthInsideExposed: number;
26
+ }
27
+ export interface HopSlice {
28
+ slice: string;
29
+ cases: number;
30
+ withHop: HopArm;
31
+ withoutHop: HopArm;
32
+ }
33
+ /** Measured 2026-08-29 through analyzeMaintenance, HOP_SEEDS 5 against 0. */
34
+ export declare const HOP_ABLATION: readonly HopSlice[];
35
+ /** Top-5 recall the hop is worth on one slice. Negative means the hop cost recall there. */
36
+ export declare function hopContribution(slice: HopSlice): number;
37
+ /** Case-weighted mean contribution — the single number, with its smallness visible. */
38
+ export declare function weightedHopContribution(slices: readonly HopSlice[]): number;
39
+ /**
40
+ * Why the jarvis `union recall` floor fell from 0.8813 to 0.8508 when the hop went in.
41
+ *
42
+ * It was recorded as an open question, with a hypothesis: the hop pushes lexically-matched files out
43
+ * of the top ten, which shrinks the symbols the call closure is seeded from, so `impactedFiles`
44
+ * loses answers. Measured on the pinned case set with HOP_SEEDS at 5 and 0, that hypothesis is
45
+ * WRONG — not one answer left through the closure.
46
+ *
47
+ * What actually happened: the hop dropped two answers that had been in `ranked` and added three.
48
+ * File-for-file that is a net gain. The macro metric fell because both losses landed on cases whose
49
+ * change touched exactly ONE file, and a singleton case forfeits a whole 1.0 of per-case recall,
50
+ * while three gains spread across many-file cases earn only fractions.
51
+ *
52
+ * The lesson is about the metric, not the ranker: on a corpus where a change touches anywhere from
53
+ * one file to sixty-four, a per-case mean and a per-file count can move in opposite directions, and
54
+ * neither is the whole answer.
55
+ */
56
+ export interface UnionDrop {
57
+ slice: string;
58
+ lostFiles: number;
59
+ lostFromRanked: number;
60
+ lostFromImpacted: number;
61
+ lostOnSingletonCases: number;
62
+ gainedFiles: number;
63
+ macroUnionRecallWithHop: number;
64
+ macroUnionRecallWithoutHop: number;
65
+ /** The other axis, which fell for the reason A-SPEC-378 already named. */
66
+ impactRecallWithHop: number;
67
+ impactRecallWithoutHop: number;
68
+ }
69
+ /** Measured 2026-08-29 on `casesFor(JARVIS_CORPUS, 30)` — the set the floors are pinned on. */
70
+ export declare const UNION_DROP: UnionDrop;
71
+ /** Net answers the hop put into the union, counted as files rather than averaged over cases. */
72
+ export declare function unionFileDelta(d: UnionDrop): number;
73
+ /**
74
+ * What the rank fusion (A-SPEC-405) is worth, measured the same way the hop was.
75
+ *
76
+ * A-SPEC-405 was approved under a criterion of its own writing — "no existing figure falls on either
77
+ * clean slice" — and then adopted with jarvis Top-10 precision down from 0.145 to 0.135. I recorded
78
+ * the violation and left the revert decision open. This is the number that decision needs, taken
79
+ * through the product path rather than the arm the original used.
80
+ *
81
+ * The violation is REAL and is confirmed here, and a second cost the original never measured shows
82
+ * up with it: on jarvis the fusion also leaves two fewer answers inside the forty the caller sees.
83
+ *
84
+ * It is kept anyway, and the trade is stated rather than smoothed: +0.172 and +0.069 of Top-5 recall
85
+ * against −0.012 of precision and two answers. Nobody should read this as the criterion being met.
86
+ */
87
+ export interface FusionSlice {
88
+ slice: string;
89
+ cases: number;
90
+ withFusion: HopArm;
91
+ withoutFusion: HopArm;
92
+ }
93
+ /** Measured 2026-08-29 through analyzeMaintenance, `fuseTail()` on against off. */
94
+ export declare const FUSION_ABLATION: readonly FusionSlice[];
95
+ /** Where the fusion COSTS something, named rather than averaged away. */
96
+ export declare function fusionCosts(slices: readonly FusionSlice[]): Array<{
97
+ slice: string;
98
+ metric: string;
99
+ delta: number;
100
+ }>;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FUSION_ABLATION = exports.UNION_DROP = exports.HOP_ABLATION = void 0;
4
+ exports.hopContribution = hopContribution;
5
+ exports.weightedHopContribution = weightedHopContribution;
6
+ exports.unionFileDelta = unionFileDelta;
7
+ exports.fusionCosts = fusionCosts;
8
+ /** Measured 2026-08-29 through analyzeMaintenance, HOP_SEEDS 5 against 0. */
9
+ exports.HOP_ABLATION = [
10
+ {
11
+ slice: 'holmes 0-29',
12
+ cases: 30,
13
+ withHop: { top1: 0.300, top5Recall: 0.554, top10Recall: 0.634, top10Precision: 0.133, truthInsideExposed: 47 },
14
+ withoutHop: { top1: 0.300, top5Recall: 0.493, top10Recall: 0.633, top10Precision: 0.127, truthInsideExposed: 45 },
15
+ },
16
+ {
17
+ slice: 'jarvis 0-29',
18
+ cases: 30,
19
+ withHop: { top1: 0.533, top5Recall: 0.528, top10Recall: 0.612, top10Precision: 0.133, truthInsideExposed: 52 },
20
+ withoutHop: { top1: 0.533, top5Recall: 0.488, top10Recall: 0.612, top10Precision: 0.135, truthInsideExposed: 53 },
21
+ },
22
+ {
23
+ // The slice the adoption never saw. It is the one that disagrees.
24
+ slice: 'jarvis 32-63',
25
+ cases: 32,
26
+ withHop: { top1: 0.469, top5Recall: 0.463, top10Recall: 0.611, top10Precision: 0.204, truthInsideExposed: 87 },
27
+ withoutHop: { top1: 0.469, top5Recall: 0.510, top10Recall: 0.627, top10Precision: 0.204, truthInsideExposed: 88 },
28
+ },
29
+ ];
30
+ /** Top-5 recall the hop is worth on one slice. Negative means the hop cost recall there. */
31
+ function hopContribution(slice) {
32
+ return slice.withHop.top5Recall - slice.withoutHop.top5Recall;
33
+ }
34
+ /** Case-weighted mean contribution — the single number, with its smallness visible. */
35
+ function weightedHopContribution(slices) {
36
+ const cases = slices.reduce((a, s) => a + s.cases, 0);
37
+ if (cases === 0)
38
+ return 0;
39
+ return slices.reduce((a, s) => a + hopContribution(s) * s.cases, 0) / cases;
40
+ }
41
+ /** Measured 2026-08-29 on `casesFor(JARVIS_CORPUS, 30)` — the set the floors are pinned on. */
42
+ exports.UNION_DROP = {
43
+ slice: 'jarvis newest-30',
44
+ lostFiles: 2,
45
+ lostFromRanked: 2,
46
+ lostFromImpacted: 0,
47
+ lostOnSingletonCases: 2,
48
+ gainedFiles: 3,
49
+ macroUnionRecallWithHop: 0.8507936507936509,
50
+ macroUnionRecallWithoutHop: 0.8813492063492064,
51
+ impactRecallWithHop: 0.0980392156862745,
52
+ impactRecallWithoutHop: 0.20588235294117646,
53
+ };
54
+ /** Net answers the hop put into the union, counted as files rather than averaged over cases. */
55
+ function unionFileDelta(d) {
56
+ return d.gainedFiles - d.lostFiles;
57
+ }
58
+ /** Measured 2026-08-29 through analyzeMaintenance, `fuseTail()` on against off. */
59
+ exports.FUSION_ABLATION = [
60
+ {
61
+ slice: 'holmes clean-30',
62
+ cases: 30,
63
+ withFusion: { top1: 0.300, top5Recall: 0.554, top10Recall: 0.634, top10Precision: 0.133, truthInsideExposed: 47 },
64
+ withoutFusion: { top1: 0.300, top5Recall: 0.382, top10Recall: 0.604, top10Precision: 0.117, truthInsideExposed: 44 },
65
+ },
66
+ {
67
+ slice: 'jarvis clean-30',
68
+ cases: 30,
69
+ withFusion: { top1: 0.533, top5Recall: 0.528, top10Recall: 0.612, top10Precision: 0.133, truthInsideExposed: 52 },
70
+ withoutFusion: { top1: 0.533, top5Recall: 0.459, top10Recall: 0.612, top10Precision: 0.145, truthInsideExposed: 54 },
71
+ },
72
+ ];
73
+ /** Where the fusion COSTS something, named rather than averaged away. */
74
+ function fusionCosts(slices) {
75
+ const out = [];
76
+ for (const s of slices) {
77
+ const pairs = [
78
+ ['top1', s.withFusion.top1, s.withoutFusion.top1],
79
+ ['top5Recall', s.withFusion.top5Recall, s.withoutFusion.top5Recall],
80
+ ['top10Recall', s.withFusion.top10Recall, s.withoutFusion.top10Recall],
81
+ ['top10Precision', s.withFusion.top10Precision, s.withoutFusion.top10Precision],
82
+ ['truthInsideExposed', s.withFusion.truthInsideExposed, s.withoutFusion.truthInsideExposed],
83
+ ];
84
+ for (const [metric, a, b] of pairs)
85
+ if (a < b)
86
+ out.push({ slice: s.slice, metric, delta: a - b });
87
+ }
88
+ return out;
89
+ }
@@ -0,0 +1,209 @@
1
+ /**
2
+ * The "strong model with plain file search" baseline (Goal Phase B).
3
+ *
4
+ * Every other arm in this directory compares the harness against ANOTHER PROGRAM. That answers "is
5
+ * the spec layer worth its cost", never the Goal's actual claim: that the harness helps an agent
6
+ * beat what a competent developer reaches by reading and grepping. So this arm is me — given only
7
+ * the request text and a materialized tree, ranking by grep counts over the query terms, with the
8
+ * truth WITHHELD until the picks were written to disk.
9
+ *
10
+ * The rule this slice was approved under: if the baseline beats the shipped harness, the claim of
11
+ * surpassing a human does not hold, and that fact gets recorded rather than buried. It did beat it
12
+ * on recall. The record below is that measurement, kept so a later change has to move it.
13
+ *
14
+ * The `shipped` lists below are the pipeline AS IT WAS when this baseline ran (S-404). A-SPEC-405
15
+ * later changed the re-ranking in response to what this record showed; re-scoring the same eight
16
+ * cases through the new pipeline gives Top-5 recall 0.175 -> 0.217 and Top-10 recall 0.471 -> 0.533,
17
+ * still short of the 0.404 / 0.554 reached here by reading and grepping. The record is deliberately
18
+ * NOT refreshed: it is the measurement that motivated the change, and rewriting it would erase the
19
+ * comparison it exists to preserve.
20
+ *
21
+ * n=8. A sample this small cannot separate a real gap from noise; report the n alongside the
22
+ * numbers and do not treat a large difference here as a conclusion.
23
+ */
24
+ import { type ReplayOutcome } from './evaluation-metrics';
25
+ export interface ManualBaselineCase {
26
+ /** Commit subject with spec citations stripped — the only input the picks were made from. */
27
+ query: string;
28
+ /** Files chosen by grep-ranked reading, frozen before the truth was read. */
29
+ picks: string[];
30
+ /** What the shipped pipeline returned for the same query on the same tree. */
31
+ shipped: string[];
32
+ truthFiles: string[];
33
+ }
34
+ export interface ManualBaselineRecord {
35
+ corpus: string;
36
+ measuredOn: string;
37
+ caveat: string;
38
+ cases: ManualBaselineCase[];
39
+ }
40
+ export declare const MANUAL_BASELINE_RECORD: ManualBaselineRecord;
41
+ export declare const MANUAL_BASELINE_N: number;
42
+ /** Score the frozen picks with the same metric code every other arm uses. */
43
+ export declare function manualBaselineOutcomes(record: ManualBaselineRecord): ReplayOutcome[];
44
+ export declare function shippedOutcomes(record: ManualBaselineRecord): ReplayOutcome[];
45
+ /**
46
+ * Per-metric verdict, computed from the record rather than asserted by hand: `true` only where the
47
+ * shipped harness is strictly ahead of the grep-and-read baseline.
48
+ */
49
+ export declare function shippedBeatsManual(record: ManualBaselineRecord): {
50
+ top1: boolean;
51
+ top5Recall: boolean;
52
+ top10Recall: boolean;
53
+ top10Precision: boolean;
54
+ };
55
+ /**
56
+ * @implements A-SPEC-408
57
+ * The second blind set. Set 1 turned out to be markedly harder than the corpus average — Top-10
58
+ * recall 0.404 against 0.688 for the other 22 cases of the same 30 — so its verdict could have been
59
+ * a property of those eight commits rather than of the harness. These eight were drawn the same way
60
+ * from further down the same slice, their truth was unread until the picks were on disk, and the
61
+ * asymmetry came out in the same direction: the harness leads Top-1, reading and grepping leads
62
+ * recall.
63
+ *
64
+ * One difference from set 1 that matters when reading the two together: these `shipped` lists come
65
+ * from the pipeline AFTER A-SPEC-405/406/407, while set 1's are the S-404-era pipeline, kept
66
+ * deliberately unrefreshed. So compare each set's two rows to each other, never set 1's harness row
67
+ * to set 2's.
68
+ */
69
+ export declare const MANUAL_BASELINE_SET_2: ManualBaselineRecord;
70
+ /**
71
+ * @implements A-SPEC-410
72
+ * The comparison once BOTH sides get the same thing.
73
+ *
74
+ * A-SPEC-404 and 408 compared the harness ALONE against a person reading and grepping, so the
75
+ * presence of a judgement step was tangled up with the tools. Two asymmetries were then removed,
76
+ * one per round, each on a set whose truth was unread until the picks were frozen:
77
+ *
78
+ * set "evidence-shown" (cases 32-39): the harness arm had been given file PATHS only while the
79
+ * grep arm saw occurrence counts. Fixed by actually showing what A-SPEC-409 carries — lexical
80
+ * score, matched symbols, via-specs, commit prose, graph-hop count, recency.
81
+ * set "two-step" (cases 40-47): the grep arm judged TWICE (choosing the search terms, then the
82
+ * files) while the harness arm judged once. Fixed by letting the harness be re-queried after
83
+ * seeing its first result, which is what an agent actually does.
84
+ *
85
+ * Both sets: width 20 on each side, picks frozen before the truth was read.
86
+ *
87
+ * Pooled result (n=16). The verdict differs BY AXIS and is stated that way:
88
+ * Top-1 harness+judgement 0.688 > grep+judgement 0.625
89
+ * Top-5 recall harness+judgement 0.712 > grep+judgement 0.677
90
+ * Top-10 recall harness+judgement 0.764 < grep+judgement 0.821
91
+ * harness ALONE 0.438 / 0.505 / 0.651 — behind the human method on every recall axis.
92
+ *
93
+ * So the harness plus a judging caller leads at the top of the list and still trails on deep
94
+ * recall, which is the axis the Goal is worded around. "Surpasses a human developer" does not hold.
95
+ */
96
+ export interface FairCase {
97
+ set: string;
98
+ query: string;
99
+ /** The harness's own top ten, no judgement applied. */
100
+ harnessAlone: string[];
101
+ /** Ten chosen from the harness's evidence-annotated pool. */
102
+ harnessJudged: string[];
103
+ /** Ten chosen from grep output, the same judgement, the other tool. */
104
+ grepJudged: string[];
105
+ truthFiles: string[];
106
+ }
107
+ export declare const FAIR_COMPARISON: {
108
+ measuredOn: string;
109
+ caveat: string;
110
+ cases: FairCase[];
111
+ };
112
+ /**
113
+ * The third corpus, measured against a person instead of a program.
114
+ *
115
+ * A-SPEC-426 put the harness against a name-only matcher on this repository -- no spec store, a
116
+ * third party's code -- and it won all four axes. That opponent was a program, and the note there
117
+ * said the human comparison was still owed. This is it, under the A-SPEC-410 protocol: width 40 on
118
+ * both sides, the harness's evidence actually shown, picks frozen before the truth was read, eight
119
+ * cases whose answers I had never seen.
120
+ *
121
+ * grep and judgement win every axis, and not narrowly.
122
+ *
123
+ * The cause is not the reranking. Of nineteen truth files, NINE were anywhere in the forty the
124
+ * harness exposed, and two cases offered none at all. The clearest example: for "properly format
125
+ * markdown in the AskUser tool", the answer is packages/core/src/tools/ask-user.ts; one grep found
126
+ * it immediately and it appears nowhere in the harness's forty. Reranking cannot recover a file
127
+ * that was never offered.
128
+ *
129
+ * Read together with A-SPEC-426 rather than against it. Both are true, and the pair is the finding:
130
+ * beating the automated baseline does not imply beating a person.
131
+ */
132
+ export interface ThirdCorpusCase extends FairCase {
133
+ pool40: string[];
134
+ }
135
+ export declare const THIRD_CORPUS_COMPARISON: {
136
+ corpus: string;
137
+ opponent: string;
138
+ note: string;
139
+ caveat: string;
140
+ harnessAloneTop10RecallHere: number;
141
+ harnessAloneTop10RecallOn29: number;
142
+ cases: ThirdCorpusCase[];
143
+ };
144
+ /** How much of the truth the exposed pool held at all -- the ceiling any reranking could reach. */
145
+ export declare function poolCoverage(r: {
146
+ cases: ThirdCorpusCase[];
147
+ }): {
148
+ truthTotal: number;
149
+ inPool: number;
150
+ casesWithNothing: number;
151
+ };
152
+ /**
153
+ * What A-SPEC-428's window guarantee bought, measured two ways.
154
+ *
155
+ * Controlled first: the SAME eight cases A-SPEC-427 lost on, re-run after the change. Nine of
156
+ * nineteen answers were inside the exposed forty before; fourteen after. Two cases offered nothing
157
+ * at all; one after. Answers the lexical layer ranked in its top ten but the pipeline never showed:
158
+ * five before, none after. Same commits, same queries — the increase belongs to the change.
159
+ *
160
+ * Then a fresh blind set of eight, truth unread until the picks were frozen. The gap to a person
161
+ * with grep is much smaller than it was, and precision now favours the harness alone. It is still a
162
+ * gap: grep and judgement lead both recall axes.
163
+ *
164
+ * One result here goes the other way and is kept because it does: on this set MY reranking was
165
+ * WORSE than the harness's own ordering (Top-10 recall 0.446 against 0.527). The judgement step is
166
+ * not a free improvement, and the earlier sets where it helped did not establish that it always
167
+ * would.
168
+ */
169
+ export declare const WINDOW_GUARANTEE_GAIN: {
170
+ readonly sameCases: {
171
+ readonly corpus: "reference-gemini-cli, the eight cases of A-SPEC-427";
172
+ readonly truthTotal: 19;
173
+ readonly inPoolBefore: 9;
174
+ readonly inPoolAfter: 14;
175
+ readonly casesWithNothingBefore: 2;
176
+ readonly casesWithNothingAfter: 1;
177
+ readonly lexTop10BuriedBefore: 5;
178
+ readonly lexTop10BuriedAfter: 0;
179
+ };
180
+ readonly freshSet: {
181
+ readonly corpus: "reference-gemini-cli, eight cases whose truth was unread until the picks were frozen";
182
+ readonly cases: 8;
183
+ readonly harnessAlone: {
184
+ readonly top1: 0.25;
185
+ readonly top5Recall: 0.286;
186
+ readonly top10Recall: 0.527;
187
+ readonly top10Precision: 0.125;
188
+ };
189
+ readonly grepJudged: {
190
+ readonly top1: 0.25;
191
+ readonly top5Recall: 0.39;
192
+ readonly top10Recall: 0.595;
193
+ readonly top10Precision: 0.112;
194
+ };
195
+ readonly harnessJudged: {
196
+ readonly top1: 0.125;
197
+ readonly top5Recall: 0.348;
198
+ readonly top10Recall: 0.446;
199
+ readonly top10Precision: 0.1;
200
+ };
201
+ readonly poolCoverage: {
202
+ readonly inPool: 13;
203
+ readonly truthTotal: 18;
204
+ readonly casesWithNothing: 1;
205
+ };
206
+ };
207
+ readonly earlierSetHarnessAloneTop10Recall: 0.268;
208
+ readonly caveat: "두 세트는 커밋이 다르므로 직접 비교하지 않는다 — 새 세트가 더 쉽다(하네스 단독 0.527 대 0.268). not comparable.";
209
+ };