@weftlet/toolkit 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -3
- package/THIRD_PARTY_NOTICES.md +33 -0
- package/dist/application/hydrator.js +21 -6
- package/dist/application/overlay.js +46 -9
- package/dist/application/saved-evidence.d.ts +1 -0
- package/dist/configuration/config.d.ts +4 -0
- package/dist/configuration/config.js +12 -0
- package/dist/configuration/scip-project.d.ts +9 -0
- package/dist/configuration/scip-project.js +40 -0
- package/dist/extraction/codex-auth.js +9 -2
- package/dist/extraction/codex-command.d.ts +8 -0
- package/dist/extraction/codex-command.js +81 -0
- package/dist/extraction/document-references.d.ts +18 -0
- package/dist/extraction/document-references.js +94 -0
- package/dist/extraction/document.d.ts +9 -2
- package/dist/extraction/document.js +40 -47
- package/dist/extraction/module-request.d.ts +3 -0
- package/dist/extraction/module-request.js +25 -0
- package/dist/extraction/repository-nodes.d.ts +7 -0
- package/dist/extraction/repository-nodes.js +20 -0
- package/dist/extraction/routing.d.ts +14 -0
- package/dist/extraction/routing.js +151 -0
- package/dist/extraction/scip-project.d.ts +3 -0
- package/dist/extraction/scip-project.js +33 -0
- package/dist/extraction/semantic-process.js +17 -5
- package/dist/extraction/semantic.js +32 -5
- package/dist/extraction/static.d.ts +7 -3
- package/dist/extraction/static.js +15 -9
- package/dist/graph/query.js +21 -34
- package/dist/graph/ranking.d.ts +10 -0
- package/dist/graph/ranking.js +85 -0
- package/dist/interfaces/doctor.js +12 -3
- package/docs/ADR-002-saved-evidence.md +7 -1
- package/docs/ADR-003-draft-overlays.md +6 -0
- package/docs/ADR-004-rdf-query-path.md +6 -0
- package/docs/ADR-006-provider-boundaries.md +5 -0
- package/docs/ADR-010-committed-source-indexing.md +77 -0
- package/docs/ADR-011-explicit-document-links.md +48 -0
- package/docs/ADR-012-impact-ranking.md +50 -0
- package/docs/ADR-013-relative-module-requests.md +58 -0
- package/docs/ADR-014-routing-declarations.md +108 -0
- package/docs/ADR-015-repository-document-identity.md +57 -0
- package/docs/CONSUMER-TRIAL.md +166 -0
- package/docs/ENVIRONMENT-VALIDATION.md +9 -0
- package/docs/IMPACT-EVALUATION.md +363 -0
- package/docs/PROVIDERS.md +30 -2
- package/docs/RELEASE-1.0-PLAN.md +221 -0
- package/docs/RELEASING.md +16 -6
- package/docs/ROADMAP.md +53 -5
- package/docs/SOURCE-INVENTORY.md +10 -0
- package/docs/VALIDATION.md +438 -0
- package/docs/VIEWER.md +5 -0
- package/docs/WEFTLET-2.0-PLAN.md +282 -0
- package/docs/extraction-manifest.json +94 -17
- package/package.json +5 -2
|
@@ -5,6 +5,10 @@ import { copyFile, mkdir, readFile, rename, stat, writeFile } from 'node:fs/prom
|
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
import { dirname, join } from 'node:path';
|
|
7
7
|
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import { GitRepository } from "../repository/git.js";
|
|
9
|
+
import { assertScipSourceProject, assertSourceProjectInvocation } from '../configuration/scip-project.js';
|
|
10
|
+
import { prepareScipSourceProject } from './scip-project.js';
|
|
11
|
+
import { moduleRequestNode } from './module-request.js';
|
|
8
12
|
import { matchesRepositoryGlob } from "../repository/glob.js";
|
|
9
13
|
import { runProcess } from "../infrastructure/process.js";
|
|
10
14
|
import { sha256, stableJson } from "../infrastructure/util.js";
|
|
@@ -16,6 +20,10 @@ export class ScipTypeScriptIndexer {
|
|
|
16
20
|
this.storageRoot = storageRoot;
|
|
17
21
|
}
|
|
18
22
|
async index(context) {
|
|
23
|
+
if (this.config.sourceProject !== undefined) {
|
|
24
|
+
assertScipSourceProject(this.config.sourceProject);
|
|
25
|
+
assertSourceProjectInvocation(this.config.command, this.config.args);
|
|
26
|
+
}
|
|
19
27
|
const require = createRequire(import.meta.url);
|
|
20
28
|
const destination = join(this.storageRoot, 'scip', `${context.snapshotId}.scip`);
|
|
21
29
|
await mkdir(join(this.storageRoot, 'scip'), { recursive: true });
|
|
@@ -30,7 +38,8 @@ export class ScipTypeScriptIndexer {
|
|
|
30
38
|
]
|
|
31
39
|
: this.config.args;
|
|
32
40
|
await withCommittedTree(context.repositoryRoot, context.commitSha, async (worktreeRoot) => {
|
|
33
|
-
await
|
|
41
|
+
const project = this.config.sourceProject ? await prepareScipSourceProject(worktreeRoot, this.config.sourceProject, await new GitRepository(context.repositoryRoot).listAllFilesAt(context.commitSha)) : undefined;
|
|
42
|
+
await runProcess(command, project ? [...args, project] : args, worktreeRoot, {
|
|
34
43
|
...process.env,
|
|
35
44
|
NODE_OPTIONS: process.env.NODE_OPTIONS ?? '--max-old-space-size=8192',
|
|
36
45
|
});
|
|
@@ -161,11 +170,12 @@ export class ScipSymbolExtractor {
|
|
|
161
170
|
return facts;
|
|
162
171
|
}
|
|
163
172
|
}
|
|
173
|
+
export const repositoryFileExtractorIdentity = { id: 'repository-file-extractor', version: '2.0.0' };
|
|
164
174
|
export class RepositoryFileExtractor {
|
|
165
175
|
repository;
|
|
166
176
|
roots;
|
|
167
177
|
scipDigest;
|
|
168
|
-
identity =
|
|
178
|
+
identity = repositoryFileExtractorIdentity;
|
|
169
179
|
constructor(repository, roots, scipDigest) {
|
|
170
180
|
this.repository = repository;
|
|
171
181
|
this.roots = roots;
|
|
@@ -198,7 +208,7 @@ export class RepositoryFileExtractor {
|
|
|
198
208
|
commitSha: context.commitSha,
|
|
199
209
|
};
|
|
200
210
|
facts.push({
|
|
201
|
-
id: sha256(stableJson({ project, component, provenance })),
|
|
211
|
+
id: sha256(stableJson({ project, component, provenance, extractor: this.identity })),
|
|
202
212
|
subject: project,
|
|
203
213
|
predicate: 'contains',
|
|
204
214
|
object: component,
|
|
@@ -212,14 +222,10 @@ export class RepositoryFileExtractor {
|
|
|
212
222
|
const imported = line.match(/(?:from\s+|import\s*\()['"]([^'"]+)['"]/)?.[1];
|
|
213
223
|
if (!imported)
|
|
214
224
|
continue;
|
|
215
|
-
const contract =
|
|
216
|
-
id: `contract:module:${imported}`,
|
|
217
|
-
kind: 'Contract',
|
|
218
|
-
label: imported,
|
|
219
|
-
};
|
|
225
|
+
const contract = moduleRequestNode(path, imported);
|
|
220
226
|
const importProvenance = { ...provenance, startLine: index + 1, endLine: index + 1 };
|
|
221
227
|
facts.push({
|
|
222
|
-
id: sha256(stableJson({ component,
|
|
228
|
+
id: sha256(stableJson({ component, module: contract.id, importProvenance, extractor: this.identity })),
|
|
223
229
|
subject: component,
|
|
224
230
|
predicate: 'consumes',
|
|
225
231
|
object: contract,
|
package/dist/graph/query.js
CHANGED
|
@@ -3,6 +3,7 @@ import { contextPackEvidenceAuthority } from "./authority.js";
|
|
|
3
3
|
import { RdfEvidenceGraph } from "./rdf.js";
|
|
4
4
|
import { sha256, stableJson } from "../infrastructure/util.js";
|
|
5
5
|
import { diagnosticSummary, selectDiagnostics } from "./diagnostics.js";
|
|
6
|
+
import { factSearchText, IMPACT_RANKING_VERSION, rankImpactFacts } from './ranking.js';
|
|
6
7
|
export const DEFAULT_CONTEXT_PACK_FACTS = 60;
|
|
7
8
|
export const MAX_CONTEXT_PACK_FACTS = 200;
|
|
8
9
|
const contextPackLanes = [
|
|
@@ -90,21 +91,6 @@ function focusTerms(values) {
|
|
|
90
91
|
.filter((value) => value.length >= 4 && !searchStopWords.has(value))),
|
|
91
92
|
];
|
|
92
93
|
}
|
|
93
|
-
function factSearchText(fact) {
|
|
94
|
-
return [
|
|
95
|
-
fact.subject.id,
|
|
96
|
-
fact.subject.label,
|
|
97
|
-
fact.predicate,
|
|
98
|
-
fact.object.id,
|
|
99
|
-
fact.object.label,
|
|
100
|
-
fact.provenance.path,
|
|
101
|
-
]
|
|
102
|
-
.join(' ')
|
|
103
|
-
.toLowerCase();
|
|
104
|
-
}
|
|
105
|
-
function semanticPriority(fact) {
|
|
106
|
-
return /codex-cli|document-linker/.test(fact.extractor.id) ? 0 : 1;
|
|
107
|
-
}
|
|
108
94
|
function boundedFactLimit(value) {
|
|
109
95
|
if (value === undefined)
|
|
110
96
|
return DEFAULT_CONTEXT_PACK_FACTS;
|
|
@@ -121,6 +107,7 @@ export class MarkdownContextPackRenderer {
|
|
|
121
107
|
`Snapshot: \`${pack.snapshot.snapshotId}\` (${pack.snapshot.commitSha})`,
|
|
122
108
|
`Configuration: ${pack.snapshot.configurationDigest ?? 'legacy/unknown'}; scope: ${pack.snapshot.scope?.join(', ') ?? 'legacy/unknown'}`,
|
|
123
109
|
...(pack.exploration ? [
|
|
110
|
+
`Ranking: ${pack.exploration.rankingVersion ?? 'legacy/unknown'}`,
|
|
124
111
|
`Relationships returned: ${pack.exploration.returned}/${pack.exploration.reachableFacts}; truncated: ${pack.exploration.truncated}; depth limit reached: ${pack.exploration.depthLimitReached}`,
|
|
125
112
|
...(pack.exploration.continuation ? [`Continuation: ${pack.exploration.continuation}`] : []),
|
|
126
113
|
] : []),
|
|
@@ -211,7 +198,6 @@ export class SnapshotImpactQueryService {
|
|
|
211
198
|
const directTerms = anchorTerms.length > 0 ? anchorTerms : focus;
|
|
212
199
|
const direct = snapshot.facts.filter((fact) => this.matches(fact, directTerms));
|
|
213
200
|
const ranked = new Map(direct.map((fact) => [fact.id, fact]));
|
|
214
|
-
const distance = new Map(direct.map((fact) => [fact.id, 0]));
|
|
215
201
|
let frontier = new Set(direct.flatMap((fact) => [fact.subject, fact.object]
|
|
216
202
|
.filter((node) => node.kind !== 'Project')
|
|
217
203
|
.map((node) => node.id)));
|
|
@@ -222,8 +208,6 @@ export class SnapshotImpactQueryService {
|
|
|
222
208
|
if (!frontier.has(fact.subject.id) && !frontier.has(fact.object.id))
|
|
223
209
|
continue;
|
|
224
210
|
ranked.set(fact.id, fact);
|
|
225
|
-
if (!distance.has(fact.id))
|
|
226
|
-
distance.set(fact.id, depth + 1);
|
|
227
211
|
for (const node of [fact.subject, fact.object]) {
|
|
228
212
|
if (node.kind !== 'Project' && !visitedNodes.has(node.id))
|
|
229
213
|
next.add(node.id);
|
|
@@ -233,27 +217,29 @@ export class SnapshotImpactQueryService {
|
|
|
233
217
|
visitedNodes.add(node);
|
|
234
218
|
frontier = next;
|
|
235
219
|
}
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
...selectBalancedFacts(ordered, ordered.length).facts,
|
|
248
|
-
...ordered.filter((fact) => contextPackLaneForFact(fact) === undefined),
|
|
249
|
-
];
|
|
220
|
+
const tiers = new Map();
|
|
221
|
+
for (const { fact, score } of rankImpactFacts([...ranked.values()], direct, directTerms, focus)) {
|
|
222
|
+
const tier = tiers.get(score) ?? [];
|
|
223
|
+
tier.push(fact);
|
|
224
|
+
tiers.set(score, tier);
|
|
225
|
+
}
|
|
226
|
+
// Balance evidence lanes among similarly ranked facts, not across unrelated distance tiers.
|
|
227
|
+
const allBalanced = [...tiers.values()].flatMap((tier) => [
|
|
228
|
+
...selectBalancedFacts(tier, tier.length).facts,
|
|
229
|
+
...tier.filter((fact) => contextPackLaneForFact(fact) === undefined),
|
|
230
|
+
]);
|
|
250
231
|
const { continuation: _continuation, snapshotId: _snapshotId, ...query } = request;
|
|
251
|
-
const queryDigest = sha256(stableJson(query));
|
|
232
|
+
const queryDigest = sha256(stableJson({ ranking: IMPACT_RANKING_VERSION, query }));
|
|
252
233
|
let offset = 0;
|
|
253
234
|
if (request.continuation) {
|
|
254
235
|
if (request.continuation.length > 2048)
|
|
255
236
|
throw new Error('Invalid continuation');
|
|
256
237
|
const token = JSON.parse(Buffer.from(request.continuation, 'base64url').toString('utf8'));
|
|
238
|
+
if (!token || typeof token !== 'object' || Array.isArray(token))
|
|
239
|
+
throw new Error('Invalid continuation');
|
|
240
|
+
if (token.ranking !== IMPACT_RANKING_VERSION) {
|
|
241
|
+
throw new Error('Continuation uses a different ranking version; restart the scan without continuation');
|
|
242
|
+
}
|
|
257
243
|
if (token.snapshot !== snapshot.manifest.snapshotId ||
|
|
258
244
|
token.digest !== snapshot.manifest.contentDigest || token.query !== queryDigest ||
|
|
259
245
|
!Number.isInteger(token.offset) || token.offset < 0 || token.offset > allBalanced.length) {
|
|
@@ -266,7 +252,7 @@ export class SnapshotImpactQueryService {
|
|
|
266
252
|
const nextOffset = offset + facts.length;
|
|
267
253
|
const continuation = nextOffset < allBalanced.length ? Buffer.from(JSON.stringify({
|
|
268
254
|
snapshot: snapshot.manifest.snapshotId, digest: snapshot.manifest.contentDigest,
|
|
269
|
-
query: queryDigest, offset: nextOffset,
|
|
255
|
+
query: queryDigest, ranking: IMPACT_RANKING_VERSION, offset: nextOffset,
|
|
270
256
|
})).toString('base64url') : undefined;
|
|
271
257
|
const capabilities = [
|
|
272
258
|
...new Map(facts
|
|
@@ -357,6 +343,7 @@ export class SnapshotImpactQueryService {
|
|
|
357
343
|
},
|
|
358
344
|
diagnostics: selectDiagnostics(snapshot, request, facts, focus, queryDiagnostics),
|
|
359
345
|
exploration: {
|
|
346
|
+
rankingVersion: IMPACT_RANKING_VERSION,
|
|
360
347
|
returned: facts.length, reachableFacts: allBalanced.length, maxDepth,
|
|
361
348
|
depthLimitReached: frontier.size > 0, truncated: Boolean(continuation) || frontier.size > 0,
|
|
362
349
|
continuation,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GraphFact } from '@weftlet/core';
|
|
2
|
+
/** Binds continuation offsets to the ordering policy; changing this requires restarting a scan. */
|
|
3
|
+
export declare const IMPACT_RANKING_VERSION = "proximity-diversity-v1";
|
|
4
|
+
export declare function factSearchText(fact: GraphFact): string;
|
|
5
|
+
export interface RankedImpactFact {
|
|
6
|
+
readonly fact: GraphFact;
|
|
7
|
+
readonly score: number;
|
|
8
|
+
}
|
|
9
|
+
/** Order an already bounded reachable set. No facts are pruned and no authority is changed. */
|
|
10
|
+
export declare function rankImpactFacts(facts: readonly GraphFact[], direct: readonly GraphFact[], anchorTerms: readonly string[], focus: readonly string[]): readonly RankedImpactFact[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { stableJson } from '../infrastructure/util.js';
|
|
2
|
+
/** Binds continuation offsets to the ordering policy; changing this requires restarting a scan. */
|
|
3
|
+
export const IMPACT_RANKING_VERSION = 'proximity-diversity-v1';
|
|
4
|
+
export function factSearchText(fact) {
|
|
5
|
+
return [fact.subject.id, fact.subject.label, fact.predicate, fact.object.id,
|
|
6
|
+
fact.object.label, fact.provenance.path].join(' ').toLowerCase();
|
|
7
|
+
}
|
|
8
|
+
function edgeCost(fact, forward) {
|
|
9
|
+
// A shared unresolved import is context, not evidence that its co-importers consume this change.
|
|
10
|
+
if ([fact.subject, fact.object].some((node) => node.id.startsWith('contract:module:')))
|
|
11
|
+
return 8;
|
|
12
|
+
// Follow typed definitions towards consumers cheaply; upstream dependencies remain available.
|
|
13
|
+
if (forward && fact.predicate === 'consumes' && fact.object.kind === 'Contract' &&
|
|
14
|
+
['Component', 'Evidence'].includes(fact.subject.kind))
|
|
15
|
+
return 4;
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
function stableTie(fact) {
|
|
19
|
+
// Unrelated commit hashes and input order must not decide which otherwise-equal file comes first.
|
|
20
|
+
return stableJson({ subject: fact.subject.id, predicate: fact.predicate, object: fact.object.id,
|
|
21
|
+
path: fact.provenance.path, start: fact.provenance.startLine, end: fact.provenance.endLine,
|
|
22
|
+
extractor: fact.extractor, authority: fact.authority, sourceAuthority: fact.sourceAuthority,
|
|
23
|
+
extractionReview: fact.extractionReview, confidence: fact.confidence });
|
|
24
|
+
}
|
|
25
|
+
/** Order an already bounded reachable set. No facts are pruned and no authority is changed. */
|
|
26
|
+
export function rankImpactFacts(facts, direct, anchorTerms, focus) {
|
|
27
|
+
const adjacent = new Map();
|
|
28
|
+
const connect = (from, to, cost) => {
|
|
29
|
+
if (from.kind === 'Project' || to.kind === 'Project')
|
|
30
|
+
return;
|
|
31
|
+
const edges = adjacent.get(from.id) ?? [];
|
|
32
|
+
edges.push({ to: to.id, cost });
|
|
33
|
+
adjacent.set(from.id, edges);
|
|
34
|
+
};
|
|
35
|
+
for (const fact of facts) {
|
|
36
|
+
connect(fact.subject, fact.object, edgeCost(fact, true));
|
|
37
|
+
connect(fact.object, fact.subject, edgeCost(fact, false));
|
|
38
|
+
}
|
|
39
|
+
const distance = new Map();
|
|
40
|
+
// Integer edge costs permit a bucket queue: O(edges + maximum path cost), without a heap dependency.
|
|
41
|
+
const pending = [];
|
|
42
|
+
const enqueue = (id, cost) => {
|
|
43
|
+
if (cost >= (distance.get(id) ?? Infinity))
|
|
44
|
+
return;
|
|
45
|
+
distance.set(id, cost);
|
|
46
|
+
(pending[cost] ??= []).push(id);
|
|
47
|
+
};
|
|
48
|
+
for (const fact of direct) {
|
|
49
|
+
const eligible = [fact.subject, fact.object].filter((node) => node.kind !== 'Project');
|
|
50
|
+
const matching = eligible.filter((node) => anchorTerms.some((term) => `${node.id} ${node.label}`.toLowerCase().includes(term)));
|
|
51
|
+
// Some adapters only identify an anchor through its citation. Keep those queries supported.
|
|
52
|
+
for (const node of matching.length ? matching : eligible)
|
|
53
|
+
enqueue(node.id, 0);
|
|
54
|
+
}
|
|
55
|
+
for (let cost = 0; cost < pending.length; cost++) {
|
|
56
|
+
for (const id of pending[cost] ?? []) {
|
|
57
|
+
if (distance.get(id) !== cost)
|
|
58
|
+
continue;
|
|
59
|
+
for (const edge of adjacent.get(id) ?? [])
|
|
60
|
+
enqueue(edge.to, cost + edge.cost);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const directIds = new Set(direct.map((fact) => fact.id));
|
|
64
|
+
const rows = facts.map((fact) => {
|
|
65
|
+
const text = factSearchText(fact);
|
|
66
|
+
return {
|
|
67
|
+
fact,
|
|
68
|
+
proximity: directIds.has(fact.id) ? 0 : Math.min((distance.get(fact.subject.id) ?? Infinity) + edgeCost(fact, true), (distance.get(fact.object.id) ?? Infinity) + edgeCost(fact, false)),
|
|
69
|
+
focus: focus.filter((term) => text.includes(term)).length,
|
|
70
|
+
tie: stableTie(fact),
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
const lexical = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
74
|
+
const compare = (a, b) => b.focus - a.focus || lexical(a.tie, b.tie) || lexical(a.fact.id, b.fact.id);
|
|
75
|
+
rows.sort((a, b) => a.proximity - b.proximity || compare(a, b));
|
|
76
|
+
const counts = new Map();
|
|
77
|
+
const diverse = rows.map((row) => {
|
|
78
|
+
const seen = counts.get(row.fact.provenance.path) ?? 0;
|
|
79
|
+
counts.set(row.fact.provenance.path, seen + 1);
|
|
80
|
+
// Each additional fact from one source costs one ordinary edge. Details stay on later pages.
|
|
81
|
+
return { ...row, score: row.proximity + seen };
|
|
82
|
+
});
|
|
83
|
+
return diverse.sort((a, b) => a.score - b.score || compare(a, b))
|
|
84
|
+
.map(({ fact, score }) => ({ fact, score }));
|
|
85
|
+
}
|
|
@@ -3,14 +3,23 @@ import { promisify } from 'node:util';
|
|
|
3
3
|
import { loadConfig } from "../configuration/config.js";
|
|
4
4
|
import { resolveStorageRoot } from "../configuration/cache.js";
|
|
5
5
|
import { FileSnapshotStore } from "../storage/storage.js";
|
|
6
|
+
import { resolveCodexInvocation } from '../extraction/codex-command.js';
|
|
6
7
|
import { readCodexLoginMode, semanticCommandEnvironment } from '../extraction/codex-auth.js';
|
|
7
8
|
/** Reads readiness only. It never installs tools, prints credentials, or starts a model. */
|
|
8
9
|
export async function diagnoseSetup(configPath) {
|
|
9
10
|
const { config, repositoryRoot } = await loadConfig(configPath);
|
|
10
11
|
const execute = promisify(execFile);
|
|
12
|
+
const environment = semanticCommandEnvironment(process.env);
|
|
11
13
|
const probe = async (name, args) => {
|
|
12
14
|
try {
|
|
13
|
-
const
|
|
15
|
+
const started = performance.now();
|
|
16
|
+
const invocation = name === 'codex' ? await resolveCodexInvocation(name, environment) : { command: name, prefixArgs: [] };
|
|
17
|
+
const remaining = Math.ceil(5000 - (performance.now() - started));
|
|
18
|
+
if (remaining <= 0)
|
|
19
|
+
return { available: false, output: '' };
|
|
20
|
+
const result = await execute(invocation.command, [...invocation.prefixArgs, ...args], {
|
|
21
|
+
cwd: repositoryRoot, timeout: remaining, maxBuffer: 8192, windowsHide: true, env: name === 'codex' ? environment : process.env,
|
|
22
|
+
});
|
|
14
23
|
return { available: true, output: `${result.stdout}${result.stderr}` };
|
|
15
24
|
}
|
|
16
25
|
catch {
|
|
@@ -18,13 +27,13 @@ export async function diagnoseSetup(configPath) {
|
|
|
18
27
|
}
|
|
19
28
|
};
|
|
20
29
|
const git = await probe('git', ['--version']);
|
|
21
|
-
const codex = await probe(
|
|
30
|
+
const codex = await probe('codex', ['--version']);
|
|
22
31
|
const auth = config.semantic.auth;
|
|
23
32
|
const authMode = config.semantic.provider === 'codex-cli' ? auth?.mode ?? 'chatgpt' : undefined;
|
|
24
33
|
const apiKeyEnv = auth?.mode === 'api-key' ? auth.apiKeyEnv ?? 'CODEX_API_KEY' : undefined;
|
|
25
34
|
const apiKeyPresent = apiKeyEnv ? Boolean(process.env[apiKeyEnv]?.trim()) : undefined;
|
|
26
35
|
const loginMode = codex.available && authMode === 'chatgpt'
|
|
27
|
-
? await readCodexLoginMode(
|
|
36
|
+
? await readCodexLoginMode('codex', environment)
|
|
28
37
|
: undefined;
|
|
29
38
|
const docker = config.ontology.backend === 'opencaesar' ? await probe('docker', ['info']) : undefined;
|
|
30
39
|
const saved = await new FileSnapshotStore(resolveStorageRoot(repositoryRoot, config)).readHead('default');
|
|
@@ -6,12 +6,18 @@ Status: implemented, with bounded validation.
|
|
|
6
6
|
Explicit hydration produces committed evidence in disposable archive directories, without
|
|
7
7
|
registering worktrees or requiring the active checkout to be clean. Symlinks/submodules and
|
|
8
8
|
archives larger than 128 MiB are currently rejected; they are not silently partially indexed.
|
|
9
|
-
The archive path requires `git` and `tar`;
|
|
9
|
+
The archive path requires `git` and `tar`; Linux and focused hosted Windows checks are
|
|
10
|
+
recorded in [validation](VALIDATION.md). [Committed-tree materialization](../src/repository/committed-tree.ts)
|
|
11
|
+
enforces those isolation and archive bounds.
|
|
10
12
|
|
|
11
13
|
Snapshot IDs bind configuration and semantic scope in addition to commit and runtime revisions.
|
|
12
14
|
User-scope cache heads are isolated by checkout identity. Snapshots publish via an atomic
|
|
13
15
|
create-if-absent operation; head pointers replace atomically only after a complete snapshot
|
|
14
16
|
exists. Concurrent conflicting content fails instead of replacing a winner.
|
|
17
|
+
[Snapshot storage](../src/storage/storage.ts) and [atomic replacement](../src/storage/atomic-rename.ts)
|
|
18
|
+
implement publication; [hydration](../src/application/hydrator.ts) validates evidence before
|
|
19
|
+
advancing the head. [Lifecycle tests](../tests/snapshot-lifecycle.test.ts) exercise concurrent
|
|
20
|
+
publication and saved querying during edits; their presence alone is not a passing run.
|
|
15
21
|
|
|
16
22
|
Queries identify source commit, scope, configuration match and excluded working changes.
|
|
17
23
|
Final checks reject dirty working trees, old commits, configuration mismatch and draft overlays.
|
|
@@ -6,6 +6,8 @@ Status: implemented as a bounded submilestone.
|
|
|
6
6
|
bytes against checkout HEAD. Identity binds base SHA, base snapshot digest and captured content
|
|
7
7
|
digest, including deletions. Code is reindexed in an independent temporary Git repository;
|
|
8
8
|
the active checkout and its index/branch remain unchanged.
|
|
9
|
+
[Working-content capture](../src/repository/working-content.ts) reads the selected bytes;
|
|
10
|
+
[overlay assembly](../src/application/overlay.ts) performs replacement and publication.
|
|
9
11
|
|
|
10
12
|
All implementation relationships are rebuilt, so consumer links cannot retain deleted/renamed
|
|
11
13
|
definitions. Affected document assertions and links are invalidated. No model is called and no
|
|
@@ -15,6 +17,10 @@ are omitted. Code facts are explicitly draft, including in the viewer.
|
|
|
15
17
|
Overlay heads are separate from committed heads and isolated by checkout and agent name.
|
|
16
18
|
Select the returned snapshot ID explicitly for querying. Overlay snapshots never satisfy final
|
|
17
19
|
committed-evidence checks.
|
|
20
|
+
[Saved-evidence querying](../src/application/saved-evidence.ts) reports overlay freshness,
|
|
21
|
+
and [snapshot checks](../src/application/snapshot-checks.ts) reject draft evidence for final
|
|
22
|
+
validation. [Overlay tests](../src/application/overlay.test.ts) exercise edits, deletion,
|
|
23
|
+
renames and checkout isolation; this link does not assert a current passing test result.
|
|
18
24
|
|
|
19
25
|
Bounds: 500 changed paths, 1 MiB per source file, source/configuration scope must match the base.
|
|
20
26
|
Changes outside configured source roots are reported as excluded. This first overlay implementation
|
|
@@ -7,6 +7,8 @@ an RDF named graph, with reified provenance, authority, confidence, source diges
|
|
|
7
7
|
configuration metadata. A structured record preserves compatibility fields. The traversal
|
|
8
8
|
projection is read through SPARQL and has field-for-field equivalence tests, including order.
|
|
9
9
|
The viewer consumes the resulting Context Pack, not an independently extracted graph.
|
|
10
|
+
[RDF projection](../src/graph/rdf.ts) implements the dataset;
|
|
11
|
+
[RDF integration tests](../tests/rdf.test.ts) check projection equivalence and cross-source linkage.
|
|
10
12
|
|
|
11
13
|
Comunica executes local SELECT queries. Parsed queries reject remote SERVICE/dataset access
|
|
12
14
|
and updates. `rdf-validate-shacl` checks structural integrity and identity-kind consistency.
|
|
@@ -19,6 +21,10 @@ depth exhaustion, result truncation and snapshot/query-bound continuation tokens
|
|
|
19
21
|
results using `--continuation`. Depth exhaustion requires an explicit larger `--max-depth`;
|
|
20
22
|
the result never represents that partial traversal as complete.
|
|
21
23
|
The graph projection itself has a fail-closed 100,000-fact bound.
|
|
24
|
+
[Impact querying](../src/graph/query.ts) produces the [core Context Pack contract](../packages/core/src/core.ts).
|
|
25
|
+
The [MCP interface](../src/interfaces/mcp.ts) exposes that result to agents and the
|
|
26
|
+
[viewer renderer](../src/visualization/visualization.ts) consumes it. [Query tests](../src/graph/query.test.ts)
|
|
27
|
+
exercise bounded retrieval; linked tests are evidence locations, not proof of a passing run.
|
|
22
28
|
|
|
23
29
|
Only paths explicitly listed in `approvedIntentSources` receive `sourceAuthority: declared`.
|
|
24
30
|
Their extracted assertions remain `authority: inferred` and `extractionReview: unreviewed`.
|
|
@@ -9,6 +9,11 @@ CLI subprocesses have cancellation, timeouts, bounded output acceptance and narr
|
|
|
9
9
|
Errors report safe status metadata instead of private stderr. Hydration limits source scope
|
|
10
10
|
before invoking a provider, stops new work after failures, and reuses immutable artifacts.
|
|
11
11
|
Reasoning settings now participate in Codex cache identity.
|
|
12
|
+
[Semantic hydration](../src/extraction/semantic.ts) owns artifact reuse and validation;
|
|
13
|
+
[Codex command discovery](../src/extraction/codex-command.ts) resolves native/npm launchers,
|
|
14
|
+
and [authentication configuration](../src/extraction/codex-auth.ts) keeps login and API-key modes explicit.
|
|
15
|
+
[Launcher tests](../src/extraction/codex-command.test.ts) exercise synthetic executables without
|
|
16
|
+
claiming live model quality or account authentication.
|
|
12
17
|
|
|
13
18
|
These changes provide an extension seam, not a claim of interoperability with arbitrary models.
|
|
14
19
|
Additional real adapters require their own live and deterministic tests. The current cloud's
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# ADR 010: explicit committed source projects for TypeScript indexing
|
|
2
|
+
|
|
3
|
+
Status: implemented as an opt-in indexing mode. No product-policy approval or new hard
|
|
4
|
+
architecture rule is implied.
|
|
5
|
+
|
|
6
|
+
Weftlet's self-evaluation found no SCIP test relationships and no linked shared core
|
|
7
|
+
contract. The build tsconfig excludes tests, and a disposable Git archive has no installed
|
|
8
|
+
workspace links or compiled package outputs. Merely listing `evidenceRoots` does not make
|
|
9
|
+
the TypeScript indexer include those files or resolve their package imports.
|
|
10
|
+
|
|
11
|
+
Keep ordinary SCIP invocation unchanged. Add `scip.sourceProject` for explicitly selecting
|
|
12
|
+
committed TypeScript files and mapping exact local module names to committed source entry
|
|
13
|
+
points. Generate a separate compiler configuration only in the disposable archive. This
|
|
14
|
+
allows indexing an older SHA without adding a new tsconfig to that commit, installing
|
|
15
|
+
dependencies, running package scripts, rebuilding the consumer, or borrowing dirty files
|
|
16
|
+
or workspace links from its active checkout.
|
|
17
|
+
|
|
18
|
+
## Configuration
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"scip": {
|
|
23
|
+
"version": "0.4.0",
|
|
24
|
+
"command": "bundled-scip-typescript",
|
|
25
|
+
"args": ["index", "--no-global-caches"],
|
|
26
|
+
"sourceProject": {
|
|
27
|
+
"include": ["src/**/*.ts", "tests/**/*.ts", "packages/core/src/**/*.ts"],
|
|
28
|
+
"aliases": { "@weftlet/core": "packages/core/src/index.ts" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Paths and globs are relative to the configured repository root, using `/`. Globs support
|
|
35
|
+
`*`, `**` and `?`; list files directly when appropriate. `.ts`, `.tsx`, `.mts` and `.cts`
|
|
36
|
+
files are supported by this selector. Add separate globs for each extension needed.
|
|
37
|
+
Aliases are exact module names, including explicit package subpaths, rather than wildcards.
|
|
38
|
+
Alias targets must exist in the selected commit and match `include`.
|
|
39
|
+
|
|
40
|
+
Select the files that **define** the contracts as well as their consumers. The pinned SCIP
|
|
41
|
+
indexer emits documents for explicitly selected files; resolving an import does not
|
|
42
|
+
automatically index every file behind it. For example, if an entry point re-exports a type
|
|
43
|
+
from another file, include that definition file too. There is no inferred mapping from
|
|
44
|
+
package `dist` exports to source; the configuration author supplies the source entry point.
|
|
45
|
+
|
|
46
|
+
This separate program uses ES2022, NodeNext module resolution, preserved JSX, no emit,
|
|
47
|
+
no ambient `types` packages, and the supplied aliases. It does not inherit build tsconfig
|
|
48
|
+
settings. Missing external dependencies and custom build/compiler behavior remain limits;
|
|
49
|
+
this is local symbol analysis, **not** a successful type check or proof of complete dependency
|
|
50
|
+
resolution. Use the existing `scip.args` path with a committed dedicated tsconfig when that
|
|
51
|
+
configuration is more appropriate. Do not combine `sourceProject` with custom commands,
|
|
52
|
+
workspace discovery, inferred configs or additional CLI arguments: this first mode requires
|
|
53
|
+
exactly the command and args above and rejects conflicting choices.
|
|
54
|
+
|
|
55
|
+
Bounds: 1–100 include patterns, at most 100 aliases and 1–5,000 selected committed files.
|
|
56
|
+
Paths cannot be absolute or escape the repository. Empty selections, missing/unselected
|
|
57
|
+
alias targets and the reserved `.weftlet-scip-source-project.json` path fail before SCIP
|
|
58
|
+
starts. Existing archive protections against symlinks, submodules and oversized archives
|
|
59
|
+
remain in force. The generated project is removed with its disposable tree.
|
|
60
|
+
|
|
61
|
+
Configuration participates in snapshot/cache identity. Existing immutable snapshots remain
|
|
62
|
+
queryable. Source-project patterns also participate in change tracking and overlay capture,
|
|
63
|
+
including files outside `codeRoots`/`evidenceRoots`. Changed code stays draft; rename/delete
|
|
64
|
+
reindexing replaces relationships. Neither test-file inclusion nor a typed test reference
|
|
65
|
+
establishes that a test ran or passed.
|
|
66
|
+
|
|
67
|
+
## Evidence and remaining work
|
|
68
|
+
|
|
69
|
+
Real SCIP regressions cover a workspace entry point, re-exported contract, a test excluded
|
|
70
|
+
by the build config, dirty and ignored files, repeat digest stability, committed renames
|
|
71
|
+
and deletions, and draft capture outside ordinary code roots. A fresh packed consumer
|
|
72
|
+
checks the new runtime assets and an excluded test resolved through a source alias.
|
|
73
|
+
|
|
74
|
+
The [fixed self-repository evaluation](IMPACT-EVALUATION.md) adds this profile without
|
|
75
|
+
changing its source SHA, expected files, query budgets or ranking. It improves candidate
|
|
76
|
+
retrieval and supplies typed test/core-contract relationships. It does not resolve prose
|
|
77
|
+
intent linkage, external dependency installation, live model quality or query-ranking gaps.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# ADR 011: explicit document links with committed targets
|
|
2
|
+
|
|
3
|
+
Status: implemented; source approval and live semantic review remain separate.
|
|
4
|
+
|
|
5
|
+
Intent documents can name implementation and test locations using ordinary Markdown links.
|
|
6
|
+
Weftlet parses CommonMark with `mdast-util-from-markdown`, rather than treating every slash
|
|
7
|
+
in prose, code examples and URLs as a repository relationship. Inline and reference-style
|
|
8
|
+
links resolve relative to the source document; a leading `/` denotes the repository root.
|
|
9
|
+
Fragments select the file identity only, not a validated target heading or line. Percent-encoded
|
|
10
|
+
paths are decoded. External URLs, local anchors, images, HTML, fenced code and unused link
|
|
11
|
+
reference definitions do not create mappings. MDX extensions are not interpreted.
|
|
12
|
+
|
|
13
|
+
For compatibility, literal slash-separated paths in prose and single paths in inline code
|
|
14
|
+
resolve from the repository root. `./` and `../` make a literal path document-relative.
|
|
15
|
+
Use Markdown links for spaces or punctuation in paths. There is no basename search, implicit
|
|
16
|
+
root fallback, extension guessing, directory expansion, remote fetch or active-checkout lookup.
|
|
17
|
+
A stale Markdown link, linked directory or path escaping the repository produces a cited
|
|
18
|
+
`implementationGaps` diagnostic, not a relationship to a guessed target. Unresolved literal
|
|
19
|
+
mentions are ignored: slash-separated prose, package names and examples are not missing-file
|
|
20
|
+
declarations. Use Markdown links when targets need validation. Diagnostics describe extraction gaps;
|
|
21
|
+
this increment does not turn them into hard policy gates. Links to a document verify only
|
|
22
|
+
that its file exists, not that it was selected for semantic extraction.
|
|
23
|
+
|
|
24
|
+
`document-linker@2.0.0` emits `references` facts using exact committed file identities and the
|
|
25
|
+
source link's line span. Reference-style links cite the definition containing the destination.
|
|
26
|
+
Test filenames use the same Evidence kind as SCIP and repository-file extraction. Distinct
|
|
27
|
+
passages remain distinct facts; repeated identical links within one passage deduplicate.
|
|
28
|
+
Containment of model assertions now cites the assertion's passage, rather than the entire doc.
|
|
29
|
+
Missing targets are rechecked on every committed refresh even when semantic artifacts are reused.
|
|
30
|
+
|
|
31
|
+
A reference means that the document points to a file. It does not prove that the file fulfills
|
|
32
|
+
intent, that a test passed, or that its prose is correct. Facts stay `authority: inferred`;
|
|
33
|
+
explicit links are `extractionReview: deterministic`. Model containment stays `unreviewed`.
|
|
34
|
+
Only explicit `approvedIntentSources` configuration sets `sourceAuthority: declared`, including
|
|
35
|
+
for links; source approval never approves a model assertion. The Weftlet evaluation continues
|
|
36
|
+
to configure zero approved sources and zero model assertions.
|
|
37
|
+
|
|
38
|
+
Snapshot assembly advances from `graph-v2` to `graph-v3-links-2.0.0` and binds the linker version
|
|
39
|
+
in both identity and manifest. Refreshes create a different immutable identity; saved v2 snapshots remain readable
|
|
40
|
+
without a provider and are not rewritten. Semantic cache keys are unchanged because model
|
|
41
|
+
extraction is unchanged. Existing overlays conservatively invalidate document references to
|
|
42
|
+
changed/deleted targets and changed prose; they do not invent new approved mappings.
|
|
43
|
+
|
|
44
|
+
The six evaluation documents now have contributor-facing links attached to descriptions of
|
|
45
|
+
actual responsibilities. These are reviewable navigation evidence, not newly approved policy.
|
|
46
|
+
The original frozen baseline remains intact. A measurement using the new documents changes
|
|
47
|
+
its evidence input and must not be presented as a model-quality or ranking improvement.
|
|
48
|
+
Query ranking, automatic scope expansion and live provider extraction are separate work.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# ADR 012: order impact evidence by proximity and source diversity
|
|
2
|
+
|
|
3
|
+
Status: implemented as a query-only heuristic; not product policy or an evidence gate.
|
|
4
|
+
|
|
5
|
+
The self-repository evaluation found distractions connected through common imports such as
|
|
6
|
+
`node:fs/promises`. The previous ordering prioritized keyword counts, then Codex/document-link
|
|
7
|
+
extractor names, before hop distance. Global lane balancing could also bring a distant fact
|
|
8
|
+
forward ahead of close evidence. A large file could contribute many symbol facts before a
|
|
9
|
+
useful downstream consumer appeared.
|
|
10
|
+
|
|
11
|
+
Keep snapshot extraction, RDF projection, anchor matching, the reachable set, max-depth rules
|
|
12
|
+
and continuation coverage unchanged. Rank the existing bounded set in `graph/ranking.ts`:
|
|
13
|
+
|
|
14
|
+
- Direct matches start at zero. Other facts use shortest weighted paths from matching anchor
|
|
15
|
+
nodes. Adapters that identify an anchor only through citations retain endpoint fallback.
|
|
16
|
+
- Ordinary relationships cost one. Following a Component/Evidence `consumes` edge upstream
|
|
17
|
+
to a typed Contract costs four; following it towards a consumer costs one. This favors
|
|
18
|
+
downstream consumers while retaining shared contracts and their definitions.
|
|
19
|
+
- Edges touching a generic `contract:module:` import node cost eight in both directions.
|
|
20
|
+
Sharing a module specifier is weaker impact context than a resolved typed consumer or
|
|
21
|
+
explicit product relationship. No module name or evaluation target is special-cased.
|
|
22
|
+
- Each additional fact from one provenance path adds one to its ranking score. This trades
|
|
23
|
+
some repeated detail for earlier evidence from other sources. It is not a per-file cap.
|
|
24
|
+
- Within equal scores, task terms break ties and evidence lanes are balanced. Remaining
|
|
25
|
+
ties use structural/citation fields with code-point ordering, not commit-derived IDs or
|
|
26
|
+
runtime locale. Fact IDs only disambiguate otherwise indistinguishable records.
|
|
27
|
+
|
|
28
|
+
These ordinal weights are reviewable heuristics, not calibrated dependency probabilities.
|
|
29
|
+
They neither change confidence/authority nor claim that proximity proves a product dependency.
|
|
30
|
+
There is no special preference for a model provider, automatic promotion of approved intent,
|
|
31
|
+
new traversal cutoff, or removal of a weak fact. Page through to inspect lower-ranked evidence;
|
|
32
|
+
existing depth exhaustion remains explicit. Snapshot facts, provenance and identities are
|
|
33
|
+
unchanged, so saved evidence can be rescored without an indexer, compiler, provider or refresh.
|
|
34
|
+
|
|
35
|
+
Responses include `exploration.rankingVersion: proximity-diversity-v1`; the Markdown renderer
|
|
36
|
+
also identifies it. Continuations bind this version as well as snapshot identity/content and
|
|
37
|
+
request. Old or different ranking tokens fail with an instruction to restart the scan, rather
|
|
38
|
+
than reusing an offset against a changed order. Changing this policy requires a version bump.
|
|
39
|
+
The evaluator records the version and rejects changes between pages.
|
|
40
|
+
|
|
41
|
+
Tests cover many typed consumers of a large source, deep asynchronous relationships, common
|
|
42
|
+
imports with attractive keywords, citation-only adapters, stable order across input shuffles
|
|
43
|
+
and changed commit-derived IDs, full pagination, and old-token rejection. Real TypeScript/RDF
|
|
44
|
+
pipeline integration and packed-consumer queries exercise the same ordering path.
|
|
45
|
+
|
|
46
|
+
Compare against the exact saved document-link snapshot and unchanged four cases, 60 facts per
|
|
47
|
+
page, three pages, depth 32. Record candidate retrieval and labeled distractions together.
|
|
48
|
+
Even a successful comparison cannot establish exhaustive precision, prove a safe change, or
|
|
49
|
+
replace a broader semantic-quality evaluation. Large-graph performance and interactive browser
|
|
50
|
+
validation remain separate work.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ADR 013: scope relative module request identities
|
|
2
|
+
|
|
3
|
+
Status: implemented as a correction to literal import evidence. Query ranking is unchanged.
|
|
4
|
+
|
|
5
|
+
The frozen dispatch evaluation found that two planners importing `./policy.js` shared the
|
|
6
|
+
same generic contract node, although they were in different directories and SCIP correctly
|
|
7
|
+
identified separate policy symbols. This false connection brought refund and audit files
|
|
8
|
+
into dispatch scans. Adjusting ranking cannot correct that underlying identity.
|
|
9
|
+
|
|
10
|
+
`repository-file-extractor@2.0.0` keeps a literal module request separate from a resolved
|
|
11
|
+
implementation. For ordinary `./` and `../` requests (including `.` and `..`), combine the
|
|
12
|
+
importer's directory with the specifier and normalize POSIX path segments. Git paths use
|
|
13
|
+
the same separators on Windows and Linux. The node ID is `contract:module:./` followed by
|
|
14
|
+
the URI-encoded requested location. Its canonical label and attributes describe a
|
|
15
|
+
`repository-relative-request` with `resolution: unresolved`. Imports from different places
|
|
16
|
+
that request the same location share a node; identical spellings naming different locations
|
|
17
|
+
do not. Labels and attributes stay identical when a node has multiple referring spellings.
|
|
18
|
+
|
|
19
|
+
Requests escaping the repository, containing backslashes, percent escapes, loader query/hash
|
|
20
|
+
suffixes or control characters remain opaque. Their identity hashes the importer and original
|
|
21
|
+
specifier under the reserved `contract:module:./?` prefix. No path decoding, filesystem lookup,
|
|
22
|
+
basename search, source-suffix substitution, directory-index expansion or module execution occurs.
|
|
23
|
+
The prefixes distinguish these identities from bare imports, whose existing identities remain
|
|
24
|
+
unchanged. Case is preserved. Facts cite the importing line and committed content digest;
|
|
25
|
+
their IDs bind the extractor version and module identity.
|
|
26
|
+
|
|
27
|
+
This is lexical request identity, not TypeScript/Node resolution. `./policy.js`, `./policy.ts`
|
|
28
|
+
and `./policy/index.js` remain distinct requests even if a consumer's compiler could map them
|
|
29
|
+
to one source. A remaining import after target deletion is still a cited unresolved request;
|
|
30
|
+
it does not assert that the target exists. Importer renames change the relative context,
|
|
31
|
+
and deleted importers contribute no new facts. SCIP remains the source of resolved local
|
|
32
|
+
typed-symbol relationships. Bare aliases, package exports and environment-dependent resolution
|
|
33
|
+
need their own evidence; the simple line-based import recognizer is not a complete parser.
|
|
34
|
+
|
|
35
|
+
Generic request nodes retain the `contract:module:` prefix and existing ranking treatment.
|
|
36
|
+
No provider preference, confidence/authority promotion, new traversal cutoff or evaluation
|
|
37
|
+
label change accompanies this fix. Typed symbols, explicit document links and queue-routing
|
|
38
|
+
evidence are separate. In particular, matching topic strings still does not establish an
|
|
39
|
+
operational route to the archival consumer in the dispatch evaluation.
|
|
40
|
+
|
|
41
|
+
Committed refreshes use `graph-v4-links-2.0.0-files-2.0.0` and report both assembly and file
|
|
42
|
+
extractor versions. Draft overlay publication IDs also bind the current file extractor, while
|
|
43
|
+
their capture-v2 digest continues identifying the same working bytes. Overlay manifests report
|
|
44
|
+
the new extraction version even when built on a version-1 base. The committed base and prior
|
|
45
|
+
overlay remain immutable and independently queryable; neither is rewritten or advanced by
|
|
46
|
+
building a draft. Semantic artifact keys are unchanged and unchanged prose remains reusable.
|
|
47
|
+
|
|
48
|
+
To obtain corrected relationships, explicitly hydrate the desired committed ref or rebuild
|
|
49
|
+
the draft overlay. Querying saved evidence continues to use that evidence's original facts,
|
|
50
|
+
without requiring an indexer, provider, Docker or refresh. A newer extractor does not silently
|
|
51
|
+
repair or reinterpret historical evidence.
|
|
52
|
+
|
|
53
|
+
Validation covers distinct domains, equivalent requested locations, opaque paths, URI-safe
|
|
54
|
+
identity, preserved bare imports, committed/dirty isolation, importer rename/delete behavior,
|
|
55
|
+
old/new snapshot coexistence, real TypeScript and RDF/SHACL, and installed runtime assets.
|
|
56
|
+
Compare newly extracted facts with the frozen dispatch and Weftlet inputs at the existing
|
|
57
|
+
ranking, case labels and page budgets. Keep missing operational links visible separately
|
|
58
|
+
from retrieval improvements.
|