@lmzhen/dsh-evolution-merge 0.46.0 → 0.48.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 +248 -1224
- package/lib/types/adapters/engine-screen.d.ts +0 -3
- package/lib/types/authoring-flow.d.ts +5 -0
- package/lib/types/authoring-flow.js +17 -10
- package/lib/types/commit-execute.js +1 -22
- package/lib/types/contracts.d.ts +0 -1
- package/lib/types/contracts.js +0 -1
- package/lib/types/doctor.d.ts +0 -1
- package/lib/types/doctor.js +2 -31
- package/lib/types/domain/consistency.d.ts +0 -1
- package/lib/types/domain/consistency.js +2 -11
- package/lib/types/domain/merge-limits.d.ts +0 -10
- package/lib/types/domain/merge-limits.js +0 -3
- package/lib/types/domain/merge-snapshot.js +4 -6
- package/lib/types/domain/pair-ledger.d.ts +0 -1
- package/lib/types/domain/row-v3.d.ts +0 -45
- package/lib/types/domain/row-v3.js +0 -68
- package/lib/types/domain/row.d.ts +2 -84
- package/lib/types/domain/row.js +1 -110
- package/lib/types/domain/run-coordinator.d.ts +0 -24
- package/lib/types/domain/run-coordinator.js +0 -1
- package/lib/types/domain/stage-aggregate.d.ts +0 -42
- package/lib/types/domain/stage-aggregate.js +1 -116
- package/lib/types/domain/state.d.ts +1 -53
- package/lib/types/domain/state.js +0 -55
- package/lib/types/domain/undo-journal.d.ts +4 -4
- package/lib/types/domain/undo-journal.js +5 -7
- package/lib/types/index.d.ts +0 -1
- package/lib/types/index.js +0 -6
- package/lib/types/merge-routes.d.ts +5 -21
- package/lib/types/merge-routes.js +53 -93
- package/lib/types/new-child-authoring.js +4 -10
- package/lib/types/prompts.d.ts +0 -1
- package/lib/types/prompts.js +0 -1
- package/lib/types/read-commands.d.ts +0 -1
- package/lib/types/read-commands.js +0 -4
- package/lib/types/recovery-projection.js +0 -3
- package/lib/types/round2-outcome.js +15 -37
- package/lib/types/scan-run.js +0 -3
- package/lib/types/skipped.d.ts +1 -2
- package/lib/types/skipped.js +0 -8
- package/lib/types/staging.d.ts +0 -2
- package/lib/types/staging.js +0 -9
- package/lib/types/state.d.ts +1 -39
- package/lib/types/state.js +2 -78
- package/lib/types/surface/merge-view.d.ts +1 -2
- package/lib/types/surface/merge-view.js +1 -2
- package/lib/types/undo-operation.js +0 -9
- package/lib/types/unit-protocol.d.ts +6 -0
- package/lib/types/unit-protocol.js +72 -102
- package/package.json +4 -4
- package/lib/types/commit-recovery.d.ts +0 -14
- package/lib/types/commit-recovery.js +0 -96
- package/lib/types/coordinator.d.ts +0 -77
- package/lib/types/coordinator.js +0 -137
- package/lib/types/domain/commit-journal-store.d.ts +0 -6
- package/lib/types/domain/commit-journal-store.js +0 -26
- package/lib/types/domain/commit-journal.d.ts +0 -16
- package/lib/types/domain/commit-journal.js +0 -26
- package/lib/types/domain/indexes.d.ts +0 -21
- package/lib/types/domain/indexes.js +0 -15
- package/lib/types/domain/merge-report.d.ts +0 -41
- package/lib/types/domain/merge-report.js +0 -2
- package/lib/types/domain/pair-index-store.d.ts +0 -6
- package/lib/types/domain/pair-index-store.js +0 -25
- package/lib/types/domain/pair-index.d.ts +0 -9
- package/lib/types/domain/pair-index.js +0 -28
- package/lib/types/domain/resolve-pair.d.ts +0 -4
- package/lib/types/domain/resolve-pair.js +0 -4
- package/lib/types/domain/row-tx.d.ts +0 -27
- package/lib/types/domain/row-tx.js +0 -31
- package/lib/types/domain/run-record.d.ts +0 -45
- package/lib/types/domain/run-record.js +0 -24
- package/lib/types/domain/state-v3.d.ts +0 -4
- package/lib/types/domain/state-v3.js +0 -4
- package/lib/types/facts.d.ts +0 -26
- package/lib/types/facts.js +0 -65
- package/lib/types/metrics.d.ts +0 -16
- package/lib/types/metrics.js +0 -27
- package/lib/types/wire-projection.d.ts +0 -36
- package/lib/types/wire-projection.js +0 -77
package/lib/types/coordinator.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure merge transition table. No io, no run shell, no subagents.
|
|
3
|
-
*
|
|
4
|
-
* Every transition returns the whole next row (or a drop), so the caller can persist it
|
|
5
|
-
* through one state transaction. Invalid transitions are refused by name;
|
|
6
|
-
* a caller never "repairs" a row by guessing which state the operator meant.
|
|
7
|
-
*/
|
|
8
|
-
import { pairKey, queueOf, rowIdOf, } from "./contracts.js";
|
|
9
|
-
function invalid(event, state) {
|
|
10
|
-
return {
|
|
11
|
-
ok: false,
|
|
12
|
-
code: 'invalid-transition',
|
|
13
|
-
message: 'cannot apply ' + event.type + ' to ' + (state ?? 'no row'),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
/** Drop fields that must not survive a transition; optional fields stay absent. */
|
|
17
|
-
function base(row, at) {
|
|
18
|
-
return {
|
|
19
|
-
id: row.id,
|
|
20
|
-
pairKey: row.pairKey,
|
|
21
|
-
a: row.a,
|
|
22
|
-
b: row.b,
|
|
23
|
-
state: row.state,
|
|
24
|
-
queue: row.queue,
|
|
25
|
-
createdAt: row.createdAt,
|
|
26
|
-
updatedAt: at,
|
|
27
|
-
...(row.runId === undefined ? {} : { runId: row.runId }),
|
|
28
|
-
...(row.unitId === undefined ? {} : { unitId: row.unitId }),
|
|
29
|
-
...(row.origin === undefined ? {} : { origin: row.origin }),
|
|
30
|
-
...(row.decision === undefined ? {} : { decision: row.decision }),
|
|
31
|
-
...(row.sourceRevision === undefined ? {} : { sourceRevision: row.sourceRevision }),
|
|
32
|
-
...(row.targetRevision === undefined ? {} : { targetRevision: row.targetRevision }),
|
|
33
|
-
...(row.attempts === undefined ? {} : { attempts: row.attempts }),
|
|
34
|
-
...(row.stagingRef === undefined ? {} : { stagingRef: row.stagingRef }),
|
|
35
|
-
...(row.stagingExpiresAt === undefined ? {} : { stagingExpiresAt: row.stagingExpiresAt }),
|
|
36
|
-
...(row.expired === undefined ? {} : { expired: row.expired }),
|
|
37
|
-
...(row.reason === undefined ? {} : { reason: row.reason }),
|
|
38
|
-
...(row.target === undefined ? {} : { target: row.target }),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
function next(row, at, state, patch = {}) {
|
|
42
|
-
return {
|
|
43
|
-
ok: true,
|
|
44
|
-
row: {
|
|
45
|
-
...base(row, at),
|
|
46
|
-
state,
|
|
47
|
-
queue: queueOf(state),
|
|
48
|
-
...patch,
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/** Legacy v2 projection only; runtime writes must use transitionV3/RowTx. */
|
|
53
|
-
export function transitionLegacyForProjection(current, event) {
|
|
54
|
-
if (event.type === 'candidate') {
|
|
55
|
-
if (current !== undefined)
|
|
56
|
-
return { ok: true, row: current };
|
|
57
|
-
const key = pairKey(event.a, event.b);
|
|
58
|
-
return {
|
|
59
|
-
ok: true,
|
|
60
|
-
row: {
|
|
61
|
-
id: rowIdOf(key),
|
|
62
|
-
pairKey: key,
|
|
63
|
-
a: event.a,
|
|
64
|
-
b: event.b,
|
|
65
|
-
state: 'verify',
|
|
66
|
-
queue: 'verify',
|
|
67
|
-
createdAt: event.at,
|
|
68
|
-
updatedAt: event.at,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
if (event.type === 'source-gone')
|
|
73
|
-
return { ok: true, row: null };
|
|
74
|
-
if (event.type === 'gate-keep') {
|
|
75
|
-
if (current !== undefined)
|
|
76
|
-
return { ok: true, row: current };
|
|
77
|
-
const key = pairKey(event.a, event.b);
|
|
78
|
-
return {
|
|
79
|
-
ok: true,
|
|
80
|
-
row: {
|
|
81
|
-
id: rowIdOf(key),
|
|
82
|
-
pairKey: key,
|
|
83
|
-
a: event.a,
|
|
84
|
-
b: event.b,
|
|
85
|
-
state: 'skipped',
|
|
86
|
-
queue: 'skipped',
|
|
87
|
-
origin: 'agent',
|
|
88
|
-
createdAt: event.at,
|
|
89
|
-
updatedAt: event.at,
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
if (current === undefined)
|
|
94
|
-
return invalid(event, undefined);
|
|
95
|
-
switch (event.type) {
|
|
96
|
-
case 'round1-keep':
|
|
97
|
-
return current.state === 'verify' ? next(current, event.at, 'skipped', { origin: 'agent' }) : invalid(event, current.state);
|
|
98
|
-
case 'round1-merge':
|
|
99
|
-
if (current.state !== 'verify')
|
|
100
|
-
return invalid(event, current.state);
|
|
101
|
-
return event.approval === 'on'
|
|
102
|
-
? next(current, event.at, 'pending', { ...(event.decision === undefined ? {} : { decision: event.decision }), ...(event.stagingRef === undefined ? {} : { stagingRef: event.stagingRef }), ...(event.stagingExpiresAt === undefined ? {} : { stagingExpiresAt: event.stagingExpiresAt }), ...(event.sourceRevision === undefined ? {} : { sourceRevision: event.sourceRevision }), ...(event.targetRevision === undefined ? {} : { targetRevision: event.targetRevision }) })
|
|
103
|
-
: next(current, event.at, 'approved', { ...(event.decision === undefined ? {} : { decision: event.decision }), ...(event.stagingRef === undefined ? {} : { stagingRef: event.stagingRef }), ...(event.stagingExpiresAt === undefined ? {} : { stagingExpiresAt: event.stagingExpiresAt }), ...(event.sourceRevision === undefined ? {} : { sourceRevision: event.sourceRevision }), ...(event.targetRevision === undefined ? {} : { targetRevision: event.targetRevision }) });
|
|
104
|
-
case 'round1-fail':
|
|
105
|
-
return current.state === 'verify'
|
|
106
|
-
? next(current, event.at, 'verify', { failure: event.failure, attempts: (current.attempts ?? 0) + 1 })
|
|
107
|
-
: invalid(event, current.state);
|
|
108
|
-
case 'approve':
|
|
109
|
-
return current.state === 'pending'
|
|
110
|
-
? next(current, event.at, 'approved', (event.runId === undefined ? {} : { runId: event.runId }))
|
|
111
|
-
: invalid(event, current.state);
|
|
112
|
-
case 'reject':
|
|
113
|
-
return current.state === 'pending' ? next(current, event.at, 'skipped', { origin: 'human' }) : invalid(event, current.state);
|
|
114
|
-
case 'round2-cancel':
|
|
115
|
-
return current.state === 'fuse' || current.state === 'approved'
|
|
116
|
-
? next(current, event.at, 'approved')
|
|
117
|
-
: invalid(event, current.state);
|
|
118
|
-
case 'round2-fail':
|
|
119
|
-
return current.state === 'fuse' || current.state === 'approved'
|
|
120
|
-
? next(current, event.at, 'approved', { failure: event.failure, attempts: (current.attempts ?? 0) + 1 })
|
|
121
|
-
: invalid(event, current.state);
|
|
122
|
-
case 'round2-success':
|
|
123
|
-
return current.state === 'fuse' || current.state === 'approved'
|
|
124
|
-
? { ok: true, row: null }
|
|
125
|
-
: invalid(event, current.state);
|
|
126
|
-
case 'target-taken':
|
|
127
|
-
case 'source-changed':
|
|
128
|
-
return current.state === 'pending' || current.state === 'approved' || current.state === 'fuse'
|
|
129
|
-
? next(current, event.at, current.state, { expired: event.type })
|
|
130
|
-
: invalid(event, current.state);
|
|
131
|
-
case 'restore':
|
|
132
|
-
return current.state === 'skipped'
|
|
133
|
-
? next(current, event.at, 'verify')
|
|
134
|
-
: invalid(event, current.state);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
//# sourceMappingURL=coordinator.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
-
import type { CommitJournal } from './commit-journal.ts';
|
|
3
|
-
export declare function journalPath(home: string, runId: string): string;
|
|
4
|
-
export declare function writeJournal(io: EvolutionIoLike, home: string, journal: CommitJournal): Promise<string>;
|
|
5
|
-
export declare function readJournal(io: EvolutionIoLike, home: string, runId: string): Promise<CommitJournal | null>;
|
|
6
|
-
//# sourceMappingURL=commit-journal-store.d.ts.map
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Durable commit journal: <home>/merge/journals/<run>.json. */
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
export function journalPath(home, runId) {
|
|
4
|
-
return join(home, 'merge', 'journals', runId.replace(/[^A-Za-z0-9_-]+/g, '_') + '.json');
|
|
5
|
-
}
|
|
6
|
-
export async function writeJournal(io, home, journal) {
|
|
7
|
-
const path = journalPath(home, journal.runId);
|
|
8
|
-
const content = JSON.stringify(journal, null, 2);
|
|
9
|
-
if (io.transact !== undefined)
|
|
10
|
-
await io.transact(path, () => content);
|
|
11
|
-
else
|
|
12
|
-
await io.writeText(path, content);
|
|
13
|
-
return path;
|
|
14
|
-
}
|
|
15
|
-
export async function readJournal(io, home, runId) {
|
|
16
|
-
const raw = await io.readText(journalPath(home, runId));
|
|
17
|
-
if (raw === null)
|
|
18
|
-
return null;
|
|
19
|
-
try {
|
|
20
|
-
return JSON.parse(raw);
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=commit-journal-store.js.map
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/** Pure commit-journal phases for target-first merge. */
|
|
2
|
-
export type JournalPhase = 'prepared' | 'target-written' | 'sources-archived' | 'committed' | 'rolled-back' | 'failed';
|
|
3
|
-
export interface CommitJournal {
|
|
4
|
-
readonly runId: string;
|
|
5
|
-
readonly unitId?: string;
|
|
6
|
-
readonly target: string;
|
|
7
|
-
readonly create: boolean;
|
|
8
|
-
readonly sources: readonly string[];
|
|
9
|
-
readonly phase: JournalPhase;
|
|
10
|
-
readonly updatedAt: string;
|
|
11
|
-
}
|
|
12
|
-
export declare function advanceJournal(journal: CommitJournal, phase: JournalPhase, at: string): CommitJournal;
|
|
13
|
-
export type JournalRecovery = 'none' | 'retry-commit' | 'rollback-target' | 'rollback-sources' | 'refuse';
|
|
14
|
-
/** What a restart may do with the last durable phase. */
|
|
15
|
-
export declare function recoveryFor(journal: CommitJournal): JournalRecovery;
|
|
16
|
-
//# sourceMappingURL=commit-journal.d.ts.map
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const NEXT = {
|
|
2
|
-
prepared: ['committed', 'target-written', 'failed'],
|
|
3
|
-
'target-written': ['sources-archived', 'rolled-back', 'failed'],
|
|
4
|
-
'sources-archived': ['committed', 'rolled-back', 'failed'],
|
|
5
|
-
committed: [],
|
|
6
|
-
'rolled-back': [],
|
|
7
|
-
failed: [],
|
|
8
|
-
};
|
|
9
|
-
export function advanceJournal(journal, phase, at) {
|
|
10
|
-
if (!NEXT[journal.phase].includes(phase))
|
|
11
|
-
throw new Error('invalid journal transition: ' + journal.phase + ' -> ' + phase);
|
|
12
|
-
return { ...journal, phase, updatedAt: at };
|
|
13
|
-
}
|
|
14
|
-
/** What a restart may do with the last durable phase. */
|
|
15
|
-
export function recoveryFor(journal) {
|
|
16
|
-
switch (journal.phase) {
|
|
17
|
-
case 'prepared': return 'retry-commit';
|
|
18
|
-
case 'target-written': return 'rollback-target';
|
|
19
|
-
case 'sources-archived': return 'rollback-sources';
|
|
20
|
-
case 'committed':
|
|
21
|
-
case 'rolled-back':
|
|
22
|
-
case 'failed': return 'none';
|
|
23
|
-
default: return 'refuse';
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=commit-journal.js.map
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/** Pure pairKey result index and archived-history projection. No IO. */
|
|
2
|
-
export interface PairIndexEntryV2 {
|
|
3
|
-
readonly pairKey: string;
|
|
4
|
-
readonly runId: string;
|
|
5
|
-
readonly result: 'merged' | 'kept' | 'failed' | 'cancelled' | 'undone';
|
|
6
|
-
readonly target?: string;
|
|
7
|
-
readonly reportRef?: string;
|
|
8
|
-
}
|
|
9
|
-
export type HistoryKind = 'merged' | 'deleted' | 'split' | 'other';
|
|
10
|
-
export interface HistoryRecord {
|
|
11
|
-
readonly name: string;
|
|
12
|
-
readonly kind: HistoryKind;
|
|
13
|
-
readonly reason: string;
|
|
14
|
-
readonly lastVersion: number;
|
|
15
|
-
readonly archivedAt: string;
|
|
16
|
-
readonly sourceRunId?: string;
|
|
17
|
-
}
|
|
18
|
-
export declare function putPair(index: ReadonlyMap<string, PairIndexEntryV2>, entry: PairIndexEntryV2): Map<string, PairIndexEntryV2>;
|
|
19
|
-
export declare function historyRecord(input: HistoryRecord): HistoryRecord;
|
|
20
|
-
export declare function summarizeHistory(records: readonly HistoryRecord[]): Record<HistoryKind, number>;
|
|
21
|
-
//# sourceMappingURL=indexes.d.ts.map
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export function putPair(index, entry) {
|
|
2
|
-
const next = new Map(index);
|
|
3
|
-
next.set(entry.pairKey, entry);
|
|
4
|
-
return next;
|
|
5
|
-
}
|
|
6
|
-
export function historyRecord(input) {
|
|
7
|
-
return { ...input, lastVersion: Math.max(1, Math.trunc(input.lastVersion)) };
|
|
8
|
-
}
|
|
9
|
-
export function summarizeHistory(records) {
|
|
10
|
-
const counts = { merged: 0, deleted: 0, split: 0, other: 0 };
|
|
11
|
-
for (const record of records)
|
|
12
|
-
counts[record.kind] += 1;
|
|
13
|
-
return counts;
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=indexes.js.map
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/** 1b-1 additive report schema; values are filled by later batches. */
|
|
2
|
-
export interface MergeReportLimits {
|
|
3
|
-
readonly maxSkillFileBytes?: number;
|
|
4
|
-
readonly maxTokenBytes?: number;
|
|
5
|
-
readonly pairTimeoutMinutes?: number;
|
|
6
|
-
}
|
|
7
|
-
export interface MergeReportValidation {
|
|
8
|
-
readonly ok: boolean;
|
|
9
|
-
readonly errors: readonly string[];
|
|
10
|
-
}
|
|
11
|
-
export interface MergeReport {
|
|
12
|
-
readonly schemaVersion: 2;
|
|
13
|
-
readonly reportId: string;
|
|
14
|
-
readonly runId: string;
|
|
15
|
-
readonly pairKey: string;
|
|
16
|
-
readonly target: string;
|
|
17
|
-
readonly sourceRevisions?: Readonly<Record<string, string>>;
|
|
18
|
-
readonly targetRevision?: string;
|
|
19
|
-
readonly validation: MergeReportValidation;
|
|
20
|
-
readonly limits: MergeReportLimits;
|
|
21
|
-
readonly q09?: {
|
|
22
|
-
readonly forwardRefs: readonly string[];
|
|
23
|
-
readonly reverseRefs: readonly string[];
|
|
24
|
-
readonly orphanFiles: readonly string[];
|
|
25
|
-
};
|
|
26
|
-
readonly threat?: {
|
|
27
|
-
readonly body: string;
|
|
28
|
-
readonly source: string;
|
|
29
|
-
};
|
|
30
|
-
readonly createdAt: string;
|
|
31
|
-
}
|
|
32
|
-
export interface MergeReportRef {
|
|
33
|
-
readonly path: string;
|
|
34
|
-
readonly digest: string;
|
|
35
|
-
readonly writtenAt: string;
|
|
36
|
-
}
|
|
37
|
-
export interface MergeReportWriter {
|
|
38
|
-
write(report: MergeReport): Promise<MergeReportRef>;
|
|
39
|
-
read(runId: string): Promise<MergeReport | null>;
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=merge-report.d.ts.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
-
import type { PairIndexEntryV2 } from './indexes.ts';
|
|
3
|
-
export declare function pairIndexPath(home: string): string;
|
|
4
|
-
export declare function loadPairIndex(io: EvolutionIoLike, home: string): Promise<Map<string, PairIndexEntryV2>>;
|
|
5
|
-
export declare function recordPair(io: EvolutionIoLike, home: string, entry: PairIndexEntryV2): Promise<void>;
|
|
6
|
-
//# sourceMappingURL=pair-index-store.d.ts.map
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/** Durable pairKey index at <home>/merge/pair-index.json. */
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { transactIo } from '@lmzhen/dsh-evolution-core';
|
|
4
|
-
import { parsePairIndex, serializePairIndex } from "./pair-index.js";
|
|
5
|
-
export function pairIndexPath(home) {
|
|
6
|
-
return join(home, 'merge', 'pair-index.json');
|
|
7
|
-
}
|
|
8
|
-
export async function loadPairIndex(io, home) {
|
|
9
|
-
let raw = null;
|
|
10
|
-
try {
|
|
11
|
-
raw = await io.readText(pairIndexPath(home));
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return new Map();
|
|
15
|
-
}
|
|
16
|
-
return parsePairIndex(raw);
|
|
17
|
-
}
|
|
18
|
-
export async function recordPair(io, home, entry) {
|
|
19
|
-
await transactIo(io, pairIndexPath(home), (current) => {
|
|
20
|
-
const index = parsePairIndex(current);
|
|
21
|
-
index.set(entry.pairKey, entry);
|
|
22
|
-
return serializePairIndex(index);
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=pair-index-store.js.map
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Pure pairKey index serialization. No IO. */
|
|
2
|
-
import type { PairIndexEntryV2 } from './indexes.ts';
|
|
3
|
-
export interface PairIndexFile {
|
|
4
|
-
readonly schemaVersion: 1;
|
|
5
|
-
readonly entries: readonly PairIndexEntryV2[];
|
|
6
|
-
}
|
|
7
|
-
export declare function parsePairIndex(raw: string | null): Map<string, PairIndexEntryV2>;
|
|
8
|
-
export declare function serializePairIndex(index: ReadonlyMap<string, PairIndexEntryV2>): string;
|
|
9
|
-
//# sourceMappingURL=pair-index.d.ts.map
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export function parsePairIndex(raw) {
|
|
2
|
-
const index = new Map();
|
|
3
|
-
if (raw === null)
|
|
4
|
-
return index;
|
|
5
|
-
let parsed;
|
|
6
|
-
try {
|
|
7
|
-
parsed = JSON.parse(raw);
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
return index;
|
|
11
|
-
}
|
|
12
|
-
const body = parsed;
|
|
13
|
-
if (!Array.isArray(body.entries))
|
|
14
|
-
return index;
|
|
15
|
-
for (const value of body.entries) {
|
|
16
|
-
const entry = value;
|
|
17
|
-
if (typeof entry.pairKey !== 'string' || typeof entry.runId !== 'string' || typeof entry.result !== 'string')
|
|
18
|
-
continue;
|
|
19
|
-
if (!['merged', 'kept', 'failed', 'cancelled', 'undone'].includes(entry.result))
|
|
20
|
-
continue;
|
|
21
|
-
index.set(entry.pairKey, entry);
|
|
22
|
-
}
|
|
23
|
-
return index;
|
|
24
|
-
}
|
|
25
|
-
export function serializePairIndex(index) {
|
|
26
|
-
return JSON.stringify({ schemaVersion: 1, entries: [...index.values()] }, null, 2);
|
|
27
|
-
}
|
|
28
|
-
//# sourceMappingURL=pair-index.js.map
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/** Pure pair-index lookup. No run-record merge fields. */
|
|
2
|
-
import type { PairIndexEntryV2 } from './indexes.ts';
|
|
3
|
-
export declare function findPairRun(pairs: readonly PairIndexEntryV2[], pairKey: string): PairIndexEntryV2 | undefined;
|
|
4
|
-
//# sourceMappingURL=resolve-pair.d.ts.map
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/** G3-5 RowTx pure write API: only createVerify/createSkipped/transition. */
|
|
2
|
-
import { type SkippedRowV3, type V3Event, type V3Row, type VerifyRowV3 } from './row-v3.ts';
|
|
3
|
-
export type RowWrite = {
|
|
4
|
-
readonly kind: 'createVerify';
|
|
5
|
-
readonly row: VerifyRowV3;
|
|
6
|
-
} | {
|
|
7
|
-
readonly kind: 'createSkipped';
|
|
8
|
-
readonly row: SkippedRowV3;
|
|
9
|
-
} | {
|
|
10
|
-
readonly kind: 'transition';
|
|
11
|
-
readonly id: string;
|
|
12
|
-
readonly event: V3Event;
|
|
13
|
-
readonly expectedVersion: number;
|
|
14
|
-
};
|
|
15
|
-
type RowWriteErrorCode = 'row-invalid' | 'row-duplicate' | 'row-not-found' | 'version-conflict' | 'illegal-transition';
|
|
16
|
-
export type RowWriteResult = {
|
|
17
|
-
readonly ok: true;
|
|
18
|
-
readonly rows: readonly V3Row[];
|
|
19
|
-
readonly row: V3Row | null;
|
|
20
|
-
} | {
|
|
21
|
-
readonly ok: false;
|
|
22
|
-
readonly code: RowWriteErrorCode;
|
|
23
|
-
readonly message: string;
|
|
24
|
-
};
|
|
25
|
-
export declare function applyRowWrite(rows: readonly V3Row[], write: RowWrite, at: string): RowWriteResult;
|
|
26
|
-
export {};
|
|
27
|
-
//# sourceMappingURL=row-tx.d.ts.map
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/** G3-5 RowTx pure write API: only createVerify/createSkipped/transition. */
|
|
2
|
-
import { isV3Row, transitionV3 } from "./row-v3.js";
|
|
3
|
-
function bad(code, message) { return { ok: false, code, message }; }
|
|
4
|
-
export function applyRowWrite(rows, write, at) {
|
|
5
|
-
if (write.kind === 'transition') {
|
|
6
|
-
const index = rows.findIndex(row => row.id === write.id);
|
|
7
|
-
if (index < 0)
|
|
8
|
-
return bad('row-not-found', 'row not found: ' + write.id);
|
|
9
|
-
const current = rows[index];
|
|
10
|
-
const moved = transitionV3(current, write.event, write.expectedVersion, at);
|
|
11
|
-
if (!moved.ok)
|
|
12
|
-
return bad(moved.code, moved.message);
|
|
13
|
-
if (moved.row === null)
|
|
14
|
-
return { ok: true, rows: rows.filter((_, atIndex) => atIndex !== index), row: null };
|
|
15
|
-
const next = [...rows];
|
|
16
|
-
next[index] = moved.row;
|
|
17
|
-
return { ok: true, rows: next, row: moved.row };
|
|
18
|
-
}
|
|
19
|
-
if (write.row.version !== 0)
|
|
20
|
-
return bad('row-invalid', 'create requires version 0');
|
|
21
|
-
if (write.kind === 'createVerify' && (write.row.state !== 'verify' || write.row.verifySubState !== 'unverified'))
|
|
22
|
-
return bad('row-invalid', 'createVerify requires verify/unverified');
|
|
23
|
-
if (write.kind === 'createSkipped' && write.row.state !== 'skipped')
|
|
24
|
-
return bad('row-invalid', 'createSkipped requires skipped');
|
|
25
|
-
if (!isV3Row(write.row))
|
|
26
|
-
return bad('row-invalid', 'created row is invalid');
|
|
27
|
-
if (rows.some(row => row.id === write.row.id))
|
|
28
|
-
return bad('row-duplicate', 'duplicate row id: ' + write.row.id);
|
|
29
|
-
return { ok: true, rows: [...rows, write.row], row: write.row };
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=row-tx.js.map
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/** Pure RunRecord v2 types and derived 9-state projection. No IO. */
|
|
2
|
-
export type RunStatus = 'running' | 'succeeded' | 'failed' | 'cancelled' | 'interrupted' | 'orphan';
|
|
3
|
-
export type MergeStatus = 'running' | 'succeeded' | 'failed' | 'cancelled' | 'awaiting-approval' | 'undone' | 'superseded' | 'interrupted' | 'orphan';
|
|
4
|
-
export type PairResult = 'pending' | 'merged' | 'kept' | 'failed' | 'cancelled' | 'undone';
|
|
5
|
-
export interface PairRecord {
|
|
6
|
-
readonly pairKey: string;
|
|
7
|
-
readonly a: string;
|
|
8
|
-
readonly b: string;
|
|
9
|
-
readonly result: PairResult;
|
|
10
|
-
readonly target?: {
|
|
11
|
-
readonly requested: string;
|
|
12
|
-
readonly actual?: string;
|
|
13
|
-
readonly created?: boolean;
|
|
14
|
-
};
|
|
15
|
-
readonly stagingRef?: string;
|
|
16
|
-
readonly committed?: boolean;
|
|
17
|
-
}
|
|
18
|
-
export interface PairIndexEntry {
|
|
19
|
-
readonly pairKey: string;
|
|
20
|
-
readonly runId: string;
|
|
21
|
-
readonly result: PairResult;
|
|
22
|
-
readonly target?: string;
|
|
23
|
-
readonly reportRef?: string;
|
|
24
|
-
}
|
|
25
|
-
export interface RunRecordV2 {
|
|
26
|
-
readonly id: string;
|
|
27
|
-
readonly kind: 'merge';
|
|
28
|
-
readonly status: RunStatus;
|
|
29
|
-
readonly stage?: 'screening' | 'decide' | 'author' | 'commit' | 'settle';
|
|
30
|
-
readonly approval?: 'on' | 'off';
|
|
31
|
-
readonly parentRunId?: string;
|
|
32
|
-
readonly pairKey?: string;
|
|
33
|
-
readonly plane?: 'local' | 'foreign';
|
|
34
|
-
readonly startedAt: number;
|
|
35
|
-
readonly updatedAt: number;
|
|
36
|
-
readonly endedAt?: number;
|
|
37
|
-
readonly resultRef?: string;
|
|
38
|
-
readonly failure?: string;
|
|
39
|
-
readonly stagingRef?: string;
|
|
40
|
-
readonly pairs: readonly PairRecord[];
|
|
41
|
-
readonly supersededAt?: number;
|
|
42
|
-
}
|
|
43
|
-
export declare function deriveMergeStatus(record: RunRecordV2): MergeStatus;
|
|
44
|
-
export declare function allCommittedUndone(record: RunRecordV2): boolean;
|
|
45
|
-
//# sourceMappingURL=run-record.d.ts.map
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export function deriveMergeStatus(record) {
|
|
2
|
-
if (record.supersededAt !== undefined)
|
|
3
|
-
return 'superseded';
|
|
4
|
-
if (record.status === 'interrupted')
|
|
5
|
-
return 'interrupted';
|
|
6
|
-
if (record.status === 'orphan')
|
|
7
|
-
return 'orphan';
|
|
8
|
-
if (record.status === 'failed' || record.status === 'cancelled')
|
|
9
|
-
return record.status;
|
|
10
|
-
if (record.status === 'running') {
|
|
11
|
-
return record.approval === 'on' && record.pairs.some(pair => pair.result === 'pending')
|
|
12
|
-
? 'awaiting-approval'
|
|
13
|
-
: 'running';
|
|
14
|
-
}
|
|
15
|
-
const committed = record.pairs.filter(pair => pair.committed === true);
|
|
16
|
-
if (committed.length > 0 && committed.every(pair => pair.result === 'undone'))
|
|
17
|
-
return 'undone';
|
|
18
|
-
return 'succeeded';
|
|
19
|
-
}
|
|
20
|
-
export function allCommittedUndone(record) {
|
|
21
|
-
const committed = record.pairs.filter(pair => pair.committed === true);
|
|
22
|
-
return committed.length > 0 && committed.every(pair => pair.result === 'undone');
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=run-record.js.map
|
package/lib/types/facts.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** G5-2 Facts pure projection. Reads a snapshot; never writes. */
|
|
2
|
-
import type { EvolutionIoLike, RunRecord } from '@lmzhen/dsh-evolution-core';
|
|
3
|
-
import type { MergeRow } from './contracts.ts';
|
|
4
|
-
import type { Facts, MergeCounts, RunView } from './wire.ts';
|
|
5
|
-
import type { MergeRunMeta } from './domain/run-coordinator.ts';
|
|
6
|
-
export declare function countsOf(rows: readonly MergeRow[]): MergeCounts;
|
|
7
|
-
export declare function toRunView(record: RunRecord, readAt: number, meta?: MergeRunMeta | null): RunView;
|
|
8
|
-
export interface FactsInput {
|
|
9
|
-
readonly rows: readonly MergeRow[];
|
|
10
|
-
readonly runs: readonly RunRecord[];
|
|
11
|
-
readonly runMeta?: ReadonlyMap<string, MergeRunMeta>;
|
|
12
|
-
readonly approval: boolean;
|
|
13
|
-
readonly readAt: number;
|
|
14
|
-
readonly notes?: readonly string[];
|
|
15
|
-
}
|
|
16
|
-
export interface ReadFactsOptions {
|
|
17
|
-
readonly io: EvolutionIoLike;
|
|
18
|
-
readonly home: string;
|
|
19
|
-
readonly runs: readonly RunRecord[];
|
|
20
|
-
readonly approval: boolean;
|
|
21
|
-
readonly readAt: number;
|
|
22
|
-
}
|
|
23
|
-
/** GET projection: reads state + runs; never writes. */
|
|
24
|
-
export declare function readFacts(options: ReadFactsOptions): Promise<Facts>;
|
|
25
|
-
export declare function buildFacts(input: FactsInput): Facts;
|
|
26
|
-
//# sourceMappingURL=facts.d.ts.map
|
package/lib/types/facts.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { MERGE_COMMAND_SURFACE } from "./command-surface.js";
|
|
2
|
-
import { readMergeSnapshot } from "./domain/merge-snapshot.js";
|
|
3
|
-
import { buildFacts as buildSnapshotFacts } from "./surface/merge-view.js";
|
|
4
|
-
const queueOf = (row) => row.state === 'approved' ? 'fuse' : row.queue;
|
|
5
|
-
const isExpired = (row) => row.expired !== undefined;
|
|
6
|
-
export function countsOf(rows) {
|
|
7
|
-
const active = { verify: 0, pending: 0, fuse: 0, skipped: 0 };
|
|
8
|
-
let expired = 0;
|
|
9
|
-
for (const row of rows) {
|
|
10
|
-
if (isExpired(row)) {
|
|
11
|
-
expired += 1;
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
active[queueOf(row)] += 1;
|
|
15
|
-
}
|
|
16
|
-
return { active, expired };
|
|
17
|
-
}
|
|
18
|
-
export function toRunView(record, readAt, meta = null) {
|
|
19
|
-
const state = record.state;
|
|
20
|
-
void meta;
|
|
21
|
-
return {
|
|
22
|
-
wireVersion: 1,
|
|
23
|
-
readAt,
|
|
24
|
-
id: record.id,
|
|
25
|
-
state,
|
|
26
|
-
command: record.command ?? 'scan',
|
|
27
|
-
...(record.stage === undefined ? {} : { stage: record.stage }),
|
|
28
|
-
progress: record.progress === undefined ? null : { done: record.progress.done, total: record.progress.total },
|
|
29
|
-
startedAt: record.startedAt,
|
|
30
|
-
...(record.endedAt === undefined ? {} : { endedAt: record.endedAt }),
|
|
31
|
-
...(record.failure === undefined ? {} : { failure: { stage: 'settle', code: 'run-failed', retryable: false, retryFrom: 'none', detail: record.failure } }),
|
|
32
|
-
...(record.parentRunId === undefined ? {} : { parentRunId: record.parentRunId }),
|
|
33
|
-
actions: [
|
|
34
|
-
{ id: 'status', command: '/evolution merge status ' + record.id, confirm: false, enabled: true },
|
|
35
|
-
{ id: 'report', command: '/evolution merge report ' + record.id, confirm: false, enabled: record.resultRef !== undefined },
|
|
36
|
-
{ id: 'cancel', command: '/evolution merge cancel ' + record.id, confirm: false, enabled: state === 'running' },
|
|
37
|
-
],
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
/** GET projection: reads state + runs; never writes. */
|
|
41
|
-
export async function readFacts(options) {
|
|
42
|
-
const snapshot = await readMergeSnapshot({ io: options.io, home: options.home, runs: options.runs, approval: options.approval, now: () => options.readAt });
|
|
43
|
-
if (snapshot.blocked !== undefined)
|
|
44
|
-
throw new Error(snapshot.blocked.code + ': ' + snapshot.blocked.message);
|
|
45
|
-
return buildSnapshotFacts(snapshot);
|
|
46
|
-
}
|
|
47
|
-
export function buildFacts(input) {
|
|
48
|
-
const ordered = [...input.runs].sort((a, b) => b.startedAt - a.startedAt);
|
|
49
|
-
const active = ordered.find(run => run.state === 'running');
|
|
50
|
-
const last = ordered.find(run => run.state !== 'running');
|
|
51
|
-
const notes = input.notes ?? [];
|
|
52
|
-
return {
|
|
53
|
-
wireVersion: 1,
|
|
54
|
-
readAt: input.readAt,
|
|
55
|
-
approval: { mergeApproval: input.approval },
|
|
56
|
-
counts: countsOf(input.rows),
|
|
57
|
-
active: active === undefined ? null : toRunView(active, input.readAt, input.runMeta?.get(active.id) ?? null),
|
|
58
|
-
last: last === undefined ? null : toRunView(last, input.readAt, input.runMeta?.get(last.id) ?? null),
|
|
59
|
-
startCommands: { scan: '/evolution merge scan', pair: '/evolution merge pair <a> <b>' },
|
|
60
|
-
fallback: MERGE_COMMAND_SURFACE.fallback,
|
|
61
|
-
notes,
|
|
62
|
-
degraded: notes.length > 0,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
//# sourceMappingURL=facts.js.map
|
package/lib/types/metrics.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/** RG6 merge metrics projection: stable names/labels from state + runs. */
|
|
2
|
-
import type { RunRecord } from '@lmzhen/dsh-evolution-core';
|
|
3
|
-
import type { MergeRow } from './contracts.ts';
|
|
4
|
-
export interface MetricSample {
|
|
5
|
-
readonly name: string;
|
|
6
|
-
readonly labels: Readonly<Record<string, string>>;
|
|
7
|
-
readonly value: number;
|
|
8
|
-
}
|
|
9
|
-
export interface MergeMetricCounters {
|
|
10
|
-
readonly lockWaitMs?: number;
|
|
11
|
-
readonly claimConflict?: number;
|
|
12
|
-
readonly recoveryFail?: number;
|
|
13
|
-
}
|
|
14
|
-
export declare const MERGE_METRIC_NAMES: readonly ["merge_queue_depth", "merge_run_total", "merge_run_duration_ms", "merge_lock_wait_ms", "merge_claim_conflict_total", "merge_recovery_fail_total"];
|
|
15
|
-
export declare function buildMergeMetrics(rows: readonly MergeRow[], runs: readonly RunRecord[], counters?: MergeMetricCounters): readonly MetricSample[];
|
|
16
|
-
//# sourceMappingURL=metrics.d.ts.map
|