@lmzhen/dsh-evolution-merge 0.45.0 → 0.47.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 CHANGED
@@ -1273,7 +1273,7 @@ function parseV4State(raw) {
1273
1273
  * The one merge-state ledger: <home>/merge/merge-state.json.
1274
1274
  *
1275
1275
  * Reads are pure projections (no write, no reconciliation side effect). Every write goes
1276
- * through mutateMergeState and a single transactIo, so there is no second file and no
1276
+ * through pairLedger and a single transactIo, so there is no second file and no
1277
1277
  * incremental writer. A row's queue is always derived from its state on read; a hand-edited
1278
1278
  * mismatch is repaired by the next write, never by a GET.
1279
1279
  */
@@ -1357,96 +1357,6 @@ async function loadStateSchema(io, home) {
1357
1357
  return null;
1358
1358
  }
1359
1359
  }
1360
- /** G3-5 runtime write API: only createVerify/createSkipped/transition. v2 writes are rejected. */
1361
- async function transactRows(_io, _home, _fn) {
1362
- throw new Error("legacy-writer-removed");
1363
- }
1364
- /** The ONE write point. The callback returns the whole row set, not a delta. */
1365
- async function mutateMergeState(_io, _home, _now, _fn) {
1366
- throw new Error("legacy-writer-removed");
1367
- }
1368
- async function upsertRow(io, home, now, row, expect = "any", cas) {
1369
- return await mutateMergeState(io, home, now, (rows) => {
1370
- const index = rows.findIndex((item) => item.pairKey === row.pairKey);
1371
- const current = index < 0 ? void 0 : rows[index];
1372
- if (cas !== void 0 && (cas === null && current !== void 0 || cas !== null && current?.updatedAt !== cas)) return {
1373
- rows,
1374
- result: {
1375
- ok: false,
1376
- current: current ?? row
1377
- }
1378
- };
1379
- if (current !== void 0 && expect !== "any" && (expect === "absent" || queueOf(current.state) !== expect)) return {
1380
- rows,
1381
- result: {
1382
- ok: false,
1383
- current
1384
- }
1385
- };
1386
- if (current === void 0 && expect !== "any" && expect !== "absent" && expect !== "verify") return {
1387
- rows,
1388
- result: {
1389
- ok: false,
1390
- current: row
1391
- }
1392
- };
1393
- const normalized = {
1394
- ...row,
1395
- queue: queueOf(row.state)
1396
- };
1397
- const next = [...rows];
1398
- if (current === void 0) next.push(normalized);
1399
- else next[index] = {
1400
- ...normalized,
1401
- createdAt: current.createdAt
1402
- };
1403
- return {
1404
- rows: next,
1405
- result: { ok: true }
1406
- };
1407
- });
1408
- }
1409
- async function moveRow(io, home, now, id, patch) {
1410
- return await mutateMergeState(io, home, now, (rows) => {
1411
- const index = rows.findIndex((item) => item.id === id);
1412
- if (index < 0) return {
1413
- rows,
1414
- result: void 0
1415
- };
1416
- const current = rows[index];
1417
- const state = patch.state ?? current.state;
1418
- const next = {
1419
- ...current,
1420
- state,
1421
- queue: queueOf(state),
1422
- updatedAt: new Date(now()).toISOString(),
1423
- ...patch.failure === void 0 ? {} : { failure: patch.failure },
1424
- ...patch.runId === void 0 ? {} : { runId: patch.runId },
1425
- ...patch.unitId === void 0 ? {} : { unitId: patch.unitId },
1426
- ...patch.origin === void 0 ? {} : { origin: patch.origin },
1427
- ...patch.attempts === void 0 ? {} : { attempts: patch.attempts }
1428
- };
1429
- const copy = [...rows];
1430
- copy[index] = next;
1431
- return {
1432
- rows: copy,
1433
- result: next
1434
- };
1435
- });
1436
- }
1437
- /** G3-5 v3-aware drop: only terminal events. */
1438
- async function dropRowV3(_io, _home, _id, _event, _unitId = "unknown") {
1439
- throw new Error("legacy-writer-removed");
1440
- }
1441
- async function dropRow(io, home, now, id) {
1442
- return await mutateMergeState(io, home, now, (rows) => {
1443
- const next = rows.filter((item) => item.id !== id);
1444
- return {
1445
- rows: next,
1446
- result: next.length !== rows.length
1447
- };
1448
- });
1449
- }
1450
1360
  async function rowsOf(io, home, queue) {
1451
1361
  const loaded = await loadMergeState(io, home);
1452
1362
  return queue === void 0 ? loaded.state.rows : loaded.state.rows.filter((row) => row.queue === queue);
@@ -2924,7 +2834,7 @@ const emptyCounts = {
2924
2834
  },
2925
2835
  expired: 0
2926
2836
  };
2927
- const queueOf$3 = (state) => state === "approved" || state === "fuse" ? "fuse" : state === "verify" ? "verify" : state === "pending" ? "pending" : "skipped";
2837
+ const queueOf$1 = (state) => state === "approved" || state === "fuse" ? "fuse" : state === "verify" ? "verify" : state === "pending" ? "pending" : "skipped";
2928
2838
  async function readMergeSnapshot(options) {
2929
2839
  const readAt = (options.now ?? (() => Date.now()))();
2930
2840
  const hostOnly = options.hostOnly === void 0 ? void 0 : await options.hostOnly().catch((error) => ({
@@ -2976,7 +2886,7 @@ async function readMergeSnapshot(options) {
2976
2886
  const live = options.live !== void 0 && (ledger?.rows ?? []).some((row) => row.state === "pending" && row.expired === void 0 || row.state === "verify" || row.state === "fuse" || row.state === "approved") ? await options.live().catch(() => void 0) : void 0;
2977
2887
  for (const row of ledger?.rows ?? []) {
2978
2888
  const stage = stages.find((item) => item.rowId === row.id) ?? null;
2979
- const queue = queueOf$3(row.state);
2889
+ const queue = queueOf$1(row.state);
2980
2890
  const bSide = live !== void 0 && live.managed(row.b) ? "live" : "archive";
2981
2891
  const bodies = [{
2982
2892
  rowId: row.id,
@@ -4438,7 +4348,7 @@ function verifyState(row) {
4438
4348
  if (row.row.verifySubState === "screened") return "running";
4439
4349
  return "queued";
4440
4350
  }
4441
- function toRowFact$1(row) {
4351
+ function toRowFact(row) {
4442
4352
  const state = row.row;
4443
4353
  const target = state.state === "pending" || state.state === "approved" || state.state === "fuse" ? {
4444
4354
  requested: state.target,
@@ -4548,7 +4458,7 @@ function runView(run, readAt, includeResultRef = false) {
4548
4458
  ]
4549
4459
  };
4550
4460
  }
4551
- function buildFacts$1(snapshot) {
4461
+ function buildFacts(snapshot) {
4552
4462
  const ordered = [...snapshot.runs].sort((a, b) => b.record.startedAt - a.record.startedAt);
4553
4463
  const active = ordered.find((run) => run.record.state === "running");
4554
4464
  const last = ordered.find((run) => run.record.state !== "running");
@@ -4597,7 +4507,7 @@ function buildQueuePage(snapshot, queue, expiredOnly, cursor, limit = 50) {
4597
4507
  queue,
4598
4508
  expiredOnly,
4599
4509
  counts: counts(snapshot.rows),
4600
- rows: page.map(toRowFact$1),
4510
+ rows: page.map(toRowFact),
4601
4511
  ...next === void 0 ? {} : { nextCursor: next },
4602
4512
  notes: snapshot.notes
4603
4513
  };
@@ -4638,7 +4548,7 @@ function buildRunDetail(snapshot, runId) {
4638
4548
  pairKey: pair.pairKey,
4639
4549
  a: pair.pairKey.split("\0")[0] ?? "",
4640
4550
  b: pair.pairKey.split("\0")[1] ?? "",
4641
- outcome: pair.result === "kept" ? "kept" : pair.result === "failed" ? "failed" : pair.result === "cancelled" ? "cancelled" : "merged",
4551
+ outcome: pair.result === "kept" ? "kept" : pair.result === "failed" ? "failed" : pair.result === "cancelled" ? "cancelled" : pair.result === "undone" ? "undone" : "merged",
4642
4552
  ...pair.target === void 0 ? {} : { target: { requested: pair.target } }
4643
4553
  })),
4644
4554
  pairsTruncated: false
@@ -5621,240 +5531,14 @@ async function recoverCommitUnits(options) {
5621
5531
  return reports;
5622
5532
  }
5623
5533
  //#endregion
5624
- //#region lib/types/facts.js
5625
- const queueOf$2 = (row) => row.state === "approved" ? "fuse" : row.queue;
5626
- const isExpired = (row) => row.expired !== void 0;
5627
- function countsOf(rows) {
5628
- const active = {
5629
- verify: 0,
5630
- pending: 0,
5631
- fuse: 0,
5632
- skipped: 0
5633
- };
5634
- let expired = 0;
5635
- for (const row of rows) {
5636
- if (isExpired(row)) {
5637
- expired += 1;
5638
- continue;
5639
- }
5640
- active[queueOf$2(row)] += 1;
5641
- }
5642
- return {
5643
- active,
5644
- expired
5645
- };
5646
- }
5647
- function toRunView(record, readAt, meta = null) {
5648
- const state = record.state;
5649
- return {
5650
- wireVersion: 1,
5651
- readAt,
5652
- id: record.id,
5653
- state,
5654
- command: record.command ?? "scan",
5655
- ...record.stage === void 0 ? {} : { stage: record.stage },
5656
- progress: record.progress === void 0 ? null : {
5657
- done: record.progress.done,
5658
- total: record.progress.total
5659
- },
5660
- startedAt: record.startedAt,
5661
- ...record.endedAt === void 0 ? {} : { endedAt: record.endedAt },
5662
- ...record.failure === void 0 ? {} : { failure: {
5663
- stage: "settle",
5664
- code: "run-failed",
5665
- retryable: false,
5666
- retryFrom: "none",
5667
- detail: record.failure
5668
- } },
5669
- ...record.parentRunId === void 0 ? {} : { parentRunId: record.parentRunId },
5670
- actions: [
5671
- {
5672
- id: "status",
5673
- command: "/evolution merge status " + record.id,
5674
- confirm: false,
5675
- enabled: true
5676
- },
5677
- {
5678
- id: "report",
5679
- command: "/evolution merge report " + record.id,
5680
- confirm: false,
5681
- enabled: record.resultRef !== void 0
5682
- },
5683
- {
5684
- id: "cancel",
5685
- command: "/evolution merge cancel " + record.id,
5686
- confirm: false,
5687
- enabled: state === "running"
5688
- }
5689
- ]
5690
- };
5691
- }
5692
- function buildFacts(input) {
5693
- const ordered = [...input.runs].sort((a, b) => b.startedAt - a.startedAt);
5694
- const active = ordered.find((run) => run.state === "running");
5695
- const last = ordered.find((run) => run.state !== "running");
5696
- const notes = input.notes ?? [];
5697
- return {
5698
- wireVersion: 1,
5699
- readAt: input.readAt,
5700
- approval: { mergeApproval: input.approval },
5701
- counts: countsOf(input.rows),
5702
- active: active === void 0 ? null : toRunView(active, input.readAt, input.runMeta?.get(active.id) ?? null),
5703
- last: last === void 0 ? null : toRunView(last, input.readAt, input.runMeta?.get(last.id) ?? null),
5704
- startCommands: {
5705
- scan: "/evolution merge scan",
5706
- pair: "/evolution merge pair <a> <b>"
5707
- },
5708
- fallback: MERGE_COMMAND_SURFACE.fallback,
5709
- notes,
5710
- degraded: notes.length > 0
5711
- };
5712
- }
5713
- //#endregion
5714
- //#region lib/types/wire-projection.js
5715
- const queueOf$1 = (row) => row.state === "approved" ? "fuse" : row.queue;
5716
- const expiredOf = (row) => row.expired !== void 0;
5717
- /** Host-owned row actions; client only renders/copies them. */
5718
- function actionRefs(row) {
5719
- const base = "/evolution merge ";
5720
- const disabled = row.expired === void 0 ? void 0 : "expired";
5721
- const action = (id, confirm = false, enabled = true) => ({
5722
- id,
5723
- command: base + id + " " + row.id,
5724
- confirm,
5725
- enabled,
5726
- ...enabled ? {} : { disabledReason: disabled ?? "invalid" }
5727
- });
5728
- if (row.state === "pending" && row.expired !== void 0) return [{
5729
- id: "discard",
5730
- command: "/evolution merge discard " + row.id,
5731
- confirm: true,
5732
- enabled: true
5733
- }, {
5734
- id: "pair",
5735
- command: "/evolution merge pair " + row.a + " " + row.b,
5736
- confirm: true,
5737
- enabled: true
5738
- }];
5739
- if (row.state === "pending") return [
5740
- action("approve"),
5741
- action("reject", true),
5742
- action("discard", true)
5743
- ];
5744
- if (row.state === "approved" || row.state === "fuse") {
5745
- if (row.failure !== void 0 && row.failure.retryable === false) return [{
5746
- id: "pair",
5747
- command: "/evolution merge pair " + row.a + " " + row.b,
5748
- confirm: true,
5749
- enabled: true
5750
- }, action("discard", true)];
5751
- return [
5752
- {
5753
- id: "retry-resume",
5754
- command: "/evolution merge resume --retry " + row.id,
5755
- confirm: true,
5756
- enabled: true
5757
- },
5758
- action("discard", true),
5759
- action("status")
5760
- ];
5761
- }
5762
- if (row.state === "skipped") return [action("restore", true)];
5763
- if (row.state === "verify") return [{
5764
- id: "retry-candidates",
5765
- command: "/evolution merge candidates --retry " + row.id,
5766
- confirm: false,
5767
- enabled: true
5768
- }];
5769
- return [action("pair")];
5770
- }
5771
- function toRowFact(row, actions = actionRefs(row)) {
5772
- return {
5773
- id: row.id,
5774
- state: row.state,
5775
- queue: queueOf$1(row),
5776
- ...row.state === "verify" ? { verifySubState: "queued" } : {},
5777
- a: row.a,
5778
- b: row.b,
5779
- ...row.target === void 0 ? {} : { target: {
5780
- requested: row.target.requested,
5781
- ...row.target.actual === void 0 ? {} : { actual: row.target.actual },
5782
- ...row.target.created === void 0 ? {} : { created: row.target.created }
5783
- } },
5784
- ...row.state === "pending" ? { summary: {
5785
- a: null,
5786
- b: null,
5787
- rationale: row.decision?.rationale ?? null
5788
- } } : {},
5789
- ...row.reason === void 0 ? {} : { reason: row.reason },
5790
- ...row.state === "skipped" ? { origin: row.origin ?? "system" } : {},
5791
- ...row.failure === void 0 ? {} : { failure: {
5792
- stage: row.failure.stage === "screening" ? "screening" : row.failure.stage === "decide" ? "decide" : row.failure.stage === "author" ? "author" : row.failure.stage === "validate" ? "validate" : "commit",
5793
- code: row.failure.code,
5794
- retryable: row.failure.retryable,
5795
- retryFrom: row.failure.retryFrom
5796
- } },
5797
- ...row.stagingRef === void 0 ? {} : { staging: {
5798
- state: "kept",
5799
- ...row.stagingExpiresAt === void 0 ? {} : { expiresAt: row.stagingExpiresAt }
5800
- } },
5801
- ...row.expired === void 0 ? {} : { expired: {
5802
- kind: row.expired,
5803
- at: Date.parse(row.updatedAt)
5804
- } },
5805
- ...row.attempts === void 0 ? {} : { attempts: row.attempts },
5806
- ...row.decision?.confidence === void 0 ? {} : { confidence: row.decision.confidence },
5807
- ...row.state === "skipped" && row.stagingExpiresAt !== void 0 ? { expiresAt: row.stagingExpiresAt } : {},
5808
- createdAt: Date.parse(row.createdAt),
5809
- updatedAt: Date.parse(row.updatedAt),
5810
- actions
5811
- };
5812
- }
5813
- function toQueuePage(rows, queue, expiredOnly, readAt, limit = 50) {
5814
- const filtered = rows.filter((row) => queueOf$1(row) === queue && (expiredOnly ? expiredOf(row) : !expiredOf(row)));
5815
- const page = filtered.slice(0, Math.max(0, Math.min(200, limit)));
5816
- return {
5817
- wireVersion: 1,
5818
- readAt,
5819
- queue,
5820
- expiredOnly,
5821
- counts: countsOf(rows),
5822
- rows: page.map((row) => toRowFact(row)),
5823
- ...filtered.length > page.length ? { nextCursor: String(page.length) } : {},
5824
- notes: []
5825
- };
5826
- }
5827
- function toBodyView(name, side, text, readAt) {
5828
- return {
5829
- wireVersion: 1,
5830
- readAt,
5831
- name,
5832
- side,
5833
- text,
5834
- markdown: true,
5835
- truncated: false,
5836
- totalBytes: Buffer.byteLength(text, "utf8")
5837
- };
5838
- }
5839
- function toHistoryView(items, readAt) {
5840
- return {
5841
- wireVersion: 1,
5842
- readAt,
5843
- items: items.map((item) => {
5844
- const parsed = item.archivedAt === null ? null : typeof item.archivedAt === "number" ? item.archivedAt : Date.parse(item.archivedAt);
5845
- return {
5846
- name: item.name,
5847
- kind: item.kind,
5848
- reason: item.reason,
5849
- lastVersion: item.lastVersion,
5850
- archivedAt: parsed === null || Number.isFinite(parsed) ? parsed : null,
5851
- ...item.sourceRunId === void 0 ? {} : { sourceRunId: item.sourceRunId }
5852
- };
5853
- })
5854
- };
5855
- }
5856
- //#endregion
5857
5534
  //#region lib/types/merge-routes.js
5535
+ /**
5536
+ * Four read-only merge routes: view / rows / body / history.
5537
+ * The host loads one snapshot; these handlers only project wire envelopes.
5538
+ * There is no second (legacy rows/runs/history) source: the production constructor has
5539
+ * always provided the snapshot, and the legacy branches were the only consumers of
5540
+ * facts.ts / wire-projection.ts (W11 D2b).
5541
+ */
5858
5542
  const MERGE_PATHS = HOST_ROUTES.merge;
5859
5543
  const MERGE_BASE = MERGE_PATHS.view.slice(0, -4);
5860
5544
  const MERGE_ROUTES = {
@@ -5870,21 +5554,16 @@ function makeMergeRoutes(deps, _audit) {
5870
5554
  code: "E-325",
5871
5555
  message: "merge service is not mounted"
5872
5556
  });
5873
- const blocked = (readiness) => json(503, {
5557
+ const blocked = (snapshot) => snapshot.blocked === void 0 ? null : json(503, {
5874
5558
  wireVersion: 1,
5875
5559
  code: "merge-state-blocked",
5876
- readiness
5560
+ readiness: snapshot.blocked.readiness
5877
5561
  });
5878
5562
  const hostOnly = (snapshot) => snapshot.hostOnly === void 0 ? null : json(503, {
5879
5563
  wireVersion: 1,
5880
5564
  code: "E-323",
5881
5565
  message: snapshot.hostOnly.detail ?? "host data plane unavailable"
5882
5566
  });
5883
- const notReady = async (source) => {
5884
- if (source.readiness === void 0) return null;
5885
- const readiness = await source.readiness();
5886
- return readiness.ok ? null : blocked(readiness);
5887
- };
5888
5567
  const bad = (message) => json(400, {
5889
5568
  wireVersion: 1,
5890
5569
  code: "merge-bad-query",
@@ -5892,6 +5571,11 @@ function makeMergeRoutes(deps, _audit) {
5892
5571
  });
5893
5572
  const get = (request) => request.method === "GET" ? null : methodNotAllowed(["GET"]);
5894
5573
  const url = (request) => new URL(request.url, "http://local");
5574
+ const snapshotOf = async () => {
5575
+ const source = deps.getSource();
5576
+ return source === void 0 ? null : await source.snapshot();
5577
+ };
5578
+ const unavailable = (snapshot) => snapshot === null ? absent() : blocked(snapshot) ?? hostOnly(snapshot);
5895
5579
  return [
5896
5580
  {
5897
5581
  path: MERGE_ROUTES.view,
@@ -5900,28 +5584,10 @@ function makeMergeRoutes(deps, _audit) {
5900
5584
  fetch: async (request) => {
5901
5585
  const denied = get(request);
5902
5586
  if (denied !== null) return denied;
5903
- const source = deps.getSource();
5904
- if (source === void 0) return absent();
5905
- if (source.snapshot !== void 0) {
5906
- const snapshot = await source.snapshot();
5907
- if (snapshot.blocked !== void 0) return json(503, {
5908
- wireVersion: 1,
5909
- code: "merge-state-blocked",
5910
- readiness: snapshot.blocked.readiness
5911
- });
5912
- const capability = hostOnly(snapshot);
5913
- if (capability !== null) return capability;
5914
- return json(200, buildFacts$1(snapshot));
5915
- }
5916
- const readiness = await notReady(source);
5917
- if (readiness !== null) return readiness;
5918
- const readAt = Date.now();
5919
- return json(200, buildFacts({
5920
- rows: await source.rows?.() ?? [],
5921
- runs: await source.runs?.(void 0, 20) ?? [],
5922
- approval: source.approval?.() === true,
5923
- readAt
5924
- }));
5587
+ const snapshot = await snapshotOf();
5588
+ const notReady = unavailable(snapshot);
5589
+ if (notReady !== null) return notReady;
5590
+ return json(200, buildFacts(snapshot));
5925
5591
  }
5926
5592
  },
5927
5593
  {
@@ -5931,37 +5597,20 @@ function makeMergeRoutes(deps, _audit) {
5931
5597
  fetch: async (request) => {
5932
5598
  const denied = get(request);
5933
5599
  if (denied !== null) return denied;
5934
- const source = deps.getSource();
5935
- if (source === void 0) return absent();
5936
5600
  const query = url(request).searchParams;
5937
5601
  const queue = query.get("queue") ?? "verify";
5938
5602
  if (queue !== "verify" && queue !== "pending" && queue !== "fuse" && queue !== "skipped") return bad("queue must be verify/pending/fuse/skipped");
5939
5603
  const expiredOnly = query.get("expired") === "1";
5940
5604
  if (expiredOnly && queue !== "pending") return bad("expired is only allowed for pending");
5941
5605
  const limit = Number.parseInt(query.get("limit") ?? "50", 10);
5942
- if (source.snapshot !== void 0) {
5943
- const snapshot = await source.snapshot();
5944
- if (snapshot.blocked !== void 0) return json(503, {
5945
- wireVersion: 1,
5946
- code: "merge-state-blocked",
5947
- readiness: snapshot.blocked.readiness
5948
- });
5949
- const capability = hostOnly(snapshot);
5950
- if (capability !== null) return capability;
5951
- try {
5952
- return json(200, buildQueuePage(snapshot, queue, expiredOnly, query.get("cursor") ?? void 0, Number.isFinite(limit) ? limit : 50));
5953
- } catch (error) {
5954
- return bad(error instanceof Error ? error.message : "queue cursor is stale");
5955
- }
5606
+ const snapshot = await snapshotOf();
5607
+ const notReady = unavailable(snapshot);
5608
+ if (notReady !== null) return notReady;
5609
+ try {
5610
+ return json(200, buildQueuePage(snapshot, queue, expiredOnly, query.get("cursor") ?? void 0, Number.isFinite(limit) ? limit : 50));
5611
+ } catch (error) {
5612
+ return bad(error instanceof Error ? error.message : "queue cursor is stale");
5956
5613
  }
5957
- const readiness = await notReady(source);
5958
- if (readiness !== null) return readiness;
5959
- const offset = Number.parseInt(query.get("cursor") ?? "0", 10);
5960
- const page = toQueuePage(await source.rows?.() ?? [], queue, expiredOnly, Date.now(), Number.isFinite(limit) ? limit : 50);
5961
- return json(200, Number.isFinite(offset) && offset > 0 ? {
5962
- ...page,
5963
- rows: page.rows.slice(offset)
5964
- } : page);
5965
5614
  }
5966
5615
  },
5967
5616
  {
@@ -5982,39 +5631,25 @@ function makeMergeRoutes(deps, _audit) {
5982
5631
  code: "merge-body-not-found",
5983
5632
  message: "merge body requires id or name"
5984
5633
  });
5985
- if (source.snapshot !== void 0) {
5986
- const snapshot = await source.snapshot();
5987
- if (snapshot.blocked !== void 0) return json(503, {
5988
- wireVersion: 1,
5989
- code: "merge-state-blocked",
5990
- readiness: snapshot.blocked.readiness
5991
- });
5992
- const sideFilter = side === "a" ? "live" : side === "b" ? "archive" : side === "live" || side === "archive" ? side : void 0;
5993
- const refs = snapshot.bodyRefs.filter((ref) => (id === null || id === "" || ref.rowId === id) && (name === null || name === "" || ref.name === name) && (sideFilter === void 0 || ref.side === sideFilter));
5994
- if (refs.length === 0) return json(404, {
5995
- wireVersion: 1,
5996
- code: "merge-body-not-found",
5997
- message: "merge body not found"
5998
- });
5999
- if (refs.length > 1) return bad("merge body is ambiguous; pass id, name and side");
6000
- const row = snapshot.rows.find((item) => item.row.id === refs[0].rowId);
6001
- if (row?.stage?.state === "lost" || row?.stage?.state === "expired") return json(404, {
6002
- wireVersion: 1,
6003
- code: "merge-body-not-found",
6004
- message: "merge stage is lost"
6005
- });
6006
- const ref = refs[0];
6007
- return json(200, buildBodyView(snapshot, ref, (source.bodyRef === void 0 ? null : await source.bodyRef(ref)) ?? ""));
6008
- }
6009
- const readiness = await notReady(source);
6010
- if (readiness !== null) return readiness;
6011
- if (id === null || id === "" || side !== "a" && side !== "b") return bad("id and side=a|b are required");
6012
- const content = await source.body?.(id, side) ?? null;
6013
- return content === null ? json(200, {
5634
+ const snapshot = await source.snapshot();
5635
+ const notReady = blocked(snapshot);
5636
+ if (notReady !== null) return notReady;
5637
+ const sideFilter = side === "a" ? "live" : side === "b" ? "archive" : side === "live" || side === "archive" ? side : void 0;
5638
+ const refs = snapshot.bodyRefs.filter((ref) => (id === null || id === "" || ref.rowId === id) && (name === null || name === "" || ref.name === name) && (sideFilter === void 0 || ref.side === sideFilter));
5639
+ if (refs.length === 0) return json(404, {
5640
+ wireVersion: 1,
5641
+ code: "merge-body-not-found",
5642
+ message: "merge body not found"
5643
+ });
5644
+ if (refs.length > 1) return bad("merge body is ambiguous; pass id, name and side");
5645
+ const row = snapshot.rows.find((item) => item.row.id === refs[0].rowId);
5646
+ if (row?.stage?.state === "lost" || row?.stage?.state === "expired") return json(404, {
6014
5647
  wireVersion: 1,
6015
- code: "merge-body-missing",
6016
- message: "merge body not found: " + id
6017
- }) : json(200, toBodyView(id, side === "a" ? "live" : "archive", content, Date.now()));
5648
+ code: "merge-body-not-found",
5649
+ message: "merge stage is lost"
5650
+ });
5651
+ const ref = refs[0];
5652
+ return json(200, buildBodyView(snapshot, ref, (source.bodyRef === void 0 ? null : await source.bodyRef(ref)) ?? ""));
6018
5653
  }
6019
5654
  },
6020
5655
  {
@@ -6024,20 +5659,11 @@ function makeMergeRoutes(deps, _audit) {
6024
5659
  fetch: async (request) => {
6025
5660
  const denied = get(request);
6026
5661
  if (denied !== null) return denied;
6027
- const source = deps.getSource();
6028
- if (source === void 0) return absent();
6029
- if (source.snapshot !== void 0) {
6030
- const snapshot = await source.snapshot();
6031
- if (snapshot.blocked !== void 0) return json(503, {
6032
- wireVersion: 1,
6033
- code: "merge-state-blocked",
6034
- readiness: snapshot.blocked.readiness
6035
- });
6036
- return json(200, buildHistoryView(snapshot));
6037
- }
6038
- const readiness = await notReady(source);
6039
- if (readiness !== null) return readiness;
6040
- return json(200, toHistoryView(await source.history?.() ?? [], Date.now()));
5662
+ const snapshot = await snapshotOf();
5663
+ if (snapshot === null) return absent();
5664
+ const notReady = blocked(snapshot);
5665
+ if (notReady !== null) return notReady;
5666
+ return json(200, buildHistoryView(snapshot));
6041
5667
  }
6042
5668
  },
6043
5669
  {
@@ -6351,13 +5977,13 @@ async function handleReadOnlyMergeCommand(command, io, home, cards, runs, approv
6351
5977
  ...live === void 0 ? {} : { live }
6352
5978
  });
6353
5979
  if (snapshot.blocked !== void 0) throw new Error(snapshot.blocked.code + ": " + snapshot.blocked.message);
6354
- const rows = snapshot.rows.map(toRowFact$1);
5980
+ const rows = snapshot.rows.map(toRowFact);
6355
5981
  if (command.verb === "status") {
6356
5982
  if (command.id !== void 0) {
6357
5983
  const row = rows.find((item) => item.id === command.id);
6358
5984
  return row === void 0 ? "merge row not found: " + command.id : formatSnapshotStatus([row]);
6359
5985
  }
6360
- return formatFacts(buildFacts$1(snapshot));
5986
+ return formatFacts(buildFacts(snapshot));
6361
5987
  }
6362
5988
  if (command.verb === "pending") {
6363
5989
  const live = command.expired === true ? rows.filter((row) => row.expired !== void 0) : rows.filter((row) => row.queue === "pending" && row.expired === void 0);
@@ -11674,25 +11300,6 @@ function apply(ctx, rawConfig = {}) {
11674
11300
  },
11675
11301
  async bodyRef(ref) {
11676
11302
  return await resolveBody(io, home, ref, async () => ref.side === "archive" ? await skills.readArchived(ref.name) : await skills.read(ref.name));
11677
- },
11678
- approval() {
11679
- return getParams().mergeApproval;
11680
- },
11681
- readiness() {
11682
- return ready;
11683
- },
11684
- commands() {
11685
- return [
11686
- "merge scan",
11687
- "merge doctor",
11688
- "merge status",
11689
- "merge pending",
11690
- "merge skipped",
11691
- "merge report",
11692
- "merge runs",
11693
- "merge cancel",
11694
- "merge reconcile"
11695
- ];
11696
11303
  }
11697
11304
  };
11698
11305
  doctorFact = async () => {
@@ -11779,4 +11386,4 @@ function apply(ctx, rawConfig = {}) {
11779
11386
  });
11780
11387
  }
11781
11388
  //#endregion
11782
- 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 };
11389
+ 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, 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, 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, transitionLegacyForProjection, undoMergeReceipt, unitFor, validateStagedTree, withMergeRun, writeCommitReceipt, writeMergeReport, writeStagedFile };
@@ -331,11 +331,6 @@ export function apply(ctx, rawConfig = {}) {
331
331
  } });
332
332
  },
333
333
  async bodyRef(ref) { return await resolveBody(io, home, ref, async () => ref.side === 'archive' ? await skills.readArchived(ref.name) : await skills.read(ref.name)); },
334
- approval() { return getParams().mergeApproval; },
335
- readiness() { return ready; },
336
- commands() {
337
- return ['merge scan', 'merge doctor', 'merge status', 'merge pending', 'merge skipped', 'merge report', 'merge runs', 'merge cancel', 'merge reconcile'];
338
- },
339
334
  };
340
335
  doctorFact = async () => {
341
336
  const current = source;