mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 22:58:42 -10:00
fix(restore): invalidate stale native identity snapshots
This commit is contained in:
1 parent
aa5e187982
commit
92a2b17dc9
2 files changed
+50
-7
No files matched your search
@@ -11,10 +11,10 @@ export interface CurrentStateRehydrateEligibility {
|
||||
* A crash snapshot remains trustworthy only while it names every occupant that
|
||||
* durable current state still identifies as active. Boot reconciliation turns
|
||||
* rows detached after abrupt substrate loss, so detached rows must participate:
|
||||
* a different session id is positive evidence that the snapshot belongs to an
|
||||
* older occupant. Ambiguous current state is also not permission to revive a
|
||||
* historical occupant; current-state capture preserves the ambiguity and
|
||||
* restore refuses it explicitly.
|
||||
* a different session id or native resume identity is positive evidence that
|
||||
* the snapshot belongs to an older occupant. Ambiguous current state is also
|
||||
* not permission to revive a historical occupant; current-state capture
|
||||
* preserves the ambiguity and restore refuses it explicitly.
|
||||
*/
|
||||
export function snapshotMatchesCurrentOccupants(
|
||||
db: Database.Database,
|
||||
@@ -23,11 +23,18 @@ export function snapshotMatchesCurrentOccupants(
|
||||
): boolean {
|
||||
if (rig.nodes.length === 0) return true;
|
||||
const rows = db.prepare(
|
||||
`SELECT s.id, s.node_id AS nodeId, s.status
|
||||
`SELECT s.id, s.node_id AS nodeId, s.status,
|
||||
s.resume_type AS resumeType, s.resume_token AS resumeToken
|
||||
FROM sessions s
|
||||
JOIN nodes n ON n.id = s.node_id
|
||||
WHERE n.rig_id = ? AND s.status NOT IN ('superseded', 'exited')`
|
||||
).all(rig.rig.id) as Array<{ id: string; nodeId: string; status: string }>;
|
||||
).all(rig.rig.id) as Array<{
|
||||
id: string;
|
||||
nodeId: string;
|
||||
status: string;
|
||||
resumeType: string | null;
|
||||
resumeToken: string | null;
|
||||
}>;
|
||||
|
||||
for (const node of rig.nodes) {
|
||||
const current = resolveActiveOccupantRow(rows, undefined, node.id);
|
||||
@@ -38,7 +45,12 @@ export function snapshotMatchesCurrentOccupants(
|
||||
snapshot.data.activeSessionIdByNode,
|
||||
node.id,
|
||||
);
|
||||
if (captured.kind !== "resolved" || captured.session.id !== current.session.id) {
|
||||
if (
|
||||
captured.kind !== "resolved"
|
||||
|| captured.session.id !== current.session.id
|
||||
|| (captured.session.resumeType ?? null) !== current.session.resumeType
|
||||
|| (captured.session.resumeToken ?? null) !== current.session.resumeToken
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,37 @@ describe("Up API route", () => {
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("plans from current state when the same occupant row has a newer native resume identity", async () => {
|
||||
const rig = rigRepo.createRig("stale-native-identity-rig");
|
||||
const node = rigRepo.addNode(rig.id, "dev.impl", { runtime: "claude-code" });
|
||||
insertStartupContextRow(db, node.id);
|
||||
|
||||
const session = sessionRegistry.registerSession(node.id, "dev-impl@stale-native-identity-rig");
|
||||
db.prepare("UPDATE sessions SET resume_type = ?, resume_token = ?, status = ? WHERE id = ?")
|
||||
.run("claude_id", "11111111-1111-4111-8111-111111111111", "running", session.id);
|
||||
snapshotCapture.captureSnapshot(rig.id, "auto-periodic");
|
||||
|
||||
db.prepare("UPDATE sessions SET resume_type = ?, resume_token = ?, status = ? WHERE id = ?")
|
||||
.run("claude_id", "22222222-2222-4222-8222-222222222222", "detached", session.id);
|
||||
|
||||
const res = await app.request("/api/up", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ sourceRef: "stale-native-identity-rig", plan: true }),
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.snapshot).toBeNull();
|
||||
expect(body.wouldCaptureCurrentState).toBe(true);
|
||||
expect(body.nodes).toEqual([
|
||||
expect.objectContaining({
|
||||
logicalId: "dev.impl",
|
||||
intendedAction: "resume-original",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// OPR.0.3.4.9 — Option Y: auto-periodic co-equal with auto-pre-down in restore selection.
|
||||
|
||||
Reference in new issue
Block a user