@lmzhen/dsh-evolution-merge 0.41.0 → 0.43.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 +760 -101
- package/lib/types/adapters/library.d.ts +1 -1
- package/lib/types/adapters/model-runner.d.ts +1 -1
- package/lib/types/adapters/model-runner.js +1 -1
- package/lib/types/adapters/undo.d.ts +1 -6
- package/lib/types/authoring-flow.d.ts +2 -1
- package/lib/types/commit-assembly.d.ts +4 -2
- package/lib/types/commit-assembly.js +2 -1
- package/lib/types/commit-execute.d.ts +3 -2
- package/lib/types/commit-execute.js +1 -25
- package/lib/types/commit-plan.d.ts +2 -1
- package/lib/types/commit-recovery.js +1 -1
- package/lib/types/doctor.js +2 -1
- package/lib/types/domain/evidence-hash.d.ts +1 -1
- package/lib/types/index.js +5 -3
- package/lib/types/new-child-authoring.d.ts +2 -1
- package/lib/types/recovery-coordinator.d.ts +2 -1
- package/lib/types/recovery-coordinator.js +2 -1
- package/lib/types/recovery-projection.d.ts +2 -1
- package/lib/types/revision.js +2 -1
- package/lib/types/rules.js +3 -1
- package/lib/types/runtime.d.ts +3 -1
- package/lib/types/runtime.js +5 -2
- package/lib/types/scan-ports.d.ts +2 -1
- package/lib/types/skill-library-unit-backend.d.ts +5 -0
- package/lib/types/skill-library-unit-backend.js +56 -0
- package/lib/types/undo-merge.d.ts +2 -2
- package/lib/types/undo-merge.js +1 -1
- package/lib/types/undo-operation.js +1 -1
- package/lib/types/unit-claim.d.ts +1 -1
- package/lib/types/unit-protocol.d.ts +227 -0
- package/lib/types/unit-protocol.js +566 -0
- package/package.json +5 -6
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Library adapter: the only place the merge domain touches SkillLibrary's commit kernel.
|
|
3
3
|
* The domain stays port-shaped; this file is the composition seam.
|
|
4
4
|
*/
|
|
5
|
-
import type { SkillLibrary } from '@lmzhen/dsh-evolution-core';
|
|
5
|
+
import type { SkillLibrary } from '@lmzhen/dsh-evolution-core/skills';
|
|
6
6
|
import type { CommitPort } from '../commit-execute.ts';
|
|
7
7
|
export declare function skillLibraryPort(skills: SkillLibrary): CommitPort;
|
|
8
8
|
//# sourceMappingURL=library.d.ts.map
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* runner(prompt, cfg) and expects raw model text; the runtime owns provider
|
|
4
4
|
* selection, purpose, transport and timeout.
|
|
5
5
|
*/
|
|
6
|
-
import { createUserMessage } from '@
|
|
6
|
+
import { createUserMessage } from '@lmzhen/dsh-evolution-core/platform';
|
|
7
7
|
export interface ModelRoute {
|
|
8
8
|
readonly provider: string;
|
|
9
9
|
readonly model: string;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* runner(prompt, cfg) and expects raw model text; the runtime owns provider
|
|
4
4
|
* selection, purpose, transport and timeout.
|
|
5
5
|
*/
|
|
6
|
-
import { createUserMessage } from '@
|
|
6
|
+
import { createUserMessage } from '@lmzhen/dsh-evolution-core/platform';
|
|
7
7
|
import { normalizeProviderKey } from "../domain/provider-key.js";
|
|
8
8
|
export function createEngineRunner(llm, route) {
|
|
9
9
|
return async (prompt, cfg, runSignal) => {
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* SkillLibrary-backed undo sink. Blobs are content-addressed but the library exposes them by
|
|
3
|
-
* version, so the first readBlob call indexes all version bodies once; an undo is rare enough
|
|
4
|
-
* that this is preferable to adding a second hash index.
|
|
5
|
-
*/
|
|
6
|
-
import { type SkillLibrary } from '@lmzhen/dsh-evolution-core';
|
|
1
|
+
import { type SkillLibrary } from '@lmzhen/dsh-evolution-core/skills';
|
|
7
2
|
import type { MergeUndoPort } from '../undo-merge.ts';
|
|
8
3
|
export declare function skillLibraryUndoPort(skills: SkillLibrary): MergeUndoPort;
|
|
9
4
|
//# sourceMappingURL=undo.d.ts.map
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* rule gate, then commit through the injected CommitPort. The channel adapter and the
|
|
4
4
|
* author tool live outside; this is the seam they share.
|
|
5
5
|
*/
|
|
6
|
-
import type { EvolutionIoLike
|
|
6
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
7
|
+
import type { UnitTransaction } from './unit-protocol.ts';
|
|
7
8
|
import { type RuleFinding } from './rules.ts';
|
|
8
9
|
import { type CommitPort, type CommitStagedResult } from './commit-execute.ts';
|
|
9
10
|
import type { StagedTree } from './rules.ts';
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* port is called, so a source/target change is refused with a typed cause rather than a
|
|
8
8
|
* generic commit message.
|
|
9
9
|
*/
|
|
10
|
-
import type {
|
|
11
|
-
import {
|
|
10
|
+
import type { SkillLimits } from '@lmzhen/dsh-evolution-core';
|
|
11
|
+
import type { WriteOrigin } from '@lmzhen/dsh-evolution-core/skills';
|
|
12
|
+
import type { CommitMergePlan } from '@lmzhen/dsh-evolution-core/skills';
|
|
13
|
+
import { type RevisionFile } from '@lmzhen/dsh-evolution-core/skills';
|
|
12
14
|
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
13
15
|
import type { RevisionRef } from './contracts.ts';
|
|
14
16
|
export interface CommitAssemblyPort {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { isPresent
|
|
1
|
+
import { isPresent } from '@lmzhen/dsh-evolution-core';
|
|
2
2
|
import { readStaging } from "./staging.js";
|
|
3
3
|
import { readStagedTree } from "./staged-tree.js";
|
|
4
4
|
import { buildCommitPlan } from "./commit-plan.js";
|
|
5
|
+
import { revisionAnchorOf, revisionFingerprint, revisionSupportDigest } from '@lmzhen/dsh-evolution-core/skills';
|
|
5
6
|
function sameAnchor(expected, live) {
|
|
6
7
|
if (expected.kind === 'absent')
|
|
7
8
|
return live.kind === 'absent';
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* receipt. A failed commit returns without a receipt; a landed commit with a failed receipt
|
|
4
4
|
* stays successful and carries a warning.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { EvolutionIoLike, SkillLimits } from '@lmzhen/dsh-evolution-core';
|
|
7
|
+
import type { CommitMergePlan, CommitMergeResult } from '@lmzhen/dsh-evolution-core/skills';
|
|
7
8
|
import { type CommitAssemblyPort } from './commit-assembly.ts';
|
|
8
9
|
import { type CommitReceipt } from './commit-receipt.ts';
|
|
9
|
-
import type { UnitTransaction } from '
|
|
10
|
+
import type { UnitTransaction } from './unit-protocol.ts';
|
|
10
11
|
export interface CommitPort extends CommitAssemblyPort {
|
|
11
12
|
/** Transitional G2: when set, commit-execute prefers this over commitMerge. */
|
|
12
13
|
unit?: UnitTransaction;
|
|
@@ -21,7 +21,7 @@ export async function commitStagedRun(io, home, runId, port, options) {
|
|
|
21
21
|
let result;
|
|
22
22
|
let evidenceSha256;
|
|
23
23
|
let warning;
|
|
24
|
-
const transaction = options.unit ?? port.unit
|
|
24
|
+
const transaction = options.unit ?? port.unit;
|
|
25
25
|
if (transaction === undefined)
|
|
26
26
|
return { ok: false, message: 'commit port missing: UnitTransaction or commitMerge required', plan: assembled.plan };
|
|
27
27
|
{
|
|
@@ -99,28 +99,4 @@ export async function commitStagedRun(io, home, runId, port, options) {
|
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
function legacyUnitAdapter(port, plan, options, runId) {
|
|
103
|
-
if (port.commitMerge === undefined)
|
|
104
|
-
return undefined;
|
|
105
|
-
let evidence;
|
|
106
|
-
const unitId = 'legacy:' + plan.target + ':' + String(options.now());
|
|
107
|
-
return {
|
|
108
|
-
async prepare() { return { ok: true, prepared: { unitId, planDigest: unitId } }; },
|
|
109
|
-
async commit() {
|
|
110
|
-
if (port.commitMerge === undefined)
|
|
111
|
-
return { ok: false, code: 'unit-commit-failed', message: 'commitMerge missing' };
|
|
112
|
-
const result = await port.commitMerge(plan);
|
|
113
|
-
if (!result.ok)
|
|
114
|
-
return { ok: false, code: result.code ?? 'unit-commit-failed', message: result.message };
|
|
115
|
-
const actualUnitId = result.unitId ?? unitId;
|
|
116
|
-
evidence = { schemaVersion: 1, unitId: actualUnitId, runId, planDigest: actualUnitId, state: 'complete', target: { before: plan.expectedTargetBody, after: plan.body, supportDigest: '', created: !plan.targetExists }, sources: plan.expectedSourceBodies.map(source => ({ name: source.name, before: source.body, after: null, archived: (result.archived ?? []).includes(source.name), absorbedInto: plan.target })), mutations: result.mutations ?? 0, at: options.now() };
|
|
117
|
-
return { ok: true, evidence };
|
|
118
|
-
},
|
|
119
|
-
async verify() { return evidence ?? null; },
|
|
120
|
-
async rollback() { return { ok: false, code: 'unit-committed', message: 'legacy commit cannot roll back' }; },
|
|
121
|
-
async finalize() { return evidence === undefined ? { ok: false, code: 'unit-plan-missing', message: 'legacy unit has no evidence' } : { ok: true, evidence }; },
|
|
122
|
-
async rollForward() { return evidence === undefined ? { ok: false, code: 'unit-plan-missing', message: 'legacy unit has no evidence' } : { ok: true, evidence }; },
|
|
123
|
-
async cancel() { return { ok: false, phase: 'none' }; },
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
102
|
//# sourceMappingURL=commit-execute.js.map
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* This module only turns those bytes into a CommitMergePlan; it never reads the live tree and
|
|
6
6
|
* never decides whether a merge is allowed (validateStagedTree does that).
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { SkillLimits } from '@lmzhen/dsh-evolution-core';
|
|
9
|
+
import type { CommitMergePlan, WriteOrigin } from '@lmzhen/dsh-evolution-core/skills';
|
|
9
10
|
import type { RevisionRef } from './contracts.ts';
|
|
10
11
|
export type ResolveTargetResult = {
|
|
11
12
|
readonly ok: true;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { writeJournal } from "./domain/commit-journal-store.js";
|
|
4
4
|
import { readCommitReceipt } from "./commit-receipt.js";
|
|
5
|
-
import { readUnitEvidence } from '@lmzhen/dsh-evolution-core';
|
|
5
|
+
import { readUnitEvidence } from '@lmzhen/dsh-evolution-core/write';
|
|
6
6
|
export async function recoverCommitJournals(io, home, now, isActive) {
|
|
7
7
|
const dir = join(home, 'merge', 'journals');
|
|
8
8
|
const reports = [];
|
package/lib/types/doctor.js
CHANGED
|
@@ -7,7 +7,8 @@ import { readTextOrNull } from "./domain/read-probe.js";
|
|
|
7
7
|
import { createHash } from 'node:crypto';
|
|
8
8
|
import { stat } from 'node:fs/promises';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
|
-
import { probeList, readTreeLock,
|
|
10
|
+
import { probeList, readTreeLock, runsFile } from '@lmzhen/dsh-evolution-core';
|
|
11
|
+
import { readUnitJournal } from '@lmzhen/dsh-evolution-core/write';
|
|
11
12
|
import { migrationJournalPath, migrationReportPath, v2BackupPath } from "./domain/state-bootstrap.js";
|
|
12
13
|
import { readMergeSnapshot } from "./domain/merge-snapshot.js";
|
|
13
14
|
import { validateArtifacts } from "./domain/consistency.js";
|
package/lib/types/index.js
CHANGED
|
@@ -4,8 +4,9 @@ import { screen as engineScreen } from '@lmzhen/dsh-evolution-merge-engine';
|
|
|
4
4
|
import { selectWithErrors } from "./adapters/select.js";
|
|
5
5
|
import { createEngineScreening } from "./adapters/engine-screen.js";
|
|
6
6
|
import { createEngineRunner } from "./adapters/model-runner.js";
|
|
7
|
-
import { authorityFrom, createSubagentChannel, evolutionHome, evolutionIoAdapter, newRunRegistry,
|
|
7
|
+
import { authorityFrom, createSubagentChannel, evolutionHome, evolutionIoAdapter, newRunRegistry, reportsSweepLockTarget, sweepReports, transactIo } from '@lmzhen/dsh-evolution-core';
|
|
8
8
|
import { registerOwnerSettings } from '@lmzhen/dsh-evolution-core';
|
|
9
|
+
import { newSkillLibrary } from '@lmzhen/dsh-evolution-core/skills';
|
|
9
10
|
import { registerHostRoutes } from '@lmzhen/dsh-evolution-core/http-routes';
|
|
10
11
|
import { loadMergeState } from "./state.js";
|
|
11
12
|
import { pairLedger, normalizePairLedger } from "./domain/pair-ledger.js";
|
|
@@ -13,7 +14,8 @@ import { legacyToV3 } from "./domain/row-v3-bridge.js";
|
|
|
13
14
|
import { readV4Readiness } from "./domain/schema4.js";
|
|
14
15
|
import { readMergeSnapshot } from "./domain/merge-snapshot.js";
|
|
15
16
|
import { readCommitReceipts } from "./commit-receipt.js";
|
|
16
|
-
import {
|
|
17
|
+
import { MergeUnitTransactions } from "./unit-protocol.js";
|
|
18
|
+
import { skillLibraryUnitBackend } from "./skill-library-unit-backend.js";
|
|
17
19
|
import { targetClaimUnitPort } from "./unit-claim.js";
|
|
18
20
|
import { resolveLimitsSnapshot } from "./domain/merge-limits.js";
|
|
19
21
|
import { resolveEngineInput } from "./domain/engine-input.js";
|
|
@@ -213,7 +215,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
213
215
|
ctx.logger.warn('merge state is not ready: ' + value.code + ': ' + value.message);
|
|
214
216
|
return;
|
|
215
217
|
}
|
|
216
|
-
const unit = new
|
|
218
|
+
const unit = new MergeUnitTransactions({ io, home, backend: skillLibraryUnitBackend(skills), claim: targetClaimUnitPort(io, home, () => Date.now()), now: () => Date.now(), isActive: async (runId) => await runs.hasOwner(runId), serial: async (task) => await skills.runSerial(task), ...(mutationAuthority?.homeFence === undefined ? {} : { fence: mutationAuthority.homeFence }) });
|
|
217
219
|
const cards = async () => await readSkillCards(skills);
|
|
218
220
|
void (async () => {
|
|
219
221
|
// NFR-05: load the shared run index BEFORE recovery. Recovery settles through the
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Round2 with a NEW child (approval after the pending window). The child gets the round2
|
|
3
3
|
* prompt and writes the staging tree; the plugin then runs the same gate/commit path.
|
|
4
4
|
*/
|
|
5
|
-
import type { EvolutionIoLike
|
|
5
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
6
|
+
import type { UnitTransaction } from './unit-protocol.ts';
|
|
6
7
|
import type { MergeDecision, MergeRow } from './contracts.ts';
|
|
7
8
|
import type { SkillCard } from './skill-card.ts';
|
|
8
9
|
import type { ConversationChannelLike } from './channel-port.ts';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { EvolutionIoLike
|
|
1
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
import type { UnitTransaction } from './unit-protocol.ts';
|
|
2
3
|
export type RecoveryBranch = 'A' | 'B' | 'C' | 'D';
|
|
3
4
|
export interface RecoveryReport {
|
|
4
5
|
readonly unitId: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Batch 2c unit-journal recovery coordinator. UnitTransaction remains the only skill writer. */
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { probeList
|
|
3
|
+
import { probeList } from '@lmzhen/dsh-evolution-core';
|
|
4
|
+
import { readUnitEvidence } from '@lmzhen/dsh-evolution-core/write';
|
|
4
5
|
import { evidenceSha256 } from "./domain/evidence-hash.js";
|
|
5
6
|
import { projectRecoveredUnit } from "./recovery-projection.js";
|
|
6
7
|
import { readCommitReceipts } from "./commit-receipt.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Post-recovery projection: evidence -> report -> row remove -> stage drop -> settle/meta. */
|
|
2
|
-
import type { EvolutionIoLike
|
|
2
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
3
|
+
import type { UnitEvidenceRecord } from '@lmzhen/dsh-evolution-core/write';
|
|
3
4
|
export interface ProjectionResult {
|
|
4
5
|
readonly ok: boolean;
|
|
5
6
|
readonly action: string;
|
package/lib/types/revision.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { contentHash
|
|
1
|
+
import { contentHash } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
import { revisionFingerprint, revisionSupportDigest } from '@lmzhen/dsh-evolution-core/skills';
|
|
2
3
|
function fingerprint(card) {
|
|
3
4
|
return revisionFingerprint(card.name, card.body, card.files ?? []);
|
|
4
5
|
}
|
package/lib/types/rules.js
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* block = commit must not proceed; warn = report only. The path and reference checks reuse
|
|
5
5
|
* the skill-store's own `validateSupportPath` / `supportRefs`, so there is one whitelist.
|
|
6
6
|
*/
|
|
7
|
-
import { DEFAULT_SKILL_LIMITS,
|
|
7
|
+
import { DEFAULT_SKILL_LIMITS, scanContentThreats, supportRefs } from '@lmzhen/dsh-evolution-core';
|
|
8
|
+
import { parseFrontmatter, validateFrontmatter } from '@lmzhen/dsh-evolution-core/skills';
|
|
9
|
+
import { validateSupportPath } from '@lmzhen/dsh-evolution-core/skills';
|
|
8
10
|
const PLACEHOLDER = /\bTODO\b|\bxxx\b|\{\{[^}]*\}\}/i;
|
|
9
11
|
const DESC_WARN_LENGTH = 60;
|
|
10
12
|
export function validateStagedTree(tree, limits = DEFAULT_SKILL_LIMITS) {
|
package/lib/types/runtime.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
import { SkillLibrary } from '@lmzhen/dsh-evolution-core/skills';
|
|
3
|
+
import type { UnitTransaction } from './unit-protocol.ts';
|
|
2
4
|
import { type MergeCandidate, type MergeDecision, type MergeRow } from './contracts.ts';
|
|
3
5
|
import type { MergeParams } from './params.ts';
|
|
4
6
|
import type { SkillCard } from './skill-card.ts';
|
package/lib/types/runtime.js
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* out of apply() and makes the mutating verbs testable with fakes.
|
|
5
5
|
*/
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
|
-
import { errorText, isProcessAlive, probeText
|
|
7
|
+
import { errorText, isProcessAlive, probeText } from '@lmzhen/dsh-evolution-core';
|
|
8
|
+
import { SkillLibrary } from '@lmzhen/dsh-evolution-core/skills';
|
|
9
|
+
import { MergeUnitTransactions } from "./unit-protocol.js";
|
|
10
|
+
import { skillLibraryUnitBackend } from "./skill-library-unit-backend.js";
|
|
8
11
|
import { targetClaimUnitPort } from "./unit-claim.js";
|
|
9
12
|
import { canRetry, pairKey } from "./contracts.js";
|
|
10
13
|
import { skippedPairKeys } from "./skipped.js";
|
|
@@ -77,7 +80,7 @@ export function unitFor(deps) {
|
|
|
77
80
|
return deps.unit;
|
|
78
81
|
if (!(deps.skills instanceof SkillLibrary))
|
|
79
82
|
return undefined;
|
|
80
|
-
return new
|
|
83
|
+
return new MergeUnitTransactions({ io: deps.io, home: deps.home, backend: skillLibraryUnitBackend(deps.skills), claim: targetClaimUnitPort(deps.io, deps.home, deps.now), now: deps.now, serial: async (task) => await deps.skills.runSerial(task) });
|
|
81
84
|
}
|
|
82
85
|
export function createMergeRuntime(deps) {
|
|
83
86
|
const ledger = () => pairLedger(deps.io, deps.home, deps.fence);
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Approval on: round1 disposes the child immediately; the row waits in pending and the later
|
|
6
6
|
* approve run must open a new child (not handled by this factory).
|
|
7
7
|
*/
|
|
8
|
-
import type { EvolutionIoLike
|
|
8
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
9
|
+
import type { UnitTransaction } from './unit-protocol.ts';
|
|
9
10
|
import type { MergeDecision, MergeRow } from './contracts.ts';
|
|
10
11
|
import type { SkillCard } from './skill-card.ts';
|
|
11
12
|
import { type ConversationChannelLike } from './channel-port.ts';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** G2 adapter: core UnitBackend over the existing SkillLibrary. */
|
|
2
|
+
import type { SkillLibrary } from '@lmzhen/dsh-evolution-core/skills';
|
|
3
|
+
import type { UnitBackend } from './unit-protocol.ts';
|
|
4
|
+
export declare function skillLibraryUnitBackend(skills: SkillLibrary): UnitBackend;
|
|
5
|
+
//# sourceMappingURL=skill-library-unit-backend.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function skillLibraryUnitBackend(skills) {
|
|
2
|
+
return {
|
|
3
|
+
async readSkill(name) { return await skills.read(name); },
|
|
4
|
+
async readSupportFile(name, path) {
|
|
5
|
+
const probe = skills.readSupportFile;
|
|
6
|
+
return typeof probe === 'function' ? await probe.call(skills, name, path) : null;
|
|
7
|
+
},
|
|
8
|
+
async commit(plan, onPhase, signal, assertFence, options) {
|
|
9
|
+
const result = await skills.commitMerge({
|
|
10
|
+
...(plan.runId === undefined ? {} : { runId: plan.runId }),
|
|
11
|
+
...(plan.unitId === undefined ? {} : { unitId: plan.unitId }),
|
|
12
|
+
target: plan.target,
|
|
13
|
+
nested: true,
|
|
14
|
+
targetExists: plan.targetExists,
|
|
15
|
+
sources: plan.sources,
|
|
16
|
+
body: plan.body,
|
|
17
|
+
supportFiles: plan.supportFiles,
|
|
18
|
+
expectedTargetBody: plan.expectedTargetBody,
|
|
19
|
+
expectedSourceBodies: plan.expectedSourceBodies,
|
|
20
|
+
origin: plan.origin,
|
|
21
|
+
summary: plan.summary,
|
|
22
|
+
...(plan.skillLimits === undefined ? {} : { limits: plan.skillLimits }),
|
|
23
|
+
...(plan.targetRevision === undefined ? {} : { targetRevision: plan.targetRevision }),
|
|
24
|
+
...(plan.sourceRevisions === undefined ? {} : { sourceRevisions: plan.sourceRevisions }),
|
|
25
|
+
...(plan.supportDigest === undefined ? {} : { supportDigest: plan.supportDigest }),
|
|
26
|
+
...(onPhase === undefined ? {} : { onPhase }),
|
|
27
|
+
...(signal === undefined ? {} : { signal }),
|
|
28
|
+
...(assertFence === undefined ? {} : { assertFence }),
|
|
29
|
+
}, { ...(options?.serialized === undefined ? {} : { serialized: options.serialized }) });
|
|
30
|
+
return { ok: result.ok, message: result.message, ...(result.code === undefined ? {} : { code: result.code }), ...(result.archived === undefined ? {} : { archived: result.archived }), ...(result.mutations === undefined ? {} : { mutations: result.mutations }) };
|
|
31
|
+
},
|
|
32
|
+
async restoreTarget(name, body) {
|
|
33
|
+
if (body === null) {
|
|
34
|
+
await skills.archive(name, { reason: 'unit-rollback' });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const result = await skills.update(name, body);
|
|
38
|
+
if (!result.ok)
|
|
39
|
+
throw new Error(result.message);
|
|
40
|
+
},
|
|
41
|
+
async restoreSupportFile(name, path, content) {
|
|
42
|
+
const result = content === null ? await skills.removeSupportFile(name, path) : await skills.writeSupportFile(name, path, content);
|
|
43
|
+
if (!result.ok)
|
|
44
|
+
throw new Error(result.message);
|
|
45
|
+
},
|
|
46
|
+
async restoreSource(name, body) {
|
|
47
|
+
const restored = await skills.restoreFromArchive(name);
|
|
48
|
+
if (restored.ok)
|
|
49
|
+
return;
|
|
50
|
+
const result = await skills.update(name, body);
|
|
51
|
+
if (!result.ok)
|
|
52
|
+
throw new Error(result.message);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=skill-library-unit-backend.js.map
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* sources; this module resolves versions, plans the cross-skill undo and runs the shared
|
|
4
4
|
* executor. No command/routing logic here.
|
|
5
5
|
*/
|
|
6
|
-
import type { SkillVersion } from '@lmzhen/dsh-evolution-core';
|
|
7
|
-
import { type UnitUndoExecution, type UnitUndoSink } from '@lmzhen/dsh-evolution-core';
|
|
6
|
+
import type { SkillVersion } from '@lmzhen/dsh-evolution-core/skills';
|
|
7
|
+
import { type UnitUndoExecution, type UnitUndoSink } from '@lmzhen/dsh-evolution-core/write';
|
|
8
8
|
import type { CommitReceipt } from './commit-receipt.ts';
|
|
9
9
|
export interface MergeUndoPort extends UnitUndoSink {
|
|
10
10
|
listVersions(skill: string): Promise<readonly SkillVersion[]>;
|
package/lib/types/undo-merge.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { executeUnitUndo, planUnitUndoAcross } from '@lmzhen/dsh-evolution-core';
|
|
1
|
+
import { executeUnitUndo, planUnitUndoAcross } from '@lmzhen/dsh-evolution-core/write';
|
|
2
2
|
export async function undoMergeReceipt(receipt, port) {
|
|
3
3
|
const targetVersions = (await port.listVersions(receipt.target)).filter(entry => entry.unitId === receipt.unitId);
|
|
4
4
|
if (targetVersions.length === 0) {
|
|
@@ -5,7 +5,7 @@ import { writeUndoJournal } from "./domain/undo-journal.js";
|
|
|
5
5
|
import { readUndoReceipt, writeUndoReceipt } from "./domain/undo-receipt.js";
|
|
6
6
|
import { recordPair } from "./domain/pair-index-store.js";
|
|
7
7
|
import { readRunMeta, writeRunMeta } from "./domain/run-coordinator.js";
|
|
8
|
-
import { readUnitEvidence } from '@lmzhen/dsh-evolution-core';
|
|
8
|
+
import { readUnitEvidence } from '@lmzhen/dsh-evolution-core/write';
|
|
9
9
|
import { evidenceSha256 as unitEvidenceDigest } from "./domain/evidence-hash.js";
|
|
10
10
|
function digest(value) { return value === null ? null : contentHash(value); }
|
|
11
11
|
function sortReceipts(receipts) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
-
import type { UnitClaimPort } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
import type { UnitClaimPort } from '@lmzhen/dsh-evolution-core/write';
|
|
3
3
|
/** Commit-phase claim: 24h is only stale recovery; commit releases immediately, no approve TTL. */
|
|
4
4
|
export declare function targetClaimUnitPort(io: EvolutionIoLike, home: string, now: () => number): UnitClaimPort;
|
|
5
5
|
//# sourceMappingURL=unit-claim.d.ts.map
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import type { EvolutionIoLike, SkillLimits } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
import type { HomeFence } from '@lmzhen/dsh-evolution-core';
|
|
3
|
+
import { type UnitEvidenceRecord } from '@lmzhen/dsh-evolution-core/write';
|
|
4
|
+
import { type UnitJournalPhase } from '@lmzhen/dsh-evolution-core/write';
|
|
5
|
+
export interface UnitSupportFile {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly content: string;
|
|
8
|
+
}
|
|
9
|
+
export interface UnitPlan {
|
|
10
|
+
readonly runId: string;
|
|
11
|
+
/** Filled by the transaction before the backend write; history records it. */
|
|
12
|
+
readonly unitId?: string;
|
|
13
|
+
readonly pairKey: string;
|
|
14
|
+
readonly target: string;
|
|
15
|
+
readonly targetExists: boolean;
|
|
16
|
+
readonly sources: readonly string[];
|
|
17
|
+
readonly body: string;
|
|
18
|
+
readonly supportFiles: readonly UnitSupportFile[];
|
|
19
|
+
readonly expectedTargetBody: string | null;
|
|
20
|
+
readonly expectedSourceBodies: readonly {
|
|
21
|
+
name: string;
|
|
22
|
+
body: string;
|
|
23
|
+
}[];
|
|
24
|
+
readonly origin: "foreground" | "subagent" | "background_review";
|
|
25
|
+
readonly summary: string;
|
|
26
|
+
readonly skillLimits?: SkillLimits;
|
|
27
|
+
readonly limitsDigest?: string;
|
|
28
|
+
readonly validationPlan?: {
|
|
29
|
+
readonly planVersion: 1;
|
|
30
|
+
readonly stageDigest: string;
|
|
31
|
+
readonly revision: string;
|
|
32
|
+
};
|
|
33
|
+
readonly targetRevision?: {
|
|
34
|
+
readonly kind: 'absent';
|
|
35
|
+
} | {
|
|
36
|
+
readonly kind: 'present';
|
|
37
|
+
readonly sha256: string;
|
|
38
|
+
};
|
|
39
|
+
readonly sourceRevisions?: Readonly<Record<string, {
|
|
40
|
+
readonly kind: 'absent';
|
|
41
|
+
} | {
|
|
42
|
+
readonly kind: 'present';
|
|
43
|
+
readonly sha256: string;
|
|
44
|
+
}>>;
|
|
45
|
+
readonly supportDigest?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface UnitCommitResult {
|
|
48
|
+
readonly ok: boolean;
|
|
49
|
+
readonly message: string;
|
|
50
|
+
/** Typed backend refusal (revision CAS); absent means a generic commit failure. */
|
|
51
|
+
readonly code?: UnitErrorCode;
|
|
52
|
+
readonly archived?: readonly string[];
|
|
53
|
+
readonly mutations?: number;
|
|
54
|
+
}
|
|
55
|
+
export interface UnitBackend {
|
|
56
|
+
readSkill(name: string): Promise<string | null>;
|
|
57
|
+
readSupportFile(name: string, path: string): Promise<string | null>;
|
|
58
|
+
commit(plan: UnitPlan, onPhase?: (phase: "target-pending" | "target-applied" | "target-written" | "source-archiving" | "sources-archived" | "support-pending" | "unit-written", detail?: {
|
|
59
|
+
source?: string;
|
|
60
|
+
}) => void | Promise<void>, signal?: AbortSignal, assertFence?: () => Promise<boolean>, options?: {
|
|
61
|
+
readonly serialized?: boolean;
|
|
62
|
+
}): Promise<UnitCommitResult>;
|
|
63
|
+
restoreTarget(name: string, body: string | null): Promise<void>;
|
|
64
|
+
restoreSource(name: string, body: string): Promise<void>;
|
|
65
|
+
restoreSupportFile?(name: string, path: string, content: string | null): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
import type { UnitClaimPort } from '@lmzhen/dsh-evolution-core/write';
|
|
68
|
+
export interface UnitTransactionOptions {
|
|
69
|
+
readonly io: EvolutionIoLike;
|
|
70
|
+
readonly home: string;
|
|
71
|
+
readonly backend: UnitBackend;
|
|
72
|
+
readonly claim: UnitClaimPort;
|
|
73
|
+
readonly now?: () => number;
|
|
74
|
+
readonly waitMs?: number;
|
|
75
|
+
/** Gate/fault-injection seam: called after each journal phase is durable. */
|
|
76
|
+
readonly onPhase?: (phase: UnitJournalPhase) => void | Promise<void>;
|
|
77
|
+
/** Gate instrumentation: tree fence then target claim. */
|
|
78
|
+
readonly onLock?: (lock: "tree" | "claim") => void;
|
|
79
|
+
/** One home fence owned by authority; when set, MergeUnitTransactions must not acquire tree-lock itself. */
|
|
80
|
+
readonly fence?: HomeFence;
|
|
81
|
+
/** Writer queue acquired BEFORE the fence, so merge and ordinary store writes share one lock order. */
|
|
82
|
+
readonly serial?: <T>(task: () => Promise<T>) => Promise<T>;
|
|
83
|
+
/** Optional run liveness gate: false means the run is terminal and must not commit. */
|
|
84
|
+
readonly isActive?: (runId: string) => boolean | Promise<boolean>;
|
|
85
|
+
}
|
|
86
|
+
export type UnitErrorCode = "unit-plan-missing" | "unit-conflict" | "unit-committed" | "unit-prepare-failed" | "unit-commit-failed" | "unit-claim-failed" | "unit-fence-failed" | "unit-run-inactive" | "unit-rollback-blocked" | "target-changed" | "source-changed" | "support-changed";
|
|
87
|
+
export interface UnitFailure {
|
|
88
|
+
readonly ok: false;
|
|
89
|
+
readonly code: UnitErrorCode;
|
|
90
|
+
readonly message: string;
|
|
91
|
+
}
|
|
92
|
+
export interface UnitPrepared {
|
|
93
|
+
readonly unitId: string;
|
|
94
|
+
readonly planDigest: string;
|
|
95
|
+
}
|
|
96
|
+
export interface UnitTransaction {
|
|
97
|
+
prepare(plan: UnitPlan): Promise<{
|
|
98
|
+
ok: true;
|
|
99
|
+
prepared: UnitPrepared;
|
|
100
|
+
} | UnitFailure>;
|
|
101
|
+
commit(prepared: UnitPrepared, options?: {
|
|
102
|
+
signal?: AbortSignal;
|
|
103
|
+
}): Promise<{
|
|
104
|
+
ok: true;
|
|
105
|
+
evidence: UnitEvidenceRecord;
|
|
106
|
+
} | UnitFailure>;
|
|
107
|
+
verify(ref: {
|
|
108
|
+
unitId: string;
|
|
109
|
+
} | {
|
|
110
|
+
runId: string;
|
|
111
|
+
}): Promise<UnitEvidenceRecord | UnitEvidenceRecord[] | null>;
|
|
112
|
+
rollback(ref: {
|
|
113
|
+
unitId: string;
|
|
114
|
+
reason: string;
|
|
115
|
+
}): Promise<{
|
|
116
|
+
ok: true;
|
|
117
|
+
evidence: UnitEvidenceRecord;
|
|
118
|
+
} | UnitFailure>;
|
|
119
|
+
finalize(unitId: string): Promise<{
|
|
120
|
+
ok: true;
|
|
121
|
+
evidence: UnitEvidenceRecord;
|
|
122
|
+
} | UnitFailure>;
|
|
123
|
+
rollForward(unitId: string): Promise<{
|
|
124
|
+
ok: true;
|
|
125
|
+
evidence: UnitEvidenceRecord;
|
|
126
|
+
} | UnitFailure>;
|
|
127
|
+
cancel(unitId: string, reason: string): Promise<{
|
|
128
|
+
ok: boolean;
|
|
129
|
+
phase: UnitJournalPhase | "none";
|
|
130
|
+
}>;
|
|
131
|
+
}
|
|
132
|
+
export declare function unitEvidenceDigest(evidence: UnitEvidenceRecord): string;
|
|
133
|
+
export declare class MergeUnitTransactions implements UnitTransaction {
|
|
134
|
+
private readonly options;
|
|
135
|
+
private readonly prepared;
|
|
136
|
+
private readonly commits;
|
|
137
|
+
private readonly now;
|
|
138
|
+
constructor(options: UnitTransactionOptions);
|
|
139
|
+
private unitId;
|
|
140
|
+
private snapshot;
|
|
141
|
+
prepare(plan: UnitPlan): Promise<{
|
|
142
|
+
ok: true;
|
|
143
|
+
prepared: {
|
|
144
|
+
unitId: string;
|
|
145
|
+
planDigest: string;
|
|
146
|
+
};
|
|
147
|
+
code?: never;
|
|
148
|
+
message?: never;
|
|
149
|
+
} | {
|
|
150
|
+
ok: false;
|
|
151
|
+
code: "unit-conflict";
|
|
152
|
+
message: string;
|
|
153
|
+
prepared?: never;
|
|
154
|
+
}>;
|
|
155
|
+
commit(prepared: UnitPrepared, options?: {
|
|
156
|
+
signal?: AbortSignal;
|
|
157
|
+
}): Promise<{
|
|
158
|
+
ok: true;
|
|
159
|
+
evidence: UnitEvidenceRecord;
|
|
160
|
+
} | UnitFailure>;
|
|
161
|
+
private commitOnce;
|
|
162
|
+
verify(ref: {
|
|
163
|
+
unitId: string;
|
|
164
|
+
} | {
|
|
165
|
+
runId: string;
|
|
166
|
+
}): Promise<UnitEvidenceRecord | UnitEvidenceRecord[] | null>;
|
|
167
|
+
private withRecoveryFence;
|
|
168
|
+
finalize(unitId: string): Promise<{
|
|
169
|
+
ok: true;
|
|
170
|
+
evidence: UnitEvidenceRecord;
|
|
171
|
+
code?: never;
|
|
172
|
+
message?: never;
|
|
173
|
+
} | {
|
|
174
|
+
ok: false;
|
|
175
|
+
code: "unit-rollback-blocked";
|
|
176
|
+
message: string;
|
|
177
|
+
evidence?: never;
|
|
178
|
+
}>;
|
|
179
|
+
private finalizeUnlocked;
|
|
180
|
+
rollForward(unitId: string): Promise<{
|
|
181
|
+
ok: true;
|
|
182
|
+
evidence: UnitEvidenceRecord;
|
|
183
|
+
code?: never;
|
|
184
|
+
message?: never;
|
|
185
|
+
} | {
|
|
186
|
+
ok: false;
|
|
187
|
+
code: "unit-rollback-blocked";
|
|
188
|
+
message: string;
|
|
189
|
+
evidence?: never;
|
|
190
|
+
}>;
|
|
191
|
+
private rollForwardUnlocked;
|
|
192
|
+
rollback(ref: {
|
|
193
|
+
unitId: string;
|
|
194
|
+
reason: string;
|
|
195
|
+
}): Promise<{
|
|
196
|
+
ok: false;
|
|
197
|
+
code: "unit-committed";
|
|
198
|
+
message: string;
|
|
199
|
+
evidence?: never;
|
|
200
|
+
} | {
|
|
201
|
+
ok: false;
|
|
202
|
+
code: "unit-rollback-blocked";
|
|
203
|
+
message: string;
|
|
204
|
+
evidence?: never;
|
|
205
|
+
} | {
|
|
206
|
+
ok: false;
|
|
207
|
+
code: "unit-fence-failed";
|
|
208
|
+
message: string;
|
|
209
|
+
evidence?: never;
|
|
210
|
+
} | {
|
|
211
|
+
ok: true;
|
|
212
|
+
evidence: UnitEvidenceRecord;
|
|
213
|
+
code?: never;
|
|
214
|
+
message?: never;
|
|
215
|
+
}>;
|
|
216
|
+
cancel(unitId: string, reason: string): Promise<{
|
|
217
|
+
ok: boolean;
|
|
218
|
+
phase: "none";
|
|
219
|
+
} | {
|
|
220
|
+
ok: boolean;
|
|
221
|
+
phase: "target-pending" | "target-written" | "source-archiving" | "sources-archived" | "support-pending";
|
|
222
|
+
} | {
|
|
223
|
+
ok: boolean;
|
|
224
|
+
phase: "failed" | "committed" | "unit-written" | "prepared" | "rolled-back";
|
|
225
|
+
}>;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=unit-protocol.d.ts.map
|