@weftlet/toolkit 0.7.2 → 0.7.4
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/README.md +8 -4
- package/dist/graph/impact-affinity-order.d.ts +11 -0
- package/dist/graph/impact-affinity-order.js +53 -0
- package/dist/graph/impact-affinity.d.ts +6 -0
- package/dist/graph/impact-affinity.js +91 -0
- package/dist/graph/impact-order.d.ts +21 -0
- package/dist/graph/impact-order.js +77 -0
- package/dist/graph/impact-signals.d.ts +2 -1
- package/dist/graph/impact-signals.js +8 -8
- package/dist/graph/query.js +1 -1
- package/dist/graph/ranking.d.ts +1 -1
- package/dist/graph/ranking.js +6 -18
- package/dist/interfaces/graph-paths.js +1 -1
- package/docs/1.0-WORK-QUEUE.md +22 -0
- package/docs/ADR-041-bounded-relationship-retrieval.md +1 -0
- package/docs/ADR-042-indirect-local-relationships.md +61 -0
- package/docs/ADR-043-shared-symbol-specificity.md +67 -0
- package/docs/RELEASING.md +3 -3
- package/docs/VALIDATION.md +108 -0
- package/docs/extraction-manifest.json +35 -5
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Proposed future capabilities are recorded in the [Weftlet 2.0 product plan](docs
|
|
|
14
14
|
## Set up a project
|
|
15
15
|
|
|
16
16
|
<!-- weftlet:release:start -->
|
|
17
|
-
Package versions in this checkout/archive: `@weftlet/toolkit@0.7.
|
|
17
|
+
Package versions in this checkout/archive: `@weftlet/toolkit@0.7.4` and `@weftlet/core@0.7.4`.
|
|
18
18
|
<!-- weftlet:release:end -->
|
|
19
19
|
|
|
20
20
|
Weftlet uses `@weftlet/toolkit`; its neutral `@weftlet/core` dependency is installed
|
|
@@ -486,10 +486,14 @@ pending re-extraction. Queries compare captured file digests with current workin
|
|
|
486
486
|
report subsequent edits separately from captured and excluded changes. Older overlays without
|
|
487
487
|
capture metadata have unknown freshness. Historical or draft evidence cannot validate a current diff.
|
|
488
488
|
|
|
489
|
-
Impact queries [
|
|
489
|
+
Impact queries [give near and indirect relationships bounded turns](docs/ADR-042-indirect-local-relationships.md)
|
|
490
490
|
ahead of repeated detail and shared-import context. Ranking changes ordering, not saved facts or
|
|
491
|
-
the reachable scope.
|
|
492
|
-
|
|
491
|
+
the reachable scope. Calls, cross-file references (including those without call proof) and file
|
|
492
|
+
consumers share turns across distance groups, with source diversity within each group. Local
|
|
493
|
+
detail has its own turns; unindexed symbol hubs follow local evidence unless explicitly queried.
|
|
494
|
+
[Shared-symbol specificity](docs/ADR-043-shared-symbol-specificity.md) also gives turns to
|
|
495
|
+
indexed consumers sharing a selectively referenced symbol with the producer. File-scope
|
|
496
|
+
joins alone do not supply that signal. Equal evidence can still exceed a bounded page budget.
|
|
493
497
|
These are ordering signals, not new assertions or proof of execution. All evidence remains in
|
|
494
498
|
the reachable set. Responses identify the ranking version; after a ranking upgrade, restart
|
|
495
499
|
scans whose old continuation tokens are rejected. No snapshot refresh is needed for new ordering.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GraphFact } from '@weftlet/core';
|
|
2
|
+
import { type ImpactOrderRow } from './impact-order.js';
|
|
3
|
+
/** Interleave specificity with baseline waves, retaining every assertion exactly
|
|
4
|
+
* once and preserving evidence-lane balancing. No affinity means exact v3 order. */
|
|
5
|
+
export declare function interleaveAffinity(rows: ImpactOrderRow[], baseline: readonly {
|
|
6
|
+
fact: GraphFact;
|
|
7
|
+
score: number;
|
|
8
|
+
}[], affinity: ReadonlyMap<string, number>): readonly {
|
|
9
|
+
fact: GraphFact;
|
|
10
|
+
score: number;
|
|
11
|
+
}[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { sourceQueue } from './impact-order.js';
|
|
2
|
+
/** Balance reference/consumer families and sources within a specificity cohort.
|
|
3
|
+
* A later occurrence can surface without waiting for every unrelated local file. */
|
|
4
|
+
function affinityQueue(rows, affinity) {
|
|
5
|
+
const cohorts = new Map();
|
|
6
|
+
for (const row of rows) {
|
|
7
|
+
const degree = affinity.get(row.fact.id);
|
|
8
|
+
if (degree === undefined || row.contextOnly || !['references', 'consumes'].includes(row.fact.predicate))
|
|
9
|
+
continue;
|
|
10
|
+
const group = cohorts.get(degree) ?? [];
|
|
11
|
+
group.push(row);
|
|
12
|
+
cohorts.set(degree, group);
|
|
13
|
+
}
|
|
14
|
+
const ordered = [];
|
|
15
|
+
for (const [, cohort] of [...cohorts].sort(([a], [b]) => a - b)) {
|
|
16
|
+
const queues = ['references', 'consumes'].map(predicate => sourceQueue(cohort.filter(row => row.fact.predicate === predicate)));
|
|
17
|
+
let remaining = cohort.length;
|
|
18
|
+
while (remaining > 0)
|
|
19
|
+
for (const next of queues) {
|
|
20
|
+
const row = next();
|
|
21
|
+
if (row) {
|
|
22
|
+
ordered.push(row.fact);
|
|
23
|
+
remaining--;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return ordered;
|
|
28
|
+
}
|
|
29
|
+
/** Interleave specificity with baseline waves, retaining every assertion exactly
|
|
30
|
+
* once and preserving evidence-lane balancing. No affinity means exact v3 order. */
|
|
31
|
+
export function interleaveAffinity(rows, baseline, affinity) {
|
|
32
|
+
if (!affinity.size)
|
|
33
|
+
return baseline;
|
|
34
|
+
const queue = affinityQueue(rows, affinity), emitted = new Set();
|
|
35
|
+
const result = [];
|
|
36
|
+
let cursor = 0;
|
|
37
|
+
const push = (fact, score) => {
|
|
38
|
+
if (!emitted.has(fact.id)) {
|
|
39
|
+
result.push({ fact, score });
|
|
40
|
+
emitted.add(fact.id);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
for (const { fact, score } of baseline) {
|
|
44
|
+
if (emitted.has(fact.id))
|
|
45
|
+
continue;
|
|
46
|
+
while (cursor < queue.length && emitted.has(queue[cursor].id))
|
|
47
|
+
cursor++;
|
|
48
|
+
if (cursor < queue.length)
|
|
49
|
+
push(queue[cursor++], score);
|
|
50
|
+
push(fact, score);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type GraphFact } from '@weftlet/core';
|
|
2
|
+
/** Structural relevance from producer -> shared symbol <- other indexed symbol.
|
|
3
|
+
* Smaller distinct production referrer counts are more specific, not more true.
|
|
4
|
+
* File/module anchors, locations alone and unknown projections cannot supply this
|
|
5
|
+
* signal. All assertions (including tests) remain in the baseline ordering. */
|
|
6
|
+
export declare function sharedSymbolAffinity(facts: readonly GraphFact[], direct: readonly GraphFact[], anchorTerms: readonly string[]): ReadonlyMap<string, number>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { repositoryFileNode, sha256 } from '@weftlet/core';
|
|
2
|
+
const sameExtraction = (a, b) => a.snapshotId === b.snapshotId &&
|
|
3
|
+
a.provenance.commitSha === b.provenance.commitSha && a.extractor.id === b.extractor.id &&
|
|
4
|
+
a.extractor.version === b.extractor.version;
|
|
5
|
+
const indexedName = (node) => node.kind === 'CodeSymbol' &&
|
|
6
|
+
node.codeSymbol?.provider === 'scip-typescript' && node.codeSymbol.identityScope === 'repository' &&
|
|
7
|
+
node.codeSymbol.resolution === 'indexed-definition' && !node.codeSymbol.nativeSymbol.endsWith('/');
|
|
8
|
+
// Keys describe corroborating saved evidence, never a new native/legacy identity edge.
|
|
9
|
+
const projectionKey = (fact, native) => JSON.stringify([fact.snapshotId,
|
|
10
|
+
fact.provenance.commitSha, fact.provenance.digest, fact.provenance.path,
|
|
11
|
+
fact.extractor.id, fact.extractor.version, native]);
|
|
12
|
+
const witnessKey = (fact) => JSON.stringify([fact.snapshotId, fact.provenance.commitSha,
|
|
13
|
+
fact.extractor.id, fact.extractor.version, fact.object.id]);
|
|
14
|
+
/** Structural relevance from producer -> shared symbol <- other indexed symbol.
|
|
15
|
+
* Smaller distinct production referrer counts are more specific, not more true.
|
|
16
|
+
* File/module anchors, locations alone and unknown projections cannot supply this
|
|
17
|
+
* signal. All assertions (including tests) remain in the baseline ordering. */
|
|
18
|
+
export function sharedSymbolAffinity(facts, direct, anchorTerms) {
|
|
19
|
+
if (!anchorTerms.length)
|
|
20
|
+
return new Map();
|
|
21
|
+
const definitions = new Map();
|
|
22
|
+
for (const fact of facts) {
|
|
23
|
+
if (fact.extractor.id !== 'scip-symbol-extractor' || fact.predicate !== 'definedBy' ||
|
|
24
|
+
fact.codeOccurrence?.role !== 'definition' || fact.codeOccurrence.locationKind !== 'name' ||
|
|
25
|
+
!indexedName(fact.subject) || fact.object.id !== repositoryFileNode(fact.provenance.path).id)
|
|
26
|
+
continue;
|
|
27
|
+
const group = definitions.get(fact.subject.id) ?? [];
|
|
28
|
+
group.push(fact);
|
|
29
|
+
definitions.set(fact.subject.id, group);
|
|
30
|
+
}
|
|
31
|
+
const eligible = (node, fact) => indexedName(node) &&
|
|
32
|
+
definitions.get(node.id)?.some(def => sameExtraction(def, fact)) === true;
|
|
33
|
+
const production = (node, fact) => definitions.get(node.id)?.some(def => sameExtraction(def, fact) && repositoryFileNode(def.provenance.path).kind === 'Component') === true;
|
|
34
|
+
const origins = new Set(direct.filter(fact => fact.predicate === 'definedBy' &&
|
|
35
|
+
definitions.get(fact.subject.id)?.some(def => def.id === fact.id)).map(fact => fact.subject.id));
|
|
36
|
+
if (!origins.size)
|
|
37
|
+
return new Map();
|
|
38
|
+
const references = facts.filter(fact => fact.predicate === 'references' &&
|
|
39
|
+
fact.codeOccurrence?.role === 'reference' && fact.codeOccurrence.locationKind === 'name' &&
|
|
40
|
+
fact.extractor.id === 'scip-symbol-extractor' && eligible(fact.subject, fact) && eligible(fact.object, fact) &&
|
|
41
|
+
fact.subject.codeSymbol.repositoryId === fact.object.codeSymbol.repositoryId);
|
|
42
|
+
const shared = new Map();
|
|
43
|
+
for (const fact of references)
|
|
44
|
+
if (origins.has(fact.subject.id)) {
|
|
45
|
+
shared.set(witnessKey(fact), { referrers: new Set(), members: [] });
|
|
46
|
+
}
|
|
47
|
+
for (const fact of references) {
|
|
48
|
+
const group = shared.get(witnessKey(fact));
|
|
49
|
+
if (!group)
|
|
50
|
+
continue;
|
|
51
|
+
if (production(fact.subject, fact))
|
|
52
|
+
group.referrers.add(fact.subject.id);
|
|
53
|
+
if (!origins.has(fact.subject.id))
|
|
54
|
+
group.members.push(fact);
|
|
55
|
+
}
|
|
56
|
+
// Include the extraction scope: stale evidence cannot become a current witness.
|
|
57
|
+
const subjectKey = (fact) => JSON.stringify([fact.snapshotId, fact.provenance.commitSha,
|
|
58
|
+
fact.extractor.id, fact.extractor.version, fact.subject.id]);
|
|
59
|
+
const affinity = new Map();
|
|
60
|
+
for (const group of shared.values())
|
|
61
|
+
for (const fact of group.members) {
|
|
62
|
+
const key = subjectKey(fact), count = Math.max(1, group.referrers.size);
|
|
63
|
+
affinity.set(key, Math.min(affinity.get(key) ?? Infinity, count));
|
|
64
|
+
}
|
|
65
|
+
const result = new Map(), projections = new Map();
|
|
66
|
+
for (const fact of references) {
|
|
67
|
+
const degree = affinity.get(subjectKey(fact));
|
|
68
|
+
if (degree === undefined)
|
|
69
|
+
continue;
|
|
70
|
+
result.set(fact.id, degree);
|
|
71
|
+
const key = projectionKey(fact, fact.object.codeSymbol.nativeSymbol);
|
|
72
|
+
projections.set(key, Math.min(projections.get(key) ?? Infinity, degree));
|
|
73
|
+
}
|
|
74
|
+
const legacyIds = new Map();
|
|
75
|
+
for (const fact of facts) {
|
|
76
|
+
if (fact.predicate !== 'consumes' || fact.extractor.id !== 'scip-symbol-extractor' ||
|
|
77
|
+
fact.subject.id !== repositoryFileNode(fact.provenance.path).id || fact.object.kind !== 'Contract')
|
|
78
|
+
continue;
|
|
79
|
+
const degree = projections.get(projectionKey(fact, fact.object.label));
|
|
80
|
+
if (degree === undefined)
|
|
81
|
+
continue;
|
|
82
|
+
let id = legacyIds.get(fact.object.label);
|
|
83
|
+
if (!id) {
|
|
84
|
+
id = `symbol:scip:${sha256(fact.object.label)}`;
|
|
85
|
+
legacyIds.set(fact.object.label, id);
|
|
86
|
+
}
|
|
87
|
+
if (fact.object.id === id)
|
|
88
|
+
result.set(fact.id, degree);
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { GraphFact } from '@weftlet/core';
|
|
2
|
+
export interface ImpactOrderRow {
|
|
3
|
+
readonly fact: GraphFact;
|
|
4
|
+
readonly proximity: number;
|
|
5
|
+
readonly relationshipOrder: number;
|
|
6
|
+
readonly contextOnly: boolean;
|
|
7
|
+
readonly focus: number;
|
|
8
|
+
readonly tie: string;
|
|
9
|
+
}
|
|
10
|
+
/** One source per turn within a distance/family bucket, without adding an
|
|
11
|
+
* unbounded source ordinal to graph distance. Every input row is retained. */
|
|
12
|
+
export declare function sourceQueue(rows: ImpactOrderRow[]): () => ImpactOrderRow | undefined;
|
|
13
|
+
/** Ordering waves preserve evidence-lane balancing in the query facade. Near
|
|
14
|
+
* and indirect relationships each get turns (two slots for calls, references
|
|
15
|
+
* and consumers, one for other relations); local detail also gets one slot
|
|
16
|
+
* per wave. Unindexed/shared-import context follows local evidence unless it
|
|
17
|
+
* was explicitly queried. No assertion, citation or authority is rewritten. */
|
|
18
|
+
export declare function orderImpactRows(rows: ImpactOrderRow[]): {
|
|
19
|
+
fact: GraphFact;
|
|
20
|
+
score: number;
|
|
21
|
+
}[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Fixed distance bands prevent thousands of first-hop sources from exhausting
|
|
2
|
+
* every page before any second-hop evidence gets a turn. They do not bound scope. */
|
|
3
|
+
function distanceBand(distance) {
|
|
4
|
+
return distance < 4 ? distance : distance < 8 ? 4 : 5;
|
|
5
|
+
}
|
|
6
|
+
function family(fact) {
|
|
7
|
+
if (fact.predicate === 'calls' || fact.predicate === 'references' || fact.predicate === 'consumes')
|
|
8
|
+
return fact.predicate;
|
|
9
|
+
return 'other';
|
|
10
|
+
}
|
|
11
|
+
const lexical = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
12
|
+
const compare = (a, b) => b.focus - a.focus ||
|
|
13
|
+
a.proximity - b.proximity || lexical(a.tie, b.tie) || lexical(a.fact.id, b.fact.id);
|
|
14
|
+
/** One source per turn within a distance/family bucket, without adding an
|
|
15
|
+
* unbounded source ordinal to graph distance. Every input row is retained. */
|
|
16
|
+
export function sourceQueue(rows) {
|
|
17
|
+
const sources = new Map();
|
|
18
|
+
for (const row of rows.sort(compare)) {
|
|
19
|
+
const group = sources.get(row.fact.provenance.path) ?? [];
|
|
20
|
+
group.push(row);
|
|
21
|
+
sources.set(row.fact.provenance.path, group);
|
|
22
|
+
}
|
|
23
|
+
let active = [...sources.values()].map(rows => ({ rows, offset: 0 }));
|
|
24
|
+
let cursor = 0;
|
|
25
|
+
return () => {
|
|
26
|
+
if (!active.length)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (cursor === active.length) {
|
|
29
|
+
active = active.filter(source => source.offset < source.rows.length);
|
|
30
|
+
cursor = 0;
|
|
31
|
+
}
|
|
32
|
+
const source = active[cursor++];
|
|
33
|
+
return source?.rows[source.offset++];
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Ordering waves preserve evidence-lane balancing in the query facade. Near
|
|
37
|
+
* and indirect relationships each get turns (two slots for calls, references
|
|
38
|
+
* and consumers, one for other relations); local detail also gets one slot
|
|
39
|
+
* per wave. Unindexed/shared-import context follows local evidence unless it
|
|
40
|
+
* was explicitly queried. No assertion, citation or authority is rewritten. */
|
|
41
|
+
export function orderImpactRows(rows) {
|
|
42
|
+
const result = [];
|
|
43
|
+
let wave = 0;
|
|
44
|
+
for (const contextOnly of [false, true]) {
|
|
45
|
+
const selected = rows.filter(row => row.contextOnly === contextOnly);
|
|
46
|
+
const buckets = new Map();
|
|
47
|
+
const details = [];
|
|
48
|
+
for (const row of selected) {
|
|
49
|
+
if (row.relationshipOrder > 0) {
|
|
50
|
+
details.push(row);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const key = `${distanceBand(row.proximity)}:${family(row.fact)}`;
|
|
54
|
+
const bucket = buckets.get(key) ?? [];
|
|
55
|
+
bucket.push(row);
|
|
56
|
+
buckets.set(key, bucket);
|
|
57
|
+
}
|
|
58
|
+
const queues = [...buckets].sort(([a], [b]) => lexical(a, b))
|
|
59
|
+
.map(([key, group]) => ({ next: sourceQueue(group), slots: key.endsWith(':other') ? 1 : 2 }));
|
|
60
|
+
if (details.length)
|
|
61
|
+
queues.push({ next: sourceQueue(details), slots: 1 });
|
|
62
|
+
let remaining = selected.length;
|
|
63
|
+
while (remaining > 0) {
|
|
64
|
+
for (const { next, slots } of queues) {
|
|
65
|
+
for (let slot = 0; slot < slots; slot++) {
|
|
66
|
+
const row = next();
|
|
67
|
+
if (row) {
|
|
68
|
+
result.push({ fact: row.fact, score: wave });
|
|
69
|
+
remaining--;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
wave++;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
@@ -3,5 +3,6 @@ import type { GraphFact, GraphNode } from '@weftlet/core';
|
|
|
3
3
|
export declare function impactRankingSignals(facts: readonly GraphFact[], anchorTerms: readonly string[]): {
|
|
4
4
|
penalty: (fact: GraphFact) => 0 | 8;
|
|
5
5
|
seedNodes: (fact: GraphFact) => GraphNode[];
|
|
6
|
-
relationshipOrder: (fact: GraphFact) => 0 | 2
|
|
6
|
+
relationshipOrder: (fact: GraphFact) => 0 | 2;
|
|
7
|
+
contextOnly: (fact: GraphFact) => boolean;
|
|
7
8
|
};
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
export function impactRankingSignals(facts, anchorTerms) {
|
|
3
3
|
const definitions = new Map();
|
|
4
4
|
const callsiteReferences = new Set();
|
|
5
|
-
const callableNativeSymbols = new Set();
|
|
6
5
|
const localCall = (fact) => fact.predicate === 'calls' && fact.codeCall !== undefined &&
|
|
7
6
|
fact.codeCall.target !== 'external' && fact.codeCall.targetDefinitions.length > 0;
|
|
8
7
|
for (const fact of facts) {
|
|
@@ -14,8 +13,6 @@ export function impactRankingSignals(facts, anchorTerms) {
|
|
|
14
13
|
if (localCall(fact)) {
|
|
15
14
|
for (const proof of fact.codeCall.targetOccurrences)
|
|
16
15
|
callsiteReferences.add(proof.factId);
|
|
17
|
-
if (fact.object.codeSymbol)
|
|
18
|
-
callableNativeSymbols.add(fact.object.codeSymbol.nativeSymbol);
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
18
|
const explicit = (node) => anchorTerms.some(term => `${node.id} ${node.label} ${node.codeSymbol?.nativeSymbol ?? ''}`.toLowerCase().includes(term));
|
|
@@ -36,19 +33,22 @@ export function impactRankingSignals(facts, anchorTerms) {
|
|
|
36
33
|
const relationshipOrder = (fact) => {
|
|
37
34
|
if (localCall(fact) || callsiteReferences.has(fact.id))
|
|
38
35
|
return 0;
|
|
39
|
-
if (fact.predicate === 'consumes' && fact.object.id.startsWith('symbol:scip:') &&
|
|
40
|
-
callableNativeSymbols.has(fact.object.label))
|
|
41
|
-
return 0;
|
|
42
36
|
if (fact.codeOccurrence?.role === 'reference') {
|
|
43
37
|
if (explicit(fact.object))
|
|
44
38
|
return 0;
|
|
45
39
|
const targetPaths = definitions.get(fact.object.id);
|
|
40
|
+
// A saved reference across repository files is useful without a native
|
|
41
|
+
// call proof. This only changes its query order, never its predicate.
|
|
46
42
|
if (targetPaths && [...targetPaths].some(path => path !== fact.provenance.path))
|
|
47
|
-
return
|
|
43
|
+
return 0;
|
|
48
44
|
}
|
|
49
45
|
if (fact.codeOccurrence || fact.codeCall)
|
|
50
46
|
return 2;
|
|
47
|
+
if (fact.predicate === 'contains' && (fact.subject.kind === 'Project' ||
|
|
48
|
+
fact.object.kind === 'CodeSymbol' || fact.object.id.startsWith('symbol:scip:')))
|
|
49
|
+
return 2;
|
|
51
50
|
return 0; // Keep intent, queue/data contracts, validation and legacy adapter relations available.
|
|
52
51
|
};
|
|
53
|
-
|
|
52
|
+
const contextOnly = (fact) => [fact.subject, fact.object].some(node => hub(node) || (node.id.startsWith('contract:module:') && !explicit(node)));
|
|
53
|
+
return { penalty, seedNodes, relationshipOrder, contextOnly };
|
|
54
54
|
}
|
package/dist/graph/query.js
CHANGED
|
@@ -203,7 +203,7 @@ export class SnapshotImpactQueryService {
|
|
|
203
203
|
tier.push(fact);
|
|
204
204
|
tiers.set(score, tier);
|
|
205
205
|
}
|
|
206
|
-
// Balance evidence lanes
|
|
206
|
+
// Balance evidence lanes within each ordering wave; retain the scorer's wave boundaries.
|
|
207
207
|
const allBalanced = [...tiers.values()].flatMap((tier) => [
|
|
208
208
|
...selectBalancedFacts(tier, tier.length),
|
|
209
209
|
...tier.filter((fact) => contextPackLaneForFact(fact) === undefined),
|
package/dist/graph/ranking.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { GraphFact } from '@weftlet/core';
|
|
2
2
|
/** Binds continuation offsets to the ordering policy; changing this requires restarting a scan. */
|
|
3
|
-
export declare const IMPACT_RANKING_VERSION = "
|
|
3
|
+
export declare const IMPACT_RANKING_VERSION = "shared-symbol-witnesses-v4";
|
|
4
4
|
export declare function factSearchText(fact: GraphFact): string;
|
|
5
5
|
export interface RankedImpactFact {
|
|
6
6
|
readonly fact: GraphFact;
|
package/dist/graph/ranking.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { stableJson } from '../infrastructure/util.js';
|
|
2
2
|
import { impactRankingSignals } from './impact-signals.js';
|
|
3
|
+
import { orderImpactRows } from './impact-order.js';
|
|
4
|
+
import { sharedSymbolAffinity } from './impact-affinity.js';
|
|
5
|
+
import { interleaveAffinity } from './impact-affinity-order.js';
|
|
3
6
|
/** Binds continuation offsets to the ordering policy; changing this requires restarting a scan. */
|
|
4
|
-
export const IMPACT_RANKING_VERSION = '
|
|
7
|
+
export const IMPACT_RANKING_VERSION = 'shared-symbol-witnesses-v4';
|
|
5
8
|
export function factSearchText(fact) {
|
|
6
9
|
return [fact.subject.id, fact.subject.label, fact.predicate, fact.object.id,
|
|
7
10
|
fact.object.label, fact.provenance.path].join(' ').toLowerCase();
|
|
@@ -69,25 +72,10 @@ export function rankImpactFacts(facts, direct, anchorTerms, focus) {
|
|
|
69
72
|
fact,
|
|
70
73
|
proximity: costs.get(fact) + (directIds.has(fact.id) ? 0 : Math.min((distance.get(fact.subject.id) ?? Infinity) + cost(fact, true), (distance.get(fact.object.id) ?? Infinity) + cost(fact, false))),
|
|
71
74
|
relationshipOrder: signals.relationshipOrder(fact),
|
|
75
|
+
contextOnly: signals.contextOnly(fact),
|
|
72
76
|
focus: focus.filter((term) => text.includes(term)).length,
|
|
73
77
|
tie: stableTie(fact),
|
|
74
78
|
};
|
|
75
79
|
});
|
|
76
|
-
|
|
77
|
-
const compare = (a, b) => a.relationshipOrder - b.relationshipOrder || b.focus - a.focus || lexical(a.tie, b.tie) || lexical(a.fact.id, b.fact.id);
|
|
78
|
-
// Allocate each source's early slots to relationships before local occurrence
|
|
79
|
-
// detail. Otherwise hundreds of nearer variable references consume a file's
|
|
80
|
-
// diversity allowance before its file-to-symbol consumer can be considered.
|
|
81
|
-
// Final ordering below still charges distance and shared-library penalties.
|
|
82
|
-
rows.sort((a, b) => costs.get(a.fact) - costs.get(b.fact) ||
|
|
83
|
-
a.relationshipOrder - b.relationshipOrder || a.proximity - b.proximity || compare(a, b));
|
|
84
|
-
const counts = new Map();
|
|
85
|
-
const diverse = rows.map((row) => {
|
|
86
|
-
const seen = counts.get(row.fact.provenance.path) ?? 0;
|
|
87
|
-
counts.set(row.fact.provenance.path, seen + 1);
|
|
88
|
-
// Each additional fact from one source costs one ordinary edge. Details stay on later pages.
|
|
89
|
-
return { ...row, score: row.proximity + seen };
|
|
90
|
-
});
|
|
91
|
-
return diverse.sort((a, b) => a.score - b.score || compare(a, b))
|
|
92
|
-
.map(({ fact, score }) => ({ fact, score }));
|
|
80
|
+
return interleaveAffinity(rows, orderImpactRows(rows), sharedSymbolAffinity(facts, direct, anchorTerms));
|
|
93
81
|
}
|
|
@@ -3,7 +3,7 @@ import { graphPredicates } from '@weftlet/core';
|
|
|
3
3
|
import { parseGraphPathsRequest } from '../graph/path-request.js';
|
|
4
4
|
export const graphPathsTool = {
|
|
5
5
|
name: 'graph.paths',
|
|
6
|
-
description: 'Explore simple cited paths between exact node IDs in an explicit saved snapshot.
|
|
6
|
+
description: 'Explore simple cited paths between exact node IDs in an explicit saved snapshot. Shortest-first traversal, prioritizing edges to the exact target; static paths are not proof of causality. Follow continuation with identical selectors; inspect depth/work limits and unexplored frontier. No hydration or model calls.',
|
|
7
7
|
inputSchema: {
|
|
8
8
|
type: 'object', additionalProperties: false, required: ['snapshotId', 'fromNodeId', 'toNodeId'],
|
|
9
9
|
properties: {
|
package/docs/1.0-WORK-QUEUE.md
CHANGED
|
@@ -28,6 +28,28 @@ known direct paths and specific worker `references`/`consumes` assertions were d
|
|
|
28
28
|
fixed retrieval budgets. [ADR 041](ADR-041-bounded-relationship-retrieval.md) tracks the bounded
|
|
29
29
|
query-order patch and its public regression; no extraction refresh is needed for that patch.
|
|
30
30
|
|
|
31
|
+
0.7.2 private replay: **direct-path acceptance is closed**; exact stored single-edge references
|
|
32
|
+
and calls surface at the original budget. Keep **one indirect-ranking ticket open**: retrieve
|
|
33
|
+
second-hop local references and legacy consumers without a saved call proof despite broad local
|
|
34
|
+
competition. Library share and file discovery improved, but neither private target reached the
|
|
35
|
+
first three 60-fact pages. Preserve extraction, authority, reachable totals and immutable evidence;
|
|
36
|
+
record public and private denominators separately. See the latest [validation record](VALIDATION.md).
|
|
37
|
+
The [ADR 042 patch](ADR-042-indirect-local-relationships.md) retrieves both exact public targets
|
|
38
|
+
on the first page with the same saved evidence; private acceptance still requires the original replay.
|
|
39
|
+
|
|
40
|
+
0.7.3 private replay: **the same ticket remains open**. Target ranks improve to 3,364 / 456 but
|
|
41
|
+
both still miss 180 slots. The failure is competition within populated distance/predicate groups;
|
|
42
|
+
file suggestion also regresses while library share remains 0/540. Before another ranking release,
|
|
43
|
+
freeze a public within-group competition case and inspect whether saved evidence distinguishes
|
|
44
|
+
the downstream targets from peers. Do not increase budgets, infer calls, or promise recall for
|
|
45
|
+
indistinguishable consumers. Details and preserved controls are in [VALIDATION.md](VALIDATION.md).
|
|
46
|
+
|
|
47
|
+
The next sanitized handoff identifies a selective exact-symbol witness for the two targets;
|
|
48
|
+
the sampled peers have file-scope approaches instead. [ADR 043](ADR-043-shared-symbol-specificity.md)
|
|
49
|
+
uses that saved structural evidence in a query-only candidate. The frozen within-group positive
|
|
50
|
+
case returns both targets at 141 / 10; a symmetric control still misses them, documenting the
|
|
51
|
+
limit rather than promising universal recall. Preserve the original private replay as the gate.
|
|
52
|
+
|
|
31
53
|
All three public 554,516-fact strict SHACL passes succeeded but took about 35–37 minutes each
|
|
32
54
|
in this cloud. This is a capacity result, not a 30-minute hydration qualification. B6b must record
|
|
33
55
|
full hydration and agent-query latency on the consumer workload as well as correctness.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# ADR 041: useful relationships within bounded retrieval
|
|
2
2
|
|
|
3
3
|
Status: query-only patch; validation and publication status are recorded in VALIDATION.md.
|
|
4
|
+
Impact ordering is superseded by [ADR 042](ADR-042-indirect-local-relationships.md); path ordering remains in effect.
|
|
4
5
|
|
|
5
6
|
## Evidence and scope
|
|
6
7
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ADR 042: indirect local relationships under broad anchors
|
|
2
|
+
|
|
3
|
+
Status: query-only patch. Qualification and consumer status are recorded in VALIDATION.md.
|
|
4
|
+
Supersedes ADR 041's impact ordering; its shortest-first path search is unchanged.
|
|
5
|
+
The 0.7.3 private replay still misses both targets at 180 facts; the public result below is not
|
|
6
|
+
private acceptance. Investigation now focuses on competition within the same groups.
|
|
7
|
+
|
|
8
|
+
## Evidence
|
|
9
|
+
|
|
10
|
+
The 0.7.2 private replay closes the direct-path report and confirms improved file discovery
|
|
11
|
+
and library share. It does **not** close relationship recall: two exact second-hop assertions
|
|
12
|
+
remain outside the original 180-fact budget despite zero hub penalty. One is a symbol reference
|
|
13
|
+
without a saved call proof; the other is a legacy file consumer. No extraction loss is reported.
|
|
14
|
+
|
|
15
|
+
The frozen [public regression](../evaluations/indirect-retrieval/protocol.md) independently
|
|
16
|
+
reproduces this shape with actual extraction, broad catalog anchors and 192 competing local
|
|
17
|
+
setup files. Both assertions exist and are reachable in round two. The baseline returns
|
|
18
|
+
178 provenance files but misses both assertions. Per-source diversity alone is insufficient.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
Use `local-relationship-waves-v3` ordering over the **unchanged reachable set**:
|
|
23
|
+
|
|
24
|
+
- Retain weighted graph distance and explicit-anchor exceptions from ADR 041. A saved cross-file
|
|
25
|
+
reference with an indexed definition receives the relationship category even without native
|
|
26
|
+
call proof. Do not change its predicate, confidence, authority or citations.
|
|
27
|
+
- Separate relationships by weighted distance (0, 1, 2, 3, 4–7, 8+) and predicate family
|
|
28
|
+
(`calls`, `references`, `consumes`, other). Each wave takes two assertions from each populated
|
|
29
|
+
code-relationship bucket and one from each other-relationship bucket. Distances 0–3 each get
|
|
30
|
+
turns so broad near-anchor competition cannot consume every turn before indirect evidence.
|
|
31
|
+
- Round-robin provenance sources **within** each bucket. Order each source by focus match,
|
|
32
|
+
distance and stable semantic tie key. Remove the unbounded per-source ordinal added to graph
|
|
33
|
+
distance in v2. All saved facts remain eligible, including repeated-source assertions.
|
|
34
|
+
- Local symbol occurrence/definition and project/file membership detail get a separate single
|
|
35
|
+
slot per wave. Unindexed-symbol and generic module-import context follow local evidence unless
|
|
36
|
+
explicitly queried. These are retrieval signals, not proof that an unindexed symbol is external.
|
|
37
|
+
- Give every assertion in a wave the same ordering score, then retain the query facade's existing
|
|
38
|
+
evidence-lane balancing. The score is now a wave number, not a distance-plus-diversity value.
|
|
39
|
+
Version continuation tokens and reject prior policies with instructions to restart the scan.
|
|
40
|
+
|
|
41
|
+
There are no consumer names, target IDs or corpus-specific file selectors in the scorer.
|
|
42
|
+
The fixed bucket proportions are a retrieval heuristic, not a universal relevance guarantee.
|
|
43
|
+
Larger or equally competitive buckets can still displace relevant assertions. Intent, contracts
|
|
44
|
+
and validation retain their evidence lanes; the returned page is not complete product coverage.
|
|
45
|
+
|
|
46
|
+
## Tradeoffs and boundaries
|
|
47
|
+
|
|
48
|
+
The same 9,038-fact saved public graph now returns the exact reference at position 17 and consumer
|
|
49
|
+
at 15. All 180 output slots remain in the denominator. Source diversity falls from 178 to 71 files;
|
|
50
|
+
that is an explicit tradeoff for relationship recall, not lost reachable evidence. Both facts
|
|
51
|
+
remain as originally stored, including omitted authority fields on the legacy consumer. The
|
|
52
|
+
reference still has no saved call proof; do not describe it as proven invocation or execution.
|
|
53
|
+
|
|
54
|
+
The existing 36-target call-backed regression and direct-path acceptance remain separate checks.
|
|
55
|
+
The private two-target ranking ticket stays open until the original queries are replayed at their
|
|
56
|
+
original budgets. Public success cannot establish the private result positions.
|
|
57
|
+
|
|
58
|
+
No extractor, assembly, semantic-cache, snapshot, persistence, RDF or strict-validation identity
|
|
59
|
+
changes. Existing snapshots can be queried immediately without hydration or model calls. The
|
|
60
|
+
0.7.1 capacity fix remains intact. The MCP path description is corrected to shortest-first with
|
|
61
|
+
exact-target priority, retaining its static-path/non-causality warning.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# ADR 043: shared-symbol specificity in bounded impact retrieval
|
|
2
|
+
|
|
3
|
+
Status: query-only candidate; private two-assertion acceptance remains open.
|
|
4
|
+
Extends [ADR 042](ADR-042-indirect-local-relationships.md). Does not change path search.
|
|
5
|
+
|
|
6
|
+
## Evidence
|
|
7
|
+
|
|
8
|
+
0.7.3 still misses the private unconfirmed symbol reference and legacy consumer at 180 facts.
|
|
9
|
+
The subsequent sanitized chains supply a more specific signal than distance/family alone:
|
|
10
|
+
producer → shared indexed symbol ← worker indexed symbol. The shared object has three distinct
|
|
11
|
+
production referrers. The preselected median peers instead approach through file-scope symbols.
|
|
12
|
+
A reverse reference is not a forward call. This is pairwise evidence, not a universal ranking proof.
|
|
13
|
+
|
|
14
|
+
The [frozen public comparison](../evaluations/same-group-retrieval/protocol.md) uses actual
|
|
15
|
+
SCIP extraction and strict publication. Both exact targets are reached in round two, inside the
|
|
16
|
+
same populated distance/predicate groups as competitors. The reference is seventh within its
|
|
17
|
+
source/group; the consumer is first. Baseline ranks are 3,074 and 323. A symmetric control gives
|
|
18
|
+
all 136 workers the same producer connection; it cannot establish a unique preferred worker.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
Use `shared-symbol-witnesses-v4` to interleave a structural specificity queue with the unchanged
|
|
23
|
+
v3 baseline waves, over the same reachable set. Each previously unreturned baseline assertion
|
|
24
|
+
gets one opportunity for an additional specificity selection. Deduplicate by assertion ID and
|
|
25
|
+
retain baseline wave boundaries for the existing evidence-lane balancing. With no usable
|
|
26
|
+
witnesses, baseline order and scores are exactly unchanged.
|
|
27
|
+
|
|
28
|
+
Eligible witnesses require saved SCIP named definitions for repository-scoped indexed symbols,
|
|
29
|
+
and exact typed reference endpoints in the same snapshot, commit and extractor version. A direct
|
|
30
|
+
matched definition supplies the producer origin. File-scope/module anchors, declaration-only or
|
|
31
|
+
unindexed targets, local ordinal identities and unknown adapters cannot supply this signal.
|
|
32
|
+
Rank shared objects by the number of distinct production indexed referrers in the reachable
|
|
33
|
+
query scope. Repeated occurrences, file-scope imports and test referrers do not inflate that
|
|
34
|
+
count. Test assertions remain available, including supported references in their existing
|
|
35
|
+
validation lane; this does not certify production implementation.
|
|
36
|
+
|
|
37
|
+
Within equal specificity, balance references and consumers and round-robin cited source files,
|
|
38
|
+
using existing focus/distance/stable ties. A later occurrence from a selectively related worker
|
|
39
|
+
can receive a turn before hundreds of unrelated local sources. Every assertion remains eligible;
|
|
40
|
+
this adds no fact, confidence, authority, approval, call proof or runtime claim.
|
|
41
|
+
|
|
42
|
+
A legacy file consumer receives corroborating priority only when its saved SCIP native symbol
|
|
43
|
+
key/hash matches a supported typed reference in the same cited file, digest, commit, snapshot and
|
|
44
|
+
extractor version. Its first citation may be an import before the later method reference. A shared
|
|
45
|
+
file/start line alone is insufficient. This projection-specific ordering does **not** assert a
|
|
46
|
+
native-to-legacy identity edge or make the file consumer an exact invocation. Unknown projections
|
|
47
|
+
retain baseline ordering. There are no consumer names or fixture target IDs in production code.
|
|
48
|
+
|
|
49
|
+
## Results, limits and compatibility
|
|
50
|
+
|
|
51
|
+
On the unchanged positive public graph, the reference/consumer move to **141 / 10**, within the
|
|
52
|
+
same 3 × 60 slots at depth three. All 6,417 reachable facts and stored bytes remain identical.
|
|
53
|
+
Source diversity falls from 63 to 38 files; this is an explicit relevance/diversity tradeoff.
|
|
54
|
+
Two of all 136 predeclared reference/consumer pairs are retrieved together; the denominator is
|
|
55
|
+
272 assertions, not just the chosen successful targets.
|
|
56
|
+
|
|
57
|
+
On the symmetric graph, the targets remain outside the budget at **2,892 / 309**. Zero of 136
|
|
58
|
+
pairs appear together. Specificity cannot distinguish equal witnesses, nor promise retrieval
|
|
59
|
+
of every relevant occurrence in large groups. The two-mode comparison is evidence for a bounded
|
|
60
|
+
heuristic, not private acceptance or a completeness guarantee. Degree is query-scope relative,
|
|
61
|
+
not a repository-global count or a confidence score.
|
|
62
|
+
|
|
63
|
+
Continuation tokens identify v4 and reject prior offsets with restart instructions. No extractor,
|
|
64
|
+
assembly, semantic-cache, snapshot, persistence, RDF, SHACL or core contract identity changes.
|
|
65
|
+
Existing evidence can be queried without hydration or model calls. The 0.7.1 capacity fix and
|
|
66
|
+
shortest-first direct paths remain untouched. Keep the private original queries, exact assertion
|
|
67
|
+
IDs, 180-result budget, reachable totals and stored fingerprints as the next acceptance check.
|
package/docs/RELEASING.md
CHANGED
|
@@ -202,14 +202,14 @@ Verify the matching public version in a fresh disposable project after publicati
|
|
|
202
202
|
|
|
203
203
|
```sh
|
|
204
204
|
npm init -y
|
|
205
|
-
npx --yes @weftlet/toolkit@0.7.
|
|
206
|
-
npx --yes @weftlet/toolkit@0.7.
|
|
205
|
+
npx --yes @weftlet/toolkit@0.7.4 init --yes
|
|
206
|
+
npx --yes @weftlet/toolkit@0.7.4 config validate
|
|
207
207
|
```
|
|
208
208
|
|
|
209
209
|
For local installation, use both prepared archives from the same reviewed release:
|
|
210
210
|
|
|
211
211
|
```sh
|
|
212
|
-
npm install --save-dev "/path/to/weftlet/.release/weftlet-core-0.7.
|
|
212
|
+
npm install --save-dev "/path/to/weftlet/.release/weftlet-core-0.7.4.tgz" "/path/to/weftlet/.release/weftlet-toolkit-0.7.4.tgz"
|
|
213
213
|
```
|
|
214
214
|
<!-- weftlet:release:end -->
|
|
215
215
|
|
package/docs/VALIDATION.md
CHANGED
|
@@ -1,5 +1,113 @@
|
|
|
1
1
|
# Validation evidence
|
|
2
2
|
|
|
3
|
+
## Shared-symbol candidate: public evidence, 2026-11-03
|
|
4
|
+
|
|
5
|
+
[ADR 043](ADR-043-shared-symbol-specificity.md) and its
|
|
6
|
+
[frozen within-group comparison](../evaluations/same-group-retrieval/README.md) address the
|
|
7
|
+
newly supplied chain evidence without changing extraction. On one unchanged 6,417-fact saved
|
|
8
|
+
graph, both round-two targets move from 3,074 / 323 to **141 / 10** at depth three / 3 × 60.
|
|
9
|
+
All reachable facts, exact citations/authority and four stored files remain unchanged. Source
|
|
10
|
+
diversity changes from 63 to 38; two of all 136 predeclared pairs are returned together.
|
|
11
|
+
The symmetric control still misses the targets at 2,892 / 309 and retrieves zero complete pairs.
|
|
12
|
+
That negative result is retained; equal witnesses cannot guarantee universal bounded recall.
|
|
13
|
+
|
|
14
|
+
The built CLI and a fresh stdio MCP server reproduce all 180 candidate IDs in order across
|
|
15
|
+
three pages, including both exact stored target facts. No hydration/model calls or saved-byte
|
|
16
|
+
changes occur during that comparison. Focused ranking, projection-mismatch, strict hydration,
|
|
17
|
+
repeat, prior relationship and shortest-first path checks pass; full release qualification is
|
|
18
|
+
recorded separately on the change PR. Repeat hydration creates a fresh identical RDF work file
|
|
19
|
+
while preserving every prior evidence file. **The private two-assertion ticket remains open.**
|
|
20
|
+
|
|
21
|
+
## 0.7.3 private replay: same indirect-ranking ticket remains open, 2026-11-03
|
|
22
|
+
|
|
23
|
+
Maintainer-reported, sanitized evidence. At the original depth three / three pages of 60,
|
|
24
|
+
the same `references` and `consumes` assertions improve from ranks 23,052 / 3,447 in 0.7.2
|
|
25
|
+
to **3,364 / 456** in 0.7.3 (calculated pages 57 / 8). Both still miss the predeclared 180
|
|
26
|
+
slots. This is the same ranking ticket, not another extraction, scale or authority failure.
|
|
27
|
+
The earlier public two-target pass does not transfer to this larger graph.
|
|
28
|
+
|
|
29
|
+
Snapshot digest remains `53e2e3a024125389685d5bc9183f428b4e749dc7021df87623f54e32af4c7f56`;
|
|
30
|
+
360 direct matches and 420,837 reachable facts are unchanged. Both targets are reached in
|
|
31
|
+
round two. The released ranker reproduces all 180 CLI fact IDs in order; later ranks were
|
|
32
|
+
calculated without fetching additional pages. Neither target has a hub penalty or belongs
|
|
33
|
+
to the context-only phase. No saved call in the reachable set cites the reference as target proof.
|
|
34
|
+
|
|
35
|
+
| Target group | Assertions / sources | Target group rank / source ordinal | Slots in first 180 | Target wave |
|
|
36
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
37
|
+
| Weighted distance 2, `references` | 1,038 / 136 | 284 / 5 | 10 | 141 |
|
|
38
|
+
| Weighted distance 3, `consumes` | 284 / 47 | 29 / 1 | 10 | 14 |
|
|
39
|
+
|
|
40
|
+
These measurements locate displacement within populated groups and their turn allocation;
|
|
41
|
+
they do not establish a universally better weighting scheme. Across the three original cases,
|
|
42
|
+
page-one expected file suggestion/citation changes from 12/12 / 12/12 in 0.7.2 to 10/12 / 11/12;
|
|
43
|
+
through three pages it is 11/12 / 12/12. Library-target references remain 0/540 and no named
|
|
44
|
+
noise control appears. The PM first 180 cover 63 provenance sources, versus 158 previously.
|
|
45
|
+
File discovery and source diversity are separate measurements from exact relationship recall.
|
|
46
|
+
|
|
47
|
+
The direct-path report stays closed: both exact one-hop assertions retain their predicates,
|
|
48
|
+
citations and original 1,000-step budget. The MCP description is now accurate. Fresh MCP agrees
|
|
49
|
+
with the released CLI; all 35 consumer checks and all 20 stored-byte fingerprints pass, without
|
|
50
|
+
private rehydration or model calls. The 0.7.1 capacity fix remains accepted for the reported run.
|
|
51
|
+
|
|
52
|
+
The subsequent saved-chain handoff distinguishes the targets from preselected median peers.
|
|
53
|
+
Both targets have a producer → indexed shared symbol ← indexed worker witness, with **three
|
|
54
|
+
distinct production referrer symbols**. The reference's sampled peer instead approaches through
|
|
55
|
+
file-scope symbols; the consumer's peer also has no such shared-symbol witness. Peers were chosen
|
|
56
|
+
as the fifth of ten first-page results in their respective groups, before comparing paths.
|
|
57
|
+
This establishes pairwise structural specificity, not a universally optimal ranking or execution.
|
|
58
|
+
There is no all-forward indexed reference/call path within four approach hops in that diagnostic.
|
|
59
|
+
|
|
60
|
+
Native and legacy projections have no explicit identity edge. Matching usage/definition file and
|
|
61
|
+
start line are corroboration only; they must not become graph joins or fabricated calls. The
|
|
62
|
+
stored-byte SHA-256 in the handoff is
|
|
63
|
+
`e92e3c2c4b6ec65a4767fe20697657cd566c15fbdff9732af58999d8ff09554d`.
|
|
64
|
+
The query-only candidate and its positive/ambiguous public controls are tracked in
|
|
65
|
+
[ADR 043](ADR-043-shared-symbol-specificity.md). **Private acceptance remains open.**
|
|
66
|
+
|
|
67
|
+
## Indirect-ranking patch: public comparison, 2026-11-03
|
|
68
|
+
|
|
69
|
+
[ADR 042](ADR-042-indirect-local-relationships.md) and the
|
|
70
|
+
[frozen comparison](../evaluations/indirect-retrieval/README.md) cover the remaining query-only
|
|
71
|
+
ranking change. Actual TypeScript extraction produces both second-hop assertions without a
|
|
72
|
+
saved call proof for the reference. At unchanged depth three / 3 × 60 facts, their ranks move
|
|
73
|
+
from 3,704 / 1,760 to **17 / 15**. The 9,038 stored/reachable assertions and 150 direct matches
|
|
74
|
+
are unchanged. All 180 results remain in the denominator; source diversity changes from 178
|
|
75
|
+
to 71 files. The four saved evidence files retain exact bytes during the comparison. No private
|
|
76
|
+
source, semantic/model calls, new extraction or larger query budgets enter that comparison.
|
|
77
|
+
|
|
78
|
+
The prior 36-target call-backed regression and direct-path/exhaustive-oracle/viewer parity tests
|
|
79
|
+
pass locally. The MCP path description now reflects shortest-first traversal with exact-target
|
|
80
|
+
priority and retains the warning that static paths are not causality. Full release qualification
|
|
81
|
+
is recorded separately on the change PR. **Private two-target ranking acceptance stays open.**
|
|
82
|
+
Existing snapshots and caches need no refresh; impact continuation must restart after upgrading.
|
|
83
|
+
|
|
84
|
+
## 0.7.2 private replay: paths closed, indirect ranking open, 2026-11-03
|
|
85
|
+
|
|
86
|
+
The maintainer reports that the same saved endpoints now return two exact stored single-edge
|
|
87
|
+
paths (`references` and `calls`) on page one at outgoing depth three / 1,000 steps. The CLI and
|
|
88
|
+
fresh stdio MCP agree, and truncation/continuation remains truthful. **Close the direct-path
|
|
89
|
+
consumer report.** This establishes cited static paths, not exhaustive discovery or runtime causality.
|
|
90
|
+
|
|
91
|
+
The remaining ranking ticket concerns second-hop local `references` and legacy file `consumes`
|
|
92
|
+
assertions without a saved call whose target-occurrence proof cites the reference. Both still
|
|
93
|
+
exist and are reachable in expansion round two. Neither receives the unindexed-symbol penalty.
|
|
94
|
+
Do not invent a `calls` fact or change extraction to address this query-order finding.
|
|
95
|
+
|
|
96
|
+
On unchanged private inputs and budgets, 0.7.1 → 0.7.2 improves page-one file suggestion/source
|
|
97
|
+
citation from 11/12 to 12/12 and library assertions from 378/540 to 0/540. The two target
|
|
98
|
+
assertions remain absent from the first 180 PM results. Their calculated ranks improve from
|
|
99
|
+
24,801 / 69,207 to 23,052 / 3,447; v2 scores are 34 / 9, relationship-order values 1 / 0, and
|
|
100
|
+
22,791 / 3,295 assertions have strictly lower scores. The first 180 cover 158 provenance files.
|
|
101
|
+
These measurements implicate the interaction of distance, relationship category and source
|
|
102
|
+
diversity under broad local competition; they do not identify one scoring term as the sole cause.
|
|
103
|
+
|
|
104
|
+
The consumer reproduced all 180 CLI fact IDs in order, with 360 direct matches and 420,837
|
|
105
|
+
reachable assertions. All three queries retain their prior reachable counts. All 20 stored-file
|
|
106
|
+
fingerprints match; no rehydration, model calls or extra pages were used. This private two-target
|
|
107
|
+
case is distinct from the earlier public 36-relationship denominator, which remains valid for its
|
|
108
|
+
call-backed fixture. A new public regression must include broad anchors, many local competitor
|
|
109
|
+
files, and both exact second-hop assertions without call proof at depth three / 3 × 60 facts.
|
|
110
|
+
|
|
3
111
|
## 0.7.1 consumer follow-up and bounded retrieval patch, 2026-11-03
|
|
4
112
|
|
|
5
113
|
**Maintainer-reported consumer pass:** the published 0.7.1 toolkit hydrates, strictly validates,
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"destinationPath": "package.json",
|
|
109
109
|
"sourceSha256": "a575dbb2fed8c554b95e8e9b3e03a0c21cab1be7249860bbebc18e046b2ff78d",
|
|
110
110
|
"modified": true,
|
|
111
|
-
"destinationSha256": "
|
|
111
|
+
"destinationSha256": "e7287cbf5f7182b8c16c073dcc097a26616ae85868a518fc8e8966a8072b83ae"
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
"sourcePath": "tools/context-graph/packages/core/LICENSE",
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"destinationPath": "packages/core/README.md",
|
|
130
130
|
"sourceSha256": "6044bf7b9a3dc793f41e2ff048546ed11938957d10a5b143eb1739ac4cd7477c",
|
|
131
131
|
"modified": true,
|
|
132
|
-
"destinationSha256": "
|
|
132
|
+
"destinationSha256": "cd458ed031e0109f5b8455cc5e0d948a42a64f7e4d67f79b12e28a1450c7be51"
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"sourcePath": "tools/context-graph/packages/core/THIRD_PARTY_NOTICES.md",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"destinationPath": "packages/core/package.json",
|
|
144
144
|
"sourceSha256": "2a29066eb8452f22fafcb06bb035a84dda93c5705dba32aba600a1b7e5aaf19c",
|
|
145
145
|
"modified": true,
|
|
146
|
-
"destinationSha256": "
|
|
146
|
+
"destinationSha256": "825072a8f61911034ae7784935b66368eb0c945c19d68bad8fb6acecc6f5981b"
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
149
|
"sourcePath": "tools/context-graph/packages/core/src/core.ts",
|
|
@@ -388,14 +388,14 @@
|
|
|
388
388
|
"destinationPath": "src/graph/query.test.ts",
|
|
389
389
|
"sourceSha256": "ba07c52566da979fcbb451b0c0c99b466521993cb49415ff8b35df2b01d4ecaf",
|
|
390
390
|
"modified": true,
|
|
391
|
-
"destinationSha256": "
|
|
391
|
+
"destinationSha256": "11b59d7e1936c66cf453136dc01908e3c19287926a29e881effd7b631cce518c"
|
|
392
392
|
},
|
|
393
393
|
{
|
|
394
394
|
"sourcePath": "tools/context-graph/src/query.ts",
|
|
395
395
|
"destinationPath": "src/graph/query.ts",
|
|
396
396
|
"sourceSha256": "4d67f87ca660cefe9686fb5b775f86a997a25d539819ea81d321c8bd1e0aaa39",
|
|
397
397
|
"modified": true,
|
|
398
|
-
"destinationSha256": "
|
|
398
|
+
"destinationSha256": "8c13b32499f0b830cd02bfc281c37c32fd6387aa3b98a325e6a60d3706b64528"
|
|
399
399
|
},
|
|
400
400
|
{
|
|
401
401
|
"sourcePath": "tools/context-graph/src/request.test.ts",
|
|
@@ -635,6 +635,8 @@
|
|
|
635
635
|
"docs/ADR-039-mapping-proposals.md",
|
|
636
636
|
"docs/ADR-040-bounded-graph-io.md",
|
|
637
637
|
"docs/ADR-041-bounded-relationship-retrieval.md",
|
|
638
|
+
"docs/ADR-042-indirect-local-relationships.md",
|
|
639
|
+
"docs/ADR-043-shared-symbol-specificity.md",
|
|
638
640
|
"docs/CLOUD-ENVIRONMENT.md",
|
|
639
641
|
"docs/CONSUMER-TRIAL.md",
|
|
640
642
|
"docs/ENVIRONMENT-VALIDATION.md",
|
|
@@ -748,6 +750,10 @@
|
|
|
748
750
|
"evaluations/dispatch/routing-baseline.json",
|
|
749
751
|
"evaluations/dispatch/routing-protocol.md",
|
|
750
752
|
"evaluations/dispatch/routing.config.json",
|
|
753
|
+
"evaluations/indirect-retrieval/README.md",
|
|
754
|
+
"evaluations/indirect-retrieval/baseline.json",
|
|
755
|
+
"evaluations/indirect-retrieval/candidate.json",
|
|
756
|
+
"evaluations/indirect-retrieval/protocol.md",
|
|
751
757
|
"evaluations/mapping-gaps/README.md",
|
|
752
758
|
"evaluations/mapping-gaps/inputs.lock.json",
|
|
753
759
|
"evaluations/mapping-gaps/project/.gitattributes",
|
|
@@ -784,6 +790,13 @@
|
|
|
784
790
|
"evaluations/relationship-retrieval/baseline.json",
|
|
785
791
|
"evaluations/relationship-retrieval/candidate.json",
|
|
786
792
|
"evaluations/relationship-retrieval/protocol.md",
|
|
793
|
+
"evaluations/same-group-retrieval/README.md",
|
|
794
|
+
"evaluations/same-group-retrieval/prior-36-control.json",
|
|
795
|
+
"evaluations/same-group-retrieval/protocol.md",
|
|
796
|
+
"evaluations/same-group-retrieval/v3-specific.json",
|
|
797
|
+
"evaluations/same-group-retrieval/v3-symmetric.json",
|
|
798
|
+
"evaluations/same-group-retrieval/v4-specific.json",
|
|
799
|
+
"evaluations/same-group-retrieval/v4-symmetric.json",
|
|
787
800
|
"evaluations/static-calls/README.md",
|
|
788
801
|
"evaluations/static-calls/results/2026-10-29.json",
|
|
789
802
|
"evaluations/weftlet/baseline.json",
|
|
@@ -872,6 +885,8 @@
|
|
|
872
885
|
"scripts/evaluation/score.ts",
|
|
873
886
|
"scripts/git-content.d.mts",
|
|
874
887
|
"scripts/git-content.mjs",
|
|
888
|
+
"scripts/indirect-retrieval-study/measure.ts",
|
|
889
|
+
"scripts/indirect-retrieval-study/run.ts",
|
|
875
890
|
"scripts/inventory.test.ts",
|
|
876
891
|
"scripts/large-graph/lifecycle.ts",
|
|
877
892
|
"scripts/large-graph/run.ts",
|
|
@@ -901,6 +916,12 @@
|
|
|
901
916
|
"scripts/restore-publication-state.mjs",
|
|
902
917
|
"scripts/retrieval-study/preservation.ts",
|
|
903
918
|
"scripts/retrieval-study/run.ts",
|
|
919
|
+
"scripts/same-group-study/entrypoints.ts",
|
|
920
|
+
"scripts/same-group-study/measure.ts",
|
|
921
|
+
"scripts/same-group-study/run.ts",
|
|
922
|
+
"scripts/same-group-study/v3-order.ts",
|
|
923
|
+
"scripts/same-group-study/v3-rows.ts",
|
|
924
|
+
"scripts/same-group-study/v3-signals.ts",
|
|
904
925
|
"scripts/smoke-mapping-proposals.mjs",
|
|
905
926
|
"scripts/smoke-traversal.mjs",
|
|
906
927
|
"scripts/update-inventory.mjs",
|
|
@@ -1019,6 +1040,11 @@
|
|
|
1019
1040
|
"src/graph/evidence-description.ts",
|
|
1020
1041
|
"src/graph/identity-preflight.test.ts",
|
|
1021
1042
|
"src/graph/identity-preflight.ts",
|
|
1043
|
+
"src/graph/impact-affinity-order.ts",
|
|
1044
|
+
"src/graph/impact-affinity.test.ts",
|
|
1045
|
+
"src/graph/impact-affinity.ts",
|
|
1046
|
+
"src/graph/impact-order.test.ts",
|
|
1047
|
+
"src/graph/impact-order.ts",
|
|
1022
1048
|
"src/graph/impact-signals.ts",
|
|
1023
1049
|
"src/graph/neighborhood-renderer.ts",
|
|
1024
1050
|
"src/graph/neighborhood-request.ts",
|
|
@@ -1098,12 +1124,14 @@
|
|
|
1098
1124
|
"tests/fixtures/code-evidence-project.ts",
|
|
1099
1125
|
"tests/fixtures/compatibility-cases.ts",
|
|
1100
1126
|
"tests/fixtures/compatibility-project.ts",
|
|
1127
|
+
"tests/fixtures/indirect-retrieval-project.ts",
|
|
1101
1128
|
"tests/fixtures/large-typescript-project.ts",
|
|
1102
1129
|
"tests/fixtures/mapping-project.ts",
|
|
1103
1130
|
"tests/fixtures/monorepo-project.ts",
|
|
1104
1131
|
"tests/fixtures/obligation-project.ts",
|
|
1105
1132
|
"tests/fixtures/rdf-070-reference.ts",
|
|
1106
1133
|
"tests/fixtures/relationship-retrieval-project.ts",
|
|
1134
|
+
"tests/fixtures/same-group-project.ts",
|
|
1107
1135
|
"tests/fixtures/shacl.ts",
|
|
1108
1136
|
"tests/fixtures/static-call-project.ts",
|
|
1109
1137
|
"tests/fixtures/symbol-bindings.ts",
|
|
@@ -1112,6 +1140,7 @@
|
|
|
1112
1140
|
"tests/hydration-plan.test.ts",
|
|
1113
1141
|
"tests/identity-preflight.test.ts",
|
|
1114
1142
|
"tests/implementation-coverage.test.ts",
|
|
1143
|
+
"tests/indirect-retrieval.test.ts",
|
|
1115
1144
|
"tests/large-graph-lifecycle.test.ts",
|
|
1116
1145
|
"tests/mapping-proposals.test.ts",
|
|
1117
1146
|
"tests/module-boundaries.test.ts",
|
|
@@ -1123,6 +1152,7 @@
|
|
|
1123
1152
|
"tests/relationship-retrieval.test.ts",
|
|
1124
1153
|
"tests/routing.test.ts",
|
|
1125
1154
|
"tests/runtime.test.ts",
|
|
1155
|
+
"tests/same-group-retrieval.test.ts",
|
|
1126
1156
|
"tests/semantic-cache-reuse.test.ts",
|
|
1127
1157
|
"tests/semantic-identity.test.ts",
|
|
1128
1158
|
"tests/shacl-validation.test.ts",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weftlet/toolkit",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "Evidence-backed context graphs for AI development agents: product intent, contracts, code, tests and runtime behavior",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"rdf-validate-shacl": "0.6.5",
|
|
67
67
|
"sparqljs": "3.7.4",
|
|
68
68
|
"typescript": "5.5.4",
|
|
69
|
-
"@weftlet/core": "^0.7.
|
|
69
|
+
"@weftlet/core": "^0.7.4"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@changesets/cli": "3.0.3",
|
|
@@ -131,6 +131,8 @@
|
|
|
131
131
|
"benchmark:validation": "node scripts/validation-benchmark/run.mjs",
|
|
132
132
|
"evaluate:mappings": "tsx scripts/mapping-study/run.ts",
|
|
133
133
|
"evaluate:calls": "tsx scripts/call-study/run.ts",
|
|
134
|
-
"evaluate:proposals": "tsx scripts/mapping-proposal-study/run.ts"
|
|
134
|
+
"evaluate:proposals": "tsx scripts/mapping-proposal-study/run.ts",
|
|
135
|
+
"evaluate:indirect": "tsx scripts/indirect-retrieval-study/run.ts",
|
|
136
|
+
"evaluate:same-group": "tsx scripts/same-group-study/run.ts"
|
|
135
137
|
}
|
|
136
138
|
}
|