@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.
- package/README.md +25 -7
- package/data/minhash_params.json +1063 -0
- package/lib/index.js +7308 -1800
- package/lib/types/adapters/channel.d.ts +1 -0
- package/lib/types/adapters/channel.js +15 -1
- package/lib/types/adapters/engine-screen.d.ts +1 -1
- package/lib/types/adapters/engine-screen.js +5 -2
- package/lib/types/adapters/library.js +4 -1
- package/lib/types/adapters/model-runner.d.ts +0 -5
- package/lib/types/adapters/model-runner.js +8 -2
- package/lib/types/adapters/model.d.ts +31 -0
- package/lib/types/adapters/model.js +47 -0
- package/lib/types/adapters/select.js +12 -8
- package/lib/types/approve-command.d.ts +14 -1
- package/lib/types/approve-command.js +72 -44
- package/lib/types/authoring-flow.d.ts +19 -1
- package/lib/types/authoring-flow.js +49 -15
- package/lib/types/channel-port.d.ts +3 -1
- package/lib/types/channel-port.js +8 -3
- package/lib/types/command-defs.d.ts +5 -0
- package/lib/types/command-defs.js +33 -0
- package/lib/types/command-surface.d.ts +5 -0
- package/lib/types/command-surface.js +11 -0
- package/lib/types/commands.d.ts +3 -4
- package/lib/types/commands.js +33 -72
- package/lib/types/commit-assembly.d.ts +16 -1
- package/lib/types/commit-assembly.js +72 -11
- package/lib/types/commit-execute.d.ts +14 -2
- package/lib/types/commit-execute.js +76 -14
- package/lib/types/commit-plan.d.ts +7 -1
- package/lib/types/commit-plan.js +4 -0
- package/lib/types/commit-receipt.d.ts +3 -0
- package/lib/types/commit-recovery.d.ts +7 -2
- package/lib/types/commit-recovery.js +73 -96
- package/lib/types/contracts.d.ts +44 -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 +18 -1
- package/lib/types/doctor.js +164 -10
- package/lib/types/domain/authoring-manifest.d.ts +26 -0
- package/lib/types/domain/authoring-manifest.js +43 -0
- 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/engine-criteria.d.ts +10 -0
- package/lib/types/domain/engine-criteria.js +11 -0
- package/lib/types/domain/engine-input.d.ts +10 -0
- package/lib/types/domain/engine-input.js +26 -0
- package/lib/types/domain/errors.d.ts +15 -1
- package/lib/types/domain/errors.js +23 -4
- package/lib/types/domain/evidence-hash.d.ts +3 -0
- package/lib/types/domain/evidence-hash.js +14 -0
- package/lib/types/domain/failure-route.d.ts +14 -0
- package/lib/types/domain/failure-route.js +12 -0
- package/lib/types/domain/ledger.js +10 -1
- package/lib/types/domain/live-facts.d.ts +31 -0
- package/lib/types/domain/live-facts.js +54 -0
- package/lib/types/domain/merge-limits.d.ts +18 -0
- package/lib/types/domain/merge-limits.js +22 -0
- package/lib/types/domain/merge-report.d.ts +41 -0
- package/lib/types/domain/merge-report.js +2 -0
- package/lib/types/domain/merge-snapshot.d.ts +99 -0
- package/lib/types/domain/merge-snapshot.js +82 -0
- package/lib/types/domain/migrate-v3.d.ts +20 -0
- package/lib/types/domain/migrate-v3.js +68 -0
- package/lib/types/domain/pair-ledger.d.ts +34 -0
- package/lib/types/domain/pair-ledger.js +96 -0
- package/lib/types/domain/pre-1b-backup.d.ts +43 -0
- package/lib/types/domain/pre-1b-backup.js +140 -0
- package/lib/types/domain/provider-key.d.ts +2 -0
- package/lib/types/domain/provider-key.js +11 -0
- package/lib/types/domain/read-probe.d.ts +5 -0
- package/lib/types/domain/read-probe.js +11 -0
- package/lib/types/domain/reconcile.d.ts +47 -0
- package/lib/types/domain/reconcile.js +178 -0
- package/lib/types/domain/resolve-pair.d.ts +2 -9
- package/lib/types/domain/resolve-pair.js +2 -15
- 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-v4.d.ts +148 -0
- package/lib/types/domain/row-v4.js +159 -0
- package/lib/types/domain/row.d.ts +1 -0
- package/lib/types/domain/run-coordinator.d.ts +51 -0
- package/lib/types/domain/run-coordinator.js +45 -0
- package/lib/types/domain/schema4.d.ts +53 -0
- package/lib/types/domain/schema4.js +129 -0
- package/lib/types/domain/selection-policy.d.ts +14 -0
- package/lib/types/domain/selection-policy.js +6 -0
- package/lib/types/domain/skipped-expiry.d.ts +5 -0
- package/lib/types/domain/skipped-expiry.js +16 -0
- package/lib/types/domain/stage-aggregate.d.ts +96 -0
- package/lib/types/domain/stage-aggregate.js +293 -0
- package/lib/types/domain/staging-index.d.ts +15 -0
- package/lib/types/domain/staging-index.js +41 -0
- package/lib/types/domain/state-bootstrap.d.ts +43 -0
- package/lib/types/domain/state-bootstrap.js +267 -0
- package/lib/types/domain/state-v3.d.ts +4 -0
- package/lib/types/domain/state-v3.js +4 -0
- package/lib/types/domain/undo-journal.d.ts +19 -0
- package/lib/types/domain/undo-journal.js +20 -0
- package/lib/types/domain/undo-receipt.d.ts +18 -0
- package/lib/types/domain/undo-receipt.js +22 -0
- package/lib/types/domain/validation-plan.d.ts +65 -0
- package/lib/types/domain/validation-plan.js +49 -0
- package/lib/types/facts.d.ts +26 -0
- package/lib/types/facts.js +65 -0
- package/lib/types/gate-filter.d.ts +31 -0
- package/lib/types/gate-filter.js +64 -0
- package/lib/types/index.d.ts +7 -2
- package/lib/types/index.js +185 -82
- package/lib/types/ladder.js +1 -1
- package/lib/types/merge-command.d.ts +3 -1
- package/lib/types/merge-command.js +10 -6
- package/lib/types/merge-routes.d.ts +12 -7
- package/lib/types/merge-routes.js +87 -23
- package/lib/types/merge-tool.d.ts +1 -1
- package/lib/types/merge-tool.js +2 -2
- package/lib/types/metrics.d.ts +16 -0
- package/lib/types/metrics.js +27 -0
- package/lib/types/new-child-authoring.d.ts +5 -1
- package/lib/types/new-child-authoring.js +71 -13
- package/lib/types/params.d.ts +2 -4
- package/lib/types/params.js +13 -0
- package/lib/types/pending-actions.d.ts +2 -2
- package/lib/types/pending-actions.js +17 -17
- package/lib/types/prompts.d.ts +2 -4
- package/lib/types/prompts.js +21 -8
- package/lib/types/propose.d.ts +26 -0
- package/lib/types/propose.js +69 -0
- package/lib/types/read-commands.d.ts +4 -4
- package/lib/types/read-commands.js +19 -26
- package/lib/types/recovery-coordinator.d.ts +34 -0
- package/lib/types/recovery-coordinator.js +84 -0
- package/lib/types/recovery-projection.d.ts +31 -0
- package/lib/types/recovery-projection.js +96 -0
- package/lib/types/resume-command.d.ts +7 -1
- package/lib/types/resume-command.js +50 -14
- package/lib/types/revision.d.ts +17 -0
- package/lib/types/revision.js +39 -3
- package/lib/types/round2-outcome.d.ts +2 -1
- package/lib/types/round2-outcome.js +68 -12
- package/lib/types/run-shell.d.ts +16 -2
- package/lib/types/run-shell.js +49 -5
- package/lib/types/runtime.d.ts +4 -6
- package/lib/types/runtime.js +341 -86
- package/lib/types/scan-command.d.ts +1 -0
- package/lib/types/scan-command.js +2 -0
- package/lib/types/scan-ports.d.ts +21 -1
- package/lib/types/scan-ports.js +51 -23
- package/lib/types/scan-run.d.ts +17 -0
- package/lib/types/scan-run.js +221 -63
- 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/stage-tool-registry.d.ts +7 -0
- package/lib/types/stage-tool-registry.js +12 -1
- package/lib/types/state.d.ts +15 -1
- package/lib/types/state.js +39 -17
- package/lib/types/status.d.ts +8 -0
- package/lib/types/status.js +64 -0
- package/lib/types/surface/body.d.ts +4 -0
- package/lib/types/surface/body.js +16 -0
- package/lib/types/surface/merge-view.d.ts +14 -0
- package/lib/types/surface/merge-view.js +126 -0
- package/lib/types/target-claim.d.ts +1 -0
- package/lib/types/target-claim.js +63 -18
- package/lib/types/undo-operation.d.ts +24 -0
- package/lib/types/undo-operation.js +135 -0
- 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 +273 -0
- package/lib/types/wire.js +3 -0
- package/package.json +10 -4
- package/lib/types/routes.d.ts +0 -70
- package/lib/types/routes.js +0 -78
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { readTextOrNull } from "./read-probe.js";
|
|
2
|
+
/** G3 Batch -1 state bootstrap: one readiness gate, backup/journal migration. */
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { chmod } from 'node:fs/promises';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { transactIo } from '@lmzhen/dsh-evolution-core';
|
|
7
|
+
import { migrateV2ToV3 } from "./migrate-v3.js";
|
|
8
|
+
import { isV3Row } from "./row-v3.js";
|
|
9
|
+
import { stagingIndexPath, writeStagingIndex } from "./staging-index.js";
|
|
10
|
+
export function mergeStatePath(home) { return join(home, 'merge', 'merge-state.json'); }
|
|
11
|
+
export function v2BackupPath(home) { return join(home, 'merge', 'merge-state.v2.bak.json'); }
|
|
12
|
+
export function migrationJournalPath(home) { return join(home, 'merge', 'merge-state.migration.json'); }
|
|
13
|
+
export function migrationReportPath(home) { return join(home, 'merge', 'merge-state.migration.report.json'); }
|
|
14
|
+
export function quarantinePath(home) { return join(home, 'merge', 'merge-state.quarantine.json'); }
|
|
15
|
+
export class StateBlockedError extends Error {
|
|
16
|
+
readiness;
|
|
17
|
+
constructor(readiness) {
|
|
18
|
+
super(readiness.ok ? 'merge state is ready' : readiness.code + ': ' + readiness.message);
|
|
19
|
+
this.name = 'StateBlockedError';
|
|
20
|
+
this.readiness = readiness;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const hash = (text) => createHash('sha256').update(text).digest('hex');
|
|
24
|
+
const json = (value) => JSON.stringify(value, null, 2) + '\n';
|
|
25
|
+
const now = () => new Date().toISOString();
|
|
26
|
+
function blocked(code, message) { return { ok: false, code, message }; }
|
|
27
|
+
function parseBody(raw) {
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
return parsed !== null && typeof parsed === 'object' ? parsed : null;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function readMarker(body) {
|
|
37
|
+
const value = body.migration;
|
|
38
|
+
if (value === undefined)
|
|
39
|
+
return null;
|
|
40
|
+
if (value === null || typeof value !== 'object')
|
|
41
|
+
return null;
|
|
42
|
+
const marker = value;
|
|
43
|
+
if (marker.migratedFromV2 !== true || typeof marker.sourceSha256 !== 'string' || typeof marker.targetSha256 !== 'string' || !Array.isArray(marker.staging) || typeof marker.at !== 'string')
|
|
44
|
+
return null;
|
|
45
|
+
return marker;
|
|
46
|
+
}
|
|
47
|
+
function rowsSha(rows) {
|
|
48
|
+
return hash(json({ schemaVersion: 3, rows }));
|
|
49
|
+
}
|
|
50
|
+
async function missingIndex(io, home, staging) {
|
|
51
|
+
for (const entry of staging)
|
|
52
|
+
if (!(await io.exists(stagingIndexPath(home, entry.rowId))))
|
|
53
|
+
return entry.rowId;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
function v3State(body) {
|
|
57
|
+
const rows = body.rows;
|
|
58
|
+
if (!Array.isArray(rows) || !rows.every(isV3Row))
|
|
59
|
+
return null;
|
|
60
|
+
return { schemaVersion: 3, rows: rows };
|
|
61
|
+
}
|
|
62
|
+
async function readJournal(io, home) {
|
|
63
|
+
const raw = await readTextOrNull(io, migrationJournalPath(home));
|
|
64
|
+
if (raw === null)
|
|
65
|
+
return null;
|
|
66
|
+
try {
|
|
67
|
+
const value = JSON.parse(raw);
|
|
68
|
+
if (value.version !== 1 || typeof value.sourceSha256 !== 'string' || typeof value.targetSha256 !== 'string' || !Array.isArray(value.staging))
|
|
69
|
+
return null;
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function readReport(io, home) {
|
|
77
|
+
const raw = await readTextOrNull(io, migrationReportPath(home));
|
|
78
|
+
if (raw === null)
|
|
79
|
+
return null;
|
|
80
|
+
try {
|
|
81
|
+
const value = JSON.parse(raw);
|
|
82
|
+
return value.version === 1 ? value : null;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function writeReport(io, home, report) {
|
|
89
|
+
await transactIo(io, migrationReportPath(home), () => json(report));
|
|
90
|
+
}
|
|
91
|
+
async function completeJournal(io, home, journal, current) {
|
|
92
|
+
if (hash(current) !== journal.targetSha256)
|
|
93
|
+
return blocked('merge-state-needs-repair', 'migration journal does not match the v3 state');
|
|
94
|
+
for (const entry of journal.staging)
|
|
95
|
+
await writeStagingIndex(io, home, entry);
|
|
96
|
+
const report = { version: 1, ready: true, migrated: false, sourceSha256: journal.sourceSha256, targetSha256: journal.targetSha256, rows: journal.rows, quarantined: journal.quarantine.length, at: now(), steps: ['journal:index', 'journal:ready'] };
|
|
97
|
+
await writeReport(io, home, report);
|
|
98
|
+
await io.remove(migrationJournalPath(home)).catch(() => { });
|
|
99
|
+
const body = parseBody(current);
|
|
100
|
+
const state = body === null ? null : v3State(body);
|
|
101
|
+
return state === null ? blocked('merge-state-needs-repair', 'v3 state is invalid') : { ok: true, schema: 3, migrated: false, state, quarantine: journal.quarantine, report };
|
|
102
|
+
}
|
|
103
|
+
/** Read-only readiness probe. Never migrates, never writes. */
|
|
104
|
+
export async function readStateReadiness(io, home) {
|
|
105
|
+
let raw;
|
|
106
|
+
try {
|
|
107
|
+
raw = await io.readText(mergeStatePath(home));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return blocked('merge-state-needs-repair', 'state file is unreadable');
|
|
111
|
+
}
|
|
112
|
+
if (raw === null)
|
|
113
|
+
return { ok: true, schema: 3, migrated: false, state: { schemaVersion: 3, rows: [] }, quarantine: [], report: await readReport(io, home) };
|
|
114
|
+
const body = parseBody(raw);
|
|
115
|
+
if (body === null)
|
|
116
|
+
return blocked('merge-state-needs-repair', 'state file is not a JSON object');
|
|
117
|
+
const version = body.schemaVersion;
|
|
118
|
+
if (typeof version === 'number' && version > 3)
|
|
119
|
+
return blocked('merge-state-forward-only', 'state schema ' + String(version) + ' is newer than this build');
|
|
120
|
+
if (version === 3) {
|
|
121
|
+
const state = v3State(body);
|
|
122
|
+
if (state === null)
|
|
123
|
+
return blocked('merge-state-needs-repair', 'v3 state has an invalid row');
|
|
124
|
+
const journal = await readJournal(io, home);
|
|
125
|
+
const report = await readReport(io, home);
|
|
126
|
+
const marker = readMarker(body);
|
|
127
|
+
if (body.migration !== undefined && marker === null)
|
|
128
|
+
return blocked('merge-state-needs-repair', 'migration marker is invalid');
|
|
129
|
+
if (journal !== null)
|
|
130
|
+
return blocked('merge-state-migration-blocked', 'migration journal is pending');
|
|
131
|
+
if (report !== null && report.ready !== true)
|
|
132
|
+
return blocked('merge-state-migration-blocked', 'migration report is not ready');
|
|
133
|
+
if (marker !== null && rowsSha(state.rows) === marker.targetSha256) {
|
|
134
|
+
if (report === null)
|
|
135
|
+
return blocked('merge-state-migration-blocked', 'migration report is missing');
|
|
136
|
+
const missing = await missingIndex(io, home, marker.staging);
|
|
137
|
+
if (missing !== null)
|
|
138
|
+
return blocked('merge-state-migration-blocked', 'staging index is missing: ' + missing);
|
|
139
|
+
}
|
|
140
|
+
return { ok: true, schema: 3, migrated: false, state, quarantine: [], report };
|
|
141
|
+
}
|
|
142
|
+
return blocked('merge-state-migration-required', 'v2 state requires v3 migration');
|
|
143
|
+
}
|
|
144
|
+
/** Compatibility name for the v3 gate; 1b-2b switches the lifecycle to v4. */
|
|
145
|
+
export const ensureV3Ready = ensureMergeStateReady;
|
|
146
|
+
/** One readiness/migration gate; used by startup and every write entry. */
|
|
147
|
+
export async function ensureMergeStateReady(io, home) {
|
|
148
|
+
let outcome;
|
|
149
|
+
let migrated = false;
|
|
150
|
+
try {
|
|
151
|
+
await transactIo(io, mergeStatePath(home), async (current) => {
|
|
152
|
+
if (current === null) {
|
|
153
|
+
outcome = { ok: true, schema: 3, migrated: false, state: { schemaVersion: 3, rows: [] }, quarantine: [], report: await readReport(io, home) };
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const body = parseBody(current);
|
|
157
|
+
if (body === null) {
|
|
158
|
+
outcome = blocked('merge-state-needs-repair', 'state file is not a JSON object');
|
|
159
|
+
return current;
|
|
160
|
+
}
|
|
161
|
+
const version = body.schemaVersion;
|
|
162
|
+
if (typeof version === 'number' && version > 3) {
|
|
163
|
+
outcome = blocked('merge-state-forward-only', 'state schema ' + String(version) + ' is newer than this build');
|
|
164
|
+
return current;
|
|
165
|
+
}
|
|
166
|
+
if (version === 3) {
|
|
167
|
+
const state = v3State(body);
|
|
168
|
+
if (state === null) {
|
|
169
|
+
outcome = blocked('merge-state-needs-repair', 'v3 state has an invalid row');
|
|
170
|
+
return current;
|
|
171
|
+
}
|
|
172
|
+
const journal = await readJournal(io, home);
|
|
173
|
+
if (journal !== null) {
|
|
174
|
+
outcome = await completeJournal(io, home, journal, current);
|
|
175
|
+
return current;
|
|
176
|
+
}
|
|
177
|
+
const report = await readReport(io, home);
|
|
178
|
+
const marker = readMarker(body);
|
|
179
|
+
if (body.migration !== undefined && marker === null) {
|
|
180
|
+
outcome = blocked('merge-state-needs-repair', 'migration marker is invalid');
|
|
181
|
+
return current;
|
|
182
|
+
}
|
|
183
|
+
if (report !== null && report.ready !== true) {
|
|
184
|
+
outcome = blocked('merge-state-migration-blocked', 'migration report is not ready');
|
|
185
|
+
return current;
|
|
186
|
+
}
|
|
187
|
+
if (marker !== null && rowsSha(state.rows) === marker.targetSha256) {
|
|
188
|
+
if (report === null) {
|
|
189
|
+
outcome = blocked('merge-state-migration-blocked', 'migration report is missing');
|
|
190
|
+
return current;
|
|
191
|
+
}
|
|
192
|
+
for (const entry of marker.staging)
|
|
193
|
+
if (!(await io.exists(stagingIndexPath(home, entry.rowId))))
|
|
194
|
+
await writeStagingIndex(io, home, entry);
|
|
195
|
+
}
|
|
196
|
+
outcome = { ok: true, schema: 3, migrated: false, state, quarantine: [], report };
|
|
197
|
+
return current;
|
|
198
|
+
}
|
|
199
|
+
const mapped = migrateV2ToV3(body);
|
|
200
|
+
if (!mapped.ok) {
|
|
201
|
+
outcome = blocked('merge-state-migration-blocked', mapped.message);
|
|
202
|
+
return current;
|
|
203
|
+
}
|
|
204
|
+
const sourceSha256 = hash(current);
|
|
205
|
+
const backup = v2BackupPath(home);
|
|
206
|
+
const existingBackup = await readTextOrNull(io, backup);
|
|
207
|
+
if (existingBackup !== null && existingBackup !== current) {
|
|
208
|
+
outcome = blocked('merge-state-migration-blocked', 'v2 backup exists with different bytes');
|
|
209
|
+
return current;
|
|
210
|
+
}
|
|
211
|
+
if (existingBackup === null)
|
|
212
|
+
await transactIo(io, backup, () => current);
|
|
213
|
+
try {
|
|
214
|
+
await chmod(backup, 0o444);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
outcome = blocked('merge-state-migration-blocked', 'v2 backup is not read-only');
|
|
218
|
+
return current;
|
|
219
|
+
}
|
|
220
|
+
const staging = [];
|
|
221
|
+
for (const row of body.rows) {
|
|
222
|
+
if (typeof row.id !== 'string' || typeof row.pairKey !== 'string' || typeof row.stagingRef !== 'string')
|
|
223
|
+
continue;
|
|
224
|
+
if (row.state !== 'pending' && row.state !== 'approved')
|
|
225
|
+
continue;
|
|
226
|
+
staging.push({
|
|
227
|
+
rowId: row.id, pairKey: row.pairKey, ref: row.stagingRef,
|
|
228
|
+
expiresAt: typeof row.stagingExpiresAt === 'number' ? row.stagingExpiresAt : Date.now() + 7 * 86_400_000,
|
|
229
|
+
...(typeof row.runId === 'string' ? { runId: row.runId } : {}),
|
|
230
|
+
createdAt: typeof row.updatedAt === 'string' && !Number.isNaN(Date.parse(row.updatedAt)) ? Date.parse(row.updatedAt) : Date.now(),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const marker = { migratedFromV2: true, sourceSha256, targetSha256: rowsSha(mapped.state.rows), staging, at: now() };
|
|
234
|
+
const targetText = json({ ...mapped.state, migration: marker });
|
|
235
|
+
const targetSha256 = hash(targetText);
|
|
236
|
+
const journal = { version: 1, sourceSha256, targetSha256, rows: mapped.state.rows.length, quarantine: mapped.quarantine, staging, backupPath: backup, at: now() };
|
|
237
|
+
await transactIo(io, migrationJournalPath(home), () => json(journal));
|
|
238
|
+
for (const entry of staging)
|
|
239
|
+
await writeStagingIndex(io, home, entry);
|
|
240
|
+
if (mapped.quarantine.length > 0)
|
|
241
|
+
await transactIo(io, quarantinePath(home), () => json(mapped.quarantine));
|
|
242
|
+
const prepared = { version: 1, ready: false, migrated: true, sourceSha256, targetSha256, rows: mapped.state.rows.length, quarantined: mapped.quarantine.length, backupReadOnly: true, at: now(), steps: ['backup', 'journal', 'staging-index', 'quarantine', 'v3-write'] };
|
|
243
|
+
await writeReport(io, home, prepared);
|
|
244
|
+
migrated = true;
|
|
245
|
+
outcome = { ok: true, schema: 3, migrated: true, state: mapped.state, quarantine: mapped.quarantine, report: prepared };
|
|
246
|
+
return targetText;
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
return blocked('merge-state-migration-blocked', 'state migration failed: ' + (error instanceof Error ? error.message : String(error)));
|
|
251
|
+
}
|
|
252
|
+
if (outcome === undefined)
|
|
253
|
+
return blocked('merge-state-migration-blocked', 'state migration did not produce a result');
|
|
254
|
+
if (migrated && outcome.ok) {
|
|
255
|
+
try {
|
|
256
|
+
const report = { ...outcome.report, ready: true, at: now(), steps: ['backup', 'journal', 'staging-index', 'quarantine', 'v3-write', 'ready'] };
|
|
257
|
+
await writeReport(io, home, report);
|
|
258
|
+
await io.remove(migrationJournalPath(home)).catch(() => { });
|
|
259
|
+
outcome = { ...outcome, report };
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// The v3 state is committed; leave the journal/report for the next ensure.
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return outcome;
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=state-bootstrap.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
export type UndoPairPhase = 'pending' | 'restore-target' | 'restore-source' | 'receipt' | 'done';
|
|
3
|
+
export interface UndoPairState {
|
|
4
|
+
readonly pairKey: string;
|
|
5
|
+
readonly unitId: string;
|
|
6
|
+
readonly phase: UndoPairPhase;
|
|
7
|
+
readonly message?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface UndoJournal {
|
|
10
|
+
readonly version: 1;
|
|
11
|
+
readonly runId: string;
|
|
12
|
+
readonly phase: 'prepared' | 'running' | 'settled';
|
|
13
|
+
readonly pairs: readonly UndoPairState[];
|
|
14
|
+
readonly updatedAt: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function undoJournalPath(home: string, runId: string): string;
|
|
17
|
+
export declare function readUndoJournal(io: EvolutionIoLike, home: string, runId: string): Promise<UndoJournal | null>;
|
|
18
|
+
export declare function writeUndoJournal(io: EvolutionIoLike, home: string, journal: UndoJournal): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=undo-journal.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readTextOrNull } from "./read-probe.js";
|
|
2
|
+
/** Per-run undo journal: one pair at a time, durable phase before each write. */
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { transactIo } from '@lmzhen/dsh-evolution-core';
|
|
5
|
+
export function undoJournalPath(home, runId) { return join(home, 'merge', 'undo-journal', encodeURIComponent(runId) + '.json'); }
|
|
6
|
+
export async function readUndoJournal(io, home, runId) {
|
|
7
|
+
const raw = await readTextOrNull(io, undoJournalPath(home, runId));
|
|
8
|
+
if (raw === null)
|
|
9
|
+
return null;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(raw);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function writeUndoJournal(io, home, journal) {
|
|
18
|
+
await transactIo(io, undoJournalPath(home, journal.runId), () => JSON.stringify(journal, null, 2) + '\n');
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=undo-journal.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
export interface UndoReceipt {
|
|
3
|
+
readonly version: 1;
|
|
4
|
+
readonly runId: string;
|
|
5
|
+
readonly parentRunId?: string;
|
|
6
|
+
readonly pairKey: string;
|
|
7
|
+
readonly unitId: string;
|
|
8
|
+
readonly evidenceSha256: string;
|
|
9
|
+
readonly target: string;
|
|
10
|
+
readonly sources: readonly string[];
|
|
11
|
+
readonly restoredTargetHash: string | null;
|
|
12
|
+
readonly restoredSourceHashes: Readonly<Record<string, string>>;
|
|
13
|
+
readonly at: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function undoReceiptPath(home: string, runId: string, pairKey: string): string;
|
|
16
|
+
export declare function readUndoReceipt(io: EvolutionIoLike, home: string, runId: string, pairKey: string): Promise<UndoReceipt | null>;
|
|
17
|
+
export declare function writeUndoReceipt(io: EvolutionIoLike, home: string, receipt: UndoReceipt): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=undo-receipt.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readTextOrNull } from "./read-probe.js";
|
|
2
|
+
/** Undo receipt: immutable per-pair settlement, separate from the commit receipt. */
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
export function undoReceiptPath(home, runId, pairKey) {
|
|
6
|
+
return join(home, 'merge', 'undo-receipts', encodeURIComponent(runId), createHash('sha256').update(pairKey).digest('hex') + '.json');
|
|
7
|
+
}
|
|
8
|
+
export async function readUndoReceipt(io, home, runId, pairKey) {
|
|
9
|
+
const raw = await readTextOrNull(io, undoReceiptPath(home, runId, pairKey));
|
|
10
|
+
if (raw === null)
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(raw);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function writeUndoReceipt(io, home, receipt) {
|
|
20
|
+
await io.writeText(undoReceiptPath(home, receipt.runId, receipt.pairKey), JSON.stringify(receipt, null, 2) + '\n');
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=undo-receipt.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { SkillCard } from '../skill-card.ts';
|
|
2
|
+
import type { MergeLimitsSnapshot } from './merge-limits.ts';
|
|
3
|
+
import type { StagedTree, RuleFinding } from '../rules.ts';
|
|
4
|
+
export interface StagedFile {
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly content: string;
|
|
7
|
+
readonly digest: string;
|
|
8
|
+
readonly bytes: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ValidationPlan {
|
|
11
|
+
readonly planVersion: 1;
|
|
12
|
+
readonly stageDigest: string;
|
|
13
|
+
readonly target: string;
|
|
14
|
+
readonly body: string;
|
|
15
|
+
readonly files: readonly StagedFile[];
|
|
16
|
+
readonly supportFiles: readonly {
|
|
17
|
+
readonly path: string;
|
|
18
|
+
readonly digest: string;
|
|
19
|
+
readonly source: string;
|
|
20
|
+
}[];
|
|
21
|
+
readonly manifestFiles: readonly string[];
|
|
22
|
+
readonly q09: {
|
|
23
|
+
readonly forwardRefs: readonly string[];
|
|
24
|
+
readonly reverseRefs: readonly string[];
|
|
25
|
+
readonly orphanFiles: readonly string[];
|
|
26
|
+
};
|
|
27
|
+
readonly supportConflicts: readonly {
|
|
28
|
+
readonly path: string;
|
|
29
|
+
readonly sources: readonly string[];
|
|
30
|
+
}[];
|
|
31
|
+
readonly threat: {
|
|
32
|
+
readonly body: boolean;
|
|
33
|
+
readonly files: readonly string[];
|
|
34
|
+
};
|
|
35
|
+
readonly sourceRevisions: Readonly<Record<string, string>>;
|
|
36
|
+
readonly targetRevision: string | null;
|
|
37
|
+
readonly limitsDigest: string;
|
|
38
|
+
readonly promptVersion?: string;
|
|
39
|
+
readonly promptHash?: string;
|
|
40
|
+
readonly revision: string;
|
|
41
|
+
readonly findings: readonly RuleFinding[];
|
|
42
|
+
}
|
|
43
|
+
export declare function buildValidationPlan(input: {
|
|
44
|
+
readonly tree: StagedTree;
|
|
45
|
+
readonly target: string;
|
|
46
|
+
readonly limits: MergeLimitsSnapshot;
|
|
47
|
+
readonly cards?: readonly SkillCard[];
|
|
48
|
+
readonly sourceRevisions?: Readonly<Record<string, string>>;
|
|
49
|
+
readonly targetRevision?: string | null;
|
|
50
|
+
readonly manifestFiles?: readonly string[];
|
|
51
|
+
readonly existingSupport?: readonly string[];
|
|
52
|
+
readonly supportFiles?: readonly {
|
|
53
|
+
readonly path: string;
|
|
54
|
+
readonly content: string;
|
|
55
|
+
readonly source: string;
|
|
56
|
+
}[];
|
|
57
|
+
readonly supportSources?: readonly {
|
|
58
|
+
readonly path: string;
|
|
59
|
+
readonly content: string;
|
|
60
|
+
readonly source: string;
|
|
61
|
+
}[];
|
|
62
|
+
readonly promptVersion?: string;
|
|
63
|
+
readonly promptHash?: string;
|
|
64
|
+
}): ValidationPlan;
|
|
65
|
+
//# sourceMappingURL=validation-plan.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Immutable post-stage validation plan: Q09/support/threat/revision facts. */
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { scanContentThreats, supportRefs } from '@lmzhen/dsh-evolution-core';
|
|
4
|
+
function digest(value) { return createHash('sha256').update(value).digest('hex'); }
|
|
5
|
+
export function buildValidationPlan(input) {
|
|
6
|
+
const files = input.tree.files.map(file => ({ path: file.path, content: file.content, digest: digest(file.content), bytes: Buffer.byteLength(file.content, 'utf8') }));
|
|
7
|
+
const manifestFiles = [...new Set(input.manifestFiles ?? files.map(file => file.path))].sort();
|
|
8
|
+
const supportFiles = (input.supportFiles ?? files.map(file => ({ path: file.path, content: file.content, source: 'staged' }))).map(file => ({ path: file.path, digest: digest(file.content), source: file.source })).sort((a, b) => a.path.localeCompare(b.path));
|
|
9
|
+
const refs = new Set(supportRefs(input.tree.body));
|
|
10
|
+
const staged = new Set(files.map(file => file.path));
|
|
11
|
+
const manifest = new Set(manifestFiles);
|
|
12
|
+
const forwardRefs = [...refs].filter(ref => !staged.has(ref)).sort();
|
|
13
|
+
const reverseRefs = files.map(file => file.path).filter(path => !refs.has(path) && manifest.has(path)).sort();
|
|
14
|
+
const existing = new Set(input.existingSupport ?? []);
|
|
15
|
+
const orphanFiles = [...existing].filter(path => !staged.has(path)).sort();
|
|
16
|
+
const byPath = new Map();
|
|
17
|
+
for (const file of input.supportSources ?? []) {
|
|
18
|
+
const entry = byPath.get(file.path) ?? { contents: new Set(), sources: [] };
|
|
19
|
+
entry.contents.add(file.content);
|
|
20
|
+
entry.sources.push(file.source);
|
|
21
|
+
byPath.set(file.path, entry);
|
|
22
|
+
}
|
|
23
|
+
const supportConflicts = [...byPath.entries()].filter(([, value]) => value.contents.size > 1).map(([path, value]) => ({ path, sources: value.sources }));
|
|
24
|
+
const sourceThreatFiles = (input.cards ?? []).flatMap(card => (card.files ?? []).filter(file => scanContentThreats(file.content) !== null).map(file => card.name + '/' + file.path));
|
|
25
|
+
const threat = { body: scanContentThreats(input.tree.body) !== null || (input.cards ?? []).some(card => scanContentThreats(card.body) !== null), files: [...files.filter(file => scanContentThreats(file.content) !== null).map(file => file.path), ...sourceThreatFiles] };
|
|
26
|
+
const sourceRevisions = input.sourceRevisions ?? Object.fromEntries((input.cards ?? []).map(card => [card.name, digest(card.body)]));
|
|
27
|
+
const revision = digest(JSON.stringify({ target: input.target, body: digest(input.tree.body), files: files.map(file => [file.path, file.digest]), q09: { forwardRefs, reverseRefs, orphanFiles }, supportConflicts, threat, sourceRevisions, targetRevision: input.targetRevision ?? null }));
|
|
28
|
+
const findings = [];
|
|
29
|
+
const stageDigest = digest(JSON.stringify(files.map(file => [file.path, file.digest])));
|
|
30
|
+
if (forwardRefs.length > 0)
|
|
31
|
+
findings.push({ rule: 'q09-forward', path: forwardRefs.join(','), severity: 'block', message: 'body references unstaged support' });
|
|
32
|
+
if (reverseRefs.length > 0)
|
|
33
|
+
findings.push({ rule: 'q09-reverse', path: reverseRefs.join(','), severity: 'block', message: 'staged manifest file is not referenced by body' });
|
|
34
|
+
if (orphanFiles.length > 0)
|
|
35
|
+
findings.push({ rule: 'q09-orphan', path: orphanFiles.join(','), severity: 'block', message: 'target tree support is not declared by plan' });
|
|
36
|
+
if (supportConflicts.length > 0)
|
|
37
|
+
findings.push({ rule: 'support-conflict', path: supportConflicts.map(item => item.path).join(','), severity: 'block', message: 'same support path has different contents' });
|
|
38
|
+
if (threat.body)
|
|
39
|
+
findings.push({ rule: 'threat', path: 'SKILL.md', severity: 'block', message: 'body tripped content threat scanner' });
|
|
40
|
+
for (const path of threat.files)
|
|
41
|
+
findings.push({ rule: 'threat', path, severity: 'block', message: 'support file tripped content threat scanner' });
|
|
42
|
+
if (Buffer.byteLength(input.tree.body, 'utf8') > input.limits.skillLimits.maxSkillFileBytes)
|
|
43
|
+
findings.push({ rule: 'limit', path: 'SKILL.md', severity: 'block', message: 'body exceeds maxSkillFileBytes' });
|
|
44
|
+
for (const file of files)
|
|
45
|
+
if (file.bytes > input.limits.skillLimits.maxSkillFileBytes)
|
|
46
|
+
findings.push({ rule: 'limit', path: file.path, severity: 'block', message: 'file exceeds maxSkillFileBytes' });
|
|
47
|
+
return { planVersion: 1, stageDigest, target: input.target, body: input.tree.body, files, supportFiles, manifestFiles, q09: { forwardRefs, reverseRefs, orphanFiles }, supportConflicts, threat, sourceRevisions, targetRevision: input.targetRevision ?? null, limitsDigest: input.limits.limitsDigest, ...(input.promptVersion === undefined ? {} : { promptVersion: input.promptVersion }), ...(input.promptHash === undefined ? {} : { promptHash: input.promptHash }), revision, findings };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=validation-plan.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate filter: turn lexical/proposed candidates into the candidates scan may process.
|
|
3
|
+
* L0 keeps are dropped unless the deterministic 10% audit re-runs L2 and overrides.
|
|
4
|
+
*/
|
|
5
|
+
import type { MergeCandidate } from './contracts.ts';
|
|
6
|
+
import { type GateCache, type GateFacts, type GateModelPort, type GateVerdict } from './ladder.ts';
|
|
7
|
+
export interface GateFilterOptions {
|
|
8
|
+
readonly facts: readonly GateFacts[];
|
|
9
|
+
readonly model: GateModelPort;
|
|
10
|
+
readonly modelName: string;
|
|
11
|
+
readonly signal: AbortSignal;
|
|
12
|
+
readonly runSeed: string;
|
|
13
|
+
readonly cache?: GateCache;
|
|
14
|
+
readonly auditRate?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface GateDecision {
|
|
17
|
+
readonly candidate: MergeCandidate;
|
|
18
|
+
readonly verdicts: readonly GateVerdict[];
|
|
19
|
+
readonly auditOverride: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface GateFilterResult {
|
|
22
|
+
readonly kept: readonly GateDecision[];
|
|
23
|
+
readonly dropped: readonly GateDecision[];
|
|
24
|
+
readonly errors: readonly {
|
|
25
|
+
readonly candidate: MergeCandidate;
|
|
26
|
+
readonly message: string;
|
|
27
|
+
}[];
|
|
28
|
+
readonly calls: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function filterCandidates(candidates: readonly MergeCandidate[], options: GateFilterOptions): Promise<GateFilterResult>;
|
|
31
|
+
//# sourceMappingURL=gate-filter.d.ts.map
|