From c3ff9c2159ca1c1e31c115ad0ef808a9c218b2f0 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 1 Sep 2026 20:44:37 +0800 Subject: [PATCH] Keep forgotten projects out of app snapshots Why: Retained project-scoped audit events could recreate an inactive project after project forget removed its registration, leaving a ghost card with no leases or pins. Changed: - Let recent events enrich only project summaries already seeded by current registration, active lease, or active pin state. - Extend the direct CLI forget regression through acquire and release while proving audit events remain visible. Verification: - node --test --test-name-pattern="project forget removes an inactive registration" client/test/simbroker.test.mjs (fails before fix; passes after fix) - npm run test:broker-core (294 pass) - npm run test:client (258 pass) - npm test (pass, including 172 macOS app tests) - npm run agent:verify -- --profile implementation --paths broker-core/index.mjs client/test/simbroker.test.mjs --session-dir task-sessions/project-forget-read-model-20260901.KOGb15 (pass) - npm run verify:public-surface (pass) - current-source public release audit (pass) - independent diff review (no P1/P2/P3 findings) Affected: Projects read-model summarization and the focused project-forget CLI regression. Event storage, retention, transport, host configuration, leases, pins, and simulator lifecycle are unchanged. Refs: https://github.com/fiveonecode/simulator-broker/issues/47 Session: task-sessions/project-forget-read-model-20260901.KOGb15 --- broker-core/index.mjs | 2 +- client/test/simbroker.test.mjs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/broker-core/index.mjs b/broker-core/index.mjs index 06b8940..049e133 100644 --- a/broker-core/index.mjs +++ b/broker-core/index.mjs @@ -3209,7 +3209,7 @@ function summarizeProjectRecords(activeLeases, pins, recentEvents, knownProjects } for (const event of recentEvents) { - const project = ensureProject(event.projectId ?? null, null, event.projectId ? (knownProjects.projects[event.projectId] ?? null) : null); + const project = event.projectId ? (byProject.get(event.projectId) ?? null) : null; if (!project) { continue; } diff --git a/client/test/simbroker.test.mjs b/client/test/simbroker.test.mjs index a71b10a..cdde374 100644 --- a/client/test/simbroker.test.mjs +++ b/client/test/simbroker.test.mjs @@ -2521,9 +2521,26 @@ test("project validate can discover the project file from --repo-root", () => { test("project forget removes an inactive registration and refreshes the local app snapshot", () => { const fixture = makeFixture(); + const leaseFilePath = path.join(fixture.root, "forgotten-project-lease.json"); assert.equal(runCli(fixture, "host", "init").status, 0); assert.equal(runCli(fixture, "project", "validate", "--repo-root", fixture.repoRoot).status, 0); + const acquired = runCli( + fixture, + "lease", + "acquire", + "--repo-root", + fixture.repoRoot, + "--purpose", + "agent-ui-session", + "--owner-pid", + String(process.pid), + "--lease-file", + leaseFilePath, + ); + assert.equal(acquired.status, 0, acquired.stderr); + const released = runCli(fixture, "lease", "release", "--lease-file", leaseFilePath); + assert.equal(released.status, 0, released.stderr); const forgotten = runCli(fixture, "project", "forget", "--project-id", "cli-demo"); assert.equal(forgotten.status, 0); @@ -2532,6 +2549,8 @@ test("project forget removes an inactive registration and refreshes the local ap assert.equal(Object.hasOwn(readJson(path.join(fixture.stateRoot, "known-projects.json")).projects, "cli-demo"), false); const snapshot = readJson(path.join(fixture.stateRoot, "app-snapshot.json")); assert.equal(snapshot.projects.some((project) => project.projectId === "cli-demo"), false); + assert.equal(snapshot.recentEvents.some((event) => event.type === "lease.acquired" && event.projectId === "cli-demo"), true); + assert.equal(snapshot.recentEvents.some((event) => event.type === "lease.released" && event.projectId === "cli-demo"), true); const repeated = runCli(fixture, "project", "forget", "--project-id", "cli-demo"); assert.equal(repeated.status, 0);