@lmzhen/dsh-evolution-merge 0.30.17 → 0.30.19

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.
Files changed (187) hide show
  1. package/README.md +25 -7
  2. package/data/minhash_params.json +1063 -0
  3. package/lib/index.js +7308 -1800
  4. package/lib/types/adapters/channel.d.ts +1 -0
  5. package/lib/types/adapters/channel.js +15 -1
  6. package/lib/types/adapters/engine-screen.d.ts +1 -1
  7. package/lib/types/adapters/engine-screen.js +5 -2
  8. package/lib/types/adapters/library.js +4 -1
  9. package/lib/types/adapters/model-runner.d.ts +0 -5
  10. package/lib/types/adapters/model-runner.js +8 -2
  11. package/lib/types/adapters/model.d.ts +31 -0
  12. package/lib/types/adapters/model.js +47 -0
  13. package/lib/types/adapters/select.js +12 -8
  14. package/lib/types/approve-command.d.ts +14 -1
  15. package/lib/types/approve-command.js +72 -44
  16. package/lib/types/authoring-flow.d.ts +19 -1
  17. package/lib/types/authoring-flow.js +49 -15
  18. package/lib/types/channel-port.d.ts +3 -1
  19. package/lib/types/channel-port.js +8 -3
  20. package/lib/types/command-defs.d.ts +5 -0
  21. package/lib/types/command-defs.js +33 -0
  22. package/lib/types/command-surface.d.ts +5 -0
  23. package/lib/types/command-surface.js +11 -0
  24. package/lib/types/commands.d.ts +3 -4
  25. package/lib/types/commands.js +33 -72
  26. package/lib/types/commit-assembly.d.ts +16 -1
  27. package/lib/types/commit-assembly.js +72 -11
  28. package/lib/types/commit-execute.d.ts +14 -2
  29. package/lib/types/commit-execute.js +76 -14
  30. package/lib/types/commit-plan.d.ts +7 -1
  31. package/lib/types/commit-plan.js +4 -0
  32. package/lib/types/commit-receipt.d.ts +3 -0
  33. package/lib/types/commit-recovery.d.ts +7 -2
  34. package/lib/types/commit-recovery.js +73 -96
  35. package/lib/types/contracts.d.ts +44 -1
  36. package/lib/types/contracts.js +1 -1
  37. package/lib/types/coordinator.d.ts +3 -2
  38. package/lib/types/coordinator.js +4 -3
  39. package/lib/types/decision.js +1 -1
  40. package/lib/types/doctor.d.ts +18 -1
  41. package/lib/types/doctor.js +164 -10
  42. package/lib/types/domain/authoring-manifest.d.ts +26 -0
  43. package/lib/types/domain/authoring-manifest.js +43 -0
  44. package/lib/types/domain/claim.d.ts +4 -1
  45. package/lib/types/domain/claim.js +2 -2
  46. package/lib/types/domain/commit-journal-store.js +5 -1
  47. package/lib/types/domain/commit-journal.js +1 -1
  48. package/lib/types/domain/consistency.d.ts +22 -0
  49. package/lib/types/domain/consistency.js +50 -0
  50. package/lib/types/domain/engine-criteria.d.ts +10 -0
  51. package/lib/types/domain/engine-criteria.js +11 -0
  52. package/lib/types/domain/engine-input.d.ts +10 -0
  53. package/lib/types/domain/engine-input.js +26 -0
  54. package/lib/types/domain/errors.d.ts +15 -1
  55. package/lib/types/domain/errors.js +23 -4
  56. package/lib/types/domain/evidence-hash.d.ts +3 -0
  57. package/lib/types/domain/evidence-hash.js +14 -0
  58. package/lib/types/domain/failure-route.d.ts +14 -0
  59. package/lib/types/domain/failure-route.js +12 -0
  60. package/lib/types/domain/ledger.js +10 -1
  61. package/lib/types/domain/live-facts.d.ts +31 -0
  62. package/lib/types/domain/live-facts.js +54 -0
  63. package/lib/types/domain/merge-limits.d.ts +18 -0
  64. package/lib/types/domain/merge-limits.js +22 -0
  65. package/lib/types/domain/merge-report.d.ts +41 -0
  66. package/lib/types/domain/merge-report.js +2 -0
  67. package/lib/types/domain/merge-snapshot.d.ts +99 -0
  68. package/lib/types/domain/merge-snapshot.js +82 -0
  69. package/lib/types/domain/migrate-v3.d.ts +20 -0
  70. package/lib/types/domain/migrate-v3.js +68 -0
  71. package/lib/types/domain/pair-ledger.d.ts +34 -0
  72. package/lib/types/domain/pair-ledger.js +96 -0
  73. package/lib/types/domain/pre-1b-backup.d.ts +43 -0
  74. package/lib/types/domain/pre-1b-backup.js +140 -0
  75. package/lib/types/domain/provider-key.d.ts +2 -0
  76. package/lib/types/domain/provider-key.js +11 -0
  77. package/lib/types/domain/read-probe.d.ts +5 -0
  78. package/lib/types/domain/read-probe.js +11 -0
  79. package/lib/types/domain/reconcile.d.ts +47 -0
  80. package/lib/types/domain/reconcile.js +178 -0
  81. package/lib/types/domain/resolve-pair.d.ts +2 -9
  82. package/lib/types/domain/resolve-pair.js +2 -15
  83. package/lib/types/domain/row-tx.d.ts +27 -0
  84. package/lib/types/domain/row-tx.js +31 -0
  85. package/lib/types/domain/row-v3-bridge.d.ts +6 -0
  86. package/lib/types/domain/row-v3-bridge.js +47 -0
  87. package/lib/types/domain/row-v3.d.ts +113 -0
  88. package/lib/types/domain/row-v3.js +107 -0
  89. package/lib/types/domain/row-v4.d.ts +148 -0
  90. package/lib/types/domain/row-v4.js +159 -0
  91. package/lib/types/domain/row.d.ts +1 -0
  92. package/lib/types/domain/run-coordinator.d.ts +51 -0
  93. package/lib/types/domain/run-coordinator.js +45 -0
  94. package/lib/types/domain/schema4.d.ts +53 -0
  95. package/lib/types/domain/schema4.js +129 -0
  96. package/lib/types/domain/selection-policy.d.ts +14 -0
  97. package/lib/types/domain/selection-policy.js +6 -0
  98. package/lib/types/domain/skipped-expiry.d.ts +5 -0
  99. package/lib/types/domain/skipped-expiry.js +16 -0
  100. package/lib/types/domain/stage-aggregate.d.ts +96 -0
  101. package/lib/types/domain/stage-aggregate.js +293 -0
  102. package/lib/types/domain/staging-index.d.ts +15 -0
  103. package/lib/types/domain/staging-index.js +41 -0
  104. package/lib/types/domain/state-bootstrap.d.ts +43 -0
  105. package/lib/types/domain/state-bootstrap.js +267 -0
  106. package/lib/types/domain/state-v3.d.ts +4 -0
  107. package/lib/types/domain/state-v3.js +4 -0
  108. package/lib/types/domain/undo-journal.d.ts +19 -0
  109. package/lib/types/domain/undo-journal.js +20 -0
  110. package/lib/types/domain/undo-receipt.d.ts +18 -0
  111. package/lib/types/domain/undo-receipt.js +22 -0
  112. package/lib/types/domain/validation-plan.d.ts +65 -0
  113. package/lib/types/domain/validation-plan.js +49 -0
  114. package/lib/types/facts.d.ts +26 -0
  115. package/lib/types/facts.js +65 -0
  116. package/lib/types/gate-filter.d.ts +31 -0
  117. package/lib/types/gate-filter.js +64 -0
  118. package/lib/types/index.d.ts +7 -2
  119. package/lib/types/index.js +185 -82
  120. package/lib/types/ladder.js +1 -1
  121. package/lib/types/merge-command.d.ts +3 -1
  122. package/lib/types/merge-command.js +10 -6
  123. package/lib/types/merge-routes.d.ts +12 -7
  124. package/lib/types/merge-routes.js +87 -23
  125. package/lib/types/merge-tool.d.ts +1 -1
  126. package/lib/types/merge-tool.js +2 -2
  127. package/lib/types/metrics.d.ts +16 -0
  128. package/lib/types/metrics.js +27 -0
  129. package/lib/types/new-child-authoring.d.ts +5 -1
  130. package/lib/types/new-child-authoring.js +71 -13
  131. package/lib/types/params.d.ts +2 -4
  132. package/lib/types/params.js +13 -0
  133. package/lib/types/pending-actions.d.ts +2 -2
  134. package/lib/types/pending-actions.js +17 -17
  135. package/lib/types/prompts.d.ts +2 -4
  136. package/lib/types/prompts.js +21 -8
  137. package/lib/types/propose.d.ts +26 -0
  138. package/lib/types/propose.js +69 -0
  139. package/lib/types/read-commands.d.ts +4 -4
  140. package/lib/types/read-commands.js +19 -26
  141. package/lib/types/recovery-coordinator.d.ts +34 -0
  142. package/lib/types/recovery-coordinator.js +84 -0
  143. package/lib/types/recovery-projection.d.ts +31 -0
  144. package/lib/types/recovery-projection.js +96 -0
  145. package/lib/types/resume-command.d.ts +7 -1
  146. package/lib/types/resume-command.js +50 -14
  147. package/lib/types/revision.d.ts +17 -0
  148. package/lib/types/revision.js +39 -3
  149. package/lib/types/round2-outcome.d.ts +2 -1
  150. package/lib/types/round2-outcome.js +68 -12
  151. package/lib/types/run-shell.d.ts +16 -2
  152. package/lib/types/run-shell.js +49 -5
  153. package/lib/types/runtime.d.ts +4 -6
  154. package/lib/types/runtime.js +341 -86
  155. package/lib/types/scan-command.d.ts +1 -0
  156. package/lib/types/scan-command.js +2 -0
  157. package/lib/types/scan-ports.d.ts +21 -1
  158. package/lib/types/scan-ports.js +51 -23
  159. package/lib/types/scan-run.d.ts +17 -0
  160. package/lib/types/scan-run.js +221 -63
  161. package/lib/types/screening-port.d.ts +47 -0
  162. package/lib/types/screening-port.js +93 -0
  163. package/lib/types/screening.d.ts +30 -0
  164. package/lib/types/screening.js +75 -0
  165. package/lib/types/stage-tool-registry.d.ts +7 -0
  166. package/lib/types/stage-tool-registry.js +12 -1
  167. package/lib/types/state.d.ts +15 -1
  168. package/lib/types/state.js +39 -17
  169. package/lib/types/status.d.ts +8 -0
  170. package/lib/types/status.js +64 -0
  171. package/lib/types/surface/body.d.ts +4 -0
  172. package/lib/types/surface/body.js +16 -0
  173. package/lib/types/surface/merge-view.d.ts +14 -0
  174. package/lib/types/surface/merge-view.js +126 -0
  175. package/lib/types/target-claim.d.ts +1 -0
  176. package/lib/types/target-claim.js +63 -18
  177. package/lib/types/undo-operation.d.ts +24 -0
  178. package/lib/types/undo-operation.js +135 -0
  179. package/lib/types/unit-claim.d.ts +5 -0
  180. package/lib/types/unit-claim.js +15 -0
  181. package/lib/types/wire-projection.d.ts +36 -0
  182. package/lib/types/wire-projection.js +69 -0
  183. package/lib/types/wire.d.ts +273 -0
  184. package/lib/types/wire.js +3 -0
  185. package/package.json +10 -4
  186. package/lib/types/routes.d.ts +0 -70
  187. package/lib/types/routes.js +0 -78
@@ -0,0 +1,148 @@
1
+ /** Batch 1b-2a additive schema4 row types and pure write helpers. */
2
+ import type { MergeDecision, MergeFailure, MergeRow } from '../contracts.ts';
3
+ import type { V3Row } from './row-v3.ts';
4
+ export type RowOriginV4 = 'agent' | 'human' | 'system';
5
+ export type VerifySubStateV4 = 'candidate' | 'screened' | 'unverified';
6
+ export interface RowBaseV4 {
7
+ readonly id: string;
8
+ readonly pairKey: string;
9
+ readonly a: string;
10
+ readonly b: string;
11
+ readonly version: number;
12
+ readonly createdAt: string;
13
+ readonly updatedAt: string;
14
+ readonly origin: RowOriginV4;
15
+ readonly failure?: MergeFailure;
16
+ readonly runId?: string;
17
+ }
18
+ export interface VerifyRowV4 extends RowBaseV4 {
19
+ readonly state: 'verify';
20
+ readonly verifySubState: VerifySubStateV4;
21
+ readonly sourceRevision?: string;
22
+ readonly attempts: number;
23
+ }
24
+ export interface PendingRowV4 extends RowBaseV4 {
25
+ readonly state: 'pending';
26
+ readonly target: string;
27
+ readonly decision: MergeDecision;
28
+ readonly targetRevision?: string;
29
+ readonly sourceRevision?: string;
30
+ readonly promptVersion?: string;
31
+ readonly promptHash?: string;
32
+ readonly expired?: 'target-taken' | 'source-changed';
33
+ readonly expiredAt?: number;
34
+ readonly attempts: number;
35
+ }
36
+ export interface ApprovedRowV4 extends RowBaseV4 {
37
+ readonly state: 'approved';
38
+ readonly target: string;
39
+ readonly decision: MergeDecision;
40
+ readonly sourceRevision?: string;
41
+ readonly targetRevision?: string;
42
+ readonly runId?: string;
43
+ readonly attempts: number;
44
+ }
45
+ export interface FuseRowV4 extends RowBaseV4 {
46
+ readonly state: 'fuse';
47
+ readonly target: string;
48
+ readonly decision: MergeDecision;
49
+ readonly sourceRevision?: string;
50
+ readonly targetRevision?: string;
51
+ readonly runId?: string;
52
+ readonly attempts: number;
53
+ }
54
+ export interface SkippedRowV4 extends RowBaseV4 {
55
+ readonly state: 'skipped';
56
+ readonly reason: string;
57
+ readonly skipSource?: string;
58
+ readonly expiredAt?: number;
59
+ readonly attempts: number;
60
+ }
61
+ export type V4Row = VerifyRowV4 | PendingRowV4 | ApprovedRowV4 | FuseRowV4 | SkippedRowV4;
62
+ export type V4Event = {
63
+ readonly type: 'verify-passed';
64
+ readonly decision: MergeDecision;
65
+ readonly target: string;
66
+ readonly sourceRevision?: string;
67
+ readonly promptVersion?: string;
68
+ readonly promptHash?: string;
69
+ } | {
70
+ readonly type: 'approved';
71
+ readonly runId: string;
72
+ } | {
73
+ readonly type: 'auto-approved';
74
+ readonly decision: MergeDecision;
75
+ readonly target: string;
76
+ readonly runId?: string;
77
+ } | {
78
+ readonly type: 'staged';
79
+ } | {
80
+ readonly type: 'committed';
81
+ readonly unitId: CommitUnitRef;
82
+ } | {
83
+ readonly type: 'skipped';
84
+ readonly reason: string;
85
+ readonly origin?: RowOriginV4;
86
+ readonly skipSource?: string;
87
+ } | {
88
+ readonly type: 'mark-skipped-expired';
89
+ readonly at: number;
90
+ } | {
91
+ readonly type: 'expired';
92
+ readonly kind: 'target-taken' | 'source-changed';
93
+ } | {
94
+ readonly type: 'failed';
95
+ readonly failure: MergeFailure;
96
+ } | {
97
+ readonly type: 'restored';
98
+ } | {
99
+ readonly type: 'superseded';
100
+ };
101
+ export interface CommitUnitRef {
102
+ readonly unitId: string;
103
+ readonly evidenceSha256: string;
104
+ }
105
+ export type V4Transition = {
106
+ readonly ok: true;
107
+ readonly row: V4Row | null;
108
+ readonly version: number;
109
+ } | {
110
+ readonly ok: false;
111
+ readonly code: 'version-conflict' | 'illegal-transition';
112
+ readonly message: string;
113
+ };
114
+ export type V4Terminal = 'committed' | 'superseded' | 'restored';
115
+ export type V4Write = {
116
+ readonly kind: 'createVerify';
117
+ readonly row: VerifyRowV4;
118
+ } | {
119
+ readonly kind: 'createSkipped';
120
+ readonly row: SkippedRowV4;
121
+ } | {
122
+ readonly kind: 'transition';
123
+ readonly id: string;
124
+ readonly event: V4Event;
125
+ readonly expectedVersion: number;
126
+ } | {
127
+ readonly kind: 'remove';
128
+ readonly id: string;
129
+ readonly terminal: V4Terminal;
130
+ readonly unit?: CommitUnitRef;
131
+ readonly expectedVersion: number;
132
+ };
133
+ export type V4WriteError = 'row-invalid' | 'row-duplicate' | 'row-not-found' | 'version-conflict' | 'illegal-transition';
134
+ export type V4WriteResult = {
135
+ readonly ok: true;
136
+ readonly rows: readonly V4Row[];
137
+ readonly row: V4Row | null;
138
+ } | {
139
+ readonly ok: false;
140
+ readonly code: V4WriteError;
141
+ readonly message: string;
142
+ };
143
+ export declare function transitionV4(row: V4Row, event: V4Event, expectedVersion: number, at?: string): V4Transition;
144
+ export declare function isV4Row(value: unknown): value is V4Row;
145
+ export declare function v3ToV4(row: V3Row): V4Row;
146
+ export declare function applyV4Write(rows: readonly V4Row[], write: V4Write, at: string): V4WriteResult;
147
+ export declare function v4ToLegacy(row: V4Row): MergeRow;
148
+ //# sourceMappingURL=row-v4.d.ts.map
@@ -0,0 +1,159 @@
1
+ function bad(code, message) { return { ok: false, code, message }; }
2
+ function conflict(message) { return { ok: false, code: 'version-conflict', message }; }
3
+ function illegal(message) { return { ok: false, code: 'illegal-transition', message }; }
4
+ function advance(row, at, patch) { return { ...row, ...patch, version: row.version + 1, updatedAt: at }; }
5
+ export function transitionV4(row, event, expectedVersion, at = new Date().toISOString()) {
6
+ if (row.version !== expectedVersion)
7
+ return conflict('expected ' + String(expectedVersion) + ', got ' + String(row.version));
8
+ switch (row.state) {
9
+ case 'verify':
10
+ if (event.type === 'verify-passed')
11
+ return { ok: true, row: advance(row, at, { state: 'pending', target: event.target, decision: event.decision, ...(event.sourceRevision === undefined ? {} : { sourceRevision: event.sourceRevision }), ...(event.promptVersion === undefined ? {} : { promptVersion: event.promptVersion }), ...(event.promptHash === undefined ? {} : { promptHash: event.promptHash }), attempts: 0 }), version: expectedVersion + 1 };
12
+ if (event.type === 'auto-approved')
13
+ return { ok: true, row: advance(row, at, { state: 'approved', target: event.target, decision: event.decision, attempts: 0, ...(event.runId === undefined ? {} : { runId: event.runId }) }), version: expectedVersion + 1 };
14
+ if (event.type === 'failed')
15
+ return { ok: true, row: advance(row, at, { failure: event.failure, attempts: row.attempts + 1 }), version: expectedVersion + 1 };
16
+ if (event.type === 'skipped')
17
+ return { ok: true, row: advance(row, at, { state: 'skipped', reason: event.reason, attempts: row.attempts, ...(event.skipSource === undefined ? {} : { skipSource: event.skipSource }) }), version: expectedVersion + 1 };
18
+ if (event.type === 'superseded')
19
+ return { ok: true, row: null, version: expectedVersion + 1 };
20
+ return illegal('verify/' + event.type);
21
+ case 'pending':
22
+ if (event.type === 'approved')
23
+ return { ok: true, row: advance(row, at, { state: 'approved', runId: event.runId }), version: expectedVersion + 1 };
24
+ if (event.type === 'auto-approved')
25
+ return { ok: true, row: advance(row, at, { state: 'approved', ...(event.runId === undefined ? {} : { runId: event.runId }) }), version: expectedVersion + 1 };
26
+ if (event.type === 'staged')
27
+ return { ok: true, row: advance(row, at, { state: 'fuse' }), version: expectedVersion + 1 };
28
+ if (event.type === 'skipped')
29
+ return { ok: true, row: advance(row, at, { state: 'skipped', reason: event.reason, ...(event.origin === undefined ? {} : { origin: event.origin }), ...(event.skipSource === undefined ? {} : { skipSource: event.skipSource }) }), version: expectedVersion + 1 };
30
+ if (event.type === 'expired')
31
+ return { ok: true, row: advance(row, at, { expired: event.kind }), version: expectedVersion + 1 };
32
+ if (event.type === 'failed')
33
+ return { ok: true, row: advance(row, at, { failure: event.failure, attempts: row.attempts + 1 }), version: expectedVersion + 1 };
34
+ if (event.type === 'superseded')
35
+ return { ok: true, row: null, version: expectedVersion + 1 };
36
+ return illegal('pending/' + event.type);
37
+ case 'approved':
38
+ if (event.type === 'staged')
39
+ return { ok: true, row: advance(row, at, { state: 'fuse' }), version: expectedVersion + 1 };
40
+ if (event.type === 'committed')
41
+ return { ok: true, row: null, version: expectedVersion + 1 };
42
+ if (event.type === 'skipped')
43
+ return { ok: true, row: advance(row, at, { state: 'skipped', reason: event.reason, ...(event.skipSource === undefined ? {} : { skipSource: event.skipSource }) }), version: expectedVersion + 1 };
44
+ if (event.type === 'failed')
45
+ return event.failure.retryable
46
+ ? { ok: true, row: advance(row, at, { state: 'fuse', failure: event.failure, attempts: row.attempts + 1 }), version: expectedVersion + 1 }
47
+ : { ok: true, row: advance(row, at, { state: 'skipped', reason: event.failure.code, failure: event.failure, attempts: row.attempts + 1 }), version: expectedVersion + 1 };
48
+ if (event.type === 'superseded')
49
+ return { ok: true, row: null, version: expectedVersion + 1 };
50
+ return illegal('approved/' + event.type);
51
+ case 'fuse':
52
+ if (event.type === 'committed')
53
+ return { ok: true, row: null, version: expectedVersion + 1 };
54
+ if (event.type === 'skipped')
55
+ return { ok: true, row: advance(row, at, { state: 'skipped', reason: event.reason, ...(event.skipSource === undefined ? {} : { skipSource: event.skipSource }) }), version: expectedVersion + 1 };
56
+ if (event.type === 'failed')
57
+ return event.failure.retryable
58
+ ? { ok: true, row: advance(row, at, { state: 'fuse', failure: event.failure, attempts: row.attempts + 1 }), version: expectedVersion + 1 }
59
+ : { ok: true, row: advance(row, at, { state: 'skipped', reason: event.failure.code, failure: event.failure, attempts: row.attempts + 1 }), version: expectedVersion + 1 };
60
+ if (event.type === 'superseded')
61
+ return { ok: true, row: null, version: expectedVersion + 1 };
62
+ return illegal('fuse/' + event.type);
63
+ case 'skipped':
64
+ if (event.type === 'restored' || event.type === 'superseded')
65
+ return { ok: true, row: null, version: expectedVersion + 1 };
66
+ if (event.type === 'mark-skipped-expired')
67
+ return row.expiredAt !== undefined ? { ok: true, row, version: expectedVersion } : { ok: true, row: { ...row, expiredAt: event.at }, version: expectedVersion + 1 };
68
+ return illegal('skipped is terminal');
69
+ }
70
+ }
71
+ function object(value) { return value !== null && typeof value === 'object'; }
72
+ function string(value) { return typeof value === 'string' && value.length > 0; }
73
+ function number(value) { return typeof value === 'number' && Number.isFinite(value); }
74
+ export function isV4Row(value) {
75
+ if (!object(value))
76
+ return false;
77
+ if (!string(value.id) || !string(value.pairKey) || !string(value.a) || !string(value.b) || !number(value.version))
78
+ return false;
79
+ if (!string(value.createdAt) || !string(value.updatedAt) || (value.origin !== 'agent' && value.origin !== 'human' && value.origin !== 'system'))
80
+ return false;
81
+ if (typeof value.attempts !== 'number')
82
+ return false;
83
+ if (value.state === 'verify')
84
+ return value.verifySubState === 'candidate' || value.verifySubState === 'screened' || value.verifySubState === 'unverified';
85
+ if (value.state === 'pending' || value.state === 'approved' || value.state === 'fuse')
86
+ return string(value.target) && object(value.decision);
87
+ if (value.state === 'skipped')
88
+ return string(value.reason);
89
+ return false;
90
+ }
91
+ export function v3ToV4(row) {
92
+ const base = { id: row.id, pairKey: row.pairKey, a: row.a, b: row.b, version: row.version, createdAt: row.createdAt, updatedAt: row.updatedAt, origin: row.origin, attempts: row.attempts, ...(row.failure === undefined ? {} : { failure: row.failure }), ...(row.runId === undefined ? {} : { runId: row.runId }) };
93
+ if (row.state === 'verify')
94
+ return { ...base, state: 'verify', verifySubState: row.verifySubState, ...(row.sourceRevision === undefined ? {} : { sourceRevision: row.sourceRevision }) };
95
+ if (row.state === 'pending')
96
+ return { ...base, state: 'pending', target: row.target, decision: row.decision, ...(row.targetRevision === undefined ? {} : { targetRevision: row.targetRevision }), ...(row.sourceRevision === undefined ? {} : { sourceRevision: row.sourceRevision }), ...(row.expired === undefined ? {} : { expired: row.expired }) };
97
+ if (row.state === 'approved')
98
+ return { ...base, state: 'approved', target: row.target, decision: row.decision, ...(row.sourceRevision === undefined ? {} : { sourceRevision: row.sourceRevision }), ...(row.runId === undefined ? {} : { runId: row.runId }) };
99
+ if (row.state === 'fuse')
100
+ return { ...base, state: 'fuse', target: row.target, decision: row.decision, ...(row.sourceRevision === undefined ? {} : { sourceRevision: row.sourceRevision }), ...(row.runId === undefined ? {} : { runId: row.runId }) };
101
+ return { ...base, state: 'skipped', reason: row.reason, ...(row.expiredAt === undefined ? {} : { expiredAt: row.expiredAt }) };
102
+ }
103
+ export function applyV4Write(rows, write, at) {
104
+ if (write.kind === 'transition') {
105
+ const index = rows.findIndex(row => row.id === write.id);
106
+ if (index < 0)
107
+ return bad('row-not-found', 'row not found: ' + write.id);
108
+ const moved = transitionV4(rows[index], write.event, write.expectedVersion, at);
109
+ if (!moved.ok)
110
+ return bad(moved.code, moved.message);
111
+ if (moved.row === null)
112
+ return { ok: true, rows: rows.filter((_, atIndex) => atIndex !== index), row: null };
113
+ const next = [...rows];
114
+ next[index] = moved.row;
115
+ return { ok: true, rows: next, row: moved.row };
116
+ }
117
+ if (write.kind === 'remove') {
118
+ const index = rows.findIndex(row => row.id === write.id);
119
+ if (index < 0)
120
+ return bad('row-not-found', 'row not found: ' + write.id);
121
+ const current = rows[index];
122
+ if (write.terminal === 'committed' && (write.unit === undefined || write.unit.unitId.length === 0 || write.unit.evidenceSha256.length === 0))
123
+ return bad('row-invalid', 'committed remove requires commit evidence');
124
+ const event = write.terminal === 'committed' ? { type: 'committed', unitId: write.unit } : { type: write.terminal };
125
+ const moved = transitionV4(current, event, write.expectedVersion, at);
126
+ if (!moved.ok)
127
+ return bad(moved.code, moved.message);
128
+ return moved.row === null ? { ok: true, rows: rows.filter((_, atIndex) => atIndex !== index), row: null } : { ok: true, rows: rows.map((row, atIndex) => atIndex === index ? moved.row : row), row: moved.row };
129
+ }
130
+ if (write.row.version !== 0)
131
+ return bad('row-invalid', 'create requires version 0');
132
+ if (write.kind === 'createVerify' && (write.row.state !== 'verify' || write.row.verifySubState !== 'unverified'))
133
+ return bad('row-invalid', 'createVerify requires verify/unverified');
134
+ if (write.kind === 'createSkipped' && write.row.state !== 'skipped')
135
+ return bad('row-invalid', 'createSkipped requires skipped');
136
+ if (!isV4Row(write.row))
137
+ return bad('row-invalid', 'created row is invalid');
138
+ if (rows.some(row => row.id === write.row.id))
139
+ return bad('row-duplicate', 'duplicate row id: ' + write.row.id);
140
+ return { ok: true, rows: [...rows, write.row], row: write.row };
141
+ }
142
+ export function v4ToLegacy(row) {
143
+ const queue = row.state === 'verify' ? 'verify' : row.state === 'pending' ? 'pending' : row.state === 'skipped' ? 'skipped' : 'fuse';
144
+ const target = row.state === 'pending' || row.state === 'approved' || row.state === 'fuse' ? { requested: row.target } : undefined;
145
+ return {
146
+ id: row.id, pairKey: row.pairKey, a: row.a, b: row.b, state: row.state, queue,
147
+ ...(row.state === 'skipped' ? { reason: row.reason } : {}),
148
+ ...(target === undefined ? {} : { target }),
149
+ ...(row.failure === undefined ? {} : { failure: row.failure }),
150
+ ...(row.state === 'pending' || row.state === 'approved' || row.state === 'fuse' ? { decision: row.decision } : {}),
151
+ ...((row.state === 'pending' || row.state === 'approved' || row.state === 'fuse') && row.sourceRevision !== undefined ? { sourceRevision: row.sourceRevision } : {}),
152
+ ...((row.state === 'pending' || row.state === 'approved' || row.state === 'fuse') && row.targetRevision !== undefined ? { targetRevision: row.targetRevision } : {}),
153
+ ...(row.runId === undefined ? {} : { runId: row.runId }),
154
+ ...(row.origin === undefined ? {} : { origin: row.origin }),
155
+ ...(row.attempts === undefined ? {} : { attempts: row.attempts }),
156
+ createdAt: row.createdAt, updatedAt: row.updatedAt,
157
+ };
158
+ }
159
+ //# sourceMappingURL=row-v4.js.map
@@ -15,6 +15,7 @@ export interface MergeRowV2 {
15
15
  readonly unitId?: string;
16
16
  readonly origin?: 'agent' | 'human' | 'system';
17
17
  readonly attempts?: number;
18
+ readonly version?: number;
18
19
  readonly stagingRef?: string;
19
20
  readonly stagingExpiresAt?: number;
20
21
  readonly target?: {
@@ -0,0 +1,51 @@
1
+ import { type EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
2
+ export type MergeRunPhase = 'begin' | 'running' | 'awaiting-approval' | 'settled' | 'cancelled' | 'undone';
3
+ export interface MergeRunMeta {
4
+ readonly version: 1;
5
+ readonly runId: string;
6
+ readonly pairCount?: number;
7
+ /** The pair a single-pair command (pair/approve/resume) is working on; absent for scan. */
8
+ readonly pairKey?: string;
9
+ readonly parentRunId?: string;
10
+ readonly phase: MergeRunPhase;
11
+ readonly stopReason?: string;
12
+ readonly approval?: 'pending' | 'approved' | 'rejected';
13
+ readonly reportRef?: string;
14
+ readonly undonePairs?: readonly string[];
15
+ readonly undoneAt?: string;
16
+ readonly degraded?: boolean;
17
+ readonly createdAt: string;
18
+ readonly updatedAt: string;
19
+ }
20
+ export interface PairIndexEntry {
21
+ readonly version: 1;
22
+ readonly runId: string;
23
+ readonly pairKey: string;
24
+ readonly a: string;
25
+ readonly b: string;
26
+ readonly target: string;
27
+ readonly targetRevision?: string;
28
+ readonly sourceRevisions?: Readonly<Record<string, string>>;
29
+ readonly state: 'pending' | 'approved' | 'fused' | 'skipped';
30
+ readonly updatedAt: string;
31
+ }
32
+ export interface RunCoordinatorRead {
33
+ readonly runId: string;
34
+ readonly meta: MergeRunMeta | null;
35
+ readonly pairs: readonly PairIndexEntry[];
36
+ readonly consistency: 'eventual';
37
+ }
38
+ export interface RunCoordinator {
39
+ begin(meta: MergeRunMeta): Promise<void>;
40
+ recordPair(entry: PairIndexEntry): Promise<void>;
41
+ read(runId: string): Promise<RunCoordinatorRead>;
42
+ }
43
+ export declare function runMetaPath(home: string, runId: string): string;
44
+ export declare function writeRunMeta(io: EvolutionIoLike, home: string, meta: MergeRunMeta): Promise<void>;
45
+ export declare function readRunMeta(io: EvolutionIoLike, home: string, runId: string): Promise<MergeRunMeta | null>;
46
+ /** Every run-meta record on disk (the in-flight index for pair/target serialisation). */
47
+ export declare function readRunMetas(io: EvolutionIoLike, home: string): Promise<readonly MergeRunMeta[]>;
48
+ /** The run currently working on this pair, if any (pairKey is written at run begin). */
49
+ export declare function inFlightPairRun(metas: readonly MergeRunMeta[], pairKeyValue: string): MergeRunMeta | undefined;
50
+ export { recordPair } from './pair-index-store.ts';
51
+ //# sourceMappingURL=run-coordinator.d.ts.map
@@ -0,0 +1,45 @@
1
+ import { readTextOrNull, listOrEmpty } from "./read-probe.js";
2
+ /** 1b-1 additive types for the merge run lifecycle; no writer is active in this phase. */
3
+ import { join } from 'node:path';
4
+ import { transactIo } from '@lmzhen/dsh-evolution-core';
5
+ export function runMetaPath(home, runId) { return join(home, 'merge', 'run-meta', encodeURIComponent(runId) + '.json'); }
6
+ export async function writeRunMeta(io, home, meta) {
7
+ await transactIo(io, runMetaPath(home, meta.runId), () => JSON.stringify(meta, null, 2) + '\n');
8
+ }
9
+ export async function readRunMeta(io, home, runId) {
10
+ const raw = await readTextOrNull(io, runMetaPath(home, runId));
11
+ if (raw === null)
12
+ return null;
13
+ try {
14
+ return JSON.parse(raw);
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ /** Every run-meta record on disk (the in-flight index for pair/target serialisation). */
21
+ export async function readRunMetas(io, home) {
22
+ const root = join(home, 'merge', 'run-meta');
23
+ const names = await io.exists(root) ? await listOrEmpty(io, root) : [];
24
+ const metas = [];
25
+ for (const name of names) {
26
+ if (!name.endsWith('.json'))
27
+ continue;
28
+ const raw = await readTextOrNull(io, join(root, name));
29
+ if (raw === null)
30
+ continue;
31
+ try {
32
+ const meta = JSON.parse(raw);
33
+ if (typeof meta.runId === 'string' && typeof meta.phase === 'string')
34
+ metas.push(meta);
35
+ }
36
+ catch { /* malformed meta is not an in-flight claim */ }
37
+ }
38
+ return metas;
39
+ }
40
+ /** The run currently working on this pair, if any (pairKey is written at run begin). */
41
+ export function inFlightPairRun(metas, pairKeyValue) {
42
+ return metas.find(meta => meta.phase === 'running' && meta.pairKey === pairKeyValue);
43
+ }
44
+ export { recordPair } from "./pair-index-store.js";
45
+ //# sourceMappingURL=run-coordinator.js.map
@@ -0,0 +1,53 @@
1
+ import { type EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
2
+ import { type CommitUnitRef, type V4Event, type V4Row, type V4WriteResult } from './row-v4.ts';
3
+ import type { V3Row } from './row-v3.ts';
4
+ export interface MergeStateV4 {
5
+ readonly schemaVersion: 4;
6
+ readonly rows: readonly V4Row[];
7
+ readonly migration?: unknown;
8
+ readonly pre1b?: {
9
+ readonly backupRef: string;
10
+ readonly at: string;
11
+ };
12
+ }
13
+ export type V4BlockCode = 'merge-state-normalize-required' | 'merge-state-migration-required' | 'merge-state-v4-required' | 'merge-state-forward-only' | 'merge-state-needs-repair' | 'merge-state-backup-missing';
14
+ export type V4Readiness = {
15
+ readonly ok: true;
16
+ readonly schema: 4;
17
+ readonly state: MergeStateV4;
18
+ } | {
19
+ readonly ok: false;
20
+ readonly code: V4BlockCode;
21
+ readonly message: string;
22
+ };
23
+ export declare class V4BlockedError extends Error {
24
+ readonly readiness: V4Readiness;
25
+ constructor(readiness: V4Readiness);
26
+ }
27
+ export declare function schema4StatePath(home: string): string;
28
+ /** Pure read-only v4 readiness. Does not normalize, repair, or write. */
29
+ export declare function readV4Readiness(io: EvolutionIoLike, home: string): Promise<V4Readiness>;
30
+ export interface V4RowTx {
31
+ rows(): readonly V4Row[];
32
+ createVerify(row: Extract<V4Row, {
33
+ state: 'verify';
34
+ }>): V4WriteResult;
35
+ createSkipped(row: Extract<V4Row, {
36
+ state: 'skipped';
37
+ }>): V4WriteResult;
38
+ transition(id: string, event: V4Event, expectedVersion: number): V4WriteResult;
39
+ remove(id: string, terminal: 'committed' | 'superseded' | 'restored', unit: CommitUnitRef | undefined, expectedVersion: number): V4WriteResult;
40
+ }
41
+ /** Atomic readiness + V4 row write under one state-file lock. No production caller in 1b-2a. */
42
+ export declare function transactMergeState<T>(io: EvolutionIoLike, home: string, fn: (tx: V4RowTx) => T | Promise<T>): Promise<T>;
43
+ /** Pure v3-to-v4 projection for fixtures; 1b-2b wires it to the production normalize. */
44
+ export declare function normalizeV3State(state: {
45
+ readonly schemaVersion: 3;
46
+ readonly rows: readonly V3Row[];
47
+ readonly migration?: unknown;
48
+ }, pre1b: {
49
+ readonly backupRef: string;
50
+ readonly at: string;
51
+ }): MergeStateV4;
52
+ export declare function parseV4State(raw: string): MergeStateV4 | null;
53
+ //# sourceMappingURL=schema4.d.ts.map
@@ -0,0 +1,129 @@
1
+ /** Batch 1b-2a schema4 state codec, readiness, and atomic row transaction. */
2
+ import { join } from 'node:path';
3
+ import { transactIo } from '@lmzhen/dsh-evolution-core';
4
+ import { isV4Row, applyV4Write, v3ToV4 } from "./row-v4.js";
5
+ import { readPre1bManifest } from "./pre-1b-backup.js";
6
+ export class V4BlockedError extends Error {
7
+ readiness;
8
+ constructor(readiness) {
9
+ super(readiness.ok ? 'merge state v4 is ready' : readiness.code + ': ' + readiness.message);
10
+ this.name = 'V4BlockedError';
11
+ this.readiness = readiness;
12
+ }
13
+ }
14
+ export function schema4StatePath(home) { return join(home, 'merge', 'merge-state.json'); }
15
+ function blocked(code, message) { return { ok: false, code, message }; }
16
+ function object(value) { return value !== null && typeof value === 'object'; }
17
+ function string(value) { return typeof value === 'string' && value.length > 0; }
18
+ function parse(raw) {
19
+ try {
20
+ const value = JSON.parse(raw);
21
+ if (!object(value) || value.schemaVersion !== 4 || !Array.isArray(value.rows) || !value.rows.every(isV4Row))
22
+ return null;
23
+ return { body: value, rows: value.rows };
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ async function inspect(io, home, body, rows) {
30
+ const pre1b = body.pre1b;
31
+ if (!object(pre1b) || !string(pre1b.backupRef))
32
+ return blocked('merge-state-needs-repair', 'schema4 state is missing pre1b backup reference');
33
+ const manifest = await readPre1bManifest(io, home, pre1b.backupRef).catch(() => null);
34
+ if (manifest === null)
35
+ return blocked('merge-state-backup-missing', 'pre-1b backup manifest is unreadable: ' + pre1b.backupRef);
36
+ return { ok: true, schema: 4, state: { schemaVersion: 4, rows, ...(body.migration === undefined ? {} : { migration: body.migration }), pre1b: { backupRef: pre1b.backupRef, at: typeof pre1b.at === 'string' ? pre1b.at : '' } } };
37
+ }
38
+ /** Pure read-only v4 readiness. Does not normalize, repair, or write. */
39
+ export async function readV4Readiness(io, home) {
40
+ let raw;
41
+ try {
42
+ raw = await io.readText(schema4StatePath(home));
43
+ }
44
+ catch {
45
+ return blocked('merge-state-needs-repair', 'merge state is unreadable');
46
+ }
47
+ if (raw === null)
48
+ return blocked('merge-state-v4-required', 'schema4 state is missing');
49
+ let value;
50
+ try {
51
+ value = JSON.parse(raw);
52
+ }
53
+ catch {
54
+ return blocked('merge-state-needs-repair', 'merge state is not JSON');
55
+ }
56
+ if (!object(value))
57
+ return blocked('merge-state-needs-repair', 'merge state is not an object');
58
+ if (typeof value.schemaVersion === 'number' && value.schemaVersion > 4)
59
+ return blocked('merge-state-forward-only', 'state schema ' + String(value.schemaVersion) + ' is newer than this build');
60
+ if (value.schemaVersion === 3)
61
+ return blocked('merge-state-normalize-required', 'schema3 state requires v4 normalize');
62
+ if (value.schemaVersion === 2 || value.schemaVersion === 1)
63
+ return blocked('merge-state-migration-required', 'legacy state requires schema migration');
64
+ if (value.schemaVersion !== 4)
65
+ return blocked('merge-state-needs-repair', 'merge state has no supported schemaVersion');
66
+ const parsed = parse(raw);
67
+ if (parsed === null)
68
+ return blocked('merge-state-needs-repair', 'schema4 state has an invalid row');
69
+ return await inspect(io, home, parsed.body, parsed.rows);
70
+ }
71
+ /** Atomic readiness + V4 row write under one state-file lock. No production caller in 1b-2a. */
72
+ export async function transactMergeState(io, home, fn) {
73
+ let result;
74
+ await transactIo(io, schema4StatePath(home), async (current) => {
75
+ if (current === null)
76
+ throw new V4BlockedError(blocked('merge-state-v4-required', 'schema4 state is missing'));
77
+ let value;
78
+ try {
79
+ value = JSON.parse(current);
80
+ }
81
+ catch {
82
+ throw new V4BlockedError(blocked('merge-state-needs-repair', 'merge state is not JSON'));
83
+ }
84
+ if (!object(value))
85
+ throw new V4BlockedError(blocked('merge-state-needs-repair', 'merge state is not an object'));
86
+ if (typeof value.schemaVersion === 'number' && value.schemaVersion > 4)
87
+ throw new V4BlockedError(blocked('merge-state-forward-only', 'state schema ' + String(value.schemaVersion) + ' is newer than this build'));
88
+ if (value.schemaVersion === 3)
89
+ throw new V4BlockedError(blocked('merge-state-normalize-required', 'schema3 state requires v4 normalize'));
90
+ if (value.schemaVersion !== 4)
91
+ throw new V4BlockedError(blocked('merge-state-needs-repair', 'merge state is not schema4'));
92
+ const parsed = parse(current);
93
+ if (parsed === null)
94
+ throw new V4BlockedError(blocked('merge-state-needs-repair', 'schema4 state has an invalid row'));
95
+ const ready = await inspect(io, home, parsed.body, parsed.rows);
96
+ if (!ready.ok)
97
+ throw new V4BlockedError(ready);
98
+ let rows = parsed.rows;
99
+ const apply = (write) => {
100
+ const next = applyV4Write(rows, write, new Date().toISOString());
101
+ if (next.ok)
102
+ rows = next.rows;
103
+ return next;
104
+ };
105
+ const tx = {
106
+ rows: () => rows,
107
+ createVerify: row => apply({ kind: 'createVerify', row }),
108
+ createSkipped: row => apply({ kind: 'createSkipped', row }),
109
+ transition: (id, event, expectedVersion) => apply({ kind: 'transition', id, event, expectedVersion }),
110
+ remove: (id, terminal, unit, expectedVersion) => apply({ kind: 'remove', id, terminal, ...(unit === undefined ? {} : { unit }), expectedVersion }),
111
+ };
112
+ result = await fn(tx);
113
+ return JSON.stringify({ ...parsed.body, schemaVersion: 4, rows }, null, 2) + '\n';
114
+ });
115
+ if (result === undefined)
116
+ throw new V4BlockedError(blocked('merge-state-needs-repair', 'v4 transaction did not produce a result'));
117
+ return result;
118
+ }
119
+ /** Pure v3-to-v4 projection for fixtures; 1b-2b wires it to the production normalize. */
120
+ export function normalizeV3State(state, pre1b) {
121
+ return { schemaVersion: 4, rows: state.rows.map(v3ToV4), ...(state.migration === undefined ? {} : { migration: state.migration }), pre1b };
122
+ }
123
+ export function parseV4State(raw) {
124
+ const parsed = parse(raw);
125
+ if (parsed === null)
126
+ return null;
127
+ return { schemaVersion: 4, rows: parsed.rows, ...(parsed.body.migration === undefined ? {} : { migration: parsed.body.migration }), ...(object(parsed.body.pre1b) && string(parsed.body.pre1b.backupRef) ? { pre1b: { backupRef: parsed.body.pre1b.backupRef, at: typeof parsed.body.pre1b.at === 'string' ? parsed.body.pre1b.at : '' } } : {}) };
128
+ }
129
+ //# sourceMappingURL=schema4.js.map
@@ -0,0 +1,14 @@
1
+ /** Batch 3 selection policy: filter managed/protected before writing skipped. */
2
+ import type { MergeCandidate } from '../contracts.ts';
3
+ export interface SelectionLike {
4
+ readonly candidates: readonly MergeCandidate[];
5
+ readonly skipped: readonly {
6
+ readonly pairKey: string;
7
+ readonly a: string;
8
+ readonly b: string;
9
+ readonly reason?: string;
10
+ }[];
11
+ readonly reportOnly: readonly unknown[];
12
+ }
13
+ export declare function filterSelection<T extends SelectionLike>(selection: T, isManaged: (name: string) => boolean, isProtected: (name: string) => boolean): T;
14
+ //# sourceMappingURL=selection-policy.d.ts.map
@@ -0,0 +1,6 @@
1
+ export function filterSelection(selection, isManaged, isProtected) {
2
+ const skipped = selection.skipped.filter(item => isManaged(item.a) && isManaged(item.b) && !isProtected(item.a) && !isProtected(item.b));
3
+ const candidates = selection.candidates.filter(item => isManaged(item.a) && isManaged(item.b) && !isProtected(item.a) && !isProtected(item.b));
4
+ return { ...selection, candidates, skipped };
5
+ }
6
+ //# sourceMappingURL=selection-policy.js.map
@@ -0,0 +1,5 @@
1
+ /** Pure skipped-TTL planning. Reconcile owns the durable write. */
2
+ import type { V4Row } from './row-v4.ts';
3
+ /** Row ids whose skipped TTL has elapsed at `now`; the caller applies one transition each. */
4
+ export declare function planSkippedExpiry(rows: readonly V4Row[], ttlDays: number, now: number): readonly string[];
5
+ //# sourceMappingURL=skipped-expiry.d.ts.map
@@ -0,0 +1,16 @@
1
+ /** Row ids whose skipped TTL has elapsed at `now`; the caller applies one transition each. */
2
+ export function planSkippedExpiry(rows, ttlDays, now) {
3
+ if (ttlDays <= 0)
4
+ return [];
5
+ const ids = [];
6
+ for (const row of rows) {
7
+ if (row.state !== 'skipped' || row.expiredAt !== undefined)
8
+ continue;
9
+ const created = Date.parse(row.createdAt);
10
+ if (!Number.isFinite(created) || now - created < ttlDays * 86_400_000)
11
+ continue;
12
+ ids.push(row.id);
13
+ }
14
+ return ids;
15
+ }
16
+ //# sourceMappingURL=skipped-expiry.js.map