@opum-ai/lore 0.1.0 → 0.2.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 +33 -22
- package/bin/lore.cjs +35 -7
- package/package.json +17 -17
- package/src/adapters/backlog.ts +0 -1084
- package/src/adapters/git.ts +0 -221
- package/src/cli.ts +0 -667
- package/src/commands/agent.ts +0 -301
- package/src/commands/agents.ts +0 -302
- package/src/commands/args.ts +0 -209
- package/src/commands/changed.ts +0 -70
- package/src/commands/check.ts +0 -1031
- package/src/commands/codex-bridge.ts +0 -49
- package/src/commands/concurrency.ts +0 -48
- package/src/commands/context.ts +0 -292
- package/src/commands/discover.ts +0 -89
- package/src/commands/explorer.ts +0 -253
- package/src/commands/export.ts +0 -93
- package/src/commands/fswrite.ts +0 -928
- package/src/commands/graph.ts +0 -291
- package/src/commands/help.ts +0 -151
- package/src/commands/impact.ts +0 -59
- package/src/commands/init.ts +0 -583
- package/src/commands/instructions.ts +0 -91
- package/src/commands/link.ts +0 -929
- package/src/commands/new.ts +0 -476
- package/src/commands/orphans.ts +0 -457
- package/src/commands/path.ts +0 -67
- package/src/commands/provenance.ts +0 -68
- package/src/commands/query.ts +0 -312
- package/src/commands/reconcile-shared.ts +0 -280
- package/src/commands/rename.ts +0 -585
- package/src/commands/replace.ts +0 -320
- package/src/commands/scaffold.ts +0 -346
- package/src/commands/schema.ts +0 -293
- package/src/commands/snapshot.ts +0 -130
- package/src/commands/supersede.ts +0 -400
- package/src/commands/sync.ts +0 -371
- package/src/commands/tasks.ts +0 -271
- package/src/commands/traversal.ts +0 -151
- package/src/commands/validate.ts +0 -226
- package/src/config.ts +0 -598
- package/src/core/agent-bridge.ts +0 -287
- package/src/core/agent-context.ts +0 -498
- package/src/core/agent-profile.ts +0 -447
- package/src/core/bundle.ts +0 -893
- package/src/core/check.ts +0 -853
- package/src/core/codex-bridge.ts +0 -100
- package/src/core/concept.ts +0 -597
- package/src/core/consumer-scaffold.ts +0 -433
- package/src/core/context.ts +0 -271
- package/src/core/explorer-contract.ts +0 -441
- package/src/core/explorer-qualification.ts +0 -58
- package/src/core/explorer.ts +0 -518
- package/src/core/finding.ts +0 -31
- package/src/core/graph.ts +0 -201
- package/src/core/indexes.ts +0 -436
- package/src/core/instructions.ts +0 -209
- package/src/core/ladybug-driver.ts +0 -1795
- package/src/core/ladybug-lifecycle.ts +0 -1178
- package/src/core/ladybug-native.ts +0 -95
- package/src/core/ladybug-source.ts +0 -667
- package/src/core/links.ts +0 -681
- package/src/core/log.ts +0 -253
- package/src/core/managed-block.ts +0 -540
- package/src/core/manifest.ts +0 -718
- package/src/core/order.ts +0 -13
- package/src/core/profile.ts +0 -1007
- package/src/core/projection.ts +0 -195
- package/src/core/query.ts +0 -542
- package/src/core/reconcile.ts +0 -236
- package/src/core/replace.ts +0 -419
- package/src/core/retrieval.ts +0 -213
- package/src/core/rewrite.ts +0 -940
- package/src/core/scaffold.ts +0 -255
- package/src/core/schema.ts +0 -366
- package/src/core/snapshot-runtime.ts +0 -52
- package/src/core/snapshot-store.ts +0 -287
- package/src/core/snapshot.ts +0 -711
- package/src/core/template.ts +0 -429
- package/src/core/traversal.ts +0 -487
- package/src/core/validate.ts +0 -517
- package/src/core/workspace-contract.ts +0 -473
- package/src/core/workspace-projection.ts +0 -365
- package/src/core/workspace-retrieval.ts +0 -196
- package/src/core/workspace-source.ts +0 -174
- package/src/errors.ts +0 -697
- package/src/meta.ts +0 -7
- package/src/output.ts +0 -589
- package/src/scripts/upstream-backlog-watch.ts +0 -288
- package/src/state.ts +0 -390
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
/** Compose validated repository-local M6 exports into one namespaced workspace snapshot. */
|
|
2
|
-
|
|
3
|
-
import { LoreError } from "../errors";
|
|
4
|
-
import { VERSION } from "../meta";
|
|
5
|
-
import type { BundleGraph, Edge, EdgeKind } from "./bundle";
|
|
6
|
-
import type { Concept } from "./concept";
|
|
7
|
-
import { EXPECTED_LADYBUG_STORAGE_VERSION, EXPECTED_LADYBUG_VERSION } from "./ladybug-native";
|
|
8
|
-
import {
|
|
9
|
-
canonicalJson,
|
|
10
|
-
digest,
|
|
11
|
-
type LadybugProjectionSource,
|
|
12
|
-
type ProjectionConceptRecord,
|
|
13
|
-
type ProjectionEdgeRecord,
|
|
14
|
-
type ProjectionTaskRecord,
|
|
15
|
-
prepareLadybugProjectionSource,
|
|
16
|
-
} from "./ladybug-source";
|
|
17
|
-
import { type ProjectionRecord, projectionStreamHash } from "./projection";
|
|
18
|
-
import {
|
|
19
|
-
buildWorkspaceIdentity,
|
|
20
|
-
namespaceWorkspaceRecord,
|
|
21
|
-
qualifyWorkspaceId,
|
|
22
|
-
type WorkspaceIdentity,
|
|
23
|
-
type WorkspaceManifest,
|
|
24
|
-
type WorkspaceRecordProvenance,
|
|
25
|
-
type WorkspaceRepositoryIdentity,
|
|
26
|
-
type WorkspaceResultScope,
|
|
27
|
-
} from "./workspace-contract";
|
|
28
|
-
|
|
29
|
-
export interface WorkspaceMemberSource {
|
|
30
|
-
readonly memberId: string;
|
|
31
|
-
readonly root: string;
|
|
32
|
-
readonly source: LadybugProjectionSource;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface WorkspaceProjectedLink {
|
|
36
|
-
readonly linkId: string;
|
|
37
|
-
readonly kind: string;
|
|
38
|
-
readonly recordKey: string;
|
|
39
|
-
readonly from: WorkspaceRecordProvenance;
|
|
40
|
-
readonly to: WorkspaceRecordProvenance;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface WorkspaceProjection {
|
|
44
|
-
readonly identity: WorkspaceIdentity;
|
|
45
|
-
readonly scope: WorkspaceResultScope;
|
|
46
|
-
readonly graph: BundleGraph;
|
|
47
|
-
readonly provenanceById: ReadonlyMap<string, WorkspaceRecordProvenance>;
|
|
48
|
-
readonly taskProvenanceById: ReadonlyMap<string, WorkspaceRecordProvenance>;
|
|
49
|
-
readonly links: readonly WorkspaceProjectedLink[];
|
|
50
|
-
readonly ladybugSource: LadybugProjectionSource;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Build a complete deterministic workspace projection from all explicit members. */
|
|
54
|
-
export function buildWorkspaceProjection(
|
|
55
|
-
manifest: WorkspaceManifest,
|
|
56
|
-
members: readonly WorkspaceMemberSource[],
|
|
57
|
-
): WorkspaceProjection {
|
|
58
|
-
const ordered = [...members].sort((a, b) => compare(a.memberId, b.memberId));
|
|
59
|
-
const identity = buildWorkspaceIdentity(
|
|
60
|
-
manifest,
|
|
61
|
-
ordered.map(({ memberId, source }) => ({
|
|
62
|
-
memberId,
|
|
63
|
-
repositoryScopeKey: source.repositoryScopeKey,
|
|
64
|
-
bundleId: source.manifest.bundle.id,
|
|
65
|
-
gitCommit: source.manifest.bundle.gitCommit,
|
|
66
|
-
exportDigest: source.exportDigest,
|
|
67
|
-
})),
|
|
68
|
-
);
|
|
69
|
-
const repositoryByMember = new Map(identity.repositories.map((repository) => [repository.memberId, repository]));
|
|
70
|
-
const conceptKeyByMemberSource = new Map<string, string>();
|
|
71
|
-
const taskKeyByMemberSource = new Map<string, string>();
|
|
72
|
-
const conceptProvenanceByMemberId = new Map<string, WorkspaceRecordProvenance>();
|
|
73
|
-
const taskProvenanceByMemberId = new Map<string, WorkspaceRecordProvenance>();
|
|
74
|
-
const provenanceById = new Map<string, WorkspaceRecordProvenance>();
|
|
75
|
-
const taskProvenanceById = new Map<string, WorkspaceRecordProvenance>();
|
|
76
|
-
const concepts: ProjectionConceptRecord[] = [];
|
|
77
|
-
const tasks: ProjectionTaskRecord[] = [];
|
|
78
|
-
const authoredEdges: ProjectionEdgeRecord[] = [];
|
|
79
|
-
const graphEdges: Edge[] = [];
|
|
80
|
-
const graphConcepts = new Map<string, Concept>();
|
|
81
|
-
const tokenEstimates = new Map<string, number>();
|
|
82
|
-
|
|
83
|
-
for (const member of ordered) {
|
|
84
|
-
const repository = requiredRepository(repositoryByMember, member.memberId);
|
|
85
|
-
for (const record of member.source.concepts) {
|
|
86
|
-
const qualifiedId = qualifyWorkspaceId(member.memberId, record.id);
|
|
87
|
-
const identityRecord = namespaceWorkspaceRecord(repository, "concept", record.key, record.path);
|
|
88
|
-
const provenance = provenanceFor(repository, "concept", record.id, identityRecord);
|
|
89
|
-
conceptKeyByMemberSource.set(memberRecordKey(member.memberId, record.key), identityRecord.workspaceRecordKey);
|
|
90
|
-
conceptProvenanceByMemberId.set(memberRecordKey(member.memberId, record.id), provenance);
|
|
91
|
-
provenanceById.set(qualifiedId, provenance);
|
|
92
|
-
concepts.push({
|
|
93
|
-
...record,
|
|
94
|
-
key: identityRecord.workspaceRecordKey,
|
|
95
|
-
id: qualifiedId,
|
|
96
|
-
path: `workspace/${member.memberId}/${record.path}`,
|
|
97
|
-
});
|
|
98
|
-
graphConcepts.set(qualifiedId, {
|
|
99
|
-
id: qualifiedId,
|
|
100
|
-
path: `workspace/${member.memberId}/${record.path}`,
|
|
101
|
-
type: record.type,
|
|
102
|
-
frontmatter: record.frontmatter,
|
|
103
|
-
body: record.body,
|
|
104
|
-
});
|
|
105
|
-
tokenEstimates.set(qualifiedId, record.tokenEstimate);
|
|
106
|
-
}
|
|
107
|
-
for (const record of member.source.tasks) {
|
|
108
|
-
const qualifiedId = qualifyWorkspaceId(member.memberId, record.id);
|
|
109
|
-
const identityRecord = namespaceWorkspaceRecord(repository, "task", record.key, null);
|
|
110
|
-
const provenance = provenanceFor(repository, "task", record.id, identityRecord);
|
|
111
|
-
taskKeyByMemberSource.set(memberRecordKey(member.memberId, record.key), identityRecord.workspaceRecordKey);
|
|
112
|
-
taskProvenanceByMemberId.set(memberRecordKey(member.memberId, record.id), provenance);
|
|
113
|
-
taskProvenanceById.set(qualifiedId, provenance);
|
|
114
|
-
tasks.push({ ...record, key: identityRecord.workspaceRecordKey, id: qualifiedId });
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
for (const member of ordered) {
|
|
119
|
-
const repository = requiredRepository(repositoryByMember, member.memberId);
|
|
120
|
-
const source = member.source;
|
|
121
|
-
const conceptIdByKey = new Map(
|
|
122
|
-
source.concepts.map((record) => [record.key, qualifyWorkspaceId(member.memberId, record.id)]),
|
|
123
|
-
);
|
|
124
|
-
for (const record of source.authoredEdges) {
|
|
125
|
-
const identityRecord = namespaceWorkspaceRecord(repository, "authored-edge", record.key, null);
|
|
126
|
-
const from = conceptKeyByMemberSource.get(memberRecordKey(member.memberId, record.from));
|
|
127
|
-
const to =
|
|
128
|
-
record.to === null
|
|
129
|
-
? null
|
|
130
|
-
: (record.kind === "task" ? taskKeyByMemberSource : conceptKeyByMemberSource).get(
|
|
131
|
-
memberRecordKey(member.memberId, record.to),
|
|
132
|
-
);
|
|
133
|
-
if (from === undefined || (record.to !== null && to === undefined)) {
|
|
134
|
-
invalidWorkspace(`member ${member.memberId} contains an edge with an unknown endpoint`);
|
|
135
|
-
}
|
|
136
|
-
authoredEdges.push({
|
|
137
|
-
...record,
|
|
138
|
-
key: identityRecord.workspaceRecordKey,
|
|
139
|
-
from,
|
|
140
|
-
to: to ?? null,
|
|
141
|
-
target: record.dangling ? record.target : qualifyWorkspaceId(member.memberId, record.target),
|
|
142
|
-
workspaceSourceRecordKey: record.key,
|
|
143
|
-
});
|
|
144
|
-
if (record.kind !== "task") {
|
|
145
|
-
const graphFrom = conceptIdByKey.get(record.from);
|
|
146
|
-
const graphTo = record.to === null ? null : conceptIdByKey.get(record.to);
|
|
147
|
-
if (graphFrom === undefined || (record.to !== null && graphTo === undefined)) {
|
|
148
|
-
invalidWorkspace(`member ${member.memberId} contains a concept edge with an unknown endpoint`);
|
|
149
|
-
}
|
|
150
|
-
graphEdges.push({
|
|
151
|
-
from: graphFrom,
|
|
152
|
-
to: graphTo ?? null,
|
|
153
|
-
kind: record.kind as EdgeKind,
|
|
154
|
-
target: record.dangling ? record.target : qualifyWorkspaceId(member.memberId, record.target),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const links: WorkspaceProjectedLink[] = [];
|
|
161
|
-
for (const link of identity.links) {
|
|
162
|
-
const fromMap = link.source.from.kind === "concept" ? conceptProvenanceByMemberId : taskProvenanceByMemberId;
|
|
163
|
-
const toMap = link.source.to.kind === "concept" ? conceptProvenanceByMemberId : taskProvenanceByMemberId;
|
|
164
|
-
const from = fromMap.get(memberRecordKey(link.source.from.memberId, link.source.from.id));
|
|
165
|
-
const to = toMap.get(memberRecordKey(link.source.to.memberId, link.source.to.id));
|
|
166
|
-
if (from === undefined || to === undefined) {
|
|
167
|
-
invalidWorkspace(`explicit link ${link.linkId} names an endpoint absent from the selected exports`);
|
|
168
|
-
}
|
|
169
|
-
links.push({ linkId: link.linkId, kind: link.source.kind, recordKey: link.workspaceLinkKey, from, to });
|
|
170
|
-
authoredEdges.push({
|
|
171
|
-
record: "edge",
|
|
172
|
-
key: link.workspaceLinkKey,
|
|
173
|
-
from: from.recordKey,
|
|
174
|
-
to: to.recordKey,
|
|
175
|
-
kind: link.source.to.kind === "task" ? "task" : "link",
|
|
176
|
-
target: qualifyWorkspaceId(link.source.to.memberId, link.source.to.id),
|
|
177
|
-
ordinal: authoredEdges.length,
|
|
178
|
-
dangling: false,
|
|
179
|
-
workspaceFromKind: link.source.from.kind,
|
|
180
|
-
workspaceToKind: link.source.to.kind,
|
|
181
|
-
workspaceLinkKind: link.source.kind,
|
|
182
|
-
workspaceSourceRecordKey: link.linkId,
|
|
183
|
-
});
|
|
184
|
-
// Context/graph topology is concept-only today. Preserve every exact
|
|
185
|
-
// authored endpoint in the projection while adding a structural edge only
|
|
186
|
-
// when both endpoints are concepts.
|
|
187
|
-
if (link.source.from.kind === "concept" && link.source.to.kind === "concept") {
|
|
188
|
-
graphEdges.push({
|
|
189
|
-
from: qualifyWorkspaceId(link.source.from.memberId, link.source.from.id),
|
|
190
|
-
to: qualifyWorkspaceId(link.source.to.memberId, link.source.to.id),
|
|
191
|
-
kind: "link",
|
|
192
|
-
target: qualifyWorkspaceId(link.source.to.memberId, link.source.to.id),
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
concepts.sort((a, b) => compare(a.id, b.id));
|
|
198
|
-
tasks.sort((a, b) => compare(a.id.toLowerCase(), b.id.toLowerCase()) || compare(a.id, b.id));
|
|
199
|
-
graphEdges.sort((a, b) => compare(a.from, b.from) || compare(a.to ?? "", b.to ?? "") || compare(a.kind, b.kind));
|
|
200
|
-
const manifestRecord: ProjectionRecord = {
|
|
201
|
-
record: "manifest",
|
|
202
|
-
schemaVersion: "1.0",
|
|
203
|
-
bundle: { id: identity.workspaceKey, okfVersion: "workspace/1", docsRoot: "workspace", gitCommit: null },
|
|
204
|
-
exporter: { name: "lore", version: VERSION },
|
|
205
|
-
generatedAt: null,
|
|
206
|
-
normalizationVersion: "1",
|
|
207
|
-
};
|
|
208
|
-
const semanticRecords: ProjectionRecord[] = [manifestRecord, ...concepts, ...authoredEdges, ...tasks];
|
|
209
|
-
const trailer: ProjectionRecord = {
|
|
210
|
-
record: "trailer",
|
|
211
|
-
recordCount: semanticRecords.length,
|
|
212
|
-
streamHash: projectionStreamHash(semanticRecords),
|
|
213
|
-
};
|
|
214
|
-
const records = [...semanticRecords, trailer];
|
|
215
|
-
const identityBytes = canonicalJson(identity);
|
|
216
|
-
const ladybugSource = prepareLadybugProjectionSource({
|
|
217
|
-
projection: { records, jsonl: `${records.map((record) => JSON.stringify(record)).join("\n")}\n` },
|
|
218
|
-
inventory: [
|
|
219
|
-
{
|
|
220
|
-
path: "workspace.identity.json",
|
|
221
|
-
byteLength: Buffer.byteLength(identityBytes),
|
|
222
|
-
byteHash: digest(identityBytes),
|
|
223
|
-
},
|
|
224
|
-
],
|
|
225
|
-
profileInventory: [],
|
|
226
|
-
ladybugVersion: EXPECTED_LADYBUG_VERSION,
|
|
227
|
-
ladybugStorageVersion: EXPECTED_LADYBUG_STORAGE_VERSION,
|
|
228
|
-
loreVersion: VERSION,
|
|
229
|
-
warnings: ordered.flatMap((member) => member.source.warnings.map((warning) => `[${member.memberId}] ${warning}`)),
|
|
230
|
-
});
|
|
231
|
-
const scope = scopeFor(manifest, identity);
|
|
232
|
-
return {
|
|
233
|
-
identity,
|
|
234
|
-
scope,
|
|
235
|
-
graph: workspaceGraph(graphConcepts, graphEdges, tokenEstimates),
|
|
236
|
-
provenanceById,
|
|
237
|
-
taskProvenanceById,
|
|
238
|
-
links,
|
|
239
|
-
ladybugSource,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export function selectWorkspaceProjection(
|
|
244
|
-
projection: WorkspaceProjection,
|
|
245
|
-
memberIds: readonly string[],
|
|
246
|
-
): WorkspaceProjection {
|
|
247
|
-
if (memberIds.length === 0) return projection;
|
|
248
|
-
const requested = new Set(memberIds);
|
|
249
|
-
if (requested.size !== memberIds.length) invalidWorkspace("repository selectors must be unique");
|
|
250
|
-
const known = new Set(projection.identity.repositories.map((repository) => repository.memberId));
|
|
251
|
-
for (const memberId of requested) if (!known.has(memberId)) invalidWorkspace(`unknown workspace member ${memberId}`);
|
|
252
|
-
const selected = projection.identity.repositories.filter((repository) => requested.has(repository.memberId));
|
|
253
|
-
const allowed = new Set(selected.map((repository) => repository.memberId));
|
|
254
|
-
const concepts = new Map([...projection.graph.concepts].filter(([id]) => allowed.has(memberOfQualifiedId(id))));
|
|
255
|
-
const provenanceById = new Map([...projection.provenanceById].filter(([, value]) => allowed.has(value.memberId)));
|
|
256
|
-
const taskProvenanceById = new Map(
|
|
257
|
-
[...projection.taskProvenanceById].filter(([, value]) => allowed.has(value.memberId)),
|
|
258
|
-
);
|
|
259
|
-
const edges = projection.graph.edges.filter(
|
|
260
|
-
(edge) => concepts.has(edge.from) && (edge.to === null || concepts.has(edge.to)),
|
|
261
|
-
);
|
|
262
|
-
const tokens = new Map([...concepts].map(([id]) => [id, projection.graph.tokenEstimate(id)]));
|
|
263
|
-
return {
|
|
264
|
-
...projection,
|
|
265
|
-
scope: {
|
|
266
|
-
...projection.scope,
|
|
267
|
-
repositories: projection.scope.repositories.filter((repo) => allowed.has(repo.memberId)),
|
|
268
|
-
},
|
|
269
|
-
graph: workspaceGraph(concepts, edges, tokens),
|
|
270
|
-
provenanceById,
|
|
271
|
-
taskProvenanceById,
|
|
272
|
-
links: projection.links.filter((link) => allowed.has(link.from.memberId) && allowed.has(link.to.memberId)),
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function scopeFor(manifest: WorkspaceManifest, identity: WorkspaceIdentity): WorkspaceResultScope {
|
|
277
|
-
return {
|
|
278
|
-
schemaVersion: "lore-workspace-result-scope/1",
|
|
279
|
-
workspaceId: manifest.workspaceId,
|
|
280
|
-
workspaceKey: identity.workspaceKey,
|
|
281
|
-
snapshotKey: identity.snapshotKey,
|
|
282
|
-
repositories: identity.repositories.map((repository) => repositoryResult(repository)),
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function provenanceFor(
|
|
287
|
-
repository: WorkspaceRepositoryIdentity,
|
|
288
|
-
recordKind: WorkspaceRecordProvenance["recordKind"],
|
|
289
|
-
sourceId: string,
|
|
290
|
-
record: ReturnType<typeof namespaceWorkspaceRecord>,
|
|
291
|
-
): WorkspaceRecordProvenance {
|
|
292
|
-
return {
|
|
293
|
-
...repositoryResult(repository),
|
|
294
|
-
recordKind,
|
|
295
|
-
recordKey: record.workspaceRecordKey,
|
|
296
|
-
sourceRecordKey: record.sourceRecordKey,
|
|
297
|
-
sourceKey: record.workspaceSourceKey,
|
|
298
|
-
sourcePath: record.sourcePath,
|
|
299
|
-
sourceId,
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function repositoryResult(repository: WorkspaceRepositoryIdentity) {
|
|
304
|
-
return {
|
|
305
|
-
memberId: repository.memberId,
|
|
306
|
-
repositoryKey: repository.repositoryKey,
|
|
307
|
-
repositoryScopeKey: repository.source.repositoryScopeKey,
|
|
308
|
-
bundleKey: repository.bundleKey,
|
|
309
|
-
bundleId: repository.source.bundleId,
|
|
310
|
-
commitKey: repository.commitKey,
|
|
311
|
-
gitCommit: repository.source.gitCommit,
|
|
312
|
-
exportKey: repository.exportKey,
|
|
313
|
-
exportDigest: repository.source.exportDigest,
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function workspaceGraph(
|
|
318
|
-
concepts: ReadonlyMap<string, Concept>,
|
|
319
|
-
edges: readonly Edge[],
|
|
320
|
-
tokens: ReadonlyMap<string, number>,
|
|
321
|
-
): BundleGraph {
|
|
322
|
-
let total: number | undefined;
|
|
323
|
-
return {
|
|
324
|
-
concepts,
|
|
325
|
-
edges,
|
|
326
|
-
tokenEstimate(id?: string): number {
|
|
327
|
-
if (id === undefined) {
|
|
328
|
-
total ??= [...tokens.values()].reduce((sum, value) => sum + value, 0);
|
|
329
|
-
return total;
|
|
330
|
-
}
|
|
331
|
-
const value = tokens.get(id);
|
|
332
|
-
if (value === undefined) throw new LoreError("not_found", `concept "${id}" is not in the workspace`);
|
|
333
|
-
return value;
|
|
334
|
-
},
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function requiredRepository(
|
|
339
|
-
repositories: ReadonlyMap<string, WorkspaceRepositoryIdentity>,
|
|
340
|
-
memberId: string,
|
|
341
|
-
): WorkspaceRepositoryIdentity {
|
|
342
|
-
const repository = repositories.get(memberId);
|
|
343
|
-
if (repository === undefined) invalidWorkspace(`identity is missing member ${memberId}`);
|
|
344
|
-
return repository;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function memberRecordKey(memberId: string, value: string): string {
|
|
348
|
-
return `${memberId}\0${value}`;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function memberOfQualifiedId(id: string): string {
|
|
352
|
-
return id.slice(0, id.indexOf("::"));
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function compare(a: string, b: string): number {
|
|
356
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function invalidWorkspace(message: string): never {
|
|
360
|
-
throw new LoreError(
|
|
361
|
-
"validation",
|
|
362
|
-
`workspace projection is invalid: ${message}`,
|
|
363
|
-
"fix the explicit workspace manifest or selected repository exports",
|
|
364
|
-
);
|
|
365
|
-
}
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/** Indexed/reference retrieval for one explicitly selected workspace. */
|
|
2
|
-
|
|
3
|
-
import type { BacklogAdapter } from "../adapters/backlog";
|
|
4
|
-
import { LoreError, WarningCollector } from "../errors";
|
|
5
|
-
import { reconcileLadybugProjection } from "./ladybug-lifecycle";
|
|
6
|
-
import {
|
|
7
|
-
type LadybugIndexedReader,
|
|
8
|
-
type LadybugNativeLoader,
|
|
9
|
-
loadLadybugNativeDriver,
|
|
10
|
-
memoizeLadybugNativeLoader,
|
|
11
|
-
supportsLadybugNative,
|
|
12
|
-
} from "./ladybug-native";
|
|
13
|
-
import { query } from "./query";
|
|
14
|
-
import type { RetrievalGraph, RetrievalPolicy } from "./retrieval";
|
|
15
|
-
import { buildTraversalSnapshot, type TraversalSnapshot, type TraversalSourceRecords } from "./traversal";
|
|
16
|
-
import type { WorkspaceRecordProvenance, WorkspaceResultScope } from "./workspace-contract";
|
|
17
|
-
import type { WorkspaceProjectedLink, WorkspaceProjection } from "./workspace-projection";
|
|
18
|
-
import { selectWorkspaceProjection } from "./workspace-projection";
|
|
19
|
-
import { type LoadWorkspaceProjectionOptions, loadWorkspaceProjection } from "./workspace-source";
|
|
20
|
-
|
|
21
|
-
export interface WorkspaceRetrievalSelection {
|
|
22
|
-
readonly manifestPath: string;
|
|
23
|
-
readonly memberIds: readonly string[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface WorkspaceRetrievalContext {
|
|
27
|
-
readonly scope: WorkspaceResultScope;
|
|
28
|
-
readonly provenanceById: ReadonlyMap<string, WorkspaceRecordProvenance>;
|
|
29
|
-
readonly taskProvenanceById: ReadonlyMap<string, WorkspaceRecordProvenance>;
|
|
30
|
-
readonly links: readonly WorkspaceProjectedLink[];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface LoadWorkspaceRetrievalOptions {
|
|
34
|
-
readonly root: string;
|
|
35
|
-
readonly selection: WorkspaceRetrievalSelection;
|
|
36
|
-
readonly warnings?: WarningCollector;
|
|
37
|
-
readonly adapter?: BacklogAdapter;
|
|
38
|
-
readonly policy?: RetrievalPolicy;
|
|
39
|
-
readonly platform?: NodeJS.Platform;
|
|
40
|
-
readonly loadNativeDriver?: LadybugNativeLoader;
|
|
41
|
-
readonly sourceOptions?: Omit<LoadWorkspaceProjectionOptions, "root" | "manifestPath" | "warnings">;
|
|
42
|
-
readonly includeTraversal?: boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function loadWorkspaceRetrievalGraph(options: LoadWorkspaceRetrievalOptions): Promise<RetrievalGraph> {
|
|
46
|
-
const policy = options.policy ?? "auto";
|
|
47
|
-
if (policy === "reference" || !supportsLadybugNative(options.platform)) {
|
|
48
|
-
if (policy === "indexed" && !supportsLadybugNative(options.platform)) throw indexedUnavailable();
|
|
49
|
-
return loadReference(options);
|
|
50
|
-
}
|
|
51
|
-
const loadNative = memoizeLadybugNativeLoader(options.loadNativeDriver ?? loadLadybugNativeDriver);
|
|
52
|
-
let latest: WorkspaceProjection | undefined;
|
|
53
|
-
let latestWarnings = new WarningCollector();
|
|
54
|
-
const loadCandidate = async () => {
|
|
55
|
-
const attemptWarnings = new WarningCollector();
|
|
56
|
-
const loaded = await loadWorkspaceProjection({
|
|
57
|
-
root: options.root,
|
|
58
|
-
manifestPath: options.selection.manifestPath,
|
|
59
|
-
warnings: attemptWarnings,
|
|
60
|
-
...options.sourceOptions,
|
|
61
|
-
});
|
|
62
|
-
latest = loaded.projection;
|
|
63
|
-
latestWarnings = attemptWarnings;
|
|
64
|
-
return loaded.projection.ladybugSource;
|
|
65
|
-
};
|
|
66
|
-
try {
|
|
67
|
-
// Load once to resolve the stable workspace key that owns this contained cache.
|
|
68
|
-
await loadCandidate();
|
|
69
|
-
const workspaceSegment = select(latest, []).identity.workspaceKey.replace(/^sha256:/u, "");
|
|
70
|
-
const lifecycle = await reconcileLadybugProjection({
|
|
71
|
-
root: options.root,
|
|
72
|
-
cacheRelRoot: `.lore/cache/workspaces/1/${workspaceSegment}`,
|
|
73
|
-
retainOnlyCurrent: true,
|
|
74
|
-
loadSource: loadCandidate,
|
|
75
|
-
loadNativeDriver: loadNative,
|
|
76
|
-
});
|
|
77
|
-
if (lifecycle.generation === undefined) {
|
|
78
|
-
if (policy === "indexed") throw indexedUnavailable();
|
|
79
|
-
return referenceFromProjection(select(latest, options.selection.memberIds), options.includeTraversal);
|
|
80
|
-
}
|
|
81
|
-
const projection = select(latest, options.selection.memberIds);
|
|
82
|
-
const native = await loadNative();
|
|
83
|
-
const reader = native.openLadybugIndexedReader(lifecycle.generation.databasePath, lifecycle.source);
|
|
84
|
-
const indexed = subsetReader(reader, projection, options.selection.memberIds.length > 0);
|
|
85
|
-
const traversal = options.includeTraversal
|
|
86
|
-
? traversalFor(projection, lifecycle.source, (await reader.readTraversalRecords?.()) ?? lifecycle.source)
|
|
87
|
-
: undefined;
|
|
88
|
-
// The lifecycle may retry source capture. Publish only the last successful
|
|
89
|
-
// attempt's advisories after the indexed reader has opened completely.
|
|
90
|
-
copyWarnings(latestWarnings, options.warnings);
|
|
91
|
-
return {
|
|
92
|
-
graph: projection.graph,
|
|
93
|
-
indexed,
|
|
94
|
-
dispose: () => indexed.close(),
|
|
95
|
-
backend: "indexed",
|
|
96
|
-
provenance: {
|
|
97
|
-
repositoryScopeKey: lifecycle.source.repositoryScopeKey,
|
|
98
|
-
snapshotKey: lifecycle.source.snapshotKey,
|
|
99
|
-
sourceFingerprint: lifecycle.source.sourceFingerprint,
|
|
100
|
-
exportDigest: lifecycle.source.exportDigest,
|
|
101
|
-
gitCommit: null,
|
|
102
|
-
},
|
|
103
|
-
workspace: contextFor(projection),
|
|
104
|
-
...(traversal !== undefined ? { traversal } : {}),
|
|
105
|
-
};
|
|
106
|
-
} catch (cause) {
|
|
107
|
-
if (policy === "indexed") throw cause;
|
|
108
|
-
// Invalid/missing/conflicting explicit candidates still fail loud here; the
|
|
109
|
-
// reference load applies the same manifest/export validation and never leaks
|
|
110
|
-
// a partial indexed candidate.
|
|
111
|
-
try {
|
|
112
|
-
return await loadReference(options);
|
|
113
|
-
} catch {
|
|
114
|
-
throw cause;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async function loadReference(options: LoadWorkspaceRetrievalOptions): Promise<RetrievalGraph> {
|
|
120
|
-
const loaded = await loadWorkspaceProjection({
|
|
121
|
-
root: options.root,
|
|
122
|
-
manifestPath: options.selection.manifestPath,
|
|
123
|
-
warnings: options.warnings,
|
|
124
|
-
...options.sourceOptions,
|
|
125
|
-
});
|
|
126
|
-
return referenceFromProjection(
|
|
127
|
-
selectWorkspaceProjection(loaded.projection, options.selection.memberIds),
|
|
128
|
-
options.includeTraversal,
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function referenceFromProjection(projection: WorkspaceProjection, includeTraversal = false): RetrievalGraph {
|
|
133
|
-
return {
|
|
134
|
-
graph: projection.graph,
|
|
135
|
-
backend: "reference",
|
|
136
|
-
workspace: contextFor(projection),
|
|
137
|
-
...(includeTraversal ? { traversal: traversalFor(projection, projection.ladybugSource) } : {}),
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function select(projection: WorkspaceProjection | undefined, memberIds: readonly string[]): WorkspaceProjection {
|
|
142
|
-
if (projection === undefined) throw new LoreError("drift", "workspace source was not retained after reconciliation");
|
|
143
|
-
return selectWorkspaceProjection(projection, memberIds);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function subsetReader(
|
|
147
|
-
reader: LadybugIndexedReader,
|
|
148
|
-
projection: WorkspaceProjection,
|
|
149
|
-
selectedSubset: boolean,
|
|
150
|
-
): LadybugIndexedReader {
|
|
151
|
-
if (!selectedSubset) return reader;
|
|
152
|
-
const readTraversalRecords = reader.readTraversalRecords?.bind(reader);
|
|
153
|
-
return {
|
|
154
|
-
readBundleGraph: async () => projection.graph,
|
|
155
|
-
readConceptBody: async (id) => projection.graph.concepts.get(id)?.body,
|
|
156
|
-
query: async (options) => query(projection.graph, options),
|
|
157
|
-
...(readTraversalRecords !== undefined ? { readTraversalRecords } : {}),
|
|
158
|
-
close: () => reader.close(),
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function traversalFor(
|
|
163
|
-
projection: WorkspaceProjection,
|
|
164
|
-
source: WorkspaceProjection["ladybugSource"],
|
|
165
|
-
records: TraversalSourceRecords = source,
|
|
166
|
-
): TraversalSnapshot {
|
|
167
|
-
const allowedIds = new Set([...projection.provenanceById.keys(), ...projection.taskProvenanceById.keys()]);
|
|
168
|
-
return buildTraversalSnapshot(source, records, {
|
|
169
|
-
workspace: projection.scope,
|
|
170
|
-
conceptProvenanceById: projection.provenanceById,
|
|
171
|
-
taskProvenanceById: projection.taskProvenanceById,
|
|
172
|
-
allowedIds,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function contextFor(projection: WorkspaceProjection): WorkspaceRetrievalContext {
|
|
177
|
-
return {
|
|
178
|
-
scope: projection.scope,
|
|
179
|
-
provenanceById: projection.provenanceById,
|
|
180
|
-
taskProvenanceById: projection.taskProvenanceById,
|
|
181
|
-
links: projection.links,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function copyWarnings(from: WarningCollector, to?: WarningCollector): void {
|
|
186
|
-
if (to === undefined) return;
|
|
187
|
-
to.merge(from);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function indexedUnavailable(): LoreError {
|
|
191
|
-
return new LoreError(
|
|
192
|
-
"validation",
|
|
193
|
-
"verified indexed workspace retrieval is unavailable",
|
|
194
|
-
"retry after the explicit workspace projection can be rebuilt, or use Lore's automatic in-memory fallback",
|
|
195
|
-
);
|
|
196
|
-
}
|