@lmzhen/dsh-evolution-merge 0.47.0 → 0.49.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/lib/index.js +224 -775
- package/lib/types/adapters/engine-screen.d.ts +0 -3
- package/lib/types/authoring-flow.d.ts +5 -0
- package/lib/types/authoring-flow.js +17 -10
- package/lib/types/commit-execute.js +1 -22
- package/lib/types/contracts.d.ts +0 -1
- package/lib/types/contracts.js +0 -1
- package/lib/types/doctor.d.ts +0 -1
- package/lib/types/doctor.js +2 -31
- package/lib/types/domain/consistency.d.ts +0 -1
- package/lib/types/domain/consistency.js +2 -11
- package/lib/types/domain/merge-limits.d.ts +0 -10
- package/lib/types/domain/merge-limits.js +0 -3
- package/lib/types/domain/merge-snapshot.js +4 -6
- package/lib/types/domain/pair-ledger.d.ts +0 -1
- package/lib/types/domain/row-v3.d.ts +0 -45
- package/lib/types/domain/row-v3.js +0 -68
- package/lib/types/domain/row.d.ts +2 -84
- package/lib/types/domain/row.js +1 -110
- package/lib/types/domain/run-coordinator.d.ts +0 -24
- package/lib/types/domain/run-coordinator.js +0 -1
- package/lib/types/domain/stage-aggregate.d.ts +0 -42
- package/lib/types/domain/stage-aggregate.js +1 -116
- package/lib/types/domain/state-migrate.d.ts +27 -0
- package/lib/types/domain/state-migrate.js +29 -0
- package/lib/types/domain/state.d.ts +1 -53
- package/lib/types/domain/state.js +0 -55
- package/lib/types/domain/undo-journal.d.ts +4 -4
- package/lib/types/domain/undo-journal.js +5 -7
- package/lib/types/index.d.ts +0 -1
- package/lib/types/index.js +8 -14
- package/lib/types/new-child-authoring.js +4 -10
- package/lib/types/prompts.d.ts +0 -1
- package/lib/types/prompts.js +0 -1
- package/lib/types/read-commands.d.ts +0 -1
- package/lib/types/read-commands.js +0 -4
- package/lib/types/recovery-projection.js +0 -3
- package/lib/types/round2-outcome.js +15 -37
- package/lib/types/scan-run.js +0 -3
- package/lib/types/skipped.d.ts +1 -2
- package/lib/types/skipped.js +0 -8
- package/lib/types/staging.d.ts +0 -2
- package/lib/types/staging.js +0 -9
- package/lib/types/state.d.ts +0 -2
- package/lib/types/state.js +0 -13
- package/lib/types/surface/merge-view.d.ts +1 -2
- package/lib/types/surface/merge-view.js +0 -1
- package/lib/types/undo-operation.js +0 -9
- package/lib/types/unit-protocol.d.ts +6 -0
- package/lib/types/unit-protocol.js +72 -102
- package/package.json +10 -4
- package/scripts/merge-backup.mjs +31 -0
- package/scripts/merge-migrate.mjs +163 -0
- package/scripts/merge-restore.mjs +36 -0
- package/lib/types/commit-recovery.d.ts +0 -14
- package/lib/types/commit-recovery.js +0 -96
- package/lib/types/coordinator.d.ts +0 -77
- package/lib/types/coordinator.js +0 -137
- package/lib/types/domain/commit-journal-store.d.ts +0 -6
- package/lib/types/domain/commit-journal-store.js +0 -26
- package/lib/types/domain/commit-journal.d.ts +0 -16
- package/lib/types/domain/commit-journal.js +0 -26
- package/lib/types/domain/indexes.d.ts +0 -21
- package/lib/types/domain/indexes.js +0 -15
- package/lib/types/domain/merge-report.d.ts +0 -41
- package/lib/types/domain/merge-report.js +0 -2
- package/lib/types/domain/pair-index-store.d.ts +0 -6
- package/lib/types/domain/pair-index-store.js +0 -25
- package/lib/types/domain/pair-index.d.ts +0 -9
- package/lib/types/domain/pair-index.js +0 -28
- package/lib/types/domain/resolve-pair.d.ts +0 -4
- package/lib/types/domain/resolve-pair.js +0 -4
- package/lib/types/domain/row-tx.d.ts +0 -27
- package/lib/types/domain/row-tx.js +0 -31
- package/lib/types/domain/run-record.d.ts +0 -45
- package/lib/types/domain/run-record.js +0 -24
- package/lib/types/domain/state-v3.d.ts +0 -4
- package/lib/types/domain/state-v3.js +0 -4
package/lib/index.js
CHANGED
|
@@ -148,12 +148,6 @@ const MERGE_PAIR_STATES = [
|
|
|
148
148
|
"fuse",
|
|
149
149
|
"skipped"
|
|
150
150
|
];
|
|
151
|
-
const MERGE_QUEUES = [
|
|
152
|
-
"verify",
|
|
153
|
-
"pending",
|
|
154
|
-
"fuse",
|
|
155
|
-
"skipped"
|
|
156
|
-
];
|
|
157
151
|
/** User-facing queue of one data state. verify/fuse share retry, not two tabs. */
|
|
158
152
|
function queueOf(state) {
|
|
159
153
|
switch (state) {
|
|
@@ -1346,17 +1340,6 @@ async function loadMergeState(io, home) {
|
|
|
1346
1340
|
...parsed.note === void 0 ? {} : { note: parsed.note }
|
|
1347
1341
|
};
|
|
1348
1342
|
}
|
|
1349
|
-
/** Schema probe for doctor; the only other reader of mergeStatePath. */
|
|
1350
|
-
async function loadStateSchema(io, home) {
|
|
1351
|
-
try {
|
|
1352
|
-
const raw = await io.readText(mergeStatePath(home));
|
|
1353
|
-
if (raw === null) return null;
|
|
1354
|
-
const parsed = JSON.parse(raw);
|
|
1355
|
-
return typeof parsed.schemaVersion === "number" ? parsed.schemaVersion : null;
|
|
1356
|
-
} catch {
|
|
1357
|
-
return null;
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
1343
|
async function rowsOf(io, home, queue) {
|
|
1361
1344
|
const loaded = await loadMergeState(io, home);
|
|
1362
1345
|
return queue === void 0 ? loaded.state.rows : loaded.state.rows.filter((row) => row.queue === queue);
|
|
@@ -1619,7 +1602,7 @@ function v3State(body) {
|
|
|
1619
1602
|
rows
|
|
1620
1603
|
};
|
|
1621
1604
|
}
|
|
1622
|
-
async function readJournal
|
|
1605
|
+
async function readJournal(io, home) {
|
|
1623
1606
|
const raw = await readTextOrNull(io, migrationJournalPath(home));
|
|
1624
1607
|
if (raw === null) return null;
|
|
1625
1608
|
try {
|
|
@@ -1696,7 +1679,7 @@ async function readStateReadiness(io, home) {
|
|
|
1696
1679
|
if (version === 3) {
|
|
1697
1680
|
const state = v3State(body);
|
|
1698
1681
|
if (state === null) return blocked("merge-state-needs-repair", "v3 state has an invalid row");
|
|
1699
|
-
const journal = await readJournal
|
|
1682
|
+
const journal = await readJournal(io, home);
|
|
1700
1683
|
const report = await readReport(io, home);
|
|
1701
1684
|
const marker = readMarker(body);
|
|
1702
1685
|
if (body.migration !== void 0 && marker === null) return blocked("merge-state-needs-repair", "migration marker is invalid");
|
|
@@ -1755,7 +1738,7 @@ async function ensureMergeStateReady(io, home, fence) {
|
|
|
1755
1738
|
outcome = blocked("merge-state-needs-repair", "v3 state has an invalid row");
|
|
1756
1739
|
return current;
|
|
1757
1740
|
}
|
|
1758
|
-
const journal = await readJournal
|
|
1741
|
+
const journal = await readJournal(io, home);
|
|
1759
1742
|
if (journal !== null) {
|
|
1760
1743
|
outcome = await completeJournal(io, home, journal, current);
|
|
1761
1744
|
return current;
|
|
@@ -2000,15 +1983,6 @@ async function readStaging(io, home, runId) {
|
|
|
2000
1983
|
return probeUnknown("staging manifest is not JSON: " + (error instanceof Error ? error.message : String(error)));
|
|
2001
1984
|
}
|
|
2002
1985
|
}
|
|
2003
|
-
async function ensureStaging(io, home, input) {
|
|
2004
|
-
const existing = await readStaging(io, home, input.runId);
|
|
2005
|
-
if (existing.kind === "present") return {
|
|
2006
|
-
ok: true,
|
|
2007
|
-
path: stagingRoot(home, input.runId),
|
|
2008
|
-
manifest: existing.value.manifest
|
|
2009
|
-
};
|
|
2010
|
-
return createStaging(io, home, input);
|
|
2011
|
-
}
|
|
2012
1986
|
async function setStagingTarget(io, home, stageKey, target, mode) {
|
|
2013
1987
|
const view = await readStaging(io, home, stageKey);
|
|
2014
1988
|
if (view.kind !== "present") return false;
|
|
@@ -2016,9 +1990,6 @@ async function setStagingTarget(io, home, stageKey, target, mode) {
|
|
|
2016
1990
|
await io.writeText(join(stagingRoot(home, stageKey), "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
2017
1991
|
return true;
|
|
2018
1992
|
}
|
|
2019
|
-
async function dropStaging(io, home, stageKey) {
|
|
2020
|
-
await io.remove(stagingRoot(home, stageKey));
|
|
2021
|
-
}
|
|
2022
1993
|
/** Remove expired run-level staging trees; `keep` names runs a live row still points at. */
|
|
2023
1994
|
async function cleanupExpiredStaging(io, home, now, keep = /* @__PURE__ */ new Set()) {
|
|
2024
1995
|
let runs;
|
|
@@ -2175,6 +2146,7 @@ async function adoptLegacyProduction(io, home) {
|
|
|
2175
2146
|
wrote
|
|
2176
2147
|
};
|
|
2177
2148
|
}
|
|
2149
|
+
/** Production stage-tree facade. These wrappers keep the low-level tree module private to StageAggregate. */
|
|
2178
2150
|
async function createStageTree(io, home, input) {
|
|
2179
2151
|
return await createStaging(io, home, input);
|
|
2180
2152
|
}
|
|
@@ -2447,61 +2419,6 @@ async function readPairLedgerV4(io, home) {
|
|
|
2447
2419
|
};
|
|
2448
2420
|
}
|
|
2449
2421
|
//#endregion
|
|
2450
|
-
//#region lib/types/domain/pair-index.js
|
|
2451
|
-
function parsePairIndex(raw) {
|
|
2452
|
-
const index = /* @__PURE__ */ new Map();
|
|
2453
|
-
if (raw === null) return index;
|
|
2454
|
-
let parsed;
|
|
2455
|
-
try {
|
|
2456
|
-
parsed = JSON.parse(raw);
|
|
2457
|
-
} catch {
|
|
2458
|
-
return index;
|
|
2459
|
-
}
|
|
2460
|
-
const body = parsed;
|
|
2461
|
-
if (!Array.isArray(body.entries)) return index;
|
|
2462
|
-
for (const value of body.entries) {
|
|
2463
|
-
const entry = value;
|
|
2464
|
-
if (typeof entry.pairKey !== "string" || typeof entry.runId !== "string" || typeof entry.result !== "string") continue;
|
|
2465
|
-
if (![
|
|
2466
|
-
"merged",
|
|
2467
|
-
"kept",
|
|
2468
|
-
"failed",
|
|
2469
|
-
"cancelled",
|
|
2470
|
-
"undone"
|
|
2471
|
-
].includes(entry.result)) continue;
|
|
2472
|
-
index.set(entry.pairKey, entry);
|
|
2473
|
-
}
|
|
2474
|
-
return index;
|
|
2475
|
-
}
|
|
2476
|
-
function serializePairIndex(index) {
|
|
2477
|
-
return JSON.stringify({
|
|
2478
|
-
schemaVersion: 1,
|
|
2479
|
-
entries: [...index.values()]
|
|
2480
|
-
}, null, 2);
|
|
2481
|
-
}
|
|
2482
|
-
//#endregion
|
|
2483
|
-
//#region lib/types/domain/pair-index-store.js
|
|
2484
|
-
/** Durable pairKey index at <home>/merge/pair-index.json. */
|
|
2485
|
-
function pairIndexPath(home) {
|
|
2486
|
-
return join(home, "merge", "pair-index.json");
|
|
2487
|
-
}
|
|
2488
|
-
async function loadPairIndex(io, home) {
|
|
2489
|
-
let raw = null;
|
|
2490
|
-
try {
|
|
2491
|
-
raw = await io.readText(pairIndexPath(home));
|
|
2492
|
-
} catch {
|
|
2493
|
-
return /* @__PURE__ */ new Map();
|
|
2494
|
-
}
|
|
2495
|
-
return parsePairIndex(raw);
|
|
2496
|
-
}
|
|
2497
|
-
async function recordPair(io, home, entry) {
|
|
2498
|
-
await transactIo(io, pairIndexPath(home), (current) => {
|
|
2499
|
-
const index = parsePairIndex(current);
|
|
2500
|
-
index.set(entry.pairKey, entry);
|
|
2501
|
-
return serializePairIndex(index);
|
|
2502
|
-
});
|
|
2503
|
-
}
|
|
2504
|
-
//#endregion
|
|
2505
2422
|
//#region lib/types/domain/run-coordinator.js
|
|
2506
2423
|
/** 1b-1 additive types for the merge run lifecycle; no writer is active in this phase. */
|
|
2507
2424
|
function runMetaPath(home, runId) {
|
|
@@ -2908,19 +2825,18 @@ async function readMergeSnapshot(options) {
|
|
|
2908
2825
|
});
|
|
2909
2826
|
bodyRefs.push(...bodies);
|
|
2910
2827
|
}
|
|
2911
|
-
const pairIndex = await loadPairIndex(options.io, options.home);
|
|
2912
2828
|
const runs = [];
|
|
2913
2829
|
for (const record of options.runs) {
|
|
2914
2830
|
const meta = await readRunMeta(options.io, options.home, record.id);
|
|
2915
|
-
const pairs = [...pairIndex.values()].filter((item) => item.runId === record.id).map((item) => ({
|
|
2916
|
-
pairKey: item.pairKey,
|
|
2917
|
-
runId: item.runId,
|
|
2918
|
-
result: item.result,
|
|
2919
|
-
...item.target === void 0 ? {} : { target: item.target }
|
|
2920
|
-
}));
|
|
2921
2831
|
const reportPath = mergeReportPath(options.home, record.id);
|
|
2922
2832
|
const probe = await readMergeReport(options.io, options.home, record.id);
|
|
2923
2833
|
const report = probe.kind === "present" ? probe.value : null;
|
|
2834
|
+
const pairs = (report?.pairs ?? []).map((pair) => ({
|
|
2835
|
+
pairKey: pair.pairKey,
|
|
2836
|
+
runId: record.id,
|
|
2837
|
+
result: pair.outcome,
|
|
2838
|
+
...pair.target === void 0 ? {} : { target: pair.target.requested }
|
|
2839
|
+
}));
|
|
2924
2840
|
runs.push({
|
|
2925
2841
|
record,
|
|
2926
2842
|
meta,
|
|
@@ -2992,8 +2908,9 @@ async function readMergeSnapshot(options) {
|
|
|
2992
2908
|
})).digest("hex");
|
|
2993
2909
|
const notes = [...options.notes ?? []];
|
|
2994
2910
|
if (historyDegraded) notes.push("history archive port unavailable; receipts only");
|
|
2995
|
-
const
|
|
2996
|
-
|
|
2911
|
+
const activePairKeys = new Set(rows.filter((item) => item.row.state === "verify" || item.row.state === "pending" || item.row.state === "approved" || item.row.state === "fuse").map((item) => item.row.pairKey));
|
|
2912
|
+
const ledgerConflicts = runs.flatMap((run) => run.pairs).filter((entry) => (entry.result === "merged" || entry.result === "undone") && activePairKeys.has(entry.pairKey));
|
|
2913
|
+
if (ledgerConflicts.length > 0) notes.push("ledger-conflict: a run says completed but the ledger still has it active (" + String(ledgerConflicts.length) + ")");
|
|
2997
2914
|
return {
|
|
2998
2915
|
wireVersion: 1,
|
|
2999
2916
|
schemaVersion: 4,
|
|
@@ -3205,34 +3122,10 @@ var MergeUnitTransactions = class {
|
|
|
3205
3122
|
await this.options.onPhase?.(phase);
|
|
3206
3123
|
};
|
|
3207
3124
|
try {
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
} else {
|
|
3213
|
-
const fallbackLease = await new HomeAuthority({
|
|
3214
|
-
home: this.options.home,
|
|
3215
|
-
waitMs: this.options.waitMs ?? 5e3
|
|
3216
|
-
}).lease("tree").catch(() => void 0);
|
|
3217
|
-
if (fallbackLease === void 0) return {
|
|
3218
|
-
ok: false,
|
|
3219
|
-
code: "unit-fence-failed",
|
|
3220
|
-
message: "tree lock unavailable"
|
|
3221
|
-
};
|
|
3222
|
-
fenceLease = {
|
|
3223
|
-
lock: leaseAsTreeLock(fallbackLease),
|
|
3224
|
-
release: async () => {
|
|
3225
|
-
await fallbackLease.release();
|
|
3226
|
-
}
|
|
3227
|
-
};
|
|
3228
|
-
lock = fenceLease.lock;
|
|
3229
|
-
this.options.onLock?.("tree");
|
|
3230
|
-
}
|
|
3231
|
-
if (!await lock.assertHeld()) return {
|
|
3232
|
-
ok: false,
|
|
3233
|
-
code: "unit-fence-failed",
|
|
3234
|
-
message: "tree fence lost"
|
|
3235
|
-
};
|
|
3125
|
+
const acquired = await this.acquireTreeFence();
|
|
3126
|
+
fenceLease = acquired.fenceLease;
|
|
3127
|
+
lock = acquired.lock;
|
|
3128
|
+
this.options.onLock?.("tree");
|
|
3236
3129
|
stage = "claim";
|
|
3237
3130
|
claim = await this.options.claim.acquire(stored.plan.target, stored.plan.runId);
|
|
3238
3131
|
this.options.onLock?.("claim");
|
|
@@ -3368,6 +3261,82 @@ var MergeUnitTransactions = class {
|
|
|
3368
3261
|
await lease.release();
|
|
3369
3262
|
}
|
|
3370
3263
|
}
|
|
3264
|
+
/** Acquire the one tree fence (or a HomeAuthority fallback) and prove it is held. */
|
|
3265
|
+
async acquireTreeFence() {
|
|
3266
|
+
let fenceLease;
|
|
3267
|
+
if (this.options.fence !== void 0) fenceLease = await this.options.fence.acquire();
|
|
3268
|
+
else {
|
|
3269
|
+
const fallbackLease = await new HomeAuthority({
|
|
3270
|
+
home: this.options.home,
|
|
3271
|
+
waitMs: this.options.waitMs ?? 5e3
|
|
3272
|
+
}).lease("tree").catch(() => void 0);
|
|
3273
|
+
if (fallbackLease === void 0) throw new Error("tree lock unavailable");
|
|
3274
|
+
fenceLease = {
|
|
3275
|
+
lock: leaseAsTreeLock(fallbackLease),
|
|
3276
|
+
release: async () => {
|
|
3277
|
+
await fallbackLease.release();
|
|
3278
|
+
}
|
|
3279
|
+
};
|
|
3280
|
+
}
|
|
3281
|
+
if (!await fenceLease.lock.assertHeld()) throw new Error("tree fence lost");
|
|
3282
|
+
return {
|
|
3283
|
+
fenceLease,
|
|
3284
|
+
lock: fenceLease.lock
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
/** Finish the idempotent support writes from the prepared snapshot, or name the drift. */
|
|
3288
|
+
async completeSupport(snapshot, journal) {
|
|
3289
|
+
let liveSupport = await Promise.all(snapshot.supportAfter.map(async (file) => ({
|
|
3290
|
+
path: file.path,
|
|
3291
|
+
content: await this.options.backend.readSupportFile(snapshot.target, file.path)
|
|
3292
|
+
})));
|
|
3293
|
+
const supportMatches = () => liveSupport.every((file) => file.content === (snapshot.supportAfter.find((item) => item.path === file.path)?.content ?? null));
|
|
3294
|
+
if (supportMatches()) return { ok: true };
|
|
3295
|
+
if (![
|
|
3296
|
+
"support-pending",
|
|
3297
|
+
"sources-archived",
|
|
3298
|
+
"unit-written"
|
|
3299
|
+
].includes(journal.phase) || this.options.backend.restoreSupportFile === void 0) return {
|
|
3300
|
+
ok: false,
|
|
3301
|
+
message: "support file live hash does not match the prepared support; manual recovery required"
|
|
3302
|
+
};
|
|
3303
|
+
for (const file of snapshot.supportAfter) await this.options.backend.restoreSupportFile(snapshot.target, file.path, file.content);
|
|
3304
|
+
liveSupport = await Promise.all(snapshot.supportAfter.map(async (file) => ({
|
|
3305
|
+
path: file.path,
|
|
3306
|
+
content: await this.options.backend.readSupportFile(snapshot.target, file.path)
|
|
3307
|
+
})));
|
|
3308
|
+
return supportMatches() ? { ok: true } : {
|
|
3309
|
+
ok: false,
|
|
3310
|
+
message: "support file could not be completed from the prepared snapshot"
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
/** The complete evidence record both settlement paths write. */
|
|
3314
|
+
async evidenceFor(unitId, snapshot, journal, targetAfter) {
|
|
3315
|
+
const checkpoints = journal.checkpoints ?? [];
|
|
3316
|
+
return {
|
|
3317
|
+
schemaVersion: 1,
|
|
3318
|
+
unitId,
|
|
3319
|
+
runId: snapshot.runId,
|
|
3320
|
+
planDigest: snapshot.planDigest,
|
|
3321
|
+
state: "complete",
|
|
3322
|
+
target: {
|
|
3323
|
+
before: snapshot.targetBefore,
|
|
3324
|
+
after: targetAfter,
|
|
3325
|
+
supportDigest: createHash("sha256").update(JSON.stringify(snapshot.supportAfter)).digest("hex"),
|
|
3326
|
+
created: !snapshot.targetExists
|
|
3327
|
+
},
|
|
3328
|
+
sources: snapshot.sourceBefore.map((source) => ({
|
|
3329
|
+
name: source.name,
|
|
3330
|
+
before: source.body,
|
|
3331
|
+
after: null,
|
|
3332
|
+
archived: checkpoints.some((item) => item.source === source.name),
|
|
3333
|
+
absorbedInto: snapshot.target
|
|
3334
|
+
})),
|
|
3335
|
+
mutations: 0,
|
|
3336
|
+
...snapshot.validationPlan === void 0 ? {} : { validationPlan: snapshot.validationPlan },
|
|
3337
|
+
at: this.now()
|
|
3338
|
+
};
|
|
3339
|
+
}
|
|
3371
3340
|
async finalize(unitId) {
|
|
3372
3341
|
return await this.withRecoveryFence(async () => this.finalizeUnlocked(unitId));
|
|
3373
3342
|
}
|
|
@@ -3412,55 +3381,13 @@ var MergeUnitTransactions = class {
|
|
|
3412
3381
|
code: "unit-rollback-blocked",
|
|
3413
3382
|
message: "source is still live: " + liveSource.name
|
|
3414
3383
|
};
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
if (!supportMatches()) {
|
|
3421
|
-
if (![
|
|
3422
|
-
"support-pending",
|
|
3423
|
-
"sources-archived",
|
|
3424
|
-
"unit-written"
|
|
3425
|
-
].includes(journal.phase) || this.options.backend.restoreSupportFile === void 0) return {
|
|
3426
|
-
ok: false,
|
|
3427
|
-
code: "unit-rollback-blocked",
|
|
3428
|
-
message: "support file live hash does not match the prepared support; manual recovery required"
|
|
3429
|
-
};
|
|
3430
|
-
for (const file of snapshot.supportAfter) await this.options.backend.restoreSupportFile(snapshot.target, file.path, file.content);
|
|
3431
|
-
liveSupport = await Promise.all(snapshot.supportAfter.map(async (file) => ({
|
|
3432
|
-
path: file.path,
|
|
3433
|
-
content: await this.options.backend.readSupportFile(snapshot.target, file.path)
|
|
3434
|
-
})));
|
|
3435
|
-
if (!supportMatches()) return {
|
|
3436
|
-
ok: false,
|
|
3437
|
-
code: "unit-rollback-blocked",
|
|
3438
|
-
message: "support file could not be completed from the prepared snapshot"
|
|
3439
|
-
};
|
|
3440
|
-
}
|
|
3441
|
-
const evidence = {
|
|
3442
|
-
schemaVersion: 1,
|
|
3443
|
-
unitId,
|
|
3444
|
-
runId: snapshot.runId,
|
|
3445
|
-
planDigest: snapshot.planDigest,
|
|
3446
|
-
state: "complete",
|
|
3447
|
-
target: {
|
|
3448
|
-
before: snapshot.targetBefore,
|
|
3449
|
-
after: await this.options.backend.readSkill(snapshot.target) ?? "",
|
|
3450
|
-
supportDigest: createHash("sha256").update(JSON.stringify(snapshot.supportAfter)).digest("hex"),
|
|
3451
|
-
created: !snapshot.targetExists
|
|
3452
|
-
},
|
|
3453
|
-
sources: snapshot.sourceBefore.map((source) => ({
|
|
3454
|
-
name: source.name,
|
|
3455
|
-
before: source.body,
|
|
3456
|
-
after: null,
|
|
3457
|
-
archived: checkpoints.some((item) => item.source === source.name),
|
|
3458
|
-
absorbedInto: snapshot.target
|
|
3459
|
-
})),
|
|
3460
|
-
mutations: 0,
|
|
3461
|
-
...snapshot.validationPlan === void 0 ? {} : { validationPlan: snapshot.validationPlan },
|
|
3462
|
-
at: this.now()
|
|
3384
|
+
const support = await this.completeSupport(snapshot, journal);
|
|
3385
|
+
if (!support.ok) return {
|
|
3386
|
+
ok: false,
|
|
3387
|
+
code: "unit-rollback-blocked",
|
|
3388
|
+
message: support.message
|
|
3463
3389
|
};
|
|
3390
|
+
const evidence = await this.evidenceFor(unitId, snapshot, journal, await this.options.backend.readSkill(snapshot.target) ?? "");
|
|
3464
3391
|
if (receipt?.evidenceSha256 !== void 0 && unitEvidenceDigest(evidence) !== receipt.evidenceSha256) return {
|
|
3465
3392
|
ok: false,
|
|
3466
3393
|
code: "unit-rollback-blocked",
|
|
@@ -3522,55 +3449,13 @@ var MergeUnitTransactions = class {
|
|
|
3522
3449
|
code: "unit-rollback-blocked",
|
|
3523
3450
|
message: "source is still live: " + liveSource.name
|
|
3524
3451
|
};
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
if (!supportMatches()) {
|
|
3531
|
-
if (![
|
|
3532
|
-
"support-pending",
|
|
3533
|
-
"sources-archived",
|
|
3534
|
-
"unit-written"
|
|
3535
|
-
].includes(journal.phase) || this.options.backend.restoreSupportFile === void 0) return {
|
|
3536
|
-
ok: false,
|
|
3537
|
-
code: "unit-rollback-blocked",
|
|
3538
|
-
message: "support file live hash does not match the prepared support; manual recovery required"
|
|
3539
|
-
};
|
|
3540
|
-
for (const file of snapshot.supportAfter) await this.options.backend.restoreSupportFile(snapshot.target, file.path, file.content);
|
|
3541
|
-
liveSupport = await Promise.all(snapshot.supportAfter.map(async (file) => ({
|
|
3542
|
-
path: file.path,
|
|
3543
|
-
content: await this.options.backend.readSupportFile(snapshot.target, file.path)
|
|
3544
|
-
})));
|
|
3545
|
-
if (!supportMatches()) return {
|
|
3546
|
-
ok: false,
|
|
3547
|
-
code: "unit-rollback-blocked",
|
|
3548
|
-
message: "support file could not be completed from the prepared snapshot"
|
|
3549
|
-
};
|
|
3550
|
-
}
|
|
3551
|
-
const evidence = {
|
|
3552
|
-
schemaVersion: 1,
|
|
3553
|
-
unitId,
|
|
3554
|
-
runId: snapshot.runId,
|
|
3555
|
-
planDigest: snapshot.planDigest,
|
|
3556
|
-
state: "complete",
|
|
3557
|
-
target: {
|
|
3558
|
-
before: snapshot.targetBefore,
|
|
3559
|
-
after: targetAfter,
|
|
3560
|
-
supportDigest: createHash("sha256").update(JSON.stringify(snapshot.supportAfter)).digest("hex"),
|
|
3561
|
-
created: !snapshot.targetExists
|
|
3562
|
-
},
|
|
3563
|
-
sources: snapshot.sourceBefore.map((source) => ({
|
|
3564
|
-
name: source.name,
|
|
3565
|
-
before: source.body,
|
|
3566
|
-
after: null,
|
|
3567
|
-
archived: checkpoints.some((item) => item.source === source.name),
|
|
3568
|
-
absorbedInto: snapshot.target
|
|
3569
|
-
})),
|
|
3570
|
-
mutations: 0,
|
|
3571
|
-
...snapshot.validationPlan === void 0 ? {} : { validationPlan: snapshot.validationPlan },
|
|
3572
|
-
at: this.now()
|
|
3452
|
+
const support = await this.completeSupport(snapshot, journal);
|
|
3453
|
+
if (!support.ok) return {
|
|
3454
|
+
ok: false,
|
|
3455
|
+
code: "unit-rollback-blocked",
|
|
3456
|
+
message: support.message
|
|
3573
3457
|
};
|
|
3458
|
+
const evidence = await this.evidenceFor(unitId, snapshot, journal, targetAfter);
|
|
3574
3459
|
await writeUnitEvidence(this.options.io, this.options.home, evidence);
|
|
3575
3460
|
await writeUnitJournal(this.options.io, this.options.home, {
|
|
3576
3461
|
...journal,
|
|
@@ -3602,33 +3487,10 @@ var MergeUnitTransactions = class {
|
|
|
3602
3487
|
let lock;
|
|
3603
3488
|
let claim;
|
|
3604
3489
|
try {
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
} else {
|
|
3609
|
-
const fallbackLease = await new HomeAuthority({
|
|
3610
|
-
home: this.options.home,
|
|
3611
|
-
waitMs: this.options.waitMs ?? 5e3
|
|
3612
|
-
}).lease("tree").catch(() => void 0);
|
|
3613
|
-
if (fallbackLease === void 0) return {
|
|
3614
|
-
ok: false,
|
|
3615
|
-
code: "unit-fence-failed",
|
|
3616
|
-
message: "tree lock unavailable"
|
|
3617
|
-
};
|
|
3618
|
-
fenceLease = {
|
|
3619
|
-
lock: leaseAsTreeLock(fallbackLease),
|
|
3620
|
-
release: async () => {
|
|
3621
|
-
await fallbackLease.release();
|
|
3622
|
-
}
|
|
3623
|
-
};
|
|
3624
|
-
lock = fenceLease.lock;
|
|
3625
|
-
}
|
|
3490
|
+
const acquired = await this.acquireTreeFence();
|
|
3491
|
+
fenceLease = acquired.fenceLease;
|
|
3492
|
+
lock = acquired.lock;
|
|
3626
3493
|
this.options.onLock?.("tree");
|
|
3627
|
-
if (!await lock.assertHeld()) return {
|
|
3628
|
-
ok: false,
|
|
3629
|
-
code: "unit-fence-failed",
|
|
3630
|
-
message: "tree fence lost"
|
|
3631
|
-
};
|
|
3632
3494
|
claim = await this.options.claim.acquire(snapshot.target, snapshot.runId);
|
|
3633
3495
|
this.options.onLock?.("claim");
|
|
3634
3496
|
const currentTarget = await this.options.backend.readSkill(snapshot.target);
|
|
@@ -4668,78 +4530,6 @@ const ALLOWED = [
|
|
|
4668
4530
|
evidence: "none",
|
|
4669
4531
|
activeRuns: 0
|
|
4670
4532
|
},
|
|
4671
|
-
{
|
|
4672
|
-
row: "fuse",
|
|
4673
|
-
run: "none",
|
|
4674
|
-
staging: "none",
|
|
4675
|
-
journal: "none",
|
|
4676
|
-
evidence: "none",
|
|
4677
|
-
activeRuns: 0,
|
|
4678
|
-
recovery: true
|
|
4679
|
-
},
|
|
4680
|
-
{
|
|
4681
|
-
row: "fuse",
|
|
4682
|
-
run: "failed",
|
|
4683
|
-
staging: "none",
|
|
4684
|
-
journal: "none",
|
|
4685
|
-
evidence: "none",
|
|
4686
|
-
activeRuns: 0,
|
|
4687
|
-
recovery: true
|
|
4688
|
-
},
|
|
4689
|
-
{
|
|
4690
|
-
row: "fuse",
|
|
4691
|
-
run: "orphan",
|
|
4692
|
-
staging: "none",
|
|
4693
|
-
journal: "none",
|
|
4694
|
-
evidence: "none",
|
|
4695
|
-
activeRuns: 0,
|
|
4696
|
-
recovery: true
|
|
4697
|
-
},
|
|
4698
|
-
{
|
|
4699
|
-
row: "fuse",
|
|
4700
|
-
run: "running",
|
|
4701
|
-
staging: "none",
|
|
4702
|
-
journal: "none",
|
|
4703
|
-
evidence: "none",
|
|
4704
|
-
activeRuns: 1,
|
|
4705
|
-
recovery: true
|
|
4706
|
-
},
|
|
4707
|
-
{
|
|
4708
|
-
row: "fuse",
|
|
4709
|
-
run: "none",
|
|
4710
|
-
staging: "missing",
|
|
4711
|
-
journal: "none",
|
|
4712
|
-
evidence: "none",
|
|
4713
|
-
activeRuns: 0,
|
|
4714
|
-
recovery: true
|
|
4715
|
-
},
|
|
4716
|
-
{
|
|
4717
|
-
row: "fuse",
|
|
4718
|
-
run: "failed",
|
|
4719
|
-
staging: "missing",
|
|
4720
|
-
journal: "none",
|
|
4721
|
-
evidence: "none",
|
|
4722
|
-
activeRuns: 0,
|
|
4723
|
-
recovery: true
|
|
4724
|
-
},
|
|
4725
|
-
{
|
|
4726
|
-
row: "fuse",
|
|
4727
|
-
run: "orphan",
|
|
4728
|
-
staging: "missing",
|
|
4729
|
-
journal: "none",
|
|
4730
|
-
evidence: "none",
|
|
4731
|
-
activeRuns: 0,
|
|
4732
|
-
recovery: true
|
|
4733
|
-
},
|
|
4734
|
-
{
|
|
4735
|
-
row: "fuse",
|
|
4736
|
-
run: "running",
|
|
4737
|
-
staging: "missing",
|
|
4738
|
-
journal: "none",
|
|
4739
|
-
evidence: "none",
|
|
4740
|
-
activeRuns: 1,
|
|
4741
|
-
recovery: true
|
|
4742
|
-
},
|
|
4743
4533
|
{
|
|
4744
4534
|
row: "fuse",
|
|
4745
4535
|
run: "running",
|
|
@@ -4774,7 +4564,7 @@ const ALLOWED = [
|
|
|
4774
4564
|
}
|
|
4775
4565
|
];
|
|
4776
4566
|
function same(a, b) {
|
|
4777
|
-
return a.row === b.row && a.run === b.run && a.staging === b.staging && a.journal === b.journal && a.evidence === b.evidence && a.activeRuns === b.activeRuns
|
|
4567
|
+
return a.row === b.row && a.run === b.run && a.staging === b.staging && a.journal === b.journal && a.evidence === b.evidence && a.activeRuns === b.activeRuns;
|
|
4778
4568
|
}
|
|
4779
4569
|
function validateArtifacts(state) {
|
|
4780
4570
|
if (state.activeRuns > 1) return {
|
|
@@ -4794,7 +4584,7 @@ function validateArtifacts(state) {
|
|
|
4794
4584
|
ok: false,
|
|
4795
4585
|
reason: "skipped-running"
|
|
4796
4586
|
};
|
|
4797
|
-
if (state.row === "fuse" && state.staging === "missing"
|
|
4587
|
+
if (state.row === "fuse" && state.staging === "missing") return {
|
|
4798
4588
|
ok: false,
|
|
4799
4589
|
reason: "fuse-lost-staging"
|
|
4800
4590
|
};
|
|
@@ -4812,35 +4602,12 @@ function validateArtifacts(state) {
|
|
|
4812
4602
|
};
|
|
4813
4603
|
}
|
|
4814
4604
|
//#endregion
|
|
4815
|
-
//#region lib/types/domain/commit-journal-store.js
|
|
4816
|
-
/** Durable commit journal: <home>/merge/journals/<run>.json. */
|
|
4817
|
-
function journalPath(home, runId) {
|
|
4818
|
-
return join(home, "merge", "journals", runId.replace(/[^A-Za-z0-9_-]+/g, "_") + ".json");
|
|
4819
|
-
}
|
|
4820
|
-
async function writeJournal(io, home, journal) {
|
|
4821
|
-
const path = journalPath(home, journal.runId);
|
|
4822
|
-
const content = JSON.stringify(journal, null, 2);
|
|
4823
|
-
if (io.transact !== void 0) await io.transact(path, () => content);
|
|
4824
|
-
else await io.writeText(path, content);
|
|
4825
|
-
return path;
|
|
4826
|
-
}
|
|
4827
|
-
async function readJournal(io, home, runId) {
|
|
4828
|
-
const raw = await io.readText(journalPath(home, runId));
|
|
4829
|
-
if (raw === null) return null;
|
|
4830
|
-
try {
|
|
4831
|
-
return JSON.parse(raw);
|
|
4832
|
-
} catch {
|
|
4833
|
-
return null;
|
|
4834
|
-
}
|
|
4835
|
-
}
|
|
4836
|
-
//#endregion
|
|
4837
4605
|
//#region lib/types/doctor.js
|
|
4838
4606
|
/**
|
|
4839
4607
|
* Merge doctor rows: state, pending, gate cache and staging are each probed read-only.
|
|
4840
4608
|
* A missing file is an empty feature, not a failure; an unreadable file is a warning the
|
|
4841
4609
|
* operator can act on. This module never repairs or writes.
|
|
4842
4610
|
*/
|
|
4843
|
-
const MERGE_DOCTOR_SCHEMA_VERSION = 3;
|
|
4844
4611
|
/** Compose the one-line global-doctor fact from buildDoctorView + the last reconcile report. */
|
|
4845
4612
|
function mergeDoctorFact(view, report) {
|
|
4846
4613
|
const counts = view.counts;
|
|
@@ -4889,13 +4656,6 @@ async function mergeDoctor(io, home, configuredRoot) {
|
|
|
4889
4656
|
status: snapshot.blocked === void 0 || !hasState ? "ok" : "warn",
|
|
4890
4657
|
detail: snapshot.blocked === void 0 ? "pairs " + String(snapshot.rows.length) + ";verify " + String(counts.verify) + " / pending " + String(counts.pending) + " / fuse " + String(counts.fuse) + " / skipped " + String(counts.skipped) + " / expired " + String(snapshot.doctor.counts.expired) : snapshot.blocked.code + ": " + snapshot.blocked.message
|
|
4891
4658
|
});
|
|
4892
|
-
const recoveryRuns = /* @__PURE__ */ new Set();
|
|
4893
|
-
try {
|
|
4894
|
-
const raw = await io.readText(join(home, "merge", "recovery.json"));
|
|
4895
|
-
if (raw !== null) {
|
|
4896
|
-
for (const report of JSON.parse(raw)) if (report.ok === true && report.runId !== void 0) recoveryRuns.add(report.runId);
|
|
4897
|
-
}
|
|
4898
|
-
} catch {}
|
|
4899
4659
|
const invalid = [];
|
|
4900
4660
|
if (snapshot.blocked !== void 0) invalid.push({
|
|
4901
4661
|
id: "state",
|
|
@@ -4908,15 +4668,14 @@ async function mergeDoctor(io, home, configuredRoot) {
|
|
|
4908
4668
|
const run = runId === void 0 ? "none" : row.state === "pending" ? "awaiting-approval" : "running";
|
|
4909
4669
|
const staging = item.stage === null ? row.state === "fuse" ? "missing" : "none" : item.stage.state === "present" || item.stage.state === "reserved" ? "kept" : row.state === "fuse" ? "missing" : "none";
|
|
4910
4670
|
const receipt = runId === void 0 ? null : await readCommitReceipt(io, home, runId);
|
|
4911
|
-
const phase = receipt
|
|
4671
|
+
const phase = receipt === null ? null : await readUnitJournal(io, home, receipt.unitId);
|
|
4912
4672
|
const result = validateArtifacts({
|
|
4913
4673
|
row: artifactRow,
|
|
4914
4674
|
run,
|
|
4915
4675
|
staging,
|
|
4916
4676
|
journal: phase === null ? "none" : phase.phase === "prepared" || phase.phase === "target-pending" ? "prepared" : phase.phase === "target-written" || phase.phase === "sources-archived" || phase.phase === "support-pending" || phase.phase === "source-archiving" ? "committing" : phase.phase === "committed" ? "committed" : phase.phase === "rolled-back" ? "rolled-back" : "failed",
|
|
4917
4677
|
evidence: receipt === null ? "none" : "receipt",
|
|
4918
|
-
activeRuns: run === "running" || run === "awaiting-approval" ? 1 : 0
|
|
4919
|
-
...runId !== void 0 && recoveryRuns.has(runId) ? { recovery: true } : {}
|
|
4678
|
+
activeRuns: run === "running" || run === "awaiting-approval" ? 1 : 0
|
|
4920
4679
|
});
|
|
4921
4680
|
if (!result.ok) invalid.push({
|
|
4922
4681
|
id: row.id,
|
|
@@ -5059,26 +4818,6 @@ async function mergeDoctor(io, home, configuredRoot) {
|
|
|
5059
4818
|
status: "ok",
|
|
5060
4819
|
detail: stagingRoot(home, "<runId>")
|
|
5061
4820
|
});
|
|
5062
|
-
try {
|
|
5063
|
-
const raw = await io.readText(join(home, "merge", "recovery.json"));
|
|
5064
|
-
if (raw !== null) {
|
|
5065
|
-
const reports = JSON.parse(raw);
|
|
5066
|
-
const failed = reports.filter((report) => report.ok !== true);
|
|
5067
|
-
rows.push({
|
|
5068
|
-
key: "recovery",
|
|
5069
|
-
label: "提交恢复",
|
|
5070
|
-
status: failed.length === 0 ? "ok" : "warn",
|
|
5071
|
-
detail: failed.length === 0 ? String(reports.length) + " 条恢复报告,无待处理" : String(failed.length) + " 条待处理:" + failed.map((report) => (report.runId ?? "?") + "=" + (report.action ?? "?")).join(", ")
|
|
5072
|
-
});
|
|
5073
|
-
}
|
|
5074
|
-
} catch {
|
|
5075
|
-
rows.push({
|
|
5076
|
-
key: "recovery",
|
|
5077
|
-
label: "提交恢复",
|
|
5078
|
-
status: "unknown",
|
|
5079
|
-
detail: "无法读取 merge/recovery.json"
|
|
5080
|
-
});
|
|
5081
|
-
}
|
|
5082
4821
|
return rows;
|
|
5083
4822
|
}
|
|
5084
4823
|
//#endregion
|
|
@@ -5135,6 +4874,48 @@ async function resolveBody(io, home, ref, rowBody) {
|
|
|
5135
4874
|
return await rowBody();
|
|
5136
4875
|
}
|
|
5137
4876
|
//#endregion
|
|
4877
|
+
//#region lib/types/domain/state-migrate.js
|
|
4878
|
+
/**
|
|
4879
|
+
* Migrate v1/v2/v3 merge state to v4. v4 is a no-op; forward-only/needs-repair are reported
|
|
4880
|
+
* before attempting anything, so a newer state is never overwritten.
|
|
4881
|
+
* @param io - the evolution IO seam.
|
|
4882
|
+
* @param home - the evolution home.
|
|
4883
|
+
* @param fence - optional home write fence used by the host.
|
|
4884
|
+
* @returns the migration outcome.
|
|
4885
|
+
*/
|
|
4886
|
+
async function migrateMergeStateToV4(io, home, fence) {
|
|
4887
|
+
const v4 = await readV4Readiness(io, home);
|
|
4888
|
+
if (v4.ok) return {
|
|
4889
|
+
ok: true,
|
|
4890
|
+
migrated: false
|
|
4891
|
+
};
|
|
4892
|
+
if (v4.code === "merge-state-forward-only" || v4.code === "merge-state-needs-repair") return {
|
|
4893
|
+
ok: false,
|
|
4894
|
+
code: v4.code,
|
|
4895
|
+
message: v4.message
|
|
4896
|
+
};
|
|
4897
|
+
const v3 = await ensureMergeStateReady(io, home, fence);
|
|
4898
|
+
if (!v3.ok) return {
|
|
4899
|
+
ok: false,
|
|
4900
|
+
code: v3.code,
|
|
4901
|
+
message: v3.message
|
|
4902
|
+
};
|
|
4903
|
+
const normalized = await normalizePairLedger(io, home);
|
|
4904
|
+
if (!normalized.ok) return {
|
|
4905
|
+
ok: false,
|
|
4906
|
+
code: "merge-state-needs-repair",
|
|
4907
|
+
message: normalized.message ?? "v3->v4 normalize failed"
|
|
4908
|
+
};
|
|
4909
|
+
return normalized.backupRef === void 0 ? {
|
|
4910
|
+
ok: true,
|
|
4911
|
+
migrated: normalized.migrated
|
|
4912
|
+
} : {
|
|
4913
|
+
ok: true,
|
|
4914
|
+
migrated: normalized.migrated,
|
|
4915
|
+
backupRef: normalized.backupRef
|
|
4916
|
+
};
|
|
4917
|
+
}
|
|
4918
|
+
//#endregion
|
|
5138
4919
|
//#region lib/types/domain/evidence-hash.js
|
|
5139
4920
|
/** Canonical evidence JSON hash shared with core unit receipts. */
|
|
5140
4921
|
function canonical(value) {
|
|
@@ -5275,13 +5056,6 @@ async function projectRecoveredUnit(options) {
|
|
|
5275
5056
|
await options.onPhase?.("stage-drop");
|
|
5276
5057
|
await dropStage(options.io, options.home, stageRowId);
|
|
5277
5058
|
}
|
|
5278
|
-
await options.onPhase?.("pair-index");
|
|
5279
|
-
await recordPair(options.io, options.home, {
|
|
5280
|
-
pairKey,
|
|
5281
|
-
runId: options.runId,
|
|
5282
|
-
result: "merged",
|
|
5283
|
-
target
|
|
5284
|
-
});
|
|
5285
5059
|
await options.onPhase?.("run-meta");
|
|
5286
5060
|
const meta = await readRunMeta(options.io, options.home, options.runId);
|
|
5287
5061
|
await writeRunMeta(options.io, options.home, {
|
|
@@ -5996,9 +5770,6 @@ async function handleReadOnlyMergeCommand(command, io, home, cards, runs, approv
|
|
|
5996
5770
|
}
|
|
5997
5771
|
return formatSnapshotQueue(rows.filter((row) => row.queue === "skipped"), "skipped");
|
|
5998
5772
|
}
|
|
5999
|
-
function mergeCommandHelp() {
|
|
6000
|
-
return mergeUsage();
|
|
6001
|
-
}
|
|
6002
5773
|
//#endregion
|
|
6003
5774
|
//#region lib/types/merge-command.js
|
|
6004
5775
|
function createReadOnlyMergeService(io, home) {
|
|
@@ -6052,10 +5823,6 @@ function createMergeCommand(service, version = MERGE_PACKAGE_VERSION) {
|
|
|
6052
5823
|
}
|
|
6053
5824
|
//#endregion
|
|
6054
5825
|
//#region lib/types/skipped.js
|
|
6055
|
-
/**
|
|
6056
|
-
* Skipped filtering: a pair the operator marked as not-worth-merging stays out of automatic
|
|
6057
|
-
* screening until mergeSkippedTtlDays expires. Explicit /merge pair ignores this set.
|
|
6058
|
-
*/
|
|
6059
5826
|
function skippedPairKeys(rows, ttlDays, now) {
|
|
6060
5827
|
const keys = /* @__PURE__ */ new Set();
|
|
6061
5828
|
for (const row of rows) {
|
|
@@ -6069,9 +5836,6 @@ function skippedPairKeys(rows, ttlDays, now) {
|
|
|
6069
5836
|
}
|
|
6070
5837
|
return keys;
|
|
6071
5838
|
}
|
|
6072
|
-
function isSkippedPair(keys, a, b) {
|
|
6073
|
-
return keys.has(pairKey(a, b));
|
|
6074
|
-
}
|
|
6075
5839
|
//#endregion
|
|
6076
5840
|
//#region lib/types/domain/engine-criteria.js
|
|
6077
5841
|
function criteriaFrom(level, verdict, action) {
|
|
@@ -6324,11 +6088,6 @@ async function runScan(options) {
|
|
|
6324
6088
|
continue;
|
|
6325
6089
|
}
|
|
6326
6090
|
row = projected;
|
|
6327
|
-
await recordPair(options.io, options.home, {
|
|
6328
|
-
pairKey: row.pairKey,
|
|
6329
|
-
runId: row.runId ?? "merge",
|
|
6330
|
-
result: "kept"
|
|
6331
|
-
});
|
|
6332
6091
|
pairs.push(pairOf(row, candidate, "kept", {
|
|
6333
6092
|
...round1.decision === void 0 ? {} : { decision: round1.decision },
|
|
6334
6093
|
...round1.usage === void 0 ? {} : { usage: round1.usage }
|
|
@@ -6538,15 +6297,7 @@ async function runScan(options) {
|
|
|
6538
6297
|
if (await pairLedger(options.io, options.home).transact((tx) => {
|
|
6539
6298
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6540
6299
|
return current === void 0 ? false : tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
6541
|
-
}))
|
|
6542
|
-
await dropStage(options.io, options.home, row.id, row.stagingRef);
|
|
6543
|
-
await recordPair(options.io, options.home, {
|
|
6544
|
-
pairKey: row.pairKey,
|
|
6545
|
-
runId: row.runId ?? "merge",
|
|
6546
|
-
result: "failed",
|
|
6547
|
-
...row.decision?.target === void 0 ? {} : { target: row.decision.target }
|
|
6548
|
-
});
|
|
6549
|
-
}
|
|
6300
|
+
})) await dropStage(options.io, options.home, row.id, row.stagingRef);
|
|
6550
6301
|
pairs.push(pairOf(row, candidate, "failed", {
|
|
6551
6302
|
decision: mergeDecision,
|
|
6552
6303
|
failure: failed,
|
|
@@ -7048,12 +6799,22 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
7048
6799
|
...outcome.criteria === void 0 ? {} : { criteria: outcome.criteria },
|
|
7049
6800
|
...outcome.validationIssues === void 0 ? {} : { validation: { issues: outcome.validationIssues } }
|
|
7050
6801
|
};
|
|
6802
|
+
const pair = (outcomeName, extra = {}, withDecision = true) => ({
|
|
6803
|
+
id: row.id,
|
|
6804
|
+
pairKey: row.pairKey,
|
|
6805
|
+
a: row.a,
|
|
6806
|
+
b: row.b,
|
|
6807
|
+
queue: row.queue,
|
|
6808
|
+
...withDecision && row.decision !== void 0 ? { decision: row.decision } : {},
|
|
6809
|
+
outcome: outcomeName,
|
|
6810
|
+
archived: [],
|
|
6811
|
+
...extra
|
|
6812
|
+
});
|
|
7051
6813
|
if (outcome.kind === "merged") {
|
|
7052
6814
|
const unitRef = {
|
|
7053
6815
|
unitId: outcome.unitId ?? "unknown",
|
|
7054
6816
|
evidenceSha256: outcome.evidenceSha256 ?? "unknown"
|
|
7055
6817
|
};
|
|
7056
|
-
const target = outcome.target?.actual ?? outcome.target?.requested ?? row.decision?.target;
|
|
7057
6818
|
const finalize = async () => {
|
|
7058
6819
|
if (!(await pairLedger(io, home, fence).transact((tx) => {
|
|
7059
6820
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
@@ -7064,50 +6825,28 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
7064
6825
|
} : tx.remove(row.id, "committed", unitRef, current.version);
|
|
7065
6826
|
})).ok) throw new Error("row changed before commit record: " + row.id);
|
|
7066
6827
|
await dropStage(io, home, row.id);
|
|
7067
|
-
await recordPair(io, home, {
|
|
7068
|
-
pairKey: row.pairKey,
|
|
7069
|
-
runId: row.runId ?? "merge",
|
|
7070
|
-
result: "merged",
|
|
7071
|
-
...target === void 0 ? {} : { target },
|
|
7072
|
-
...outcome.unitId === void 0 ? {} : { reportRef: outcome.unitId }
|
|
7073
|
-
});
|
|
7074
6828
|
};
|
|
7075
6829
|
if (deferProjection) return {
|
|
7076
6830
|
merged: true,
|
|
7077
6831
|
finalize,
|
|
7078
|
-
pair: {
|
|
7079
|
-
id: row.id,
|
|
7080
|
-
pairKey: row.pairKey,
|
|
7081
|
-
a: row.a,
|
|
7082
|
-
b: row.b,
|
|
7083
|
-
queue: row.queue,
|
|
7084
|
-
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
7085
|
-
outcome: "merged",
|
|
7086
|
-
archived: outcome.archived ?? [],
|
|
6832
|
+
pair: pair("merged", {
|
|
7087
6833
|
...outcome.lengths === void 0 ? {} : { lengths: outcome.lengths },
|
|
7088
6834
|
...outcome.unitId === void 0 ? {} : { unitId: outcome.unitId },
|
|
7089
6835
|
...outcome.target === void 0 ? {} : { target: outcome.target },
|
|
7090
6836
|
...reportExtra
|
|
7091
|
-
}
|
|
6837
|
+
})
|
|
7092
6838
|
};
|
|
7093
6839
|
await finalize();
|
|
7094
6840
|
return {
|
|
7095
6841
|
merged: true,
|
|
7096
|
-
pair: {
|
|
7097
|
-
|
|
7098
|
-
pairKey: row.pairKey,
|
|
7099
|
-
a: row.a,
|
|
7100
|
-
b: row.b,
|
|
7101
|
-
queue: row.queue,
|
|
7102
|
-
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
7103
|
-
outcome: "merged",
|
|
7104
|
-
archived: outcome.archived ?? [],
|
|
6842
|
+
pair: pair("merged", {
|
|
6843
|
+
...outcome.archived === void 0 ? {} : { archived: outcome.archived },
|
|
7105
6844
|
...outcome.lengths === void 0 ? {} : { lengths: outcome.lengths },
|
|
7106
6845
|
...reportExtra,
|
|
7107
6846
|
...outcome.unitId === void 0 ? {} : { unitId: outcome.unitId },
|
|
7108
6847
|
...outcome.target === void 0 ? {} : { target: outcome.target },
|
|
7109
6848
|
...outcome.validationPlan === void 0 ? {} : { validationPlan: outcome.validationPlan }
|
|
7110
|
-
}
|
|
6849
|
+
})
|
|
7111
6850
|
};
|
|
7112
6851
|
}
|
|
7113
6852
|
const failure = outcome.failure ?? {
|
|
@@ -7130,18 +6869,10 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
7130
6869
|
})) await dropStage(io, home, row.id);
|
|
7131
6870
|
return {
|
|
7132
6871
|
merged: false,
|
|
7133
|
-
pair: {
|
|
7134
|
-
id: row.id,
|
|
7135
|
-
pairKey: row.pairKey,
|
|
7136
|
-
a: row.a,
|
|
7137
|
-
b: row.b,
|
|
7138
|
-
queue: row.queue,
|
|
7139
|
-
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
7140
|
-
outcome: "failed",
|
|
7141
|
-
archived: [],
|
|
6872
|
+
pair: pair("failed", {
|
|
7142
6873
|
failure,
|
|
7143
6874
|
...reportExtra
|
|
7144
|
-
}
|
|
6875
|
+
})
|
|
7145
6876
|
};
|
|
7146
6877
|
}
|
|
7147
6878
|
const cas = casExpiredHook(failure.code);
|
|
@@ -7163,33 +6894,17 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
7163
6894
|
await dropStage(io, home, row.id);
|
|
7164
6895
|
return {
|
|
7165
6896
|
merged: false,
|
|
7166
|
-
pair: {
|
|
7167
|
-
id: row.id,
|
|
7168
|
-
pairKey: row.pairKey,
|
|
7169
|
-
a: row.a,
|
|
7170
|
-
b: row.b,
|
|
7171
|
-
queue: row.queue,
|
|
7172
|
-
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
7173
|
-
outcome: "failed",
|
|
7174
|
-
archived: [],
|
|
6897
|
+
pair: pair("failed", {
|
|
7175
6898
|
failure: terminalFailure,
|
|
7176
6899
|
...reportExtra
|
|
7177
|
-
}
|
|
6900
|
+
})
|
|
7178
6901
|
};
|
|
7179
6902
|
}
|
|
7180
6903
|
if (failure.code === "commit-failed" || failure.code === "validation-failed") {
|
|
7181
6904
|
if (await pairLedger(io, home, fence).transact((tx) => {
|
|
7182
6905
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
7183
6906
|
return current === void 0 ? false : tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
7184
|
-
}))
|
|
7185
|
-
await dropStage(io, home, row.id, row.stagingRef);
|
|
7186
|
-
await recordPair(io, home, {
|
|
7187
|
-
pairKey: row.pairKey,
|
|
7188
|
-
runId: row.runId ?? "merge",
|
|
7189
|
-
result: "failed",
|
|
7190
|
-
...row.decision?.target === void 0 ? {} : { target: row.decision.target }
|
|
7191
|
-
});
|
|
7192
|
-
}
|
|
6907
|
+
})) await dropStage(io, home, row.id, row.stagingRef);
|
|
7193
6908
|
}
|
|
7194
6909
|
if (failure.code === "source-gone") {
|
|
7195
6910
|
if (await pairLedger(io, home, fence).transact((tx) => {
|
|
@@ -7198,16 +6913,7 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
7198
6913
|
})) await dropStage(io, home, row.id, row.stagingRef);
|
|
7199
6914
|
return {
|
|
7200
6915
|
merged: false,
|
|
7201
|
-
pair: {
|
|
7202
|
-
id: row.id,
|
|
7203
|
-
pairKey: row.pairKey,
|
|
7204
|
-
a: row.a,
|
|
7205
|
-
b: row.b,
|
|
7206
|
-
queue: row.queue,
|
|
7207
|
-
outcome: "failed",
|
|
7208
|
-
archived: [],
|
|
7209
|
-
failure
|
|
7210
|
-
}
|
|
6916
|
+
pair: pair("failed", { failure }, false)
|
|
7211
6917
|
};
|
|
7212
6918
|
}
|
|
7213
6919
|
if (await pairLedger(io, home, fence).transact((tx) => {
|
|
@@ -7220,42 +6926,21 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
7220
6926
|
return moved.ok && moved.row !== null ? v4ToLegacy(moved.row) : null;
|
|
7221
6927
|
}) === null) return {
|
|
7222
6928
|
merged: false,
|
|
7223
|
-
pair: {
|
|
7224
|
-
id: row.id,
|
|
7225
|
-
pairKey: row.pairKey,
|
|
7226
|
-
a: row.a,
|
|
7227
|
-
b: row.b,
|
|
7228
|
-
queue: row.queue,
|
|
7229
|
-
outcome: "failed",
|
|
7230
|
-
archived: [],
|
|
6929
|
+
pair: pair("failed", {
|
|
7231
6930
|
failure: {
|
|
7232
6931
|
...failure,
|
|
7233
6932
|
code: "row-changed",
|
|
7234
6933
|
message: "row changed before failure record"
|
|
7235
6934
|
},
|
|
7236
6935
|
...reportExtra
|
|
7237
|
-
}
|
|
6936
|
+
}, false)
|
|
7238
6937
|
};
|
|
7239
|
-
await recordPair(io, home, {
|
|
7240
|
-
pairKey: row.pairKey,
|
|
7241
|
-
runId: row.runId ?? "merge",
|
|
7242
|
-
result: "failed",
|
|
7243
|
-
...row.decision?.target === void 0 ? {} : { target: row.decision.target }
|
|
7244
|
-
});
|
|
7245
6938
|
return {
|
|
7246
6939
|
merged: false,
|
|
7247
|
-
pair: {
|
|
7248
|
-
id: row.id,
|
|
7249
|
-
pairKey: row.pairKey,
|
|
7250
|
-
a: row.a,
|
|
7251
|
-
b: row.b,
|
|
7252
|
-
queue: row.queue,
|
|
7253
|
-
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
7254
|
-
outcome: "failed",
|
|
7255
|
-
archived: [],
|
|
6940
|
+
pair: pair("failed", {
|
|
7256
6941
|
failure,
|
|
7257
6942
|
...reportExtra
|
|
7258
|
-
}
|
|
6943
|
+
})
|
|
7259
6944
|
};
|
|
7260
6945
|
}
|
|
7261
6946
|
//#endregion
|
|
@@ -7704,14 +7389,6 @@ async function runUndoSteps(options) {
|
|
|
7704
7389
|
};
|
|
7705
7390
|
}
|
|
7706
7391
|
if (await readUndoReceipt(options.io, options.home, options.runId, pairKey) !== null) {
|
|
7707
|
-
await markPhase("pair-index");
|
|
7708
|
-
await options.onPhase?.("pair-index", pairKey);
|
|
7709
|
-
await recordPair(options.io, options.home, {
|
|
7710
|
-
pairKey,
|
|
7711
|
-
runId: options.runId,
|
|
7712
|
-
result: "undone",
|
|
7713
|
-
target: receipt.target
|
|
7714
|
-
});
|
|
7715
7392
|
await markPhase("run-meta");
|
|
7716
7393
|
await options.onPhase?.("run-meta", pairKey);
|
|
7717
7394
|
await settleRun(options.io, options.home, options.runId, pairKey, allPairKeys, options.now());
|
|
@@ -7738,14 +7415,6 @@ async function runUndoSteps(options) {
|
|
|
7738
7415
|
restoredSourceHashes: Object.fromEntries(checked.evidence.sources.map((source) => [source.name, contentHash(source.before)])),
|
|
7739
7416
|
at: options.now()
|
|
7740
7417
|
});
|
|
7741
|
-
await markPhase("pair-index");
|
|
7742
|
-
await options.onPhase?.("pair-index", pairKey);
|
|
7743
|
-
await recordPair(options.io, options.home, {
|
|
7744
|
-
pairKey,
|
|
7745
|
-
runId: options.runId,
|
|
7746
|
-
result: "undone",
|
|
7747
|
-
target: receipt.target
|
|
7748
|
-
});
|
|
7749
7418
|
await markPhase("run-meta");
|
|
7750
7419
|
await options.onPhase?.("run-meta", pairKey);
|
|
7751
7420
|
await settleRun(options.io, options.home, options.runId, pairKey, allPairKeys, options.now());
|
|
@@ -7805,13 +7474,6 @@ async function runUndoSteps(options) {
|
|
|
7805
7474
|
restoredSourceHashes: Object.fromEntries(checked.evidence.sources.map((source) => [source.name, contentHash(source.before)])),
|
|
7806
7475
|
at: options.now()
|
|
7807
7476
|
});
|
|
7808
|
-
await options.onPhase?.("pair-index", pairKey);
|
|
7809
|
-
await recordPair(options.io, options.home, {
|
|
7810
|
-
pairKey,
|
|
7811
|
-
runId: options.runId,
|
|
7812
|
-
result: "undone",
|
|
7813
|
-
target: receipt.target
|
|
7814
|
-
});
|
|
7815
7477
|
await options.onPhase?.("run-meta", pairKey);
|
|
7816
7478
|
await settleRun(options.io, options.home, options.runId, pairKey, allPairKeys, options.now());
|
|
7817
7479
|
undone.push(pairKey);
|
|
@@ -8042,9 +7704,6 @@ function buildRound2Prompt(row, decision, mode = "continue") {
|
|
|
8042
7704
|
...body
|
|
8043
7705
|
] : [...head, ...body]).join("\n");
|
|
8044
7706
|
}
|
|
8045
|
-
function buildStandbyPrompt() {
|
|
8046
|
-
return STANDBY_PROMPT;
|
|
8047
|
-
}
|
|
8048
7707
|
//#endregion
|
|
8049
7708
|
//#region lib/types/channel-port.js
|
|
8050
7709
|
async function runRound1OverChannel(options, candidate, signal) {
|
|
@@ -8552,14 +8211,6 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
8552
8211
|
message: "target is blocked by curator: " + assembled.plan.target,
|
|
8553
8212
|
plan: assembled.plan
|
|
8554
8213
|
};
|
|
8555
|
-
const journal = {
|
|
8556
|
-
runId,
|
|
8557
|
-
target: assembled.plan.target,
|
|
8558
|
-
create: !assembled.plan.targetExists,
|
|
8559
|
-
sources: assembled.plan.sources,
|
|
8560
|
-
phase: "prepared",
|
|
8561
|
-
updatedAt: new Date(options.now()).toISOString()
|
|
8562
|
-
};
|
|
8563
8214
|
let result;
|
|
8564
8215
|
let evidenceSha256$1;
|
|
8565
8216
|
let warning;
|
|
@@ -8589,9 +8240,6 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
8589
8240
|
...options.sourceRevisions === void 0 ? {} : { sourceRevisions: options.sourceRevisions },
|
|
8590
8241
|
...options.supportDigest === void 0 ? {} : { supportDigest: options.supportDigest }
|
|
8591
8242
|
};
|
|
8592
|
-
try {
|
|
8593
|
-
await writeJournal(io, home, journal);
|
|
8594
|
-
} catch {}
|
|
8595
8243
|
const prepared = await transaction.prepare(unitPlan);
|
|
8596
8244
|
if (!prepared.ok) return {
|
|
8597
8245
|
ok: false,
|
|
@@ -8599,21 +8247,12 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
8599
8247
|
plan: assembled.plan
|
|
8600
8248
|
};
|
|
8601
8249
|
const committed = await transaction.commit(prepared.prepared, options.signal === void 0 ? void 0 : { signal: options.signal });
|
|
8602
|
-
if (!committed.ok) {
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
});
|
|
8609
|
-
} catch {}
|
|
8610
|
-
return {
|
|
8611
|
-
ok: false,
|
|
8612
|
-
message: committed.message,
|
|
8613
|
-
plan: assembled.plan,
|
|
8614
|
-
...committed.code === "target-changed" || committed.code === "source-changed" || committed.code === "support-changed" ? { code: committed.code } : {}
|
|
8615
|
-
};
|
|
8616
|
-
}
|
|
8250
|
+
if (!committed.ok) return {
|
|
8251
|
+
ok: false,
|
|
8252
|
+
message: committed.message,
|
|
8253
|
+
plan: assembled.plan,
|
|
8254
|
+
...committed.code === "target-changed" || committed.code === "source-changed" || committed.code === "support-changed" ? { code: committed.code } : {}
|
|
8255
|
+
};
|
|
8617
8256
|
const unitId = committed.evidence.unitId;
|
|
8618
8257
|
if (committed.evidence.unitId !== prepared.prepared.unitId && !prepared.prepared.unitId.startsWith("legacy:")) return {
|
|
8619
8258
|
ok: false,
|
|
@@ -8634,29 +8273,11 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
8634
8273
|
mutations: committed.evidence.mutations
|
|
8635
8274
|
};
|
|
8636
8275
|
}
|
|
8637
|
-
if (!result.ok) {
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
phase: "failed",
|
|
8642
|
-
updatedAt: new Date(options.now()).toISOString()
|
|
8643
|
-
});
|
|
8644
|
-
} catch {}
|
|
8645
|
-
return {
|
|
8646
|
-
ok: false,
|
|
8647
|
-
message: result.message,
|
|
8648
|
-
plan: assembled.plan
|
|
8649
|
-
};
|
|
8650
|
-
}
|
|
8651
|
-
const committed = {
|
|
8652
|
-
...journal,
|
|
8653
|
-
phase: "committed",
|
|
8654
|
-
updatedAt: new Date(options.now()).toISOString(),
|
|
8655
|
-
...result.unitId === void 0 ? {} : { unitId: result.unitId }
|
|
8276
|
+
if (!result.ok) return {
|
|
8277
|
+
ok: false,
|
|
8278
|
+
message: result.message,
|
|
8279
|
+
plan: assembled.plan
|
|
8656
8280
|
};
|
|
8657
|
-
try {
|
|
8658
|
-
await writeJournal(io, home, committed);
|
|
8659
|
-
} catch {}
|
|
8660
8281
|
if (result.unitId === void 0) return {
|
|
8661
8282
|
ok: true,
|
|
8662
8283
|
plan: assembled.plan,
|
|
@@ -8938,32 +8559,38 @@ function missingManifestFile(files, stagedPaths) {
|
|
|
8938
8559
|
}
|
|
8939
8560
|
//#endregion
|
|
8940
8561
|
//#region lib/types/authoring-flow.js
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8562
|
+
/** The target facts both authoring entries may refuse on before writing anything. */
|
|
8563
|
+
async function targetAdmission(target, targetCard, createMode, isBlocked) {
|
|
8564
|
+
if (target.includes("/") || target.includes(":")) return {
|
|
8944
8565
|
code: "target-invalid",
|
|
8945
|
-
message: "target must be a bare skill name: " +
|
|
8566
|
+
message: "target must be a bare skill name: " + target
|
|
8946
8567
|
};
|
|
8947
|
-
const targetCard = options.cards.find((card) => card.name === options.target);
|
|
8948
|
-
const requestedMode = options.mode ?? (targetCard === void 0 ? "create-umbrella" : "merge-into-existing");
|
|
8949
|
-
const mode = requestedMode === "merge-into-existing" && targetCard === void 0 ? "create-umbrella" : requestedMode;
|
|
8950
|
-
const createMode = mode === "create-umbrella";
|
|
8951
8568
|
if (!createMode && targetCard !== void 0) {
|
|
8952
8569
|
if (targetCard.managed !== true) return {
|
|
8953
|
-
ok: false,
|
|
8954
8570
|
code: "target-not-managed",
|
|
8955
|
-
message: "target is not family-managed: " +
|
|
8571
|
+
message: "target is not family-managed: " + target
|
|
8956
8572
|
};
|
|
8957
8573
|
if (targetCard.protectionUnknown === true || targetCard.protectedBy != null) return {
|
|
8958
|
-
ok: false,
|
|
8959
8574
|
code: "target-protected",
|
|
8960
|
-
message: "target is protected (" + (targetCard.protectedBy ?? "unknown") + "): " +
|
|
8575
|
+
message: "target is protected (" + (targetCard.protectedBy ?? "unknown") + "): " + target
|
|
8961
8576
|
};
|
|
8962
8577
|
}
|
|
8963
|
-
if (!createMode && await
|
|
8964
|
-
ok: false,
|
|
8578
|
+
if (!createMode && await isBlocked?.(target) === true) return {
|
|
8965
8579
|
code: "target-blocked",
|
|
8966
|
-
message: "target is blocked by curator: " +
|
|
8580
|
+
message: "target is blocked by curator: " + target
|
|
8581
|
+
};
|
|
8582
|
+
return null;
|
|
8583
|
+
}
|
|
8584
|
+
async function runAuthoring(options) {
|
|
8585
|
+
const targetCard = options.cards.find((card) => card.name === options.target);
|
|
8586
|
+
const requestedMode = options.mode ?? (targetCard === void 0 ? "create-umbrella" : "merge-into-existing");
|
|
8587
|
+
const mode = requestedMode === "merge-into-existing" && targetCard === void 0 ? "create-umbrella" : requestedMode;
|
|
8588
|
+
const createMode = mode === "create-umbrella";
|
|
8589
|
+
const targetRefusal = await targetAdmission(options.target, targetCard, createMode, options.isBlocked);
|
|
8590
|
+
if (targetRefusal !== null) return {
|
|
8591
|
+
ok: false,
|
|
8592
|
+
code: targetRefusal.code,
|
|
8593
|
+
message: targetRefusal.message
|
|
8967
8594
|
};
|
|
8968
8595
|
const stageKey = options.stageKey ?? options.runId;
|
|
8969
8596
|
if (options.reuseStaging === true) {
|
|
@@ -9285,46 +8912,15 @@ const STALE_CAUSES = new Set([
|
|
|
9285
8912
|
"target-gone"
|
|
9286
8913
|
]);
|
|
9287
8914
|
async function runNewChildAuthoring(options, row, decision, signal) {
|
|
9288
|
-
if (decision.target.includes("/") || decision.target.includes(":")) return {
|
|
9289
|
-
kind: "failed",
|
|
9290
|
-
failure: {
|
|
9291
|
-
stage: "author",
|
|
9292
|
-
code: "target-invalid",
|
|
9293
|
-
message: "target must be a bare skill name: " + decision.target,
|
|
9294
|
-
retryable: false,
|
|
9295
|
-
retryFrom: "none"
|
|
9296
|
-
}
|
|
9297
|
-
};
|
|
9298
8915
|
const targetCard = options.cards.find((card) => card.name === decision.target);
|
|
9299
8916
|
const createMode = decision.mode === "create-umbrella";
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
kind: "failed",
|
|
9303
|
-
failure: {
|
|
9304
|
-
stage: "author",
|
|
9305
|
-
code: "target-not-managed",
|
|
9306
|
-
message: "target is not family-managed: " + decision.target,
|
|
9307
|
-
retryable: false,
|
|
9308
|
-
retryFrom: "none"
|
|
9309
|
-
}
|
|
9310
|
-
};
|
|
9311
|
-
if (targetCard.protectionUnknown === true || targetCard.protectedBy != null) return {
|
|
9312
|
-
kind: "failed",
|
|
9313
|
-
failure: {
|
|
9314
|
-
stage: "author",
|
|
9315
|
-
code: "target-protected",
|
|
9316
|
-
message: "target is protected: " + decision.target,
|
|
9317
|
-
retryable: false,
|
|
9318
|
-
retryFrom: "none"
|
|
9319
|
-
}
|
|
9320
|
-
};
|
|
9321
|
-
}
|
|
9322
|
-
if (!createMode && await options.isBlocked?.(decision.target) === true) return {
|
|
8917
|
+
const targetRefusal = await targetAdmission(decision.target, targetCard, createMode, options.isBlocked);
|
|
8918
|
+
if (targetRefusal !== null) return {
|
|
9323
8919
|
kind: "failed",
|
|
9324
8920
|
failure: {
|
|
9325
8921
|
stage: "author",
|
|
9326
|
-
code:
|
|
9327
|
-
message:
|
|
8922
|
+
code: targetRefusal.code,
|
|
8923
|
+
message: targetRefusal.message,
|
|
9328
8924
|
retryable: false,
|
|
9329
8925
|
retryFrom: "none"
|
|
9330
8926
|
}
|
|
@@ -10787,143 +10383,6 @@ async function supersedeMergeRuns(registry) {
|
|
|
10787
10383
|
return ids.length;
|
|
10788
10384
|
}
|
|
10789
10385
|
//#endregion
|
|
10790
|
-
//#region lib/types/coordinator.js
|
|
10791
|
-
/**
|
|
10792
|
-
* Pure merge transition table. No io, no run shell, no subagents.
|
|
10793
|
-
*
|
|
10794
|
-
* Every transition returns the whole next row (or a drop), so the caller can persist it
|
|
10795
|
-
* through one state transaction. Invalid transitions are refused by name;
|
|
10796
|
-
* a caller never "repairs" a row by guessing which state the operator meant.
|
|
10797
|
-
*/
|
|
10798
|
-
function invalid(event, state) {
|
|
10799
|
-
return {
|
|
10800
|
-
ok: false,
|
|
10801
|
-
code: "invalid-transition",
|
|
10802
|
-
message: "cannot apply " + event.type + " to " + (state ?? "no row")
|
|
10803
|
-
};
|
|
10804
|
-
}
|
|
10805
|
-
/** Drop fields that must not survive a transition; optional fields stay absent. */
|
|
10806
|
-
function base(row, at) {
|
|
10807
|
-
return {
|
|
10808
|
-
id: row.id,
|
|
10809
|
-
pairKey: row.pairKey,
|
|
10810
|
-
a: row.a,
|
|
10811
|
-
b: row.b,
|
|
10812
|
-
state: row.state,
|
|
10813
|
-
queue: row.queue,
|
|
10814
|
-
createdAt: row.createdAt,
|
|
10815
|
-
updatedAt: at,
|
|
10816
|
-
...row.runId === void 0 ? {} : { runId: row.runId },
|
|
10817
|
-
...row.unitId === void 0 ? {} : { unitId: row.unitId },
|
|
10818
|
-
...row.origin === void 0 ? {} : { origin: row.origin },
|
|
10819
|
-
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
10820
|
-
...row.sourceRevision === void 0 ? {} : { sourceRevision: row.sourceRevision },
|
|
10821
|
-
...row.targetRevision === void 0 ? {} : { targetRevision: row.targetRevision },
|
|
10822
|
-
...row.attempts === void 0 ? {} : { attempts: row.attempts },
|
|
10823
|
-
...row.stagingRef === void 0 ? {} : { stagingRef: row.stagingRef },
|
|
10824
|
-
...row.stagingExpiresAt === void 0 ? {} : { stagingExpiresAt: row.stagingExpiresAt },
|
|
10825
|
-
...row.expired === void 0 ? {} : { expired: row.expired },
|
|
10826
|
-
...row.reason === void 0 ? {} : { reason: row.reason },
|
|
10827
|
-
...row.target === void 0 ? {} : { target: row.target }
|
|
10828
|
-
};
|
|
10829
|
-
}
|
|
10830
|
-
function next(row, at, state, patch = {}) {
|
|
10831
|
-
return {
|
|
10832
|
-
ok: true,
|
|
10833
|
-
row: {
|
|
10834
|
-
...base(row, at),
|
|
10835
|
-
state,
|
|
10836
|
-
queue: queueOf(state),
|
|
10837
|
-
...patch
|
|
10838
|
-
}
|
|
10839
|
-
};
|
|
10840
|
-
}
|
|
10841
|
-
/** Legacy v2 projection only; runtime writes must use transitionV3/RowTx. */
|
|
10842
|
-
function transitionLegacyForProjection(current, event) {
|
|
10843
|
-
if (event.type === "candidate") {
|
|
10844
|
-
if (current !== void 0) return {
|
|
10845
|
-
ok: true,
|
|
10846
|
-
row: current
|
|
10847
|
-
};
|
|
10848
|
-
const key = pairKey(event.a, event.b);
|
|
10849
|
-
return {
|
|
10850
|
-
ok: true,
|
|
10851
|
-
row: {
|
|
10852
|
-
id: rowIdOf(key),
|
|
10853
|
-
pairKey: key,
|
|
10854
|
-
a: event.a,
|
|
10855
|
-
b: event.b,
|
|
10856
|
-
state: "verify",
|
|
10857
|
-
queue: "verify",
|
|
10858
|
-
createdAt: event.at,
|
|
10859
|
-
updatedAt: event.at
|
|
10860
|
-
}
|
|
10861
|
-
};
|
|
10862
|
-
}
|
|
10863
|
-
if (event.type === "source-gone") return {
|
|
10864
|
-
ok: true,
|
|
10865
|
-
row: null
|
|
10866
|
-
};
|
|
10867
|
-
if (event.type === "gate-keep") {
|
|
10868
|
-
if (current !== void 0) return {
|
|
10869
|
-
ok: true,
|
|
10870
|
-
row: current
|
|
10871
|
-
};
|
|
10872
|
-
const key = pairKey(event.a, event.b);
|
|
10873
|
-
return {
|
|
10874
|
-
ok: true,
|
|
10875
|
-
row: {
|
|
10876
|
-
id: rowIdOf(key),
|
|
10877
|
-
pairKey: key,
|
|
10878
|
-
a: event.a,
|
|
10879
|
-
b: event.b,
|
|
10880
|
-
state: "skipped",
|
|
10881
|
-
queue: "skipped",
|
|
10882
|
-
origin: "agent",
|
|
10883
|
-
createdAt: event.at,
|
|
10884
|
-
updatedAt: event.at
|
|
10885
|
-
}
|
|
10886
|
-
};
|
|
10887
|
-
}
|
|
10888
|
-
if (current === void 0) return invalid(event, void 0);
|
|
10889
|
-
switch (event.type) {
|
|
10890
|
-
case "round1-keep": return current.state === "verify" ? next(current, event.at, "skipped", { origin: "agent" }) : invalid(event, current.state);
|
|
10891
|
-
case "round1-merge":
|
|
10892
|
-
if (current.state !== "verify") return invalid(event, current.state);
|
|
10893
|
-
return event.approval === "on" ? next(current, event.at, "pending", {
|
|
10894
|
-
...event.decision === void 0 ? {} : { decision: event.decision },
|
|
10895
|
-
...event.stagingRef === void 0 ? {} : { stagingRef: event.stagingRef },
|
|
10896
|
-
...event.stagingExpiresAt === void 0 ? {} : { stagingExpiresAt: event.stagingExpiresAt },
|
|
10897
|
-
...event.sourceRevision === void 0 ? {} : { sourceRevision: event.sourceRevision },
|
|
10898
|
-
...event.targetRevision === void 0 ? {} : { targetRevision: event.targetRevision }
|
|
10899
|
-
}) : next(current, event.at, "approved", {
|
|
10900
|
-
...event.decision === void 0 ? {} : { decision: event.decision },
|
|
10901
|
-
...event.stagingRef === void 0 ? {} : { stagingRef: event.stagingRef },
|
|
10902
|
-
...event.stagingExpiresAt === void 0 ? {} : { stagingExpiresAt: event.stagingExpiresAt },
|
|
10903
|
-
...event.sourceRevision === void 0 ? {} : { sourceRevision: event.sourceRevision },
|
|
10904
|
-
...event.targetRevision === void 0 ? {} : { targetRevision: event.targetRevision }
|
|
10905
|
-
});
|
|
10906
|
-
case "round1-fail": return current.state === "verify" ? next(current, event.at, "verify", {
|
|
10907
|
-
failure: event.failure,
|
|
10908
|
-
attempts: (current.attempts ?? 0) + 1
|
|
10909
|
-
}) : invalid(event, current.state);
|
|
10910
|
-
case "approve": return current.state === "pending" ? next(current, event.at, "approved", event.runId === void 0 ? {} : { runId: event.runId }) : invalid(event, current.state);
|
|
10911
|
-
case "reject": return current.state === "pending" ? next(current, event.at, "skipped", { origin: "human" }) : invalid(event, current.state);
|
|
10912
|
-
case "round2-cancel": return current.state === "fuse" || current.state === "approved" ? next(current, event.at, "approved") : invalid(event, current.state);
|
|
10913
|
-
case "round2-fail": return current.state === "fuse" || current.state === "approved" ? next(current, event.at, "approved", {
|
|
10914
|
-
failure: event.failure,
|
|
10915
|
-
attempts: (current.attempts ?? 0) + 1
|
|
10916
|
-
}) : invalid(event, current.state);
|
|
10917
|
-
case "round2-success": return current.state === "fuse" || current.state === "approved" ? {
|
|
10918
|
-
ok: true,
|
|
10919
|
-
row: null
|
|
10920
|
-
} : invalid(event, current.state);
|
|
10921
|
-
case "target-taken":
|
|
10922
|
-
case "source-changed": return current.state === "pending" || current.state === "approved" || current.state === "fuse" ? next(current, event.at, current.state, { expired: event.type }) : invalid(event, current.state);
|
|
10923
|
-
case "restore": return current.state === "skipped" ? next(current, event.at, "verify") : invalid(event, current.state);
|
|
10924
|
-
}
|
|
10925
|
-
}
|
|
10926
|
-
//#endregion
|
|
10927
10386
|
//#region lib/types/pair-command.js
|
|
10928
10387
|
async function runPairCommand(options) {
|
|
10929
10388
|
return await runScanCommand({
|
|
@@ -11044,37 +10503,27 @@ function apply(ctx, rawConfig = {}) {
|
|
|
11044
10503
|
if (registry === void 0) return;
|
|
11045
10504
|
const io = evolutionIoAdapter(() => registry.provider());
|
|
11046
10505
|
const home = evolutionHome();
|
|
10506
|
+
const homeFence = ctx.get("evolutionMutationAuthority")?.homeFence;
|
|
11047
10507
|
const ready = (async () => {
|
|
11048
|
-
const
|
|
11049
|
-
if (
|
|
10508
|
+
const migration = await migrateMergeStateToV4(io, home, homeFence);
|
|
10509
|
+
if (!migration.ok) return migration;
|
|
10510
|
+
const loaded = await loadMergeState(io, home);
|
|
10511
|
+
return {
|
|
11050
10512
|
ok: true,
|
|
11051
10513
|
schema: 3,
|
|
11052
|
-
migrated:
|
|
10514
|
+
migrated: migration.migrated,
|
|
11053
10515
|
state: {
|
|
11054
10516
|
schemaVersion: 3,
|
|
11055
|
-
rows:
|
|
10517
|
+
rows: loaded.state.rows.map(legacyToV3).filter((row) => row !== null)
|
|
11056
10518
|
},
|
|
11057
10519
|
quarantine: [],
|
|
11058
10520
|
report: null
|
|
11059
10521
|
};
|
|
11060
|
-
const state = await ensureMergeStateReady(io, home, ctx.get("evolutionMutationAuthority")?.homeFence);
|
|
11061
|
-
if (!state.ok) return v4.code === "merge-state-forward-only" || v4.code === "merge-state-needs-repair" ? {
|
|
11062
|
-
ok: false,
|
|
11063
|
-
code: v4.code,
|
|
11064
|
-
message: v4.message
|
|
11065
|
-
} : state;
|
|
11066
|
-
const normalized = await normalizePairLedger(io, home);
|
|
11067
|
-
return normalized.ok ? state : {
|
|
11068
|
-
ok: false,
|
|
11069
|
-
code: "merge-state-needs-repair",
|
|
11070
|
-
message: normalized.message ?? "v4 normalize failed"
|
|
11071
|
-
};
|
|
11072
10522
|
})().catch((error) => ({
|
|
11073
10523
|
ok: false,
|
|
11074
10524
|
code: "merge-state-migration-blocked",
|
|
11075
10525
|
message: error instanceof Error ? error.message : String(error)
|
|
11076
10526
|
}));
|
|
11077
|
-
const homeFence = ctx.get("evolutionMutationAuthority")?.homeFence;
|
|
11078
10527
|
const runs = newRunRegistry({
|
|
11079
10528
|
io,
|
|
11080
10529
|
home,
|
|
@@ -11386,4 +10835,4 @@ function apply(ctx, rawConfig = {}) {
|
|
|
11386
10835
|
});
|
|
11387
10836
|
}
|
|
11388
10837
|
//#endregion
|
|
11389
|
-
export { Config,
|
|
10838
|
+
export { Config, MERGE_LIFECYCLE_VERBS, MERGE_MAX_ATTEMPTS, MERGE_PAIR_STATES, MERGE_PARAM_DEFAULTS, MERGE_PROMPT_VERSION, MERGE_ROUTES, MERGE_STAGE_TOOL_NAME, MERGE_STATE_VERSION, STAGING_SCHEMA_VERSION, STANDBY_PROMPT, StageToolRegistry, apply, applyStageTool, approvePending, assembleStagedCommit, buildCommitPlan, buildRound1Prompt, buildRound2Prompt, busyTarget, canRetry, cleanupExpiredStaging, commitReceiptPath, commitStagedRun, committedPairs, configureMergePresentation, copyStagedFile, countRows, createChannelScanPorts, createEngineRunner, createEngineScreening, createMergeCommand, createMergeRuntime, createMergeStageTool, createNewChildRound2, createReadOnlyMergeService, createStageForCards, createStaging, durableReceiptDir, durableReceiptPath, emptyMergeState, engineConfigFromRoot, forcedDecisionFor, formatFacts, formatQueue, formatRunDetail, formatRunsView, formatSnapshotQueue, formatSnapshotStatus, formatStatus, handleReadOnlyMergeCommand, hasBlockingFinding, inject, listStagedFiles, liveRowFactsOf, loadMergeState, makeMergeRoutes, manifestFilesOf, mergeDoctor, mergeDoctorAvailability, mergeDoctorFact, mergeReportDigestPath, mergeReportPath, mergeRunIdsToSupersede, mergeStatePath, mergeUsage, missingManifestFile, pairKey, parseAuthoringManifest, parseDecision, parseMergeCommand, pendingExpiry, persistedExpiry, promptHashOf, queueOf, readCommitReceipt, readCommitReceipts, readMergeReport, readStagedFile, readStagedTree, readStaging, recordRound2Outcome, rejectPending, resolveMergeParams, resolveTargetName, retryMergeReport, rewriteFrontmatterName, rowIdOf, rowsOf, runApproveCommand, runAuthoring, runMergeCommand, runNewChildAuthoring, runPairCommand, runResumeCommand, runRound1OverChannel, runScan, runScanCommand, setStagingTarget, skillLibraryPort, skillLibraryUndoPort, skippedPairKeys, snapshotCards, sourceBlock, stageKeyOf, stageOf, stagingRoot, subagentChannelAdapter, supersedeMergeRuns, targetAdmission, undoMergeReceipt, unitFor, validateStagedTree, withMergeRun, writeCommitReceipt, writeMergeReport, writeStagedFile };
|