@weftlet/toolkit 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -1
- package/dist/application/application.js +7 -4
- package/dist/application/cache-adoption.d.ts +43 -0
- package/dist/application/cache-adoption.js +129 -0
- package/dist/application/extraction-identity.d.ts +6 -0
- package/dist/application/extraction-identity.js +15 -0
- package/dist/application/hydration-failure.d.ts +41 -0
- package/dist/application/hydration-failure.js +43 -0
- package/dist/application/hydration-plan.d.ts +4 -7
- package/dist/application/hydration-plan.js +7 -15
- package/dist/application/hydrator.d.ts +5 -5
- package/dist/application/hydrator.js +37 -52
- package/dist/application/overlay.d.ts +3 -1
- package/dist/application/overlay.js +13 -4
- package/dist/application/saved-evidence.d.ts +6 -3
- package/dist/application/saved-evidence.js +7 -1
- package/dist/application/semantic-artifact-store.d.ts +4 -0
- package/dist/application/semantic-artifact-store.js +7 -0
- package/dist/application/semantic-run.d.ts +32 -0
- package/dist/application/semantic-run.js +111 -0
- package/dist/application/semantic-scope.d.ts +13 -0
- package/dist/application/semantic-scope.js +12 -0
- package/dist/application/snapshot-checks.d.ts +2 -1
- package/dist/application/snapshot-checks.js +8 -1
- package/dist/configuration/cache.d.ts +6 -0
- package/dist/configuration/cache.js +18 -0
- package/dist/configuration/config.js +1 -1
- package/dist/extraction/document.js +1 -1
- package/dist/extraction/providers.js +10 -3
- package/dist/extraction/repository-nodes.d.ts +2 -3
- package/dist/extraction/repository-nodes.js +5 -11
- package/dist/extraction/routing.d.ts +1 -1
- package/dist/extraction/routing.js +3 -6
- package/dist/extraction/semantic-cache.d.ts +1 -0
- package/dist/extraction/semantic-cache.js +9 -2
- package/dist/extraction/semantic-diagnostics.d.ts +9 -0
- package/dist/extraction/semantic-diagnostics.js +15 -0
- package/dist/extraction/semantic.d.ts +5 -1
- package/dist/extraction/semantic.js +39 -6
- package/dist/extraction/static.d.ts +13 -5
- package/dist/extraction/static.js +8 -15
- package/dist/governance/governance.js +11 -7
- package/dist/graph/identity-preflight.d.ts +57 -0
- package/dist/graph/identity-preflight.js +71 -0
- package/dist/graph/query.js +2 -0
- package/dist/graph/rdf.d.ts +4 -1
- package/dist/graph/rdf.js +10 -6
- package/dist/graph/validation-progress.d.ts +14 -0
- package/dist/graph/validation-progress.js +18 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/interfaces/cli.js +52 -22
- package/dist/storage/storage.d.ts +7 -1
- package/dist/storage/storage.js +15 -3
- package/dist/visualization/visualization.js +1 -0
- package/docs/ADR-016-hydration-preview.md +1 -1
- package/docs/ADR-017-shacl-validation-union.md +1 -1
- package/docs/ADR-018-operational-semantic-cache.md +100 -0
- package/docs/ADR-019-semantic-failure-diagnostics.md +82 -0
- package/docs/ADR-020-canonical-physical-file-producers.md +97 -0
- package/docs/ADR-021-identity-preflight-and-validation-timing.md +81 -0
- package/docs/PROVIDERS.md +58 -1
- package/docs/RELEASE-1.0-PLAN.md +100 -34
- package/docs/RELEASING.md +4 -2
- package/docs/ROADMAP.md +40 -15
- package/docs/VALIDATION.md +351 -4
- package/docs/extraction-manifest.json +50 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -101,8 +101,24 @@ evidence roots; selected validation documents still undergo semantic extraction
|
|
|
101
101
|
SOT traversal and default validation-document separation remain T07 work. See
|
|
102
102
|
[ADR 016](docs/ADR-016-hydration-preview.md) and the [1.0 task list](docs/RELEASE-1.0-PLAN.md).
|
|
103
103
|
|
|
104
|
+
Starting with **0.4.0**, preview also maps opaque diagnostic source IDs to local paths. Semantic
|
|
105
|
+
failures report completed/remaining work and recovery guidance; `hydrate --json --quiet` emits
|
|
106
|
+
their structured report on stderr. Built-in Codex can reuse completed artifacts after supported
|
|
107
|
+
operational changes such as increasing a timeout. See [provider recovery](docs/PROVIDERS.md#limits-and-reuse)
|
|
108
|
+
for preview, retry and explicit legacy-cache adoption instructions.
|
|
109
|
+
|
|
104
110
|
## SHACL validation and performance
|
|
105
111
|
|
|
112
|
+
Starting with **0.5.0**, identity preflight runs before ontology/RDF work.
|
|
113
|
+
`hydrate --json --quiet` reports conflicts using opaque references; `--validation-details` explicitly
|
|
114
|
+
includes local identities, producer versions and citations. Validation emits phase-boundary times
|
|
115
|
+
and graph counts, without a percentage or live heartbeat. Full SHACL remains required after a
|
|
116
|
+
passing preflight. See [ADR 021](docs/ADR-021-identity-preflight-and-validation-timing.md).
|
|
117
|
+
|
|
118
|
+
Version 0.5.0 also accepts `semantic.timeoutMs: 600000` for an approved ten-minute extraction
|
|
119
|
+
attempt; the default remains two minutes. See [provider limits and reuse](docs/PROVIDERS.md#limits-and-reuse)
|
|
120
|
+
before retrying with compatible cached artifacts.
|
|
121
|
+
|
|
106
122
|
Starting with 0.3.0, the toolkit indexes a temporary union to accelerate cold validation.
|
|
107
123
|
The toolkit uses JavaScript `rdf-validate-shacl` and an in-memory
|
|
108
124
|
N3 store. Each hydration validates the complete graph; semantic artifact reuse does not make
|
|
@@ -129,7 +145,7 @@ pnpm benchmark:shacl
|
|
|
129
145
|
|
|
130
146
|
Current hydration progress reports phase boundaries. The validator performs synchronous work,
|
|
131
147
|
so an ordinary timer cannot provide an honest heartbeat while that work blocks Node's main
|
|
132
|
-
thread. Worker-based progress,
|
|
148
|
+
thread. Worker-based progress, validation-result reuse and incremental
|
|
133
149
|
validation remain separate T05 work; they do not delay the focused cold-validation release.
|
|
134
150
|
|
|
135
151
|
## Development
|
|
@@ -172,6 +188,15 @@ For tests excluded by the build config or workspace packages whose outputs are a
|
|
|
172
188
|
committed archives, configure an [explicit TypeScript source project](docs/ADR-010-committed-source-indexing.md)
|
|
173
189
|
with selected files and local package aliases.
|
|
174
190
|
|
|
191
|
+
Repository adapters using **0.4.0 or newer** should import `repositoryFileNode` from
|
|
192
|
+
`@weftlet/core` for physical-file nodes. It preserves canonical document IDs, spec/test Evidence
|
|
193
|
+
and production Component kinds, including test files containing mocked calls or decorators.
|
|
194
|
+
Keep business-concept nodes separate; see the [core API example](packages/core/README.md) and
|
|
195
|
+
[producer decision](docs/ADR-020-canonical-physical-file-producers.md). After upgrading, rebuild
|
|
196
|
+
the graph with compatible cached artifacts; historical snapshots remain queryable but cannot
|
|
197
|
+
satisfy current validation with old extraction versions. Consumer-owned adapters must be migrated
|
|
198
|
+
as well; the toolkit cannot correct nodes they still classify independently.
|
|
199
|
+
|
|
175
200
|
In a selected document such as `docs/intent.md`, link a responsibility to its sources:
|
|
176
201
|
|
|
177
202
|
```md
|
|
@@ -10,11 +10,13 @@ import { OpenCaesarOntologyCompiler, DirectRdfOntologyCompiler } from "../extrac
|
|
|
10
10
|
import { SnapshotImpactQueryService } from "../graph/query.js";
|
|
11
11
|
import { SemanticHydrator } from "../extraction/semantic.js";
|
|
12
12
|
import { createSemanticExtractor } from "../extraction/providers.js";
|
|
13
|
-
import {
|
|
13
|
+
import { FileSnapshotStore } from "../storage/storage.js";
|
|
14
|
+
import { createSemanticArtifactStore } from './semantic-artifact-store.js';
|
|
14
15
|
import { RepositoryContractExtractor, RepositoryFileExtractor, ScipSymbolExtractor, ScipTypeScriptIndexer, } from "../extraction/static.js";
|
|
15
16
|
import { OtlpJsonRuntimeEvidenceProvider } from "../extraction/telemetry.js";
|
|
16
17
|
import { SnapshotChecks } from "./snapshot-checks.js";
|
|
17
18
|
import { resolveConsumerModule } from "../infrastructure/module-resolution.js";
|
|
19
|
+
import { requiredSnapshotExtractorVersions } from './extraction-identity.js';
|
|
18
20
|
const emptyAdapter = {
|
|
19
21
|
factExtractors: [],
|
|
20
22
|
invariantEvaluators: [],
|
|
@@ -45,7 +47,7 @@ export async function createContextGraphApplication(configPath) {
|
|
|
45
47
|
const repository = new GitRepository(repositoryRoot);
|
|
46
48
|
const adapter = await loadContextGraphAdapterModule(config.adapter, repository, repositoryRoot);
|
|
47
49
|
const snapshotStore = new FileSnapshotStore(storageRoot);
|
|
48
|
-
const artifactStore =
|
|
50
|
+
const artifactStore = createSemanticArtifactStore(repositoryRoot, config);
|
|
49
51
|
const semanticExtractor = createSemanticExtractor(config.semantic, repositoryRoot);
|
|
50
52
|
const semanticHydrator = new SemanticHydrator(config.semantic, artifactStore, semanticExtractor);
|
|
51
53
|
const staticIndexer = new ScipTypeScriptIndexer(config.scip, storageRoot);
|
|
@@ -75,7 +77,8 @@ export async function createContextGraphApplication(configPath) {
|
|
|
75
77
|
const rawQueryService = scopeResolver
|
|
76
78
|
? new PreparedImpactQueryService(snapshotStore, hydrator, scopeResolver)
|
|
77
79
|
: new SnapshotImpactQueryService(snapshotStore, headName);
|
|
78
|
-
const
|
|
80
|
+
const expectedExtractors = requiredSnapshotExtractorVersions(config);
|
|
81
|
+
const queryService = new SavedEvidenceQueryService(rawQueryService, repository, sha256(stableJson(config)), expectedExtractors);
|
|
79
82
|
return {
|
|
80
83
|
...loaded,
|
|
81
84
|
storageRoot,
|
|
@@ -84,7 +87,7 @@ export async function createContextGraphApplication(configPath) {
|
|
|
84
87
|
ontologyCompiler,
|
|
85
88
|
hydrator,
|
|
86
89
|
queryService,
|
|
87
|
-
snapshotChecks: new SnapshotChecks(snapshotStore, repository, hydrator, scopeResolver, sha256(stableJson(config))),
|
|
90
|
+
snapshotChecks: new SnapshotChecks(snapshotStore, repository, hydrator, scopeResolver, sha256(stableJson(config)), expectedExtractors),
|
|
88
91
|
scopeResolver,
|
|
89
92
|
headName,
|
|
90
93
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ImpactScanRequest } from '@weftlet/core';
|
|
2
|
+
import { type SemanticInputIssue, type SemanticSelection } from '../extraction/semantic-sources.js';
|
|
3
|
+
import { type SemanticScope } from './semantic-scope.js';
|
|
4
|
+
export interface SemanticCacheAdoptionOptions {
|
|
5
|
+
readonly fromConfigPath: string;
|
|
6
|
+
readonly ref?: string;
|
|
7
|
+
readonly dryRun?: boolean;
|
|
8
|
+
readonly request?: ImpactScanRequest;
|
|
9
|
+
}
|
|
10
|
+
export type SemanticCacheAdoptionStatus = 'adopted' | 'reusable' | 'planned' | 'missing' | 'corrupt' | 'unavailable' | 'conflict';
|
|
11
|
+
export interface SemanticCacheAdoptionRecord {
|
|
12
|
+
readonly path: string;
|
|
13
|
+
readonly key: string;
|
|
14
|
+
readonly status: SemanticCacheAdoptionStatus;
|
|
15
|
+
readonly reason: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SemanticCacheAdoption {
|
|
18
|
+
readonly schemaVersion: 1;
|
|
19
|
+
readonly kind: 'semantic-cache-adoption';
|
|
20
|
+
readonly repositoryId: string;
|
|
21
|
+
readonly ref: string;
|
|
22
|
+
readonly commitSha: string;
|
|
23
|
+
readonly configurationDigest: string;
|
|
24
|
+
readonly previousConfigurationDigest: string;
|
|
25
|
+
readonly dryRun: boolean;
|
|
26
|
+
readonly ready: boolean;
|
|
27
|
+
readonly scope: SemanticScope & {
|
|
28
|
+
readonly complete: boolean;
|
|
29
|
+
};
|
|
30
|
+
readonly records: readonly SemanticCacheAdoptionRecord[];
|
|
31
|
+
readonly counts: Readonly<Record<SemanticCacheAdoptionStatus, number>>;
|
|
32
|
+
readonly selections: readonly SemanticSelection[];
|
|
33
|
+
readonly budgets: {
|
|
34
|
+
readonly maxDocuments: number;
|
|
35
|
+
readonly maxSourceBytes: number;
|
|
36
|
+
readonly selectedDocuments: number;
|
|
37
|
+
readonly totalSourceBytes: number;
|
|
38
|
+
};
|
|
39
|
+
readonly issues: readonly SemanticInputIssue[];
|
|
40
|
+
readonly assumptions: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
/** Explicitly adopts only committed-source artifacts from a compatible, named legacy profile. */
|
|
43
|
+
export declare function adoptSemanticCache(configPath: string, options: SemanticCacheAdoptionOptions): Promise<SemanticCacheAdoption>;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { loadConfig } from '../configuration/config.js';
|
|
2
|
+
import { resolveSemanticStorageRoots, resolveStorageRoot, semanticCachePartition } from '../configuration/cache.js';
|
|
3
|
+
import { inspectSemanticCache } from '../extraction/semantic-cache.js';
|
|
4
|
+
import { semanticProviderIdentity } from '../extraction/semantic-identity.js';
|
|
5
|
+
import { selectSemanticInputs } from '../extraction/semantic-sources.js';
|
|
6
|
+
import { sha256, stableJson } from '../infrastructure/util.js';
|
|
7
|
+
import { GitRepository } from '../repository/git.js';
|
|
8
|
+
import { SemanticArtifactStore } from '../storage/storage.js';
|
|
9
|
+
import { resolveSemanticScope } from './semantic-scope.js';
|
|
10
|
+
const problemStatuses = new Set(['corrupt', 'unavailable', 'conflict']);
|
|
11
|
+
function problem(inspection, source, location) {
|
|
12
|
+
if (inspection.status !== 'corrupt' && inspection.status !== 'unavailable')
|
|
13
|
+
return undefined;
|
|
14
|
+
return { path: source.path, key: inspection.key, status: inspection.status,
|
|
15
|
+
reason: `${location} cache: ${inspection.reason}` };
|
|
16
|
+
}
|
|
17
|
+
async function inspectCandidate(source, config, identity, previous, current) {
|
|
18
|
+
const [before, existing] = await Promise.all([
|
|
19
|
+
inspectSemanticCache(previous, config, source, identity),
|
|
20
|
+
inspectSemanticCache(current, config, source, identity),
|
|
21
|
+
]);
|
|
22
|
+
const failure = problem(existing, source, 'Current') ?? problem(before, source, 'Previous');
|
|
23
|
+
if (failure)
|
|
24
|
+
return { source, record: failure };
|
|
25
|
+
const base = { path: source.path, key: existing.key };
|
|
26
|
+
if (existing.status === 'hit') {
|
|
27
|
+
const conflict = before.status === 'hit' && before.artifact.outputDigest !== existing.artifact.outputDigest;
|
|
28
|
+
return { source, record: { ...base, status: conflict ? 'conflict' : 'reusable', reason: conflict
|
|
29
|
+
? 'Current and previous caches contain different valid artifacts for this key; neither was overwritten.'
|
|
30
|
+
: 'A validated artifact is already available in the current cache.' } };
|
|
31
|
+
}
|
|
32
|
+
if (before.status === 'miss')
|
|
33
|
+
return { source, record: { ...base, status: 'missing',
|
|
34
|
+
reason: 'The explicit previous profile has no complete artifact for this source and extraction contract.' } };
|
|
35
|
+
if (before.status !== 'hit' || existing.status !== 'miss')
|
|
36
|
+
throw new Error('Unexpected cache inspection state');
|
|
37
|
+
return { source, artifact: before.artifact, record: { ...base, status: 'planned',
|
|
38
|
+
reason: 'A validated previous artifact can be copied without extraction or changing its provenance.' } };
|
|
39
|
+
}
|
|
40
|
+
async function publishCandidate(candidate, config, identity, current) {
|
|
41
|
+
let publicationError;
|
|
42
|
+
let published = false;
|
|
43
|
+
try {
|
|
44
|
+
await current.write(candidate.artifact);
|
|
45
|
+
published = true;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
publicationError = error;
|
|
49
|
+
}
|
|
50
|
+
// Reinspect even after a successful write: an immutable-key winner can have
|
|
51
|
+
// matching output but corrupt identity metadata. A digest match alone is not
|
|
52
|
+
// permission to report that artifact as reusable.
|
|
53
|
+
const existing = await inspectSemanticCache(current, config, candidate.source, identity);
|
|
54
|
+
const failure = problem(existing, candidate.source, 'Current');
|
|
55
|
+
if (failure)
|
|
56
|
+
return failure;
|
|
57
|
+
if (existing.status === 'hit' && existing.artifact.outputDigest !== candidate.artifact.outputDigest) {
|
|
58
|
+
return { ...candidate.record, status: 'conflict',
|
|
59
|
+
reason: 'A concurrent writer published a different valid artifact; neither artifact was overwritten.' };
|
|
60
|
+
}
|
|
61
|
+
if (!published) {
|
|
62
|
+
throw new Error('Unable to adopt a semantic artifact because storage publication failed.', { cause: publicationError });
|
|
63
|
+
}
|
|
64
|
+
if (existing.status !== 'hit')
|
|
65
|
+
return { ...candidate.record, status: 'unavailable',
|
|
66
|
+
reason: 'Published artifact is no longer readable; no successful adoption is claimed.' };
|
|
67
|
+
return { ...candidate.record, status: 'adopted',
|
|
68
|
+
reason: 'Validated artifact is available in the current cache; the previous artifact is unchanged.' };
|
|
69
|
+
}
|
|
70
|
+
/** Explicitly adopts only committed-source artifacts from a compatible, named legacy profile. */
|
|
71
|
+
export async function adoptSemanticCache(configPath, options) {
|
|
72
|
+
const [loaded, previous] = await Promise.all([loadConfig(configPath), loadConfig(options.fromConfigPath)]);
|
|
73
|
+
const { config, repositoryRoot } = loaded;
|
|
74
|
+
if (repositoryRoot !== previous.repositoryRoot) {
|
|
75
|
+
throw new Error('Cache adoption requires both configurations to resolve to the same checkout.');
|
|
76
|
+
}
|
|
77
|
+
if (semanticCachePartition(repositoryRoot, config) !== semanticCachePartition(repositoryRoot, previous.config)) {
|
|
78
|
+
throw new Error('Cache adoption requires matching non-operational configuration and provider contracts.');
|
|
79
|
+
}
|
|
80
|
+
const repository = new GitRepository(repositoryRoot);
|
|
81
|
+
const ref = options.ref ?? 'HEAD';
|
|
82
|
+
const commitSha = await repository.resolveRef(ref);
|
|
83
|
+
const scope = await resolveSemanticScope(config, repository, commitSha, options.request);
|
|
84
|
+
const selection = await selectSemanticInputs(repository, commitSha, scope.selectors, config.semantic);
|
|
85
|
+
const issues = [...selection.issues];
|
|
86
|
+
const limited = scope.omittedDocuments.length > 0 || scope.ignoredSearchTerms > 0;
|
|
87
|
+
const complete = !limited && selection.selections.every((item) => item.status === 'matched');
|
|
88
|
+
if (limited)
|
|
89
|
+
issues.push({ code: 'selection-limited', severity: 'warning',
|
|
90
|
+
message: `Existing agent selection limits omitted ${scope.omittedDocuments.length} documents and ${scope.ignoredSearchTerms} search terms; adoption describes only the selected scope.`,
|
|
91
|
+
paths: scope.omittedDocuments });
|
|
92
|
+
const dryRun = options.dryRun ?? false;
|
|
93
|
+
const records = [];
|
|
94
|
+
if (!issues.some((issue) => issue.severity === 'error')) {
|
|
95
|
+
const current = new SemanticArtifactStore(resolveSemanticStorageRoots(repositoryRoot, config).primary);
|
|
96
|
+
// Do not scan sibling profiles or infer ownership from an artifact key. The
|
|
97
|
+
// supplied old configuration proves the exact legacy location to inspect.
|
|
98
|
+
const old = new SemanticArtifactStore(resolveStorageRoot(repositoryRoot, previous.config));
|
|
99
|
+
const identity = semanticProviderIdentity(config.semantic);
|
|
100
|
+
for (const source of selection.sources) {
|
|
101
|
+
const candidate = await inspectCandidate(source, config.semantic, identity, old, current);
|
|
102
|
+
const result = candidate.artifact && !dryRun
|
|
103
|
+
? await publishCandidate(candidate, config.semantic, identity, current) : candidate.record;
|
|
104
|
+
records.push(result);
|
|
105
|
+
if (problemStatuses.has(result.status))
|
|
106
|
+
issues.push({ code: `cache-${result.status}`, severity: 'error',
|
|
107
|
+
message: result.reason, paths: [result.path] });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const counts = {
|
|
111
|
+
adopted: 0, reusable: 0, planned: 0, missing: 0, corrupt: 0, unavailable: 0, conflict: 0,
|
|
112
|
+
};
|
|
113
|
+
for (const record of records)
|
|
114
|
+
counts[record.status]++;
|
|
115
|
+
return {
|
|
116
|
+
schemaVersion: 1, kind: 'semantic-cache-adoption', repositoryId: config.repositoryId,
|
|
117
|
+
ref, commitSha, configurationDigest: sha256(stableJson(config)),
|
|
118
|
+
previousConfigurationDigest: sha256(stableJson(previous.config)), dryRun,
|
|
119
|
+
ready: complete && !issues.some((issue) => issue.severity === 'error'), scope: { ...scope, complete },
|
|
120
|
+
records, counts, selections: selection.selections, budgets: selection.budgets, issues,
|
|
121
|
+
assumptions: [
|
|
122
|
+
'Selection uses the same configured or bounded agent scope as hydration planning at the resolved commit; only expected keys in the explicitly named previous profile are inspected.',
|
|
123
|
+
'Missing or incomplete previous artifacts need later extraction. Corrupt or conflicting complete artifacts are retained for diagnosis.',
|
|
124
|
+
'Adoption preserves original artifact identity and provenance; it does not validate or publish a current snapshot.',
|
|
125
|
+
'Artifacts are adopted independently; earlier valid copies remain available if a later artifact or storage operation fails.',
|
|
126
|
+
'No provider, authentication, indexing, ontology or repository adapter is initialized. Dry runs do not write cache files.',
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ContextGraphConfig } from '../configuration/config.js';
|
|
2
|
+
export declare const graphAssemblyVersion = 7;
|
|
3
|
+
export declare const graphAssemblyIdentity = "standalone-graph-v7";
|
|
4
|
+
/** Runtime expectations are separate from source/configuration identity and artifact reuse. */
|
|
5
|
+
export declare function requiredSnapshotExtractorVersions(config?: Pick<ContextGraphConfig, 'routingSources'>): Readonly<Record<string, string>>;
|
|
6
|
+
export declare function incompatibleSnapshotExtractors(actual: Readonly<Record<string, string>> | undefined, expected: Readonly<Record<string, string>>): readonly string[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { repositoryContractExtractorIdentity, scipSymbolExtractorIdentity } from '../extraction/static.js';
|
|
2
|
+
import { routingExtractorIdentity } from '../extraction/routing.js';
|
|
3
|
+
export const graphAssemblyVersion = 7;
|
|
4
|
+
export const graphAssemblyIdentity = `standalone-graph-v${graphAssemblyVersion}`;
|
|
5
|
+
/** Runtime expectations are separate from source/configuration identity and artifact reuse. */
|
|
6
|
+
export function requiredSnapshotExtractorVersions(config) {
|
|
7
|
+
return { assembly: graphAssemblyIdentity,
|
|
8
|
+
[repositoryContractExtractorIdentity.id]: repositoryContractExtractorIdentity.version,
|
|
9
|
+
[scipSymbolExtractorIdentity.id]: scipSymbolExtractorIdentity.version,
|
|
10
|
+
...(config?.routingSources?.length ? { [routingExtractorIdentity.id]: routingExtractorIdentity.version } : {}),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function incompatibleSnapshotExtractors(actual, expected) {
|
|
14
|
+
return Object.keys(expected).filter((id) => actual?.[id] !== expected[id]).sort();
|
|
15
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SemanticProviderError } from '../extraction/semantic-process.js';
|
|
2
|
+
import { SemanticPolicyError, type SemanticHydrationPhase } from '../extraction/semantic-diagnostics.js';
|
|
3
|
+
export type SemanticFailureCode = SemanticProviderError['code'] | SemanticPolicyError['code'] | 'cache-failure' | 'provider-failure';
|
|
4
|
+
export interface SemanticRunProgress {
|
|
5
|
+
readonly total: number;
|
|
6
|
+
readonly completed: number;
|
|
7
|
+
readonly extracted: number;
|
|
8
|
+
readonly reused: number;
|
|
9
|
+
readonly failed: number;
|
|
10
|
+
readonly cancelled: number;
|
|
11
|
+
readonly notStarted: number;
|
|
12
|
+
/** Includes failed, cancelled and not-started sources; it is not an extraction estimate. */
|
|
13
|
+
readonly remaining: number;
|
|
14
|
+
}
|
|
15
|
+
export interface SemanticHydrationFailure {
|
|
16
|
+
readonly schemaVersion: 1;
|
|
17
|
+
readonly kind: 'semantic-hydration-failure';
|
|
18
|
+
readonly code: SemanticFailureCode;
|
|
19
|
+
readonly category: 'timeout' | 'cancellation' | 'provider' | 'infrastructure' | 'extraction-gap';
|
|
20
|
+
readonly phase: SemanticHydrationPhase | 'semantic-scheduling';
|
|
21
|
+
/** Absent when cancellation occurs between sources, without one failing source. */
|
|
22
|
+
readonly sourceId?: string;
|
|
23
|
+
readonly commitSha: string;
|
|
24
|
+
readonly configurationDigest: string;
|
|
25
|
+
/** Processing time through failure, including cache checks/retries for a source. */
|
|
26
|
+
readonly elapsedMs: number;
|
|
27
|
+
readonly elapsedScope: 'source' | 'semantic-run';
|
|
28
|
+
readonly timeoutMs: number;
|
|
29
|
+
readonly timeoutScope: 'provider-attempt' | 'module-execution';
|
|
30
|
+
readonly progress: SemanticRunProgress;
|
|
31
|
+
readonly forced: boolean;
|
|
32
|
+
readonly recovery: readonly string[];
|
|
33
|
+
}
|
|
34
|
+
export declare function semanticFailureCode(error: unknown, phase: SemanticHydrationPhase, signal?: AbortSignal): SemanticFailureCode;
|
|
35
|
+
export declare function semanticFailureCategory(code: SemanticFailureCode): SemanticHydrationFailure['category'];
|
|
36
|
+
/** Public failure envelope deliberately excludes raw errors, causes and provider output. */
|
|
37
|
+
export declare class SemanticHydrationError extends Error {
|
|
38
|
+
readonly report: SemanticHydrationFailure;
|
|
39
|
+
readonly code: SemanticFailureCode;
|
|
40
|
+
constructor(report: SemanticHydrationFailure);
|
|
41
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { SemanticProviderError } from '../extraction/semantic-process.js';
|
|
2
|
+
import { SemanticPolicyError } from '../extraction/semantic-diagnostics.js';
|
|
3
|
+
export function semanticFailureCode(error, phase, signal) {
|
|
4
|
+
if (signal?.aborted && (error === signal.reason ||
|
|
5
|
+
(error instanceof Error && error.name === 'AbortError') ||
|
|
6
|
+
(phase === 'semantic-extract' && !(error instanceof SemanticProviderError))))
|
|
7
|
+
return 'cancelled';
|
|
8
|
+
if (error instanceof SemanticProviderError &&
|
|
9
|
+
['timeout', 'cancelled', 'unavailable', 'exit', 'invalid-output'].includes(error.code))
|
|
10
|
+
return error.code;
|
|
11
|
+
if (error instanceof SemanticPolicyError && ['unresolved', 'low-confidence'].includes(error.code))
|
|
12
|
+
return error.code;
|
|
13
|
+
if (phase === 'semantic-cache-read' || phase === 'semantic-cache-write')
|
|
14
|
+
return 'cache-failure';
|
|
15
|
+
return phase === 'semantic-validate' ? 'invalid-output' : 'provider-failure';
|
|
16
|
+
}
|
|
17
|
+
export function semanticFailureCategory(code) {
|
|
18
|
+
if (code === 'timeout')
|
|
19
|
+
return 'timeout';
|
|
20
|
+
if (code === 'cancelled')
|
|
21
|
+
return 'cancellation';
|
|
22
|
+
if (code === 'unavailable' || code === 'cache-failure')
|
|
23
|
+
return 'infrastructure';
|
|
24
|
+
if (code === 'invalid-output' || code === 'unresolved' || code === 'low-confidence')
|
|
25
|
+
return 'extraction-gap';
|
|
26
|
+
return 'provider';
|
|
27
|
+
}
|
|
28
|
+
/** Public failure envelope deliberately excludes raw errors, causes and provider output. */
|
|
29
|
+
export class SemanticHydrationError extends Error {
|
|
30
|
+
report;
|
|
31
|
+
code;
|
|
32
|
+
constructor(report) {
|
|
33
|
+
const p = report.progress;
|
|
34
|
+
super(`Semantic hydration ${report.code}: ${report.sourceId ?? 'between semantic sources'}; phase ${report.phase}; ` +
|
|
35
|
+
`${report.elapsedScope} elapsed ${report.elapsedMs} ms; configured ${report.timeoutScope} timeout ${report.timeoutMs} ms. ` +
|
|
36
|
+
`${p.completed}/${p.total} completed (${p.extracted} extracted, ${p.reused} reused); ` +
|
|
37
|
+
`${p.remaining} remaining (${p.failed} failed, ${p.cancelled} cancelled, ${p.notStarted} not started).\n` +
|
|
38
|
+
report.recovery.join('\n'));
|
|
39
|
+
this.report = report;
|
|
40
|
+
this.name = 'SemanticHydrationError';
|
|
41
|
+
this.code = report.code;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ImpactScanRequest } from '@weftlet/core';
|
|
2
2
|
import { type SemanticCacheInspection } from '../extraction/semantic-cache.js';
|
|
3
3
|
import { type SemanticInputIssue, type SemanticSelection } from '../extraction/semantic-sources.js';
|
|
4
|
+
import { type SemanticScope } from './semantic-scope.js';
|
|
4
5
|
export interface HydrationPlanOptions {
|
|
5
6
|
readonly ref?: string;
|
|
6
7
|
readonly forceSemantic?: boolean;
|
|
@@ -8,6 +9,8 @@ export interface HydrationPlanOptions {
|
|
|
8
9
|
}
|
|
9
10
|
export interface HydrationPlanDocument {
|
|
10
11
|
readonly path: string;
|
|
12
|
+
/** Local mapping for opaque IDs in semantic progress and failure reports. */
|
|
13
|
+
readonly sourceId: string;
|
|
11
14
|
readonly stableSemanticId: string;
|
|
12
15
|
readonly sourceDigest: string;
|
|
13
16
|
readonly bytes: number;
|
|
@@ -27,14 +30,8 @@ export interface HydrationPlan {
|
|
|
27
30
|
readonly commitSha: string;
|
|
28
31
|
readonly configurationDigest: string;
|
|
29
32
|
readonly ready: boolean;
|
|
30
|
-
readonly scope: {
|
|
31
|
-
readonly mode: 'configured' | 'agent';
|
|
32
|
-
readonly selectors: readonly string[];
|
|
33
|
-
readonly digest: string;
|
|
34
|
-
readonly headName: string;
|
|
33
|
+
readonly scope: SemanticScope & {
|
|
35
34
|
readonly complete: boolean;
|
|
36
|
-
readonly omittedDocuments: readonly string[];
|
|
37
|
-
readonly ignoredSearchTerms: number;
|
|
38
35
|
};
|
|
39
36
|
readonly documents: readonly HydrationPlanDocument[];
|
|
40
37
|
readonly selections: readonly SemanticSelection[];
|
|
@@ -1,42 +1,34 @@
|
|
|
1
1
|
import { loadConfig } from '../configuration/config.js';
|
|
2
|
-
import { resolveStorageRoot } from '../configuration/cache.js';
|
|
3
2
|
import { GitRepository } from '../repository/git.js';
|
|
4
3
|
import { matchesRepositoryGlob } from '../repository/glob.js';
|
|
5
|
-
import {
|
|
4
|
+
import { createSemanticArtifactStore } from './semantic-artifact-store.js';
|
|
6
5
|
import { inspectSemanticCache } from '../extraction/semantic-cache.js';
|
|
7
6
|
import { semanticProviderIdentity } from '../extraction/semantic-identity.js';
|
|
8
7
|
import { selectSemanticInputs } from '../extraction/semantic-sources.js';
|
|
9
8
|
import { sha256, stableJson } from '../infrastructure/util.js';
|
|
10
|
-
import {
|
|
9
|
+
import { resolveSemanticScope } from './semantic-scope.js';
|
|
10
|
+
import { semanticSourceId } from '../extraction/semantic-diagnostics.js';
|
|
11
11
|
async function describeDocument(source, selections, config, evidencePaths, store, forceSemantic = false) {
|
|
12
12
|
const inspection = forceSemantic
|
|
13
13
|
? { status: 'forced', reason: 'Explicit force bypasses existing artifacts; a new revision is allocated only during hydration.' }
|
|
14
14
|
: await inspectSemanticCache(store, config.semantic, source, semanticProviderIdentity(config.semantic));
|
|
15
15
|
const { artifact: _artifact, ...cache } = inspection;
|
|
16
16
|
const approvedBy = (config.approvedIntentSources ?? []).filter((pattern) => matchesRepositoryGlob(source.path, pattern));
|
|
17
|
-
return { path: source.path,
|
|
17
|
+
return { path: source.path, sourceId: semanticSourceId(config.repositoryId, source),
|
|
18
|
+
stableSemanticId: source.stableSemanticId, sourceDigest: source.digest,
|
|
18
19
|
bytes: Buffer.byteLength(source.content), lines: source.content.split('\n').length,
|
|
19
20
|
selectedBy: selections.filter((item) => item.paths.includes(source.path)).map((item) => item.selector),
|
|
20
21
|
role: evidencePaths.has(source.path) ? 'validation-evidence' : 'semantic-document',
|
|
21
22
|
sourceAuthority: approvedBy.length ? 'declared' : 'inferred', approvedBy,
|
|
22
23
|
extractionReview: 'unreviewed', cache };
|
|
23
24
|
}
|
|
24
|
-
async function resolvePlanningScope(config, repository, commitSha, request = {}) {
|
|
25
|
-
const agent = config.agentContext
|
|
26
|
-
? await new AgentSemanticScopeResolver(config, repository).resolve(request, commitSha)
|
|
27
|
-
: undefined;
|
|
28
|
-
const selectors = agent?.semanticSources ?? config.semanticSources;
|
|
29
|
-
return { mode: agent ? 'agent' : 'configured', selectors, digest: sha256(stableJson(selectors)),
|
|
30
|
-
headName: agent?.headName ?? 'default', omittedDocuments: agent?.omittedDocuments ?? [],
|
|
31
|
-
ignoredSearchTerms: agent?.ignoredSearchTerms ?? 0 };
|
|
32
|
-
}
|
|
33
25
|
/** Inspect only committed source objects, config and existing artifacts; never initialize a provider or adapter. */
|
|
34
26
|
export async function planHydration(configPath, options = {}) {
|
|
35
27
|
const { config, repositoryRoot } = await loadConfig(configPath);
|
|
36
28
|
const repository = new GitRepository(repositoryRoot);
|
|
37
29
|
const ref = options.ref ?? 'HEAD';
|
|
38
30
|
const commitSha = await repository.resolveRef(ref);
|
|
39
|
-
const scope = await
|
|
31
|
+
const scope = await resolveSemanticScope(config, repository, commitSha, options.request);
|
|
40
32
|
const selection = await selectSemanticInputs(repository, commitSha, scope.selectors, config.semantic);
|
|
41
33
|
const issues = [...selection.issues];
|
|
42
34
|
const { omittedDocuments, ignoredSearchTerms } = scope;
|
|
@@ -46,7 +38,7 @@ export async function planHydration(configPath, options = {}) {
|
|
|
46
38
|
issues.push({ code: 'selection-limited', severity: 'warning',
|
|
47
39
|
message: `Existing agent selection limits omitted ${omittedDocuments.length} documents and ${ignoredSearchTerms} search terms; this plan describes only the selected scope.`,
|
|
48
40
|
paths: omittedDocuments });
|
|
49
|
-
const store =
|
|
41
|
+
const store = createSemanticArtifactStore(repositoryRoot, config);
|
|
50
42
|
const evidencePaths = new Set(await repository.listFilesAt(commitSha, config.evidenceRoots));
|
|
51
43
|
const documents = [];
|
|
52
44
|
for (const source of selection.sources) {
|
|
@@ -4,18 +4,18 @@ import type { GitRepository } from "../repository/git.js";
|
|
|
4
4
|
import type { OntologyCompiler } from "../extraction/ontology.js";
|
|
5
5
|
import type { SemanticHydrator } from "../extraction/semantic.js";
|
|
6
6
|
import type { ScipIndexResult, StaticIndexer } from "../extraction/static.js";
|
|
7
|
+
import { type HydrationProgressEvent } from './semantic-run.js';
|
|
8
|
+
export type { HydrationProgressEvent } from './semantic-run.js';
|
|
7
9
|
export interface HydrationOptions {
|
|
8
10
|
readonly signal?: AbortSignal;
|
|
9
|
-
readonly onProgress?: (event:
|
|
10
|
-
phase: string;
|
|
11
|
-
completed?: number;
|
|
12
|
-
total?: number;
|
|
13
|
-
}) => void;
|
|
11
|
+
readonly onProgress?: (event: HydrationProgressEvent) => void;
|
|
14
12
|
readonly ref: string;
|
|
15
13
|
readonly headName: string;
|
|
16
14
|
readonly forceSemantic?: boolean;
|
|
17
15
|
readonly snapshotSuffix?: string;
|
|
18
16
|
readonly requireClean?: boolean;
|
|
17
|
+
/** Explicit local diagnostics may disclose repository identities, producers and citations. */
|
|
18
|
+
readonly validationDetails?: boolean;
|
|
19
19
|
readonly semanticSources?: readonly string[];
|
|
20
20
|
readonly runtimeEvidenceRevision?: RuntimeEvidenceRevision;
|
|
21
21
|
}
|