3.6 KiB
3.6 KiB
Claude Provider Invariants and Regression Test Plan
Goal
Lock down behavior for Claude provider stream handling without changing provider interface contracts.
Non-Negotiable Contract Invariants
-
I1Provider interface remains unchanged.stream(prompt) -> AsyncGenerator<AgentStreamEvent>stays the only provider surface for run streaming.- Reference:
packages/server/src/server/agent/agent-sdk-types.ts:337
-
I2One prompt submission produces exactly one terminal turn event.- Terminal set:
turn_completed | turn_failed | turn_canceled. - No ambiguous completion path.
- Terminal set:
-
I3Wait semantics remain status-based, not message-correlation-based.waitForFinishresolves on lifecycle outcomes (idle|error|permission|timeout).- References:
packages/server/src/server/session.ts:5366packages/server/src/shared/messages.ts:1523
-
I4App chat/input semantics remain status-transition-based.- Input processing and queue flush continue to rely on
runningtransitions andupdatedAtordering. - References:
packages/app/src/components/agent-input-area.tsx:222packages/app/src/components/agent-input-area.tsx:300packages/app/src/contexts/session-context.tsx:485
- Input processing and queue flush continue to rely on
-
I5New prompt must never be satisfied by stale pre-prompt assistant/result events.- Old queued events (including system-triggered activity) must not be misattributed as response to latest prompt.
Existing Coverage (Keep)
-
Wait semantics after immediate/rapid sends:
packages/server/src/server/daemon-e2e/wait-for-idle.e2e.test.ts:33
-
Send-while-running recovery under real providers:
packages/server/src/server/daemon-e2e/send-while-running-stuck.real.e2e.test.ts:18packages/server/src/server/daemon-e2e/send-while-running-stuck-claude.real.e2e.test.ts:18
-
Claude stale old-turn protection (interrupt-failure path):
packages/server/src/server/agent/providers/claude-agent.interrupt-restart-regression.test.ts:217
-
App stream head/tail flush on terminal events:
packages/app/src/types/stream-event.test.ts:115
Missing Coverage (Add)
M1: Task Notification Interleaving Regression (Claude Unit)
- Add mocked SDK sequence where a queued system/task-notification-related burst causes old assistant/result events to appear before new prompt output.
- Assert latest
session.stream(newPrompt)does not emit stale assistant text as its reply. - Expected:
I5enforced.
M2: Stale Result Preemption Regression (Claude Unit)
- Add mocked SDK sequence with old pending
resultavailable right after pushing a new prompt. - Assert provider ignores/contains stale terminal for previous activity and does not complete new prompt prematurely.
- Expected:
I2+I5enforced.
M3: Wait Final Snapshot Coherence (Daemon E2E/Integration)
- Add/strengthen test to ensure
waitForFinishnever returnsstatus: idlewhilefinal.status === running. - Remove need for retry loop workaround in stress scenarios.
- Current stress workaround location:
packages/server/src/server/daemon-e2e/ui-action-stress.real.e2e.test.ts:387
- Expected:
I3hard guarantee.
Implementation Notes (Provider-Internal Only)
- Turn events are valid internal lifecycle markers.
- Do not introduce user-message to assistant-message correlation as a new cross-layer contract.
- Do not modify app/CLI/public daemon wait interfaces.
- Keep all changes scoped to Claude provider internals and regression tests.
Definition of Done
- Invariants
I1-I5are unchanged and explicitly covered. - New tests for
M1-M3are added and passing. - Existing tests listed above remain green.
npm run typecheckpasses.