@lmzhen/dsh-evolution-merge 0.39.1 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +406 -289
- package/lib/types/approve-command.d.ts +4 -0
- package/lib/types/approve-command.js +5 -4
- package/lib/types/domain/pair-ledger.d.ts +3 -1
- package/lib/types/domain/pair-ledger.js +2 -2
- package/lib/types/domain/row-run.d.ts +4 -2
- package/lib/types/domain/row-run.js +22 -10
- package/lib/types/domain/run-coordinator.d.ts +5 -1
- package/lib/types/domain/run-coordinator.js +35 -2
- package/lib/types/domain/schema4.d.ts +3 -1
- package/lib/types/domain/schema4.js +48 -42
- package/lib/types/domain/stage-aggregate.js +21 -3
- package/lib/types/domain/state-bootstrap.d.ts +3 -1
- package/lib/types/domain/state-bootstrap.js +95 -89
- package/lib/types/index.js +13 -4
- package/lib/types/pair-command.d.ts +3 -0
- package/lib/types/pair-command.js +1 -0
- package/lib/types/pending-actions.d.ts +6 -2
- package/lib/types/pending-actions.js +6 -6
- package/lib/types/resume-command.d.ts +3 -0
- package/lib/types/resume-command.js +4 -4
- package/lib/types/round2-outcome.d.ts +3 -1
- package/lib/types/round2-outcome.js +7 -7
- package/lib/types/run-shell.d.ts +8 -0
- package/lib/types/run-shell.js +41 -19
- package/lib/types/runtime.d.ts +16 -1
- package/lib/types/runtime.js +23 -17
- package/lib/types/scan-command.d.ts +3 -0
- package/lib/types/scan-command.js +1 -0
- package/lib/types/target-claim.js +8 -2
- package/lib/types/undo-operation.d.ts +3 -0
- package/package.json +4 -4
package/lib/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { dirname, join } from "node:path";
|
|
1
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { pairKeyOf, screen, selectCandidates } from "@lmzhen/dsh-evolution-merge-engine";
|
|
4
4
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
5
|
-
import { DEFAULT_SKILL_LIMITS, SkillLibrary, UnitTransactions, authorityFrom, contentHash, createSubagentChannel, errorText, evolutionHome, evolutionIoAdapter, executeUnitUndo, isPresent, isProcessAlive, newRunRegistry, newSkillLibrary, parseFrontmatter, planUnitUndoAcross, policyStageLimits, probeAbsent, probeList, probePresent, probeText, probeUnknown, readTreeLock, readUnitEvidence, readUnitJournal, registerOwnerSettings, reportsSweepLockTarget, revisionAnchorOf, revisionFingerprint, revisionSupportDigest, runsFile, scanContentThreats, skillLibraryUnitBackend, supportRefs, sweepReports, transactIo, validateFrontmatter, validateSupportPath } from "@lmzhen/dsh-evolution-core";
|
|
5
|
+
import { DEFAULT_SKILL_LIMITS, LeaseStore, SkillLibrary, UnitTransactions, authorityFrom, contentHash, createSubagentChannel, errorText, evolutionHome, evolutionIoAdapter, executeUnitUndo, isPresent, isProcessAlive, newRunRegistry, newSkillLibrary, parseFrontmatter, planUnitUndoAcross, policyStageLimits, probeAbsent, probeList, probePresent, probeText, probeUnknown, readTreeLock, readUnitEvidence, readUnitJournal, registerOwnerSettings, reportsSweepLockTarget, revisionAnchorOf, revisionFingerprint, revisionSupportDigest, runsFile, scanContentThreats, skillLibraryUnitBackend, supportRefs, sweepReports, transactIo, validateFrontmatter, validateSupportPath } from "@lmzhen/dsh-evolution-core";
|
|
6
6
|
import { HOST_ROUTES, json, methodNotAllowed, registerHostRoutes } from "@lmzhen/dsh-evolution-core/http-routes";
|
|
7
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
8
|
import { chmod, mkdir, open, readFile, stat, unlink } from "node:fs/promises";
|
|
@@ -1183,60 +1183,64 @@ async function readV4Readiness(io, home) {
|
|
|
1183
1183
|
return await inspect(io, home, parsed.body, parsed.rows);
|
|
1184
1184
|
}
|
|
1185
1185
|
/** Atomic readiness + V4 row write under one state-file lock. No production caller in 1b-2a. */
|
|
1186
|
-
async function transactMergeState(io, home, fn) {
|
|
1186
|
+
async function transactMergeState(io, home, fn, fence) {
|
|
1187
1187
|
let result;
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1188
|
+
const run = async () => {
|
|
1189
|
+
await transactIo(io, schema4StatePath(home), async (current) => {
|
|
1190
|
+
if (current === null) throw new V4BlockedError(blocked$2("merge-state-v4-required", "schema4 state is missing"));
|
|
1191
|
+
let value;
|
|
1192
|
+
try {
|
|
1193
|
+
value = JSON.parse(current);
|
|
1194
|
+
} catch {
|
|
1195
|
+
throw new V4BlockedError(blocked$2("merge-state-needs-repair", "merge state is not JSON"));
|
|
1196
|
+
}
|
|
1197
|
+
if (!object(value)) throw new V4BlockedError(blocked$2("merge-state-needs-repair", "merge state is not an object"));
|
|
1198
|
+
if (typeof value.schemaVersion === "number" && value.schemaVersion > 4) throw new V4BlockedError(blocked$2("merge-state-forward-only", "state schema " + String(value.schemaVersion) + " is newer than this build"));
|
|
1199
|
+
if (value.schemaVersion === 3) throw new V4BlockedError(blocked$2("merge-state-normalize-required", "schema3 state requires v4 normalize"));
|
|
1200
|
+
if (value.schemaVersion !== 4) throw new V4BlockedError(blocked$2("merge-state-needs-repair", "merge state is not schema4"));
|
|
1201
|
+
const parsed = parse(current);
|
|
1202
|
+
if (parsed === null) throw new V4BlockedError(blocked$2("merge-state-needs-repair", "schema4 state has an invalid row"));
|
|
1203
|
+
const ready = await inspect(io, home, parsed.body, parsed.rows);
|
|
1204
|
+
if (!ready.ok) throw new V4BlockedError(ready);
|
|
1205
|
+
let rows = parsed.rows;
|
|
1206
|
+
const apply = (write) => {
|
|
1207
|
+
const next = applyV4Write(rows, write, (/* @__PURE__ */ new Date()).toISOString());
|
|
1208
|
+
if (next.ok) rows = next.rows;
|
|
1209
|
+
return next;
|
|
1210
|
+
};
|
|
1211
|
+
result = await fn({
|
|
1212
|
+
rows: () => rows,
|
|
1213
|
+
createVerify: (row) => apply({
|
|
1214
|
+
kind: "createVerify",
|
|
1215
|
+
row
|
|
1216
|
+
}),
|
|
1217
|
+
createSkipped: (row) => apply({
|
|
1218
|
+
kind: "createSkipped",
|
|
1219
|
+
row
|
|
1220
|
+
}),
|
|
1221
|
+
transition: (id, event, expectedVersion) => apply({
|
|
1222
|
+
kind: "transition",
|
|
1223
|
+
id,
|
|
1224
|
+
event,
|
|
1225
|
+
expectedVersion
|
|
1226
|
+
}),
|
|
1227
|
+
remove: (id, terminal, unit, expectedVersion) => apply({
|
|
1228
|
+
kind: "remove",
|
|
1229
|
+
id,
|
|
1230
|
+
terminal,
|
|
1231
|
+
...unit === void 0 ? {} : { unit },
|
|
1232
|
+
expectedVersion
|
|
1233
|
+
})
|
|
1234
|
+
});
|
|
1235
|
+
return JSON.stringify({
|
|
1236
|
+
...parsed.body,
|
|
1237
|
+
schemaVersion: 4,
|
|
1238
|
+
rows
|
|
1239
|
+
}, null, 2) + "\n";
|
|
1233
1240
|
});
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
rows
|
|
1238
|
-
}, null, 2) + "\n";
|
|
1239
|
-
});
|
|
1241
|
+
};
|
|
1242
|
+
if (fence !== void 0) await fence.run(run);
|
|
1243
|
+
else await run();
|
|
1240
1244
|
if (result === void 0) throw new V4BlockedError(blocked$2("merge-state-needs-repair", "v4 transaction did not produce a result"));
|
|
1241
1245
|
return result;
|
|
1242
1246
|
}
|
|
@@ -1804,160 +1808,164 @@ async function readStateReadiness(io, home) {
|
|
|
1804
1808
|
return blocked("merge-state-migration-required", "v2 state requires v3 migration");
|
|
1805
1809
|
}
|
|
1806
1810
|
/** One readiness/migration gate; used by startup and every write entry. */
|
|
1807
|
-
async function ensureMergeStateReady(io, home) {
|
|
1811
|
+
async function ensureMergeStateReady(io, home, fence) {
|
|
1808
1812
|
let outcome;
|
|
1809
1813
|
let migrated = false;
|
|
1810
1814
|
try {
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
}
|
|
1831
|
-
const version = body.schemaVersion;
|
|
1832
|
-
if (typeof version === "number" && version > 3) {
|
|
1833
|
-
outcome = blocked("merge-state-forward-only", "state schema " + String(version) + " is newer than this build");
|
|
1834
|
-
return current;
|
|
1835
|
-
}
|
|
1836
|
-
if (version === 3) {
|
|
1837
|
-
const state = v3State(body);
|
|
1838
|
-
if (state === null) {
|
|
1839
|
-
outcome = blocked("merge-state-needs-repair", "v3 state has an invalid row");
|
|
1815
|
+
const run = async () => {
|
|
1816
|
+
await transactIo(io, mergeStatePath$1(home), async (current) => {
|
|
1817
|
+
if (current === null) {
|
|
1818
|
+
outcome = {
|
|
1819
|
+
ok: true,
|
|
1820
|
+
schema: 3,
|
|
1821
|
+
migrated: false,
|
|
1822
|
+
state: {
|
|
1823
|
+
schemaVersion: 3,
|
|
1824
|
+
rows: []
|
|
1825
|
+
},
|
|
1826
|
+
quarantine: [],
|
|
1827
|
+
report: await readReport(io, home)
|
|
1828
|
+
};
|
|
1829
|
+
return null;
|
|
1830
|
+
}
|
|
1831
|
+
const body = parseBody(current);
|
|
1832
|
+
if (body === null) {
|
|
1833
|
+
outcome = blocked("merge-state-needs-repair", "state file is not a JSON object");
|
|
1840
1834
|
return current;
|
|
1841
1835
|
}
|
|
1842
|
-
const
|
|
1843
|
-
if (
|
|
1844
|
-
outcome =
|
|
1836
|
+
const version = body.schemaVersion;
|
|
1837
|
+
if (typeof version === "number" && version > 3) {
|
|
1838
|
+
outcome = blocked("merge-state-forward-only", "state schema " + String(version) + " is newer than this build");
|
|
1845
1839
|
return current;
|
|
1846
1840
|
}
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1841
|
+
if (version === 3) {
|
|
1842
|
+
const state = v3State(body);
|
|
1843
|
+
if (state === null) {
|
|
1844
|
+
outcome = blocked("merge-state-needs-repair", "v3 state has an invalid row");
|
|
1845
|
+
return current;
|
|
1846
|
+
}
|
|
1847
|
+
const journal = await readJournal$1(io, home);
|
|
1848
|
+
if (journal !== null) {
|
|
1849
|
+
outcome = await completeJournal(io, home, journal, current);
|
|
1850
|
+
return current;
|
|
1851
|
+
}
|
|
1852
|
+
const report = await readReport(io, home);
|
|
1853
|
+
const marker = readMarker(body);
|
|
1854
|
+
if (body.migration !== void 0 && marker === null) {
|
|
1855
|
+
outcome = blocked("merge-state-needs-repair", "migration marker is invalid");
|
|
1856
|
+
return current;
|
|
1857
|
+
}
|
|
1858
|
+
if (report !== null && report.ready !== true) {
|
|
1859
|
+
outcome = blocked("merge-state-migration-blocked", "migration report is not ready");
|
|
1860
|
+
return current;
|
|
1861
|
+
}
|
|
1862
|
+
if (marker !== null && rowsSha(state.rows) === marker.targetSha256) {
|
|
1863
|
+
if (report === null) {
|
|
1864
|
+
outcome = blocked("merge-state-migration-blocked", "migration report is missing");
|
|
1865
|
+
return current;
|
|
1866
|
+
}
|
|
1867
|
+
for (const entry of marker.staging) if (!await io.exists(stagingIndexPath(home, entry.rowId))) await writeStagingIndex(io, home, entry);
|
|
1868
|
+
}
|
|
1869
|
+
outcome = {
|
|
1870
|
+
ok: true,
|
|
1871
|
+
schema: 3,
|
|
1872
|
+
migrated: false,
|
|
1873
|
+
state,
|
|
1874
|
+
quarantine: [],
|
|
1875
|
+
report
|
|
1876
|
+
};
|
|
1851
1877
|
return current;
|
|
1852
1878
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1879
|
+
const mapped = migrateV2ToV3(body);
|
|
1880
|
+
if (!mapped.ok) {
|
|
1881
|
+
outcome = blocked("merge-state-migration-blocked", mapped.message);
|
|
1855
1882
|
return current;
|
|
1856
1883
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1884
|
+
const sourceSha256 = hash(current);
|
|
1885
|
+
const backup = v2BackupPath(home);
|
|
1886
|
+
const existingBackup = await readTextOrNull(io, backup);
|
|
1887
|
+
if (existingBackup !== null && existingBackup !== current) {
|
|
1888
|
+
outcome = blocked("merge-state-migration-blocked", "v2 backup exists with different bytes");
|
|
1889
|
+
return current;
|
|
1890
|
+
}
|
|
1891
|
+
if (existingBackup === null) await transactIo(io, backup, () => current);
|
|
1892
|
+
try {
|
|
1893
|
+
await chmod(backup, 292);
|
|
1894
|
+
} catch {
|
|
1895
|
+
outcome = blocked("merge-state-migration-blocked", "v2 backup is not read-only");
|
|
1896
|
+
return current;
|
|
1897
|
+
}
|
|
1898
|
+
const staging = [];
|
|
1899
|
+
for (const row of body.rows) {
|
|
1900
|
+
if (typeof row.id !== "string" || typeof row.pairKey !== "string" || typeof row.stagingRef !== "string") continue;
|
|
1901
|
+
if (row.state !== "pending" && row.state !== "approved") continue;
|
|
1902
|
+
staging.push({
|
|
1903
|
+
rowId: row.id,
|
|
1904
|
+
pairKey: row.pairKey,
|
|
1905
|
+
ref: row.stagingRef,
|
|
1906
|
+
expiresAt: typeof row.stagingExpiresAt === "number" ? row.stagingExpiresAt : Date.now() + 7 * 864e5,
|
|
1907
|
+
...typeof row.runId === "string" ? { runId: row.runId } : {},
|
|
1908
|
+
createdAt: typeof row.updatedAt === "string" && !Number.isNaN(Date.parse(row.updatedAt)) ? Date.parse(row.updatedAt) : Date.now()
|
|
1909
|
+
});
|
|
1863
1910
|
}
|
|
1911
|
+
const marker = {
|
|
1912
|
+
migratedFromV2: true,
|
|
1913
|
+
sourceSha256,
|
|
1914
|
+
targetSha256: rowsSha(mapped.state.rows),
|
|
1915
|
+
staging,
|
|
1916
|
+
at: now()
|
|
1917
|
+
};
|
|
1918
|
+
const targetText = json$1({
|
|
1919
|
+
...mapped.state,
|
|
1920
|
+
migration: marker
|
|
1921
|
+
});
|
|
1922
|
+
const targetSha256 = hash(targetText);
|
|
1923
|
+
const journal = {
|
|
1924
|
+
version: 1,
|
|
1925
|
+
sourceSha256,
|
|
1926
|
+
targetSha256,
|
|
1927
|
+
rows: mapped.state.rows.length,
|
|
1928
|
+
quarantine: mapped.quarantine,
|
|
1929
|
+
staging,
|
|
1930
|
+
backupPath: backup,
|
|
1931
|
+
at: now()
|
|
1932
|
+
};
|
|
1933
|
+
await transactIo(io, migrationJournalPath(home), () => json$1(journal));
|
|
1934
|
+
for (const entry of staging) await writeStagingIndex(io, home, entry);
|
|
1935
|
+
if (mapped.quarantine.length > 0) await transactIo(io, quarantinePath(home), () => json$1(mapped.quarantine));
|
|
1936
|
+
const prepared = {
|
|
1937
|
+
version: 1,
|
|
1938
|
+
ready: false,
|
|
1939
|
+
migrated: true,
|
|
1940
|
+
sourceSha256,
|
|
1941
|
+
targetSha256,
|
|
1942
|
+
rows: mapped.state.rows.length,
|
|
1943
|
+
quarantined: mapped.quarantine.length,
|
|
1944
|
+
backupReadOnly: true,
|
|
1945
|
+
at: now(),
|
|
1946
|
+
steps: [
|
|
1947
|
+
"backup",
|
|
1948
|
+
"journal",
|
|
1949
|
+
"staging-index",
|
|
1950
|
+
"quarantine",
|
|
1951
|
+
"v3-write"
|
|
1952
|
+
]
|
|
1953
|
+
};
|
|
1954
|
+
await writeReport(io, home, prepared);
|
|
1955
|
+
migrated = true;
|
|
1864
1956
|
outcome = {
|
|
1865
1957
|
ok: true,
|
|
1866
1958
|
schema: 3,
|
|
1867
|
-
migrated:
|
|
1868
|
-
state,
|
|
1869
|
-
quarantine:
|
|
1870
|
-
report
|
|
1959
|
+
migrated: true,
|
|
1960
|
+
state: mapped.state,
|
|
1961
|
+
quarantine: mapped.quarantine,
|
|
1962
|
+
report: prepared
|
|
1871
1963
|
};
|
|
1872
|
-
return
|
|
1873
|
-
}
|
|
1874
|
-
const mapped = migrateV2ToV3(body);
|
|
1875
|
-
if (!mapped.ok) {
|
|
1876
|
-
outcome = blocked("merge-state-migration-blocked", mapped.message);
|
|
1877
|
-
return current;
|
|
1878
|
-
}
|
|
1879
|
-
const sourceSha256 = hash(current);
|
|
1880
|
-
const backup = v2BackupPath(home);
|
|
1881
|
-
const existingBackup = await readTextOrNull(io, backup);
|
|
1882
|
-
if (existingBackup !== null && existingBackup !== current) {
|
|
1883
|
-
outcome = blocked("merge-state-migration-blocked", "v2 backup exists with different bytes");
|
|
1884
|
-
return current;
|
|
1885
|
-
}
|
|
1886
|
-
if (existingBackup === null) await transactIo(io, backup, () => current);
|
|
1887
|
-
try {
|
|
1888
|
-
await chmod(backup, 292);
|
|
1889
|
-
} catch {
|
|
1890
|
-
outcome = blocked("merge-state-migration-blocked", "v2 backup is not read-only");
|
|
1891
|
-
return current;
|
|
1892
|
-
}
|
|
1893
|
-
const staging = [];
|
|
1894
|
-
for (const row of body.rows) {
|
|
1895
|
-
if (typeof row.id !== "string" || typeof row.pairKey !== "string" || typeof row.stagingRef !== "string") continue;
|
|
1896
|
-
if (row.state !== "pending" && row.state !== "approved") continue;
|
|
1897
|
-
staging.push({
|
|
1898
|
-
rowId: row.id,
|
|
1899
|
-
pairKey: row.pairKey,
|
|
1900
|
-
ref: row.stagingRef,
|
|
1901
|
-
expiresAt: typeof row.stagingExpiresAt === "number" ? row.stagingExpiresAt : Date.now() + 7 * 864e5,
|
|
1902
|
-
...typeof row.runId === "string" ? { runId: row.runId } : {},
|
|
1903
|
-
createdAt: typeof row.updatedAt === "string" && !Number.isNaN(Date.parse(row.updatedAt)) ? Date.parse(row.updatedAt) : Date.now()
|
|
1904
|
-
});
|
|
1905
|
-
}
|
|
1906
|
-
const marker = {
|
|
1907
|
-
migratedFromV2: true,
|
|
1908
|
-
sourceSha256,
|
|
1909
|
-
targetSha256: rowsSha(mapped.state.rows),
|
|
1910
|
-
staging,
|
|
1911
|
-
at: now()
|
|
1912
|
-
};
|
|
1913
|
-
const targetText = json$1({
|
|
1914
|
-
...mapped.state,
|
|
1915
|
-
migration: marker
|
|
1964
|
+
return targetText;
|
|
1916
1965
|
});
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
sourceSha256,
|
|
1921
|
-
targetSha256,
|
|
1922
|
-
rows: mapped.state.rows.length,
|
|
1923
|
-
quarantine: mapped.quarantine,
|
|
1924
|
-
staging,
|
|
1925
|
-
backupPath: backup,
|
|
1926
|
-
at: now()
|
|
1927
|
-
};
|
|
1928
|
-
await transactIo(io, migrationJournalPath(home), () => json$1(journal));
|
|
1929
|
-
for (const entry of staging) await writeStagingIndex(io, home, entry);
|
|
1930
|
-
if (mapped.quarantine.length > 0) await transactIo(io, quarantinePath(home), () => json$1(mapped.quarantine));
|
|
1931
|
-
const prepared = {
|
|
1932
|
-
version: 1,
|
|
1933
|
-
ready: false,
|
|
1934
|
-
migrated: true,
|
|
1935
|
-
sourceSha256,
|
|
1936
|
-
targetSha256,
|
|
1937
|
-
rows: mapped.state.rows.length,
|
|
1938
|
-
quarantined: mapped.quarantine.length,
|
|
1939
|
-
backupReadOnly: true,
|
|
1940
|
-
at: now(),
|
|
1941
|
-
steps: [
|
|
1942
|
-
"backup",
|
|
1943
|
-
"journal",
|
|
1944
|
-
"staging-index",
|
|
1945
|
-
"quarantine",
|
|
1946
|
-
"v3-write"
|
|
1947
|
-
]
|
|
1948
|
-
};
|
|
1949
|
-
await writeReport(io, home, prepared);
|
|
1950
|
-
migrated = true;
|
|
1951
|
-
outcome = {
|
|
1952
|
-
ok: true,
|
|
1953
|
-
schema: 3,
|
|
1954
|
-
migrated: true,
|
|
1955
|
-
state: mapped.state,
|
|
1956
|
-
quarantine: mapped.quarantine,
|
|
1957
|
-
report: prepared
|
|
1958
|
-
};
|
|
1959
|
-
return targetText;
|
|
1960
|
-
});
|
|
1966
|
+
};
|
|
1967
|
+
if (fence !== void 0) await fence.run(run);
|
|
1968
|
+
else await run();
|
|
1961
1969
|
} catch (error) {
|
|
1962
1970
|
return blocked("merge-state-migration-blocked", "state migration failed: " + (error instanceof Error ? error.message : String(error)));
|
|
1963
1971
|
}
|
|
@@ -2304,11 +2312,22 @@ async function presentStage(io, home, entry) {
|
|
|
2304
2312
|
state: "present"
|
|
2305
2313
|
});
|
|
2306
2314
|
}
|
|
2315
|
+
function safeRunTree(home, ref) {
|
|
2316
|
+
const name = ref.trim();
|
|
2317
|
+
if (name === "" || name.includes("\\") || isAbsolute(name)) return null;
|
|
2318
|
+
const parts = name.split("/");
|
|
2319
|
+
if (parts.some((part) => part === "" || part === "." || part === "..")) return null;
|
|
2320
|
+
return join(home, "merge", "runs", ...parts);
|
|
2321
|
+
}
|
|
2307
2322
|
async function dropStage(io, home, rowId, ref, onError) {
|
|
2308
2323
|
const index = join(home, "merge", "staging-index", encodeURIComponent(rowId) + ".json");
|
|
2309
2324
|
await io.remove(index).catch((error) => onError?.(index, error));
|
|
2310
2325
|
if (ref !== void 0 && ref !== "") {
|
|
2311
|
-
const tree =
|
|
2326
|
+
const tree = safeRunTree(home, ref);
|
|
2327
|
+
if (tree === null) {
|
|
2328
|
+
onError?.(join(home, "merge", "runs"), /* @__PURE__ */ new Error("invalid staging ref: " + JSON.stringify(ref)));
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2312
2331
|
await io.remove(tree).catch((error) => onError?.(tree, error));
|
|
2313
2332
|
}
|
|
2314
2333
|
}
|
|
@@ -2374,7 +2393,12 @@ async function cleanupStage(io, home, now, rows, limit = 100) {
|
|
|
2374
2393
|
for (const run of runNames.slice(0, limit)) {
|
|
2375
2394
|
const prefix = run + "/";
|
|
2376
2395
|
if ([...refs].some((ref) => ref === run || ref.startsWith(prefix))) continue;
|
|
2377
|
-
|
|
2396
|
+
const tree = safeRunTree(home, run);
|
|
2397
|
+
if (tree === null) {
|
|
2398
|
+
fail(join(runRoot, run), /* @__PURE__ */ new Error("invalid run dir name: " + JSON.stringify(run)));
|
|
2399
|
+
continue;
|
|
2400
|
+
}
|
|
2401
|
+
await io.remove(tree).catch((error) => fail(tree, error));
|
|
2378
2402
|
orphans.push(run);
|
|
2379
2403
|
}
|
|
2380
2404
|
return {
|
|
@@ -2421,7 +2445,7 @@ async function readPairLedger(io, home) {
|
|
|
2421
2445
|
};
|
|
2422
2446
|
}
|
|
2423
2447
|
/** 1b-2a write skeleton. Production stays on the v3 gate until 1b-2b. */
|
|
2424
|
-
function pairLedger(io, home) {
|
|
2448
|
+
function pairLedger(io, home, fence) {
|
|
2425
2449
|
return {
|
|
2426
2450
|
read: () => readPairLedger(io, home),
|
|
2427
2451
|
transact: async (fn) => {
|
|
@@ -2429,7 +2453,7 @@ function pairLedger(io, home) {
|
|
|
2429
2453
|
const normalized = await normalizePairLedger(io, home);
|
|
2430
2454
|
if (!normalized.ok) throw new Error(normalized.message ?? "normalize failed");
|
|
2431
2455
|
}
|
|
2432
|
-
return await transactMergeState(io, home, fn);
|
|
2456
|
+
return await transactMergeState(io, home, fn, fence);
|
|
2433
2457
|
},
|
|
2434
2458
|
normalize: () => normalizePairLedger(io, home)
|
|
2435
2459
|
};
|
|
@@ -2572,8 +2596,39 @@ async function recordPair(io, home, entry) {
|
|
|
2572
2596
|
function runMetaPath(home, runId) {
|
|
2573
2597
|
return join(home, "merge", "run-meta", encodeURIComponent(runId) + ".json");
|
|
2574
2598
|
}
|
|
2575
|
-
async function writeRunMeta(io, home, meta) {
|
|
2576
|
-
|
|
2599
|
+
async function writeRunMeta(io, home, meta, fence) {
|
|
2600
|
+
if (meta.revision !== void 0) {
|
|
2601
|
+
const currentRevision = (await readRunMeta(io, home, meta.runId))?.revision ?? 0;
|
|
2602
|
+
if (currentRevision !== meta.revision) throw new Error("run-meta revision conflict: expected " + String(meta.revision) + ", found " + String(currentRevision));
|
|
2603
|
+
}
|
|
2604
|
+
const write = async () => {
|
|
2605
|
+
await transactIo(io, runMetaPath(home, meta.runId), (current) => {
|
|
2606
|
+
let next = meta;
|
|
2607
|
+
let previousRevision = 0;
|
|
2608
|
+
if (current !== null) try {
|
|
2609
|
+
const prev = JSON.parse(current);
|
|
2610
|
+
previousRevision = typeof prev.revision === "number" ? prev.revision : 0;
|
|
2611
|
+
if (meta.revision !== void 0 && meta.revision !== previousRevision) throw new Error("run-meta revision conflict: expected " + String(meta.revision) + ", found " + String(previousRevision));
|
|
2612
|
+
if (prev.degraded === true) next = {
|
|
2613
|
+
...next,
|
|
2614
|
+
degraded: true
|
|
2615
|
+
};
|
|
2616
|
+
const terminal = prev.phase === "settled" || prev.phase === "cancelled" || prev.phase === "undone";
|
|
2617
|
+
const undoFromSettled = prev.phase === "settled" && next.phase === "undone";
|
|
2618
|
+
if (terminal && next.phase !== prev.phase && !undoFromSettled) next = {
|
|
2619
|
+
...next,
|
|
2620
|
+
phase: prev.phase,
|
|
2621
|
+
...prev.stopReason === void 0 ? {} : { stopReason: prev.stopReason }
|
|
2622
|
+
};
|
|
2623
|
+
} catch {}
|
|
2624
|
+
return JSON.stringify({
|
|
2625
|
+
...next,
|
|
2626
|
+
revision: previousRevision + 1
|
|
2627
|
+
}, null, 2) + "\n";
|
|
2628
|
+
});
|
|
2629
|
+
};
|
|
2630
|
+
if (fence !== void 0) await fence.run(write);
|
|
2631
|
+
else await write();
|
|
2577
2632
|
}
|
|
2578
2633
|
async function readRunMeta(io, home, runId) {
|
|
2579
2634
|
const raw = await readTextOrNull(io, runMetaPath(home, runId));
|
|
@@ -3087,6 +3142,7 @@ function releaseClaim(current, token) {
|
|
|
3087
3142
|
//#region lib/types/target-claim.js
|
|
3088
3143
|
/** Durable target claim: one approve writer per merge target at a time. */
|
|
3089
3144
|
const PROCESS_STARTED_AT = Date.now() - process.uptime() * 1e3;
|
|
3145
|
+
const TARGET_CLAIM_TTL_MS = 30 * 6e4;
|
|
3090
3146
|
function claimPath(home, target) {
|
|
3091
3147
|
return join(home, "merge", "claims", contentHash(target) + ".json");
|
|
3092
3148
|
}
|
|
@@ -3105,20 +3161,26 @@ function parseClaim(raw) {
|
|
|
3105
3161
|
async function withGuard(path, fn) {
|
|
3106
3162
|
const guard = path + ".guard";
|
|
3107
3163
|
await mkdir(dirname(path), { recursive: true });
|
|
3108
|
-
for (let attempt = 0; attempt < 500; attempt += 1)
|
|
3109
|
-
|
|
3110
|
-
await handle.writeFile(String(process.pid));
|
|
3111
|
-
await handle.close();
|
|
3164
|
+
for (let attempt = 0; attempt < 500; attempt += 1) {
|
|
3165
|
+
let handle;
|
|
3112
3166
|
try {
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
await
|
|
3167
|
+
handle = await open(guard, "wx");
|
|
3168
|
+
await handle.writeFile(String(process.pid));
|
|
3169
|
+
await handle.close();
|
|
3170
|
+
handle = void 0;
|
|
3171
|
+
try {
|
|
3172
|
+
return await fn();
|
|
3173
|
+
} finally {
|
|
3174
|
+
await unlink(guard).catch(() => {});
|
|
3175
|
+
}
|
|
3176
|
+
} catch (error) {
|
|
3177
|
+
await handle?.close().catch(() => {});
|
|
3178
|
+
if (error.code !== "EEXIST") await unlink(guard).catch(() => {});
|
|
3179
|
+
if (error.code !== "EEXIST") throw error;
|
|
3180
|
+
const pid = Number(await readFile(guard, "utf8").catch(() => ""));
|
|
3181
|
+
if (Number.isFinite(pid) && pid > 0 && !isProcessAlive(pid)) await unlink(guard).catch(() => {});
|
|
3182
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
3116
3183
|
}
|
|
3117
|
-
} catch (error) {
|
|
3118
|
-
if (error.code !== "EEXIST") throw error;
|
|
3119
|
-
const pid = Number(await readFile(guard, "utf8").catch(() => ""));
|
|
3120
|
-
if (Number.isFinite(pid) && pid > 0 && !isProcessAlive(pid)) await unlink(guard).catch(() => {});
|
|
3121
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
3122
3184
|
}
|
|
3123
3185
|
throw new Error("target-claim-guard-timeout");
|
|
3124
3186
|
}
|
|
@@ -3149,7 +3211,7 @@ async function acquireTargetClaim(io, home, target, now) {
|
|
|
3149
3211
|
let granted;
|
|
3150
3212
|
await compareAndSwap(io, path, (raw) => {
|
|
3151
3213
|
const current = parseClaim(raw);
|
|
3152
|
-
const claim = acquireClaim(current !== void 0 && current.pid === process.pid && current.startedAt !== void 0 && Math.abs(current.startedAt - PROCESS_STARTED_AT) > 1e3 || current !== void 0 && current.expiresAt > now && current.pid !== void 0 && current.pid !== process.pid && !isProcessAlive(current.pid) ? void 0 : current, now,
|
|
3214
|
+
const claim = acquireClaim(current !== void 0 && current.pid === process.pid && current.startedAt !== void 0 && Math.abs(current.startedAt - PROCESS_STARTED_AT) > 1e3 || current !== void 0 && current.expiresAt > now && current.pid !== void 0 && current.pid !== process.pid && !isProcessAlive(current.pid) ? void 0 : current, now, TARGET_CLAIM_TTL_MS, token, process.pid, PROCESS_STARTED_AT);
|
|
3153
3215
|
if (claim === void 0) return raw;
|
|
3154
3216
|
granted = claim;
|
|
3155
3217
|
return JSON.stringify(claim) + "\n";
|
|
@@ -6230,31 +6292,42 @@ async function withMergeRun(options) {
|
|
|
6230
6292
|
...options.parentRunId === void 0 ? {} : { parentRunId: options.parentRunId }
|
|
6231
6293
|
});
|
|
6232
6294
|
const startedAt = handle.startedAt;
|
|
6295
|
+
const writeMeta = async (meta) => {
|
|
6296
|
+
await writeRunMeta(options.io, options.home, meta, options.fence);
|
|
6297
|
+
};
|
|
6233
6298
|
const stage = (name, progress) => options.registry.stage(handle.id, name, progress);
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
pairKey:
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6299
|
+
try {
|
|
6300
|
+
await stage("screening", options.total === void 0 ? void 0 : {
|
|
6301
|
+
done: 0,
|
|
6302
|
+
total: options.total
|
|
6303
|
+
});
|
|
6304
|
+
await writeMeta({
|
|
6305
|
+
version: 1,
|
|
6306
|
+
runId: handle.id,
|
|
6307
|
+
pid: process.pid,
|
|
6308
|
+
...options.parentRunId === void 0 ? {} : { parentRunId: options.parentRunId },
|
|
6309
|
+
...options.pairKey === void 0 ? {} : { pairKey: options.pairKey },
|
|
6310
|
+
...options.stagingRef === void 0 ? {} : { stagingRef: options.stagingRef },
|
|
6311
|
+
...options.pairKey === void 0 ? {} : { pairs: [{
|
|
6312
|
+
pairKey: options.pairKey,
|
|
6313
|
+
state: "pending"
|
|
6314
|
+
}] },
|
|
6315
|
+
pairCount: options.pairCount ?? (options.pairKey === void 0 ? 0 : 1),
|
|
6316
|
+
phase: "running",
|
|
6317
|
+
approval: options.approval.mergeApproval ? "pending" : "approved",
|
|
6318
|
+
createdAt: new Date(startedAt).toISOString(),
|
|
6319
|
+
updatedAt: new Date(options.now()).toISOString()
|
|
6320
|
+
});
|
|
6321
|
+
} catch (error) {
|
|
6322
|
+
await options.registry.settle(handle.id, {
|
|
6323
|
+
state: "failed",
|
|
6324
|
+
failure: "merge run failed before body: " + (error instanceof Error ? error.message : String(error))
|
|
6325
|
+
}).catch(() => {});
|
|
6326
|
+
throw error;
|
|
6327
|
+
}
|
|
6255
6328
|
const writeFailureMeta = async (stopReason) => {
|
|
6256
6329
|
try {
|
|
6257
|
-
await
|
|
6330
|
+
await writeMeta({
|
|
6258
6331
|
version: 1,
|
|
6259
6332
|
runId: handle.id,
|
|
6260
6333
|
pid: process.pid,
|
|
@@ -6308,13 +6381,23 @@ async function withMergeRun(options) {
|
|
|
6308
6381
|
timedOut = true;
|
|
6309
6382
|
let drainTimer;
|
|
6310
6383
|
const drained = await Promise.race([bodyOutcome, new Promise((resolve) => {
|
|
6311
|
-
drainTimer = setTimeout(() => resolve({ kind: "drain-timeout" }), DETACHED_DRAIN_MS);
|
|
6384
|
+
drainTimer = setTimeout(() => resolve({ kind: "drain-timeout" }), options.drainMs ?? DETACHED_DRAIN_MS);
|
|
6312
6385
|
})]);
|
|
6313
6386
|
if (drainTimer !== void 0) clearTimeout(drainTimer);
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6387
|
+
if (drained.kind === "drain-timeout") {
|
|
6388
|
+
await options.registry.settle(handle.id, {
|
|
6389
|
+
state: "failed",
|
|
6390
|
+
failure: "timeout: body did not stop within 30s drain window"
|
|
6391
|
+
}).catch((error) => {
|
|
6392
|
+
(options.warn ?? ((message) => {
|
|
6393
|
+
console.warn(message);
|
|
6394
|
+
}))("evolution-merge: drain-timeout settle failed for " + handle.id + ": " + (error instanceof Error ? error.message : String(error)));
|
|
6395
|
+
});
|
|
6396
|
+
outcome = {
|
|
6397
|
+
kind: "error",
|
|
6398
|
+
error: /* @__PURE__ */ new Error("timeout: body did not stop within 30s drain window")
|
|
6399
|
+
};
|
|
6400
|
+
} else outcome = drained;
|
|
6318
6401
|
}
|
|
6319
6402
|
}
|
|
6320
6403
|
draft = outcome.kind === "draft" ? outcome.value : emptyDraft("run body failed: " + (outcome.error instanceof Error ? outcome.error.message : String(outcome.error)));
|
|
@@ -6398,10 +6481,12 @@ async function withMergeRun(options) {
|
|
|
6398
6481
|
pairKey: options.pairKey,
|
|
6399
6482
|
state: "pending"
|
|
6400
6483
|
}];
|
|
6401
|
-
await
|
|
6484
|
+
const currentMeta = await readRunMeta(options.io, options.home, handle.id);
|
|
6485
|
+
await writeMeta({
|
|
6402
6486
|
version: 1,
|
|
6403
6487
|
runId: handle.id,
|
|
6404
6488
|
pid: process.pid,
|
|
6489
|
+
...currentMeta?.revision === void 0 ? {} : { revision: currentMeta.revision },
|
|
6405
6490
|
...options.parentRunId === void 0 ? {} : { parentRunId: options.parentRunId },
|
|
6406
6491
|
...options.pairKey === void 0 ? {} : { pairKey: options.pairKey },
|
|
6407
6492
|
...options.stagingRef === void 0 ? {} : { stagingRef: options.stagingRef },
|
|
@@ -6428,7 +6513,11 @@ async function withMergeRun(options) {
|
|
|
6428
6513
|
};
|
|
6429
6514
|
};
|
|
6430
6515
|
if (options.detached === true) {
|
|
6431
|
-
finish().catch(() => {
|
|
6516
|
+
finish().catch((error) => {
|
|
6517
|
+
(options.warn ?? ((message) => {
|
|
6518
|
+
console.warn(message);
|
|
6519
|
+
}))("evolution-merge: detached run " + handle.id + " finish failed: " + (error instanceof Error ? error.message : String(error)));
|
|
6520
|
+
});
|
|
6432
6521
|
return {
|
|
6433
6522
|
ok: true,
|
|
6434
6523
|
accepted: true,
|
|
@@ -6444,6 +6533,7 @@ async function runScanCommand(options) {
|
|
|
6444
6533
|
io: options.io,
|
|
6445
6534
|
home: options.home,
|
|
6446
6535
|
registry: options.registry,
|
|
6536
|
+
...options.fence === void 0 ? {} : { fence: options.fence },
|
|
6447
6537
|
command: "scan",
|
|
6448
6538
|
approval: {
|
|
6449
6539
|
platformWrite: options.platformWrite,
|
|
@@ -6476,8 +6566,8 @@ async function runScanCommand(options) {
|
|
|
6476
6566
|
}
|
|
6477
6567
|
//#endregion
|
|
6478
6568
|
//#region lib/types/pending-actions.js
|
|
6479
|
-
async function act(io, home, now, id, type) {
|
|
6480
|
-
return await pairLedger(io, home).transact((tx) => {
|
|
6569
|
+
async function act(io, home, now, id, type, fence) {
|
|
6570
|
+
return await pairLedger(io, home, fence).transact((tx) => {
|
|
6481
6571
|
const row = tx.rows().find((item) => item.id === id);
|
|
6482
6572
|
if (row === void 0) return {
|
|
6483
6573
|
ok: false,
|
|
@@ -6519,24 +6609,36 @@ async function act(io, home, now, id, type) {
|
|
|
6519
6609
|
};
|
|
6520
6610
|
});
|
|
6521
6611
|
}
|
|
6522
|
-
async function approvePending(io, home, now, id) {
|
|
6523
|
-
return await act(io, home, now, id, "approve");
|
|
6612
|
+
async function approvePending(io, home, now, id, fence) {
|
|
6613
|
+
return await act(io, home, now, id, "approve", fence);
|
|
6524
6614
|
}
|
|
6525
|
-
async function rejectPending(io, home, now, id) {
|
|
6526
|
-
return await act(io, home, now, id, "reject");
|
|
6615
|
+
async function rejectPending(io, home, now, id, fence) {
|
|
6616
|
+
return await act(io, home, now, id, "reject", fence);
|
|
6527
6617
|
}
|
|
6528
6618
|
//#endregion
|
|
6529
6619
|
//#region lib/types/domain/row-run.js
|
|
6530
6620
|
/** Attach the in-flight child run to a row before its round2 starts (P1-8). */
|
|
6531
|
-
async function attachRunToRow(io, home, rowId, runId) {
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6621
|
+
async function attachRunToRow(io, home, rowId, runId, isActive, fence) {
|
|
6622
|
+
const leaseStore = new LeaseStore({
|
|
6623
|
+
io,
|
|
6624
|
+
home,
|
|
6625
|
+
ttlMs: 3e4
|
|
6626
|
+
});
|
|
6627
|
+
const lease = await leaseStore.acquire("foreground", "row-attach:" + rowId);
|
|
6628
|
+
if (!lease.ok) throw new Error("row attach is busy: " + rowId);
|
|
6629
|
+
try {
|
|
6630
|
+
if (!await pairLedger(io, home, fence).transact(async (tx) => {
|
|
6631
|
+
const current = tx.rows().find((row) => row.id === rowId);
|
|
6632
|
+
if (current === void 0) return false;
|
|
6633
|
+
if (current.runId !== void 0 && current.runId !== runId && await isActive?.(current.runId) === true) return false;
|
|
6634
|
+
return tx.transition(rowId, {
|
|
6635
|
+
type: "run-attached",
|
|
6636
|
+
runId
|
|
6637
|
+
}, current.version).ok;
|
|
6638
|
+
})) throw new Error("row is not attachable to run: " + rowId);
|
|
6639
|
+
} finally {
|
|
6640
|
+
await leaseStore.release(lease.token);
|
|
6641
|
+
}
|
|
6540
6642
|
}
|
|
6541
6643
|
//#endregion
|
|
6542
6644
|
//#region lib/types/domain/failure-route.js
|
|
@@ -6555,7 +6657,7 @@ function casExpiredHook(code) {
|
|
|
6555
6657
|
}
|
|
6556
6658
|
//#endregion
|
|
6557
6659
|
//#region lib/types/round2-outcome.js
|
|
6558
|
-
async function recordRound2Outcome(io, home, now, row, outcome, deferProjection = false) {
|
|
6660
|
+
async function recordRound2Outcome(io, home, now, row, outcome, deferProjection = false, fence) {
|
|
6559
6661
|
const reportExtra = {
|
|
6560
6662
|
...outcome.manifestFiles === void 0 ? {} : { manifestFiles: outcome.manifestFiles },
|
|
6561
6663
|
...outcome.criteria === void 0 ? {} : { criteria: outcome.criteria },
|
|
@@ -6568,7 +6670,7 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6568
6670
|
};
|
|
6569
6671
|
const target = outcome.target?.actual ?? outcome.target?.requested ?? row.decision?.target;
|
|
6570
6672
|
const finalize = async () => {
|
|
6571
|
-
if (!(await pairLedger(io, home).transact((tx) => {
|
|
6673
|
+
if (!(await pairLedger(io, home, fence).transact((tx) => {
|
|
6572
6674
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6573
6675
|
return current === void 0 ? {
|
|
6574
6676
|
ok: false,
|
|
@@ -6632,7 +6734,7 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6632
6734
|
};
|
|
6633
6735
|
if (failure.code === "merge-over-limit" || failure.code === "limit") {
|
|
6634
6736
|
const reason = "merge-over-limit";
|
|
6635
|
-
if (await pairLedger(io, home).transact((tx) => {
|
|
6737
|
+
if (await pairLedger(io, home, fence).transact((tx) => {
|
|
6636
6738
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6637
6739
|
return current === void 0 ? false : tx.transition(row.id, {
|
|
6638
6740
|
type: "skipped",
|
|
@@ -6665,7 +6767,7 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6665
6767
|
retryable: false,
|
|
6666
6768
|
retryFrom: "none"
|
|
6667
6769
|
};
|
|
6668
|
-
await pairLedger(io, home).transact((tx) => {
|
|
6770
|
+
await pairLedger(io, home, fence).transact((tx) => {
|
|
6669
6771
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6670
6772
|
if (current === void 0) return false;
|
|
6671
6773
|
return tx.transition(row.id, {
|
|
@@ -6691,7 +6793,7 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6691
6793
|
};
|
|
6692
6794
|
}
|
|
6693
6795
|
if (failure.code === "commit-failed" || failure.code === "validation-failed") {
|
|
6694
|
-
if (await pairLedger(io, home).transact((tx) => {
|
|
6796
|
+
if (await pairLedger(io, home, fence).transact((tx) => {
|
|
6695
6797
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6696
6798
|
return current === void 0 ? false : tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
6697
6799
|
})) {
|
|
@@ -6705,7 +6807,7 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6705
6807
|
}
|
|
6706
6808
|
}
|
|
6707
6809
|
if (failure.code === "source-gone") {
|
|
6708
|
-
if (await pairLedger(io, home).transact((tx) => {
|
|
6810
|
+
if (await pairLedger(io, home, fence).transact((tx) => {
|
|
6709
6811
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6710
6812
|
return current === void 0 ? false : tx.remove(row.id, "superseded", void 0, current.version).ok;
|
|
6711
6813
|
})) await dropStage(io, home, row.id, row.stagingRef);
|
|
@@ -6723,7 +6825,7 @@ async function recordRound2Outcome(io, home, now, row, outcome, deferProjection
|
|
|
6723
6825
|
}
|
|
6724
6826
|
};
|
|
6725
6827
|
}
|
|
6726
|
-
if (await pairLedger(io, home).transact((tx) => {
|
|
6828
|
+
if (await pairLedger(io, home, fence).transact((tx) => {
|
|
6727
6829
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6728
6830
|
if (current === void 0) return null;
|
|
6729
6831
|
const moved = tx.transition(row.id, {
|
|
@@ -6784,7 +6886,7 @@ async function runApproveCommand(options) {
|
|
|
6784
6886
|
if (options.agentless === true) return failure("approve", "e-305", errorText("e-305-this-invocation-carries-no", { a1: "approve" }), "在 GUI 或 CLI 的会话里运行 /evolution merge approve " + before.id);
|
|
6785
6887
|
const parentRunId = options.parentRunId ?? before.runId;
|
|
6786
6888
|
if (parentRunId === void 0) return failure("approve", "merge-parent-required", "缺 parent run id(E-305 提示:该行可能由无会话调用创建,或父 run 已丢失)", "在 agent 会话中执行 /evolution merge approve " + before.id, before.runId);
|
|
6787
|
-
const taken = await approvePending(options.io, options.home, options.now, options.rowId);
|
|
6889
|
+
const taken = await approvePending(options.io, options.home, options.now, options.rowId, options.fence);
|
|
6788
6890
|
if (!taken.ok) return failure("approve", "approve-" + taken.reason, taken.message, "重试;若行状态已变化,运行 /evolution merge status " + options.rowId, before.runId);
|
|
6789
6891
|
let row = taken.row;
|
|
6790
6892
|
const decision = row.decision;
|
|
@@ -6792,7 +6894,7 @@ async function runApproveCommand(options) {
|
|
|
6792
6894
|
if (options.ports.stage !== void 0) {
|
|
6793
6895
|
const staged = await options.ports.stage(row, decision);
|
|
6794
6896
|
if (!staged.ok) return failure("approve", "stage-failed", staged.message, "检查暂存目录后重试;不影响已记录的决策", row.runId);
|
|
6795
|
-
const fused = await pairLedger(options.io, options.home).transact((tx) => {
|
|
6897
|
+
const fused = await pairLedger(options.io, options.home, options.fence).transact((tx) => {
|
|
6796
6898
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
6797
6899
|
if (current === void 0) return null;
|
|
6798
6900
|
const moved = tx.transition(row.id, {
|
|
@@ -6810,6 +6912,7 @@ async function runApproveCommand(options) {
|
|
|
6810
6912
|
io: options.io,
|
|
6811
6913
|
home: options.home,
|
|
6812
6914
|
registry: options.registry,
|
|
6915
|
+
...options.fence === void 0 ? {} : { fence: options.fence },
|
|
6813
6916
|
command: "approve",
|
|
6814
6917
|
approval: {
|
|
6815
6918
|
platformWrite: options.platformWrite,
|
|
@@ -6821,7 +6924,7 @@ async function runApproveCommand(options) {
|
|
|
6821
6924
|
now: options.now,
|
|
6822
6925
|
...options.detached === true ? { detached: true } : {},
|
|
6823
6926
|
async body(ctx) {
|
|
6824
|
-
await attachRunToRow(options.io, options.home, row.id, ctx.runId);
|
|
6927
|
+
await attachRunToRow(options.io, options.home, row.id, ctx.runId, options.isOwnerActive, options.fence);
|
|
6825
6928
|
const stage = (name) => ctx.stage(name, {
|
|
6826
6929
|
done: 0,
|
|
6827
6930
|
total: 1
|
|
@@ -6854,7 +6957,7 @@ async function runApproveCommand(options) {
|
|
|
6854
6957
|
},
|
|
6855
6958
|
notes: []
|
|
6856
6959
|
};
|
|
6857
|
-
const record = await recordRound2Outcome(options.io, options.home, options.now, row, outcome, true);
|
|
6960
|
+
const record = await recordRound2Outcome(options.io, options.home, options.now, row, outcome, true, options.fence);
|
|
6858
6961
|
return {
|
|
6859
6962
|
...base,
|
|
6860
6963
|
stopReason: record.merged ? "completed" : "failed",
|
|
@@ -6891,6 +6994,7 @@ async function runResumeCommand(options) {
|
|
|
6891
6994
|
io: options.io,
|
|
6892
6995
|
home: options.home,
|
|
6893
6996
|
registry: options.registry,
|
|
6997
|
+
...options.fence === void 0 ? {} : { fence: options.fence },
|
|
6894
6998
|
approval: options.approval,
|
|
6895
6999
|
platformWrite: options.platformWrite,
|
|
6896
7000
|
now: options.now,
|
|
@@ -6926,7 +7030,7 @@ async function runResumeCommand(options) {
|
|
|
6926
7030
|
if (selected.state === "approved" && options.ports.stage !== void 0) {
|
|
6927
7031
|
const staged = await options.ports.stage(selected, decision);
|
|
6928
7032
|
if (!staged.ok) return failure("resume", "stage-failed", staged.message, "检查暂存目录后重试;不影响已记录的决策", selected.runId);
|
|
6929
|
-
const fused = await pairLedger(options.io, options.home).transact((tx) => {
|
|
7033
|
+
const fused = await pairLedger(options.io, options.home, options.fence).transact((tx) => {
|
|
6930
7034
|
const current = tx.rows().find((item) => item.id === selected.id);
|
|
6931
7035
|
if (current === void 0) return null;
|
|
6932
7036
|
const moved = tx.transition(selected.id, {
|
|
@@ -6955,7 +7059,7 @@ async function runResumeCommand(options) {
|
|
|
6955
7059
|
now: options.now,
|
|
6956
7060
|
...options.detached === true ? { detached: true } : {},
|
|
6957
7061
|
async body(ctx) {
|
|
6958
|
-
await attachRunToRow(options.io, options.home, row.id, ctx.runId);
|
|
7062
|
+
await attachRunToRow(options.io, options.home, row.id, ctx.runId, options.isOwnerActive, options.fence);
|
|
6959
7063
|
await ctx.stage("author", {
|
|
6960
7064
|
done: 0,
|
|
6961
7065
|
total: 1
|
|
@@ -6975,7 +7079,7 @@ async function runResumeCommand(options) {
|
|
|
6975
7079
|
}
|
|
6976
7080
|
};
|
|
6977
7081
|
}
|
|
6978
|
-
const record = await recordRound2Outcome(options.io, options.home, options.now, row, outcome, true);
|
|
7082
|
+
const record = await recordRound2Outcome(options.io, options.home, options.now, row, outcome, true, options.fence);
|
|
6979
7083
|
return {
|
|
6980
7084
|
stopReason: record.merged ? "completed" : "failed",
|
|
6981
7085
|
scope: {
|
|
@@ -9229,7 +9333,7 @@ async function hostOnlyFailure(verb, capabilities) {
|
|
|
9229
9333
|
* same-target run would collide with (the pair itself is guarded by run-meta).
|
|
9230
9334
|
*/
|
|
9231
9335
|
function busyTarget(rows, target, excludeRowId) {
|
|
9232
|
-
const row = rows.find((item) => item.id !== excludeRowId && (item.state === "approved" || item.state === "fuse") && item.decision?.target === target);
|
|
9336
|
+
const row = rows.find((item) => item.id !== excludeRowId && (item.state === "approved" || item.state === "fuse") && item.decision?.target === target && !(item.failure !== void 0 && item.failure.retryable !== true));
|
|
9233
9337
|
return row === void 0 ? void 0 : {
|
|
9234
9338
|
id: row.id,
|
|
9235
9339
|
...row.runId === void 0 ? {} : { runId: row.runId }
|
|
@@ -9249,8 +9353,8 @@ function forcedDecisionFor(a, b) {
|
|
|
9249
9353
|
bSummary: "B: " + sorted[1] + " 的现有技能面。"
|
|
9250
9354
|
};
|
|
9251
9355
|
}
|
|
9252
|
-
async function removeRuntimeRow(io, home, id, event, unitId) {
|
|
9253
|
-
return await pairLedger(io, home).transact((tx) => {
|
|
9356
|
+
async function removeRuntimeRow(io, home, id, event, unitId, fence) {
|
|
9357
|
+
return await pairLedger(io, home, fence).transact((tx) => {
|
|
9254
9358
|
const current = tx.rows().find((item) => item.id === id);
|
|
9255
9359
|
if (current === void 0) return false;
|
|
9256
9360
|
const unit = event === "committed" ? {
|
|
@@ -9272,6 +9376,7 @@ function unitFor(deps) {
|
|
|
9272
9376
|
});
|
|
9273
9377
|
}
|
|
9274
9378
|
function createMergeRuntime(deps) {
|
|
9379
|
+
const ledger = () => pairLedger(deps.io, deps.home, deps.fence);
|
|
9275
9380
|
const readOnly = createReadOnlyMergeService(deps.io, deps.home);
|
|
9276
9381
|
const timeout = deps.timeoutMinutes > 0 ? deps.timeoutMinutes : void 0;
|
|
9277
9382
|
const paramsOf = () => {
|
|
@@ -9288,6 +9393,7 @@ function createMergeRuntime(deps) {
|
|
|
9288
9393
|
now: deps.now,
|
|
9289
9394
|
...deps.model === void 0 ? {} : { model: deps.model },
|
|
9290
9395
|
...deps.isOwnerActive === void 0 ? {} : { isOwnerActive: deps.isOwnerActive },
|
|
9396
|
+
...deps.fence === void 0 ? {} : { fence: deps.fence },
|
|
9291
9397
|
...timeout === void 0 ? {} : { timeoutMinutes: timeout }
|
|
9292
9398
|
});
|
|
9293
9399
|
const stage = deps.stage;
|
|
@@ -9336,7 +9442,7 @@ function createMergeRuntime(deps) {
|
|
|
9336
9442
|
const existing = (await loadMergeState(deps.io, deps.home)).state.rows.find((row) => row.pairKey === pairKey(command.a, command.b));
|
|
9337
9443
|
let cleared = false;
|
|
9338
9444
|
if (existing?.state === "skipped" || existing?.state === "fuse" && existing.failure?.retryable === false) {
|
|
9339
|
-
if (!await removeRuntimeRow(deps.io, deps.home, existing.id, "superseded")) return fail("pair", "row-changed", "清理可替换行时发现并发修改:" + existing.id, "运行 /evolution merge status 后重试");
|
|
9445
|
+
if (!await removeRuntimeRow(deps.io, deps.home, existing.id, "superseded", void 0, deps.fence)) return fail("pair", "row-changed", "清理可替换行时发现并发修改:" + existing.id, "运行 /evolution merge status 后重试");
|
|
9340
9446
|
cleared = true;
|
|
9341
9447
|
}
|
|
9342
9448
|
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);
|
|
@@ -9511,7 +9617,7 @@ function createMergeRuntime(deps) {
|
|
|
9511
9617
|
if (command.verb === "reject") {
|
|
9512
9618
|
const row = (await loadMergeState(deps.io, deps.home)).state.rows.find((item) => item.id === command.id);
|
|
9513
9619
|
if (row === void 0) return fail("reject", "row-not-found", "找不到合并行 " + command.id, "运行 /evolution merge pending 查看待处理行", command.id);
|
|
9514
|
-
if (!await
|
|
9620
|
+
if (!await ledger().transact((tx) => {
|
|
9515
9621
|
const current = tx.rows().find((item) => item.id === row.id);
|
|
9516
9622
|
if (current === void 0 || current.state !== "pending") return false;
|
|
9517
9623
|
return tx.transition(row.id, {
|
|
@@ -9593,7 +9699,7 @@ function createMergeRuntime(deps) {
|
|
|
9593
9699
|
const row = (await loadMergeState(deps.io, deps.home)).state.rows.find((item) => item.id === command.id);
|
|
9594
9700
|
if (row === void 0) return fail("discard", "row-not-found", "找不到合并行 " + command.id, "运行 /evolution merge status 查看当前队列", command.id);
|
|
9595
9701
|
if (row.state !== "pending" && row.state !== "approved" && row.state !== "fuse") return fail("discard", "row-not-discardable", "该行状态是 " + row.state + ",不可丢弃:" + command.id, "只有 pending/approved/fuse 行可丢弃", row.runId);
|
|
9596
|
-
if (!await removeRuntimeRow(deps.io, deps.home, row.id, "superseded")) return fail("discard", "row-changed", "丢弃时发现并发修改:" + command.id, "运行 /evolution merge status " + command.id + " 后重试");
|
|
9702
|
+
if (!await removeRuntimeRow(deps.io, deps.home, row.id, "superseded", void 0, deps.fence)) return fail("discard", "row-changed", "丢弃时发现并发修改:" + command.id, "运行 /evolution merge status " + command.id + " 后重试");
|
|
9597
9703
|
return {
|
|
9598
9704
|
ok: true,
|
|
9599
9705
|
text: "已丢弃合并行:" + command.id
|
|
@@ -9612,6 +9718,7 @@ function createMergeRuntime(deps) {
|
|
|
9612
9718
|
port: skillLibraryUndoPort(deps.skills),
|
|
9613
9719
|
now: deps.now,
|
|
9614
9720
|
target: command.target,
|
|
9721
|
+
...deps.fence === void 0 ? {} : { fence: deps.fence },
|
|
9615
9722
|
...unit === void 0 ? {} : { finalizeUnit: (id) => unit.finalize.call(unit, id) }
|
|
9616
9723
|
});
|
|
9617
9724
|
return result.ok ? {
|
|
@@ -9622,7 +9729,7 @@ function createMergeRuntime(deps) {
|
|
|
9622
9729
|
if (command.verb === "skipped" && command.restore !== void 0) {
|
|
9623
9730
|
const row = (await loadMergeState(deps.io, deps.home)).state.rows.find((item) => item.id === command.restore);
|
|
9624
9731
|
if (row === void 0 || row.state !== "skipped") return fail("skipped", "row-not-found", "找不到可恢复的 skipped 行:" + command.restore, "运行 /evolution merge skipped 查看跳过队列", command.restore);
|
|
9625
|
-
if (!await removeRuntimeRow(deps.io, deps.home, row.id, "restored")) return fail("skipped", "row-changed", "恢复时发现并发修改:" + row.id, "运行 /evolution merge status 后重试");
|
|
9732
|
+
if (!await removeRuntimeRow(deps.io, deps.home, row.id, "restored", void 0, deps.fence)) return fail("skipped", "row-changed", "恢复时发现并发修改:" + row.id, "运行 /evolution merge status 后重试");
|
|
9626
9733
|
return {
|
|
9627
9734
|
ok: true,
|
|
9628
9735
|
text: "已恢复参与筛选:" + row.id
|
|
@@ -9632,7 +9739,7 @@ function createMergeRuntime(deps) {
|
|
|
9632
9739
|
if (command.clear !== void 0) {
|
|
9633
9740
|
const cleared = (await loadMergeState(deps.io, deps.home)).state.rows.find((item) => item.id === command.clear);
|
|
9634
9741
|
if (cleared === void 0) return fail("resume", "row-not-found", "找不到要清除的行:" + command.clear, "运行 /evolution merge status 查看当前队列");
|
|
9635
|
-
if (!await removeRuntimeRow(deps.io, deps.home, cleared.id, "superseded")) return fail("resume", "row-changed", "清除时发现并发修改:" + cleared.id, "运行 /evolution merge status 后重试");
|
|
9742
|
+
if (!await removeRuntimeRow(deps.io, deps.home, cleared.id, "superseded", void 0, deps.fence)) return fail("resume", "row-changed", "清除时发现并发修改:" + cleared.id, "运行 /evolution merge status 后重试");
|
|
9636
9743
|
return {
|
|
9637
9744
|
ok: true,
|
|
9638
9745
|
text: "已清除合并行:" + cleared.id
|
|
@@ -9753,12 +9860,12 @@ function createMergeRuntime(deps) {
|
|
|
9753
9860
|
if (targetCardNow !== void 0 && (targetCardNow.protectionUnknown === true || targetCardNow.protectedBy != null)) return fail("approve", "target-protected", "目标技能「" + target + "」在决策后变为受保护,拒绝提交", "解除保护或改选其它目标", candidate.runId);
|
|
9754
9861
|
const missingSource = [candidate.a, candidate.b].find((name) => !cards.some((card) => card.name === name && card.managed === true));
|
|
9755
9862
|
if (missingSource !== void 0) {
|
|
9756
|
-
if (!await removeRuntimeRow(deps.io, deps.home, candidate.id, "superseded")) return fail("approve", "row-changed", "source-gone,但行已被并发修改,未清理:" + candidate.id, "运行 /evolution merge status " + candidate.id + " 后重试");
|
|
9863
|
+
if (!await removeRuntimeRow(deps.io, deps.home, candidate.id, "superseded", void 0, deps.fence)) return fail("approve", "row-changed", "source-gone,但行已被并发修改,未清理:" + candidate.id, "运行 /evolution merge status " + candidate.id + " 后重试");
|
|
9757
9864
|
return fail("approve", "source-gone", missingSource + " 已不在 family-managed 技能库,已从 active 队列清除", "重新 pair 该技能对;剩余行用 /evolution merge discard " + candidate.id, candidate.runId);
|
|
9758
9865
|
}
|
|
9759
9866
|
const sourceExpected = candidate.decision?.sourceRevisionRef ?? candidate.sourceRevisionRef;
|
|
9760
9867
|
if (revisionMismatch(candidate.decision?.targetRevisionRef ?? candidate.targetRevisionRef, targetRevisionRefOf(cards, candidate.decision.target)) !== null) {
|
|
9761
|
-
await
|
|
9868
|
+
await ledger().transact((tx) => {
|
|
9762
9869
|
const current = tx.rows().find((item) => item.id === candidate.id);
|
|
9763
9870
|
return current === void 0 ? false : tx.transition(candidate.id, {
|
|
9764
9871
|
type: "expired",
|
|
@@ -9772,7 +9879,7 @@ function createMergeRuntime(deps) {
|
|
|
9772
9879
|
candidate.a,
|
|
9773
9880
|
candidate.b
|
|
9774
9881
|
])) {
|
|
9775
|
-
await
|
|
9882
|
+
await ledger().transact((tx) => {
|
|
9776
9883
|
const current = tx.rows().find((item) => item.id === candidate.id);
|
|
9777
9884
|
return current === void 0 ? false : tx.transition(candidate.id, {
|
|
9778
9885
|
type: "expired",
|
|
@@ -9782,7 +9889,7 @@ function createMergeRuntime(deps) {
|
|
|
9782
9889
|
return fail("approve", "source-changed", "源技能内容在决策后变化,拒绝照跑", "重新 pair,或 /evolution merge discard " + candidate.id, candidate.runId);
|
|
9783
9890
|
}
|
|
9784
9891
|
if (revisionMismatch(sourceExpected, sourceRevisionRefOf(cards, candidate.a, candidate.b)) !== null) {
|
|
9785
|
-
await
|
|
9892
|
+
await ledger().transact((tx) => {
|
|
9786
9893
|
const current = tx.rows().find((item) => item.id === candidate.id);
|
|
9787
9894
|
return current === void 0 ? false : tx.transition(candidate.id, {
|
|
9788
9895
|
type: "expired",
|
|
@@ -9793,7 +9900,7 @@ function createMergeRuntime(deps) {
|
|
|
9793
9900
|
}
|
|
9794
9901
|
const currentRevision = sourceRevisionOf(cards, candidate.a, candidate.b);
|
|
9795
9902
|
if (candidate.sourceRevision !== void 0 && currentRevision !== void 0 && currentRevision !== candidate.sourceRevision) {
|
|
9796
|
-
if (!await
|
|
9903
|
+
if (!await ledger().transact((tx) => {
|
|
9797
9904
|
const current = tx.rows().find((item) => item.id === candidate.id);
|
|
9798
9905
|
return current === void 0 ? false : tx.transition(candidate.id, {
|
|
9799
9906
|
type: "expired",
|
|
@@ -9804,7 +9911,7 @@ function createMergeRuntime(deps) {
|
|
|
9804
9911
|
}
|
|
9805
9912
|
const currentTargetRevision = targetRevisionOf(cards, candidate.decision.target);
|
|
9806
9913
|
if (candidate.targetRevision !== void 0 && currentTargetRevision !== void 0 && currentTargetRevision !== candidate.targetRevision) {
|
|
9807
|
-
if (!await
|
|
9914
|
+
if (!await ledger().transact((tx) => {
|
|
9808
9915
|
const current = tx.rows().find((item) => item.id === candidate.id);
|
|
9809
9916
|
return current === void 0 ? false : tx.transition(candidate.id, {
|
|
9810
9917
|
type: "expired",
|
|
@@ -9820,7 +9927,7 @@ function createMergeRuntime(deps) {
|
|
|
9820
9927
|
if (await deps.isBlocked?.(candidate.decision.target) === true) return fail("approve", "target-blocked", "target 被范围门拦下:" + candidate.decision.target, "运行 /evolution scope 查看范围门", candidate.runId);
|
|
9821
9928
|
}
|
|
9822
9929
|
if (candidate.decision.mode === "merge-into-existing" && !cards.some((card) => card.name === candidate.decision?.target)) {
|
|
9823
|
-
if (!await
|
|
9930
|
+
if (!await ledger().transact((tx) => {
|
|
9824
9931
|
const current = tx.rows().find((item) => item.id === candidate.id);
|
|
9825
9932
|
return current === void 0 ? false : tx.transition(candidate.id, {
|
|
9826
9933
|
type: "expired",
|
|
@@ -10487,6 +10594,7 @@ async function runPairCommand(options) {
|
|
|
10487
10594
|
io: options.io,
|
|
10488
10595
|
home: options.home,
|
|
10489
10596
|
registry: options.registry,
|
|
10597
|
+
...options.fence === void 0 ? {} : { fence: options.fence },
|
|
10490
10598
|
approval: options.approval,
|
|
10491
10599
|
platformWrite: options.platformWrite,
|
|
10492
10600
|
now: options.now,
|
|
@@ -10614,7 +10722,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10614
10722
|
quarantine: [],
|
|
10615
10723
|
report: null
|
|
10616
10724
|
};
|
|
10617
|
-
const state = await ensureMergeStateReady(io, home);
|
|
10725
|
+
const state = await ensureMergeStateReady(io, home, ctx.get("evolutionMutationAuthority")?.homeFence);
|
|
10618
10726
|
if (!state.ok) return v4.code === "merge-state-forward-only" || v4.code === "merge-state-needs-repair" ? {
|
|
10619
10727
|
ok: false,
|
|
10620
10728
|
code: v4.code,
|
|
@@ -10631,12 +10739,14 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10631
10739
|
code: "merge-state-migration-blocked",
|
|
10632
10740
|
message: error instanceof Error ? error.message : String(error)
|
|
10633
10741
|
}));
|
|
10742
|
+
const homeFence = ctx.get("evolutionMutationAuthority")?.homeFence;
|
|
10634
10743
|
const runs = newRunRegistry({
|
|
10635
10744
|
io,
|
|
10636
10745
|
home,
|
|
10637
10746
|
warn: (message) => {
|
|
10638
10747
|
ctx.logger.warn(message);
|
|
10639
|
-
}
|
|
10748
|
+
},
|
|
10749
|
+
...homeFence === void 0 ? {} : { fence: homeFence }
|
|
10640
10750
|
});
|
|
10641
10751
|
ctx.effect(() => {
|
|
10642
10752
|
const host = ctx;
|
|
@@ -10655,11 +10765,13 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10655
10765
|
limitsSnapshot,
|
|
10656
10766
|
platformWrite: getPlatformWrite()
|
|
10657
10767
|
});
|
|
10768
|
+
const mutationAuthority = ctx.get("evolutionMutationAuthority");
|
|
10658
10769
|
const skills = newSkillLibrary({
|
|
10659
10770
|
io,
|
|
10660
10771
|
ctx,
|
|
10661
10772
|
authority: authorityFrom({ origin: "subagent" }),
|
|
10662
|
-
limits: limitsSnapshot.skillLimits
|
|
10773
|
+
limits: limitsSnapshot.skillLimits,
|
|
10774
|
+
...mutationAuthority?.mutationLock === void 0 ? {} : { mutationLock: mutationAuthority.mutationLock }
|
|
10663
10775
|
});
|
|
10664
10776
|
ready.then((value) => {
|
|
10665
10777
|
if (!value.ok) {
|
|
@@ -10671,18 +10783,22 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10671
10783
|
home,
|
|
10672
10784
|
backend: skillLibraryUnitBackend(skills),
|
|
10673
10785
|
claim: targetClaimUnitPort(io, home, () => Date.now()),
|
|
10674
|
-
now: () => Date.now()
|
|
10786
|
+
now: () => Date.now(),
|
|
10787
|
+
isActive: async (runId) => await runs.hasOwner(runId),
|
|
10788
|
+
...mutationAuthority?.homeFence === void 0 ? {} : { fence: mutationAuthority.homeFence }
|
|
10675
10789
|
});
|
|
10676
10790
|
const cards = async () => await readSkillCards(skills);
|
|
10677
10791
|
(async () => {
|
|
10678
10792
|
const loaded = await runs.load();
|
|
10679
10793
|
if (!loaded.ok) ctx.logger.warn("merge run index: " + (loaded.note ?? "unreadable"));
|
|
10794
|
+
const activeRuns = /* @__PURE__ */ new Set();
|
|
10795
|
+
for (const record of runs.runs().filter((record) => record.state === "running")) if (await runs.hasOwner(record.id)) activeRuns.add(record.id);
|
|
10680
10796
|
const reports = await recoverCommitUnits({
|
|
10681
10797
|
io,
|
|
10682
10798
|
home,
|
|
10683
10799
|
unit,
|
|
10684
10800
|
now: () => Date.now(),
|
|
10685
|
-
isActive: () =>
|
|
10801
|
+
isActive: (runId) => activeRuns.has(runId),
|
|
10686
10802
|
runs
|
|
10687
10803
|
});
|
|
10688
10804
|
if (reports.length > 0) ctx.logger.info("merge unit recovery: " + reports.map((report) => report.runId + "/" + report.unitId + "=" + report.action).join(", "));
|
|
@@ -10888,6 +11004,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10888
11004
|
now: () => Date.now(),
|
|
10889
11005
|
getParams,
|
|
10890
11006
|
getPlatformWrite,
|
|
11007
|
+
...mutationAuthority?.homeFence === void 0 ? {} : { fence: mutationAuthority.homeFence },
|
|
10891
11008
|
...defaultModel === void 0 ? {} : { model: defaultModel.currentSelection().model },
|
|
10892
11009
|
channel,
|
|
10893
11010
|
parent: void 0,
|
|
@@ -10952,4 +11069,4 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10952
11069
|
});
|
|
10953
11070
|
}
|
|
10954
11071
|
//#endregion
|
|
10955
|
-
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 };
|
|
11072
|
+
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, busyTarget, 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 };
|