@descryy/core 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/dist/budget/index.d.ts +2 -0
- package/dist/budget/index.d.ts.map +1 -1
- package/dist/budget/index.js +1 -0
- package/dist/budget/index.js.map +1 -1
- package/dist/budget/reply.d.ts +67 -0
- package/dist/budget/reply.d.ts.map +1 -0
- package/dist/budget/reply.js +76 -0
- package/dist/budget/reply.js.map +1 -0
- package/dist/contracts/engine.d.ts.map +1 -1
- package/dist/contracts/engine.js +11 -1
- package/dist/contracts/engine.js.map +1 -1
- package/dist/contracts/orphans.d.ts +17 -0
- package/dist/contracts/orphans.d.ts.map +1 -1
- package/dist/contracts/orphans.js +19 -2
- package/dist/contracts/orphans.js.map +1 -1
- package/dist/graph/cross-language.d.ts +0 -7
- package/dist/graph/cross-language.d.ts.map +1 -1
- package/dist/graph/cross-language.js +36 -2
- package/dist/graph/cross-language.js.map +1 -1
- package/dist/graph/index.d.ts +2 -0
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +1 -0
- package/dist/graph/index.js.map +1 -1
- package/dist/graph/observed-tests.d.ts +118 -0
- package/dist/graph/observed-tests.d.ts.map +1 -0
- package/dist/graph/observed-tests.js +123 -0
- package/dist/graph/observed-tests.js.map +1 -0
- package/dist/query/index.d.ts +5 -1
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/index.js +3 -1
- package/dist/query/index.js.map +1 -1
- package/dist/query/prominence.d.ts +73 -0
- package/dist/query/prominence.d.ts.map +1 -0
- package/dist/query/prominence.js +79 -0
- package/dist/query/prominence.js.map +1 -0
- package/dist/query/test-coverage.d.ts +65 -0
- package/dist/query/test-coverage.d.ts.map +1 -0
- package/dist/query/test-coverage.js +95 -0
- package/dist/query/test-coverage.js.map +1 -0
- package/dist/query/traverse.d.ts +60 -2
- package/dist/query/traverse.d.ts.map +1 -1
- package/dist/query/traverse.js +0 -0
- package/dist/query/traverse.js.map +1 -1
- package/dist/sources/git/diff.d.ts +14 -6
- package/dist/sources/git/diff.d.ts.map +1 -1
- package/dist/sources/git/diff.js +33 -10
- package/dist/sources/git/diff.js.map +1 -1
- package/dist/store/schema.d.ts +5 -4
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +42 -2
- package/dist/store/schema.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `observed_tests` — the EARNED record of which tests actually ran.
|
|
3
|
+
*
|
|
4
|
+
* This is the coverage axis, and coverage is deliberately not confidence: AI
|
|
5
|
+
* layer §20 separates *"what I actually verified"* from *"how sure am I"*, and
|
|
6
|
+
* the five report categories say nothing about the first. This module is the
|
|
7
|
+
* carrier for that separation, ruled in
|
|
8
|
+
* `documents/decisions-inbox/DEC-NEXT-test-run-coverage-has-no-carrier.md`.
|
|
9
|
+
*
|
|
10
|
+
* ## Why this is a table and not an R4 edge promotion
|
|
11
|
+
*
|
|
12
|
+
* The obvious-looking design is to run the suite and promote each `TESTS` edge
|
|
13
|
+
* it exercised to R4, reusing {@link applyRuntimeObservations}. That is not
|
|
14
|
+
* available, and the reason is the same one `observe-runtime.ts`'s header
|
|
15
|
+
* already gives for refusing to derive edges from correlated evidence.
|
|
16
|
+
*
|
|
17
|
+
* An edge needs **two endpoints and a witnessed relationship between them**. A
|
|
18
|
+
* test-runner's evidence carries no stack — every item it emits has
|
|
19
|
+
* `stackTrace: null` — so an observed test names exactly one node, its own
|
|
20
|
+
* `TEST_CASE`. Nothing in the run witnesses which function that test reached.
|
|
21
|
+
* Pairing the test with a function that merely co-occurred in the same run
|
|
22
|
+
* would mint an edge from temporal coincidence, which is precisely the
|
|
23
|
+
* wrong-direction failure rule 2 exists to prevent, arriving through the one
|
|
24
|
+
* mechanism built to make the graph *more* trustworthy.
|
|
25
|
+
*
|
|
26
|
+
* So the honest fact a test run establishes is a fact about **one node**: this
|
|
27
|
+
* test case executed, and this is what happened. That is not an edge and R4 in
|
|
28
|
+
* this store is a property of an edge, so it needs a carrier of its own.
|
|
29
|
+
*
|
|
30
|
+
* ## Why EARNED
|
|
31
|
+
*
|
|
32
|
+
* Same argument as `observed_edges`, one step over. Nothing in the source says
|
|
33
|
+
* which tests ran — the source says which tests *exist*. A rebuild that dropped
|
|
34
|
+
* this table would silently turn "what I actually verified" back into "what
|
|
35
|
+
* might be affected", which is the exact collapse the coverage axis exists to
|
|
36
|
+
* prevent.
|
|
37
|
+
*
|
|
38
|
+
* ## What this module refuses to do
|
|
39
|
+
*
|
|
40
|
+
* - **Invent a node.** An observation naming a node this graph does not hold is
|
|
41
|
+
* refused with its reason, never created — rule 2, and the same posture
|
|
42
|
+
* {@link applyRuntimeObservations} takes for an edge endpoint.
|
|
43
|
+
* - **Accept a category error.** A test outcome against a node that is not a
|
|
44
|
+
* `TEST_CASE` is refused rather than written. The graph holds the id, so this
|
|
45
|
+
* is not a missing-node refusal; it is a claim that would make every later
|
|
46
|
+
* coverage read meaningless, and it is far more likely to be a resolver bug
|
|
47
|
+
* than a real observation.
|
|
48
|
+
* - **Judge anything.** No finding, no hypothesis, no report category. It
|
|
49
|
+
* records what ran; whether that is *enough* is a judgement for a layer above
|
|
50
|
+
* this one.
|
|
51
|
+
*
|
|
52
|
+
* @see `packages/core/src/graph/runtime-confirmation.ts` — the edge half.
|
|
53
|
+
*/
|
|
54
|
+
const SYSTEM_CLOCK = { now: () => Date.now() };
|
|
55
|
+
function nodeTypeOf(driver, id) {
|
|
56
|
+
const row = driver.prepare("SELECT type FROM nodes WHERE id = ?").get(id);
|
|
57
|
+
return row?.type;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Record a run's test observations.
|
|
61
|
+
*
|
|
62
|
+
* Total over its input: every observation lands in exactly one of
|
|
63
|
+
* {@link ObservedTestsResult.recorded} or {@link ObservedTestsResult.refused},
|
|
64
|
+
* so a caller can account for all of them without inspecting the store. Never
|
|
65
|
+
* throws for an observation it cannot act on — that is a refusal, per the
|
|
66
|
+
* five-result-states rule the tool contract applies one layer up.
|
|
67
|
+
*/
|
|
68
|
+
export function applyObservedTests(driver, input, clock = SYSTEM_CLOCK) {
|
|
69
|
+
const recorded = [];
|
|
70
|
+
const refused = [];
|
|
71
|
+
const now = clock.now();
|
|
72
|
+
driver.transaction(() => {
|
|
73
|
+
for (const observation of input.observations) {
|
|
74
|
+
const type = nodeTypeOf(driver, observation.nodeId);
|
|
75
|
+
if (type === undefined) {
|
|
76
|
+
refused.push({
|
|
77
|
+
nodeId: observation.nodeId,
|
|
78
|
+
qualifiedName: observation.qualifiedName,
|
|
79
|
+
reason: `${observation.nodeId} is not in this graph, so the observed test cannot be recorded against it. ` +
|
|
80
|
+
"The graph is not extended from a runtime name.",
|
|
81
|
+
});
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (type !== "TEST_CASE") {
|
|
85
|
+
refused.push({
|
|
86
|
+
nodeId: observation.nodeId,
|
|
87
|
+
qualifiedName: observation.qualifiedName,
|
|
88
|
+
reason: `${observation.nodeId} is a ${type}, not a TEST_CASE — a test outcome recorded against it would ` +
|
|
89
|
+
"make every later coverage read meaningless. Refused rather than written.",
|
|
90
|
+
});
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
driver
|
|
94
|
+
.prepare("INSERT INTO observed_tests (node_id, run_id, qualified_name, outcome, duration_ms, commit_sha, observed_at)" +
|
|
95
|
+
" VALUES (?, ?, ?, ?, ?, ?, ?)" +
|
|
96
|
+
" ON CONFLICT(node_id, run_id) DO UPDATE SET qualified_name = excluded.qualified_name," +
|
|
97
|
+
" outcome = excluded.outcome, duration_ms = excluded.duration_ms, observed_at = excluded.observed_at")
|
|
98
|
+
.run(observation.nodeId, input.runId, observation.qualifiedName, observation.outcome, observation.durationMs, input.commitSha, now);
|
|
99
|
+
recorded.push(observation.nodeId);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
return { recorded, refused };
|
|
103
|
+
}
|
|
104
|
+
/** Read the EARNED ledger, newest observation first, optionally for one run. */
|
|
105
|
+
export function observedTests(driver, options = {}) {
|
|
106
|
+
const where = options.runId === undefined ? "" : " WHERE run_id = ?";
|
|
107
|
+
const sql = "SELECT node_id, run_id, qualified_name, outcome, duration_ms, commit_sha, observed_at" +
|
|
108
|
+
` FROM observed_tests${where} ORDER BY observed_at DESC, node_id ASC`;
|
|
109
|
+
const args = options.runId === undefined ? [] : [options.runId];
|
|
110
|
+
return driver
|
|
111
|
+
.prepare(sql)
|
|
112
|
+
.all(...args)
|
|
113
|
+
.map((row) => ({
|
|
114
|
+
nodeId: row.node_id,
|
|
115
|
+
runId: row.run_id,
|
|
116
|
+
qualifiedName: row.qualified_name,
|
|
117
|
+
outcome: row.outcome,
|
|
118
|
+
durationMs: row.duration_ms,
|
|
119
|
+
commitSha: row.commit_sha,
|
|
120
|
+
observedAt: row.observed_at,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=observed-tests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observed-tests.js","sourceRoot":"","sources":["../../src/graph/observed-tests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AA4DH,MAAM,YAAY,GAAU,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;AAMtD,SAAS,UAAU,CAAC,MAAiB,EAAE,EAAU;IAC/C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,GAAG,CAAC,EAAE,CAA4B,CAAC;IACrG,OAAO,GAAG,EAAE,IAAI,CAAC;AACnB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAiB,EACjB,KAAyB,EACzB,QAAe,YAAY;IAE3B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,OAAO,GAA0B,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IAExB,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE;QACtB,KAAK,MAAM,WAAW,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;YAEpD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM,EAAE,WAAW,CAAC,MAAM;oBAC1B,aAAa,EAAE,WAAW,CAAC,aAAa;oBACxC,MAAM,EACJ,GAAG,WAAW,CAAC,MAAM,6EAA6E;wBAClG,gDAAgD;iBACnD,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;gBACzB,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM,EAAE,WAAW,CAAC,MAAM;oBAC1B,aAAa,EAAE,WAAW,CAAC,aAAa;oBACxC,MAAM,EACJ,GAAG,WAAW,CAAC,MAAM,SAAS,IAAI,+DAA+D;wBACjG,0EAA0E;iBAC7E,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM;iBACH,OAAO,CACN,6GAA6G;gBAC3G,+BAA+B;gBAC/B,uFAAuF;gBACvF,qGAAqG,CACxG;iBACA,GAAG,CACF,WAAW,CAAC,MAAM,EAClB,KAAK,CAAC,KAAK,EACX,WAAW,CAAC,aAAa,EACzB,WAAW,CAAC,OAAO,EACnB,WAAW,CAAC,UAAU,EACtB,KAAK,CAAC,SAAS,EACf,GAAG,CACJ,CAAC;YAEJ,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAC3B,MAAiB,EACjB,UAAuC,EAAE;IAEzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC;IACrE,MAAM,GAAG,GACP,uFAAuF;QACvF,uBAAuB,KAAK,yCAAyC,CAAC;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChE,OAAO,MAAM;SACV,OAAO,CAQL,GAAG,CAAC;SACN,GAAG,CAAC,GAAG,IAAI,CAAC;SACZ,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACb,MAAM,EAAE,GAAG,CAAC,OAAO;QACnB,KAAK,EAAE,GAAG,CAAC,MAAM;QACjB,aAAa,EAAE,GAAG,CAAC,cAAc;QACjC,OAAO,EAAE,GAAG,CAAC,OAA8B;QAC3C,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,UAAU,EAAE,GAAG,CAAC,WAAW;KAC5B,CAAC,CAAC,CAAC;AACR,CAAC"}
|
package/dist/query/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { AdjacencyProvider, AdjacencyOptions, GraphStamp, QueryResult, } fr
|
|
|
3
3
|
export { createMemoryProvider } from "./memory.ts";
|
|
4
4
|
export type { MemoryGraph } from "./memory.ts";
|
|
5
5
|
export { createStoreProvider, commitSpread } from "./sqlite.ts";
|
|
6
|
-
export { bfs, weightedBfs, findPath, API_JOIN_EDGE_TYPES } from "./traverse.ts";
|
|
6
|
+
export { bfs, weightedBfs, findPath, defaultTraversalWeight, API_JOIN_EDGE_TYPES } from "./traverse.ts";
|
|
7
7
|
export type { Direction, Reached, TraversalOptions, TraversalOutcome, WeightedOptions } from "./traverse.ts";
|
|
8
8
|
export { impactOf, rootCauseSearch, testsCovering, dataFlowFrom, DATA_FLOW_EDGE_TYPES, traceApi, changeRisk, changesWith, } from "./queries.ts";
|
|
9
9
|
export type { ImpactOptions, RootCauseMatch, ChangeRisk, CoChange } from "./queries.ts";
|
|
@@ -18,6 +18,10 @@ export type { RefusalSite, RefusalQuestion, RefusalDropCounts, GroupingResult, }
|
|
|
18
18
|
export { summariseRefusals, fetchRefusalPage } from "./refusal-fetch.ts";
|
|
19
19
|
export type { RefusalSummary, RefusalPage, RefusalPageOk, RefusalPageRefused } from "./refusal-fetch.ts";
|
|
20
20
|
export { computeVerificationStatus, resolutionOfExercised } from "./verification-status.ts";
|
|
21
|
+
export { observedTestCoverage } from "./test-coverage.ts";
|
|
22
|
+
export type { ObservedTestFor, TestCoverageForRef } from "./test-coverage.ts";
|
|
23
|
+
export { prominenceOf, withProminence, prominenceCounts, PROMINENCE_CUTOFFS } from "./prominence.ts";
|
|
24
|
+
export type { Prominence, Ranked } from "./prominence.ts";
|
|
21
25
|
export type { CoverageCheck, ScopeItem } from "./verification-status.ts";
|
|
22
26
|
export { applyDeclaredValues, DECLARED_VALUE_RELIABILITY } from "./declared-value-closure.ts";
|
|
23
27
|
export type { ClosureState, DeclaredValueClosure, DeclaredValueApplication, } from "./declared-value-closure.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"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"}
|
package/dist/query/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { rankEdges, applyAdjacencyOptions } from "./provider.js";
|
|
2
2
|
export { createMemoryProvider } from "./memory.js";
|
|
3
3
|
export { createStoreProvider, commitSpread } from "./sqlite.js";
|
|
4
|
-
export { bfs, weightedBfs, findPath, API_JOIN_EDGE_TYPES } from "./traverse.js";
|
|
4
|
+
export { bfs, weightedBfs, findPath, defaultTraversalWeight, API_JOIN_EDGE_TYPES } from "./traverse.js";
|
|
5
5
|
export { impactOf, rootCauseSearch, testsCovering, dataFlowFrom, DATA_FLOW_EDGE_TYPES, traceApi, changeRisk, changesWith, } from "./queries.js";
|
|
6
6
|
export { similarIncidents, tokenSetSimilarity, fileOverlapSimilarity } from "./similar-incidents.js";
|
|
7
7
|
export { classifyRefusal, classifyRefusals, readRate } from "./unresolved.js";
|
|
@@ -9,6 +9,8 @@ export { applyConfirmedFacts } from "./confirmed-facts.js";
|
|
|
9
9
|
export { groupRefusalsIntoQuestions, groupRefusalsAcrossRepos, askableHandle } from "./refusal-questions.js";
|
|
10
10
|
export { summariseRefusals, fetchRefusalPage } from "./refusal-fetch.js";
|
|
11
11
|
export { computeVerificationStatus, resolutionOfExercised } from "./verification-status.js";
|
|
12
|
+
export { observedTestCoverage } from "./test-coverage.js";
|
|
13
|
+
export { prominenceOf, withProminence, prominenceCounts, PROMINENCE_CUTOFFS } from "./prominence.js";
|
|
12
14
|
export { applyDeclaredValues, DECLARED_VALUE_RELIABILITY } from "./declared-value-closure.js";
|
|
13
15
|
export { describeRowClosures } from "./row-closure-picture.js";
|
|
14
16
|
export { rankRootCause, DEFAULT_ROOT_CAUSE_WEIGHTS, ROOT_CAUSE_TRACE_EDGE_TYPES, INCIDENT_SATURATION } from "./root-cause-score.js";
|
package/dist/query/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAQjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGnD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAQjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGnD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGxG,OAAO,EACL,QAAQ,EACR,eAAe,EACf,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AASrG,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAG3D,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAQ7G,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGzE,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAIrG,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAO9F,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAQ/D,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AASpI,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prominence — "show me the top few, not a flat list", without borrowing a
|
|
3
|
+
* calibration that does not apply.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this is not `tierFor`
|
|
6
|
+
*
|
|
7
|
+
* `scoping/tiers.ts` already bands a score into §12.7's tiers 1-4 on **absolute
|
|
8
|
+
* thresholds** (0.7 and 0.3). Reusing it here was the obvious move and it is
|
|
9
|
+
* the wrong one, for a reason that is invisible from the type: the two scores
|
|
10
|
+
* are different quantities that happen to share the range (0, 1].
|
|
11
|
+
*
|
|
12
|
+
* - `scope` scores through `scoping/`: designed per-edge weights, then
|
|
13
|
+
* `finalScore` applies co-change and incident boosts. `weights.ts` says in
|
|
14
|
+
* its own comment that these must "stay on the same scale as an absolute tier
|
|
15
|
+
* threshold" — the thresholds were calibrated against *this* pipeline.
|
|
16
|
+
* - `impact` and `pr_analysis` score through `weightedBfs`, whose default
|
|
17
|
+
* `weightFor` is `edge.confidence` and nothing else. No designed weights, no
|
|
18
|
+
* boosts. `pr_analysis` passes only a budget, so it takes that default.
|
|
19
|
+
*
|
|
20
|
+
* Feeding the second into thresholds calibrated for the first would produce
|
|
21
|
+
* bands that look like §12.7's and mean something else — a calibrated label on
|
|
22
|
+
* an uncalibrated number, which is worse than no band at all because it reads
|
|
23
|
+
* as more precise than it is.
|
|
24
|
+
*
|
|
25
|
+
* ## So this bands on rank, and rank is a fact about the answer
|
|
26
|
+
*
|
|
27
|
+
* "This was the highest-scoring item in this reply" is true whatever produced
|
|
28
|
+
* the score, and it is exactly what "show me the top few" asks for. It claims
|
|
29
|
+
* nothing about magnitude, nothing about a threshold, and nothing that a change
|
|
30
|
+
* of scoring pipeline could silently falsify.
|
|
31
|
+
*
|
|
32
|
+
* The cut-offs are reading-surface sizes rather than measurements — the same
|
|
33
|
+
* footing `NODE_BUDGETS`'s own comment puts its figures on. Tune from data.
|
|
34
|
+
*/
|
|
35
|
+
/** Where an item falls in its answer's own ordering. */
|
|
36
|
+
export type Prominence = "top" | "notable" | "rest";
|
|
37
|
+
/**
|
|
38
|
+
* Rank cut-offs, inclusive upper bounds.
|
|
39
|
+
*
|
|
40
|
+
* `top` is three because that is what a developer reads before acting; the
|
|
41
|
+
* same figure `FINDING_CAPS["pre-push"]` reaches independently, from the same
|
|
42
|
+
* reasoning about a blocked developer's attention. It is **not** imported from
|
|
43
|
+
* there: that cap governs findings through the governance funnel, this governs
|
|
44
|
+
* presentation ordering, and coupling them would make a change to either mean
|
|
45
|
+
* a change to both.
|
|
46
|
+
*/
|
|
47
|
+
export declare const PROMINENCE_CUTOFFS: {
|
|
48
|
+
readonly top: 3;
|
|
49
|
+
readonly notable: 10;
|
|
50
|
+
};
|
|
51
|
+
/** `rank` is 1-based: the highest-scoring item in an answer is rank 1. */
|
|
52
|
+
export declare function prominenceOf(rank: number): Prominence;
|
|
53
|
+
export interface Ranked {
|
|
54
|
+
/** 1-based position in this answer, highest score first. */
|
|
55
|
+
readonly rank: number;
|
|
56
|
+
readonly prominence: Prominence;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Attach `rank` and `prominence` to items already ordered highest-first.
|
|
60
|
+
*
|
|
61
|
+
* Deliberately does **not** sort. Every caller here already orders its own
|
|
62
|
+
* list, and re-sorting would either be a no-op or silently disagree with the
|
|
63
|
+
* order the caller reported elsewhere in the same reply. Ranking a list that
|
|
64
|
+
* is not in score order is a caller bug this function cannot detect, which is
|
|
65
|
+
* why it says so here rather than pretending to be total over any input.
|
|
66
|
+
*/
|
|
67
|
+
export declare function withProminence<T>(ordered: readonly T[]): readonly (T & Ranked)[];
|
|
68
|
+
/**
|
|
69
|
+
* How many of each band a list holds — the counts a reply shows so a reader
|
|
70
|
+
* knows what the shown items were selected from.
|
|
71
|
+
*/
|
|
72
|
+
export declare function prominenceCounts(total: number): Readonly<Record<Prominence, number>>;
|
|
73
|
+
//# sourceMappingURL=prominence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prominence.d.ts","sourceRoot":"","sources":["../../src/query/prominence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB;;;CAAmC,CAAC;AAEnE,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAIrD;AAED,MAAM,WAAW,MAAM;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,CAMhF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAIpF"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prominence — "show me the top few, not a flat list", without borrowing a
|
|
3
|
+
* calibration that does not apply.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this is not `tierFor`
|
|
6
|
+
*
|
|
7
|
+
* `scoping/tiers.ts` already bands a score into §12.7's tiers 1-4 on **absolute
|
|
8
|
+
* thresholds** (0.7 and 0.3). Reusing it here was the obvious move and it is
|
|
9
|
+
* the wrong one, for a reason that is invisible from the type: the two scores
|
|
10
|
+
* are different quantities that happen to share the range (0, 1].
|
|
11
|
+
*
|
|
12
|
+
* - `scope` scores through `scoping/`: designed per-edge weights, then
|
|
13
|
+
* `finalScore` applies co-change and incident boosts. `weights.ts` says in
|
|
14
|
+
* its own comment that these must "stay on the same scale as an absolute tier
|
|
15
|
+
* threshold" — the thresholds were calibrated against *this* pipeline.
|
|
16
|
+
* - `impact` and `pr_analysis` score through `weightedBfs`, whose default
|
|
17
|
+
* `weightFor` is `edge.confidence` and nothing else. No designed weights, no
|
|
18
|
+
* boosts. `pr_analysis` passes only a budget, so it takes that default.
|
|
19
|
+
*
|
|
20
|
+
* Feeding the second into thresholds calibrated for the first would produce
|
|
21
|
+
* bands that look like §12.7's and mean something else — a calibrated label on
|
|
22
|
+
* an uncalibrated number, which is worse than no band at all because it reads
|
|
23
|
+
* as more precise than it is.
|
|
24
|
+
*
|
|
25
|
+
* ## So this bands on rank, and rank is a fact about the answer
|
|
26
|
+
*
|
|
27
|
+
* "This was the highest-scoring item in this reply" is true whatever produced
|
|
28
|
+
* the score, and it is exactly what "show me the top few" asks for. It claims
|
|
29
|
+
* nothing about magnitude, nothing about a threshold, and nothing that a change
|
|
30
|
+
* of scoring pipeline could silently falsify.
|
|
31
|
+
*
|
|
32
|
+
* The cut-offs are reading-surface sizes rather than measurements — the same
|
|
33
|
+
* footing `NODE_BUDGETS`'s own comment puts its figures on. Tune from data.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Rank cut-offs, inclusive upper bounds.
|
|
37
|
+
*
|
|
38
|
+
* `top` is three because that is what a developer reads before acting; the
|
|
39
|
+
* same figure `FINDING_CAPS["pre-push"]` reaches independently, from the same
|
|
40
|
+
* reasoning about a blocked developer's attention. It is **not** imported from
|
|
41
|
+
* there: that cap governs findings through the governance funnel, this governs
|
|
42
|
+
* presentation ordering, and coupling them would make a change to either mean
|
|
43
|
+
* a change to both.
|
|
44
|
+
*/
|
|
45
|
+
export const PROMINENCE_CUTOFFS = { top: 3, notable: 10 };
|
|
46
|
+
/** `rank` is 1-based: the highest-scoring item in an answer is rank 1. */
|
|
47
|
+
export function prominenceOf(rank) {
|
|
48
|
+
if (rank <= PROMINENCE_CUTOFFS.top)
|
|
49
|
+
return "top";
|
|
50
|
+
if (rank <= PROMINENCE_CUTOFFS.notable)
|
|
51
|
+
return "notable";
|
|
52
|
+
return "rest";
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Attach `rank` and `prominence` to items already ordered highest-first.
|
|
56
|
+
*
|
|
57
|
+
* Deliberately does **not** sort. Every caller here already orders its own
|
|
58
|
+
* list, and re-sorting would either be a no-op or silently disagree with the
|
|
59
|
+
* order the caller reported elsewhere in the same reply. Ranking a list that
|
|
60
|
+
* is not in score order is a caller bug this function cannot detect, which is
|
|
61
|
+
* why it says so here rather than pretending to be total over any input.
|
|
62
|
+
*/
|
|
63
|
+
export function withProminence(ordered) {
|
|
64
|
+
return ordered.map((item, index) => ({
|
|
65
|
+
...item,
|
|
66
|
+
rank: index + 1,
|
|
67
|
+
prominence: prominenceOf(index + 1),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* How many of each band a list holds — the counts a reply shows so a reader
|
|
72
|
+
* knows what the shown items were selected from.
|
|
73
|
+
*/
|
|
74
|
+
export function prominenceCounts(total) {
|
|
75
|
+
const top = Math.min(total, PROMINENCE_CUTOFFS.top);
|
|
76
|
+
const notable = Math.max(0, Math.min(total, PROMINENCE_CUTOFFS.notable) - top);
|
|
77
|
+
return { top, notable, rest: Math.max(0, total - top - notable) };
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=prominence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prominence.js","sourceRoot":"","sources":["../../src/query/prominence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAKH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAW,CAAC;AAEnE,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,IAAI,IAAI,kBAAkB,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,IAAI,IAAI,kBAAkB,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACzD,OAAO,MAAM,CAAC;AAChB,CAAC;AAQD;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAI,OAAqB;IACrD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnC,GAAG,IAAI;QACP,IAAI,EAAE,KAAK,GAAG,CAAC;QACf,UAAU,EAAE,YAAY,CAAC,KAAK,GAAG,CAAC,CAAC;KACpC,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,kBAAkB,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;IAC/E,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The join from `observed_tests` to the code a caller actually asked about.
|
|
3
|
+
*
|
|
4
|
+
* `observed_tests` records facts about `TEST_CASE` nodes: this test ran, and
|
|
5
|
+
* this is how it finished. Almost nobody asks about a test case. They ask about
|
|
6
|
+
* a function, a file, a scope from an earlier `impact` call, and getting from
|
|
7
|
+
* one to the other crosses a `TESTS` edge.
|
|
8
|
+
*
|
|
9
|
+
* ## What the crossing costs, and why it is disclosed rather than hidden
|
|
10
|
+
*
|
|
11
|
+
* The `TESTS` edge is **static**. An adapter inferred it, at whatever
|
|
12
|
+
* resolution that adapter reached, and nothing in a test run witnesses it: the
|
|
13
|
+
* run witnessed that a test executed, not which functions it touched. So the
|
|
14
|
+
* honest claim this query supports is *"a test that claims to cover this ran,
|
|
15
|
+
* and here is how it finished"* and **not** *"this function executed"*. The two
|
|
16
|
+
* are close enough in English to be conflated by a reader in a hurry, and far
|
|
17
|
+
* enough apart that conflating them turns an inference into a fabricated
|
|
18
|
+
* observation. {@link ObservedTestFor} carries `direct` for exactly this
|
|
19
|
+
* reason: `true` means the caller asked about the test case itself and the
|
|
20
|
+
* answer is witnessed end to end; `false` means the answer leans on an edge no
|
|
21
|
+
* run confirmed.
|
|
22
|
+
*
|
|
23
|
+
* That is also why this lives here rather than inside
|
|
24
|
+
* `computeVerificationStatus`. That module's contract is the *static* half, it
|
|
25
|
+
* says so in its own header, and it stays that way. Composing the static half
|
|
26
|
+
* with this earned one is the tool layer's job, the same place `observe_runtime`
|
|
27
|
+
* composes rather than absorbs.
|
|
28
|
+
*
|
|
29
|
+
* ## Absent, not empty
|
|
30
|
+
*
|
|
31
|
+
* A node no observed test reaches is missing from the returned map rather than
|
|
32
|
+
* present with an empty list. Inventing a key would let a caller iterate the
|
|
33
|
+
* map and believe it had asked about everything.
|
|
34
|
+
*/
|
|
35
|
+
import type { ObservedTestOutcome } from "../graph/observed-tests.ts";
|
|
36
|
+
import type { SqlDriver } from "../store/driver/driver.ts";
|
|
37
|
+
export interface ObservedTestFor {
|
|
38
|
+
/** The `TEST_CASE` node that ran. */
|
|
39
|
+
readonly nodeId: string;
|
|
40
|
+
/** The name the run itself printed. */
|
|
41
|
+
readonly qualifiedName: string;
|
|
42
|
+
readonly outcome: ObservedTestOutcome;
|
|
43
|
+
readonly runId: string;
|
|
44
|
+
readonly observedAt: number;
|
|
45
|
+
/**
|
|
46
|
+
* `true` when the caller asked about this very test case, witnessed end to
|
|
47
|
+
* end. `false` when the answer was reached across a static `TESTS` edge,
|
|
48
|
+
* which no run confirmed. See the module header.
|
|
49
|
+
*/
|
|
50
|
+
readonly direct: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface TestCoverageForRef {
|
|
53
|
+
readonly ref: string;
|
|
54
|
+
readonly tests: readonly ObservedTestFor[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* For each requested node id, the observed tests that bear on it.
|
|
58
|
+
*
|
|
59
|
+
* One test contributes at most one entry per ref: a test re-run across several
|
|
60
|
+
* runs is still one test, and the **most recent** observation is what the code
|
|
61
|
+
* stands verified against now. Reporting every historical run instead would
|
|
62
|
+
* make a flaky test look like broad coverage.
|
|
63
|
+
*/
|
|
64
|
+
export declare function observedTestCoverage(driver: SqlDriver, refs: readonly string[]): ReadonlyMap<string, TestCoverageForRef>;
|
|
65
|
+
//# sourceMappingURL=test-coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-coverage.d.ts","sourceRoot":"","sources":["../../src/query/test-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;CAC5C;AAaD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAwDzC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The join from `observed_tests` to the code a caller actually asked about.
|
|
3
|
+
*
|
|
4
|
+
* `observed_tests` records facts about `TEST_CASE` nodes: this test ran, and
|
|
5
|
+
* this is how it finished. Almost nobody asks about a test case. They ask about
|
|
6
|
+
* a function, a file, a scope from an earlier `impact` call, and getting from
|
|
7
|
+
* one to the other crosses a `TESTS` edge.
|
|
8
|
+
*
|
|
9
|
+
* ## What the crossing costs, and why it is disclosed rather than hidden
|
|
10
|
+
*
|
|
11
|
+
* The `TESTS` edge is **static**. An adapter inferred it, at whatever
|
|
12
|
+
* resolution that adapter reached, and nothing in a test run witnesses it: the
|
|
13
|
+
* run witnessed that a test executed, not which functions it touched. So the
|
|
14
|
+
* honest claim this query supports is *"a test that claims to cover this ran,
|
|
15
|
+
* and here is how it finished"* and **not** *"this function executed"*. The two
|
|
16
|
+
* are close enough in English to be conflated by a reader in a hurry, and far
|
|
17
|
+
* enough apart that conflating them turns an inference into a fabricated
|
|
18
|
+
* observation. {@link ObservedTestFor} carries `direct` for exactly this
|
|
19
|
+
* reason: `true` means the caller asked about the test case itself and the
|
|
20
|
+
* answer is witnessed end to end; `false` means the answer leans on an edge no
|
|
21
|
+
* run confirmed.
|
|
22
|
+
*
|
|
23
|
+
* That is also why this lives here rather than inside
|
|
24
|
+
* `computeVerificationStatus`. That module's contract is the *static* half, it
|
|
25
|
+
* says so in its own header, and it stays that way. Composing the static half
|
|
26
|
+
* with this earned one is the tool layer's job, the same place `observe_runtime`
|
|
27
|
+
* composes rather than absorbs.
|
|
28
|
+
*
|
|
29
|
+
* ## Absent, not empty
|
|
30
|
+
*
|
|
31
|
+
* A node no observed test reaches is missing from the returned map rather than
|
|
32
|
+
* present with an empty list. Inventing a key would let a caller iterate the
|
|
33
|
+
* map and believe it had asked about everything.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* For each requested node id, the observed tests that bear on it.
|
|
37
|
+
*
|
|
38
|
+
* One test contributes at most one entry per ref: a test re-run across several
|
|
39
|
+
* runs is still one test, and the **most recent** observation is what the code
|
|
40
|
+
* stands verified against now. Reporting every historical run instead would
|
|
41
|
+
* make a flaky test look like broad coverage.
|
|
42
|
+
*/
|
|
43
|
+
export function observedTestCoverage(driver, refs) {
|
|
44
|
+
const out = new Map();
|
|
45
|
+
if (refs.length === 0)
|
|
46
|
+
return out;
|
|
47
|
+
const placeholders = refs.map(() => "?").join(", ");
|
|
48
|
+
// Two arms, unioned rather than fetched separately so the de-duplication
|
|
49
|
+
// below sees both at once: a test case that both ran itself and is joined to
|
|
50
|
+
// by a TESTS edge must not be counted twice for one subject.
|
|
51
|
+
const sql = "SELECT e.target AS subject, o.node_id, o.qualified_name, o.outcome, o.run_id, o.observed_at," +
|
|
52
|
+
" 0 AS direct, o.rowid AS seq" +
|
|
53
|
+
" FROM observed_tests o" +
|
|
54
|
+
" JOIN edges e ON e.source = o.node_id AND e.type = 'TESTS'" +
|
|
55
|
+
` WHERE e.target IN (${placeholders})` +
|
|
56
|
+
" UNION ALL " +
|
|
57
|
+
"SELECT o.node_id AS subject, o.node_id, o.qualified_name, o.outcome, o.run_id, o.observed_at," +
|
|
58
|
+
" 1 AS direct, o.rowid AS seq" +
|
|
59
|
+
" FROM observed_tests o" +
|
|
60
|
+
` WHERE o.node_id IN (${placeholders})` +
|
|
61
|
+
// `seq` breaks the tie, and the tie is real rather than theoretical: two
|
|
62
|
+
// runs of one suite land in the same millisecond easily, and `observed_at`
|
|
63
|
+
// alone then leaves SQLite free to return the older run first. That made
|
|
64
|
+
// this query answer "verified against a failing run" when a later run had
|
|
65
|
+
// passed. rowid is monotonic for an ordinary (non-WITHOUT ROWID) table, so
|
|
66
|
+
// ordering by it is exactly "whichever row was written last" -- which is
|
|
67
|
+
// the semantics the timestamp was reaching for and could not express at
|
|
68
|
+
// millisecond resolution.
|
|
69
|
+
" ORDER BY observed_at DESC, seq DESC, node_id ASC";
|
|
70
|
+
const rows = driver.prepare(sql).all(...refs, ...refs);
|
|
71
|
+
// Rows arrive newest first, so the first sighting of a (subject, test) pair
|
|
72
|
+
// is the observation that stands. Later ones are older runs of the same test.
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const row of rows) {
|
|
75
|
+
const key = `${row.subject} ${row.node_id}`;
|
|
76
|
+
if (seen.has(key))
|
|
77
|
+
continue;
|
|
78
|
+
seen.add(key);
|
|
79
|
+
const entry = {
|
|
80
|
+
nodeId: row.node_id,
|
|
81
|
+
qualifiedName: row.qualified_name,
|
|
82
|
+
outcome: row.outcome,
|
|
83
|
+
runId: row.run_id,
|
|
84
|
+
observedAt: row.observed_at,
|
|
85
|
+
direct: row.direct === 1,
|
|
86
|
+
};
|
|
87
|
+
const existing = out.get(row.subject);
|
|
88
|
+
out.set(row.subject, {
|
|
89
|
+
ref: row.subject,
|
|
90
|
+
tests: existing === undefined ? [entry] : [...existing.tests, entry],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=test-coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-coverage.js","sourceRoot":"","sources":["../../src/query/test-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAqCH;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAiB,EACjB,IAAuB;IAEvB,MAAM,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAC;IAClD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAElC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,yEAAyE;IACzE,6EAA6E;IAC7E,6DAA6D;IAC7D,MAAM,GAAG,GACP,8FAA8F;QAC9F,8BAA8B;QAC9B,wBAAwB;QACxB,4DAA4D;QAC5D,uBAAuB,YAAY,GAAG;QACtC,aAAa;QACb,+FAA+F;QAC/F,8BAA8B;QAC9B,wBAAwB;QACxB,wBAAwB,YAAY,GAAG;QACvC,yEAAyE;QACzE,2EAA2E;QAC3E,yEAAyE;QACzE,0EAA0E;QAC1E,2EAA2E;QAC3E,yEAAyE;QACzE,wEAAwE;QACxE,0BAA0B;QAC1B,mDAAmD,CAAC;IAEtD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAc,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IAEpE,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEd,MAAM,KAAK,GAAoB;YAC7B,MAAM,EAAE,GAAG,CAAC,OAAO;YACnB,aAAa,EAAE,GAAG,CAAC,cAAc;YACjC,OAAO,EAAE,GAAG,CAAC,OAA8B;YAC3C,KAAK,EAAE,GAAG,CAAC,MAAM;YACjB,UAAU,EAAE,GAAG,CAAC,WAAW;YAC3B,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,CAAC;SACzB,CAAC;QAEF,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE;YACnB,GAAG,EAAE,GAAG,CAAC,OAAO;YAChB,KAAK,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;SACrE,CAAC,CAAC;IACL,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/query/traverse.d.ts
CHANGED
|
@@ -24,6 +24,43 @@
|
|
|
24
24
|
* improvement (relaxation), and any hop whose combined factor reaches 1.0 is
|
|
25
25
|
* rejected with an error naming the edge. Step 12 supplies the weights; this is
|
|
26
26
|
* the layer that refuses to run with weights that cannot terminate.
|
|
27
|
+
*
|
|
28
|
+
* ## Confidence is not the traversal weight
|
|
29
|
+
*
|
|
30
|
+
* This module's default `weightFor` used to be `edge.confidence` and nothing
|
|
31
|
+
* else, which made two shipped modules contradict each other. `runtime-
|
|
32
|
+
* confirmation.ts` writes `confidence = 1.0` on every edge it promotes or mints
|
|
33
|
+
* at R4 — correctly, because rule 3 makes R4 unconditionally class A and a
|
|
34
|
+
* witnessed edge is not 99% believed. The old default then handed that same 1.0
|
|
35
|
+
* to the guard above as a *weight*, so the strongest edge in the graph was the
|
|
36
|
+
* one edge `impact` refused to walk. Any graph that had ever had a runtime
|
|
37
|
+
* observation applied threw.
|
|
38
|
+
*
|
|
39
|
+
* The two quantities were never the same thing, and `weights.ts` has said so
|
|
40
|
+
* all along: `validateScopingConfig` asserts
|
|
41
|
+
* `0 < edgeWeight x maxFanOutMultiplier x maxConfidence < 1` and notes in its
|
|
42
|
+
* own comment that *"maxConfidence is 1.0 by definition"*. Termination is the
|
|
43
|
+
* **edge weight's** job; confidence is evidence strength and is entitled to
|
|
44
|
+
* reach 1.0. The old default was the degenerate weight table with every edge
|
|
45
|
+
* type at 1.0 — precisely the configuration `validateScopingConfig` refuses to
|
|
46
|
+
* load. The in-process path was running a config the config loader would
|
|
47
|
+
* reject, while the worker path passed a real table and did not.
|
|
48
|
+
*
|
|
49
|
+
* So the default is now {@link defaultTraversalWeight}: the per-edge-type decay
|
|
50
|
+
* of `DEFAULT_EDGE_WEIGHTS` times the edge's confidence — the same product
|
|
51
|
+
* `weightFromTable` builds for the worker. Termination survives on three
|
|
52
|
+
* independent legs, none of which is a stored confidence value:
|
|
53
|
+
*
|
|
54
|
+
* 1. every weight in `DEFAULT_EDGE_WEIGHTS` is <= 0.95, and confidence is
|
|
55
|
+
* capped at 1.0 by the IR, so the per-hop factor is <= 0.95 < 1 by
|
|
56
|
+
* construction rather than by data;
|
|
57
|
+
* 2. `validateScopingConfig` refuses, at config load, any table that would
|
|
58
|
+
* break leg 1;
|
|
59
|
+
* 3. the per-hop `RangeError` below is unchanged and still refuses any weight
|
|
60
|
+
* outside (0, 1), which is what holds a caller-supplied `weightFor` to the
|
|
61
|
+
* same rule.
|
|
62
|
+
*
|
|
63
|
+
* Ruled in `DEC-386`.
|
|
27
64
|
*/
|
|
28
65
|
import type { EdgeType, IREdge, ResolutionLevel } from "@descryy/ir";
|
|
29
66
|
import type { AdjacencyProvider } from "./provider.ts";
|
|
@@ -79,11 +116,32 @@ export interface TraversalOutcome {
|
|
|
79
116
|
* here precisely because there is no score to improve on a second visit.
|
|
80
117
|
*/
|
|
81
118
|
export declare function bfs(provider: AdjacencyProvider, seeds: readonly string[], options?: TraversalOptions): TraversalOutcome;
|
|
119
|
+
/**
|
|
120
|
+
* The default per-hop decay: §12.4's `EdgeWeight` for the edge's *type*, times
|
|
121
|
+
* the edge's own confidence.
|
|
122
|
+
*
|
|
123
|
+
* Both terms are §12.4's, and neither one alone is the weight. Confidence
|
|
124
|
+
* carries how much the producer believes the edge; the type weight carries how
|
|
125
|
+
* far a change actually travels along that kind of edge — an `IMPORTS` hop at
|
|
126
|
+
* 0.4 is a weak signal even when it is certain, and a witnessed `CALLS` hop at
|
|
127
|
+
* 0.95 x 1.0 is strong precisely because it was witnessed.
|
|
128
|
+
*
|
|
129
|
+
* Exported so a caller can compose with it rather than reconstruct it, and so
|
|
130
|
+
* the in-process and worker paths are demonstrably the same function — see the
|
|
131
|
+
* parity test in `worker.test.ts`. It is the callback form of
|
|
132
|
+
* `weightFromTable({ weights: DEFAULT_EDGE_WEIGHTS, fallback: ... })`.
|
|
133
|
+
*/
|
|
134
|
+
export declare function defaultTraversalWeight(edge: {
|
|
135
|
+
readonly type: EdgeType;
|
|
136
|
+
readonly confidence: number;
|
|
137
|
+
}): number;
|
|
82
138
|
export interface WeightedOptions extends TraversalOptions {
|
|
83
139
|
/**
|
|
84
140
|
* The per-hop decay factor for an edge. Must be strictly less than 1.0 —
|
|
85
|
-
* see the note at the top of this file. Defaults to
|
|
86
|
-
*
|
|
141
|
+
* see the note at the top of this file. Defaults to
|
|
142
|
+
* {@link defaultTraversalWeight}: the edge type's §12.4 weight times the
|
|
143
|
+
* edge's confidence. **Not confidence alone** — that conflated evidence
|
|
144
|
+
* strength with decay and made a witnessed R4 edge untraversable.
|
|
87
145
|
*/
|
|
88
146
|
readonly weightFor?: (edge: IREdge) => number;
|
|
89
147
|
/** Stop expanding below this score. The main reason traversal ends before the budget. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traverse.d.ts","sourceRoot":"","sources":["../../src/query/traverse.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"traverse.d.ts","sourceRoot":"","sources":["../../src/query/traverse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAMrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;AAE9C;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,QAAQ,EAA+B,CAAC;AAEnF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,qFAAqF;IACrF,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AA2CD;;;;;GAKG;AACH,wBAAgB,GAAG,CACjB,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,GAAE,gBAAqB,GAC7B,gBAAgB,CAqClB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE7G;AAED,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,GAAE,eAAoB,GAC5B,gBAAgB,CAyDlB;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,gBAAqB,GAC7B,SAAS,MAAM,EAAE,CA8BnB"}
|
package/dist/query/traverse.js
CHANGED
|
Binary file
|