@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.
- package/CHANGELOG.md +35 -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 -17
- package/dist/holmes/cli/codex-toml.d.ts +26 -0
- package/dist/holmes/cli/codex-toml.js +282 -0
- package/dist/holmes/cli/doctor.d.ts +56 -0
- package/dist/holmes/cli/doctor.js +348 -18
- package/dist/holmes/cli/index.js +16 -2
- package/dist/holmes/cli/init.js +78 -0
- package/dist/holmes/cli/interactive-prompt.js +4 -4
- package/dist/holmes/cli/mcp-launcher.d.ts +2 -2
- 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 +48 -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/handlers.d.ts +88 -0
- package/dist/holmes/mcp/handlers.js +597 -16
- 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/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,435 @@
|
|
|
1
|
+
import type { ScannedFile } from '../cpg/cpg-scanner';
|
|
2
|
+
import type { Spec } from '../spec/spec-parser';
|
|
3
|
+
import type { RtmGraph } from '../rtm/rtm-graph';
|
|
4
|
+
import type { ContextBundle } from '../context/bundler';
|
|
5
|
+
import { type TestScope } from '../rtm/test-scope';
|
|
6
|
+
import type { ResolutionReport } from '../rtm/rtm-builder';
|
|
7
|
+
import { type TestEvidence } from '../review/test-evidence';
|
|
8
|
+
import { type LanguageGap } from '../cpg/language-capability';
|
|
9
|
+
export declare const MAINTENANCE_ANALYSIS_SCHEMA = "maintenance-analysis/1.13";
|
|
10
|
+
export declare const MAINTENANCE_ANALYSIS_EXTRACTOR = "holmes-graph-fusion/1.13";
|
|
11
|
+
/**
|
|
12
|
+
* The evidence kinds an ablation arm can actually produce. Completeness is measured by asking each
|
|
13
|
+
* arm which of these it FILLED — the first cut hardcoded 1 / 2 / 2+n, so "enhanced beats current"
|
|
14
|
+
* was an identity of the code rather than a result of a measurement.
|
|
15
|
+
*/
|
|
16
|
+
export declare const EVIDENCE_DIMENSIONS: readonly ["candidates", "specs", "directImpact", "transitiveImpact", "tests", "history", "versionedBasis"];
|
|
17
|
+
export type EvidenceDimension = typeof EVIDENCE_DIMENSIONS[number];
|
|
18
|
+
/**
|
|
19
|
+
* Externally supplied truth for a replayed change — normally the files, symbols, and test files a
|
|
20
|
+
* historical commit actually touched. The arms cannot see it; it only scores them.
|
|
21
|
+
*/
|
|
22
|
+
export interface MaintenanceGroundTruth {
|
|
23
|
+
files?: readonly string[];
|
|
24
|
+
symbols?: readonly string[];
|
|
25
|
+
tests?: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @implements A-SPEC-271
|
|
29
|
+
* How many ranked candidates form the `primary` tier. A fixed rank cut, not a score threshold:
|
|
30
|
+
* measured over the 12-commit replay corpus at matched set sizes, a `score >= ratio * top` cut was
|
|
31
|
+
* never better than the plain rank cut on both axes at once (top-3 beat ratio>=0.5 on recall AND
|
|
32
|
+
* precision), and tuning a threshold on 12 samples would be fitting noise.
|
|
33
|
+
*/
|
|
34
|
+
export declare const PRIMARY_TIER_WIDTH = 3;
|
|
35
|
+
export interface AblationArm {
|
|
36
|
+
localizationCandidates: number;
|
|
37
|
+
localizationRecall: number | null;
|
|
38
|
+
localizationPrecision: number | null;
|
|
39
|
+
impactRecall: number | null;
|
|
40
|
+
impactPrecision: number | null;
|
|
41
|
+
testRecall: number | null;
|
|
42
|
+
testPrecision: number | null;
|
|
43
|
+
evidenceDimensions: EvidenceDimension[];
|
|
44
|
+
evidenceCompleteness: number;
|
|
45
|
+
/**
|
|
46
|
+
* @implements A-SPEC-271
|
|
47
|
+
* Recall/precision of the `primary` tier alone. Reported only by the arm that HAS tiers; null
|
|
48
|
+
* everywhere ground truth is absent, exactly like the flat metrics.
|
|
49
|
+
*/
|
|
50
|
+
primaryRecall: number | null;
|
|
51
|
+
primaryPrecision: number | null;
|
|
52
|
+
/**
|
|
53
|
+
* @implements A-SPEC-274
|
|
54
|
+
* The closure-only tier, scored separately so the decision to drop it from the prediction stays
|
|
55
|
+
* open to review: if another corpus shows it carrying true positives, the number says so.
|
|
56
|
+
*/
|
|
57
|
+
impactedFilesRecall: number | null;
|
|
58
|
+
impactedFilesPrecision: number | null;
|
|
59
|
+
/** @implements A-SPEC-289 — the import tier, scored on its own so its worth stays visible. */
|
|
60
|
+
importedByRecall: number | null;
|
|
61
|
+
importedByPrecision: number | null;
|
|
62
|
+
}
|
|
63
|
+
export interface MaintenanceHistoryRecord {
|
|
64
|
+
commit: string;
|
|
65
|
+
date: string;
|
|
66
|
+
subject: string;
|
|
67
|
+
files: readonly string[];
|
|
68
|
+
}
|
|
69
|
+
export interface MaintenanceAnalysisInput {
|
|
70
|
+
/**
|
|
71
|
+
* @implements A-SPEC-478 — the INJECTED semantic runtime for the uncited head rerank. The
|
|
72
|
+
* handler resolves the tier, awaits the query embedding, and injects; the replay harness
|
|
73
|
+
* injects nothing, which is what keeps every pinned benchmark constant untouched. queryVector
|
|
74
|
+
* null (embedding failed) disables the rerank — the hot path never breaks on this layer.
|
|
75
|
+
*/
|
|
76
|
+
semantic?: {
|
|
77
|
+
label: string;
|
|
78
|
+
queryVector: number[] | null;
|
|
79
|
+
cachedDocVector: (text: string) => number[] | null;
|
|
80
|
+
};
|
|
81
|
+
request: string;
|
|
82
|
+
scanned: readonly ScannedFile[];
|
|
83
|
+
specs: readonly Spec[];
|
|
84
|
+
graph: RtmGraph;
|
|
85
|
+
testAnchors: Readonly<Record<string, readonly string[]>>;
|
|
86
|
+
history: readonly MaintenanceHistoryRecord[];
|
|
87
|
+
/**
|
|
88
|
+
* @implements A-SPEC-378
|
|
89
|
+
* Per-file multipliers from the temporal graph: how recently each file changed, as of the analysis
|
|
90
|
+
* basis. Absent files score 1 — unknown history is not evidence against a file.
|
|
91
|
+
*
|
|
92
|
+
* The first signal measured to beat plain word matching on requests that cite no spec, which is
|
|
93
|
+
* the condition every incoming bug report is in and where five spec-graph mechanisms had all come
|
|
94
|
+
* back negative. Measured at a 10-commit half-life over 60 point-in-time cases: uncited Top-10
|
|
95
|
+
* recall 0.349 -> 0.434 against a 0.357 baseline, cited 0.770 -> 0.843, second corpus 0.684 ->
|
|
96
|
+
* 0.724. Change FREQUENCY was measured alongside recency and made the uncited case worse, so it is
|
|
97
|
+
* deliberately not here.
|
|
98
|
+
*
|
|
99
|
+
* Supplied by the caller rather than read here, because this function performs no I/O — the same
|
|
100
|
+
* property that lets the replay benchmark run it against a materialised tree.
|
|
101
|
+
*/
|
|
102
|
+
changePrior?: Record<string, number>;
|
|
103
|
+
/**
|
|
104
|
+
* @implements A-SPEC-388
|
|
105
|
+
* Per-file multipliers from how much each file's COMMIT PROSE talks like the request, normalised
|
|
106
|
+
* to 0..1. Absent files are unaffected.
|
|
107
|
+
*
|
|
108
|
+
* The path is query -> Commit -> `MODIFIED_IN` -> File: the request's words are matched against
|
|
109
|
+
* what the graph records about a file rather than against the file's own identifiers, which is
|
|
110
|
+
* what lets it reach code named nothing like the report that describes it.
|
|
111
|
+
*
|
|
112
|
+
* Measured on history sampled away from the benchmarker's own recent commits (A-SPEC-387), on both
|
|
113
|
+
* corpora: Top-1 0.244 -> 0.333 and 0.400 -> 0.500, Top-5 recall 0.539 -> 0.624 and 0.376 -> 0.464,
|
|
114
|
+
* Top-10 recall 0.668 -> 0.674 and 0.393 -> 0.601. Precision falls, 0.156 -> 0.117 and 0.244 ->
|
|
115
|
+
* 0.145 — the answer is found more often and the list of ten is less pure, and neither half of
|
|
116
|
+
* that is the whole result.
|
|
117
|
+
*/
|
|
118
|
+
commitTextBoost?: Record<string, number>;
|
|
119
|
+
contextBundle?: ContextBundle | null;
|
|
120
|
+
/**
|
|
121
|
+
* @implements A-SPEC-290
|
|
122
|
+
* The stored test-run record, if any. Used ONLY when it was produced at this analysis's HEAD:
|
|
123
|
+
* a stale execution record read as current evidence is exactly the failure this codebase keeps
|
|
124
|
+
* closing.
|
|
125
|
+
*/
|
|
126
|
+
runtimeEvidence?: TestEvidence;
|
|
127
|
+
/**
|
|
128
|
+
* @implements A-SPEC-292
|
|
129
|
+
* What graph resolution discarded, produced by the resolution loop itself. Absent means nobody
|
|
130
|
+
* measured it — reported as null, never as zeros, because zeros would claim nothing was discarded.
|
|
131
|
+
*/
|
|
132
|
+
resolution?: ResolutionReport;
|
|
133
|
+
/** Optional replay ground truth. Absent means recall/precision are UNKNOWN, reported as null. */
|
|
134
|
+
groundTruth?: MaintenanceGroundTruth;
|
|
135
|
+
/**
|
|
136
|
+
* @implements A-SPEC-269
|
|
137
|
+
* Optional changed-file basis (e.g. a base..head diff). The architecture axis reads
|
|
138
|
+
* `.ax/decisions/*.md` and the C-SPEC folder — documents the CPG scanner never scans — so it
|
|
139
|
+
* cannot be derived from the localized code files. Without this the axis is reported as NOT
|
|
140
|
+
* assessed rather than as empty.
|
|
141
|
+
*/
|
|
142
|
+
changedFiles?: readonly string[];
|
|
143
|
+
basis: {
|
|
144
|
+
head: string;
|
|
145
|
+
loadedBuild: string;
|
|
146
|
+
diskBuild: string;
|
|
147
|
+
specFingerprint: string;
|
|
148
|
+
};
|
|
149
|
+
coverage: {
|
|
150
|
+
scannedFiles: number;
|
|
151
|
+
skippedFiles: readonly string[];
|
|
152
|
+
unsupportedLanguages: readonly string[];
|
|
153
|
+
historyStatus?: 'available' | 'unavailable' | 'not-requested';
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export interface MaintenanceAnalysis {
|
|
157
|
+
schemaVersion: string;
|
|
158
|
+
extractorVersion: string;
|
|
159
|
+
classification: 'existing-requirement-change' | 'new-requirement' | 'ambiguous';
|
|
160
|
+
/**
|
|
161
|
+
* @implements A-SPEC-436
|
|
162
|
+
* MEASURED ANTI-PREDICTIVE. On both corpora's 30-case clean slices this field correlates
|
|
163
|
+
* NEGATIVELY with how much of the change the analysis actually found — holmes r = −0.309, the
|
|
164
|
+
* second corpus r = −0.212 — and on both the highest band is the worst one. On the second corpus
|
|
165
|
+
* the two cases scored 0.85 returned nothing correct at all. Do not read it as a quality signal;
|
|
166
|
+
* a caller who weights answers by it weights the wrong ones up. The numbers and their sample
|
|
167
|
+
* sizes are pinned in review/confidence-calibration.ts, and the formula is unchanged because
|
|
168
|
+
* nobody has a hypothesis for why it inverts.
|
|
169
|
+
*/
|
|
170
|
+
confidence: number;
|
|
171
|
+
/**
|
|
172
|
+
* @implements A-SPEC-375
|
|
173
|
+
* A-SPECs worth citing, when the request cited nothing. Empty when it already did.
|
|
174
|
+
*
|
|
175
|
+
* Measured over 60 point-in-time cases: a request naming its spec reaches Top-10 recall 0.770
|
|
176
|
+
* against 0.305 for matching its words alone; a request naming nothing reaches 0.349 against
|
|
177
|
+
* 0.357 — level, with five graph mechanisms tried in that condition and every one negative. So
|
|
178
|
+
* this is not a nag, it is the single input that changes what the analysis can do.
|
|
179
|
+
*
|
|
180
|
+
* Few, deliberately: 26.6 A-SPECs match lexically per case on this repository, and offering all of
|
|
181
|
+
* them would be a haystack rather than a suggestion.
|
|
182
|
+
*/
|
|
183
|
+
citationCandidates: string[];
|
|
184
|
+
/**
|
|
185
|
+
* @implements A-SPEC-390
|
|
186
|
+
* What kind of call edges this graph actually holds.
|
|
187
|
+
*
|
|
188
|
+
* Measured on this repository: 7,917 `calls` edges, 1,078 binding to exactly one other file and
|
|
189
|
+
* 6,050 naming a target the scan does not contain — and those 6,050 are `join`, `push`, `map`,
|
|
190
|
+
* `filter`, `existsSync`. Builtins, correctly unresolvable, stored under the same relation as
|
|
191
|
+
* internal calls. Reporting "7,917 call edges" without this split describes a graph seven times
|
|
192
|
+
* denser than the one that exists.
|
|
193
|
+
*
|
|
194
|
+
* Reporting only: the impact walk already skips targets it cannot resolve, so no ranking, impact
|
|
195
|
+
* set or candidate order depends on these counts.
|
|
196
|
+
*/
|
|
197
|
+
callEdges: {
|
|
198
|
+
total: number;
|
|
199
|
+
/** Binds to exactly one OTHER file in the scan — the edges a walk can actually use. */
|
|
200
|
+
internalResolved: number;
|
|
201
|
+
/** The name is declared in several files, so binding it would be a guess. */
|
|
202
|
+
ambiguous: number;
|
|
203
|
+
/** Declared nowhere in the scan: a library, a builtin, or a method on an unknown type. */
|
|
204
|
+
outOfScan: number;
|
|
205
|
+
/** Lands in the file it came from. */
|
|
206
|
+
withinFile: number;
|
|
207
|
+
/** null when there are no call edges — unmeasured, never a perfect score. */
|
|
208
|
+
internalResolvedShare: number | null;
|
|
209
|
+
};
|
|
210
|
+
candidates: Array<{
|
|
211
|
+
file: string;
|
|
212
|
+
score: number;
|
|
213
|
+
symbols: string[];
|
|
214
|
+
evidence: string[];
|
|
215
|
+
}>;
|
|
216
|
+
/**
|
|
217
|
+
* @implements A-SPEC-494 — the semantic ALTERNATES: top-3 cached-vector cosines among files
|
|
218
|
+
* the emission did NOT reach. Additive evidence (cloud tier only) — S-511 measured the ceiling
|
|
219
|
+
* gain at ×1.25–×2.25 in weak windows with precision rising too; selection stays with the
|
|
220
|
+
* consumer. Absent when the gate is shut or nothing off-emission is covered.
|
|
221
|
+
*/
|
|
222
|
+
semanticAlternates?: Array<{
|
|
223
|
+
file: string;
|
|
224
|
+
cos: number;
|
|
225
|
+
}>;
|
|
226
|
+
/**
|
|
227
|
+
* @implements A-SPEC-409
|
|
228
|
+
* Ranks 11-20 of the same fused ordering, in the same shape as `candidates`.
|
|
229
|
+
*
|
|
230
|
+
* Two blind sets agree the harness picks the single best file better than reading-and-grepping
|
|
231
|
+
* does (Top-1 0.375 vs 0.125, then 0.750 vs 0.625) and recovers the whole change worse (Top-10
|
|
232
|
+
* recall 0.404 vs 0.554, then 0.833 vs 0.938). The gap was traced to judgement, not retrieval:
|
|
233
|
+
* reranking the fused top-20 by hand moved Top-1 to 0.500 and Top-5 recall to 0.575 on eight
|
|
234
|
+
* cases whose truth was unread at the time. The reachable recall of the wider list is measured —
|
|
235
|
+
* holmes 0.634 at ten to 0.691 at twenty, jarvis 0.612 to 0.739, and jarvis is flat by thirty.
|
|
236
|
+
*
|
|
237
|
+
* This field buys none of that on its own. It hands the calling agent the material; whether recall
|
|
238
|
+
* actually improves depends on how well that agent reranks, and that is not measured here.
|
|
239
|
+
*/
|
|
240
|
+
rerankPool: Array<{
|
|
241
|
+
file: string;
|
|
242
|
+
score: number;
|
|
243
|
+
symbols: string[];
|
|
244
|
+
evidence: string[];
|
|
245
|
+
}>;
|
|
246
|
+
candidateSpecs: string[];
|
|
247
|
+
impacts: {
|
|
248
|
+
/**
|
|
249
|
+
* Every file this analysis names as a modification candidate: the ranked localization hits PLUS
|
|
250
|
+
* the files that own transitively impacted symbols. This is the ONE prediction that gets
|
|
251
|
+
* persisted and scored — the enhanced ablation arm takes credit for caller-reachable files, so
|
|
252
|
+
* the result has to name them rather than leave them visible only as a count.
|
|
253
|
+
*/
|
|
254
|
+
files: string[];
|
|
255
|
+
/**
|
|
256
|
+
* @implements A-SPEC-271
|
|
257
|
+
* The same files, GRADED. `primary` is the head of the ranking, `secondary` the rest of the
|
|
258
|
+
* ranked candidates, `reachable` the files reached only by a call edge. This does not narrow
|
|
259
|
+
* the prediction — the union is exactly `files` — it says where the density is. Measured
|
|
260
|
+
* 2026-08-28 over 12 real commits: top-3 precision 0.167 against 0.061 for the flat set.
|
|
261
|
+
*/
|
|
262
|
+
tiers: {
|
|
263
|
+
primary: string[];
|
|
264
|
+
secondary: string[];
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* @implements A-SPEC-274
|
|
268
|
+
* Files reached ONLY through a call edge. These are not modification candidates — measured
|
|
269
|
+
* 2026-08-28 over 12 real commits, this list contributed 59 files and **zero** true positives,
|
|
270
|
+
* about a third of the whole prediction. They are what the change may AFFECT, so they are still
|
|
271
|
+
* reported (and still drive spec ancestry and test selection) but they are no longer folded
|
|
272
|
+
* into `files`. Nothing is lost; the name now matches what the list actually is.
|
|
273
|
+
*/
|
|
274
|
+
impactedFiles: string[];
|
|
275
|
+
/**
|
|
276
|
+
* @implements A-SPEC-469
|
|
277
|
+
* The GRADED impact prediction: PPR mass over the same graph, top-K, the changed set excluded.
|
|
278
|
+
* The closure above stays the gate-facing reach set (mass never enters a gate decision); this
|
|
279
|
+
* list is the consumer-facing answer to "what will change WITH this". Adopted on S-484's
|
|
280
|
+
* three-corpus measurement — the closure's own numbers were recall 0.0347 / precision 0.0413
|
|
281
|
+
* (HOLMES), the graded top-10's 0.5713 / 0.1611, and both metrics won on all three corpora,
|
|
282
|
+
* which is the bar that A-SPEC-309/313 set and their expansions failed.
|
|
283
|
+
*/
|
|
284
|
+
rankedImpact: Array<{
|
|
285
|
+
file: string;
|
|
286
|
+
score: number;
|
|
287
|
+
semCos?: number;
|
|
288
|
+
}>;
|
|
289
|
+
/**
|
|
290
|
+
* @implements A-SPEC-289
|
|
291
|
+
* Files that IMPORT a predicted file. A coarser dependency than a call, and the only axis that
|
|
292
|
+
* reaches a file depending on another without calling a symbol the resolver could pin down.
|
|
293
|
+
*
|
|
294
|
+
* Reported, never folded into `files`. Measured over 12 real commits: folding it in lifts recall
|
|
295
|
+
* 0.826 -> 0.913 but adds 129 files carrying 2 truth files (precision 0.0155) and roughly halves
|
|
296
|
+
* the prediction's density. Those 2 are reachable by nothing else, so the information is worth
|
|
297
|
+
* reporting — and, by A-SPEC-274's rule, being affected is still not being a candidate.
|
|
298
|
+
*/
|
|
299
|
+
importedByFiles: string[];
|
|
300
|
+
/**
|
|
301
|
+
* @implements A-SPEC-290
|
|
302
|
+
* Which impacted contracts were actually OBSERVED executing, and whether that observation can
|
|
303
|
+
* be trusted at all. `fresh` means the record was produced at this very commit; `stale` means a
|
|
304
|
+
* record exists for a different one and is therefore not evidence about this tree; `absent`
|
|
305
|
+
* means none exists. Only `fresh` ever populates `specs`.
|
|
306
|
+
*
|
|
307
|
+
* This NEVER removes a test from the selection. Absence of observation is not absence of
|
|
308
|
+
* relevance — the test may simply not have run — so runtime evidence adds a quality label and
|
|
309
|
+
* takes nothing away.
|
|
310
|
+
*/
|
|
311
|
+
runtimeObserved: {
|
|
312
|
+
status: 'fresh' | 'stale' | 'absent';
|
|
313
|
+
specs: string[];
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* @implements A-SPEC-292
|
|
317
|
+
* Relations the graph declined to link, by reason. `null` when unmeasured.
|
|
318
|
+
*/
|
|
319
|
+
unresolvedRelations: ResolutionReport | null;
|
|
320
|
+
direct: string[];
|
|
321
|
+
transitive: string[];
|
|
322
|
+
contract: string[];
|
|
323
|
+
test: string[];
|
|
324
|
+
};
|
|
325
|
+
impactBounds: {
|
|
326
|
+
maxCallDepth: number;
|
|
327
|
+
hubInDegree: number;
|
|
328
|
+
stoppedAt: Array<{
|
|
329
|
+
id: string;
|
|
330
|
+
reason: 'hub' | 'depth';
|
|
331
|
+
inDegree?: number;
|
|
332
|
+
}>;
|
|
333
|
+
seedIsHub?: {
|
|
334
|
+
callers: number;
|
|
335
|
+
threshold: number;
|
|
336
|
+
};
|
|
337
|
+
};
|
|
338
|
+
/**
|
|
339
|
+
* @implements A-SPEC-279
|
|
340
|
+
* The two constraint axes Phase 2 asks for beyond architecture. Both are ALWAYS assessed when
|
|
341
|
+
* their input exists — taint reachability costs ~5ms on an already-built graph, measured — and
|
|
342
|
+
* `assessed: false` means the input was missing, never that the axis came back clean.
|
|
343
|
+
*/
|
|
344
|
+
constraints: {
|
|
345
|
+
security: {
|
|
346
|
+
assessed: boolean;
|
|
347
|
+
limits: string[];
|
|
348
|
+
paths: Array<{
|
|
349
|
+
source: string;
|
|
350
|
+
sink: string;
|
|
351
|
+
files: string[];
|
|
352
|
+
}>;
|
|
353
|
+
};
|
|
354
|
+
compatibility: {
|
|
355
|
+
assessed: boolean;
|
|
356
|
+
breakingChanges: Array<{
|
|
357
|
+
spec: string;
|
|
358
|
+
kind: string;
|
|
359
|
+
}>;
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* @implements A-SPEC-293
|
|
363
|
+
* Architecture decisions that constrain the predicted files or candidate specs — what the
|
|
364
|
+
* change must not break, as opposed to what it touches.
|
|
365
|
+
*/
|
|
366
|
+
decisions: string[];
|
|
367
|
+
};
|
|
368
|
+
/**
|
|
369
|
+
* @implements A-SPEC-285
|
|
370
|
+
* For the languages ACTUALLY in this scan, the relation kinds their extractor does not recover.
|
|
371
|
+
* An empty list means every language present gave up everything it could — not that no language
|
|
372
|
+
* was involved.
|
|
373
|
+
*/
|
|
374
|
+
languageCoverage: LanguageGap[];
|
|
375
|
+
/**
|
|
376
|
+
* @implements A-SPEC-294
|
|
377
|
+
* Why the candidate requirements exist, read from each REQ's `source` frontmatter. The analysis
|
|
378
|
+
* used to report WHICH specs and never WHY — and the why is the one thing the code cannot be read
|
|
379
|
+
* back from. An `adr` source links to the decision it came from.
|
|
380
|
+
*/
|
|
381
|
+
/**
|
|
382
|
+
* @implements A-SPEC-295
|
|
383
|
+
* Candidate requirements whose Success Criteria cannot answer "did we satisfy this?" — the
|
|
384
|
+
* section is missing, or it says only that the implementation was completed and tested.
|
|
385
|
+
*/
|
|
386
|
+
unactionableCriteria: Array<{
|
|
387
|
+
req: string;
|
|
388
|
+
quality: 'absent' | 'boilerplate';
|
|
389
|
+
}>;
|
|
390
|
+
intent: Array<{
|
|
391
|
+
req: string;
|
|
392
|
+
kind: string;
|
|
393
|
+
ref: string | null;
|
|
394
|
+
retrieved: string | null;
|
|
395
|
+
note: string | null;
|
|
396
|
+
decision: string | null;
|
|
397
|
+
}>;
|
|
398
|
+
testScope: TestScope;
|
|
399
|
+
relevantTests: string[];
|
|
400
|
+
relevantTestSpecs: string[];
|
|
401
|
+
historicalRationale: MaintenanceHistoryRecord[];
|
|
402
|
+
contextBundle: ContextBundle | null;
|
|
403
|
+
assumptions: string[];
|
|
404
|
+
contradictions: string[];
|
|
405
|
+
unknowns: string[];
|
|
406
|
+
coverage: {
|
|
407
|
+
scannedFiles: number;
|
|
408
|
+
skippedFiles: string[];
|
|
409
|
+
unsupportedLanguages: string[];
|
|
410
|
+
historyStatus?: 'available' | 'unavailable' | 'not-requested';
|
|
411
|
+
};
|
|
412
|
+
graphBasis: MaintenanceAnalysisInput['basis'] & {
|
|
413
|
+
stale: boolean;
|
|
414
|
+
};
|
|
415
|
+
ablation: {
|
|
416
|
+
graphOff: AblationArm;
|
|
417
|
+
current: AblationArm;
|
|
418
|
+
enhanced: AblationArm;
|
|
419
|
+
};
|
|
420
|
+
evidenceDigest: string;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Decode a pathname as git prints it in `--name-only`/`--name-status` output. With the default
|
|
424
|
+
* `core.quotepath=true`, any non-ASCII or control byte makes git wrap the name in double quotes and
|
|
425
|
+
* emit C-style escapes (`"src/\354\227\260.ts"`). Comparing that raw form against the scanner's
|
|
426
|
+
* real relative path never matches, which silently emptied the historical rationale while the
|
|
427
|
+
* provenance still claimed the history was `available`.
|
|
428
|
+
*/
|
|
429
|
+
export declare function unquoteGitPath(line: string): string;
|
|
430
|
+
/**
|
|
431
|
+
* Fuse lexical localization, RTM ancestry, call-graph blast radius, test anchors, and history into a
|
|
432
|
+
* single immutable evidence contract. This core intentionally performs no I/O, so handlers can
|
|
433
|
+
* reproduce and compare an analysis from the exact same captured inputs.
|
|
434
|
+
*/
|
|
435
|
+
export declare function analyzeMaintenance(input: MaintenanceAnalysisInput): MaintenanceAnalysis;
|