@openclawbrain/cli 0.4.35 → 0.4.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli.d.ts +69 -1
- package/dist/src/cli.js +817 -4
- package/dist/src/graphify-compiled-artifacts.d.ts +127 -0
- package/dist/src/graphify-compiled-artifacts.js +1185 -0
- package/dist/src/graphify-import-slice.js +1091 -0
- package/dist/src/graphify-lints.js +977 -0
- package/dist/src/graphify-maintenance-diff.d.ts +167 -0
- package/dist/src/graphify-maintenance-diff.js +1288 -0
- package/dist/src/graphify-runner.js +745 -0
- package/dist/src/import-export.d.ts +127 -0
- package/dist/src/import-export.js +938 -26
- package/dist/src/index.js +4 -2
- package/dist/src/session-store.js +37 -0
- package/dist/src/session-tail.js +111 -2
- package/package.json +9 -9
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { canonicalJson } from "@openclawbrain/contracts";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const defaultRepoRoot = path.resolve(__dirname, "../../../..");
|
|
11
|
+
const defaultWorkspaceRoot = path.resolve(defaultRepoRoot, "..");
|
|
12
|
+
const defaultOutputRoot = path.join(defaultWorkspaceRoot, "artifacts", "graphify-imports");
|
|
13
|
+
|
|
14
|
+
export const GRAPHIFY_IMPORT_SLICE_LAYOUT_V1 = {
|
|
15
|
+
importSlice: "import-slice.json",
|
|
16
|
+
candidatePackInput: "candidate-pack-input.json",
|
|
17
|
+
importReport: "import-report.md",
|
|
18
|
+
proposalEnvelope: "proposal-envelope.json",
|
|
19
|
+
replayGate: "replay-gate.json",
|
|
20
|
+
};
|
|
21
|
+
export const GRAPHIFY_IMPORT_SLICE_CANDIDATE_PACK_INPUT_CONTRACT_V1 = "graphify_import_slice_candidate_pack_input.v1";
|
|
22
|
+
const TRUST_CLASS_EXTRACTED = "EXTRACTED";
|
|
23
|
+
const BLOCKED_TRUST_CLASSES = ["INFERRED", "AMBIGUOUS"];
|
|
24
|
+
const BLOCKED_EFFECTS = [
|
|
25
|
+
"current_truth_write",
|
|
26
|
+
"correction_like_memory",
|
|
27
|
+
"live_eligible_edge",
|
|
28
|
+
"hot_path_serve_integration",
|
|
29
|
+
];
|
|
30
|
+
const SOURCE_TRUTH_ANCHORS = [
|
|
31
|
+
{
|
|
32
|
+
id: "graphify-bridge-artifact-first",
|
|
33
|
+
state: "shipped",
|
|
34
|
+
kind: "docs_truth",
|
|
35
|
+
source: "docs/architecture/graphify-bridge.md#4-artifact-first-rule",
|
|
36
|
+
note: "Graphify stays artifact-first and import comes after derived artifacts.",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "graphify-bridge-extracted-only",
|
|
40
|
+
state: "shipped",
|
|
41
|
+
kind: "docs_truth",
|
|
42
|
+
source: "docs/architecture/graphify-bridge.md#6-extracted-inferred-ambiguous-handling",
|
|
43
|
+
note: "EXTRACTED is the only trust class that can feed a later live-eligible import slice.",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "graphify-bridge-rollback-discipline",
|
|
47
|
+
state: "shipped",
|
|
48
|
+
kind: "docs_truth",
|
|
49
|
+
source: "docs/architecture/graphify-bridge.md#7-promotion-and-rollback-discipline",
|
|
50
|
+
note: "Graphify outputs never promote themselves and must keep rollback binding explicit.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "compiled-artifacts-core-rules",
|
|
54
|
+
state: "shipped",
|
|
55
|
+
kind: "docs_truth",
|
|
56
|
+
source: "docs/architecture/compiled-artifacts.md#core-rules",
|
|
57
|
+
note: "Derived artifacts remain off-path and subordinate to stronger truth layers.",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
function normalizeText(value) {
|
|
62
|
+
if (typeof value !== "string") {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const trimmed = value.trim();
|
|
66
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
67
|
+
}
|
|
68
|
+
function slugify(value) {
|
|
69
|
+
return String(value ?? "")
|
|
70
|
+
.toLowerCase()
|
|
71
|
+
.replace(/[^a-z0-9]+/gu, "-")
|
|
72
|
+
.replace(/^-+|-+$/gu, "")
|
|
73
|
+
.replace(/-{2,}/gu, "-") || "bundle";
|
|
74
|
+
}
|
|
75
|
+
function timestampToken(value = new Date().toISOString()) {
|
|
76
|
+
return String(value).replace(/[:]/g, "-");
|
|
77
|
+
}
|
|
78
|
+
function stableJson(value) {
|
|
79
|
+
return canonicalJson(value);
|
|
80
|
+
}
|
|
81
|
+
function sha256Text(text) {
|
|
82
|
+
return `sha256:${createHash("sha256").update(String(text ?? ""), "utf8").digest("hex")}`;
|
|
83
|
+
}
|
|
84
|
+
function ensureDir(dirPath) {
|
|
85
|
+
mkdirSync(dirPath, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
function writeText(filePath, text) {
|
|
88
|
+
ensureDir(path.dirname(filePath));
|
|
89
|
+
writeFileSync(filePath, text, "utf8");
|
|
90
|
+
return filePath;
|
|
91
|
+
}
|
|
92
|
+
function writeJson(filePath, value) {
|
|
93
|
+
return writeText(filePath, `${stableJson(value)}\n`);
|
|
94
|
+
}
|
|
95
|
+
function readJson(filePath) {
|
|
96
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
97
|
+
}
|
|
98
|
+
function readJsonIfExists(filePath) {
|
|
99
|
+
return existsSync(filePath) ? readJson(filePath) : null;
|
|
100
|
+
}
|
|
101
|
+
function readTextIfExists(filePath) {
|
|
102
|
+
return existsSync(filePath) ? readFileSync(filePath, "utf8") : null;
|
|
103
|
+
}
|
|
104
|
+
function relativeWorkspacePath(absPath, workspaceRoot) {
|
|
105
|
+
const resolvedPath = path.resolve(absPath);
|
|
106
|
+
const relative = path.relative(workspaceRoot, resolvedPath);
|
|
107
|
+
return relative.startsWith("..") ? resolvedPath : relative.replace(/\\/g, "/");
|
|
108
|
+
}
|
|
109
|
+
function uniqueBy(items, keyFn) {
|
|
110
|
+
const seen = new Set();
|
|
111
|
+
const unique = [];
|
|
112
|
+
for (const item of items) {
|
|
113
|
+
const key = keyFn(item);
|
|
114
|
+
if (seen.has(key)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
seen.add(key);
|
|
118
|
+
unique.push(item);
|
|
119
|
+
}
|
|
120
|
+
return unique;
|
|
121
|
+
}
|
|
122
|
+
function summarizeArtifactRecord(record) {
|
|
123
|
+
return {
|
|
124
|
+
artifactId: record.artifactId,
|
|
125
|
+
kind: record.kind,
|
|
126
|
+
title: record.title,
|
|
127
|
+
subjectIds: [...(record.subjectIds ?? [])],
|
|
128
|
+
sourceRoots: [...(record.provenance?.sourceRoots ?? record.sourceRoots ?? [])],
|
|
129
|
+
markdownPath: record.markdownPath,
|
|
130
|
+
metaPath: record.metaPath,
|
|
131
|
+
contentHash: record.contentHash ?? null,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function loadGraphifyCompiledArtifactPack(bundleRoot) {
|
|
135
|
+
const resolvedBundleRoot = path.resolve(bundleRoot);
|
|
136
|
+
const manifestPath = path.join(resolvedBundleRoot, "pack.manifest.json");
|
|
137
|
+
if (!existsSync(manifestPath)) {
|
|
138
|
+
throw new Error(`graphify import slice expects a compiled-artifact pack root with pack.manifest.json: ${resolvedBundleRoot}`);
|
|
139
|
+
}
|
|
140
|
+
const manifest = readJson(manifestPath);
|
|
141
|
+
const artifactSummaries = Array.isArray(manifest.artifacts) ? manifest.artifacts : [];
|
|
142
|
+
if (artifactSummaries.length === 0) {
|
|
143
|
+
throw new Error(`graphify import slice found no artifacts in pack.manifest.json at ${resolvedBundleRoot}`);
|
|
144
|
+
}
|
|
145
|
+
const artifactRecords = artifactSummaries.map((summary) => {
|
|
146
|
+
const summaryMetaPath = typeof summary.metaPath === "string" && summary.metaPath.trim().length > 0
|
|
147
|
+
? path.resolve(resolvedBundleRoot, summary.metaPath)
|
|
148
|
+
: path.join(resolvedBundleRoot, "artifacts", summary.artifactId, "artifact.meta.json");
|
|
149
|
+
if (!existsSync(summaryMetaPath)) {
|
|
150
|
+
throw new Error(`graphify import slice could not read artifact meta for ${summary.artifactId}: ${summaryMetaPath}`);
|
|
151
|
+
}
|
|
152
|
+
const meta = readJson(summaryMetaPath);
|
|
153
|
+
const markdownPath = typeof meta.markdownPath === "string" && meta.markdownPath.trim().length > 0
|
|
154
|
+
? path.resolve(resolvedBundleRoot, meta.markdownPath)
|
|
155
|
+
: path.join(resolvedBundleRoot, "artifacts", meta.artifactId, "artifact.md");
|
|
156
|
+
const markdownText = readTextIfExists(markdownPath);
|
|
157
|
+
return {
|
|
158
|
+
...meta,
|
|
159
|
+
markdownText,
|
|
160
|
+
markdownPath,
|
|
161
|
+
metaPath: summaryMetaPath,
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
const surfaceMap = readJsonIfExists(path.join(resolvedBundleRoot, "surface-map.json"));
|
|
165
|
+
const proposalReport = readJsonIfExists(path.join(resolvedBundleRoot, "proposal-report.json"));
|
|
166
|
+
const verdict = readJsonIfExists(path.join(resolvedBundleRoot, "verdict.json"));
|
|
167
|
+
return {
|
|
168
|
+
bundleRoot: resolvedBundleRoot,
|
|
169
|
+
manifestPath,
|
|
170
|
+
manifest,
|
|
171
|
+
surfaceMap,
|
|
172
|
+
proposalReport,
|
|
173
|
+
verdict,
|
|
174
|
+
artifacts: artifactRecords,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function buildSourceBundleSummary(pack, workspaceRoot) {
|
|
178
|
+
const artifacts = pack.artifacts.map((record) => summarizeArtifactRecord(record));
|
|
179
|
+
return {
|
|
180
|
+
contract: "graphify_import_slice_source_bundle.v1",
|
|
181
|
+
bundleRoot: relativeWorkspacePath(pack.bundleRoot, workspaceRoot),
|
|
182
|
+
packId: pack.manifest.packId ?? path.basename(pack.bundleRoot),
|
|
183
|
+
title: pack.manifest.title ?? null,
|
|
184
|
+
contractId: pack.manifest.contract ?? null,
|
|
185
|
+
status: pack.manifest.status ?? null,
|
|
186
|
+
proposalId: pack.manifest.proposalId ?? null,
|
|
187
|
+
lane: pack.manifest.lane ?? null,
|
|
188
|
+
scope: pack.manifest.scope ?? null,
|
|
189
|
+
createdAt: pack.manifest.createdAt ?? null,
|
|
190
|
+
updatedAt: pack.manifest.updatedAt ?? null,
|
|
191
|
+
graphifyRun: pack.manifest.graphifyRun ?? null,
|
|
192
|
+
sourceDocs: Array.isArray(pack.manifest.sourceDocs) ? [...pack.manifest.sourceDocs] : [],
|
|
193
|
+
sourceFixtures: Array.isArray(pack.manifest.sourceFixtures) ? [...pack.manifest.sourceFixtures] : [],
|
|
194
|
+
artifacts,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function hashBundleSummary(summary) {
|
|
198
|
+
return sha256Text(stableJson(summary));
|
|
199
|
+
}
|
|
200
|
+
function collectEvidencePointers(artifacts) {
|
|
201
|
+
const pointers = [];
|
|
202
|
+
const seen = new Set();
|
|
203
|
+
for (const artifact of artifacts) {
|
|
204
|
+
for (const evidence of artifact.evidence ?? []) {
|
|
205
|
+
const pointerId = evidence.evidenceId ?? sha256Text(`${artifact.artifactId}:${evidence.sourceId ?? evidence.excerpt ?? "evidence"}`).slice(7, 19);
|
|
206
|
+
const key = `${pointerId}:${artifact.artifactId}`;
|
|
207
|
+
if (seen.has(key)) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
seen.add(key);
|
|
211
|
+
pointers.push({
|
|
212
|
+
pointerId,
|
|
213
|
+
trustClass: TRUST_CLASS_EXTRACTED,
|
|
214
|
+
sourceArtifactId: artifact.artifactId,
|
|
215
|
+
sourceArtifactKind: artifact.kind,
|
|
216
|
+
sourceArtifactTitle: artifact.title,
|
|
217
|
+
sourceKind: evidence.sourceKind,
|
|
218
|
+
sourceId: evidence.sourceId,
|
|
219
|
+
authority: evidence.authority ?? "raw_source",
|
|
220
|
+
derivation: evidence.derivation ?? "graphify_import_slice",
|
|
221
|
+
excerpt: evidence.excerpt ?? null,
|
|
222
|
+
sourceHash: evidence.sourceHash ?? null,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return pointers;
|
|
227
|
+
}
|
|
228
|
+
function collectRationalePointers(artifacts) {
|
|
229
|
+
const pointers = [];
|
|
230
|
+
const seen = new Set();
|
|
231
|
+
for (const artifact of artifacts) {
|
|
232
|
+
for (const claim of artifact.claims ?? []) {
|
|
233
|
+
const pointerId = claim.claimId ?? sha256Text(`${artifact.artifactId}:${claim.text ?? "claim"}`).slice(7, 19);
|
|
234
|
+
const key = `${pointerId}:${artifact.artifactId}`;
|
|
235
|
+
if (seen.has(key)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
seen.add(key);
|
|
239
|
+
pointers.push({
|
|
240
|
+
pointerId,
|
|
241
|
+
trustClass: TRUST_CLASS_EXTRACTED,
|
|
242
|
+
sourceArtifactId: artifact.artifactId,
|
|
243
|
+
sourceArtifactKind: artifact.kind,
|
|
244
|
+
sourceArtifactTitle: artifact.title,
|
|
245
|
+
text: claim.text,
|
|
246
|
+
confidence: claim.confidence ?? null,
|
|
247
|
+
status: claim.status ?? null,
|
|
248
|
+
evidenceIds: Array.isArray(claim.evidenceIds) ? [...claim.evidenceIds] : [],
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return pointers;
|
|
253
|
+
}
|
|
254
|
+
function buildHubPriors(artifacts, sourceBundle, sliceId) {
|
|
255
|
+
const hubArtifacts = artifacts.filter((artifact) => artifact.kind === "map_of_territory" || artifact.kind === "concept_page");
|
|
256
|
+
return hubArtifacts.map((artifact, index) => {
|
|
257
|
+
const evidenceIds = uniqueBy(artifact.evidence ?? [], (evidence) => evidence.evidenceId ?? JSON.stringify(evidence)).map((evidence) => evidence.evidenceId ?? null).filter((value) => value !== null);
|
|
258
|
+
const rationaleIds = uniqueBy(artifact.claims ?? [], (claim) => claim.claimId ?? JSON.stringify(claim)).map((claim) => claim.claimId ?? null).filter((value) => value !== null);
|
|
259
|
+
return {
|
|
260
|
+
priorId: `hub-prior-${index + 1}-${slugify(artifact.artifactId)}`,
|
|
261
|
+
trustClass: TRUST_CLASS_EXTRACTED,
|
|
262
|
+
kind: "hub_prior",
|
|
263
|
+
hubId: artifact.artifactId,
|
|
264
|
+
label: artifact.title,
|
|
265
|
+
title: artifact.title,
|
|
266
|
+
artifactKind: artifact.kind,
|
|
267
|
+
subjectIds: Array.isArray(artifact.subjectIds) ? [...artifact.subjectIds] : [],
|
|
268
|
+
sourceRoots: Array.isArray(artifact.sourceRoots ?? artifact.provenance?.sourceRoots) ? [...(artifact.sourceRoots ?? artifact.provenance?.sourceRoots)] : [],
|
|
269
|
+
sourceArtifactId: artifact.artifactId,
|
|
270
|
+
sourceArtifactPath: artifact.markdownPath,
|
|
271
|
+
sourceMetaPath: artifact.metaPath,
|
|
272
|
+
sourceBundleId: sourceBundle.packId,
|
|
273
|
+
sourceBundleHash: sourceBundle.sourceBundleHash,
|
|
274
|
+
evidencePointerIds: evidenceIds,
|
|
275
|
+
rationalePointerIds: rationaleIds,
|
|
276
|
+
authority: "raw_source",
|
|
277
|
+
derivation: "graphify_import_slice",
|
|
278
|
+
rollbackKey: `rollback:graphify-import-slice:${sliceId}:${slugify(artifact.artifactId)}`,
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
function buildNeighborhoodPriors(artifacts, sourceBundle, sliceId) {
|
|
283
|
+
const neighborhoodArtifacts = artifacts.filter((artifact) => artifact.kind === "neighborhood_summary");
|
|
284
|
+
return neighborhoodArtifacts.map((artifact, index) => {
|
|
285
|
+
const evidenceIds = uniqueBy(artifact.evidence ?? [], (evidence) => evidence.evidenceId ?? JSON.stringify(evidence)).map((evidence) => evidence.evidenceId ?? null).filter((value) => value !== null);
|
|
286
|
+
const rationaleIds = uniqueBy(artifact.claims ?? [], (claim) => claim.claimId ?? JSON.stringify(claim)).map((claim) => claim.claimId ?? null).filter((value) => value !== null);
|
|
287
|
+
return {
|
|
288
|
+
priorId: `neighborhood-prior-${index + 1}-${slugify(artifact.artifactId)}`,
|
|
289
|
+
trustClass: TRUST_CLASS_EXTRACTED,
|
|
290
|
+
kind: "neighborhood_prior",
|
|
291
|
+
neighborhoodId: artifact.artifactId,
|
|
292
|
+
label: artifact.title,
|
|
293
|
+
title: artifact.title,
|
|
294
|
+
artifactKind: artifact.kind,
|
|
295
|
+
subjectIds: Array.isArray(artifact.subjectIds) ? [...artifact.subjectIds] : [],
|
|
296
|
+
sourceRoots: Array.isArray(artifact.sourceRoots ?? artifact.provenance?.sourceRoots) ? [...(artifact.sourceRoots ?? artifact.provenance?.sourceRoots)] : [],
|
|
297
|
+
sourceArtifactId: artifact.artifactId,
|
|
298
|
+
sourceArtifactPath: artifact.markdownPath,
|
|
299
|
+
sourceMetaPath: artifact.metaPath,
|
|
300
|
+
sourceBundleId: sourceBundle.packId,
|
|
301
|
+
sourceBundleHash: sourceBundle.sourceBundleHash,
|
|
302
|
+
evidencePointerIds: evidenceIds,
|
|
303
|
+
rationalePointerIds: rationaleIds,
|
|
304
|
+
authority: "raw_source",
|
|
305
|
+
derivation: "graphify_import_slice",
|
|
306
|
+
rollbackKey: `rollback:graphify-import-slice:${sliceId}:${slugify(artifact.artifactId)}`,
|
|
307
|
+
};
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
function buildTruthBoundary(sourceBundle) {
|
|
311
|
+
return {
|
|
312
|
+
authority: "graphify_proposal_truth",
|
|
313
|
+
strongerTruthLayers: ["runtime truth", "proof truth", "docs truth"],
|
|
314
|
+
correctionPrecedence: [
|
|
315
|
+
"explicit correction memory",
|
|
316
|
+
"recent raw user/source turns",
|
|
317
|
+
"raw proof/runtime evidence",
|
|
318
|
+
"frozen docs truth",
|
|
319
|
+
],
|
|
320
|
+
allowedTrustClasses: [TRUST_CLASS_EXTRACTED],
|
|
321
|
+
blockedTrustClasses: [...BLOCKED_TRUST_CLASSES],
|
|
322
|
+
blockedEffects: [...BLOCKED_EFFECTS],
|
|
323
|
+
artifactFirst: true,
|
|
324
|
+
rollbackSafe: true,
|
|
325
|
+
removable: true,
|
|
326
|
+
liveEligible: false,
|
|
327
|
+
sourceBundleKind: sourceBundle.contractId,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function buildReplayGate(input) {
|
|
331
|
+
return {
|
|
332
|
+
contract: "graphify_import_slice_replay_gate.v1",
|
|
333
|
+
gateId: `replay-gate:${input.sliceId}`,
|
|
334
|
+
bundleId: input.sliceId,
|
|
335
|
+
proposalId: input.proposalId,
|
|
336
|
+
proposalClass: "import",
|
|
337
|
+
reviewMode: "candidate_only",
|
|
338
|
+
status: "validated",
|
|
339
|
+
targetStateOnly: true,
|
|
340
|
+
allowedTrustClasses: [TRUST_CLASS_EXTRACTED],
|
|
341
|
+
blockedTrustClasses: [...BLOCKED_TRUST_CLASSES],
|
|
342
|
+
blockedEffects: [...BLOCKED_EFFECTS],
|
|
343
|
+
rollbackKey: input.rollbackKey,
|
|
344
|
+
strongerTruthAnchors: [...SOURCE_TRUTH_ANCHORS],
|
|
345
|
+
requirements: [
|
|
346
|
+
{
|
|
347
|
+
id: "artifact-first",
|
|
348
|
+
summary: "Import stays artifact-first and never outranks runtime, proof, or docs truth.",
|
|
349
|
+
requirements: [
|
|
350
|
+
"Import slice is derived from Graphify output, not live state.",
|
|
351
|
+
"No hot-path serve integration is added here.",
|
|
352
|
+
"Graphify remains off-path and review-only.",
|
|
353
|
+
],
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
id: "extracted-only",
|
|
357
|
+
summary: "Only EXTRACTED priors are allowed into the Wave 1 slice.",
|
|
358
|
+
requirements: [
|
|
359
|
+
"Hub priors remain EXTRACTED only.",
|
|
360
|
+
"Neighborhood priors remain EXTRACTED only.",
|
|
361
|
+
"INFERRED and AMBIGUOUS stay blocked from the import slice.",
|
|
362
|
+
],
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
id: "correction-precedence",
|
|
366
|
+
summary: "Explicit corrections keep precedence over any Graphify-derived prior.",
|
|
367
|
+
requirements: [
|
|
368
|
+
"No correction-like durable memory is written.",
|
|
369
|
+
"No current-truth-like overwrite path is introduced.",
|
|
370
|
+
"Any conflict routes to review rather than mutation.",
|
|
371
|
+
],
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "rollback-safe",
|
|
375
|
+
summary: "The slice must be removable and rollback-bound.",
|
|
376
|
+
requirements: [
|
|
377
|
+
"Rollback key is explicit and stable.",
|
|
378
|
+
"Import surfaces are bounded and inspectable.",
|
|
379
|
+
"Rejected slices keep no live eligibility.",
|
|
380
|
+
],
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
id: "boundedness",
|
|
384
|
+
summary: "Keep the reviewable surface compact enough for one operator sitting.",
|
|
385
|
+
requirements: [
|
|
386
|
+
"Evidence and rationale pointers stay source-grounded.",
|
|
387
|
+
"The slice avoids raw corpus dumps.",
|
|
388
|
+
"The output remains small and explicit.",
|
|
389
|
+
],
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function buildCandidatePackInput(input) {
|
|
395
|
+
const importedPriors = {
|
|
396
|
+
hubPriors: input.hubPriors.map((prior) => ({ ...prior })),
|
|
397
|
+
neighborhoodPriors: input.neighborhoodPriors.map((prior) => ({ ...prior })),
|
|
398
|
+
evidencePointers: input.evidencePointers.map((pointer) => ({ ...pointer })),
|
|
399
|
+
};
|
|
400
|
+
return {
|
|
401
|
+
contract: GRAPHIFY_IMPORT_SLICE_CANDIDATE_PACK_INPUT_CONTRACT_V1,
|
|
402
|
+
inputId: `candidate-pack-input:${input.sliceId}`,
|
|
403
|
+
bundleId: `candidate-pack-input:${input.sliceId}`,
|
|
404
|
+
sliceId: input.sliceId,
|
|
405
|
+
proposalId: input.proposalId,
|
|
406
|
+
candidatePackId: `candidate-pack:${input.sliceId}`,
|
|
407
|
+
sourceBundleId: input.sourceBundleId,
|
|
408
|
+
sourceBundleHash: input.sourceBundleHash,
|
|
409
|
+
sourceBundleKind: input.sourceBundleKind,
|
|
410
|
+
generatedAt: input.generatedAt,
|
|
411
|
+
updatedAt: input.generatedAt,
|
|
412
|
+
reviewMode: "candidate_only",
|
|
413
|
+
targetStateOnly: true,
|
|
414
|
+
truthBoundary: input.truthBoundary,
|
|
415
|
+
seedingBoundary: {
|
|
416
|
+
liveEligible: false,
|
|
417
|
+
currentTruthWrites: false,
|
|
418
|
+
correctionMemoryWrites: false,
|
|
419
|
+
hotPathDependency: false,
|
|
420
|
+
removable: true,
|
|
421
|
+
rollbackSafe: true,
|
|
422
|
+
},
|
|
423
|
+
provenance: {
|
|
424
|
+
producer: "graphify-import-slice",
|
|
425
|
+
producerVersion: input.graphifyVersion,
|
|
426
|
+
producerRunId: input.graphifyRunId,
|
|
427
|
+
graphifyCommand: input.graphifyCommand,
|
|
428
|
+
scope: "graphify/import-slice/candidate-pack-input",
|
|
429
|
+
idempotencyKey: sha256Text(stableJson({
|
|
430
|
+
sliceId: input.sliceId,
|
|
431
|
+
proposalId: input.proposalId,
|
|
432
|
+
sourceBundleId: input.sourceBundleId,
|
|
433
|
+
sourceBundleHash: input.sourceBundleHash,
|
|
434
|
+
sourceBundleKind: input.sourceBundleKind,
|
|
435
|
+
graphifyRunId: input.graphifyRunId,
|
|
436
|
+
graphifyVersion: input.graphifyVersion,
|
|
437
|
+
})),
|
|
438
|
+
sourceRoots: Array.isArray(input.sourceBundle.sourceDocs)
|
|
439
|
+
? input.sourceBundle.sourceDocs.filter((value) => typeof value === "string")
|
|
440
|
+
: [],
|
|
441
|
+
transformChain: [
|
|
442
|
+
"extract",
|
|
443
|
+
"candidate_pack_seed",
|
|
444
|
+
"rollback_bound",
|
|
445
|
+
],
|
|
446
|
+
},
|
|
447
|
+
importedPriors,
|
|
448
|
+
counts: {
|
|
449
|
+
hubPriors: input.hubPriorCount,
|
|
450
|
+
neighborhoodPriors: input.neighborhoodPriorCount,
|
|
451
|
+
evidencePointers: input.evidencePointerCount,
|
|
452
|
+
},
|
|
453
|
+
notes: [
|
|
454
|
+
"This candidate-pack input is derived from EXTRACTED-only Graphify priors.",
|
|
455
|
+
"It is rollback-bound, removable, and never hot-path eligible.",
|
|
456
|
+
"It seeds later candidate compilation inputs without writing current truth or correction memory.",
|
|
457
|
+
],
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function buildProposalEnvelope(input, sourceBundle) {
|
|
461
|
+
const promptHash = sha256Text(stableJson({
|
|
462
|
+
sliceId: input.sliceId,
|
|
463
|
+
proposalId: input.proposalId,
|
|
464
|
+
sourceBundleHash: input.sourceBundleHash,
|
|
465
|
+
sourceBundleId: input.sourceBundleId,
|
|
466
|
+
graphifyRunId: input.graphifyRunId,
|
|
467
|
+
graphifyVersion: input.graphifyVersion,
|
|
468
|
+
}));
|
|
469
|
+
return {
|
|
470
|
+
contract: "graphify_import_slice_proposal.v1",
|
|
471
|
+
proposalId: input.proposalId,
|
|
472
|
+
proposalClass: "import",
|
|
473
|
+
lane: "import",
|
|
474
|
+
status: "validated",
|
|
475
|
+
reviewMode: "candidate_only",
|
|
476
|
+
lineage: {
|
|
477
|
+
proposalClass: "import",
|
|
478
|
+
producerVersion: input.graphifyVersion,
|
|
479
|
+
producerBuildId: input.graphifyRunId,
|
|
480
|
+
promptHash,
|
|
481
|
+
templateId: "graphify-import-slice/import-v1",
|
|
482
|
+
scope: "graphify/import-slice",
|
|
483
|
+
profile: "bridge-scaffold",
|
|
484
|
+
idempotencyKey: sha256Text(stableJson({
|
|
485
|
+
proposalId: input.proposalId,
|
|
486
|
+
sliceId: input.sliceId,
|
|
487
|
+
sourceBundleHash: input.sourceBundleHash,
|
|
488
|
+
sourceBundleId: input.sourceBundleId,
|
|
489
|
+
graphifyRunId: input.graphifyRunId,
|
|
490
|
+
})),
|
|
491
|
+
sourceBundleId: input.sourceBundleId,
|
|
492
|
+
parentProposalIds: [],
|
|
493
|
+
},
|
|
494
|
+
subjectIds: ["topic:graphify", "topic:import-slice", "topic:compiled-artifacts", "topic:truth-boundary"],
|
|
495
|
+
evidence: [
|
|
496
|
+
{
|
|
497
|
+
evidenceId: "ev-graphify-bridge-artifact-first",
|
|
498
|
+
sourceKind: "file",
|
|
499
|
+
sourceId: "docs/architecture/graphify-bridge.md#4-artifact-first-rule",
|
|
500
|
+
authority: "raw_source",
|
|
501
|
+
derivation: "teacher_compilation",
|
|
502
|
+
excerpt: "Graphify may help produce compiled artifacts, lints, or candidate imports, but it does not invent a separate lifecycle beside that chain.",
|
|
503
|
+
sourceHash: sha256Text("docs/architecture/graphify-bridge.md#4-artifact-first-rule\nGraphify may help produce compiled artifacts, lints, or candidate imports, but it does not invent a separate lifecycle beside that chain."),
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
evidenceId: "ev-graphify-bridge-extracted",
|
|
507
|
+
sourceKind: "file",
|
|
508
|
+
sourceId: "docs/architecture/graphify-bridge.md#6-extracted-inferred-ambiguous-handling",
|
|
509
|
+
authority: "raw_source",
|
|
510
|
+
derivation: "teacher_compilation",
|
|
511
|
+
excerpt: "EXTRACTED is the only class that can be considered for any later live-eligible import slice.",
|
|
512
|
+
sourceHash: sha256Text("docs/architecture/graphify-bridge.md#6-extracted-inferred-ambiguous-handling\nEXTRACTED is the only class that can be considered for any later live-eligible import slice."),
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
evidenceId: "ev-graphify-bridge-rollback",
|
|
516
|
+
sourceKind: "file",
|
|
517
|
+
sourceId: "docs/architecture/graphify-bridge.md#7-promotion-and-rollback-discipline",
|
|
518
|
+
authority: "raw_source",
|
|
519
|
+
derivation: "teacher_compilation",
|
|
520
|
+
excerpt: "Graphify outputs never promote themselves.",
|
|
521
|
+
sourceHash: sha256Text("docs/architecture/graphify-bridge.md#7-promotion-and-rollback-discipline\nGraphify outputs never promote themselves."),
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
evidenceId: "ev-compiled-artifacts-core-rules",
|
|
525
|
+
sourceKind: "file",
|
|
526
|
+
sourceId: "docs/architecture/compiled-artifacts.md#core-rules",
|
|
527
|
+
authority: "raw_source",
|
|
528
|
+
derivation: "teacher_compilation",
|
|
529
|
+
excerpt: "compiled artifacts are derived, off-path knowledge products",
|
|
530
|
+
sourceHash: sha256Text("docs/architecture/compiled-artifacts.md#core-rules\ncompiled artifacts are derived, off-path knowledge products"),
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
evidenceId: "ev-source-bundle-pack",
|
|
534
|
+
sourceKind: "file",
|
|
535
|
+
sourceId: `${sourceBundle.bundleRoot}/pack.manifest.json`,
|
|
536
|
+
authority: "raw_source",
|
|
537
|
+
derivation: "graphify_import_slice",
|
|
538
|
+
excerpt: "Graphify compiled-artifact pack selected for conservative EXTRACTED-only slicing.",
|
|
539
|
+
sourceHash: sourceBundle.sourceBundleHash,
|
|
540
|
+
},
|
|
541
|
+
],
|
|
542
|
+
counterevidence: [
|
|
543
|
+
{
|
|
544
|
+
evidenceId: "cevi-graphify-not-truth",
|
|
545
|
+
sourceKind: "file",
|
|
546
|
+
sourceId: "docs/architecture/graphify-bridge.md#8-what-this-bridge-is-not",
|
|
547
|
+
authority: "raw_source",
|
|
548
|
+
derivation: "teacher_compilation",
|
|
549
|
+
excerpt: "This bridge does not make Graphify a live truth layer.",
|
|
550
|
+
sourceHash: sha256Text("docs/architecture/graphify-bridge.md#8-what-this-bridge-is-not\nThis bridge does not make Graphify a live truth layer."),
|
|
551
|
+
},
|
|
552
|
+
],
|
|
553
|
+
payload: {
|
|
554
|
+
kind: "graphify-import-slice",
|
|
555
|
+
summary: "Conservative EXTRACTED-only Graphify prior slice with explicit evidence/rationale pointers and rollback binding.",
|
|
556
|
+
sliceId: input.sliceId,
|
|
557
|
+
sourceBundleId: input.sourceBundleId,
|
|
558
|
+
sourceBundleHash: input.sourceBundleHash,
|
|
559
|
+
sourceBundleKind: sourceBundle.contractId,
|
|
560
|
+
trustClass: TRUST_CLASS_EXTRACTED,
|
|
561
|
+
hubPriorCount: input.hubPriorCount,
|
|
562
|
+
neighborhoodPriorCount: input.neighborhoodPriorCount,
|
|
563
|
+
evidencePointerCount: input.evidencePointerCount,
|
|
564
|
+
rationalePointerCount: input.rationalePointerCount,
|
|
565
|
+
removedEffects: [...BLOCKED_EFFECTS],
|
|
566
|
+
},
|
|
567
|
+
expectedEffect: {
|
|
568
|
+
retrieval: "better",
|
|
569
|
+
truthRisk: "low",
|
|
570
|
+
reviewBurden: "bounded",
|
|
571
|
+
},
|
|
572
|
+
confidence: 0.92,
|
|
573
|
+
replaySuites: ["graphify-import-slice-smoke", "truth-boundary-smoke"],
|
|
574
|
+
rollbackKey: input.rollbackKey,
|
|
575
|
+
replayGate: buildReplayGate(input),
|
|
576
|
+
strongerTruthAnchors: [...SOURCE_TRUTH_ANCHORS],
|
|
577
|
+
createdAt: input.generatedAt,
|
|
578
|
+
updatedAt: input.generatedAt,
|
|
579
|
+
targetStateOnly: true,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function buildImportReportMarkdown(input) {
|
|
583
|
+
const hubLines = input.hubPriors.length === 0
|
|
584
|
+
? ["- none"]
|
|
585
|
+
: input.hubPriors.map((prior) => `- ${prior.label} (${prior.trustClass}) — ${prior.sourceArtifactId}`);
|
|
586
|
+
const neighborhoodLines = input.neighborhoodPriors.length === 0
|
|
587
|
+
? ["- none"]
|
|
588
|
+
: input.neighborhoodPriors.map((prior) => `- ${prior.label} (${prior.trustClass}) — ${prior.sourceArtifactId}`);
|
|
589
|
+
const evidenceLines = input.evidencePointers.length === 0
|
|
590
|
+
? ["- none"]
|
|
591
|
+
: input.evidencePointers.slice(0, 12).map((pointer) => `- \`${pointer.pointerId}\` — ${pointer.sourceId}`);
|
|
592
|
+
const rationaleLines = input.rationalePointers.length === 0
|
|
593
|
+
? ["- none"]
|
|
594
|
+
: input.rationalePointers.slice(0, 12).map((pointer) => `- \`${pointer.pointerId}\` — ${pointer.sourceArtifactId}: ${pointer.text}`);
|
|
595
|
+
const boundaryLines = [
|
|
596
|
+
"- artifact-first then import-second",
|
|
597
|
+
"- EXTRACTED only for Wave 1",
|
|
598
|
+
"- correction precedence preserved",
|
|
599
|
+
"- rollback-safe and removable",
|
|
600
|
+
"- no current-truth-like write",
|
|
601
|
+
"- no correction-like durable memory",
|
|
602
|
+
"- no INFERRED live-eligible edges",
|
|
603
|
+
"- no hot-path serve integration",
|
|
604
|
+
];
|
|
605
|
+
const sourceBundle = input.sourceBundle;
|
|
606
|
+
const lines = [
|
|
607
|
+
`# Graphify import slice report`,
|
|
608
|
+
"",
|
|
609
|
+
"Conservative EXTRACTED-only slice. Derived structure only; no live truth write.",
|
|
610
|
+
"",
|
|
611
|
+
`- slice id: \`${input.sliceId}\``,
|
|
612
|
+
`- proposal id: \`${input.proposalId}\``,
|
|
613
|
+
`- source pack: \`${sourceBundle.packId}\``,
|
|
614
|
+
`- source bundle hash: \`${input.sourceBundleHash}\``,
|
|
615
|
+
`- graphify run: \`${input.graphifyRunId}\``,
|
|
616
|
+
`- graphify version: \`${input.graphifyVersion}\``,
|
|
617
|
+
`- graphify command: \`${input.graphifyCommand}\``,
|
|
618
|
+
`- output root: \`${input.outputRoot}\``,
|
|
619
|
+
`- rollback key: \`${input.rollbackKey}\``,
|
|
620
|
+
"",
|
|
621
|
+
"## Imported priors",
|
|
622
|
+
"",
|
|
623
|
+
`Hub priors: ${input.hubPriorCount}`,
|
|
624
|
+
`Neighborhood priors: ${input.neighborhoodPriorCount}`,
|
|
625
|
+
`Evidence pointers: ${input.evidencePointerCount}`,
|
|
626
|
+
`Rationale pointers: ${input.rationalePointerCount}`,
|
|
627
|
+
"",
|
|
628
|
+
"## Candidate pack input",
|
|
629
|
+
"",
|
|
630
|
+
`- candidate pack id: \`${input.candidatePackInput.candidatePackId}\``,
|
|
631
|
+
`- candidate input id: \`${input.candidatePackInput.inputId}\``,
|
|
632
|
+
`- target-state only: \`${input.candidatePackInput.targetStateOnly}\``,
|
|
633
|
+
`- removable: \`${input.candidatePackInput.seedingBoundary.removable}\``,
|
|
634
|
+
`- rollback safe: \`${input.candidatePackInput.seedingBoundary.rollbackSafe}\``,
|
|
635
|
+
`- live eligible: \`${input.candidatePackInput.seedingBoundary.liveEligible}\``,
|
|
636
|
+
`- current truth writes: \`${input.candidatePackInput.seedingBoundary.currentTruthWrites}\``,
|
|
637
|
+
`- hot-path dependency: \`${input.candidatePackInput.seedingBoundary.hotPathDependency}\``,
|
|
638
|
+
`- correction memory writes: \`${input.candidatePackInput.seedingBoundary.correctionMemoryWrites}\``,
|
|
639
|
+
`- source bundle: \`${sourceBundle.packId}\` / \`${input.sourceBundleHash}\``,
|
|
640
|
+
"",
|
|
641
|
+
"### Imported prior material",
|
|
642
|
+
"",
|
|
643
|
+
`- hub priors: ${input.candidatePackInput.counts.hubPriors}`,
|
|
644
|
+
`- neighborhood priors: ${input.candidatePackInput.counts.neighborhoodPriors}`,
|
|
645
|
+
`- evidence pointers: ${input.candidatePackInput.counts.evidencePointers}`,
|
|
646
|
+
`- candidate input path: \`${input.candidatePackInputPath}\``,
|
|
647
|
+
"",
|
|
648
|
+
"### Hub priors",
|
|
649
|
+
...hubLines,
|
|
650
|
+
"",
|
|
651
|
+
"### Neighborhood priors",
|
|
652
|
+
...neighborhoodLines,
|
|
653
|
+
"",
|
|
654
|
+
"### Evidence pointers",
|
|
655
|
+
...evidenceLines,
|
|
656
|
+
"",
|
|
657
|
+
"### Rationale pointers",
|
|
658
|
+
...rationaleLines,
|
|
659
|
+
"",
|
|
660
|
+
"## Truth boundary",
|
|
661
|
+
"",
|
|
662
|
+
...boundaryLines,
|
|
663
|
+
"",
|
|
664
|
+
"## Replay gate",
|
|
665
|
+
"",
|
|
666
|
+
`- review mode: \`${input.replayGate.reviewMode}\``,
|
|
667
|
+
`- allowed trust classes: ${input.replayGate.allowedTrustClasses.join(", ")}`,
|
|
668
|
+
`- blocked trust classes: ${input.replayGate.blockedTrustClasses.join(", ")}`,
|
|
669
|
+
`- blocked effects: ${input.replayGate.blockedEffects.join(", ")}`,
|
|
670
|
+
"",
|
|
671
|
+
"This slice is bounded, explicit, rollback-bound, and removable. If a later lane wants broader import, it must do so after replay and proof checks, not by widening this file.",
|
|
672
|
+
"",
|
|
673
|
+
];
|
|
674
|
+
return lines.join("\n");
|
|
675
|
+
}
|
|
676
|
+
function buildImportSliceDigest(files) {
|
|
677
|
+
const digest = createHash("sha256");
|
|
678
|
+
const entries = Object.entries(files).sort(([left], [right]) => left.localeCompare(right));
|
|
679
|
+
for (const [name, text] of entries) {
|
|
680
|
+
digest.update(`${name}\u0000${text}\n`);
|
|
681
|
+
}
|
|
682
|
+
return `sha256:${digest.digest("hex")}`;
|
|
683
|
+
}
|
|
684
|
+
export function buildGraphifyImportSlice(options = {}) {
|
|
685
|
+
const repoRoot = path.resolve(options.repoRoot ?? defaultRepoRoot);
|
|
686
|
+
const workspaceRoot = path.resolve(options.workspaceRoot ?? defaultWorkspaceRoot);
|
|
687
|
+
const bundleRoot = path.resolve(options.bundleRoot ?? options.bundleDir ?? options.bundlePath ?? "");
|
|
688
|
+
if (!existsSync(bundleRoot)) {
|
|
689
|
+
throw new Error(`graphify import slice bundle root does not exist: ${bundleRoot}`);
|
|
690
|
+
}
|
|
691
|
+
const generatedAt = normalizeText(options.generatedAt) ?? new Date().toISOString();
|
|
692
|
+
const runId = normalizeText(options.runId) ?? `graphify-import-slice-${timestampToken(generatedAt)}`;
|
|
693
|
+
const outputRoot = path.resolve(options.outputRoot ?? defaultOutputRoot);
|
|
694
|
+
const outputDir = path.join(outputRoot, runId);
|
|
695
|
+
const sliceId = normalizeText(options.sliceId) ?? `graphify-import-slice-${slugify(runId)}`;
|
|
696
|
+
const proposalId = normalizeText(options.proposalId) ?? `prop_${slugify(sliceId)}`;
|
|
697
|
+
const rollbackKey = normalizeText(options.rollbackKey) ?? `rollback:graphify-import-slice:${slugify(sliceId)}`;
|
|
698
|
+
const pack = loadGraphifyCompiledArtifactPack(bundleRoot);
|
|
699
|
+
const sourceBundle = buildSourceBundleSummary(pack, workspaceRoot);
|
|
700
|
+
const sourceBundleHash = hashBundleSummary(sourceBundle);
|
|
701
|
+
sourceBundle.sourceBundleHash = sourceBundleHash;
|
|
702
|
+
const sourceBundleId = sourceBundle.packId;
|
|
703
|
+
const sourceBundleKind = sourceBundle.contractId;
|
|
704
|
+
const sourceBundleArtifacts = pack.artifacts;
|
|
705
|
+
const evidencePointers = collectEvidencePointers(sourceBundleArtifacts);
|
|
706
|
+
const rationalePointers = collectRationalePointers(sourceBundleArtifacts);
|
|
707
|
+
const hubPriors = buildHubPriors(sourceBundleArtifacts, { packId: sourceBundleId, sourceBundleHash }, sliceId);
|
|
708
|
+
const neighborhoodPriors = buildNeighborhoodPriors(sourceBundleArtifacts, { packId: sourceBundleId, sourceBundleHash }, sliceId);
|
|
709
|
+
const counts = {
|
|
710
|
+
hubPriors: hubPriors.length,
|
|
711
|
+
neighborhoodPriors: neighborhoodPriors.length,
|
|
712
|
+
evidencePointers: evidencePointers.length,
|
|
713
|
+
rationalePointers: rationalePointers.length,
|
|
714
|
+
sourceArtifacts: sourceBundleArtifacts.length,
|
|
715
|
+
};
|
|
716
|
+
const truthBoundary = buildTruthBoundary({ contractId: sourceBundleKind });
|
|
717
|
+
const candidatePackInput = buildCandidatePackInput({
|
|
718
|
+
sliceId,
|
|
719
|
+
proposalId,
|
|
720
|
+
sourceBundleId,
|
|
721
|
+
sourceBundleHash,
|
|
722
|
+
sourceBundleKind,
|
|
723
|
+
generatedAt,
|
|
724
|
+
graphifyRunId: sourceBundle.graphifyRun?.runId ?? null,
|
|
725
|
+
graphifyVersion: sourceBundle.graphifyRun?.graphifyVersion ?? null,
|
|
726
|
+
graphifyCommand: sourceBundle.graphifyRun?.graphifyCommand ?? null,
|
|
727
|
+
truthBoundary,
|
|
728
|
+
hubPriors,
|
|
729
|
+
neighborhoodPriors,
|
|
730
|
+
evidencePointers,
|
|
731
|
+
hubPriorCount: counts.hubPriors,
|
|
732
|
+
neighborhoodPriorCount: counts.neighborhoodPriors,
|
|
733
|
+
evidencePointerCount: counts.evidencePointers,
|
|
734
|
+
sourceBundle,
|
|
735
|
+
});
|
|
736
|
+
const importSlice = {
|
|
737
|
+
contract: "graphify_import_slice.v1",
|
|
738
|
+
sliceId,
|
|
739
|
+
proposalId,
|
|
740
|
+
bundleId: sliceId,
|
|
741
|
+
sourceBundleId,
|
|
742
|
+
sourceBundleHash,
|
|
743
|
+
sourceBundleKind,
|
|
744
|
+
sourceBundle,
|
|
745
|
+
generatedAt,
|
|
746
|
+
updatedAt: generatedAt,
|
|
747
|
+
truthBoundary,
|
|
748
|
+
counts,
|
|
749
|
+
hubPriors,
|
|
750
|
+
neighborhoodPriors,
|
|
751
|
+
evidencePointers,
|
|
752
|
+
rationalePointers,
|
|
753
|
+
notes: [
|
|
754
|
+
"Artifact-first then import-second.",
|
|
755
|
+
"Wave 1 is EXTRACTED only.",
|
|
756
|
+
"Explicit correction precedence remains stronger than any Graphify-derived prior.",
|
|
757
|
+
"The slice is candidate-only and never live-eligible in this cut.",
|
|
758
|
+
],
|
|
759
|
+
};
|
|
760
|
+
const replayGate = buildReplayGate({
|
|
761
|
+
sliceId,
|
|
762
|
+
proposalId,
|
|
763
|
+
rollbackKey,
|
|
764
|
+
generatedAt,
|
|
765
|
+
});
|
|
766
|
+
const proposalEnvelope = buildProposalEnvelope({
|
|
767
|
+
sliceId,
|
|
768
|
+
proposalId,
|
|
769
|
+
sourceBundleId,
|
|
770
|
+
sourceBundleHash,
|
|
771
|
+
graphifyRunId: sourceBundle.graphifyRun?.runId ?? null,
|
|
772
|
+
graphifyVersion: sourceBundle.graphifyRun?.graphifyVersion ?? null,
|
|
773
|
+
hubPriorCount: counts.hubPriors,
|
|
774
|
+
neighborhoodPriorCount: counts.neighborhoodPriors,
|
|
775
|
+
evidencePointerCount: counts.evidencePointers,
|
|
776
|
+
rationalePointerCount: counts.rationalePointers,
|
|
777
|
+
rollbackKey,
|
|
778
|
+
generatedAt,
|
|
779
|
+
}, sourceBundle);
|
|
780
|
+
const reportMarkdown = buildImportReportMarkdown({
|
|
781
|
+
sliceId,
|
|
782
|
+
proposalId,
|
|
783
|
+
sourceBundle,
|
|
784
|
+
sourceBundleHash,
|
|
785
|
+
graphifyRunId: sourceBundle.graphifyRun?.runId ?? null,
|
|
786
|
+
graphifyVersion: sourceBundle.graphifyRun?.graphifyVersion ?? null,
|
|
787
|
+
graphifyCommand: sourceBundle.graphifyRun?.graphifyCommand ?? null,
|
|
788
|
+
outputRoot: relativeWorkspacePath(outputDir, workspaceRoot),
|
|
789
|
+
rollbackKey,
|
|
790
|
+
hubPriors,
|
|
791
|
+
neighborhoodPriors,
|
|
792
|
+
evidencePointers,
|
|
793
|
+
rationalePointers,
|
|
794
|
+
hubPriorCount: counts.hubPriors,
|
|
795
|
+
neighborhoodPriorCount: counts.neighborhoodPriors,
|
|
796
|
+
evidencePointerCount: counts.evidencePointers,
|
|
797
|
+
rationalePointerCount: counts.rationalePointers,
|
|
798
|
+
candidatePackInput,
|
|
799
|
+
candidatePackInputPath: relativeWorkspacePath(path.join(outputDir, GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.candidatePackInput), workspaceRoot),
|
|
800
|
+
replayGate,
|
|
801
|
+
});
|
|
802
|
+
const files = {
|
|
803
|
+
[GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.importSlice]: stableJson(importSlice),
|
|
804
|
+
[GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.candidatePackInput]: stableJson(candidatePackInput),
|
|
805
|
+
[GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.importReport]: reportMarkdown,
|
|
806
|
+
[GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.proposalEnvelope]: stableJson(proposalEnvelope),
|
|
807
|
+
[GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.replayGate]: stableJson(replayGate),
|
|
808
|
+
};
|
|
809
|
+
const digest = {
|
|
810
|
+
bundleHash: buildImportSliceDigest(files),
|
|
811
|
+
fileCount: Object.keys(files).length,
|
|
812
|
+
files: Object.fromEntries(Object.entries(files).map(([name, text]) => [name, sha256Text(text)])),
|
|
813
|
+
};
|
|
814
|
+
const paths = {
|
|
815
|
+
importSlice: path.join(outputDir, GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.importSlice),
|
|
816
|
+
candidatePackInput: path.join(outputDir, GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.candidatePackInput),
|
|
817
|
+
importReport: path.join(outputDir, GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.importReport),
|
|
818
|
+
proposalEnvelope: path.join(outputDir, GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.proposalEnvelope),
|
|
819
|
+
replayGate: path.join(outputDir, GRAPHIFY_IMPORT_SLICE_LAYOUT_V1.replayGate),
|
|
820
|
+
};
|
|
821
|
+
return {
|
|
822
|
+
ok: true,
|
|
823
|
+
runId,
|
|
824
|
+
sliceId,
|
|
825
|
+
proposalId,
|
|
826
|
+
rollbackKey,
|
|
827
|
+
outputRoot,
|
|
828
|
+
outputDir,
|
|
829
|
+
bundleRoot,
|
|
830
|
+
repoRoot,
|
|
831
|
+
workspaceRoot,
|
|
832
|
+
sourceBundleId,
|
|
833
|
+
sourceBundleHash,
|
|
834
|
+
sourceBundleKind,
|
|
835
|
+
graphifyRunId: sourceBundle.graphifyRun?.runId ?? null,
|
|
836
|
+
graphifyVersion: sourceBundle.graphifyRun?.graphifyVersion ?? null,
|
|
837
|
+
graphifyCommand: sourceBundle.graphifyRun?.graphifyCommand ?? null,
|
|
838
|
+
counts,
|
|
839
|
+
truthBoundary,
|
|
840
|
+
candidatePackInput,
|
|
841
|
+
importSlice,
|
|
842
|
+
proposalEnvelope,
|
|
843
|
+
replayGate,
|
|
844
|
+
reportMarkdown,
|
|
845
|
+
files,
|
|
846
|
+
paths,
|
|
847
|
+
digest,
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
export function writeGraphifyImportSliceBundle(outputDir, bundle) {
|
|
851
|
+
rmSync(outputDir, { recursive: true, force: true });
|
|
852
|
+
ensureDir(outputDir);
|
|
853
|
+
const writtenFiles = [];
|
|
854
|
+
for (const [name, text] of Object.entries(bundle.files)) {
|
|
855
|
+
const filePath = path.join(outputDir, name);
|
|
856
|
+
writeText(filePath, text);
|
|
857
|
+
writtenFiles.push(filePath);
|
|
858
|
+
}
|
|
859
|
+
return {
|
|
860
|
+
writtenFiles,
|
|
861
|
+
fileCount: writtenFiles.length,
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
export function resolveGraphifyImportSliceOutputDir(options = {}) {
|
|
865
|
+
const runId = normalizeText(options.runId) ?? `graphify-import-slice-${timestampToken(new Date().toISOString())}`;
|
|
866
|
+
return path.join(path.resolve(options.outputRoot ?? defaultOutputRoot), runId);
|
|
867
|
+
}
|
|
868
|
+
export function exportGraphifyImportSlice(options = {}) {
|
|
869
|
+
try {
|
|
870
|
+
const bundle = buildGraphifyImportSlice(options);
|
|
871
|
+
const writeResult = writeGraphifyImportSliceBundle(bundle.outputDir, bundle);
|
|
872
|
+
return {
|
|
873
|
+
ok: true,
|
|
874
|
+
runId: bundle.runId,
|
|
875
|
+
sliceId: bundle.sliceId,
|
|
876
|
+
proposalId: bundle.proposalId,
|
|
877
|
+
rollbackKey: bundle.rollbackKey,
|
|
878
|
+
outputRoot: bundle.outputRoot,
|
|
879
|
+
outputDir: bundle.outputDir,
|
|
880
|
+
bundleRoot: bundle.bundleRoot,
|
|
881
|
+
repoRoot: bundle.repoRoot,
|
|
882
|
+
workspaceRoot: bundle.workspaceRoot,
|
|
883
|
+
sourceBundleId: bundle.sourceBundleId,
|
|
884
|
+
sourceBundleHash: bundle.sourceBundleHash,
|
|
885
|
+
sourceBundleKind: bundle.sourceBundleKind,
|
|
886
|
+
graphifyRunId: bundle.graphifyRunId,
|
|
887
|
+
graphifyVersion: bundle.graphifyVersion,
|
|
888
|
+
graphifyCommand: bundle.graphifyCommand,
|
|
889
|
+
counts: bundle.counts,
|
|
890
|
+
truthBoundary: bundle.truthBoundary,
|
|
891
|
+
candidatePackInput: bundle.candidatePackInput,
|
|
892
|
+
importSlice: bundle.importSlice,
|
|
893
|
+
proposalEnvelope: bundle.proposalEnvelope,
|
|
894
|
+
replayGate: bundle.replayGate,
|
|
895
|
+
report: bundle.reportMarkdown,
|
|
896
|
+
paths: bundle.paths,
|
|
897
|
+
digest: bundle.digest,
|
|
898
|
+
writtenFiles: writeResult.writtenFiles,
|
|
899
|
+
fileCount: writeResult.fileCount,
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
catch (error) {
|
|
903
|
+
const outputRoot = path.resolve(options.outputRoot ?? defaultOutputRoot);
|
|
904
|
+
const runId = normalizeText(options.runId) ?? null;
|
|
905
|
+
return {
|
|
906
|
+
ok: false,
|
|
907
|
+
outputRoot,
|
|
908
|
+
outputDir: runId === null ? outputRoot : path.join(outputRoot, runId),
|
|
909
|
+
bundleRoot: path.resolve(options.bundleRoot ?? options.bundleDir ?? options.bundlePath ?? "."),
|
|
910
|
+
candidatePackInput: null,
|
|
911
|
+
error: error instanceof Error ? error.message : String(error),
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function formatGraphifyImportSliceSummary(result) {
|
|
916
|
+
if (result.help) {
|
|
917
|
+
return "";
|
|
918
|
+
}
|
|
919
|
+
const lines = [
|
|
920
|
+
"GRAPHIFY IMPORT SLICE ok",
|
|
921
|
+
` Slice: ${result.sliceId}`,
|
|
922
|
+
` Proposal: ${result.proposalId}`,
|
|
923
|
+
` Source pack: ${result.sourceBundleId}`,
|
|
924
|
+
` Source hash: ${result.sourceBundleHash}`,
|
|
925
|
+
` Hub priors: ${result.counts.hubPriors}`,
|
|
926
|
+
` Neighborhoods: ${result.counts.neighborhoodPriors}`,
|
|
927
|
+
` Evidence: ${result.counts.evidencePointers}`,
|
|
928
|
+
` Rationale: ${result.counts.rationalePointers}`,
|
|
929
|
+
` Candidate input: ${result.paths.candidatePackInput}`,
|
|
930
|
+
` Output root: ${result.outputRoot}`,
|
|
931
|
+
` Output dir: ${result.outputDir}`,
|
|
932
|
+
` Import: ${result.paths.importSlice}`,
|
|
933
|
+
` Report: ${result.paths.importReport}`,
|
|
934
|
+
` Envelope: ${result.paths.proposalEnvelope}`,
|
|
935
|
+
` Replay gate: ${result.paths.replayGate}`,
|
|
936
|
+
];
|
|
937
|
+
return `${lines.join("\n")}\n`;
|
|
938
|
+
}
|
|
939
|
+
export function parseGraphifyImportSliceCliArgs(argv) {
|
|
940
|
+
let bundleRoot = null;
|
|
941
|
+
let repoRoot = defaultRepoRoot;
|
|
942
|
+
let workspaceRoot = defaultWorkspaceRoot;
|
|
943
|
+
let outputRoot = null;
|
|
944
|
+
let runId = null;
|
|
945
|
+
let json = false;
|
|
946
|
+
let help = false;
|
|
947
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
948
|
+
const arg = argv[index];
|
|
949
|
+
if (arg === "--help" || arg === "-h") {
|
|
950
|
+
help = true;
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
if (arg === "--json") {
|
|
954
|
+
json = true;
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
if (arg === "--bundle-root") {
|
|
958
|
+
const next = argv[index + 1];
|
|
959
|
+
if (next === undefined) {
|
|
960
|
+
throw new Error("--bundle-root requires a value");
|
|
961
|
+
}
|
|
962
|
+
bundleRoot = next;
|
|
963
|
+
index += 1;
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
if (arg === "--repo-root") {
|
|
967
|
+
const next = argv[index + 1];
|
|
968
|
+
if (next === undefined) {
|
|
969
|
+
throw new Error("--repo-root requires a value");
|
|
970
|
+
}
|
|
971
|
+
repoRoot = next;
|
|
972
|
+
index += 1;
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
if (arg === "--workspace-root") {
|
|
976
|
+
const next = argv[index + 1];
|
|
977
|
+
if (next === undefined) {
|
|
978
|
+
throw new Error("--workspace-root requires a value");
|
|
979
|
+
}
|
|
980
|
+
workspaceRoot = next;
|
|
981
|
+
index += 1;
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
if (arg === "--output-root") {
|
|
985
|
+
const next = argv[index + 1];
|
|
986
|
+
if (next === undefined) {
|
|
987
|
+
throw new Error("--output-root requires a value");
|
|
988
|
+
}
|
|
989
|
+
outputRoot = next;
|
|
990
|
+
index += 1;
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
if (arg === "--run-id") {
|
|
994
|
+
const next = argv[index + 1];
|
|
995
|
+
if (next === undefined) {
|
|
996
|
+
throw new Error("--run-id requires a value");
|
|
997
|
+
}
|
|
998
|
+
runId = next;
|
|
999
|
+
index += 1;
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
throw new Error(`unknown argument for graphify-import-slice: ${arg}`);
|
|
1003
|
+
}
|
|
1004
|
+
return {
|
|
1005
|
+
command: "graphify-import-slice",
|
|
1006
|
+
bundleRoot: bundleRoot === null ? null : path.resolve(bundleRoot),
|
|
1007
|
+
repoRoot: path.resolve(repoRoot),
|
|
1008
|
+
workspaceRoot: path.resolve(workspaceRoot),
|
|
1009
|
+
outputRoot: outputRoot === null ? null : path.resolve(outputRoot),
|
|
1010
|
+
runId,
|
|
1011
|
+
json,
|
|
1012
|
+
help,
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
export function runGraphifyImportSlice(argvOrOptions = {}) {
|
|
1016
|
+
const parsed = Array.isArray(argvOrOptions)
|
|
1017
|
+
? parseGraphifyImportSliceCliArgs(argvOrOptions)
|
|
1018
|
+
: { command: "graphify-import-slice", json: false, help: false, ...argvOrOptions };
|
|
1019
|
+
if (parsed.help) {
|
|
1020
|
+
return {
|
|
1021
|
+
ok: true,
|
|
1022
|
+
help: true,
|
|
1023
|
+
summary: "",
|
|
1024
|
+
candidatePackInput: null,
|
|
1025
|
+
importSlice: null,
|
|
1026
|
+
proposalEnvelope: null,
|
|
1027
|
+
replayGate: null,
|
|
1028
|
+
report: null,
|
|
1029
|
+
paths: null,
|
|
1030
|
+
outputRoot: null,
|
|
1031
|
+
outputDir: null,
|
|
1032
|
+
bundleRoot: null,
|
|
1033
|
+
repoRoot: null,
|
|
1034
|
+
workspaceRoot: null,
|
|
1035
|
+
runId: null,
|
|
1036
|
+
sliceId: null,
|
|
1037
|
+
proposalId: null,
|
|
1038
|
+
counts: null,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
const result = exportGraphifyImportSlice({
|
|
1042
|
+
bundleRoot: parsed.bundleRoot,
|
|
1043
|
+
repoRoot: parsed.repoRoot,
|
|
1044
|
+
workspaceRoot: parsed.workspaceRoot,
|
|
1045
|
+
outputRoot: parsed.outputRoot ?? undefined,
|
|
1046
|
+
runId: parsed.runId ?? undefined,
|
|
1047
|
+
});
|
|
1048
|
+
if (!result.ok) {
|
|
1049
|
+
return {
|
|
1050
|
+
ok: false,
|
|
1051
|
+
help: false,
|
|
1052
|
+
summary: "",
|
|
1053
|
+
candidatePackInput: null,
|
|
1054
|
+
importSlice: null,
|
|
1055
|
+
proposalEnvelope: null,
|
|
1056
|
+
replayGate: null,
|
|
1057
|
+
report: null,
|
|
1058
|
+
paths: null,
|
|
1059
|
+
outputRoot: result.outputRoot ?? null,
|
|
1060
|
+
outputDir: result.outputDir ?? null,
|
|
1061
|
+
bundleRoot: result.bundleRoot ?? null,
|
|
1062
|
+
repoRoot: null,
|
|
1063
|
+
workspaceRoot: null,
|
|
1064
|
+
runId: null,
|
|
1065
|
+
sliceId: null,
|
|
1066
|
+
proposalId: null,
|
|
1067
|
+
counts: null,
|
|
1068
|
+
error: result.error ?? "graphify import slice failed",
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
return {
|
|
1072
|
+
ok: true,
|
|
1073
|
+
help: false,
|
|
1074
|
+
summary: formatGraphifyImportSliceSummary(result),
|
|
1075
|
+
candidatePackInput: result.candidatePackInput,
|
|
1076
|
+
importSlice: result.importSlice,
|
|
1077
|
+
proposalEnvelope: result.proposalEnvelope,
|
|
1078
|
+
replayGate: result.replayGate,
|
|
1079
|
+
report: result.report,
|
|
1080
|
+
paths: result.paths,
|
|
1081
|
+
outputRoot: result.outputRoot,
|
|
1082
|
+
outputDir: result.outputDir,
|
|
1083
|
+
bundleRoot: result.bundleRoot,
|
|
1084
|
+
repoRoot: result.repoRoot,
|
|
1085
|
+
workspaceRoot: result.workspaceRoot,
|
|
1086
|
+
runId: result.runId,
|
|
1087
|
+
sliceId: result.sliceId,
|
|
1088
|
+
proposalId: result.proposalId,
|
|
1089
|
+
counts: result.counts,
|
|
1090
|
+
};
|
|
1091
|
+
}
|