fix(sessions): attribute session startup progress by id before workspace

Startup progress resolved its target row by workspace path first and only
fell back to a known session id, which let one row be shown another row's
phase. While a create is pending in a workspace, an existing session in that
same workspace can also be opened -- by selecting another row, by another
tab, or by a subsession open -- and that open publishes the same cwd. The
cwd-first order rewrote such an event onto the pending create row, so a user
watching a session being created could be told a phase that belonged to a
different session. That is exactly the dishonest attribution this work set
out to avoid.

A known session id is the strongest available proof of the target, so it is
now checked first; workspace routing is used only when the id is unknown,
which is precisely the pre-session case it exists for. No wording changed and
no event changed; only which row an event is applied to.

Two tests were added where behavior was asserted but not proved. The
controller test fails against the previous order, so the misattribution is
now pinned. The service test covers a startup whose extension binding
rejects, proving the window still ends with an idle report rather than
leaving a waiting row labelled with a phase the service has left.
This commit is contained in:
Federico Jaramillo Martinez
2026-07-26 17:05:56 +02:00
parent 49e7c390f3
commit 67be9fb523
3 changed files with 65 additions and 17 deletions
@@ -187,6 +187,23 @@ describe("PiSessionService session startup progress", () => {
await service.dispose();
});
it("ends the startup window when extension binding fails, leaving no stale phase label", async () => {
const failure = new Error("extension refused to load");
const { hub, fake, service } = startupService();
fake.session.bindExtensions = () => Promise.reject(failure);
await expect(service.start("/workspace")).rejects.toBe(failure);
// The last word on this startup must not be an "active" phase the service is
// no longer inside; otherwise a waiting row keeps a label that is now false.
expect(startupText(hub)).toEqual([
"Creating session: Starting the Pi session",
"Creating session: Loading session extensions",
"idle",
]);
await service.dispose();
});
it("keeps startup reporting event-only, writing no session or workspace activity state", async () => {
const recorder = recordingWorkspaceActivity();
const failure = new Error("runtime unavailable");