@lmzhen/dsh-evolution-merge 0.30.24 → 0.30.27
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 +549 -183
- package/lib/types/approve-command.js +2 -1
- package/lib/types/authoring-flow.d.ts +5 -0
- package/lib/types/authoring-flow.js +14 -6
- package/lib/types/commands.d.ts +7 -0
- package/lib/types/commands.js +8 -1
- package/lib/types/commit-execute.d.ts +4 -0
- package/lib/types/commit-execute.js +8 -5
- package/lib/types/commit-receipt.js +3 -1
- package/lib/types/contracts.d.ts +20 -1
- package/lib/types/doctor.js +1 -1
- package/lib/types/domain/errors.js +5 -1
- package/lib/types/domain/merge-snapshot.d.ts +12 -3
- package/lib/types/domain/merge-snapshot.js +9 -4
- package/lib/types/domain/reconcile.js +1 -1
- package/lib/types/domain/row-v4.d.ts +4 -0
- package/lib/types/domain/row-v4.js +9 -9
- package/lib/types/domain/run-coordinator.d.ts +9 -0
- package/lib/types/domain/stage-aggregate.d.ts +6 -1
- package/lib/types/domain/stage-aggregate.js +16 -9
- package/lib/types/domain/validation-plan.js +5 -1
- package/lib/types/host-capabilities.d.ts +22 -0
- package/lib/types/host-capabilities.js +24 -0
- package/lib/types/index.js +12 -2
- package/lib/types/kernel/adapter-v4.d.ts +12 -0
- package/lib/types/kernel/adapter-v4.js +54 -0
- package/lib/types/kernel/failure.d.ts +8 -0
- package/lib/types/kernel/failure.js +33 -0
- package/lib/types/kernel/index.d.ts +9 -0
- package/lib/types/kernel/index.js +9 -0
- package/lib/types/kernel/invariants.d.ts +4 -0
- package/lib/types/kernel/invariants.js +47 -0
- package/lib/types/kernel/ledger.d.ts +46 -0
- package/lib/types/kernel/ledger.js +62 -0
- package/lib/types/kernel/machine.d.ts +13 -0
- package/lib/types/kernel/machine.js +108 -0
- package/lib/types/kernel/model.d.ts +154 -0
- package/lib/types/kernel/model.js +5 -0
- package/lib/types/kernel/projection.d.ts +28 -0
- package/lib/types/kernel/projection.js +44 -0
- package/lib/types/kernel/recovery.d.ts +31 -0
- package/lib/types/kernel/recovery.js +19 -0
- package/lib/types/kernel/unit.d.ts +43 -0
- package/lib/types/kernel/unit.js +63 -0
- package/lib/types/merge-routes.js +9 -0
- package/lib/types/new-child-authoring.d.ts +1 -0
- package/lib/types/new-child-authoring.js +15 -7
- package/lib/types/pending-actions.d.ts +1 -1
- package/lib/types/pending-actions.js +4 -0
- package/lib/types/recovery-coordinator.d.ts +2 -0
- package/lib/types/recovery-coordinator.js +14 -4
- package/lib/types/recovery-projection.d.ts +4 -2
- package/lib/types/recovery-projection.js +3 -3
- package/lib/types/resume-command.js +2 -1
- package/lib/types/round2-outcome.js +26 -9
- package/lib/types/run-shell.d.ts +1 -0
- package/lib/types/run-shell.js +57 -15
- package/lib/types/runtime.d.ts +6 -1
- package/lib/types/runtime.js +46 -22
- package/lib/types/scan-command.d.ts +1 -0
- package/lib/types/scan-command.js +1 -0
- package/lib/types/scan-ports.js +3 -0
- package/lib/types/scan-run.d.ts +6 -1
- package/lib/types/scan-run.js +34 -18
- package/lib/types/surface/merge-view.js +12 -4
- package/lib/types/undo-operation.d.ts +2 -1
- package/lib/types/undo-operation.js +10 -4
- package/lib/types/wire-projection.js +7 -2
- package/lib/types/wire.d.ts +1 -0
- package/package.json +5 -5
- package/lib/types/adapters/model.d.ts +0 -31
- package/lib/types/adapters/model.js +0 -47
- package/lib/types/gate-cache.d.ts +0 -12
- package/lib/types/gate-cache.js +0 -36
- package/lib/types/gate-filter.d.ts +0 -31
- package/lib/types/gate-filter.js +0 -64
- package/lib/types/ladder.d.ts +0 -65
- package/lib/types/ladder.js +0 -162
- package/lib/types/propose.d.ts +0 -26
- package/lib/types/propose.js +0 -69
- package/lib/types/screening-port.d.ts +0 -47
- package/lib/types/screening-port.js +0 -93
- package/lib/types/screening.d.ts +0 -30
- package/lib/types/screening.js +0 -75
package/lib/index.js
CHANGED
|
@@ -596,7 +596,11 @@ function transitionV4(row, event, expectedVersion, at = (/* @__PURE__ */ new Dat
|
|
|
596
596
|
};
|
|
597
597
|
if (event.type === "staged") return {
|
|
598
598
|
ok: true,
|
|
599
|
-
row: advance(row, at, {
|
|
599
|
+
row: advance(row, at, {
|
|
600
|
+
state: "fuse",
|
|
601
|
+
...event.stagingRef === void 0 ? {} : { stagingRef: event.stagingRef },
|
|
602
|
+
...event.stagingExpiresAt === void 0 ? {} : { stagingExpiresAt: event.stagingExpiresAt }
|
|
603
|
+
}),
|
|
600
604
|
version: expectedVersion + 1
|
|
601
605
|
};
|
|
602
606
|
if (event.type === "skipped") return {
|
|
@@ -631,7 +635,11 @@ function transitionV4(row, event, expectedVersion, at = (/* @__PURE__ */ new Dat
|
|
|
631
635
|
case "approved":
|
|
632
636
|
if (event.type === "staged") return {
|
|
633
637
|
ok: true,
|
|
634
|
-
row: advance(row, at, {
|
|
638
|
+
row: advance(row, at, {
|
|
639
|
+
state: "fuse",
|
|
640
|
+
...event.stagingRef === void 0 ? {} : { stagingRef: event.stagingRef },
|
|
641
|
+
...event.stagingExpiresAt === void 0 ? {} : { stagingExpiresAt: event.stagingExpiresAt }
|
|
642
|
+
}),
|
|
635
643
|
version: expectedVersion + 1
|
|
636
644
|
};
|
|
637
645
|
if (event.type === "committed") return {
|
|
@@ -648,7 +656,7 @@ function transitionV4(row, event, expectedVersion, at = (/* @__PURE__ */ new Dat
|
|
|
648
656
|
}),
|
|
649
657
|
version: expectedVersion + 1
|
|
650
658
|
};
|
|
651
|
-
if (event.type === "failed") return
|
|
659
|
+
if (event.type === "failed") return {
|
|
652
660
|
ok: true,
|
|
653
661
|
row: advance(row, at, {
|
|
654
662
|
state: "fuse",
|
|
@@ -656,15 +664,6 @@ function transitionV4(row, event, expectedVersion, at = (/* @__PURE__ */ new Dat
|
|
|
656
664
|
attempts: row.attempts + 1
|
|
657
665
|
}),
|
|
658
666
|
version: expectedVersion + 1
|
|
659
|
-
} : {
|
|
660
|
-
ok: true,
|
|
661
|
-
row: advance(row, at, {
|
|
662
|
-
state: "skipped",
|
|
663
|
-
reason: event.failure.code,
|
|
664
|
-
failure: event.failure,
|
|
665
|
-
attempts: row.attempts + 1
|
|
666
|
-
}),
|
|
667
|
-
version: expectedVersion + 1
|
|
668
667
|
};
|
|
669
668
|
if (event.type === "superseded") return {
|
|
670
669
|
ok: true,
|
|
@@ -687,7 +686,7 @@ function transitionV4(row, event, expectedVersion, at = (/* @__PURE__ */ new Dat
|
|
|
687
686
|
}),
|
|
688
687
|
version: expectedVersion + 1
|
|
689
688
|
};
|
|
690
|
-
if (event.type === "failed") return
|
|
689
|
+
if (event.type === "failed") return {
|
|
691
690
|
ok: true,
|
|
692
691
|
row: advance(row, at, {
|
|
693
692
|
state: "fuse",
|
|
@@ -695,15 +694,6 @@ function transitionV4(row, event, expectedVersion, at = (/* @__PURE__ */ new Dat
|
|
|
695
694
|
attempts: row.attempts + 1
|
|
696
695
|
}),
|
|
697
696
|
version: expectedVersion + 1
|
|
698
|
-
} : {
|
|
699
|
-
ok: true,
|
|
700
|
-
row: advance(row, at, {
|
|
701
|
-
state: "skipped",
|
|
702
|
-
reason: event.failure.code,
|
|
703
|
-
failure: event.failure,
|
|
704
|
-
attempts: row.attempts + 1
|
|
705
|
-
}),
|
|
706
|
-
version: expectedVersion + 1
|
|
707
697
|
};
|
|
708
698
|
if (event.type === "superseded") return {
|
|
709
699
|
ok: true,
|
|
@@ -794,7 +784,14 @@ function v3ToV4(row) {
|
|
|
794
784
|
target: row.target,
|
|
795
785
|
decision: row.decision,
|
|
796
786
|
...row.sourceRevision === void 0 ? {} : { sourceRevision: row.sourceRevision },
|
|
797
|
-
...row.runId === void 0 ? {} : { runId: row.runId }
|
|
787
|
+
...row.runId === void 0 ? {} : { runId: row.runId },
|
|
788
|
+
...row.staging?.kind === "present" ? {
|
|
789
|
+
stagingRef: row.staging.ref,
|
|
790
|
+
stagingExpiresAt: row.staging.expiresAt
|
|
791
|
+
} : row.staging?.kind === "expired" && row.staging.ref !== void 0 ? {
|
|
792
|
+
stagingRef: row.staging.ref,
|
|
793
|
+
stagingExpiresAt: row.staging.expiresAt
|
|
794
|
+
} : {}
|
|
798
795
|
};
|
|
799
796
|
return {
|
|
800
797
|
...base,
|
|
@@ -870,6 +867,8 @@ function v4ToLegacy(row) {
|
|
|
870
867
|
...(row.state === "pending" || row.state === "approved" || row.state === "fuse") && row.sourceRevision !== void 0 ? { sourceRevision: row.sourceRevision } : {},
|
|
871
868
|
...(row.state === "pending" || row.state === "approved" || row.state === "fuse") && row.targetRevision !== void 0 ? { targetRevision: row.targetRevision } : {},
|
|
872
869
|
...row.runId === void 0 ? {} : { runId: row.runId },
|
|
870
|
+
...row.state === "fuse" && row.stagingRef !== void 0 ? { stagingRef: row.stagingRef } : {},
|
|
871
|
+
...row.state === "fuse" && row.stagingExpiresAt !== void 0 ? { stagingExpiresAt: row.stagingExpiresAt } : {},
|
|
873
872
|
...row.origin === void 0 ? {} : { origin: row.origin },
|
|
874
873
|
...row.attempts === void 0 ? {} : { attempts: row.attempts },
|
|
875
874
|
createdAt: row.createdAt,
|
|
@@ -2311,9 +2310,13 @@ async function presentStage(io, home, entry) {
|
|
|
2311
2310
|
state: "present"
|
|
2312
2311
|
});
|
|
2313
2312
|
}
|
|
2314
|
-
async function dropStage(io, home, rowId, ref) {
|
|
2315
|
-
|
|
2316
|
-
|
|
2313
|
+
async function dropStage(io, home, rowId, ref, onError) {
|
|
2314
|
+
const index = join(home, "merge", "staging-index", encodeURIComponent(rowId) + ".json");
|
|
2315
|
+
await io.remove(index).catch((error) => onError?.(index, error));
|
|
2316
|
+
if (ref !== void 0 && ref !== "") {
|
|
2317
|
+
const tree = join(home, "merge", "runs", ref);
|
|
2318
|
+
await io.remove(tree).catch((error) => onError?.(tree, error));
|
|
2319
|
+
}
|
|
2317
2320
|
}
|
|
2318
2321
|
/** Bounded cleanup: reserved/present TTL, pending/verify stale index removal, terminal/absent rows, orphan trees. */
|
|
2319
2322
|
async function cleanupStage(io, home, now, rows, limit = 100) {
|
|
@@ -2321,6 +2324,13 @@ async function cleanupStage(io, home, now, rows, limit = 100) {
|
|
|
2321
2324
|
const expired = [];
|
|
2322
2325
|
const kept = [];
|
|
2323
2326
|
const orphans = [];
|
|
2327
|
+
const failures = [];
|
|
2328
|
+
const fail = (path, error) => {
|
|
2329
|
+
failures.push({
|
|
2330
|
+
path,
|
|
2331
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2332
|
+
});
|
|
2333
|
+
};
|
|
2324
2334
|
const refs = /* @__PURE__ */ new Set();
|
|
2325
2335
|
const indexRoot = join(home, "merge", "staging-index");
|
|
2326
2336
|
const names = await io.exists(indexRoot) ? await listOrEmpty(io, indexRoot) : [];
|
|
@@ -2338,7 +2348,7 @@ async function cleanupStage(io, home, now, rows, limit = 100) {
|
|
|
2338
2348
|
if (entry.ref !== "") refs.add(entry.ref);
|
|
2339
2349
|
const row = rows.get(entry.rowId);
|
|
2340
2350
|
if (row === void 0 || row.state === "skipped" || row?.state === "pending" || row?.state === "verify") {
|
|
2341
|
-
await dropStage(io, home, entry.rowId, entry.ref);
|
|
2351
|
+
await dropStage(io, home, entry.rowId, entry.ref, fail);
|
|
2342
2352
|
removed.push(entry.rowId);
|
|
2343
2353
|
continue;
|
|
2344
2354
|
}
|
|
@@ -2353,11 +2363,13 @@ async function cleanupStage(io, home, now, rows, limit = 100) {
|
|
|
2353
2363
|
createdAt: typeof entry.createdAt === "number" ? entry.createdAt : now,
|
|
2354
2364
|
state: "expired"
|
|
2355
2365
|
});
|
|
2356
|
-
} catch {
|
|
2366
|
+
} catch (error) {
|
|
2367
|
+
fail(join(indexRoot, name), error);
|
|
2368
|
+
}
|
|
2357
2369
|
expired.push(entry.rowId);
|
|
2358
2370
|
continue;
|
|
2359
2371
|
}
|
|
2360
|
-
await dropStage(io, home, entry.rowId, entry.ref);
|
|
2372
|
+
await dropStage(io, home, entry.rowId, entry.ref, fail);
|
|
2361
2373
|
removed.push(entry.rowId);
|
|
2362
2374
|
continue;
|
|
2363
2375
|
}
|
|
@@ -2368,14 +2380,15 @@ async function cleanupStage(io, home, now, rows, limit = 100) {
|
|
|
2368
2380
|
for (const run of runNames.slice(0, limit)) {
|
|
2369
2381
|
const prefix = run + "/";
|
|
2370
2382
|
if ([...refs].some((ref) => ref === run || ref.startsWith(prefix))) continue;
|
|
2371
|
-
await io.remove(join(runRoot, run)).catch(() =>
|
|
2383
|
+
await io.remove(join(runRoot, run)).catch((error) => fail(join(runRoot, run), error));
|
|
2372
2384
|
orphans.push(run);
|
|
2373
2385
|
}
|
|
2374
2386
|
return {
|
|
2375
2387
|
removed,
|
|
2376
2388
|
expired,
|
|
2377
2389
|
kept,
|
|
2378
|
-
orphans
|
|
2390
|
+
orphans,
|
|
2391
|
+
failures
|
|
2379
2392
|
};
|
|
2380
2393
|
}
|
|
2381
2394
|
/** Read all production v2 stage entries. */
|
|
@@ -2685,7 +2698,8 @@ async function writeCommitReceipt(io, home, receipt) {
|
|
|
2685
2698
|
const path = commitReceiptPath(home, receipt.runId);
|
|
2686
2699
|
await io.writeText(path, JSON.stringify(receipt, null, 2) + "\n");
|
|
2687
2700
|
const parent = receipt.parentRunId ?? receipt.runId.split("/pairs/")[0] ?? receipt.runId;
|
|
2688
|
-
|
|
2701
|
+
const leaf = (receipt.runId + "-" + receipt.unitId).replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
2702
|
+
await io.writeText(durableReceiptPath(home, parent, leaf), JSON.stringify({
|
|
2689
2703
|
...receipt,
|
|
2690
2704
|
parentRunId: parent
|
|
2691
2705
|
}, null, 2) + "\n");
|
|
@@ -2863,6 +2877,10 @@ const emptyCounts = {
|
|
|
2863
2877
|
const queueOf$3 = (state) => state === "approved" || state === "fuse" ? "fuse" : state === "verify" ? "verify" : state === "pending" ? "pending" : "skipped";
|
|
2864
2878
|
async function readMergeSnapshot(options) {
|
|
2865
2879
|
const readAt = (options.now ?? (() => Date.now()))();
|
|
2880
|
+
const hostOnly = options.hostOnly === void 0 ? void 0 : await options.hostOnly().catch((error) => ({
|
|
2881
|
+
code: "E-323",
|
|
2882
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
2883
|
+
}));
|
|
2866
2884
|
const readiness = await readV4Readiness(options.io, options.home);
|
|
2867
2885
|
if (!readiness.ok) return {
|
|
2868
2886
|
wireVersion: 1,
|
|
@@ -2870,6 +2888,7 @@ async function readMergeSnapshot(options) {
|
|
|
2870
2888
|
readAt,
|
|
2871
2889
|
queueRevision: "blocked",
|
|
2872
2890
|
consistency: "eventual",
|
|
2891
|
+
...hostOnly === void 0 ? {} : { hostOnly },
|
|
2873
2892
|
blocked: {
|
|
2874
2893
|
code: readiness.code,
|
|
2875
2894
|
message: readiness.message,
|
|
@@ -2908,6 +2927,7 @@ async function readMergeSnapshot(options) {
|
|
|
2908
2927
|
for (const row of ledger?.rows ?? []) {
|
|
2909
2928
|
const stage = stages.find((item) => item.rowId === row.id) ?? null;
|
|
2910
2929
|
const queue = queueOf$3(row.state);
|
|
2930
|
+
const bSide = live !== void 0 && live.managed(row.b) ? "live" : "archive";
|
|
2911
2931
|
const bodies = [{
|
|
2912
2932
|
rowId: row.id,
|
|
2913
2933
|
name: row.a,
|
|
@@ -2915,9 +2935,9 @@ async function readMergeSnapshot(options) {
|
|
|
2915
2935
|
}, {
|
|
2916
2936
|
rowId: row.id,
|
|
2917
2937
|
name: row.b,
|
|
2918
|
-
side:
|
|
2938
|
+
side: bSide
|
|
2919
2939
|
}];
|
|
2920
|
-
const expiredKind = persistedExpiry(row) ?? pendingExpiry(row, live);
|
|
2940
|
+
const expiredKind = row.state === "skipped" && row.expiredAt !== void 0 ? "skipped-ttl" : persistedExpiry(row) ?? pendingExpiry(row, live);
|
|
2921
2941
|
rows.push({
|
|
2922
2942
|
row,
|
|
2923
2943
|
stage,
|
|
@@ -3020,6 +3040,7 @@ async function readMergeSnapshot(options) {
|
|
|
3020
3040
|
readAt,
|
|
3021
3041
|
queueRevision,
|
|
3022
3042
|
consistency: "eventual",
|
|
3043
|
+
...hostOnly === void 0 ? {} : { hostOnly },
|
|
3023
3044
|
rows,
|
|
3024
3045
|
stages,
|
|
3025
3046
|
runs,
|
|
@@ -3345,7 +3366,13 @@ async function reconcileMergeState(options) {
|
|
|
3345
3366
|
if (current === void 0 || current.state !== "approved") continue;
|
|
3346
3367
|
const entry = byRow.get(row.id);
|
|
3347
3368
|
if (usable.get(row.id) === true) {
|
|
3348
|
-
if (tx.transition(row.id, {
|
|
3369
|
+
if (tx.transition(row.id, {
|
|
3370
|
+
type: "staged",
|
|
3371
|
+
...entry === void 0 ? {} : {
|
|
3372
|
+
stagingRef: entry.ref,
|
|
3373
|
+
stagingExpiresAt: entry.expiresAt
|
|
3374
|
+
}
|
|
3375
|
+
}, current.version).ok) {
|
|
3349
3376
|
actions.push({
|
|
3350
3377
|
kind: "approved-fused",
|
|
3351
3378
|
rowId: row.id,
|
|
@@ -3577,12 +3604,16 @@ function actions(row) {
|
|
|
3577
3604
|
renderAction("reject", context, true, true),
|
|
3578
3605
|
renderAction("discard", context, true, true)
|
|
3579
3606
|
];
|
|
3580
|
-
if (row.row.state === "approved" || row.row.state === "fuse")
|
|
3581
|
-
renderAction("
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3607
|
+
if (row.row.state === "approved" || row.row.state === "fuse") {
|
|
3608
|
+
if (row.row.failure !== void 0 && row.row.failure.retryable === false) return [renderAction("pair", context, true, true), renderAction("discard", context, true, true)];
|
|
3609
|
+
return [
|
|
3610
|
+
renderAction("retry-resume", context, true, true),
|
|
3611
|
+
renderAction("discard", context, true, true),
|
|
3612
|
+
renderAction("status", context)
|
|
3613
|
+
];
|
|
3614
|
+
}
|
|
3585
3615
|
if (row.row.state === "skipped") return [renderAction("restore", context, true, true)];
|
|
3616
|
+
if (row.row.state === "verify") return [renderAction("retry-candidates", context)];
|
|
3586
3617
|
return [renderAction("pair", context)];
|
|
3587
3618
|
}
|
|
3588
3619
|
function verifyState(row) {
|
|
@@ -3627,7 +3658,10 @@ function toRowFact$1(row) {
|
|
|
3627
3658
|
b: state.b,
|
|
3628
3659
|
...target === void 0 ? {} : { target },
|
|
3629
3660
|
...summary === void 0 ? {} : { summary },
|
|
3630
|
-
...state.state === "skipped" ? {
|
|
3661
|
+
...state.state === "skipped" ? {
|
|
3662
|
+
reason: state.reason,
|
|
3663
|
+
reasonCode: state.reason
|
|
3664
|
+
} : {},
|
|
3631
3665
|
...state.origin === void 0 ? {} : { origin: state.origin },
|
|
3632
3666
|
...state.failure === void 0 ? {} : { failure: {
|
|
3633
3667
|
stage: state.failure.stage,
|
|
@@ -3648,7 +3682,7 @@ function toRowFact$1(row) {
|
|
|
3648
3682
|
}
|
|
3649
3683
|
function runView(run, readAt, includeResultRef = false) {
|
|
3650
3684
|
const record = run.record;
|
|
3651
|
-
const state = run.meta?.phase === "undone" ? "undone" : run.meta?.approval === "pending" ? "awaiting-approval" : record.state;
|
|
3685
|
+
const state = run.meta?.phase === "undone" ? "undone" : run.meta?.stopReason === "interrupted" ? "interrupted" : run.meta?.approval === "pending" ? "awaiting-approval" : record.state;
|
|
3652
3686
|
const stopReason = run.meta?.stopReason ?? (record.state === "cancelled" ? "cancelled" : record.state === "failed" ? "failed" : record.state === "succeeded" ? "completed" : void 0);
|
|
3653
3687
|
return {
|
|
3654
3688
|
wireVersion: 1,
|
|
@@ -3807,6 +3841,7 @@ function buildDoctorView(snapshot) {
|
|
|
3807
3841
|
...snapshot.runs[0] === void 0 ? {} : { latestRun: runView(snapshot.runs[0], snapshot.readAt) },
|
|
3808
3842
|
staging: snapshot.doctor.staging,
|
|
3809
3843
|
approval: snapshot.approval,
|
|
3844
|
+
...snapshot.hostOnly === void 0 ? {} : { hostOnly: snapshot.hostOnly },
|
|
3810
3845
|
...snapshot.blocked === void 0 ? {} : { blocked: {
|
|
3811
3846
|
code: snapshot.blocked.code,
|
|
3812
3847
|
message: snapshot.blocked.message
|
|
@@ -4124,7 +4159,7 @@ async function mergeDoctor(io, home, configuredRoot) {
|
|
|
4124
4159
|
key: "state",
|
|
4125
4160
|
label: "合并状态",
|
|
4126
4161
|
status: snapshot.blocked === void 0 || !hasState ? "ok" : "warn",
|
|
4127
|
-
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) : snapshot.blocked.code + ": " + snapshot.blocked.message
|
|
4162
|
+
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
|
|
4128
4163
|
});
|
|
4129
4164
|
const recoveryRuns = /* @__PURE__ */ new Set();
|
|
4130
4165
|
try {
|
|
@@ -4323,7 +4358,7 @@ async function mergeDoctor(io, home, configuredRoot) {
|
|
|
4323
4358
|
//#endregion
|
|
4324
4359
|
//#region lib/types/domain/errors.js
|
|
4325
4360
|
function redact(text) {
|
|
4326
|
-
return text.replace(/sk-[A-Za-z0-9_-]{16,}/g, "[redacted]").replace(/Bearer\s+\S+/gi, "Bearer [redacted]").replace(/(api[_-]?key\s*[:=]\s*)\S+/gi, "$1[redacted]");
|
|
4361
|
+
return text.replace(/sk-[A-Za-z0-9_-]{16,}/g, "[redacted]").replace(/ghp_[A-Za-z0-9]{20,}/g, "[redacted]").replace(/github_pat_[A-Za-z0-9_]{20,}/g, "[redacted]").replace(/xox[baprs]-[A-Za-z0-9-]{10,}/g, "[redacted]").replace(/AKIA[0-9A-Z]{16}/g, "[redacted]").replace(/Bearer\s+\S+/gi, "Bearer [redacted]").replace(/((?:api[_-]?key|token|password|secret)\s*[:=]\s*)\S+/gi, "$1[redacted]");
|
|
4327
4362
|
}
|
|
4328
4363
|
/** Build the typed failure every command/CLI/runtime branch returns. */
|
|
4329
4364
|
function commandError(stage, code, hint, next, runId) {
|
|
@@ -4452,7 +4487,7 @@ async function projectRecoveredUnit(options) {
|
|
|
4452
4487
|
command: "approve",
|
|
4453
4488
|
startedAt: at,
|
|
4454
4489
|
endedAt: at,
|
|
4455
|
-
stopReason: "completed",
|
|
4490
|
+
stopReason: options.interrupted === true ? "interrupted" : "completed",
|
|
4456
4491
|
approval: {
|
|
4457
4492
|
platformWrite: false,
|
|
4458
4493
|
mergeApproval: true
|
|
@@ -4526,17 +4561,21 @@ async function projectRecoveredUnit(options) {
|
|
|
4526
4561
|
await writeRunMeta(options.io, options.home, {
|
|
4527
4562
|
version: 1,
|
|
4528
4563
|
runId: options.runId,
|
|
4564
|
+
...meta?.pid === void 0 ? {} : { pid: meta.pid },
|
|
4529
4565
|
phase: "settled",
|
|
4566
|
+
...options.interrupted === true ? { stopReason: "interrupted" } : {},
|
|
4530
4567
|
reportRef: reportPath,
|
|
4531
4568
|
pairCount: 1,
|
|
4532
4569
|
...meta?.parentRunId === void 0 ? {} : { parentRunId: meta.parentRunId },
|
|
4570
|
+
...meta?.stagingRef === void 0 ? {} : { stagingRef: meta.stagingRef },
|
|
4571
|
+
...meta?.pairs === void 0 ? {} : { pairs: meta.pairs },
|
|
4533
4572
|
createdAt: meta?.createdAt ?? new Date(at).toISOString(),
|
|
4534
4573
|
updatedAt: new Date(at).toISOString()
|
|
4535
4574
|
});
|
|
4536
4575
|
await options.onPhase?.("settle");
|
|
4537
4576
|
if (options.runs !== void 0) {
|
|
4538
4577
|
const outcome = {
|
|
4539
|
-
state: "succeeded",
|
|
4578
|
+
state: options.interrupted === true ? "interrupted" : "succeeded",
|
|
4540
4579
|
resultRef: reportPath
|
|
4541
4580
|
};
|
|
4542
4581
|
if (!(options.runs.settleRecovered === void 0 ? false : await options.runs.settleRecovered(options.runId, outcome))) await options.runs.settle(options.runId, outcome);
|
|
@@ -4548,6 +4587,24 @@ async function projectRecoveredUnit(options) {
|
|
|
4548
4587
|
};
|
|
4549
4588
|
}
|
|
4550
4589
|
//#endregion
|
|
4590
|
+
//#region lib/types/domain/undo-receipt.js
|
|
4591
|
+
/** Undo receipt: immutable per-pair settlement, separate from the commit receipt. */
|
|
4592
|
+
function undoReceiptPath(home, runId, pairKey) {
|
|
4593
|
+
return join(home, "merge", "undo-receipts", encodeURIComponent(runId), createHash("sha256").update(pairKey).digest("hex") + ".json");
|
|
4594
|
+
}
|
|
4595
|
+
async function readUndoReceipt(io, home, runId, pairKey) {
|
|
4596
|
+
const raw = await readTextOrNull(io, undoReceiptPath(home, runId, pairKey));
|
|
4597
|
+
if (raw === null) return null;
|
|
4598
|
+
try {
|
|
4599
|
+
return JSON.parse(raw);
|
|
4600
|
+
} catch {
|
|
4601
|
+
return null;
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
async function writeUndoReceipt(io, home, receipt) {
|
|
4605
|
+
await io.writeText(undoReceiptPath(home, receipt.runId, receipt.pairKey), JSON.stringify(receipt, null, 2) + "\n");
|
|
4606
|
+
}
|
|
4607
|
+
//#endregion
|
|
4551
4608
|
//#region lib/types/recovery-coordinator.js
|
|
4552
4609
|
/** Batch 2c unit-journal recovery coordinator. UnitTransaction remains the only skill writer. */
|
|
4553
4610
|
const branchOf = (phase) => {
|
|
@@ -4584,6 +4641,20 @@ async function recoverCommitUnits(options) {
|
|
|
4584
4641
|
continue;
|
|
4585
4642
|
}
|
|
4586
4643
|
const evidence = await readUnitEvidence(options.io, options.home, journal.unitId);
|
|
4644
|
+
const receipt = (await readCommitReceipts(options.io, options.home)).find((item) => item.unitId === journal.unitId);
|
|
4645
|
+
const undoRunId = receipt?.parentRunId ?? journal.runId;
|
|
4646
|
+
if (receipt?.pairKey !== void 0 && await readUndoReceipt(options.io, options.home, undoRunId, receipt.pairKey) !== null) {
|
|
4647
|
+
reports.push({
|
|
4648
|
+
unitId: journal.unitId,
|
|
4649
|
+
runId: journal.runId,
|
|
4650
|
+
phase: journal.phase,
|
|
4651
|
+
branch: branchOf(journal.phase),
|
|
4652
|
+
action: "none",
|
|
4653
|
+
ok: true,
|
|
4654
|
+
code: "undo-recorded"
|
|
4655
|
+
});
|
|
4656
|
+
continue;
|
|
4657
|
+
}
|
|
4587
4658
|
const branch = branchOf(journal.phase);
|
|
4588
4659
|
if (journal.phase === "committed" || evidence !== null || journal.phase === "unit-written" || journal.phase === "sources-archived") {
|
|
4589
4660
|
const result = await options.unit.finalize(journal.unitId);
|
|
@@ -4597,6 +4668,7 @@ async function recoverCommitUnits(options) {
|
|
|
4597
4668
|
unitId: journal.unitId,
|
|
4598
4669
|
evidence: result.evidence,
|
|
4599
4670
|
now: options.now,
|
|
4671
|
+
...branch === "D" ? { interrupted: true } : {},
|
|
4600
4672
|
...options.runs === void 0 ? {} : { runs: options.runs },
|
|
4601
4673
|
...options.onProjectionPhase === void 0 ? {} : { onPhase: options.onProjectionPhase }
|
|
4602
4674
|
});
|
|
@@ -4615,7 +4687,8 @@ async function recoverCommitUnits(options) {
|
|
|
4615
4687
|
action: "blocked",
|
|
4616
4688
|
ok: false,
|
|
4617
4689
|
code: "projection-failed",
|
|
4618
|
-
message: projection.message
|
|
4690
|
+
message: projection.message,
|
|
4691
|
+
...branch === "D" ? { state: "interrupted" } : {}
|
|
4619
4692
|
});
|
|
4620
4693
|
else reports.push({
|
|
4621
4694
|
unitId: journal.unitId,
|
|
@@ -4624,7 +4697,8 @@ async function recoverCommitUnits(options) {
|
|
|
4624
4697
|
branch,
|
|
4625
4698
|
action: "finalize",
|
|
4626
4699
|
ok: true,
|
|
4627
|
-
evidenceSha256: evidenceSha256(result.evidence)
|
|
4700
|
+
evidenceSha256: evidenceSha256(result.evidence),
|
|
4701
|
+
...branch === "D" ? { state: "interrupted" } : {}
|
|
4628
4702
|
});
|
|
4629
4703
|
continue;
|
|
4630
4704
|
}
|
|
@@ -4636,7 +4710,8 @@ async function recoverCommitUnits(options) {
|
|
|
4636
4710
|
action: "finalize",
|
|
4637
4711
|
ok: false,
|
|
4638
4712
|
code: result.code,
|
|
4639
|
-
message: result.message
|
|
4713
|
+
message: result.message,
|
|
4714
|
+
...branch === "D" ? { state: "interrupted" } : {}
|
|
4640
4715
|
});
|
|
4641
4716
|
continue;
|
|
4642
4717
|
}
|
|
@@ -4838,8 +4913,31 @@ function actionRefs(row) {
|
|
|
4838
4913
|
action("reject", true),
|
|
4839
4914
|
action("discard", true)
|
|
4840
4915
|
];
|
|
4841
|
-
if (row.state === "approved" || row.state === "fuse")
|
|
4916
|
+
if (row.state === "approved" || row.state === "fuse") {
|
|
4917
|
+
if (row.failure !== void 0 && row.failure.retryable === false) return [{
|
|
4918
|
+
id: "pair",
|
|
4919
|
+
command: "/evolution merge pair " + row.a + " " + row.b,
|
|
4920
|
+
confirm: true,
|
|
4921
|
+
enabled: true
|
|
4922
|
+
}, action("discard", true)];
|
|
4923
|
+
return [
|
|
4924
|
+
{
|
|
4925
|
+
id: "retry-resume",
|
|
4926
|
+
command: "/evolution merge resume --retry " + row.id,
|
|
4927
|
+
confirm: true,
|
|
4928
|
+
enabled: true
|
|
4929
|
+
},
|
|
4930
|
+
action("discard", true),
|
|
4931
|
+
action("status")
|
|
4932
|
+
];
|
|
4933
|
+
}
|
|
4842
4934
|
if (row.state === "skipped") return [action("restore", true)];
|
|
4935
|
+
if (row.state === "verify") return [{
|
|
4936
|
+
id: "retry-candidates",
|
|
4937
|
+
command: "/evolution merge candidates --retry " + row.id,
|
|
4938
|
+
confirm: false,
|
|
4939
|
+
enabled: true
|
|
4940
|
+
}];
|
|
4843
4941
|
return [action("pair")];
|
|
4844
4942
|
}
|
|
4845
4943
|
function toRowFact(row, actions = actionRefs(row)) {
|
|
@@ -4945,6 +5043,11 @@ function makeMergeRoutes(deps, _audit) {
|
|
|
4945
5043
|
code: "merge-state-blocked",
|
|
4946
5044
|
readiness
|
|
4947
5045
|
});
|
|
5046
|
+
const hostOnly = (snapshot) => snapshot.hostOnly === void 0 ? null : json(503, {
|
|
5047
|
+
wireVersion: 1,
|
|
5048
|
+
code: "E-323",
|
|
5049
|
+
message: snapshot.hostOnly.detail ?? "host data plane unavailable"
|
|
5050
|
+
});
|
|
4948
5051
|
const notReady = async (source) => {
|
|
4949
5052
|
if (source.readiness === void 0) return null;
|
|
4950
5053
|
const readiness = await source.readiness();
|
|
@@ -4974,6 +5077,8 @@ function makeMergeRoutes(deps, _audit) {
|
|
|
4974
5077
|
code: "merge-state-blocked",
|
|
4975
5078
|
readiness: snapshot.blocked.readiness
|
|
4976
5079
|
});
|
|
5080
|
+
const capability = hostOnly(snapshot);
|
|
5081
|
+
if (capability !== null) return capability;
|
|
4977
5082
|
return json(200, buildFacts$1(snapshot));
|
|
4978
5083
|
}
|
|
4979
5084
|
const readiness = await notReady(source);
|
|
@@ -5009,6 +5114,8 @@ function makeMergeRoutes(deps, _audit) {
|
|
|
5009
5114
|
code: "merge-state-blocked",
|
|
5010
5115
|
readiness: snapshot.blocked.readiness
|
|
5011
5116
|
});
|
|
5117
|
+
const capability = hostOnly(snapshot);
|
|
5118
|
+
if (capability !== null) return capability;
|
|
5012
5119
|
try {
|
|
5013
5120
|
return json(200, buildQueuePage(snapshot, queue, expiredOnly, query.get("cursor") ?? void 0, Number.isFinite(limit) ? limit : 50));
|
|
5014
5121
|
} catch (error) {
|
|
@@ -5261,8 +5368,26 @@ function parseMergeCommand(argv) {
|
|
|
5261
5368
|
};
|
|
5262
5369
|
}
|
|
5263
5370
|
}
|
|
5371
|
+
/** Machine-readable three-verb table: merge undo / skill undo / skill restore. */
|
|
5372
|
+
const MERGE_LIFECYCLE_VERBS = [
|
|
5373
|
+
{
|
|
5374
|
+
verb: "merge undo",
|
|
5375
|
+
appliesTo: "一次 merge run(按 run 反序撤销 unit)",
|
|
5376
|
+
excludes: ["skill undo", "skill restore"]
|
|
5377
|
+
},
|
|
5378
|
+
{
|
|
5379
|
+
verb: "skill undo",
|
|
5380
|
+
appliesTo: "单个技能的一次 patch",
|
|
5381
|
+
excludes: ["merge undo"]
|
|
5382
|
+
},
|
|
5383
|
+
{
|
|
5384
|
+
verb: "skill restore",
|
|
5385
|
+
appliesTo: "从 .archive 恢复整棵技能树",
|
|
5386
|
+
excludes: ["merge undo"]
|
|
5387
|
+
}
|
|
5388
|
+
];
|
|
5264
5389
|
function mergeUsage() {
|
|
5265
|
-
return "usage: " + MERGE_COMMAND_DEFS.filter((def) => def.id !== "merge.doctor" && def.usage !== false).map((def) => def.fallback).join(" | ") + "\ndefaults: mergeApproval=false, mergeSkippedTtlDays=0, mergeRunTimeoutMinutes=0, mergePairTimeoutMinutes=15, mergeModelsRoot=\"\"";
|
|
5390
|
+
return "usage: " + MERGE_COMMAND_DEFS.filter((def) => def.id !== "merge.doctor" && def.usage !== false).map((def) => def.fallback).join(" | ") + "\ndefaults: mergeApproval=false, mergeSkippedTtlDays=0, mergeRunTimeoutMinutes=0, mergePairTimeoutMinutes=15, mergeModelsRoot=\"\"\nlifecycle: " + MERGE_LIFECYCLE_VERBS.map((item) => item.verb + "=" + item.appliesTo + ";互斥 " + item.excludes.join("/")).join(" | ");
|
|
5266
5391
|
}
|
|
5267
5392
|
//#endregion
|
|
5268
5393
|
//#region lib/types/presentation-budget.js
|
|
@@ -5492,6 +5617,36 @@ function failureOf(stage, error) {
|
|
|
5492
5617
|
retryFrom: stage === "decide" ? "agent1" : "agent2"
|
|
5493
5618
|
};
|
|
5494
5619
|
}
|
|
5620
|
+
function pairCriteria(outcome) {
|
|
5621
|
+
if (outcome === "merged") return {
|
|
5622
|
+
decision: "merge",
|
|
5623
|
+
validation: "required",
|
|
5624
|
+
commit: "required",
|
|
5625
|
+
archive: "sources",
|
|
5626
|
+
evidence: "receipt"
|
|
5627
|
+
};
|
|
5628
|
+
if (outcome === "kept") return {
|
|
5629
|
+
decision: "keep",
|
|
5630
|
+
validation: "report-only",
|
|
5631
|
+
commit: "never",
|
|
5632
|
+
archive: "none",
|
|
5633
|
+
evidence: "none"
|
|
5634
|
+
};
|
|
5635
|
+
if (outcome === "pending") return {
|
|
5636
|
+
decision: "merge",
|
|
5637
|
+
validation: "required",
|
|
5638
|
+
commit: "if-approved",
|
|
5639
|
+
archive: "none",
|
|
5640
|
+
evidence: "none"
|
|
5641
|
+
};
|
|
5642
|
+
return {
|
|
5643
|
+
decision: "merge",
|
|
5644
|
+
validation: "required",
|
|
5645
|
+
commit: "never",
|
|
5646
|
+
archive: "none",
|
|
5647
|
+
evidence: "none"
|
|
5648
|
+
};
|
|
5649
|
+
}
|
|
5495
5650
|
function pairOf(row, candidate, outcome, extra = {}) {
|
|
5496
5651
|
return {
|
|
5497
5652
|
id: row.id,
|
|
@@ -5503,6 +5658,7 @@ function pairOf(row, candidate, outcome, extra = {}) {
|
|
|
5503
5658
|
...candidate.gate === void 0 ? {} : { gate: candidate.gate },
|
|
5504
5659
|
outcome,
|
|
5505
5660
|
archived: [],
|
|
5661
|
+
criteria: pairCriteria(outcome),
|
|
5506
5662
|
...extra
|
|
5507
5663
|
};
|
|
5508
5664
|
}
|
|
@@ -5699,7 +5855,11 @@ async function runScan(options) {
|
|
|
5699
5855
|
totals.failed += 1;
|
|
5700
5856
|
continue;
|
|
5701
5857
|
}
|
|
5702
|
-
const
|
|
5858
|
+
const decided = options.model === void 0 || round1.decision.model !== void 0 ? round1.decision : {
|
|
5859
|
+
...round1.decision,
|
|
5860
|
+
model: options.model
|
|
5861
|
+
};
|
|
5862
|
+
const mergeDecision = options.cards !== void 0 ? withDecisionRevisions(decided, options.cards, row.a, row.b) : decided;
|
|
5703
5863
|
if (options.approval) {
|
|
5704
5864
|
const event = {
|
|
5705
5865
|
type: "verify-passed",
|
|
@@ -5764,7 +5924,13 @@ async function runScan(options) {
|
|
|
5764
5924
|
const fused = await pairLedger(options.io, options.home).transact((tx) => {
|
|
5765
5925
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
5766
5926
|
if (current === void 0) return null;
|
|
5767
|
-
const moved = tx.transition(row.id, {
|
|
5927
|
+
const moved = tx.transition(row.id, {
|
|
5928
|
+
type: "staged",
|
|
5929
|
+
...stageEntry === void 0 ? {} : {
|
|
5930
|
+
stagingRef: stageEntry.ref,
|
|
5931
|
+
stagingExpiresAt: stageEntry.expiresAt
|
|
5932
|
+
}
|
|
5933
|
+
}, current.version);
|
|
5768
5934
|
return moved.ok && moved.row !== null ? v4ToLegacy(moved.row) : null;
|
|
5769
5935
|
});
|
|
5770
5936
|
if (fused === null) {
|
|
@@ -5778,20 +5944,15 @@ async function runScan(options) {
|
|
|
5778
5944
|
row = fused;
|
|
5779
5945
|
if (stageEntry !== void 0) await presentStage(options.io, options.home, stageEntry);
|
|
5780
5946
|
if (isAborted(options.signal)) {
|
|
5781
|
-
|
|
5947
|
+
await pairLedger(options.io, options.home).transact((tx) => {
|
|
5782
5948
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
5783
|
-
|
|
5784
|
-
const moved = tx.transition(row.id, {
|
|
5785
|
-
type: "skipped",
|
|
5786
|
-
reason: "cancelled"
|
|
5787
|
-
}, current.version);
|
|
5788
|
-
return moved.ok && moved.row !== null ? v4ToLegacy(moved.row) : null;
|
|
5949
|
+
return current === void 0 ? false : tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
5789
5950
|
});
|
|
5790
|
-
|
|
5951
|
+
await dropStage(options.io, options.home, row.id, row.stagingRef);
|
|
5791
5952
|
stopReason = "cancelled";
|
|
5792
5953
|
remaining.push(...candidates.slice(index).map((candidate) => ({
|
|
5793
5954
|
pairKey: pairKey(candidate.a, candidate.b),
|
|
5794
|
-
queue: "
|
|
5955
|
+
queue: "verify"
|
|
5795
5956
|
})));
|
|
5796
5957
|
break;
|
|
5797
5958
|
}
|
|
@@ -5817,6 +5978,8 @@ async function runScan(options) {
|
|
|
5817
5978
|
pairs.push({
|
|
5818
5979
|
...pairOf(row, candidate, "merged", {
|
|
5819
5980
|
decision: mergeDecision,
|
|
5981
|
+
...round2.manifestFiles === void 0 ? {} : { manifestFiles: round2.manifestFiles },
|
|
5982
|
+
...round2.validationIssues === void 0 ? {} : { validation: { issues: round2.validationIssues } },
|
|
5820
5983
|
...round2.unitId === void 0 ? {} : { unitId: round2.unitId },
|
|
5821
5984
|
...round2.target === void 0 ? {} : { target: round2.target },
|
|
5822
5985
|
...round2.usage === void 0 ? {} : { usage: round2.usage },
|
|
@@ -5829,8 +5992,8 @@ async function runScan(options) {
|
|
|
5829
5992
|
continue;
|
|
5830
5993
|
}
|
|
5831
5994
|
const failed = round2.failure ?? failureOf("author", "round2 failed");
|
|
5832
|
-
if (failed.code === "merge-over-limit" || failed.code === "limit"
|
|
5833
|
-
const reason =
|
|
5995
|
+
if (failed.code === "merge-over-limit" || failed.code === "limit") {
|
|
5996
|
+
const reason = "merge-over-limit";
|
|
5834
5997
|
if (await pairLedger(options.io, options.home).transact((tx) => {
|
|
5835
5998
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
5836
5999
|
return current === void 0 ? false : tx.transition(row.id, {
|
|
@@ -5842,33 +6005,35 @@ async function runScan(options) {
|
|
|
5842
6005
|
})) await dropStage(options.io, options.home, row.id);
|
|
5843
6006
|
pairs.push(pairOf(row, candidate, "failed", {
|
|
5844
6007
|
decision: mergeDecision,
|
|
5845
|
-
failure: failed
|
|
6008
|
+
failure: failed,
|
|
6009
|
+
...round2.manifestFiles === void 0 ? {} : { manifestFiles: round2.manifestFiles },
|
|
6010
|
+
...round2.criteria === void 0 ? {} : { criteria: round2.criteria },
|
|
6011
|
+
...round2.validationIssues === void 0 ? {} : { validation: { issues: round2.validationIssues } }
|
|
5846
6012
|
}));
|
|
5847
6013
|
totals.failed += 1;
|
|
5848
6014
|
continue;
|
|
5849
6015
|
}
|
|
5850
6016
|
if (failed.code === "target-changed" || failed.code === "target-taken" || failed.code === "source-changed" || failed.code === "support-changed" || failed.code === "source-gone") {
|
|
5851
6017
|
const code = failed.code === "target-changed" ? "target-taken" : failed.code === "support-changed" ? "source-changed" : failed.code;
|
|
6018
|
+
const terminalFailure = {
|
|
6019
|
+
...failed,
|
|
6020
|
+
code,
|
|
6021
|
+
retryable: false,
|
|
6022
|
+
retryFrom: "none"
|
|
6023
|
+
};
|
|
5852
6024
|
await pairLedger(options.io, options.home).transact((tx) => {
|
|
5853
6025
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
5854
6026
|
if (current === void 0) return false;
|
|
5855
6027
|
if (code === "source-gone") return tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
5856
6028
|
return tx.transition(row.id, {
|
|
5857
|
-
type: "
|
|
5858
|
-
|
|
5859
|
-
origin: "system",
|
|
5860
|
-
skipSource: "system"
|
|
6029
|
+
type: "failed",
|
|
6030
|
+
failure: terminalFailure
|
|
5861
6031
|
}, current.version).ok;
|
|
5862
6032
|
});
|
|
5863
6033
|
await dropStage(options.io, options.home, row.id);
|
|
5864
6034
|
pairs.push(pairOf(row, candidate, "failed", {
|
|
5865
6035
|
decision: mergeDecision,
|
|
5866
|
-
failure:
|
|
5867
|
-
...failed,
|
|
5868
|
-
code,
|
|
5869
|
-
retryable: false,
|
|
5870
|
-
retryFrom: "none"
|
|
5871
|
-
}
|
|
6036
|
+
failure: terminalFailure
|
|
5872
6037
|
}));
|
|
5873
6038
|
totals.failed += 1;
|
|
5874
6039
|
continue;
|
|
@@ -5884,13 +6049,24 @@ async function runScan(options) {
|
|
|
5884
6049
|
return moved.ok && moved.row !== null ? v4ToLegacy(moved.row) : null;
|
|
5885
6050
|
});
|
|
5886
6051
|
if (projected === null && terminal) {
|
|
5887
|
-
await pairLedger(options.io, options.home).transact((tx) => {
|
|
6052
|
+
if (await pairLedger(options.io, options.home).transact((tx) => {
|
|
5888
6053
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
5889
6054
|
return current === void 0 ? false : tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
5890
|
-
})
|
|
6055
|
+
})) {
|
|
6056
|
+
await dropStage(options.io, options.home, row.id, row.stagingRef);
|
|
6057
|
+
await recordPair(options.io, options.home, {
|
|
6058
|
+
pairKey: row.pairKey,
|
|
6059
|
+
runId: row.runId ?? "merge",
|
|
6060
|
+
result: "failed",
|
|
6061
|
+
...row.decision?.target === void 0 ? {} : { target: row.decision.target }
|
|
6062
|
+
});
|
|
6063
|
+
}
|
|
5891
6064
|
pairs.push(pairOf(row, candidate, "failed", {
|
|
5892
6065
|
decision: mergeDecision,
|
|
5893
|
-
failure: failed
|
|
6066
|
+
failure: failed,
|
|
6067
|
+
...round2.manifestFiles === void 0 ? {} : { manifestFiles: round2.manifestFiles },
|
|
6068
|
+
...round2.criteria === void 0 ? {} : { criteria: round2.criteria },
|
|
6069
|
+
...round2.validationIssues === void 0 ? {} : { validation: { issues: round2.validationIssues } }
|
|
5894
6070
|
}));
|
|
5895
6071
|
totals.failed += 1;
|
|
5896
6072
|
continue;
|
|
@@ -5906,7 +6082,10 @@ async function runScan(options) {
|
|
|
5906
6082
|
row = projected;
|
|
5907
6083
|
pairs.push(pairOf(row, candidate, "failed", {
|
|
5908
6084
|
decision: mergeDecision,
|
|
5909
|
-
failure: failed
|
|
6085
|
+
failure: failed,
|
|
6086
|
+
...round2.manifestFiles === void 0 ? {} : { manifestFiles: round2.manifestFiles },
|
|
6087
|
+
...round2.criteria === void 0 ? {} : { criteria: round2.criteria },
|
|
6088
|
+
...round2.validationIssues === void 0 ? {} : { validation: { issues: round2.validationIssues } }
|
|
5910
6089
|
}));
|
|
5911
6090
|
totals.failed += 1;
|
|
5912
6091
|
}
|
|
@@ -5978,6 +6157,8 @@ async function runScan(options) {
|
|
|
5978
6157
|
}
|
|
5979
6158
|
//#endregion
|
|
5980
6159
|
//#region lib/types/run-shell.js
|
|
6160
|
+
/** Bounded wait for a detached body before the run is reported as timed out. */
|
|
6161
|
+
const DETACHED_DRAIN_MS = 3e4;
|
|
5981
6162
|
function emptyDraft(reason) {
|
|
5982
6163
|
return {
|
|
5983
6164
|
stopReason: "failed",
|
|
@@ -6018,8 +6199,14 @@ async function withMergeRun(options) {
|
|
|
6018
6199
|
await writeRunMeta(options.io, options.home, {
|
|
6019
6200
|
version: 1,
|
|
6020
6201
|
runId: handle.id,
|
|
6202
|
+
pid: process.pid,
|
|
6021
6203
|
...options.parentRunId === void 0 ? {} : { parentRunId: options.parentRunId },
|
|
6022
6204
|
...options.pairKey === void 0 ? {} : { pairKey: options.pairKey },
|
|
6205
|
+
...options.stagingRef === void 0 ? {} : { stagingRef: options.stagingRef },
|
|
6206
|
+
...options.pairKey === void 0 ? {} : { pairs: [{
|
|
6207
|
+
pairKey: options.pairKey,
|
|
6208
|
+
state: "pending"
|
|
6209
|
+
}] },
|
|
6023
6210
|
pairCount: options.pairCount ?? (options.pairKey === void 0 ? 0 : 1),
|
|
6024
6211
|
phase: "running",
|
|
6025
6212
|
approval: options.approval.mergeApproval ? "pending" : "approved",
|
|
@@ -6035,26 +6222,50 @@ async function withMergeRun(options) {
|
|
|
6035
6222
|
handle.signal.addEventListener("abort", onAbort, { once: true });
|
|
6036
6223
|
let timer;
|
|
6037
6224
|
const timeoutMinutes = options.timeoutMinutes;
|
|
6038
|
-
const
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
})
|
|
6225
|
+
const bodyOutcome = options.body({
|
|
6226
|
+
runId: handle.id,
|
|
6227
|
+
startedAt,
|
|
6228
|
+
signal: runController.signal,
|
|
6229
|
+
stage
|
|
6230
|
+
}).then((value) => ({
|
|
6231
|
+
kind: "draft",
|
|
6232
|
+
value
|
|
6233
|
+
})).catch((error) => ({
|
|
6234
|
+
kind: "error",
|
|
6235
|
+
error
|
|
6236
|
+
}));
|
|
6237
|
+
let outcome;
|
|
6238
|
+
let timedOut = false;
|
|
6044
6239
|
try {
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6240
|
+
if (timeoutMinutes === void 0 || timeoutMinutes <= 0) outcome = await bodyOutcome;
|
|
6241
|
+
else {
|
|
6242
|
+
const timeout = new Promise((resolve) => {
|
|
6243
|
+
timer = setTimeout(() => {
|
|
6244
|
+
runController.abort();
|
|
6245
|
+
resolve({ kind: "timeout" });
|
|
6246
|
+
}, timeoutMinutes * 6e4);
|
|
6247
|
+
});
|
|
6248
|
+
const raced = await Promise.race([bodyOutcome, timeout]);
|
|
6249
|
+
if (raced.kind !== "timeout") outcome = raced;
|
|
6250
|
+
else {
|
|
6251
|
+
timedOut = true;
|
|
6252
|
+
let drainTimer;
|
|
6253
|
+
const drained = await Promise.race([bodyOutcome, new Promise((resolve) => {
|
|
6254
|
+
drainTimer = setTimeout(() => resolve({ kind: "drain-timeout" }), DETACHED_DRAIN_MS);
|
|
6255
|
+
})]);
|
|
6256
|
+
if (drainTimer !== void 0) clearTimeout(drainTimer);
|
|
6257
|
+
outcome = drained.kind === "drain-timeout" ? {
|
|
6258
|
+
kind: "error",
|
|
6259
|
+
error: /* @__PURE__ */ new Error("timeout: body did not stop within 30s drain window")
|
|
6260
|
+
} : drained;
|
|
6261
|
+
}
|
|
6262
|
+
}
|
|
6263
|
+
draft = outcome.kind === "draft" ? outcome.value : emptyDraft("run body failed: " + (outcome.error instanceof Error ? outcome.error.message : String(outcome.error)));
|
|
6264
|
+
if (timedOut) draft = {
|
|
6265
|
+
...draft,
|
|
6266
|
+
stopReason: "timeout",
|
|
6267
|
+
notes: [...draft.notes, "merge run timeout reached; body aborted"]
|
|
6268
|
+
};
|
|
6058
6269
|
} finally {
|
|
6059
6270
|
if (timer !== void 0) clearTimeout(timer);
|
|
6060
6271
|
handle.signal.removeEventListener("abort", onAbort);
|
|
@@ -6070,9 +6281,13 @@ async function withMergeRun(options) {
|
|
|
6070
6281
|
approval: options.approval,
|
|
6071
6282
|
scope: draft.scope,
|
|
6072
6283
|
pairs: draft.pairs,
|
|
6284
|
+
processedPairs: draft.pairs,
|
|
6073
6285
|
totals: draft.totals,
|
|
6074
6286
|
notes: draft.notes,
|
|
6075
|
-
...draft.remaining === void 0 ? {} : {
|
|
6287
|
+
...draft.remaining === void 0 ? {} : {
|
|
6288
|
+
remaining: draft.remaining,
|
|
6289
|
+
remainingPairs: draft.remaining
|
|
6290
|
+
},
|
|
6076
6291
|
...draft.reportOnly === void 0 ? {} : { reportOnly: draft.reportOnly },
|
|
6077
6292
|
...draft.validationFailures === void 0 ? {} : { validationFailures: draft.validationFailures },
|
|
6078
6293
|
...draft.archived === void 0 ? {} : { archived: draft.archived },
|
|
@@ -6117,11 +6332,21 @@ async function withMergeRun(options) {
|
|
|
6117
6332
|
...runState === "failed" ? { failure: "merge " + report.stopReason } : {}
|
|
6118
6333
|
});
|
|
6119
6334
|
const awaiting = options.approval.mergeApproval && (options.command === "scan" || options.command === "pair") && report.stopReason === "completed" && report.totals.pending > 0;
|
|
6335
|
+
const finalPairs = draft.pairs.length > 0 ? draft.pairs.map((pair) => ({
|
|
6336
|
+
pairKey: pair.pairKey,
|
|
6337
|
+
state: pair.outcome === "merged" ? "fused" : pair.outcome === "pending" ? "pending" : "skipped"
|
|
6338
|
+
})) : options.pairKey === void 0 ? [] : [{
|
|
6339
|
+
pairKey: options.pairKey,
|
|
6340
|
+
state: "pending"
|
|
6341
|
+
}];
|
|
6120
6342
|
await writeRunMeta(options.io, options.home, {
|
|
6121
6343
|
version: 1,
|
|
6122
6344
|
runId: handle.id,
|
|
6345
|
+
pid: process.pid,
|
|
6123
6346
|
...options.parentRunId === void 0 ? {} : { parentRunId: options.parentRunId },
|
|
6124
6347
|
...options.pairKey === void 0 ? {} : { pairKey: options.pairKey },
|
|
6348
|
+
...options.stagingRef === void 0 ? {} : { stagingRef: options.stagingRef },
|
|
6349
|
+
...finalPairs.length === 0 ? {} : { pairs: finalPairs },
|
|
6125
6350
|
pairCount: options.pairCount ?? (options.pairKey === void 0 ? 0 : 1),
|
|
6126
6351
|
phase: awaiting ? "awaiting-approval" : "settled",
|
|
6127
6352
|
approval: awaiting ? "pending" : "approved",
|
|
@@ -6184,7 +6409,8 @@ async function runScanCommand(options) {
|
|
|
6184
6409
|
...options.timeoutMinutes === void 0 ? {} : { timeoutMinutes: options.timeoutMinutes },
|
|
6185
6410
|
...options.notes === void 0 ? {} : { notes: options.notes },
|
|
6186
6411
|
...options.isOwnerActive === void 0 ? {} : { isOwnerActive: options.isOwnerActive },
|
|
6187
|
-
...options.cards === void 0 ? {} : { cards: options.cards }
|
|
6412
|
+
...options.cards === void 0 ? {} : { cards: options.cards },
|
|
6413
|
+
...options.model === void 0 ? {} : { model: options.model }
|
|
6188
6414
|
});
|
|
6189
6415
|
}
|
|
6190
6416
|
});
|
|
@@ -6204,6 +6430,11 @@ async function act(io, home, now, id, type) {
|
|
|
6204
6430
|
reason: "not-pending",
|
|
6205
6431
|
message: "row is not pending: " + row.state
|
|
6206
6432
|
};
|
|
6433
|
+
if (row.expired !== void 0) return {
|
|
6434
|
+
ok: false,
|
|
6435
|
+
reason: "expired",
|
|
6436
|
+
message: "pending row expired (" + row.expired + "): only discard or re-pair"
|
|
6437
|
+
};
|
|
6207
6438
|
const event = type === "approve" ? {
|
|
6208
6439
|
type: "approved",
|
|
6209
6440
|
runId: row.runId ?? "approve:" + id
|
|
@@ -6253,6 +6484,11 @@ function casExpiredHook(code) {
|
|
|
6253
6484
|
//#endregion
|
|
6254
6485
|
//#region lib/types/round2-outcome.js
|
|
6255
6486
|
async function recordRound2Outcome(io, home, now, row, outcome, deferProjection = false) {
|
|
6487
|
+
const reportExtra = {
|
|
6488
|
+
...outcome.manifestFiles === void 0 ? {} : { manifestFiles: outcome.manifestFiles },
|
|
6489
|
+
...outcome.criteria === void 0 ? {} : { criteria: outcome.criteria },
|
|
6490
|
+
...outcome.validationIssues === void 0 ? {} : { validation: { issues: outcome.validationIssues } }
|
|
6491
|
+
};
|
|
6256
6492
|
if (outcome.kind === "merged") {
|
|
6257
6493
|
const unitRef = {
|
|
6258
6494
|
unitId: outcome.unitId ?? "unknown",
|
|
@@ -6289,8 +6525,10 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6289
6525
|
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
6290
6526
|
outcome: "merged",
|
|
6291
6527
|
archived: outcome.archived ?? [],
|
|
6528
|
+
...outcome.lengths === void 0 ? {} : { lengths: outcome.lengths },
|
|
6292
6529
|
...outcome.unitId === void 0 ? {} : { unitId: outcome.unitId },
|
|
6293
|
-
...outcome.target === void 0 ? {} : { target: outcome.target }
|
|
6530
|
+
...outcome.target === void 0 ? {} : { target: outcome.target },
|
|
6531
|
+
...reportExtra
|
|
6294
6532
|
}
|
|
6295
6533
|
};
|
|
6296
6534
|
await finalize();
|
|
@@ -6305,6 +6543,8 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6305
6543
|
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
6306
6544
|
outcome: "merged",
|
|
6307
6545
|
archived: outcome.archived ?? [],
|
|
6546
|
+
...outcome.lengths === void 0 ? {} : { lengths: outcome.lengths },
|
|
6547
|
+
...reportExtra,
|
|
6308
6548
|
...outcome.unitId === void 0 ? {} : { unitId: outcome.unitId },
|
|
6309
6549
|
...outcome.target === void 0 ? {} : { target: outcome.target },
|
|
6310
6550
|
...outcome.validationPlan === void 0 ? {} : { validationPlan: outcome.validationPlan }
|
|
@@ -6318,8 +6558,8 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6318
6558
|
retryable: true,
|
|
6319
6559
|
retryFrom: "agent2"
|
|
6320
6560
|
};
|
|
6321
|
-
if (failure.code === "merge-over-limit" || failure.code === "limit"
|
|
6322
|
-
const reason =
|
|
6561
|
+
if (failure.code === "merge-over-limit" || failure.code === "limit") {
|
|
6562
|
+
const reason = "merge-over-limit";
|
|
6323
6563
|
if (await pairLedger(io, home).transact((tx) => {
|
|
6324
6564
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6325
6565
|
return current === void 0 ? false : tx.transition(row.id, {
|
|
@@ -6340,20 +6580,25 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6340
6580
|
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
6341
6581
|
outcome: "failed",
|
|
6342
6582
|
archived: [],
|
|
6343
|
-
failure
|
|
6583
|
+
failure,
|
|
6584
|
+
...reportExtra
|
|
6344
6585
|
}
|
|
6345
6586
|
};
|
|
6346
6587
|
}
|
|
6347
6588
|
const cas = casExpiredHook(failure.code);
|
|
6348
6589
|
if (cas !== null) {
|
|
6590
|
+
const terminalFailure = {
|
|
6591
|
+
...failure,
|
|
6592
|
+
code: cas.reason,
|
|
6593
|
+
retryable: false,
|
|
6594
|
+
retryFrom: "none"
|
|
6595
|
+
};
|
|
6349
6596
|
await pairLedger(io, home).transact((tx) => {
|
|
6350
6597
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6351
6598
|
if (current === void 0) return false;
|
|
6352
6599
|
return tx.transition(row.id, {
|
|
6353
|
-
type: "
|
|
6354
|
-
|
|
6355
|
-
origin: "system",
|
|
6356
|
-
skipSource: "system"
|
|
6600
|
+
type: "failed",
|
|
6601
|
+
failure: terminalFailure
|
|
6357
6602
|
}, current.version).ok;
|
|
6358
6603
|
});
|
|
6359
6604
|
await dropStage(io, home, row.id);
|
|
@@ -6368,24 +6613,30 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6368
6613
|
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
6369
6614
|
outcome: "failed",
|
|
6370
6615
|
archived: [],
|
|
6371
|
-
failure:
|
|
6372
|
-
|
|
6373
|
-
code: cas.reason,
|
|
6374
|
-
retryable: false,
|
|
6375
|
-
retryFrom: "none"
|
|
6376
|
-
}
|
|
6616
|
+
failure: terminalFailure,
|
|
6617
|
+
...reportExtra
|
|
6377
6618
|
}
|
|
6378
6619
|
};
|
|
6379
6620
|
}
|
|
6380
|
-
if (failure.code === "commit-failed" || failure.code === "validation-failed")
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6621
|
+
if (failure.code === "commit-failed" || failure.code === "validation-failed") {
|
|
6622
|
+
if (await pairLedger(io, home).transact((tx) => {
|
|
6623
|
+
const current = tx.rows().find((item) => item.id === row.id);
|
|
6624
|
+
return current === void 0 ? false : tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
6625
|
+
})) {
|
|
6626
|
+
await dropStage(io, home, row.id, row.stagingRef);
|
|
6627
|
+
await recordPair(io, home, {
|
|
6628
|
+
pairKey: row.pairKey,
|
|
6629
|
+
runId: row.runId ?? "merge",
|
|
6630
|
+
result: "failed",
|
|
6631
|
+
...row.decision?.target === void 0 ? {} : { target: row.decision.target }
|
|
6632
|
+
});
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6384
6635
|
if (failure.code === "source-gone") {
|
|
6385
|
-
await pairLedger(io, home).transact((tx) => {
|
|
6636
|
+
if (await pairLedger(io, home).transact((tx) => {
|
|
6386
6637
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6387
6638
|
return current === void 0 ? false : tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
6388
|
-
});
|
|
6639
|
+
})) await dropStage(io, home, row.id, row.stagingRef);
|
|
6389
6640
|
return {
|
|
6390
6641
|
merged: false,
|
|
6391
6642
|
pair: {
|
|
@@ -6422,7 +6673,8 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6422
6673
|
...failure,
|
|
6423
6674
|
code: "row-changed",
|
|
6424
6675
|
message: "row changed before failure record"
|
|
6425
|
-
}
|
|
6676
|
+
},
|
|
6677
|
+
...reportExtra
|
|
6426
6678
|
}
|
|
6427
6679
|
};
|
|
6428
6680
|
await recordPair(io, home, {
|
|
@@ -6442,7 +6694,8 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6442
6694
|
...row.decision === void 0 ? {} : { decision: row.decision },
|
|
6443
6695
|
outcome: "failed",
|
|
6444
6696
|
archived: [],
|
|
6445
|
-
failure
|
|
6697
|
+
failure,
|
|
6698
|
+
...reportExtra
|
|
6446
6699
|
}
|
|
6447
6700
|
};
|
|
6448
6701
|
}
|
|
@@ -6470,7 +6723,11 @@ async function runApproveCommand(options) {
|
|
|
6470
6723
|
const fused = await pairLedger(options.io, options.home).transact((tx) => {
|
|
6471
6724
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6472
6725
|
if (current === void 0) return null;
|
|
6473
|
-
const moved = tx.transition(row.id, {
|
|
6726
|
+
const moved = tx.transition(row.id, {
|
|
6727
|
+
type: "staged",
|
|
6728
|
+
stagingRef: staged.entry.ref,
|
|
6729
|
+
stagingExpiresAt: staged.entry.expiresAt
|
|
6730
|
+
}, current.version);
|
|
6474
6731
|
return moved.ok && moved.row !== null ? v4ToLegacy(moved.row) : null;
|
|
6475
6732
|
});
|
|
6476
6733
|
if (fused === null) return failure("approve", "row-changed", "行在暂存完成前被并发修改:" + row.id, "运行 /evolution merge status " + row.id + " 后重试", row.runId);
|
|
@@ -6487,6 +6744,7 @@ async function runApproveCommand(options) {
|
|
|
6487
6744
|
mergeApproval: true
|
|
6488
6745
|
},
|
|
6489
6746
|
pairKey: row.pairKey,
|
|
6747
|
+
...row.stagingRef === void 0 ? {} : { stagingRef: row.stagingRef },
|
|
6490
6748
|
parentRunId,
|
|
6491
6749
|
now: options.now,
|
|
6492
6750
|
...options.detached === true ? { detached: true } : {},
|
|
@@ -6598,7 +6856,11 @@ async function runResumeCommand(options) {
|
|
|
6598
6856
|
const fused = await pairLedger(options.io, options.home).transact((tx) => {
|
|
6599
6857
|
const current = tx.rows().find((item) => item.id === selected.id);
|
|
6600
6858
|
if (current === void 0) return null;
|
|
6601
|
-
const moved = tx.transition(selected.id, {
|
|
6859
|
+
const moved = tx.transition(selected.id, {
|
|
6860
|
+
type: "staged",
|
|
6861
|
+
stagingRef: staged.entry.ref,
|
|
6862
|
+
stagingExpiresAt: staged.entry.expiresAt
|
|
6863
|
+
}, current.version);
|
|
6602
6864
|
return moved.ok && moved.row !== null ? v4ToLegacy(moved.row) : null;
|
|
6603
6865
|
});
|
|
6604
6866
|
if (fused === null) return failure("resume", "row-changed", "行在暂存完成前被并发修改:" + selected.id, "运行 /evolution merge status " + selected.id + " 后重试", selected.runId);
|
|
@@ -6615,6 +6877,7 @@ async function runResumeCommand(options) {
|
|
|
6615
6877
|
mergeApproval: options.approval
|
|
6616
6878
|
},
|
|
6617
6879
|
pairKey: row.pairKey,
|
|
6880
|
+
...row.stagingRef === void 0 ? {} : { stagingRef: row.stagingRef },
|
|
6618
6881
|
parentRunId,
|
|
6619
6882
|
now: options.now,
|
|
6620
6883
|
...options.detached === true ? { detached: true } : {},
|
|
@@ -6710,24 +6973,6 @@ async function writeUndoJournal(io, home, journal) {
|
|
|
6710
6973
|
await transactIo(io, undoJournalPath(home, journal.runId), () => JSON.stringify(journal, null, 2) + "\n");
|
|
6711
6974
|
}
|
|
6712
6975
|
//#endregion
|
|
6713
|
-
//#region lib/types/domain/undo-receipt.js
|
|
6714
|
-
/** Undo receipt: immutable per-pair settlement, separate from the commit receipt. */
|
|
6715
|
-
function undoReceiptPath(home, runId, pairKey) {
|
|
6716
|
-
return join(home, "merge", "undo-receipts", encodeURIComponent(runId), createHash("sha256").update(pairKey).digest("hex") + ".json");
|
|
6717
|
-
}
|
|
6718
|
-
async function readUndoReceipt(io, home, runId, pairKey) {
|
|
6719
|
-
const raw = await readTextOrNull(io, undoReceiptPath(home, runId, pairKey));
|
|
6720
|
-
if (raw === null) return null;
|
|
6721
|
-
try {
|
|
6722
|
-
return JSON.parse(raw);
|
|
6723
|
-
} catch {
|
|
6724
|
-
return null;
|
|
6725
|
-
}
|
|
6726
|
-
}
|
|
6727
|
-
async function writeUndoReceipt(io, home, receipt) {
|
|
6728
|
-
await io.writeText(undoReceiptPath(home, receipt.runId, receipt.pairKey), JSON.stringify(receipt, null, 2) + "\n");
|
|
6729
|
-
}
|
|
6730
|
-
//#endregion
|
|
6731
6976
|
//#region lib/types/undo-operation.js
|
|
6732
6977
|
function digest$1(value) {
|
|
6733
6978
|
return value === null ? null : contentHash(value);
|
|
@@ -6749,11 +6994,16 @@ function sortReceipts(receipts) {
|
|
|
6749
6994
|
}
|
|
6750
6995
|
async function preflight(io, home, port, receipt) {
|
|
6751
6996
|
const evidence = await readUnitEvidence(io, home, receipt.unitId);
|
|
6752
|
-
if (evidence === null) return {
|
|
6997
|
+
if (evidence === null) return receipt.evidenceSha256 === void 0 ? {
|
|
6753
6998
|
ok: false,
|
|
6754
6999
|
code: "undo-unavailable-legacy-receipt",
|
|
6755
7000
|
message: "commit evidence is missing; undo unavailable for legacy receipt",
|
|
6756
7001
|
undone: []
|
|
7002
|
+
} : {
|
|
7003
|
+
ok: false,
|
|
7004
|
+
code: "undo-evidence-expired",
|
|
7005
|
+
message: "commit evidence aged out of the retention window; undo unavailable (fail-closed)",
|
|
7006
|
+
undone: []
|
|
6757
7007
|
};
|
|
6758
7008
|
const expectedSha = receipt.evidenceSha256 ?? evidenceSha256(evidence);
|
|
6759
7009
|
if (evidenceSha256(evidence) !== expectedSha) return {
|
|
@@ -6814,8 +7064,9 @@ async function settleRun(io, home, runId, pairKey, allPairs, at) {
|
|
|
6814
7064
|
});
|
|
6815
7065
|
}
|
|
6816
7066
|
async function runUndoOperation(options) {
|
|
6817
|
-
const
|
|
6818
|
-
const allPairKeys =
|
|
7067
|
+
const runReceipts = (await readCommitReceipts(options.io, options.home)).filter((receipt) => (receipt.parentRunId ?? receipt.runId) === options.runId);
|
|
7068
|
+
const allPairKeys = runReceipts.map((receipt) => receipt.pairKey ?? receipt.unitId);
|
|
7069
|
+
const allReceipts = runReceipts.filter((receipt) => options.target === void 0 || receipt.target === options.target);
|
|
6819
7070
|
const receipts = options.pairKey === void 0 ? allReceipts : allReceipts.filter((receipt) => receipt.pairKey === options.pairKey);
|
|
6820
7071
|
if (receipts.length === 0) return {
|
|
6821
7072
|
ok: false,
|
|
@@ -7712,6 +7963,7 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
7712
7963
|
};
|
|
7713
7964
|
let result;
|
|
7714
7965
|
let evidenceSha256$1;
|
|
7966
|
+
let warning;
|
|
7715
7967
|
const transaction = options.unit ?? port.unit ?? legacyUnitAdapter(port, assembled.plan, options, runId);
|
|
7716
7968
|
if (transaction === void 0) return {
|
|
7717
7969
|
ok: false,
|
|
@@ -7747,7 +7999,7 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
7747
7999
|
message: prepared.message,
|
|
7748
8000
|
plan: assembled.plan
|
|
7749
8001
|
};
|
|
7750
|
-
const committed = await transaction.commit(prepared.prepared);
|
|
8002
|
+
const committed = await transaction.commit(prepared.prepared, options.signal === void 0 ? void 0 : { signal: options.signal });
|
|
7751
8003
|
if (!committed.ok) {
|
|
7752
8004
|
try {
|
|
7753
8005
|
await writeJournal(io, home, {
|
|
@@ -7772,11 +8024,7 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
7772
8024
|
const finalize = transaction.finalize;
|
|
7773
8025
|
if (finalize !== void 0) {
|
|
7774
8026
|
const finalized = await finalize.call(transaction, unitId);
|
|
7775
|
-
if (!finalized.ok)
|
|
7776
|
-
ok: false,
|
|
7777
|
-
message: "unit committed but finalize failed: " + finalized.message,
|
|
7778
|
-
plan: assembled.plan
|
|
7779
|
-
};
|
|
8027
|
+
if (!finalized.ok) warning = "commit landed but finalize failed: " + finalized.message;
|
|
7780
8028
|
}
|
|
7781
8029
|
evidenceSha256$1 = evidenceSha256(committed.evidence);
|
|
7782
8030
|
result = {
|
|
@@ -7820,7 +8068,7 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
7820
8068
|
runId,
|
|
7821
8069
|
...evidenceSha256$1 === void 0 ? {} : { evidenceSha256: evidenceSha256$1 },
|
|
7822
8070
|
...options.validationPlan === void 0 ? {} : { validationPlan: options.validationPlan },
|
|
7823
|
-
parentRunId:
|
|
8071
|
+
...options.parentRunId === void 0 ? {} : { parentRunId: options.parentRunId },
|
|
7824
8072
|
unitId: result.unitId,
|
|
7825
8073
|
target: assembled.plan.target,
|
|
7826
8074
|
create: !assembled.plan.targetExists,
|
|
@@ -7838,14 +8086,15 @@ async function commitStagedRun(io, home, runId, port, options) {
|
|
|
7838
8086
|
ok: true,
|
|
7839
8087
|
plan: assembled.plan,
|
|
7840
8088
|
result,
|
|
7841
|
-
receipt
|
|
8089
|
+
receipt,
|
|
8090
|
+
...warning === void 0 ? {} : { warning }
|
|
7842
8091
|
};
|
|
7843
8092
|
} catch (error) {
|
|
7844
8093
|
return {
|
|
7845
8094
|
ok: true,
|
|
7846
8095
|
plan: assembled.plan,
|
|
7847
8096
|
result,
|
|
7848
|
-
warning: "commit landed but receipt write failed: " + (error instanceof Error ? error.message : String(error))
|
|
8097
|
+
warning: (warning === void 0 ? "" : warning + "; ") + "commit landed but receipt write failed: " + (error instanceof Error ? error.message : String(error))
|
|
7849
8098
|
};
|
|
7850
8099
|
}
|
|
7851
8100
|
}
|
|
@@ -7971,7 +8220,8 @@ function buildValidationPlan(input) {
|
|
|
7971
8220
|
const reverseRefs = files.map((file) => file.path).filter((path) => !refs.has(path) && manifest.has(path)).sort();
|
|
7972
8221
|
const orphanFiles = [...new Set(input.existingSupport ?? [])].filter((path) => !staged.has(path)).sort();
|
|
7973
8222
|
const byPath = /* @__PURE__ */ new Map();
|
|
7974
|
-
|
|
8223
|
+
const supportSources = input.supportSources ?? [];
|
|
8224
|
+
for (const file of supportSources) {
|
|
7975
8225
|
const entry = byPath.get(file.path) ?? {
|
|
7976
8226
|
contents: /* @__PURE__ */ new Set(),
|
|
7977
8227
|
sources: []
|
|
@@ -8127,7 +8377,8 @@ async function runAuthoring(options) {
|
|
|
8127
8377
|
message: "target must be a bare skill name: " + options.target
|
|
8128
8378
|
};
|
|
8129
8379
|
const targetCard = options.cards.find((card) => card.name === options.target);
|
|
8130
|
-
const
|
|
8380
|
+
const requestedMode = options.mode ?? (targetCard === void 0 ? "create-umbrella" : "merge-into-existing");
|
|
8381
|
+
const mode = requestedMode === "merge-into-existing" && targetCard === void 0 ? "create-umbrella" : requestedMode;
|
|
8131
8382
|
const createMode = mode === "create-umbrella";
|
|
8132
8383
|
if (!createMode && targetCard !== void 0) {
|
|
8133
8384
|
if (targetCard.managed !== true) return {
|
|
@@ -8202,7 +8453,8 @@ async function runAuthoring(options) {
|
|
|
8202
8453
|
message: "manifest file missing from staged tree: " + missing
|
|
8203
8454
|
};
|
|
8204
8455
|
const limitsSnapshot = limitsSnapshotOf(options.limits ?? DEFAULT_SKILL_LIMITS);
|
|
8205
|
-
const
|
|
8456
|
+
const participants = new Set(options.sources ?? []);
|
|
8457
|
+
const supportSources = options.cards.filter((card) => participants.has(card.name)).flatMap((card) => (card.files ?? []).map((file) => ({
|
|
8206
8458
|
path: file.path,
|
|
8207
8459
|
content: file.content,
|
|
8208
8460
|
source: card.name
|
|
@@ -8224,28 +8476,31 @@ async function runAuthoring(options) {
|
|
|
8224
8476
|
existingSupport: (options.cards.find((card) => card.name === options.target)?.files ?? []).map((file) => file.path)
|
|
8225
8477
|
});
|
|
8226
8478
|
const findings = [...validateStagedTree(tree.value, limitsSnapshot.skillLimits), ...validationPlan.findings];
|
|
8479
|
+
const lengths = {
|
|
8480
|
+
bodyBytes: Buffer.byteLength(tree.value.body, "utf8"),
|
|
8481
|
+
fileBytes: tree.value.files.reduce((sum, file) => sum + Buffer.byteLength(file.content, "utf8"), 0),
|
|
8482
|
+
totalBytes: Buffer.byteLength(tree.value.body, "utf8") + tree.value.files.reduce((sum, file) => sum + Buffer.byteLength(file.content, "utf8"), 0)
|
|
8483
|
+
};
|
|
8227
8484
|
if (hasBlockingFinding(findings)) return {
|
|
8228
8485
|
ok: false,
|
|
8229
8486
|
code: findings.some((finding) => finding.rule === "limit") ? "merge-over-limit" : "merge-validation",
|
|
8230
8487
|
message: "staged tree failed the rule gate",
|
|
8231
8488
|
findings,
|
|
8232
|
-
lengths
|
|
8233
|
-
bodyBytes: Buffer.byteLength(tree.value.body, "utf8"),
|
|
8234
|
-
fileBytes: tree.value.files.reduce((sum, file) => sum + Buffer.byteLength(file.content, "utf8"), 0),
|
|
8235
|
-
totalBytes: Buffer.byteLength(tree.value.body, "utf8") + tree.value.files.reduce((sum, file) => sum + Buffer.byteLength(file.content, "utf8"), 0)
|
|
8236
|
-
}
|
|
8489
|
+
lengths
|
|
8237
8490
|
};
|
|
8238
8491
|
const commit = await commitStagedRun(options.io, options.home, stageKey, options.commitPort, {
|
|
8239
8492
|
origin: "foreground",
|
|
8240
8493
|
summary: options.summary,
|
|
8241
8494
|
now: options.now,
|
|
8495
|
+
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
8242
8496
|
...options.isBlocked === void 0 ? {} : { isBlocked: options.isBlocked },
|
|
8243
8497
|
...options.unit === void 0 ? {} : { unit: options.unit },
|
|
8244
|
-
|
|
8498
|
+
limits: limitsSnapshot.skillLimits,
|
|
8245
8499
|
validationPlan: {
|
|
8246
8500
|
planVersion: validationPlan.planVersion,
|
|
8247
8501
|
stageDigest: validationPlan.stageDigest,
|
|
8248
|
-
revision: validationPlan.revision
|
|
8502
|
+
revision: validationPlan.revision,
|
|
8503
|
+
manifestFiles: validationPlan.manifestFiles
|
|
8249
8504
|
},
|
|
8250
8505
|
...options.revisions?.targetRevision === void 0 ? {} : { targetRevision: options.revisions.targetRevision },
|
|
8251
8506
|
...options.revisions?.sourceRevisions === void 0 ? {} : { sourceRevisions: options.revisions.sourceRevisions },
|
|
@@ -8255,7 +8510,8 @@ async function runAuthoring(options) {
|
|
|
8255
8510
|
ok: true,
|
|
8256
8511
|
tree: tree.value,
|
|
8257
8512
|
validationPlan,
|
|
8258
|
-
commit
|
|
8513
|
+
commit,
|
|
8514
|
+
lengths
|
|
8259
8515
|
};
|
|
8260
8516
|
return {
|
|
8261
8517
|
ok: false,
|
|
@@ -8391,6 +8647,7 @@ function createChannelScanPorts(options) {
|
|
|
8391
8647
|
reuseStaging: true,
|
|
8392
8648
|
target: decision.target,
|
|
8393
8649
|
mode: decision.mode,
|
|
8650
|
+
sources: [row.a, row.b],
|
|
8394
8651
|
cards: options.cards,
|
|
8395
8652
|
commitPort: options.commitPort,
|
|
8396
8653
|
...options.unit === void 0 ? {} : { unit: options.unit },
|
|
@@ -8400,6 +8657,7 @@ function createChannelScanPorts(options) {
|
|
|
8400
8657
|
...options.limits === void 0 ? {} : { limits: options.limits },
|
|
8401
8658
|
promptVersion: MERGE_PROMPT_VERSION,
|
|
8402
8659
|
promptHash: promptHashOf(buildRound2Prompt(row, decision)),
|
|
8660
|
+
signal,
|
|
8403
8661
|
revisions: {
|
|
8404
8662
|
...decision.targetRevisionRef === void 0 ? {} : { targetRevision: decision.targetRevisionRef },
|
|
8405
8663
|
...decision.sourceRevisions === void 0 ? {} : { sourceRevisions: decision.sourceRevisions },
|
|
@@ -8430,6 +8688,7 @@ function createChannelScanPorts(options) {
|
|
|
8430
8688
|
...receipt?.unitId === void 0 ? {} : { unitId: receipt.unitId },
|
|
8431
8689
|
...receipt?.evidenceSha256 === void 0 ? {} : { evidenceSha256: receipt.evidenceSha256 },
|
|
8432
8690
|
...receipt?.validationPlan === void 0 ? {} : { validationPlan: receipt.validationPlan },
|
|
8691
|
+
...authored.lengths === void 0 ? {} : { lengths: authored.lengths },
|
|
8433
8692
|
...usage === void 0 ? {} : { usage },
|
|
8434
8693
|
archived: receipt?.archived ?? [],
|
|
8435
8694
|
target: {
|
|
@@ -8632,7 +8891,7 @@ async function runNewChildAuthoring(options, row, decision, signal) {
|
|
|
8632
8891
|
})),
|
|
8633
8892
|
manifestFiles,
|
|
8634
8893
|
existingSupport: (options.cards.find((card) => card.name === decision.target)?.files ?? []).map((file) => file.path),
|
|
8635
|
-
supportSources: options.cards.flatMap((card) => (card.files ?? []).map((file) => ({
|
|
8894
|
+
supportSources: options.cards.filter((card) => card.name === row.a || card.name === row.b).flatMap((card) => (card.files ?? []).map((file) => ({
|
|
8636
8895
|
path: file.path,
|
|
8637
8896
|
content: file.content,
|
|
8638
8897
|
source: card.name
|
|
@@ -8646,6 +8905,12 @@ async function runNewChildAuthoring(options, row, decision, signal) {
|
|
|
8646
8905
|
totalBytes: Buffer.byteLength(tree.value.body, "utf8") + tree.value.files.reduce((sum, file) => sum + Buffer.byteLength(file.content, "utf8"), 0)
|
|
8647
8906
|
};
|
|
8648
8907
|
const limit = findings.find((finding) => finding.rule === "limit");
|
|
8908
|
+
const plan = {
|
|
8909
|
+
planVersion: validationPlan.planVersion,
|
|
8910
|
+
stageDigest: validationPlan.stageDigest,
|
|
8911
|
+
revision: validationPlan.revision,
|
|
8912
|
+
manifestFiles: validationPlan.manifestFiles
|
|
8913
|
+
};
|
|
8649
8914
|
return limit === void 0 ? {
|
|
8650
8915
|
kind: "failed",
|
|
8651
8916
|
failure: {
|
|
@@ -8654,6 +8919,16 @@ async function runNewChildAuthoring(options, row, decision, signal) {
|
|
|
8654
8919
|
message: "staged tree failed the rule gate",
|
|
8655
8920
|
retryable: false,
|
|
8656
8921
|
retryFrom: "none"
|
|
8922
|
+
},
|
|
8923
|
+
validationPlan: plan,
|
|
8924
|
+
manifestFiles: validationPlan.manifestFiles,
|
|
8925
|
+
validationIssues: findings,
|
|
8926
|
+
criteria: {
|
|
8927
|
+
decision: "merge",
|
|
8928
|
+
validation: "required",
|
|
8929
|
+
commit: "never",
|
|
8930
|
+
archive: "none",
|
|
8931
|
+
evidence: "none"
|
|
8657
8932
|
}
|
|
8658
8933
|
} : {
|
|
8659
8934
|
kind: "failed",
|
|
@@ -8664,16 +8939,29 @@ async function runNewChildAuthoring(options, row, decision, signal) {
|
|
|
8664
8939
|
retryable: false,
|
|
8665
8940
|
retryFrom: "none",
|
|
8666
8941
|
lengths
|
|
8942
|
+
},
|
|
8943
|
+
validationPlan: plan,
|
|
8944
|
+
manifestFiles: validationPlan.manifestFiles,
|
|
8945
|
+
validationIssues: findings,
|
|
8946
|
+
criteria: {
|
|
8947
|
+
decision: "merge",
|
|
8948
|
+
validation: "required",
|
|
8949
|
+
commit: "never",
|
|
8950
|
+
archive: "none",
|
|
8951
|
+
evidence: "none"
|
|
8667
8952
|
}
|
|
8668
8953
|
};
|
|
8669
8954
|
}
|
|
8955
|
+
const parentRunId = typeof options.parentRunId === "function" ? options.parentRunId(options.runId) : options.parentRunId;
|
|
8670
8956
|
const commit = await commitStagedRun(options.io, options.home, stageKey, options.commitPort, {
|
|
8671
8957
|
origin: "foreground",
|
|
8672
8958
|
summary: "merge " + row.a + " + " + row.b,
|
|
8673
8959
|
now: options.now,
|
|
8960
|
+
signal,
|
|
8674
8961
|
...options.isBlocked === void 0 ? {} : { isBlocked: options.isBlocked },
|
|
8675
8962
|
...options.unit === void 0 ? {} : { unit: options.unit },
|
|
8676
8963
|
...options.limits === void 0 ? {} : { limits: options.limits },
|
|
8964
|
+
...parentRunId === void 0 ? {} : { parentRunId },
|
|
8677
8965
|
validationPlan: {
|
|
8678
8966
|
planVersion: validationPlan.planVersion,
|
|
8679
8967
|
stageDigest: validationPlan.stageDigest,
|
|
@@ -8707,6 +8995,22 @@ async function runNewChildAuthoring(options, row, decision, signal) {
|
|
|
8707
8995
|
...receipt?.unitId === void 0 ? {} : { unitId: receipt.unitId },
|
|
8708
8996
|
archived: receipt?.archived ?? [],
|
|
8709
8997
|
lengths,
|
|
8998
|
+
validationPlan: {
|
|
8999
|
+
planVersion: validationPlan.planVersion,
|
|
9000
|
+
stageDigest: validationPlan.stageDigest,
|
|
9001
|
+
revision: validationPlan.revision,
|
|
9002
|
+
manifestFiles: validationPlan.manifestFiles
|
|
9003
|
+
},
|
|
9004
|
+
...parentRunId === void 0 ? {} : { parentRunId },
|
|
9005
|
+
manifestFiles: validationPlan.manifestFiles,
|
|
9006
|
+
validationIssues: findings,
|
|
9007
|
+
criteria: {
|
|
9008
|
+
decision: "merge",
|
|
9009
|
+
validation: "required",
|
|
9010
|
+
commit: "required",
|
|
9011
|
+
archive: "sources",
|
|
9012
|
+
evidence: "receipt"
|
|
9013
|
+
},
|
|
8710
9014
|
target: {
|
|
8711
9015
|
requested: decision.target,
|
|
8712
9016
|
actual: commit.plan.target,
|
|
@@ -8777,6 +9081,11 @@ function failRun(stage, hint, next, result) {
|
|
|
8777
9081
|
text: formatMergeError(result.error)
|
|
8778
9082
|
};
|
|
8779
9083
|
}
|
|
9084
|
+
async function hostOnlyFailure(verb, capabilities) {
|
|
9085
|
+
if (capabilities === void 0) return void 0;
|
|
9086
|
+
const state = await capabilities.hostOnly();
|
|
9087
|
+
return state === void 0 ? void 0 : fail(verb, "e-323", "host data plane unavailable: " + state.code + (state.detail === void 0 ? "" : " (" + state.detail + ")"), "run /evolution merge doctor");
|
|
9088
|
+
}
|
|
8780
9089
|
/**
|
|
8781
9090
|
* The approved/fuse row that already holds a target. Pair/approve move a row to
|
|
8782
9091
|
* approved/fuse BEFORE their run starts, so this row IS the in-flight claim a second
|
|
@@ -8834,6 +9143,7 @@ function createMergeRuntime(deps) {
|
|
|
8834
9143
|
registry: deps.registry,
|
|
8835
9144
|
platformWrite: deps.platformWrite,
|
|
8836
9145
|
now: deps.now,
|
|
9146
|
+
...deps.model === void 0 ? {} : { model: deps.model },
|
|
8837
9147
|
...deps.isOwnerActive === void 0 ? {} : { isOwnerActive: deps.isOwnerActive },
|
|
8838
9148
|
...timeout === void 0 ? {} : { timeoutMinutes: timeout }
|
|
8839
9149
|
};
|
|
@@ -8853,11 +9163,22 @@ function createMergeRuntime(deps) {
|
|
|
8853
9163
|
if (command.verb === "pair") {
|
|
8854
9164
|
const signal = new AbortController().signal;
|
|
8855
9165
|
if (command.a === command.b) return fail("pair", "pair-same-source", "merge pair 需要两个不同技能源:" + command.a, "重新指定两个不同的 family-managed 技能");
|
|
8856
|
-
if ([command.a, command.b].some((name) => name.includes("/") || name.includes(":"))) return
|
|
9166
|
+
if ([command.a, command.b].some((name) => name.includes("/") || name.includes(":"))) return {
|
|
9167
|
+
ok: false,
|
|
9168
|
+
text: "merge: 技能名不能包含 provider/name 或 provider:name 语法:" + command.a + " / " + command.b + " (provider-conflict)\nusage: /evolution merge pair <a> <b>"
|
|
9169
|
+
};
|
|
8857
9170
|
if (agentless) return fail("pair", "e-305", errorText("e-305-this-invocation-carries-no", { a1: "pair" }), "在 GUI 或 CLI 的会话里运行 /evolution merge pair " + command.a + " " + command.b);
|
|
9171
|
+
const pairHost = await hostOnlyFailure("pair", deps.hostCapabilities);
|
|
9172
|
+
if (pairHost !== void 0) return pairHost;
|
|
8858
9173
|
const pairKeyValue = pairKey(command.a, command.b);
|
|
8859
9174
|
const inFlight = inFlightPairRun(await readRunMetas(deps.io, deps.home), pairKeyValue);
|
|
8860
|
-
|
|
9175
|
+
let ownerActive = false;
|
|
9176
|
+
if (inFlight !== void 0) if (deps.isOwnerActive !== void 0) ownerActive = await deps.isOwnerActive(inFlight.runId);
|
|
9177
|
+
else ownerActive = inFlight.pid === void 0 ? true : isProcessAlive(inFlight.pid);
|
|
9178
|
+
if (inFlight !== void 0 && ownerActive) return {
|
|
9179
|
+
ok: true,
|
|
9180
|
+
text: "merge pair 已在运行:" + inFlight.runId + ";作用域:family-managed 技能对,1 对,无 per-run 上限"
|
|
9181
|
+
};
|
|
8861
9182
|
const cards = await deps.cards(signal);
|
|
8862
9183
|
const cardA = cards.find((card) => card.name === command.a);
|
|
8863
9184
|
const cardB = cards.find((card) => card.name === command.b);
|
|
@@ -8869,12 +9190,14 @@ function createMergeRuntime(deps) {
|
|
|
8869
9190
|
b: command.b
|
|
8870
9191
|
}];
|
|
8871
9192
|
const existing = (await loadMergeState(deps.io, deps.home)).state.rows.find((row) => row.pairKey === pairKey(command.a, command.b));
|
|
8872
|
-
|
|
8873
|
-
|
|
9193
|
+
let cleared = false;
|
|
9194
|
+
if (existing?.state === "skipped" || existing?.state === "fuse" && existing.failure?.retryable === false) {
|
|
9195
|
+
if (!await removeRuntimeRow(deps.io, deps.home, existing.id, "superseded")) return fail("pair", "row-changed", "清理可替换行时发现并发修改:" + existing.id, "运行 /evolution merge status 后重试");
|
|
9196
|
+
cleared = true;
|
|
8874
9197
|
}
|
|
8875
9198
|
if (existing?.state === "verify" && existing.runId !== void 0 && await deps.isOwnerActive?.(existing.runId) === true) return fail("pair", "merge-pair-in-flight", "同一 pair 已在运行 " + existing.runId, "等待该 run 结束,或运行 /evolution merge status " + existing.runId, existing.runId);
|
|
8876
9199
|
if (existing?.state === "pending") return fail("pair", "pair-pending", "该 pair 已在待批准队列:" + existing.id, "先运行 /evolution merge approve/reject/discard " + existing.id, existing.runId);
|
|
8877
|
-
if (existing?.state === "approved" || existing?.state === "fuse") return fail("pair", "pair-fuse", "该 pair 已在待合并队列,可复用决策重跑", "运行 /evolution merge resume --retry " + (existing.runId ?? existing.id), existing.runId);
|
|
9200
|
+
if (!cleared && (existing?.state === "approved" || existing?.state === "fuse")) return fail("pair", "pair-fuse", "该 pair 已在待合并队列,可复用决策重跑", "运行 /evolution merge resume --retry " + (existing.runId ?? existing.id), existing.runId);
|
|
8878
9201
|
const forced = command.force === true ? forcedDecisionFor(command.a, command.b) : void 0;
|
|
8879
9202
|
const approval = deps.params.mergeApproval;
|
|
8880
9203
|
const result = await runScanCommand({
|
|
@@ -8914,7 +9237,7 @@ function createMergeRuntime(deps) {
|
|
|
8914
9237
|
if (!result.ok) return failRun("pair", result.message, "修复后重新发起 pair;run 报告见 /evolution merge report", result);
|
|
8915
9238
|
if ("accepted" in result) return {
|
|
8916
9239
|
ok: true,
|
|
8917
|
-
text: "merge pair 已启动:" + result.runId
|
|
9240
|
+
text: "merge pair 已启动:" + result.runId + ";作用域:family-managed 技能对,1 对,无 per-run 上限"
|
|
8918
9241
|
};
|
|
8919
9242
|
if (!("report" in result)) return fail("pair", "report-missing", "merge pair 已结束但没有写报告", "重新运行 pair,或运行 /evolution merge runs 查看运行");
|
|
8920
9243
|
return {
|
|
@@ -8924,6 +9247,8 @@ function createMergeRuntime(deps) {
|
|
|
8924
9247
|
}
|
|
8925
9248
|
if (command.verb === "scan") {
|
|
8926
9249
|
if (agentless) return fail("scan", "e-305", errorText("e-305-this-invocation-carries-no", { a1: "scan" }), "在 GUI 或 CLI 的会话里运行 /evolution merge scan");
|
|
9250
|
+
const scanHost = await hostOnlyFailure("scan", deps.hostCapabilities);
|
|
9251
|
+
if (scanHost !== void 0) return scanHost;
|
|
8927
9252
|
const signal = new AbortController().signal;
|
|
8928
9253
|
const cards = await deps.cards(signal);
|
|
8929
9254
|
const blocked = /* @__PURE__ */ new Set();
|
|
@@ -8980,7 +9305,7 @@ function createMergeRuntime(deps) {
|
|
|
8980
9305
|
if (!result.ok) return failRun("scan", result.message, "修复后重新 scan;run 报告见 /evolution merge report", result);
|
|
8981
9306
|
if ("accepted" in result) return {
|
|
8982
9307
|
ok: true,
|
|
8983
|
-
text: "merge scan 已启动:" + result.runId
|
|
9308
|
+
text: "merge scan 已启动:" + result.runId + ";作用域:family-managed 技能库,候选来源 engine screening,无 per-run 上限"
|
|
8984
9309
|
};
|
|
8985
9310
|
if (!("report" in result)) return fail("scan", "report-missing", "merge scan 已结束但没有写报告", "重新运行 scan,或运行 /evolution merge runs 查看运行");
|
|
8986
9311
|
return {
|
|
@@ -9031,7 +9356,7 @@ function createMergeRuntime(deps) {
|
|
|
9031
9356
|
if (!result.ok) return failRun("candidates", result.message, "修复后重试 candidates --retry", result);
|
|
9032
9357
|
if ("accepted" in result) return {
|
|
9033
9358
|
ok: true,
|
|
9034
|
-
text: "candidates --retry 已启动:" + result.runId
|
|
9359
|
+
text: "candidates --retry 已启动:" + result.runId + ";作用域:单条 verify 行重跑,无 per-run 上限"
|
|
9035
9360
|
};
|
|
9036
9361
|
if (!("report" in result)) return fail("candidates", "report-missing", "candidates --retry 已结束但没有写报告", "运行 /evolution merge runs 查看运行");
|
|
9037
9362
|
return {
|
|
@@ -9131,10 +9456,9 @@ function createMergeRuntime(deps) {
|
|
|
9131
9456
|
};
|
|
9132
9457
|
}
|
|
9133
9458
|
if (command.verb === "undo") {
|
|
9134
|
-
const
|
|
9135
|
-
if (
|
|
9136
|
-
|
|
9137
|
-
const undoRunId = receipt.parentRunId ?? receipt.runId;
|
|
9459
|
+
const receipts = (await readCommitReceipts(deps.io, deps.home)).filter((receipt) => (receipt.parentRunId ?? receipt.runId) === command.run && receipt.target === command.target);
|
|
9460
|
+
if (receipts.length === 0) return fail("undo", "receipt-not-found", "该 run 没有目标回执:" + command.run + " / " + command.target, "只有已提交的 run 可 undo;运行 /evolution merge runs 查看");
|
|
9461
|
+
const undoRunId = receipts[0].parentRunId ?? receipts[0].runId;
|
|
9138
9462
|
if (deps.registry.runs("merge").some((run) => run.id === undoRunId && run.supersededAt !== void 0)) return fail("undo", "run-superseded", "该 run 已因技能树 restore 失效,不能再 undo:" + undoRunId, "运行 /evolution merge runs 查看失效标记");
|
|
9139
9463
|
const unit = unitFor(deps);
|
|
9140
9464
|
const result = await runUndoOperation({
|
|
@@ -9143,7 +9467,7 @@ function createMergeRuntime(deps) {
|
|
|
9143
9467
|
runId: undoRunId,
|
|
9144
9468
|
port: skillLibraryUndoPort(deps.skills),
|
|
9145
9469
|
now: deps.now,
|
|
9146
|
-
|
|
9470
|
+
target: command.target,
|
|
9147
9471
|
...unit === void 0 ? {} : { finalizeUnit: (id) => unit.finalize.call(unit, id) }
|
|
9148
9472
|
});
|
|
9149
9473
|
return result.ok ? {
|
|
@@ -9241,6 +9565,7 @@ function createMergeRuntime(deps) {
|
|
|
9241
9565
|
cards,
|
|
9242
9566
|
channel: deps.channel,
|
|
9243
9567
|
parent,
|
|
9568
|
+
parentRunId: (runId) => runId,
|
|
9244
9569
|
allow: deps.allow,
|
|
9245
9570
|
...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
|
|
9246
9571
|
timeoutMs: deps.params.mergePairTimeoutMinutes * 6e4,
|
|
@@ -9255,7 +9580,7 @@ function createMergeRuntime(deps) {
|
|
|
9255
9580
|
if (!result.ok) return failRun("resume", result.message, "修复后重新 resume;run 报告见 /evolution merge report", result);
|
|
9256
9581
|
if ("accepted" in result) return {
|
|
9257
9582
|
ok: true,
|
|
9258
|
-
text: "merge resume 已启动:" + result.runId
|
|
9583
|
+
text: "merge resume 已启动:" + result.runId + ";作用域:单条待合并行重跑 agent2,无 per-run 上限"
|
|
9259
9584
|
};
|
|
9260
9585
|
if (!("report" in result)) return fail("resume", "report-missing", "merge resume 已结束但没有写报告", "重新运行 resume,或运行 /evolution merge runs 查看运行");
|
|
9261
9586
|
return {
|
|
@@ -9381,6 +9706,7 @@ function createMergeRuntime(deps) {
|
|
|
9381
9706
|
cards,
|
|
9382
9707
|
channel: deps.channel,
|
|
9383
9708
|
parent,
|
|
9709
|
+
parentRunId: (runId) => runId,
|
|
9384
9710
|
allow: deps.allow,
|
|
9385
9711
|
...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
|
|
9386
9712
|
timeoutMs: deps.params.mergePairTimeoutMinutes * 6e4,
|
|
@@ -9396,7 +9722,7 @@ function createMergeRuntime(deps) {
|
|
|
9396
9722
|
if (!result.ok) return failRun("approve", result.message, "修复后重新 approve;run 报告见 /evolution merge report", result);
|
|
9397
9723
|
if ("accepted" in result) return {
|
|
9398
9724
|
ok: true,
|
|
9399
|
-
text: "merge approve 已启动:" + result.runId
|
|
9725
|
+
text: "merge approve 已启动:" + result.runId + ";作用域:单条 pending 行批准后重跑 agent2,无 per-run 上限"
|
|
9400
9726
|
};
|
|
9401
9727
|
if (!("report" in result)) return fail("approve", "report-missing", "merge approve 已结束但没有写报告", "重新运行 approve,或运行 /evolution merge runs 查看运行");
|
|
9402
9728
|
return {
|
|
@@ -9440,6 +9766,31 @@ function createMergeRuntime(deps) {
|
|
|
9440
9766
|
} };
|
|
9441
9767
|
}
|
|
9442
9768
|
//#endregion
|
|
9769
|
+
//#region lib/types/host-capabilities.js
|
|
9770
|
+
/**
|
|
9771
|
+
* The host's data-plane capability as ONE value shared by snapshot, doctor, HTTP and run preflight.
|
|
9772
|
+
* The probe is injected so the Cordis wiring stays in index.ts; a rejected probe is the same E-323
|
|
9773
|
+
* answer as an unavailable one, never an unhandled promise.
|
|
9774
|
+
* @module @lmzhen/dsh-evolution-merge
|
|
9775
|
+
*/
|
|
9776
|
+
/**
|
|
9777
|
+
* Wrap one probe with the shared error normalization.
|
|
9778
|
+
* @param probe - the platform-specific availability check.
|
|
9779
|
+
* @returns the capability value consumed by snapshot and runtime.
|
|
9780
|
+
*/
|
|
9781
|
+
function createHostCapabilities(probe) {
|
|
9782
|
+
return { hostOnly: async () => {
|
|
9783
|
+
try {
|
|
9784
|
+
return await probe();
|
|
9785
|
+
} catch (error) {
|
|
9786
|
+
return {
|
|
9787
|
+
code: "E-323",
|
|
9788
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
9789
|
+
};
|
|
9790
|
+
}
|
|
9791
|
+
} };
|
|
9792
|
+
}
|
|
9793
|
+
//#endregion
|
|
9443
9794
|
//#region lib/types/params.js
|
|
9444
9795
|
/**
|
|
9445
9796
|
* Merge parameter resolution. E2/E3 values arrive as one JSON object; this is the only
|
|
@@ -10185,6 +10536,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10185
10536
|
if (v4.ok) {
|
|
10186
10537
|
const cleanup = await cleanupStage(io, home, Date.now(), new Map(v4.state.rows.map((row) => [row.id, { state: row.state }])));
|
|
10187
10538
|
if (cleanup.expired.length > 0 || cleanup.removed.length > 0) ctx.logger.info("merge stage cleanup: " + String(cleanup.expired.length) + " expired, " + String(cleanup.removed.length) + " removed, " + String(cleanup.orphans.length) + " orphan(s)");
|
|
10539
|
+
if (cleanup.failures.length > 0) ctx.logger.warn("merge stage cleanup failed for " + String(cleanup.failures.length) + " path(s): " + cleanup.failures.map((item) => item.path + "=" + item.message).join("; "));
|
|
10188
10540
|
const library = v4.state.rows.some((row) => row.state === "pending") ? await cards().catch(() => []) : [];
|
|
10189
10541
|
const live = library.length === 0 ? void 0 : liveRowFactsOf(library);
|
|
10190
10542
|
const reconciled = await reconcileMergeState({
|
|
@@ -10215,7 +10567,18 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10215
10567
|
}).catch((error) => {
|
|
10216
10568
|
ctx.logger.warn("merge state readiness failed: " + (error instanceof Error ? error.message : String(error)));
|
|
10217
10569
|
});
|
|
10218
|
-
const
|
|
10570
|
+
const platformChannel = createSubagentChannel(ctx);
|
|
10571
|
+
const channel = subagentChannelAdapter(platformChannel);
|
|
10572
|
+
const hostCapabilities = createHostCapabilities(async () => {
|
|
10573
|
+
const state = await platformChannel.prepare({
|
|
10574
|
+
kind: "reviewer",
|
|
10575
|
+
allow: ["skill"]
|
|
10576
|
+
});
|
|
10577
|
+
return state.kind === "unavailable" ? {
|
|
10578
|
+
code: "E-323",
|
|
10579
|
+
detail: state.reason
|
|
10580
|
+
} : void 0;
|
|
10581
|
+
});
|
|
10219
10582
|
const stage = new StageToolRegistry();
|
|
10220
10583
|
const engineInput = resolveEngineInput({ modelsRoot: params.mergeModelsRoot });
|
|
10221
10584
|
const engineConfig = engineConfigFromRoot(engineInput.modelsRoot);
|
|
@@ -10279,6 +10642,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10279
10642
|
runs: runs.runs("merge"),
|
|
10280
10643
|
approval: params.mergeApproval,
|
|
10281
10644
|
platformWrite,
|
|
10645
|
+
hostOnly: hostCapabilities.hostOnly,
|
|
10282
10646
|
live: async () => liveRowFactsOf(await readSkillCards(skills)),
|
|
10283
10647
|
history: async () => {
|
|
10284
10648
|
const items = /* @__PURE__ */ new Map();
|
|
@@ -10367,10 +10731,12 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10367
10731
|
now: () => Date.now(),
|
|
10368
10732
|
params,
|
|
10369
10733
|
platformWrite,
|
|
10734
|
+
...defaultModel === void 0 ? {} : { model: defaultModel.currentSelection().model },
|
|
10370
10735
|
channel,
|
|
10371
10736
|
parent: void 0,
|
|
10372
10737
|
allow: ["merge_stage"],
|
|
10373
10738
|
timeoutMinutes: params.mergeRunTimeoutMinutes,
|
|
10739
|
+
hostCapabilities,
|
|
10374
10740
|
isBlocked: async (name) => {
|
|
10375
10741
|
return await ctx.get("evolutionCurator")?.isBlocked?.(name) === true;
|
|
10376
10742
|
},
|
|
@@ -10415,4 +10781,4 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10415
10781
|
});
|
|
10416
10782
|
}
|
|
10417
10783
|
//#endregion
|
|
10418
|
-
export { Config, MERGE_DOCTOR_SCHEMA_VERSION, MERGE_MAX_ATTEMPTS, MERGE_PAIR_STATES, MERGE_PARAM_DEFAULTS, MERGE_PROMPT_VERSION, MERGE_QUEUES, MERGE_ROUTES, MERGE_STAGE_TOOL_NAME, MERGE_STATE_VERSION, STAGING_SCHEMA_VERSION, STANDBY_PROMPT, StageToolRegistry, apply, applyStageTool, approvePending, assembleStagedCommit, buildCommitPlan, buildRound1Prompt, buildRound2Prompt, buildStandbyPrompt, canRetry, cleanupExpiredStaging, commitReceiptPath, commitStagedRun, committedPairs, configureMergePresentation, copyStagedFile, countRows, createChannelScanPorts, createEngineRunner, createEngineScreening, createMergeCommand, createMergeRuntime, createMergeStageTool, createNewChildRound2, createReadOnlyMergeService, createStageForCards, createStaging, dropRow, dropRowV3, dropStaging, durableReceiptDir, durableReceiptPath, emptyMergeState, engineConfigFromRoot, ensureStaging, formatFacts, formatQueue, formatRunDetail, formatRunsView, formatSnapshotQueue, formatSnapshotStatus, formatStatus, handleReadOnlyMergeCommand, hasBlockingFinding, inject, isSkippedPair, listStagedFiles, liveRowFactsOf, loadMergeState, loadStateSchema, makeMergeRoutes, manifestFilesOf, mergeCommandHelp, mergeDoctor, mergeDoctorAvailability, mergeDoctorFact, mergeReportDigestPath, mergeReportPath, mergeRunIdsToSupersede, mergeStatePath, mergeUsage, missingManifestFile, moveRow, mutateMergeState, 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, transactRows, transitionLegacyForProjection, undoMergeReceipt, unitFor, upsertRow, validateStagedTree, withMergeRun, writeCommitReceipt, writeMergeReport, writeStagedFile };
|
|
10784
|
+
export { Config, MERGE_DOCTOR_SCHEMA_VERSION, MERGE_LIFECYCLE_VERBS, MERGE_MAX_ATTEMPTS, MERGE_PAIR_STATES, MERGE_PARAM_DEFAULTS, MERGE_PROMPT_VERSION, MERGE_QUEUES, MERGE_ROUTES, MERGE_STAGE_TOOL_NAME, MERGE_STATE_VERSION, STAGING_SCHEMA_VERSION, STANDBY_PROMPT, StageToolRegistry, apply, applyStageTool, approvePending, assembleStagedCommit, buildCommitPlan, buildRound1Prompt, buildRound2Prompt, buildStandbyPrompt, canRetry, cleanupExpiredStaging, commitReceiptPath, commitStagedRun, committedPairs, configureMergePresentation, copyStagedFile, countRows, createChannelScanPorts, createEngineRunner, createEngineScreening, createMergeCommand, createMergeRuntime, createMergeStageTool, createNewChildRound2, createReadOnlyMergeService, createStageForCards, createStaging, dropRow, dropRowV3, dropStaging, durableReceiptDir, durableReceiptPath, emptyMergeState, engineConfigFromRoot, ensureStaging, forcedDecisionFor, formatFacts, formatQueue, formatRunDetail, formatRunsView, formatSnapshotQueue, formatSnapshotStatus, formatStatus, handleReadOnlyMergeCommand, hasBlockingFinding, inject, isSkippedPair, listStagedFiles, liveRowFactsOf, loadMergeState, loadStateSchema, makeMergeRoutes, manifestFilesOf, mergeCommandHelp, mergeDoctor, mergeDoctorAvailability, mergeDoctorFact, mergeReportDigestPath, mergeReportPath, mergeRunIdsToSupersede, mergeStatePath, mergeUsage, missingManifestFile, moveRow, mutateMergeState, 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, transactRows, transitionLegacyForProjection, undoMergeReceipt, unitFor, upsertRow, validateStagedTree, withMergeRun, writeCommitReceipt, writeMergeReport, writeStagedFile };
|