The `MALFORMED_TOOL_CALL` JSDoc claimed nothing durable is written, but the
loop appends an `assistant/chunk` for every yielded chunk; only the assistant
message and tool result are withheld. The bounded-recovery Note still listed
a five-code transient set, and neither Note linked the other.
A keyless `malformed-tool-call-retry` scenario now records the refusal, the
retry, and the absence of a `tool/call` for the failed attempt. A translator
case pins that an already closable block also withholds its `block-end`.
Adding `MALFORMED_TOOL_CALL` to the default retryable set changes two
expected outputs the session snapshot lane does not own: the shipped Web
composition's inline snapshot, and the canonical packed layout of the
refreshed `empty-response-retry` fixture.
A continuation SSE delta that repeats a tool call's `id` or `name` as an
empty string — or as `null`, which some OpenAI-compatible gateways send —
erased the identity established by the call's first delta. The assembled
block reached the loop with an empty name and failed as `unknown tool ""`,
and the empty `callId` persisted into `tool/result`, which the session
reader refuses on reopen.
`acceptIdentity` accepts only a non-empty string, so a repeated empty or
null field means "no update". A tool call still missing `id` or `name` at
`[DONE]` ends the response with the new retryable `MALFORMED_TOOL_CALL`
code instead of closing an unusable block.
The two styling notes name the surveyed product; the mechanism facts
(superellipse token, guard, full-round opt-out, stroke-in-shadow
elevation, 0.5px hairline) stand alone, so the notes now state them
without the product reference. Pairing records re-recorded.
The composer hairline moves one step up from the menus' l1; the seven
menu-fill dropdown cards grow from 16px to 20px corners. Notes and the
token comment record the new layering.
Re-declare the derived elevation tokens on body * so per-surface
--dsw-elevation-stroke-color rebinds reach the consuming shadow (custom
properties inherit with var() already substituted); pin that mechanism
and add synthetic rejection cases to the stylesheet scans; take the
ring-track basenames through node:path so the exemption matches on
Windows; update the ModelsSection row-card spec to the hairline recipe;
align the elevation note with the shipped l1 menu rebind, refresh the
feedback-popover note's surface recipe, and document the soft tier.
Apply global visual polish across the web client: corner-shape:
superellipse(1.5) with corner-shape: round pairing for full circles,
elevation tokens that draw 0.5px stroke outlines inside box-shadow for
floating surfaces, 0.5px hairline borders and divider lines for
neutral-token strokes, and tuned stroke contrast plus larger radii for
menus, settings panels, and cards. Stylesheet-scan specs in ui-theme
reject unpaired circles, border+shadow mixes, and 1px neutral hairlines
repo-wide.
Closes#3287