@lmzhen/dsh-evolution-core 0.30.37 → 0.30.38
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 +3776 -2300
- package/lib/types/evolution-events.d.ts +6 -2
- package/lib/types/evolution-events.js +6 -0
- package/lib/types/extract-plan.js +17 -11
- package/lib/types/extract-prompt.d.ts +1 -5
- package/lib/types/extract-prompt.js +2 -0
- package/lib/types/extract-request.d.ts +32 -0
- package/lib/types/extract-request.js +155 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.js +4 -0
- package/lib/types/mutation-storage-adapter.d.ts +14 -0
- package/lib/types/mutation-storage-adapter.js +107 -0
- package/lib/types/mutation-unit.d.ts +164 -0
- package/lib/types/mutation-unit.js +509 -0
- package/lib/types/prompts.d.ts +3 -3
- package/lib/types/prompts.js +8 -8
- package/lib/types/proposal-view.d.ts +4 -0
- package/lib/types/proposal-view.js +3 -1
- package/lib/types/skill-health.d.ts +5 -0
- package/lib/types/skill-health.js +3 -0
- package/lib/types/skill-history.d.ts +2 -0
- package/lib/types/skill-history.js +3 -0
- package/lib/types/skill-lint.d.ts +2 -0
- package/lib/types/skill-lint.js +13 -2
- package/lib/types/skill-model.d.ts +45 -0
- package/lib/types/skill-model.js +74 -0
- package/lib/types/skill-store.d.ts +22 -1
- package/lib/types/skill-store.js +415 -274
- package/lib/types/unit-members.js +3 -3
- package/lib/types/unit-storage.d.ts +10 -0
- package/lib/types/unit-transaction.d.ts +1 -1
- package/lib/types/unit-undo-plan.js +5 -3
- package/lib/types/unit-undo.d.ts +1 -1
- package/lib/types/unit-undo.js +18 -3
- package/package.json +1 -1
- package/persisted-write-inventory.json +3 -3
|
@@ -50,9 +50,9 @@ export function resolveUnitMembersFromIndexes(head, indexes, readEntries) {
|
|
|
50
50
|
for (const path of shardPaths)
|
|
51
51
|
if (!wanted.includes(path))
|
|
52
52
|
conflicts.push({ kind: 'foreign', path });
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
// No duplicated-path check: the manifest path is skill-relative and carries no skill, so one
|
|
54
|
+
// path in TWO skills (extract's source support removal + target support write) is the normal
|
|
55
|
+
// shape. A path-only duplicate refusal made every cross-skill support move un-undoable.
|
|
56
56
|
}
|
|
57
57
|
return { members, missing, conflicts };
|
|
58
58
|
}
|
|
@@ -23,6 +23,16 @@ export interface UnitSnapshot {
|
|
|
23
23
|
}[];
|
|
24
24
|
readonly origin: string;
|
|
25
25
|
readonly summary: string;
|
|
26
|
+
/** Tree-change units reuse this store; merge snapshots leave these absent. */
|
|
27
|
+
readonly kind?: 'merge' | 'tree-change';
|
|
28
|
+
readonly mutationState?: 'prepared' | 'committed' | 'undone';
|
|
29
|
+
readonly ops?: readonly {
|
|
30
|
+
readonly target: string;
|
|
31
|
+
readonly before: string | null;
|
|
32
|
+
readonly after: string | null;
|
|
33
|
+
readonly skill?: string;
|
|
34
|
+
}[];
|
|
35
|
+
readonly cleanup?: readonly string[];
|
|
26
36
|
readonly validationPlan?: {
|
|
27
37
|
readonly planVersion: 1;
|
|
28
38
|
readonly stageDigest: string;
|
|
@@ -214,7 +214,7 @@ export declare class UnitTransactions implements UnitTransaction {
|
|
|
214
214
|
phase: "target-pending" | "target-written" | "source-archiving" | "sources-archived";
|
|
215
215
|
} | {
|
|
216
216
|
ok: boolean;
|
|
217
|
-
phase: "failed" | "prepared" | "
|
|
217
|
+
phase: "failed" | "prepared" | "unit-written" | "committed" | "support-pending" | "rolled-back";
|
|
218
218
|
}>;
|
|
219
219
|
}
|
|
220
220
|
//# sourceMappingURL=unit-transaction.d.ts.map
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* means the caller writes nothing.
|
|
14
14
|
*/
|
|
15
15
|
import { BASELINE_ACTION, entryTarget } from "./skill-history.js";
|
|
16
|
+
import { contentHash } from "./mutations.js";
|
|
16
17
|
function slotsOf(entries) {
|
|
17
18
|
const body = {};
|
|
18
19
|
const support = new Map();
|
|
@@ -72,13 +73,14 @@ export function planUnitUndoAcross(members) {
|
|
|
72
73
|
for (const [path, slot] of support) {
|
|
73
74
|
if (slot.newest === undefined)
|
|
74
75
|
continue;
|
|
75
|
-
const
|
|
76
|
+
const removal = slot.oldest?.removed === true;
|
|
77
|
+
const preUnitHash = removal ? slot.oldest?.hash : slot.baseline !== undefined ? slot.baseline.hash : slot.oldest?.beforeHash;
|
|
76
78
|
ops.push({
|
|
77
79
|
skill: member.skill,
|
|
78
80
|
path,
|
|
79
|
-
remove: preUnitHash === undefined,
|
|
81
|
+
remove: !removal && preUnitHash === undefined,
|
|
80
82
|
...(preUnitHash === undefined ? {} : { preUnitHash }),
|
|
81
|
-
expectedHash: slot.newest.hash,
|
|
83
|
+
expectedHash: removal ? contentHash('') : slot.newest.hash,
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
86
|
if (body.newest !== undefined) {
|
package/lib/types/unit-undo.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface UnitUndoSink {
|
|
|
12
12
|
updateSkill(skill: string, body: string, anchor: {
|
|
13
13
|
sha256: string;
|
|
14
14
|
}): Promise<SkillActionResult>;
|
|
15
|
-
writeSupportFile(skill: string, path: string, content: string, anchor
|
|
15
|
+
writeSupportFile(skill: string, path: string, content: string, anchor?: {
|
|
16
16
|
sha256: string;
|
|
17
17
|
}): Promise<SkillActionResult>;
|
|
18
18
|
removeSupportFile(skill: string, path: string, anchor: {
|
package/lib/types/unit-undo.js
CHANGED
|
@@ -25,6 +25,21 @@ export async function executeUnitUndo(plan, sink) {
|
|
|
25
25
|
for (const op of plan.ops) {
|
|
26
26
|
const live = op.path === '' ? await sink.readSkill(op.skill) : await sink.readSupport(op.skill, op.path);
|
|
27
27
|
if (live === null) {
|
|
28
|
+
// The unit may have DELETED this path (extract moving an exclusive support file). Its
|
|
29
|
+
// after-hash is then the absent-content hash, and undo must recreate it from the pre-unit
|
|
30
|
+
// blob. A remove op against an already-absent path is a no-op.
|
|
31
|
+
// A remove op against an already-absent path is a no-op (the unit's deletion landed).
|
|
32
|
+
if (op.remove)
|
|
33
|
+
continue;
|
|
34
|
+
if (op.expectedHash === contentHash('')) {
|
|
35
|
+
const bytes = op.preUnitHash === undefined ? null : await sink.readBlob(op.preUnitHash);
|
|
36
|
+
if (bytes === null) {
|
|
37
|
+
refusals.push(`${op.skill}/${op.path === '' ? 'SKILL.md' : op.path}(单元前字节读不到:blob ${op.preUnitHash ?? '(missing)'})`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
prepared.push({ ...op, bytes, live: '', absent: true });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
28
43
|
refusals.push(`${op.skill}/${op.path === '' ? 'SKILL.md' : op.path}(盘上文件不存在)`);
|
|
29
44
|
continue;
|
|
30
45
|
}
|
|
@@ -33,7 +48,7 @@ export async function executeUnitUndo(plan, sink) {
|
|
|
33
48
|
continue;
|
|
34
49
|
}
|
|
35
50
|
if (op.remove) {
|
|
36
|
-
prepared.push({ ...op, bytes: '', live });
|
|
51
|
+
prepared.push({ ...op, bytes: '', live, absent: false });
|
|
37
52
|
continue;
|
|
38
53
|
}
|
|
39
54
|
const bytes = op.preUnitHash === undefined ? null : await sink.readBlob(op.preUnitHash);
|
|
@@ -41,7 +56,7 @@ export async function executeUnitUndo(plan, sink) {
|
|
|
41
56
|
refusals.push(`${op.skill}/${op.path === '' ? 'SKILL.md' : op.path}(单元前字节读不到:blob ${op.preUnitHash ?? '(missing)'})`);
|
|
42
57
|
continue;
|
|
43
58
|
}
|
|
44
|
-
prepared.push({ ...op, bytes, live });
|
|
59
|
+
prepared.push({ ...op, bytes, live, absent: false });
|
|
45
60
|
}
|
|
46
61
|
if (refusals.length > 0) {
|
|
47
62
|
return { ok: false, message: `整单元撤销未开始(零写)—— 预检未通过:${refusals.join(';')}。修好后可重试。`, reverted: [] };
|
|
@@ -59,7 +74,7 @@ export async function executeUnitUndo(plan, sink) {
|
|
|
59
74
|
for (const op of supportOps) {
|
|
60
75
|
const result = op.remove
|
|
61
76
|
? await sink.removeSupportFile(op.skill, op.path, { sha256: op.expectedHash })
|
|
62
|
-
: await sink.writeSupportFile(op.skill, op.path, op.bytes, { sha256: op.expectedHash });
|
|
77
|
+
: op.absent ? await sink.writeSupportFile(op.skill, op.path, op.bytes) : await sink.writeSupportFile(op.skill, op.path, op.bytes, { sha256: op.expectedHash });
|
|
63
78
|
if (!result.ok)
|
|
64
79
|
return { ok: false, message: `${op.skill}/${op.path}:${result.message} —— 撤销未完成(已回退:${done()})。`, reverted };
|
|
65
80
|
reverted.push(op.skill + '/' + op.path);
|
package/package.json
CHANGED
|
@@ -122,10 +122,10 @@
|
|
|
122
122
|
"id": "skill-tree",
|
|
123
123
|
"path": "<skillsRoot>/**",
|
|
124
124
|
"writer": "evolution-core/src/skill-store.ts",
|
|
125
|
-
"serializedBy": "
|
|
126
|
-
"marker": "
|
|
125
|
+
"serializedBy": "write-lock",
|
|
126
|
+
"marker": "fileMutationLock(",
|
|
127
127
|
"state": [],
|
|
128
|
-
"note": "The skill tree itself.
|
|
128
|
+
"note": "The skill tree itself. G1: ordinary byte writers take the root mutation lock (fileMutationLock) and still run their per-file read-modify-write through `transact`; the root lock is what keeps extract/restructure/update/patch/create from interleaving. Destructive movers refuse only when a support directory cannot be listed (fail-closed)."
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
131
|
"id": "activity-log",
|