Protect ancestors of locked snapshots during pruning
Pruning could delete unlocked intermediate nodes between locked snapshots and the root, creating orphan branches. Now ancestors of all locked snapshots are added to the protected set in both regular and node capture paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1453,16 +1453,24 @@ async function captureSnapshot(label = "Auto") {
|
|||||||
try {
|
try {
|
||||||
await db_put(record);
|
await db_put(record);
|
||||||
if (branchingEnabled) {
|
if (branchingEnabled) {
|
||||||
// Compute protected IDs: ancestors of this capture + fork points
|
// Compute protected IDs: ancestors of this capture + fork points + ancestors of locked snapshots
|
||||||
const allRecs = await db_getAllForWorkflow(workflowKey);
|
const allRecs = await db_getAllForWorkflow(workflowKey);
|
||||||
const tempTree = buildSnapshotTree(allRecs);
|
const tempTree = buildSnapshotTree(allRecs);
|
||||||
const ancestors = getAncestorIds(record.id, tempTree.parentOf);
|
const protectedIds = getAncestorIds(record.id, tempTree.parentOf);
|
||||||
// Protect fork points (snapshots with >1 child)
|
// Protect fork points (snapshots with >1 child)
|
||||||
for (const [pid, children] of tempTree.childrenOf) {
|
for (const [pid, children] of tempTree.childrenOf) {
|
||||||
if (children.length > 1) ancestors.add(pid);
|
if (children.length > 1) protectedIds.add(pid);
|
||||||
}
|
}
|
||||||
ancestors.add(record.id); // protect the just-captured snapshot
|
// Protect ancestors of locked snapshots to prevent orphan branches
|
||||||
await pruneSnapshots(workflowKey, [...ancestors]);
|
for (const rec of allRecs) {
|
||||||
|
if (rec.locked) {
|
||||||
|
for (const aid of getAncestorIds(rec.id, tempTree.parentOf)) {
|
||||||
|
protectedIds.add(aid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protectedIds.add(record.id); // protect the just-captured snapshot
|
||||||
|
await pruneSnapshots(workflowKey, [...protectedIds]);
|
||||||
} else {
|
} else {
|
||||||
await pruneSnapshots(workflowKey);
|
await pruneSnapshots(workflowKey);
|
||||||
}
|
}
|
||||||
@@ -1525,13 +1533,20 @@ async function captureNodeSnapshot(label = "Node Trigger") {
|
|||||||
try {
|
try {
|
||||||
await db_put(record);
|
await db_put(record);
|
||||||
if (branchingEnabled) {
|
if (branchingEnabled) {
|
||||||
// Compute protected IDs: ancestors + fork points
|
// Compute protected IDs: ancestors + fork points + ancestors of locked snapshots
|
||||||
const allRecs = await db_getAllForWorkflow(workflowKey);
|
const allRecs = await db_getAllForWorkflow(workflowKey);
|
||||||
const tempTree = buildSnapshotTree(allRecs);
|
const tempTree = buildSnapshotTree(allRecs);
|
||||||
const protectedNodeIds = getAncestorIds(record.id, tempTree.parentOf);
|
const protectedNodeIds = getAncestorIds(record.id, tempTree.parentOf);
|
||||||
for (const [pid, children] of tempTree.childrenOf) {
|
for (const [pid, children] of tempTree.childrenOf) {
|
||||||
if (children.length > 1) protectedNodeIds.add(pid);
|
if (children.length > 1) protectedNodeIds.add(pid);
|
||||||
}
|
}
|
||||||
|
for (const rec of allRecs) {
|
||||||
|
if (rec.locked) {
|
||||||
|
for (const aid of getAncestorIds(rec.id, tempTree.parentOf)) {
|
||||||
|
protectedNodeIds.add(aid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
protectedNodeIds.add(record.id);
|
protectedNodeIds.add(record.id);
|
||||||
await pruneNodeSnapshots(workflowKey, [...protectedNodeIds]);
|
await pruneNodeSnapshots(workflowKey, [...protectedNodeIds]);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user