@holmes-lab/holmes-kit 0.2.1 → 0.3.1
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.
- package/CHANGELOG.md +55 -0
- package/dist/.build-id +1 -1
- package/dist/holmes/assoc/assoc-arm.d.ts +94 -0
- package/dist/holmes/assoc/assoc-arm.js +187 -0
- package/dist/holmes/assoc/explore.d.ts +21 -0
- package/dist/holmes/assoc/explore.js +160 -0
- package/dist/holmes/assoc/impact-baseline.d.ts +13 -0
- package/dist/holmes/assoc/impact-baseline.js +48 -0
- package/dist/holmes/assoc/ppr.d.ts +64 -0
- package/dist/holmes/assoc/ppr.js +110 -0
- package/dist/holmes/cli/agents.d.ts +24 -11
- package/dist/holmes/cli/agents.js +93 -21
- package/dist/holmes/cli/doctor.d.ts +56 -0
- package/dist/holmes/cli/doctor.js +311 -5
- package/dist/holmes/cli/index.js +13 -1
- package/dist/holmes/cli/semantic-key.d.ts +19 -0
- package/dist/holmes/cli/semantic-key.js +93 -0
- package/dist/holmes/config/config.d.ts +9 -0
- package/dist/holmes/config/config.js +8 -1
- package/dist/holmes/cpg/language-capability.d.ts +65 -0
- package/dist/holmes/cpg/language-capability.js +145 -0
- package/dist/holmes/cpg/language-parser-walk.js +179 -34
- package/dist/holmes/cpg/language-parser.d.ts +1 -1
- package/dist/holmes/governance/ledger-rechain.d.ts +12 -0
- package/dist/holmes/governance/ledger-rechain.js +17 -2
- package/dist/holmes/governance/provenance-ledger.js +21 -0
- package/dist/holmes/guardrail/blind-spots.js +12 -1
- package/dist/holmes/guardrail/impact-gate.d.ts +77 -0
- package/dist/holmes/guardrail/impact-gate.js +263 -0
- package/dist/holmes/guardrail/write-target.d.ts +38 -1
- package/dist/holmes/guardrail/write-target.js +41 -4
- package/dist/holmes/hooks/adapters/antigravity.js +12 -1
- package/dist/holmes/hooks/corrupt-state-run.d.ts +33 -0
- package/dist/holmes/hooks/corrupt-state-run.js +16 -0
- package/dist/holmes/hooks/pre-tool-use.d.ts +26 -0
- package/dist/holmes/hooks/pre-tool-use.js +203 -11
- package/dist/holmes/hooks/stop.d.ts +65 -0
- package/dist/holmes/hooks/stop.js +200 -2
- package/dist/holmes/mcp/elicit-approval.d.ts +32 -2
- package/dist/holmes/mcp/elicit-approval.js +30 -2
- package/dist/holmes/mcp/handlers.d.ts +88 -0
- package/dist/holmes/mcp/handlers.js +636 -35
- package/dist/holmes/mcp/maintenance-analyze.d.ts +435 -0
- package/dist/holmes/mcp/maintenance-analyze.js +994 -0
- package/dist/holmes/mcp/maintenance-evidence.d.ts +140 -0
- package/dist/holmes/mcp/maintenance-evidence.js +253 -0
- package/dist/holmes/mcp/server.js +13 -10
- package/dist/holmes/mcp/tool-schemas.js +71 -0
- package/dist/holmes/project/root.js +3 -1
- package/dist/holmes/review/baseline-arm.d.ts +37 -0
- package/dist/holmes/review/baseline-arm.js +51 -0
- package/dist/holmes/review/captured-stdin-guard.d.ts +8 -0
- package/dist/holmes/review/captured-stdin-guard.js +48 -0
- package/dist/holmes/review/coherence-verify.d.ts +31 -0
- package/dist/holmes/review/coherence-verify.js +144 -0
- package/dist/holmes/review/commit-text.d.ts +50 -0
- package/dist/holmes/review/commit-text.js +76 -0
- package/dist/holmes/review/confidence-calibration.d.ts +39 -0
- package/dist/holmes/review/confidence-calibration.js +39 -0
- package/dist/holmes/review/content-baseline.d.ts +38 -0
- package/dist/holmes/review/content-baseline.js +103 -0
- package/dist/holmes/review/content-verify.d.ts +20 -0
- package/dist/holmes/review/content-verify.js +73 -0
- package/dist/holmes/review/dense-retrieval.d.ts +66 -0
- package/dist/holmes/review/dense-retrieval.js +97 -0
- package/dist/holmes/review/edge-quality.d.ts +44 -0
- package/dist/holmes/review/edge-quality.js +117 -0
- package/dist/holmes/review/evaluation-metrics.d.ts +138 -0
- package/dist/holmes/review/evaluation-metrics.js +175 -0
- package/dist/holmes/review/graph-verifier.d.ts +34 -0
- package/dist/holmes/review/graph-verifier.js +62 -0
- package/dist/holmes/review/hop-ablation.d.ts +100 -0
- package/dist/holmes/review/hop-ablation.js +89 -0
- package/dist/holmes/review/manual-baseline.d.ts +209 -0
- package/dist/holmes/review/manual-baseline.js +2846 -0
- package/dist/holmes/review/oracle-gap.d.ts +32 -0
- package/dist/holmes/review/oracle-gap.js +102 -0
- package/dist/holmes/review/point-in-time-replay.d.ts +41 -0
- package/dist/holmes/review/point-in-time-replay.js +161 -0
- package/dist/holmes/review/rank-diagnosis.d.ts +43 -0
- package/dist/holmes/review/rank-diagnosis.js +163 -0
- package/dist/holmes/review/replay-calibration.d.ts +62 -0
- package/dist/holmes/review/replay-calibration.js +83 -0
- package/dist/holmes/review/replay-corpus.d.ts +135 -0
- package/dist/holmes/review/replay-corpus.js +210 -0
- package/dist/holmes/review/run-replay.d.ts +260 -0
- package/dist/holmes/review/run-replay.js +729 -0
- package/dist/holmes/review/semantic-arm.d.ts +271 -0
- package/dist/holmes/review/semantic-arm.js +717 -0
- package/dist/holmes/review/semantic-retrieval.d.ts +55 -0
- package/dist/holmes/review/semantic-retrieval.js +156 -0
- package/dist/holmes/review/spec-layer-stats.d.ts +38 -0
- package/dist/holmes/review/spec-layer-stats.js +52 -0
- package/dist/holmes/review/temporal-prior.d.ts +33 -0
- package/dist/holmes/review/temporal-prior.js +53 -0
- package/dist/holmes/review/test-runner.d.ts +15 -0
- package/dist/holmes/review/test-runner.js +41 -4
- package/dist/holmes/review/test-selection-breadth.d.ts +75 -0
- package/dist/holmes/review/test-selection-breadth.js +57 -0
- package/dist/holmes/review/traceability-benchmark.d.ts +81 -0
- package/dist/holmes/review/traceability-benchmark.js +135 -0
- package/dist/holmes/review/union-verify.d.ts +12 -0
- package/dist/holmes/review/union-verify.js +70 -0
- package/dist/holmes/rtm/graph-store.d.ts +51 -0
- package/dist/holmes/rtm/graph-store.js +122 -0
- package/dist/holmes/rtm/incremental.d.ts +25 -1
- package/dist/holmes/rtm/incremental.js +18 -1
- package/dist/holmes/rtm/localize.d.ts +28 -0
- package/dist/holmes/rtm/localize.js +272 -14
- package/dist/holmes/rtm/rtm-builder.d.ts +118 -3
- package/dist/holmes/rtm/rtm-builder.js +265 -28
- package/dist/holmes/rtm/rtm-graph.d.ts +117 -2
- package/dist/holmes/rtm/rtm-graph.js +194 -31
- package/dist/holmes/rtm/taint-benchmark.d.ts +97 -0
- package/dist/holmes/rtm/taint-benchmark.js +141 -0
- package/dist/holmes/rtm/test-scope.js +24 -1
- package/dist/holmes/semantic/credentials.d.ts +15 -0
- package/dist/holmes/semantic/credentials.js +134 -0
- package/dist/holmes/semantic/embedder.d.ts +44 -0
- package/dist/holmes/semantic/embedder.js +185 -0
- package/dist/holmes/semantic/hit-rerank.d.ts +4 -0
- package/dist/holmes/semantic/hit-rerank.js +38 -0
- package/dist/holmes/semantic/tier.d.ts +37 -0
- package/dist/holmes/semantic/tier.js +54 -0
- package/dist/holmes/semantic/vector-cache.d.ts +11 -0
- package/dist/holmes/semantic/vector-cache.js +91 -0
- package/dist/holmes/spec/acceptance-quality.d.ts +81 -0
- package/dist/holmes/spec/acceptance-quality.js +169 -0
- package/dist/holmes/spec/validator.js +33 -1
- package/dist/holmes/spec/yaml-scalar.d.ts +1 -0
- package/dist/holmes/spec/yaml-scalar.js +43 -0
- 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
|
+
};
|