* feat(ui): add chat media player cards
* fix(ui): harden chat media attachment lifecycle
* fix(ui): finalize chat media fallbacks
* fix(ui): rely on video playback errors
* fix(ui): guard deferred media resume
* test(ui): align local media assertions with player cards
* docs(ui): note cross-origin download fallback
* fix(ui): keep player helpers module-private
* test(ui): wait for encoded video attachment
* test(ui): wait for hidden audio media element
* test(scripts): give oxlint shard-kill test headroom under CI load
* chore(scripts): end failing oxlint runs with a stable status line
A crashed run-oxlint wrapper printed only a stack trace, and a lint
invocation whose output was truncated (cmd | tail -N) read as success —
which recently let a wrapper crash (stale node_modules after a dep-adding
merge) masquerade as a clean lint. Route the CLI entry through a small
wrapper that converts crashes into exit 1 and ends every failing run
with '[oxlint] FAILED (exit N)' as the final line.
* chore(scripts): declare runOxlintCliEntry in the script declaration contract
check-guards verifies .d.mts contracts against .mjs exports and
check-test-types consumes them; the new entry export needed both the
declaration and explicit log-parameter annotations in the test.
* perf(ci): unblock parallel oxlint shards and gate the lint-lane i18n verify
check-lint is the slowest static lane (median 252s): the control-UI
i18n verify runs 86s unconditionally and the three oxlint shards run
serially (89s) because the parallel gate demands 48GB of memory — a
local-Mac threshold that Blacksmith's 32GB 16 vCPU class can never
meet. CI now parallelizes shards at 8+ cores / 24GB+, and the lint case
reuses the existing run_control_ui_i18n manifest gate so diffs that
cannot affect generated catalogs skip the verify while oxlint always
runs.
* chore(ci): print oxlint shard concurrency decision
The lint-lane validation run still executed shards serially on a 16
vCPU runner; one log line exposes the resolved concurrency and the
host resources the decision saw.
Rely on the lint wrapper to prepare extension package-boundary artifacts during pnpm check instead of invoking the same prep script again at the end.
Add a script regression so the duplicate check path does not return.