From 86bffff779e00bd12bce293936791c91166566f2 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 24 Jul 2026 15:18:11 +0200 Subject: [PATCH] fix: compare CaseRun launch identity semantically --- internal/harnessrl/harnessrl.test.ts | 21 ++++++++++++++++++++- internal/harnessrl/service.ts | 6 +++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/internal/harnessrl/harnessrl.test.ts b/internal/harnessrl/harnessrl.test.ts index d5da8f2b..769eb6f8 100644 --- a/internal/harnessrl/harnessrl.test.ts +++ b/internal/harnessrl/harnessrl.test.ts @@ -48,7 +48,7 @@ test("API restart preserves PostgreSQL-shaped registry read model", async () => test("Workbench launch context is frozen and conflicting run replay is rejected", async () => { const fixture = await softwareSmokeFixture(); - const registry = new MemoryRegistry(); + const registry = new JsonbMemoryRegistry(); const service = createHarnessRLService({ registry, temporal: new FakeTemporal(), cwd: fixture.root, caseRepo: fixture.root, stateRoot: fixture.stateRoot, caseAuthority: fixture.caseAuthority }); const launchContext = { workbenchSessionRef: { sessionId: "ses_caserun_arm2d", actorId: "usr_arm2d" }, @@ -376,3 +376,22 @@ class MemoryRegistry implements HarnessRLRegistry { async health() { return { ok: true as const }; } async close() {} } + +class JsonbMemoryRegistry extends MemoryRegistry { + async createRun(input: StartCaseRunInput) { + const record = await super.createRun(input); + if (!record.launchContext) return record; + const context = record.launchContext; + return { + ...record, + launchContext: { + hwpodId: context.hwpodId, + ...(context.subjectRef ? { subjectRef: context.subjectRef } : {}), + workbenchSessionRef: { + actorId: context.workbenchSessionRef.actorId, + sessionId: context.workbenchSessionRef.sessionId, + }, + }, + }; + } +} diff --git a/internal/harnessrl/service.ts b/internal/harnessrl/service.ts index 1f7e0a47..e7613e74 100644 --- a/internal/harnessrl/service.ts +++ b/internal/harnessrl/service.ts @@ -209,7 +209,11 @@ function requiredLaunchText(value: unknown, field: string) { } function sameLaunchContext(left: CaseRunLaunchContext | null, right: CaseRunLaunchContext | null) { - return JSON.stringify(left) === JSON.stringify(right); + if (left === null || right === null) return left === right; + return left.workbenchSessionRef.sessionId === right.workbenchSessionRef.sessionId + && left.workbenchSessionRef.actorId === right.workbenchSessionRef.actorId + && left.hwpodId === right.hwpodId + && (left.subjectRef ?? "") === (right.subjectRef ?? ""); } function finiteNumber(value: unknown): number | null {