The startup-selection flow leaves a fresh world (no Workspace) in the
locked view state, so every e2e scenario that types into the composer
now connects one first via the shared connectFreshWorkspace helper
(hero picker create-by-name dialog; the default 'workspace' name keeps
the session-header cwd assertions intact).
Golden refreshes carry the current composer chrome: the plan/model
control seats are empty until their owning plugins register (the seats
shipped without occupants on this branch), the sidebar shows the
connected workspace group pre-send, and the bash details material
renders Input/code/Output as separate nodes. The cancel scenario polls
the frozen-partial swap instead of counting synchronously — the abort
frame reaches the browser over SSE after the host settles.
Validate replay sidecars and cross-copy failure facts, make browser console tripwires and macOS temp paths deterministic, and wait for asynchronous TUI resume details. Keep the owning docs, translations, and generated catalog aligned.
Both ds-review-bot findings were real:
- markLlmAdapterFailure's carried-facts cross-check read error.code
directly; a foreign Error with a valid own failure payload but a
throwing code accessor would replace the original adapter error with
the accessor exception, breaking the error-identity guarantee. The
read now goes through foreignErrorCode(), which contains the trap and
falls back to the normalized snapshot (test: hostile code accessor
beside a valid failure payload -> original identity kept, UNKNOWN
facts).
- live-interactions' afterEach caught scaffold.close() into undefined,
silently disabling ReplayHandle.assertConsumed() — the fixture-drift
tripwire — and hiding cleanup defects. Teardown now runs every step,
collects failures, and rethrows (AggregateError when several).
Every spec now commits at least one golden and the interactive ones one
per distinct end-state (nine new .expected.md):
- live-interactions: cancel.expected.md (frozen partial + 已停止 marker),
error-auth.expected.md (the prompt bubble alone — the committed artifact
of the web-error-surface gap, the diff that flips when error rendering
lands), retry.expected.md (indistinguishable from a clean completion —
retries are deliberately invisible in the transcript).
- question-composer: answered.expected.md (the question resolved into its
tool round trip plus the final reply, takeover gone) beside the existing
waiting-state golden.
- steering: mid-steer.expected.md pins the accepted-but-INVISIBLE state
(the loop drains steering only at the step boundary, so no interjection
bubble exists while the question still blocks — if the client ever
renders pending steers eagerly, this golden flips first) and
settled.expected.md the badged bubble plus obeying reply.
- navigation-panes: waterfall.expected.md and details-open.expected.md
(tool-name header, Input args, Output result) beside the trajectory one.
- lifecycle-chrome: reloaded.expected.md — rendering the same settled
transcript from persistence alone IS the recovery claim.
Fixture inventories extended to the new closed sets; the Agent Note's
expected-outputs policy updated in both languages (per-end-state goldens
for interactive scenarios), pairing re-recorded.
Five browser e2e scenarios over the existing keyless lane, one recorded
base fixture per spec family:
- live-interactions: one tool-free recorded turn + per-run override
sidecars authored in the spec (content single-sourced from the fixture
via deriveReplayScript, minted into a spec-owned temp dir). Cancel uses
a hang patch with a readyFile marker — the marker proves the stream is
parked mid-turn before the Stop click, so mid-stream cancellation is
deterministic by construction (turn/end 'aborted', composer re-enabled).
AUTH pins the non-retryable path: turn/end 'error', zero llm/retry
events, composer recovers; FIXME(web-error-surface) marks the found
product gap (no error copy renders — the client consumes no agent/error
frames and a pre-chunk failure freezes no partial). SERVER retry appends
the fixture's own success after an injected throw and proves llm-retry
end-to-end in the browser via the durable llm/retry record.
- question-composer: the shipped ask_user_question takeover blocks the
turn mid-step on the real userInteraction seam; the test answers through
the composer (the one sanctioned model-content-reactive drive step: the
turn cannot complete without it) and the tool result carries the answer.
Adds the composer waiting-state aria golden.
- steering: steers mid-turn while the composer blocks the step (the
deterministic mid-turn window). The steer rides the real wire
(session.prompt mode:'steer' POSTed from the page; the locked composer
has no steering gesture yet — TODO(web-steer-composer)); downstream is
all product: gateway -> Agent.steer -> step-boundary drain -> durable
steering/message -> SSE -> badged interjection bubble. Record mode
rejects a fixture whose live reply ignored the steer.
Scaffold gains the replayOverride passthrough; specs register in both
tsconfig planes (client exclude, host include).