@lmzhen/dsh-evolution-merge 0.30.17 → 0.30.18
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/README.md +4 -3
- package/lib/index.js +2321 -854
- package/lib/types/adapters/library.js +4 -1
- package/lib/types/adapters/model.d.ts +31 -0
- package/lib/types/adapters/model.js +46 -0
- package/lib/types/approve-command.d.ts +2 -0
- package/lib/types/approve-command.js +41 -46
- package/lib/types/authoring-flow.d.ts +3 -1
- package/lib/types/authoring-flow.js +15 -5
- package/lib/types/command-surface.d.ts +5 -0
- package/lib/types/command-surface.js +44 -0
- package/lib/types/commit-assembly.d.ts +2 -0
- package/lib/types/commit-assembly.js +7 -2
- package/lib/types/commit-execute.d.ts +7 -1
- package/lib/types/commit-execute.js +40 -10
- package/lib/types/commit-recovery.d.ts +7 -2
- package/lib/types/commit-recovery.js +73 -96
- package/lib/types/contracts.d.ts +1 -1
- package/lib/types/contracts.js +1 -1
- package/lib/types/coordinator.d.ts +3 -2
- package/lib/types/coordinator.js +4 -3
- package/lib/types/decision.js +1 -1
- package/lib/types/doctor.d.ts +7 -0
- package/lib/types/doctor.js +104 -4
- package/lib/types/domain/claim.d.ts +4 -1
- package/lib/types/domain/claim.js +2 -2
- package/lib/types/domain/commit-journal-store.js +5 -1
- package/lib/types/domain/commit-journal.js +1 -1
- package/lib/types/domain/consistency.d.ts +22 -0
- package/lib/types/domain/consistency.js +50 -0
- package/lib/types/domain/ledger.js +10 -1
- package/lib/types/domain/migrate-v3.d.ts +20 -0
- package/lib/types/domain/migrate-v3.js +68 -0
- package/lib/types/domain/row-tx.d.ts +27 -0
- package/lib/types/domain/row-tx.js +31 -0
- package/lib/types/domain/row-v3-bridge.d.ts +6 -0
- package/lib/types/domain/row-v3-bridge.js +47 -0
- package/lib/types/domain/row-v3.d.ts +113 -0
- package/lib/types/domain/row-v3.js +107 -0
- package/lib/types/domain/row.d.ts +1 -0
- package/lib/types/domain/staging-index.d.ts +14 -0
- package/lib/types/domain/staging-index.js +41 -0
- package/lib/types/domain/state-v3.d.ts +15 -0
- package/lib/types/domain/state-v3.js +80 -0
- package/lib/types/facts.d.ts +24 -0
- package/lib/types/facts.js +68 -0
- package/lib/types/gate-filter.d.ts +31 -0
- package/lib/types/gate-filter.js +64 -0
- package/lib/types/index.d.ts +5 -2
- package/lib/types/index.js +16 -26
- package/lib/types/ladder.js +1 -1
- package/lib/types/merge-routes.d.ts +2 -2
- package/lib/types/merge-routes.js +21 -22
- package/lib/types/metrics.d.ts +16 -0
- package/lib/types/metrics.js +27 -0
- package/lib/types/new-child-authoring.d.ts +3 -1
- package/lib/types/new-child-authoring.js +14 -3
- package/lib/types/pending-actions.d.ts +2 -2
- package/lib/types/pending-actions.js +17 -17
- package/lib/types/propose.d.ts +26 -0
- package/lib/types/propose.js +69 -0
- package/lib/types/read-commands.d.ts +2 -2
- package/lib/types/read-commands.js +5 -3
- package/lib/types/resume-command.d.ts +2 -0
- package/lib/types/resume-command.js +4 -1
- package/lib/types/round2-outcome.js +17 -9
- package/lib/types/runtime.d.ts +4 -1
- package/lib/types/runtime.js +99 -29
- package/lib/types/scan-ports.d.ts +3 -1
- package/lib/types/scan-ports.js +2 -0
- package/lib/types/scan-run.js +114 -51
- package/lib/types/screening-port.d.ts +47 -0
- package/lib/types/screening-port.js +93 -0
- package/lib/types/screening.d.ts +30 -0
- package/lib/types/screening.js +75 -0
- package/lib/types/state.d.ts +14 -0
- package/lib/types/state.js +70 -1
- package/lib/types/status.d.ts +3 -0
- package/lib/types/status.js +17 -0
- package/lib/types/target-claim.d.ts +1 -0
- package/lib/types/target-claim.js +63 -18
- package/lib/types/unit-claim.d.ts +5 -0
- package/lib/types/unit-claim.js +15 -0
- package/lib/types/wire-projection.d.ts +36 -0
- package/lib/types/wire-projection.js +69 -0
- package/lib/types/wire.d.ts +254 -0
- package/lib/types/wire.js +3 -0
- package/package.json +8 -4
- package/lib/types/routes.d.ts +0 -70
- package/lib/types/routes.js +0 -78
package/lib/types/status.js
CHANGED
|
@@ -20,6 +20,23 @@ export function formatStatus(rows) {
|
|
|
20
20
|
lines.push('- ' + describe(row));
|
|
21
21
|
return fitToolText(lines.join('\n'));
|
|
22
22
|
}
|
|
23
|
+
export function formatFacts(facts) {
|
|
24
|
+
const lines = [
|
|
25
|
+
'merge: verify ' + String(facts.counts.active.verify) + ', pending ' + String(facts.counts.active.pending) + ', fuse ' + String(facts.counts.active.fuse) + ', skipped ' + String(facts.counts.active.skipped) + ', expired ' + String(facts.counts.expired),
|
|
26
|
+
];
|
|
27
|
+
if (facts.active !== null)
|
|
28
|
+
lines.push('active: ' + facts.active.id + ' [' + facts.active.state + ']');
|
|
29
|
+
if (facts.last !== null)
|
|
30
|
+
lines.push('last: ' + facts.last.id + ' [' + facts.last.state + ']');
|
|
31
|
+
if (facts.degraded)
|
|
32
|
+
lines.push('degraded: ' + facts.notes.join(','));
|
|
33
|
+
return fitToolText(lines.join('\n'));
|
|
34
|
+
}
|
|
35
|
+
export function formatRunsView(view) {
|
|
36
|
+
if (view.runs.length === 0)
|
|
37
|
+
return 'merge runs: empty';
|
|
38
|
+
return fitToolText(['merge runs: ' + String(view.runs.length)].concat(view.runs.map(run => '- ' + run.id + ' [' + run.state + ']' + (run.endedAt === undefined ? '' : ' ' + new Date(run.endedAt).toISOString()))).join('\n'));
|
|
39
|
+
}
|
|
23
40
|
export function formatQueue(rows, queue) {
|
|
24
41
|
const selected = rows.filter(row => queueOf(row.state) === queue);
|
|
25
42
|
if (selected.length === 0)
|
|
@@ -3,5 +3,6 @@ export interface TargetClaim {
|
|
|
3
3
|
readonly token: string;
|
|
4
4
|
release(): Promise<void>;
|
|
5
5
|
}
|
|
6
|
+
export declare function releaseTargetClaimQuietly(claim: TargetClaim | null | undefined): Promise<void>;
|
|
6
7
|
export declare function acquireTargetClaim(io: EvolutionIoLike, home: string, target: string, now: number): Promise<TargetClaim | null>;
|
|
7
8
|
//# sourceMappingURL=target-claim.d.ts.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** Durable target claim: one approve writer per merge target at a time. */
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { mkdir, open, readFile, unlink } from 'node:fs/promises';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { contentHash, isProcessAlive } from '@lmzhen/dsh-evolution-core';
|
|
5
6
|
import { acquireClaim, releaseClaim } from "./domain/claim.js";
|
|
6
|
-
const
|
|
7
|
+
const PROCESS_STARTED_AT = Date.now() - process.uptime() * 1000;
|
|
7
8
|
function claimPath(home, target) {
|
|
8
9
|
return join(home, 'merge', 'claims', contentHash(target) + '.json');
|
|
9
10
|
}
|
|
@@ -13,32 +14,76 @@ function parseClaim(raw) {
|
|
|
13
14
|
const parsed = JSON.parse(raw);
|
|
14
15
|
if (typeof parsed.token !== 'string' || typeof parsed.expiresAt !== 'number')
|
|
15
16
|
throw new Error('corrupt target claim');
|
|
16
|
-
return {
|
|
17
|
+
return {
|
|
18
|
+
id: 'claim', token: parsed.token, expiresAt: parsed.expiresAt,
|
|
19
|
+
...(typeof parsed.pid === 'number' ? { pid: parsed.pid } : {}),
|
|
20
|
+
...(typeof parsed.startedAt === 'number' ? { startedAt: parsed.startedAt } : {}),
|
|
21
|
+
};
|
|
17
22
|
}
|
|
18
|
-
async function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (current !== null)
|
|
23
|
+
async function withGuard(path, fn) {
|
|
24
|
+
const guard = path + '.guard';
|
|
25
|
+
await mkdir(dirname(path), { recursive: true });
|
|
26
|
+
for (let attempt = 0; attempt < 500; attempt += 1) {
|
|
27
|
+
try {
|
|
28
|
+
const handle = await open(guard, 'wx');
|
|
29
|
+
await handle.writeFile(String(process.pid));
|
|
30
|
+
await handle.close();
|
|
27
31
|
try {
|
|
28
|
-
await
|
|
32
|
+
return await fn();
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
await unlink(guard).catch(() => { });
|
|
29
36
|
}
|
|
30
|
-
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error.code !== 'EEXIST')
|
|
40
|
+
throw error;
|
|
41
|
+
const pid = Number(await readFile(guard, 'utf8').catch(() => ''));
|
|
42
|
+
if (Number.isFinite(pid) && pid > 0 && !isProcessAlive(pid))
|
|
43
|
+
await unlink(guard).catch(() => { });
|
|
44
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
45
|
+
}
|
|
31
46
|
}
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
throw new Error('target-claim-guard-timeout');
|
|
48
|
+
}
|
|
49
|
+
async function compareAndSwap(io, path, task) {
|
|
50
|
+
await withGuard(path, async () => {
|
|
51
|
+
if (io.transact !== undefined) {
|
|
52
|
+
await io.transact(path, current => task(current));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const current = (await io.exists(path)) ? await io.readText(path) : null;
|
|
56
|
+
const next = task(current);
|
|
57
|
+
if (next === null) {
|
|
58
|
+
if (current !== null)
|
|
59
|
+
try {
|
|
60
|
+
await io.remove(path);
|
|
61
|
+
}
|
|
62
|
+
catch { /* already gone */ }
|
|
63
|
+
}
|
|
64
|
+
else if (next !== current) {
|
|
65
|
+
await io.writeText(path, next);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export async function releaseTargetClaimQuietly(claim) {
|
|
70
|
+
if (claim === null || claim === undefined)
|
|
71
|
+
return;
|
|
72
|
+
try {
|
|
73
|
+
await claim.release();
|
|
34
74
|
}
|
|
75
|
+
catch { /* best effort; dead-holder reclaim or TTL handles it */ }
|
|
35
76
|
}
|
|
36
77
|
export async function acquireTargetClaim(io, home, target, now) {
|
|
37
78
|
const path = claimPath(home, target);
|
|
38
79
|
const token = randomUUID();
|
|
39
80
|
let granted;
|
|
40
81
|
await compareAndSwap(io, path, (raw) => {
|
|
41
|
-
const
|
|
82
|
+
const current = parseClaim(raw);
|
|
83
|
+
const reused = current !== undefined && current.pid === process.pid && current.startedAt !== undefined && Math.abs(current.startedAt - PROCESS_STARTED_AT) > 1_000;
|
|
84
|
+
const abandoned = reused || (current !== undefined && current.expiresAt > now
|
|
85
|
+
&& current.pid !== undefined && current.pid !== process.pid && !isProcessAlive(current.pid));
|
|
86
|
+
const claim = acquireClaim(abandoned ? undefined : current, now, Number.MAX_SAFE_INTEGER, token, process.pid, PROCESS_STARTED_AT);
|
|
42
87
|
if (claim === undefined)
|
|
43
88
|
return raw;
|
|
44
89
|
granted = claim;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
import type { UnitClaimPort } from '@lmzhen/dsh-evolution-core';
|
|
3
|
+
/** Commit-phase claim: 24h is only stale recovery; commit releases immediately, no approve TTL. */
|
|
4
|
+
export declare function targetClaimUnitPort(io: EvolutionIoLike, home: string, now: () => number): UnitClaimPort;
|
|
5
|
+
//# sourceMappingURL=unit-claim.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { acquireTargetClaim, releaseTargetClaimQuietly } from "./target-claim.js";
|
|
2
|
+
/** Commit-phase claim: 24h is only stale recovery; commit releases immediately, no approve TTL. */
|
|
3
|
+
export function targetClaimUnitPort(io, home, now) {
|
|
4
|
+
return {
|
|
5
|
+
async acquire(target) {
|
|
6
|
+
const claim = await acquireTargetClaim(io, home, target, now());
|
|
7
|
+
if (claim === null)
|
|
8
|
+
throw new Error('target-claim-busy: ' + target);
|
|
9
|
+
return {
|
|
10
|
+
async release() { await releaseTargetClaimQuietly(claim); },
|
|
11
|
+
};
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=unit-claim.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** G5-4 wire projections from merge rows to the frozen envelopes. */
|
|
2
|
+
import type { RunRecord } from '@lmzhen/dsh-evolution-core';
|
|
3
|
+
import type { MergeRow } from './contracts.ts';
|
|
4
|
+
import type { BodyView, DoctorView, HistoryView, MergeCounts, QueueId, QueuePage, RowFact, RunView, RunsView } from './wire.ts';
|
|
5
|
+
/** Host-owned row actions; client only renders/copies them. */
|
|
6
|
+
export declare function actionRefs(row: MergeRow): RowFact['actions'];
|
|
7
|
+
export declare function toRowFact(row: MergeRow, actions?: RowFact['actions']): RowFact;
|
|
8
|
+
export declare function toQueuePage(rows: readonly MergeRow[], queue: QueueId, expiredOnly: boolean, readAt: number, limit?: number): QueuePage;
|
|
9
|
+
export declare function toRunsView(runs: readonly RunRecord[], readAt: number): RunsView;
|
|
10
|
+
export interface DoctorSourceFact {
|
|
11
|
+
readonly key: string;
|
|
12
|
+
readonly status: 'ok' | 'warn' | 'fail' | 'unknown';
|
|
13
|
+
readonly count?: number;
|
|
14
|
+
readonly detail?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function toDoctorView(input: {
|
|
17
|
+
readonly readAt: number;
|
|
18
|
+
readonly counts: MergeCounts;
|
|
19
|
+
readonly latestRun?: RunView;
|
|
20
|
+
readonly approval: {
|
|
21
|
+
mergeApproval: boolean;
|
|
22
|
+
writeApproval?: boolean;
|
|
23
|
+
};
|
|
24
|
+
readonly staging: DoctorView['staging'];
|
|
25
|
+
readonly rows: readonly DoctorSourceFact[];
|
|
26
|
+
}): DoctorView;
|
|
27
|
+
export declare function toBodyView(name: string, side: 'live' | 'archive', text: string, readAt: number): BodyView;
|
|
28
|
+
export declare function toHistoryView(items: readonly {
|
|
29
|
+
name: string;
|
|
30
|
+
kind: 'merged' | 'deleted' | 'split' | 'other';
|
|
31
|
+
reason: string;
|
|
32
|
+
lastVersion: number;
|
|
33
|
+
archivedAt: string;
|
|
34
|
+
sourceRunId?: string;
|
|
35
|
+
}[], readAt: number): HistoryView;
|
|
36
|
+
//# sourceMappingURL=wire-projection.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { countsOf, toRunView } from "./facts.js";
|
|
2
|
+
const queueOf = (row) => row.state === 'approved' ? 'fuse' : row.queue;
|
|
3
|
+
const expiredOf = (row) => row.expired !== undefined;
|
|
4
|
+
/** Host-owned row actions; client only renders/copies them. */
|
|
5
|
+
export function actionRefs(row) {
|
|
6
|
+
const base = '/evolution merge ';
|
|
7
|
+
const disabled = row.expired === undefined ? undefined : 'expired';
|
|
8
|
+
const action = (id, confirm = false, enabled = true) => ({ id, command: base + id + ' ' + row.id, confirm, enabled, ...(enabled ? {} : { disabledReason: disabled ?? 'invalid' }) });
|
|
9
|
+
if (row.state === 'pending' && row.expired !== undefined)
|
|
10
|
+
return [{ id: 'discard', command: base + 'discard ' + row.id, confirm: true, enabled: true }, { id: 'pair', command: base + 'pair ' + row.a + ' ' + row.b, confirm: true, enabled: true }];
|
|
11
|
+
if (row.state === 'pending')
|
|
12
|
+
return [action('approve'), action('reject', true), action('discard', true)];
|
|
13
|
+
if (row.state === 'approved' || row.state === 'fuse')
|
|
14
|
+
return [action('discard', true), action('status')];
|
|
15
|
+
if (row.state === 'skipped')
|
|
16
|
+
return [action('restore', true)];
|
|
17
|
+
return [action('pair')];
|
|
18
|
+
}
|
|
19
|
+
export function toRowFact(row, actions = actionRefs(row)) {
|
|
20
|
+
return {
|
|
21
|
+
id: row.id,
|
|
22
|
+
state: row.state,
|
|
23
|
+
queue: queueOf(row),
|
|
24
|
+
...(row.state === 'verify' ? { verifySubState: 'queued' } : {}),
|
|
25
|
+
a: row.a,
|
|
26
|
+
b: row.b,
|
|
27
|
+
...(row.target === undefined ? {} : { target: { requested: row.target.requested, ...(row.target.actual === undefined ? {} : { actual: row.target.actual }), ...(row.target.created === undefined ? {} : { created: row.target.created }) } }),
|
|
28
|
+
...(row.state === 'pending' ? { summary: { a: null, b: null, rationale: row.decision?.rationale ?? null } } : {}),
|
|
29
|
+
...(row.reason === undefined ? {} : { reason: row.reason }),
|
|
30
|
+
...(row.state === 'skipped' ? { origin: row.origin ?? 'system' } : {}),
|
|
31
|
+
...(row.failure === undefined ? {} : { failure: { stage: row.failure.stage === 'screening' ? 'screening' : row.failure.stage === 'decide' ? 'decide' : row.failure.stage === 'author' ? 'author' : row.failure.stage === 'validate' ? 'validate' : 'commit', code: row.failure.code, retryable: row.failure.retryable, retryFrom: row.failure.retryFrom } }),
|
|
32
|
+
...(row.stagingRef === undefined ? {} : { staging: { state: 'kept', ...(row.stagingExpiresAt === undefined ? {} : { expiresAt: row.stagingExpiresAt }) } }),
|
|
33
|
+
...(row.expired === undefined ? {} : { expired: { kind: row.expired, at: Date.parse(row.updatedAt) } }),
|
|
34
|
+
...(row.attempts === undefined ? {} : { attempts: row.attempts }),
|
|
35
|
+
...(row.decision?.confidence === undefined ? {} : { confidence: row.decision.confidence }),
|
|
36
|
+
...(row.state === 'skipped' && row.stagingExpiresAt !== undefined ? { expiresAt: row.stagingExpiresAt } : {}),
|
|
37
|
+
createdAt: Date.parse(row.createdAt),
|
|
38
|
+
updatedAt: Date.parse(row.updatedAt),
|
|
39
|
+
actions,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function toQueuePage(rows, queue, expiredOnly, readAt, limit = 50) {
|
|
43
|
+
const filtered = rows.filter(row => queueOf(row) === queue && (expiredOnly ? expiredOf(row) : !expiredOf(row)));
|
|
44
|
+
const page = filtered.slice(0, Math.max(0, Math.min(200, limit)));
|
|
45
|
+
const counts = countsOf(rows);
|
|
46
|
+
return { wireVersion: 1, readAt, queue, expiredOnly, counts, rows: page.map(row => toRowFact(row)), ...(filtered.length > page.length ? { nextCursor: String(page.length) } : {}), notes: [] };
|
|
47
|
+
}
|
|
48
|
+
export function toRunsView(runs, readAt) {
|
|
49
|
+
const ordered = [...runs].sort((a, b) => b.startedAt - a.startedAt).slice(0, 50);
|
|
50
|
+
return { wireVersion: 1, readAt, runs: ordered.map(run => toRunView(run, readAt)) };
|
|
51
|
+
}
|
|
52
|
+
export function toDoctorView(input) {
|
|
53
|
+
return {
|
|
54
|
+
wireVersion: 1,
|
|
55
|
+
readAt: input.readAt,
|
|
56
|
+
counts: input.counts,
|
|
57
|
+
...(input.latestRun === undefined ? {} : { latestRun: input.latestRun }),
|
|
58
|
+
staging: input.staging,
|
|
59
|
+
approval: input.approval,
|
|
60
|
+
rows: input.rows.map(row => ({ key: row.key, status: row.status, ...(row.count === undefined ? {} : { count: row.count }), ...(row.detail === undefined ? {} : { detail: row.detail }) })),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function toBodyView(name, side, text, readAt) {
|
|
64
|
+
return { wireVersion: 1, readAt, name, side, text, markdown: true, truncated: false, totalBytes: Buffer.byteLength(text, 'utf8') };
|
|
65
|
+
}
|
|
66
|
+
export function toHistoryView(items, readAt) {
|
|
67
|
+
return { wireVersion: 1, readAt, items: items.map(item => ({ name: item.name, kind: item.kind, reason: item.reason, lastVersion: item.lastVersion, archivedAt: Date.parse(item.archivedAt), ...(item.sourceRunId === undefined ? {} : { sourceRunId: item.sourceRunId }) })) };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=wire-projection.js.map
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/** Merge wire contract v1. Read-only envelopes shared with the client. */
|
|
2
|
+
export declare const WIRE_VERSION: 1;
|
|
3
|
+
export type QueueId = 'verify' | 'pending' | 'fuse' | 'skipped';
|
|
4
|
+
export type WireRowState = 'verify' | 'pending' | 'approved' | 'fuse' | 'skipped';
|
|
5
|
+
export type RowStage = 'screening' | 'decide' | 'author' | 'validate' | 'commit';
|
|
6
|
+
export type RunStage = RowStage | 'settle';
|
|
7
|
+
export type VerifySubState = 'queued' | 'running' | 'failed' | 'cancelled';
|
|
8
|
+
export type StagingState = 'none' | 'kept' | 'cleared-source-ok' | 'cleared-source-gone';
|
|
9
|
+
export type SkipOrigin = 'agent' | 'human' | 'system';
|
|
10
|
+
export type SkipKind = 'agent-keep' | 'human-reject' | 'over-limit' | 'forced';
|
|
11
|
+
export type ExpiredKind = 'target-taken' | 'source-changed' | 'skipped-ttl';
|
|
12
|
+
export type WireRunState = 'running' | 'succeeded' | 'failed' | 'cancelled' | 'awaiting-approval' | 'undone' | 'superseded' | 'interrupted' | 'orphan';
|
|
13
|
+
export type ActionId = 'approve' | 'reject' | 'retry-candidates' | 'retry-resume' | 'discard' | 'restore' | 'pair' | 'status' | 'report' | 'cancel';
|
|
14
|
+
export interface MergeCounts {
|
|
15
|
+
readonly active: {
|
|
16
|
+
readonly verify: number;
|
|
17
|
+
readonly pending: number;
|
|
18
|
+
readonly fuse: number;
|
|
19
|
+
readonly skipped: number;
|
|
20
|
+
};
|
|
21
|
+
readonly expired: number;
|
|
22
|
+
}
|
|
23
|
+
export interface FailureFact {
|
|
24
|
+
readonly stage: RowStage | 'settle';
|
|
25
|
+
readonly code: string;
|
|
26
|
+
readonly retryable: boolean;
|
|
27
|
+
readonly retryFrom: 'agent1' | 'agent2' | 'none';
|
|
28
|
+
readonly detail?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface SummarySlots {
|
|
31
|
+
readonly a: string | null;
|
|
32
|
+
readonly b: string | null;
|
|
33
|
+
readonly rationale: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface CommandRef {
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly command: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ActionRef {
|
|
40
|
+
readonly id: ActionId;
|
|
41
|
+
readonly command: string;
|
|
42
|
+
readonly confirm: boolean;
|
|
43
|
+
readonly enabled: boolean;
|
|
44
|
+
readonly disabledReason?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface RowFact {
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly state: WireRowState;
|
|
49
|
+
readonly queue: QueueId;
|
|
50
|
+
readonly verifySubState?: VerifySubState;
|
|
51
|
+
readonly a: string;
|
|
52
|
+
readonly b: string;
|
|
53
|
+
readonly target?: {
|
|
54
|
+
readonly requested: string;
|
|
55
|
+
readonly actual?: string;
|
|
56
|
+
readonly created?: boolean;
|
|
57
|
+
};
|
|
58
|
+
readonly summary?: SummarySlots;
|
|
59
|
+
readonly reason?: string;
|
|
60
|
+
readonly origin?: SkipOrigin;
|
|
61
|
+
readonly skipKind?: SkipKind;
|
|
62
|
+
readonly confidence?: number;
|
|
63
|
+
readonly failure?: FailureFact;
|
|
64
|
+
readonly staging?: {
|
|
65
|
+
readonly state: StagingState;
|
|
66
|
+
readonly expiresAt?: number;
|
|
67
|
+
};
|
|
68
|
+
readonly expired?: {
|
|
69
|
+
readonly kind: ExpiredKind;
|
|
70
|
+
readonly at: number;
|
|
71
|
+
};
|
|
72
|
+
readonly bodies?: readonly {
|
|
73
|
+
readonly name: string;
|
|
74
|
+
readonly side: 'live' | 'archive';
|
|
75
|
+
}[];
|
|
76
|
+
readonly attempts?: number;
|
|
77
|
+
readonly createdAt: number;
|
|
78
|
+
readonly updatedAt: number;
|
|
79
|
+
readonly expiresAt?: number;
|
|
80
|
+
readonly actions: readonly ActionRef[];
|
|
81
|
+
}
|
|
82
|
+
export interface Facts {
|
|
83
|
+
readonly wireVersion: 1;
|
|
84
|
+
readonly readAt: number;
|
|
85
|
+
readonly approval: {
|
|
86
|
+
readonly mergeApproval: boolean;
|
|
87
|
+
};
|
|
88
|
+
readonly counts: MergeCounts;
|
|
89
|
+
readonly active: RunView | null;
|
|
90
|
+
readonly last: RunView | null;
|
|
91
|
+
readonly startCommands: {
|
|
92
|
+
readonly scan: string;
|
|
93
|
+
readonly pair: string;
|
|
94
|
+
};
|
|
95
|
+
readonly fallback: readonly CommandRef[];
|
|
96
|
+
readonly notes: readonly string[];
|
|
97
|
+
readonly degraded: boolean;
|
|
98
|
+
}
|
|
99
|
+
export interface QueuePage {
|
|
100
|
+
readonly wireVersion: 1;
|
|
101
|
+
readonly readAt: number;
|
|
102
|
+
readonly queue: QueueId;
|
|
103
|
+
readonly expiredOnly: boolean;
|
|
104
|
+
readonly counts: MergeCounts;
|
|
105
|
+
readonly rows: readonly RowFact[];
|
|
106
|
+
readonly nextCursor?: string;
|
|
107
|
+
readonly notes: readonly string[];
|
|
108
|
+
}
|
|
109
|
+
export interface RunView {
|
|
110
|
+
readonly wireVersion: 1;
|
|
111
|
+
readonly readAt: number;
|
|
112
|
+
readonly id: string;
|
|
113
|
+
readonly state: WireRunState;
|
|
114
|
+
readonly command: 'scan' | 'pair' | 'approve' | 'resume';
|
|
115
|
+
readonly stage?: RunStage;
|
|
116
|
+
readonly progress?: {
|
|
117
|
+
readonly done: number;
|
|
118
|
+
readonly total: number;
|
|
119
|
+
} | null;
|
|
120
|
+
readonly startedAt: number;
|
|
121
|
+
readonly endedAt?: number;
|
|
122
|
+
readonly stopReason?: 'timeout' | 'cancelled' | 'error' | 'orphan';
|
|
123
|
+
readonly failure?: FailureFact;
|
|
124
|
+
readonly parentRunId?: string;
|
|
125
|
+
readonly currentPairKey?: string;
|
|
126
|
+
readonly summary?: {
|
|
127
|
+
readonly merged: number;
|
|
128
|
+
readonly kept: number;
|
|
129
|
+
readonly failed: number;
|
|
130
|
+
readonly pending: number;
|
|
131
|
+
readonly cancelled: number;
|
|
132
|
+
};
|
|
133
|
+
readonly actions: readonly ActionRef[];
|
|
134
|
+
}
|
|
135
|
+
export interface RunPair {
|
|
136
|
+
readonly pairKey: string;
|
|
137
|
+
readonly a: string;
|
|
138
|
+
readonly b: string;
|
|
139
|
+
readonly outcome: 'merged' | 'kept' | 'pending' | 'failed' | 'rejected' | 'cancelled' | 'undone';
|
|
140
|
+
readonly stage?: RowStage;
|
|
141
|
+
readonly code?: string;
|
|
142
|
+
readonly detail?: string;
|
|
143
|
+
readonly target?: {
|
|
144
|
+
readonly requested: string;
|
|
145
|
+
readonly actual?: string;
|
|
146
|
+
readonly created?: boolean;
|
|
147
|
+
};
|
|
148
|
+
readonly actions?: readonly ActionRef[];
|
|
149
|
+
}
|
|
150
|
+
export interface RunDetail extends RunView {
|
|
151
|
+
readonly pairs: readonly RunPair[];
|
|
152
|
+
readonly pairsTruncated: boolean;
|
|
153
|
+
}
|
|
154
|
+
export interface RunsView {
|
|
155
|
+
readonly wireVersion: 1;
|
|
156
|
+
readonly readAt: number;
|
|
157
|
+
readonly runs: readonly RunView[];
|
|
158
|
+
}
|
|
159
|
+
export interface BodyView {
|
|
160
|
+
readonly wireVersion: 1;
|
|
161
|
+
readonly readAt: number;
|
|
162
|
+
readonly name: string;
|
|
163
|
+
readonly side: 'live' | 'archive';
|
|
164
|
+
readonly text: string;
|
|
165
|
+
readonly markdown: boolean;
|
|
166
|
+
readonly truncated: boolean;
|
|
167
|
+
readonly totalBytes: number;
|
|
168
|
+
}
|
|
169
|
+
export interface HistoryItem {
|
|
170
|
+
readonly name: string;
|
|
171
|
+
readonly kind: 'merged' | 'deleted' | 'split' | 'other';
|
|
172
|
+
readonly reason: string;
|
|
173
|
+
readonly lastVersion: number;
|
|
174
|
+
readonly archivedAt: number;
|
|
175
|
+
readonly sourceRunId?: string;
|
|
176
|
+
}
|
|
177
|
+
export interface HistoryView {
|
|
178
|
+
readonly wireVersion: 1;
|
|
179
|
+
readonly readAt: number;
|
|
180
|
+
readonly items: readonly HistoryItem[];
|
|
181
|
+
}
|
|
182
|
+
export interface DoctorFact {
|
|
183
|
+
readonly key: string;
|
|
184
|
+
readonly status: 'ok' | 'warn' | 'fail' | 'unknown';
|
|
185
|
+
readonly code?: string;
|
|
186
|
+
readonly count?: number;
|
|
187
|
+
readonly detail?: string;
|
|
188
|
+
}
|
|
189
|
+
export interface DoctorView {
|
|
190
|
+
readonly wireVersion: 1;
|
|
191
|
+
readonly readAt: number;
|
|
192
|
+
readonly counts: MergeCounts;
|
|
193
|
+
readonly latestRun?: RunView;
|
|
194
|
+
readonly staging: {
|
|
195
|
+
readonly state: 'ok' | 'absent' | 'unreadable';
|
|
196
|
+
readonly dirs: number;
|
|
197
|
+
readonly oldestAgeMs?: number;
|
|
198
|
+
readonly detail?: string;
|
|
199
|
+
};
|
|
200
|
+
readonly approval: {
|
|
201
|
+
readonly mergeApproval: boolean;
|
|
202
|
+
readonly writeApproval?: boolean;
|
|
203
|
+
};
|
|
204
|
+
readonly hostOnly?: {
|
|
205
|
+
readonly code: 'E-323';
|
|
206
|
+
readonly detail?: string;
|
|
207
|
+
};
|
|
208
|
+
readonly rows: readonly DoctorFact[];
|
|
209
|
+
}
|
|
210
|
+
export interface WireFailure {
|
|
211
|
+
readonly wireVersion: 1;
|
|
212
|
+
readonly readAt?: number;
|
|
213
|
+
readonly code: string;
|
|
214
|
+
readonly message?: string;
|
|
215
|
+
}
|
|
216
|
+
export interface CommandFlagSpec {
|
|
217
|
+
readonly name: string;
|
|
218
|
+
readonly kind: 'boolean' | 'value';
|
|
219
|
+
readonly valueName?: string;
|
|
220
|
+
readonly required?: boolean;
|
|
221
|
+
readonly repeatable?: boolean;
|
|
222
|
+
readonly values?: readonly string[];
|
|
223
|
+
}
|
|
224
|
+
export interface CommandPositionalSpec {
|
|
225
|
+
readonly name: string;
|
|
226
|
+
readonly kind: 'skill' | 'target' | 'runId' | 'id' | 'json' | 'text';
|
|
227
|
+
readonly required: boolean;
|
|
228
|
+
readonly variadic?: boolean;
|
|
229
|
+
}
|
|
230
|
+
export interface CommandActionRule {
|
|
231
|
+
readonly id: ActionId;
|
|
232
|
+
readonly command: string;
|
|
233
|
+
readonly confirm?: boolean;
|
|
234
|
+
readonly enabledWhen?: string;
|
|
235
|
+
}
|
|
236
|
+
export interface CommandSpec {
|
|
237
|
+
readonly id: string;
|
|
238
|
+
readonly words: readonly string[];
|
|
239
|
+
readonly flags: readonly CommandFlagSpec[];
|
|
240
|
+
readonly positional: readonly CommandPositionalSpec[];
|
|
241
|
+
readonly summary: string;
|
|
242
|
+
readonly readOnly: boolean;
|
|
243
|
+
readonly needsRun: boolean;
|
|
244
|
+
readonly defaults: Readonly<Record<string, string>>;
|
|
245
|
+
readonly rowActions: readonly CommandActionRule[];
|
|
246
|
+
readonly fallback: string;
|
|
247
|
+
}
|
|
248
|
+
export interface CommandSurface {
|
|
249
|
+
readonly wireVersion: 1;
|
|
250
|
+
readonly prefix: '/evolution merge';
|
|
251
|
+
readonly commands: readonly CommandSpec[];
|
|
252
|
+
readonly fallback: readonly CommandRef[];
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=wire.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-merge",
|
|
3
3
|
"description": "Skill auto-merge domain: screening, two-round authoring, queues and commit orchestration (community build)",
|
|
4
|
-
"version": "0.30.
|
|
4
|
+
"version": "0.30.18",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -22,13 +22,17 @@
|
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"lib/**/*.js",
|
|
25
|
-
"lib/types/**/*.d.ts"
|
|
25
|
+
"lib/types/**/*.d.ts",
|
|
26
|
+
"data/**/*"
|
|
26
27
|
],
|
|
27
28
|
"license": "MIT",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"verify:publish-manifest": "node scripts/verify-publish-manifest.mjs"
|
|
31
|
+
},
|
|
28
32
|
"dependencies": {
|
|
29
33
|
"@deepseek-ai/schemastery": "^3.18.5",
|
|
30
|
-
"@lmzhen/dsh-evolution-core": "^0.30.
|
|
31
|
-
"@lmzhen/dsh-evolution-merge-engine": "
|
|
34
|
+
"@lmzhen/dsh-evolution-core": "^0.30.18",
|
|
35
|
+
"@lmzhen/dsh-evolution-merge-engine": "0.30.18"
|
|
32
36
|
},
|
|
33
37
|
"peerDependencies": {
|
|
34
38
|
"@deepseek-ai/cordis": "^4.0.5"
|
package/lib/types/routes.d.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read-only view models for the four merge routes. Pure functions; the host loads rows and
|
|
3
|
-
* runs, the client renders these shapes. No io, no write paths.
|
|
4
|
-
*/
|
|
5
|
-
import type { RunRecord } from '@lmzhen/dsh-evolution-core';
|
|
6
|
-
import type { MergeQueue, MergeRow } from './contracts.ts';
|
|
7
|
-
import { type MergeCounts } from './status.ts';
|
|
8
|
-
export interface MergeRowView {
|
|
9
|
-
readonly id: string;
|
|
10
|
-
readonly pairKey: string;
|
|
11
|
-
readonly a: string;
|
|
12
|
-
readonly b: string;
|
|
13
|
-
readonly state: MergeRow['state'];
|
|
14
|
-
readonly queue: MergeQueue;
|
|
15
|
-
readonly stage: string | null;
|
|
16
|
-
readonly label: string;
|
|
17
|
-
readonly updatedAt: string;
|
|
18
|
-
readonly createdAt: string;
|
|
19
|
-
readonly target?: string;
|
|
20
|
-
readonly confidence?: number;
|
|
21
|
-
readonly aSummary?: string;
|
|
22
|
-
readonly bSummary?: string;
|
|
23
|
-
readonly rationale?: string;
|
|
24
|
-
readonly failure?: {
|
|
25
|
-
readonly stage: string;
|
|
26
|
-
readonly code: string;
|
|
27
|
-
readonly message: string;
|
|
28
|
-
readonly retryable: boolean;
|
|
29
|
-
};
|
|
30
|
-
readonly attempts?: number;
|
|
31
|
-
readonly origin?: 'agent' | 'human' | 'system';
|
|
32
|
-
readonly stagingExpiresAt?: number;
|
|
33
|
-
readonly expired?: 'target-taken' | 'source-changed';
|
|
34
|
-
readonly reason?: string;
|
|
35
|
-
readonly summary?: readonly string[];
|
|
36
|
-
readonly actions: readonly string[];
|
|
37
|
-
}
|
|
38
|
-
export declare function toRowView(row: MergeRow): MergeRowView;
|
|
39
|
-
export interface MergeView {
|
|
40
|
-
readonly schemaVersion: 2;
|
|
41
|
-
readonly counts: MergeCounts;
|
|
42
|
-
readonly tabs: readonly {
|
|
43
|
-
readonly queue: MergeQueue;
|
|
44
|
-
readonly count: number;
|
|
45
|
-
}[];
|
|
46
|
-
readonly commands: readonly string[];
|
|
47
|
-
readonly latestRun?: MergeRunFact;
|
|
48
|
-
readonly approval?: boolean;
|
|
49
|
-
}
|
|
50
|
-
export declare function buildMergeView(rows: readonly MergeRow[], commands: readonly string[], latestRun?: MergeRunFact, approval?: boolean): MergeView;
|
|
51
|
-
export interface QueuePage {
|
|
52
|
-
readonly queue: MergeQueue;
|
|
53
|
-
readonly items: readonly MergeRowView[];
|
|
54
|
-
readonly nextCursor?: string;
|
|
55
|
-
}
|
|
56
|
-
export declare function pageQueue(rows: readonly MergeRow[], queue: MergeQueue, cursor?: number, limit?: number): QueuePage;
|
|
57
|
-
export interface MergeRunFact {
|
|
58
|
-
readonly id: string;
|
|
59
|
-
readonly state: RunRecord['state'];
|
|
60
|
-
readonly command?: RunRecord['command'];
|
|
61
|
-
readonly stage?: RunRecord['stage'];
|
|
62
|
-
readonly progress?: RunRecord['progress'];
|
|
63
|
-
readonly startedAt: number;
|
|
64
|
-
readonly endedAt?: number;
|
|
65
|
-
readonly resultRef?: string;
|
|
66
|
-
readonly failure?: string;
|
|
67
|
-
readonly supersededAt?: number;
|
|
68
|
-
}
|
|
69
|
-
export declare function toRunFact(run: RunRecord): MergeRunFact;
|
|
70
|
-
//# sourceMappingURL=routes.d.ts.map
|