diff --git a/scripts/harness-version.json b/scripts/harness-version.json index 4a087d7c2..5dd942307 100644 --- a/scripts/harness-version.json +++ b/scripts/harness-version.json @@ -38,7 +38,7 @@ "test/helpers/pty/classify.ts": {"blob":"3f2daa12ff6484f96b8d28d910a50231c4bb3ae8","since":6}, "test/helpers/pty/fake-session.ts": {"blob":"554e064ef259b8444280eddfe9b0db93c66a8202","since":6}, "test/helpers/pty/judge.ts": {"blob":"81c5bb4c48ab928aca2f186a1ade9df7169d7fbe","since":6}, - "test/helpers/pty/launch.ts": {"blob":"d2e51418ce284815a26e174d777ca8b47ffd7e68","since":6,"nonBehavioral":"PTY sessions price their ledger row from the session transcript; the launcher pins a random --session-id (Claude otherwise picks its own random one) when the caller passes none and is not resuming; no prompt, answer, timeout or verdict changes"}, + "test/helpers/pty/launch.ts": {"blob":"11bbba2ed8425edd91a6214c05ecb32b33a00c41","since":6,"nonBehavioral":"the launcher also recognises --session-id= and --resume= forms before pinning a session id; no prompt, answer, timeout or verdict changes"}, "test/helpers/pty/plan-native.ts": {"blob":"ddba9cb68c7c0da935cff7c1cea258dee2dff740","since":6}, "test/helpers/pty/runners/counting.ts": {"blob":"8c5c05e54abf961259d34d9c3d77db67138d7794","since":6}, "test/helpers/pty/runners/floor.ts": {"blob":"54e2145832bb98913644d10ff303792d67f9362e","since":6,"nonBehavioral":"Removes the plan-design 0D focus picker from the shared harness; floor.ts gains an optional pickSetupChoice hook that no other caller passes, so no other case's input or verdict can change. plan-design-finding-floor, the only case whose seed raises that menu, passes the picker from its owned fixture test/fixtures/design-floor-focus-picker.ts, so its owned bytes start its own new series."}, diff --git a/test/helpers/claude-pty-runner.launch.unit.test.ts b/test/helpers/claude-pty-runner.launch.unit.test.ts index 5913435b6..413836faf 100644 --- a/test/helpers/claude-pty-runner.launch.unit.test.ts +++ b/test/helpers/claude-pty-runner.launch.unit.test.ts @@ -58,7 +58,9 @@ process.stdout.write(JSON.stringify({ capture: resolveEvalModel('capture'), fallback: await argv({}, undefined), env: await argv({}, 'env-model'), explicit: await argv({ model: 'opts-model' }, 'env-model'), extra: await argv({ extraArgs: ['--model', 'override'] }, undefined), pinned: await argv({ extraArgs: ['--session-id', '11111111-2222-4333-8444-555555555555'] }, undefined), - resumed: await argv({ extraArgs: ['--resume', 'prior'] }, undefined) })); + resumed: await argv({ extraArgs: ['--resume', 'prior'] }, undefined), + pinnedEq: await argv({ extraArgs: ['--session-id=11111111-2222-4333-8444-555555555555'] }, undefined), + resumedEq: await argv({ extraArgs: ['--resume=prior'] }, undefined) })); `); const run = (hermetic: string) => { const result = spawnSync(process.execPath, [worker], { cwd: join(import.meta.dir, '../..'), encoding: 'utf8', timeout: 30_000, @@ -81,6 +83,8 @@ process.stdout.write(JSON.stringify({ capture: resolveEvalModel('capture'), expect(sessionIds(hermetic.extra)).toHaveLength(1); expect(sessionIds(hermetic.pinned)).toEqual(['11111111-2222-4333-8444-555555555555']); expect(sessionIds(hermetic.resumed)).toEqual([]); + expect(hermetic.pinnedEq.filter((arg: string) => arg.startsWith('--session-id'))).toEqual(['--session-id=11111111-2222-4333-8444-555555555555']); + expect(sessionIds(hermetic.resumedEq)).toEqual([]); expect(sessionIds(run('0').fallback)).toEqual([]); } finally { rmSync(dir, { recursive: true, force: true }); } }, 60_000); diff --git a/test/helpers/pty/launch.ts b/test/helpers/pty/launch.ts index d2e51418c..11bbba2ed 100644 --- a/test/helpers/pty/launch.ts +++ b/test/helpers/pty/launch.ts @@ -290,9 +290,10 @@ function prepareLaunch(opts: ClaudePtyOptions): LaunchSetup { const hermetic = isHermeticEnabled(); if (hermetic) args.push('--strict-mcp-config'); const extra = opts.extraArgs ?? []; - const resumes = extra.some(arg => ['--resume', '-r', '--continue', '-c'].includes(arg)); - let sessionId = extra.includes('--session-id') ? extra[extra.indexOf('--session-id') + 1] : undefined; - if (hermetic && !sessionId && !resumes) args.push('--session-id', sessionId = randomUUID()); + const resumes = extra.some(arg => /^(--resume|--continue|-r|-c)(=|$)/.test(arg)); + const pinned = extra.findIndex(arg => /^--session-id(=|$)/.test(arg)); + let sessionId = pinned < 0 ? undefined : extra[pinned].startsWith('--session-id=') ? extra[pinned].slice('--session-id='.length) : extra[pinned + 1]; + if (hermetic && pinned < 0 && !resumes) args.push('--session-id', sessionId = randomUUID()); if (opts.extraArgs) args.push(...opts.extraArgs); // Hermetic by default (test/helpers/hermetic-env.ts): operator session