Skip to content

Commit 8da70e7

Browse files
Keep idle cleanup apply from persisting snapshot-only registry edits
Why: - Confirmed cleanup planned from a read-only snapshot, then wrote that in-memory registry. Snapshot-only stale-lease grace restarts and cleared active-lease ids could land on disk while lease files remained. Changed: - After the confirmed plan matches, persist shutdown results onto a freshly loaded registry plus current simulator inventory. - Leave unreclaimed stale-lease files and their on-disk release timestamps unchanged until a mutating load reclaims them. Verification: - npm run agent:verify -- --profile implementation --paths broker-core/index.mjs broker-core/test/broker-core.test.mjs --session-dir task-sessions/20260817-pr1-review-loop-5 - passed Affected: - broker-core/index.mjs - broker-core/test/broker-core.test.mjs Refs: - #1 Session: - task-sessions/20260817-pr1-review-loop-5 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a2f4416 commit 8da70e7

2 files changed

Lines changed: 69 additions & 1 deletion

File tree

broker-core/index.mjs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4383,7 +4383,12 @@ export function cleanupIdleBroker(paths, options = {}) {
43834383
reasonCode: "idle-plan-stale",
43844384
});
43854385
}
4386-
const result = performIdleShutdowns(paths, state, plan.candidates, {
4386+
const persistState = {
4387+
...state,
4388+
registry: normalizeRegistry(readJsonIfExists(paths.registryPath), state.hostConfig, timestamp),
4389+
};
4390+
syncRegistryWithSimctl(persistState.hostConfig, persistState.registry, stateLoadOptions(options, timestamp));
4391+
const result = performIdleShutdowns(paths, persistState, plan.candidates, {
43874392
...options,
43884393
actorId,
43894394
actorType: "human",

broker-core/test/broker-core.test.mjs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5392,6 +5392,69 @@ test("idle cleanup apply does not contain stale leases excluded from the confirm
53925392
assert.equal(readJson(paths.simctl.statePath).devices.find((device) => device.udid === idleLease.simulatorId).state, "Shutdown");
53935393
});
53945394

5395+
test("idle cleanup apply does not persist snapshot-only stale-lease registry edits", () => {
5396+
const paths = makePaths();
5397+
writeBaseHostConfig(paths.hostConfigPath);
5398+
writeBaseProject(paths.projectFilePath);
5399+
const resolvedPaths = brokerPaths(paths);
5400+
5401+
initBroker(resolvedPaths, runtimeOptions(paths, { processExists: () => true }));
5402+
const staleLease = acquireLeaseBroker(resolvedPaths, {
5403+
actorId: "dead-agent",
5404+
actorType: "agent",
5405+
now: "2026-01-01T00:00:00.000Z",
5406+
ownerPid: 424242,
5407+
processExists: () => true,
5408+
purposeId: "agent-ui-session",
5409+
simctlAdapter: paths.simctl.adapter,
5410+
}).lease;
5411+
const idleLease = acquireLeaseBroker(resolvedPaths, {
5412+
actorId: "idle-agent",
5413+
actorType: "agent",
5414+
now: "2026-01-01T00:00:00.000Z",
5415+
ownerPid: process.pid,
5416+
processExists: () => true,
5417+
purposeId: "agent-ui-session",
5418+
simctlAdapter: paths.simctl.adapter,
5419+
}).lease;
5420+
releaseLeaseBroker(resolvedPaths, {
5421+
leaseId: idleLease.leaseId,
5422+
now: "2026-01-01T00:00:00.000Z",
5423+
processExists: () => true,
5424+
simctlAdapter: paths.simctl.adapter,
5425+
});
5426+
const registry = readJson(resolvedPaths.registryPath);
5427+
registry.aliases[staleLease.alias].driftReason = "test-unhealthy";
5428+
registry.aliases[staleLease.alias].health = "repair-needed";
5429+
writeJson(resolvedPaths.registryPath, registry);
5430+
const staleAliasBefore = readJson(resolvedPaths.registryPath).aliases[staleLease.alias];
5431+
5432+
const preview = cleanupIdleBroker(resolvedPaths, {
5433+
now: "2026-01-01T02:00:00.000Z",
5434+
processExists: () => false,
5435+
simctlAdapter: paths.simctl.adapter,
5436+
});
5437+
assert.equal(preview.eligibleCount, 1);
5438+
5439+
const result = cleanupIdleBroker(resolvedPaths, {
5440+
actorId: "operator",
5441+
actorType: "human",
5442+
apply: true,
5443+
confirmPlanId: preview.planId,
5444+
now: "2026-01-01T02:00:00.000Z",
5445+
processExists: () => false,
5446+
simctlAdapter: paths.simctl.adapter,
5447+
});
5448+
5449+
assert.equal(result.shutdownCount, 1);
5450+
const staleAliasAfter = readJson(resolvedPaths.registryPath).aliases[staleLease.alias];
5451+
assert.equal(staleAliasAfter.activeLeaseId, staleLease.leaseId);
5452+
assert.equal(staleAliasAfter.lastLeaseReleasedAt, staleAliasBefore.lastLeaseReleasedAt);
5453+
assert.equal(staleAliasAfter.health, "repair-needed");
5454+
assert.equal(fs.existsSync(path.join(resolvedPaths.leasesDir, `${staleLease.leaseId}.json`)), true);
5455+
assert.equal(readJson(paths.simctl.statePath).devices.find((device) => device.udid === idleLease.simulatorId).state, "Shutdown");
5456+
});
5457+
53955458
test("releasing a healthy lease is not blocked by unrelated stale containment failure", () => {
53965459
const paths = makePaths();
53975460
writeBaseHostConfig(paths.hostConfigPath);

0 commit comments

Comments
 (0)