These goldens were recorded on a machine with personal skills under ~/.dsh and
~/.agents, so every one of them pinned a context-injection row a clean runner
cannot produce — which is why the browser lane failed on CI and passed
locally. master's scaffold fix confines skill-local's host-level roots to the
temp world; re-recording against it removes the row and nothing else.
seeded-history keeps its row: that scenario seeds the event itself.
Merging master swapped the plain "▸ 上下文注入" button for the shared
DisclosureRow, which every conversation golden carrying an injected-context
row renders. The only change in all twelve files is that row.
The native adapter's route was named deepseek, colliding with pi-ai's
catalog provider of the same name, so the two DeepSeek paths could never
be mounted side by side. The web settings page needs both configurable at
once. Compositions, fixtures, goldens, scaffolding defaults, and docs all
move together (pre-release, no shim); TUI/session-query-spill/
missing-credential goldens re-recorded through their keyless refresh
modes because provider-name length shifts box padding and spill
truncation points.
Settled history now exposes user/assistant chrome (including date-aware
clocks) in the accessibility tree; collapse clocks via scaffold and update
keyless scenario goldens.
The assembled fixture app now takes over the composer while fx-alpha's
resident approval is pending — the feature under test, not chrome noise —
so the session-title snapshot answers it (允许一次) before asserting the
model seat, mirroring what a user sees. The eleven keyless ui.expected.md
goldens re-record (DSH_SNAPSHOT=refresh, no key) for the Access placeholder
→ projection chip swap (Danger Full Access under the roster default) plus
the master-side chrome drift the goldens had accumulated (copy/branch/edit
buttons, tab rows) since their last refresh.
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.
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).