|
| 1 | +# Reasoning-only no-auto-retry after write |
| 2 | + |
| 3 | +```yaml qa-scenario |
| 4 | +id: reasoning-only-no-auto-retry-after-write |
| 5 | +title: Reasoning-only no-auto-retry after write |
| 6 | +surface: runtime |
| 7 | +objective: Verify a GPT-style reasoning-only turn after a mutating write stays replay-unsafe and does not auto-retry. |
| 8 | +successCriteria: |
| 9 | + - Scenario is mock-openai only so live lanes do not pick it up implicitly. |
| 10 | + - The agent performs the seeded mutating write. |
| 11 | + - Mock trace does not include an automatic reasoning-only retry instruction. |
| 12 | + - Mock trace stops after the write-side reasoning-only terminal turn instead of attempting a continuation. |
| 13 | +docsRefs: |
| 14 | + - docs/help/testing.md |
| 15 | + - docs/help/gpt54-codex-agentic-parity.md |
| 16 | +codeRefs: |
| 17 | + - extensions/qa-lab/src/mock-openai-server.ts |
| 18 | + - src/agents/pi-embedded-runner/run/incomplete-turn.ts |
| 19 | +execution: |
| 20 | + kind: flow |
| 21 | + summary: Verify reasoning-only turns after a write do not auto-retry. |
| 22 | + config: |
| 23 | + requiredProvider: mock-openai |
| 24 | + promptSnippet: Reasoning-only after write safety check |
| 25 | + prompt: "Reasoning-only after write safety check: write reasoning-only-side-effect.txt, then answer with exactly SIDE-EFFECT-GUARD-OK." |
| 26 | + retryNeedle: recorded reasoning but did not produce a user-visible answer |
| 27 | + outputFile: reasoning-only-side-effect.txt |
| 28 | +``` |
| 29 | +
|
| 30 | +```yaml qa-flow |
| 31 | +steps: |
| 32 | + - name: keeps replay-unsafety explicit after a mutating write |
| 33 | + actions: |
| 34 | + - assert: |
| 35 | + expr: "env.providerMode === 'mock-openai'" |
| 36 | + message: this seeded scenario is mock-openai only |
| 37 | + - call: waitForGatewayHealthy |
| 38 | + args: |
| 39 | + - ref: env |
| 40 | + - 60000 |
| 41 | + - call: reset |
| 42 | + - set: requestCountBefore |
| 43 | + value: |
| 44 | + expr: "env.mock ? (await fetchJson(`${env.mock.baseUrl}/debug/requests`)).length : 0" |
| 45 | + - set: sessionKey |
| 46 | + value: |
| 47 | + expr: "`agent:qa:reasoning-only-write:${randomUUID().slice(0, 8)}`" |
| 48 | + - call: startAgentRun |
| 49 | + saveAs: started |
| 50 | + args: |
| 51 | + - ref: env |
| 52 | + - sessionKey: |
| 53 | + ref: sessionKey |
| 54 | + message: |
| 55 | + expr: config.prompt |
| 56 | + timeoutMs: |
| 57 | + expr: liveTurnTimeoutMs(env, 45000) |
| 58 | + - set: waited |
| 59 | + value: |
| 60 | + expr: "await env.gateway.call('agent.wait', { runId: started.runId, timeoutMs: liveTurnTimeoutMs(env, 45000) }, { timeoutMs: liveTurnTimeoutMs(env, 50000) })" |
| 61 | + - assert: |
| 62 | + expr: "waited?.status === 'ok'" |
| 63 | + message: |
| 64 | + expr: "`agent.wait returned ${String(waited?.status ?? 'unknown')}: ${String(waited?.error ?? '')}`" |
| 65 | + - call: fs.readFile |
| 66 | + saveAs: sideEffect |
| 67 | + args: |
| 68 | + - expr: "path.join(env.gateway.workspaceDir, config.outputFile)" |
| 69 | + - utf8 |
| 70 | + - assert: |
| 71 | + expr: "sideEffect.includes('side effects already happened')" |
| 72 | + message: |
| 73 | + expr: "`side-effect file missing expected contents: ${sideEffect}`" |
| 74 | + - if: |
| 75 | + expr: "Boolean(env.mock)" |
| 76 | + then: |
| 77 | + - set: scenarioRequests |
| 78 | + value: |
| 79 | + expr: "(await fetchJson(`${env.mock.baseUrl}/debug/requests`)).slice(requestCountBefore)" |
| 80 | + - assert: |
| 81 | + expr: "scenarioRequests.some((request) => String(request.allInputText ?? '').includes(config.promptSnippet) && request.plannedToolName === 'write')" |
| 82 | + message: expected mutating write request in mock trace |
| 83 | + - assert: |
| 84 | + expr: "!scenarioRequests.some((request) => String(request.allInputText ?? '').includes(config.retryNeedle))" |
| 85 | + message: reasoning-only retry instruction should not be injected after a write |
| 86 | + - assert: |
| 87 | + expr: "scenarioRequests.filter((request) => String(request.allInputText ?? '').includes(config.promptSnippet)).length === 2" |
| 88 | + message: expected exactly the write request plus the reasoning-only terminal request |
| 89 | + detailsExpr: "env.mock ? `requests=${String(scenarioRequests?.length ?? 0)} sideEffect=${sideEffect.trim()}` : sideEffect" |
| 90 | +``` |
0 commit comments