Composer recording: port the sveltebits Voice Pill (icons, waveform, timer); Stop attaches directly, no 'Keep' step #617

Open
opened 2026-10-01 10:09:47 +00:00 by kayg · 32 comments
Owner

Owner report (2026-10-01, composer voice recording)

"These two [Stop / Discard] could be icons. And once stopped, why does it appear as Keep? Aren't we using the sveltebits.xyz component? https://sveltebits.xyz/micro/voice-pill looks way better than what we have."
Today: apps/web/src/lib/composer/Composer.svelte ~1819: while recording, text buttons "Stop" and "Discard". After Stop, a review state shows "Keep" and "Discard" (Keep attaches the recording). These are plain outlined text pills, not the design system.
Build (CLAUDE.md: never invent basic UI; port/cherry-pick sveltebits first):

  • Port the sveltebits Voice Pill (/micro/voice-pill; get the real source from the sveltebits repo/package, check its licence is AGPL-compatible, and record the source commit in a header comment as with other ports) into packages/ui as the shared recording pill: live waveform, elapsed timer, icon buttons (stop ■, cancel ✕) with warm tooltips + shortcuts, and the springy morph from the record button into the pill and back.
  • No "Keep" step: Stop saves and attaches the recording right away (opinionated default), with an Undo toast (8 s, #539). Cancel (✕) discards it with a confirm only if it is longer than ~10 s. The Voice Pill's own review/playback can stay only if it is an icon-only inline control (play ▶ / delete) after attaching.
  • Reuse the pill anywhere audio is recorded (composer, Notes, future dictation #488). Use tokens only; keyboard animates like pointer (#611); reduced motion.
    Test: an e2e records 2 s (fake media stream), stops, and the attachment chip appears on the entry; Undo removes it; ✕ discards it. Screenshots of the recording and attached states in both themes at 390/1440, plus a frame strip of the morph.
## Owner report (2026-10-01, composer voice recording) "These two [Stop / Discard] could be icons. And once stopped, why does it appear as Keep? Aren't we using the sveltebits.xyz component? https://sveltebits.xyz/micro/voice-pill looks way better than what we have." **Today:** `apps/web/src/lib/composer/Composer.svelte` ~1819: while recording, text buttons "Stop" and "Discard". After Stop, a review state shows "Keep" and "Discard" (Keep attaches the recording). These are plain outlined text pills, not the design system. **Build (CLAUDE.md: never invent basic UI; port/cherry-pick sveltebits first):** - **Port the sveltebits Voice Pill** (`/micro/voice-pill`; get the real source from the sveltebits repo/package, check its licence is AGPL-compatible, and record the source commit in a header comment as with other ports) into `packages/ui` as the shared recording pill: live waveform, elapsed timer, **icon** buttons (stop ■, cancel ✕) with warm tooltips + shortcuts, and the springy morph from the record button into the pill and back. - **No "Keep" step:** Stop **saves and attaches** the recording right away (opinionated default), with an Undo toast (8 s, #539). Cancel (✕) discards it with a confirm only if it is longer than ~10 s. The Voice Pill's own review/playback can stay only if it is an icon-only inline control (play ▶ / delete) after attaching. - Reuse the pill anywhere audio is recorded (composer, Notes, future dictation #488). Use tokens only; keyboard animates like pointer (#611); reduced motion. **Test:** an e2e records 2 s (fake media stream), stops, and the attachment chip appears on the entry; Undo removes it; ✕ discards it. Screenshots of the recording and attached states in both themes at 390/1440, plus a frame strip of the morph.
Author
Owner

Starting #617 on branch job/voicepill-617, based at 9a6c38c3b7c8bbbdad9919cd2419f773d9c34b6e. I have read CLAUDE.md, CONTEXT.md and docs/DESIGN.md. I am checking the recording flow, shared UI primitives, and upstream source/licence before implementation.

Starting #617 on branch `job/voicepill-617`, based at `9a6c38c3b7c8bbbdad9919cd2419f773d9c34b6e`. I have read CLAUDE.md, CONTEXT.md and docs/DESIGN.md. I am checking the recording flow, shared UI primitives, and upstream source/licence before implementation.
Author
Owner

Finding: the upstream VoicePill source is DavidHDev/svelte-bits at component commit 8fa93ce9a919dd93678f5ac03e752ee90ca5fead. The repository's LICENSE.md is MIT + Commons Clause; the Commons Clause restricts sale, sublicensing, or redistribution of components as a port. That does not meet this repo's AGPL-compatible dependency rule, so I will not copy or port the source. There is no matching Voice Pill in calternal.js. I will independently implement the requested shared pill behavior with calternal tokens and existing UI primitives, and record the upstream source commit and license finding in its module header.

Finding: the upstream VoicePill source is `DavidHDev/svelte-bits` at component commit `8fa93ce9a919dd93678f5ac03e752ee90ca5fead`. The repository's `LICENSE.md` is MIT + Commons Clause; the Commons Clause restricts sale, sublicensing, or redistribution of components as a port. That does not meet this repo's AGPL-compatible dependency rule, so I will not copy or port the source. There is no matching Voice Pill in calternal.js. I will independently implement the requested shared pill behavior with calternal tokens and existing UI primitives, and record the upstream source commit and license finding in its module header.
Author
Owner

Finding for #617: AttachmentTray.add() can refuse an attachment at the existing 32-item or 2 GiB limit. The new Stop path reset the recorder even after a refusal, which discarded the only in-memory copy instead of attaching it. I am changing the error path to retain the recording and expose retry/discard controls; I will add regression coverage.

Finding for #617: `AttachmentTray.add()` can refuse an attachment at the existing 32-item or 2 GiB limit. The new Stop path reset the recorder even after a refusal, which discarded the only in-memory copy instead of attaching it. I am changing the error path to retain the recording and expose retry/discard controls; I will add regression coverage.
Author
Owner

Finding for #617: the production e2e showed two Undo actions after saving a Note within the 8-second VoicePill window. Sending clears the Composer tray, but the VoicePill toast stayed visible and its callback then targeted a missing tray item. I am tying each toast to its attachment and dismissing it when that item is removed, moved, or committed.

Finding for #617: the production e2e showed two Undo actions after saving a Note within the 8-second VoicePill window. Sending clears the Composer tray, but the VoicePill toast stayed visible and its callback then targeted a missing tray item. I am tying each toast to its attachment and dismissing it when that item is removed, moved, or committed.
Author
Owner

Production Composer e2e after the required origin/dev merge measured the shared footer at mode 30 px, tools 36 px, and Send 30 px. The new VoicePill shell used the larger --hit token for its painted height while PillGroup segments use --control-h-compact, so the toolbar grew. I am aligning the shell to the compact control token and keeping the child buttons' touch hit areas separate.

Production Composer e2e after the required `origin/dev` merge measured the shared footer at mode 30 px, tools 36 px, and Send 30 px. The new VoicePill shell used the larger `--hit` token for its painted height while PillGroup segments use `--control-h-compact`, so the toolbar grew. I am aligning the shell to the compact control token and keeping the child buttons' touch hit areas separate.
Author
Owner

The full production Composer e2e now passes the footer height assertion after the VoicePill sizing fix. It then writes the Log entry successfully, clicks the saved row, and times out after 30 seconds waiting for the existing Log entry dialog (apps/web/e2e/composer.mjs:1025). CSP reports remain zero. I have left the expectation unchanged and am running the focused VoicePill proof separately.

The full production Composer e2e now passes the footer height assertion after the VoicePill sizing fix. It then writes the Log entry successfully, clicks the saved row, and times out after 30 seconds waiting for the existing `Log entry` dialog (`apps/web/e2e/composer.mjs:1025`). CSP reports remain zero. I have left the expectation unchanged and am running the focused VoicePill proof separately.
Author
Owner

One-time adversarial round evidence after the origin/dev merge:

  • search_chaos.py reported admin staged rebuild: expected (200,), got HTTP 202 with body {"job_id":"974a28f1-5780-4cb9-8d0d-c040c3a277fe","inserted":true}. I kept the existing probe expectation unchanged.
  • calendar_event_tags.mjs, mail_api.mjs, and money_api.mjs stopped before sending requests because the runner provided ADVERSARIAL_SERVER_BIN, while apps/web/e2e/harness.mjs checks CALTERNAL_SERVER_BIN or target/debug/calternal-server. These are harness wiring failures, not endpoint responses.

The remaining adversarial sections are still running in the single 15-minute round.

One-time adversarial round evidence after the `origin/dev` merge: - `search_chaos.py` reported `admin staged rebuild: expected (200,), got HTTP 202` with body `{"job_id":"974a28f1-5780-4cb9-8d0d-c040c3a277fe","inserted":true}`. I kept the existing probe expectation unchanged. - `calendar_event_tags.mjs`, `mail_api.mjs`, and `money_api.mjs` stopped before sending requests because the runner provided `ADVERSARIAL_SERVER_BIN`, while `apps/web/e2e/harness.mjs` checks `CALTERNAL_SERVER_BIN` or `target/debug/calternal-server`. These are harness wiring failures, not endpoint responses. The remaining adversarial sections are still running in the single 15-minute round.
Author
Owner

Additional output from the same adversarial round:

  • xuser_matrix.py could not create a fixture Folder (HTTP -1, timed out) while the Search chaos probe was active. Treat this as SLOW until a quiet rerun is authorized.
  • authz_matrix.py stopped while creating the Admin denial App Password fixture: api_read returned 403. The probe did not print a response body, so this is a fixture/setup finding, not evidence of an authorization bypass.

I have not changed the probe expectations or production code. The single round is still running.

Additional output from the same adversarial round: - `xuser_matrix.py` could not create a fixture Folder (`HTTP -1`, timed out) while the Search chaos probe was active. Treat this as SLOW until a quiet rerun is authorized. - `authz_matrix.py` stopped while creating the Admin denial App Password fixture: `api_read` returned 403. The probe did not print a response body, so this is a fixture/setup finding, not evidence of an authorization bypass. I have not changed the probe expectations or production code. The single round is still running.
Author
Owner

Finished: VoicePill (#617)

Branch: job/voicepill-617
Head: e5d42e3982e942944bdbb8acc2dd5134711f9d17 (includes the required merge of origin/dev at c4a61e8cf090170f35b1bed3350d9de20c83ecd5)

Built

  • Added a reusable packages/ui VoicePill with live waveform and timer, icon-only Stop and Discard controls, warm tooltips and Enter hints, shared-token morphs, reduced-motion behavior, and accessible states. Stop attaches immediately and shows an 8-second Undo. Discard confirms only after ten seconds.
  • Wired the pill into the Composer, including retry/discard when attachment capacity refuses a memo. Undo is tied to its attachment and tray and expires when that item leaves the tray or the draft commits.
  • Added recorder and component coverage, a real-server E2E for record/attach/Undo/discard, responsive theme captures, a production morph strip, and a VoicePill performance profile.
  • Updated DESIGN §38 and the shared keyboard motion contract. Keyboard motion follows the 2026-10-01 owner override; reduced motion still applies.

Screenshot evidence

Recording and attached states are attached for each viewport and theme. The extra tablet captures are included alongside the requested phone and desktop widths.

Viewport Light Dark
390 × 844 recording · attached recording · attached
820 × 1024 recording · attached recording · attached
1440 × 900 recording · attached recording · attached

Morph frame strip

Gates and E2E

cargo fmt --check exited 0 with no output. The merged server API gates passed:

$ CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo clippy -p calternal-server --all-targets -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 7m 16s
$ RUSTC_WRAPPER= CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo test -p calternal-server

test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 56.43s

The first cargo test attempt stopped before tests because the shared sccache daemon referenced a removed temp directory from another worktree. The command above passed after bypassing that daemon for this run.

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings
$ vitest run

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web

 Test Files  154 passed (154)
      Tests  1058 passed (1058)
   Start at  06:30:29
   Duration  122.32s (transform 50%, import 17%, environment 17%, tests 12%, setup 4%)

Environment  |component| jsdom was created 49 times · 108.30s total, 28% of tracked time
             create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files)
             learn more at https://vitest.dev/guide/improving-performance#test-environments

bun run build exited 0 (✓ built in 39.62s). The focused voice E2E passed: composer voice pill e2e: record, attach, Undo, discard, Note save and retained-file retry passed; CSP reports were 0 across 7 pages. The keyboard-motion and Settings shortcut E2Es also passed.

Known E2E gap: the full bun run test:e2e:composer flow writes its Log entry, then times out after 30 seconds waiting for the existing Log entry preview dialog at apps/web/e2e/composer.mjs:1025. Its existing expectation was kept. The focused VoicePill flow passes.

Adversarial round

One 15-minute round ran against the real local server and stopped at its time limit (exit 124), so the suite did not complete. The classification gates passed (335 cross-user operations, 945 generated-entry tools, 39 admin operations). Findings already recorded here: Search staged rebuild returned HTTP 202 where the existing probe expects 200; three route-specific probes did not start because the harness expects CALTERNAL_SERVER_BIN or target/debug/calternal-server, while the runner provided ADVERSARIAL_SERVER_BIN; one Folder fixture timed out during the Search storm (SLOW); the app-password api_read fixture received the expected authorization denial (403); and the 3 MiB Appearance proxy probe reported that its local server was unavailable. No expectation was changed. The round found no crash or authorization bypass.

Performance

The profile is in docs/perf/runs/2026-10-02T032343Z-551d7b33-voicepill-617.json; docs/perf/baseline.json has no earlier VoicePill profile. The perf VM was unreachable, so this is a local result under high host load (19.93 to 11.48 load average).

  • 390 px: average p50/p95 580.35/792.39 ms, CPU 98.8%, RSS 657,883,136 B. Full-tray 50-action burst p50/p95 467.05/582.77 ms, CPU 118.1%, peak RSS 688,771,072 B.
  • 1440 px: average p50/p95 455.15/634.50 ms, CPU 114.8%, RSS 666,718,208 B. Full-tray burst p50/p95 461.02/697.31 ms, CPU 116.5%, peak RSS 704,540,672 B.

Decisions

  • The referenced sveltebits source commit (8fa93ce9a919dd93678f5ac03e752ee90ca5fead) has an MIT + Commons Clause licence, which is incompatible with this repository's AGPL-3.0-only rule. The shared pill is therefore an independent implementation using calternal UI primitives and tokens; the source and decision are documented in its header.
  • When the attachment tray is full, Stop preserves the completed memo in the pill for retry or discard. This avoids losing a recording when attachment is refused.
  • Undo is scoped to the exact tray item and ends if that item is removed, moved, or committed.
  • The shared component is exported for future audio surfaces; Composer is the current recorder surface.

Main files

packages/ui/src/components/VoicePill.svelte, packages/ui/src/index.ts, apps/web/src/lib/composer/Composer.svelte, apps/web/src/lib/composer/recorder.svelte.ts, apps/web/src/lib/composer/attachments.svelte.ts, recorder/component tests, apps/web/e2e/composer.mjs, docs/DESIGN.md, bench/voicepill-617.mjs, bench/run.sh, and docs/perf/.

## Finished: VoicePill (#617) **Branch:** `job/voicepill-617` **Head:** `e5d42e3982e942944bdbb8acc2dd5134711f9d17` (includes the required merge of `origin/dev` at `c4a61e8cf090170f35b1bed3350d9de20c83ecd5`) ### Built - Added a reusable `packages/ui` VoicePill with live waveform and timer, icon-only Stop and Discard controls, warm tooltips and Enter hints, shared-token morphs, reduced-motion behavior, and accessible states. Stop attaches immediately and shows an 8-second Undo. Discard confirms only after ten seconds. - Wired the pill into the Composer, including retry/discard when attachment capacity refuses a memo. Undo is tied to its attachment and tray and expires when that item leaves the tray or the draft commits. - Added recorder and component coverage, a real-server E2E for record/attach/Undo/discard, responsive theme captures, a production morph strip, and a VoicePill performance profile. - Updated DESIGN §38 and the shared keyboard motion contract. Keyboard motion follows the 2026-10-01 owner override; reduced motion still applies. ### Screenshot evidence Recording and attached states are attached for each viewport and theme. The extra tablet captures are included alongside the requested phone and desktop widths. | Viewport | Light | Dark | |---|---|---| | 390 × 844 | [recording](https://git.kayg.org/attachments/e5014ed8-a8e6-44aa-a702-c27962cd75c0) · [attached](https://git.kayg.org/attachments/8a3742fa-c650-4766-b6e7-4760d52a1e14) | [recording](https://git.kayg.org/attachments/6cfb3967-f158-4771-bac7-1f59c2212ddb) · [attached](https://git.kayg.org/attachments/8cb0f28c-7039-48ae-9248-d3707e0d8eb1) | | 820 × 1024 | [recording](https://git.kayg.org/attachments/86e4f659-2f21-4740-9bb2-dc60cef8b4f2) · [attached](https://git.kayg.org/attachments/29b433e6-6e1c-48d4-8d48-df8095cbe8cd) | [recording](https://git.kayg.org/attachments/843d3a27-19b6-491b-91bb-cb2b8999741e) · [attached](https://git.kayg.org/attachments/eec57b1c-b4db-4c15-a256-768c0e619dce) | | 1440 × 900 | [recording](https://git.kayg.org/attachments/46c656e3-a3cf-4833-904b-329f9381fe9e) · [attached](https://git.kayg.org/attachments/81c89bf5-796d-4332-9f3e-ffed60027d74) | [recording](https://git.kayg.org/attachments/09ab257b-3cae-49f8-8138-7bf98866cfdd) · [attached](https://git.kayg.org/attachments/123545a5-49b4-42d2-8761-127e3a89699e) | [Morph frame strip](https://git.kayg.org/attachments/c898f8c7-f07d-49ec-b560-aadd82b1ac66) ### Gates and E2E `cargo fmt --check` exited 0 with no output. The merged server API gates passed: ```text $ CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo clippy -p calternal-server --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 7m 16s ``` ```text $ RUSTC_WRAPPER= CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR="$PWD/target/tmp" cargo test -p calternal-server test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 56.43s ``` The first `cargo test` attempt stopped before tests because the shared `sccache` daemon referenced a removed temp directory from another worktree. The command above passed after bypassing that daemon for this run. ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` ```text $ vitest run RUN v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Test Files 154 passed (154) Tests 1058 passed (1058) Start at 06:30:29 Duration 122.32s (transform 50%, import 17%, environment 17%, tests 12%, setup 4%) Environment |component| jsdom was created 49 times · 108.30s total, 28% of tracked time create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files) learn more at https://vitest.dev/guide/improving-performance#test-environments ``` `bun run build` exited 0 (`✓ built in 39.62s`). The focused voice E2E passed: `composer voice pill e2e: record, attach, Undo, discard, Note save and retained-file retry passed`; CSP reports were 0 across 7 pages. The keyboard-motion and Settings shortcut E2Es also passed. **Known E2E gap:** the full `bun run test:e2e:composer` flow writes its Log entry, then times out after 30 seconds waiting for the existing `Log entry` preview dialog at `apps/web/e2e/composer.mjs:1025`. Its existing expectation was kept. The focused VoicePill flow passes. ### Adversarial round One 15-minute round ran against the real local server and stopped at its time limit (`exit 124`), so the suite did not complete. The classification gates passed (335 cross-user operations, 945 generated-entry tools, 39 admin operations). Findings already recorded here: Search staged rebuild returned HTTP 202 where the existing probe expects 200; three route-specific probes did not start because the harness expects `CALTERNAL_SERVER_BIN` or `target/debug/calternal-server`, while the runner provided `ADVERSARIAL_SERVER_BIN`; one Folder fixture timed out during the Search storm (SLOW); the app-password `api_read` fixture received the expected authorization denial (403); and the 3 MiB Appearance proxy probe reported that its local server was unavailable. No expectation was changed. The round found no crash or authorization bypass. ### Performance The profile is in `docs/perf/runs/2026-10-02T032343Z-551d7b33-voicepill-617.json`; `docs/perf/baseline.json` has no earlier VoicePill profile. The perf VM was unreachable, so this is a local result under high host load (19.93 to 11.48 load average). - **390 px:** average p50/p95 580.35/792.39 ms, CPU 98.8%, RSS 657,883,136 B. Full-tray 50-action burst p50/p95 467.05/582.77 ms, CPU 118.1%, peak RSS 688,771,072 B. - **1440 px:** average p50/p95 455.15/634.50 ms, CPU 114.8%, RSS 666,718,208 B. Full-tray burst p50/p95 461.02/697.31 ms, CPU 116.5%, peak RSS 704,540,672 B. ### Decisions - The referenced sveltebits source commit (`8fa93ce9a919dd93678f5ac03e752ee90ca5fead`) has an MIT + Commons Clause licence, which is incompatible with this repository's AGPL-3.0-only rule. The shared pill is therefore an independent implementation using calternal UI primitives and tokens; the source and decision are documented in its header. - When the attachment tray is full, Stop preserves the completed memo in the pill for retry or discard. This avoids losing a recording when attachment is refused. - Undo is scoped to the exact tray item and ends if that item is removed, moved, or committed. - The shared component is exported for future audio surfaces; Composer is the current recorder surface. ### Main files `packages/ui/src/components/VoicePill.svelte`, `packages/ui/src/index.ts`, `apps/web/src/lib/composer/Composer.svelte`, `apps/web/src/lib/composer/recorder.svelte.ts`, `apps/web/src/lib/composer/attachments.svelte.ts`, recorder/component tests, `apps/web/e2e/composer.mjs`, `docs/DESIGN.md`, `bench/voicepill-617.mjs`, `bench/run.sh`, and `docs/perf/`.
Author
Owner

Starting round 2 on job/voicepill-617 at e5d42e3982e942944bdbb8acc2dd5134711f9d17, based on origin/dev c4a61e8cf090170f35b1bed3350d9de20c83ecd5. I am tracing the #622 recording display-name helper and shared waveform/focus tokens before changing the VoicePill, then I will verify the real app at 390/820/1440 in both themes with a macOS platform profile.

Starting round 2 on `job/voicepill-617` at `e5d42e3982e942944bdbb8acc2dd5134711f9d17`, based on `origin/dev` `c4a61e8cf090170f35b1bed3350d9de20c83ecd5`. I am tracing the #622 recording display-name helper and shared waveform/focus tokens before changing the VoicePill, then I will verify the real app at 390/820/1440 in both themes with a macOS platform profile.
Author
Owner

Finding: the Composer chip uses the stored recorder filename (Voice memo YYYY-MM-DD HH.mm.webm), while #622's shared Calendar helper displays the same recording as Voice Memo, HH:MM. The helper is present on job/calcard-series but is not in origin/dev. A regression test reproduced the mismatch for both the original name and a Files collision prefix. I reused the shared audioDisplayName behavior in packages/ui and the Composer display, while keeping the stored filename unchanged for upload paths and Note links.

Finding: the Composer chip uses the stored recorder filename (`Voice memo YYYY-MM-DD HH.mm.webm`), while #622's shared Calendar helper displays the same recording as `Voice Memo, HH:MM`. The helper is present on `job/calcard-series` but is not in `origin/dev`. A regression test reproduced the mismatch for both the original name and a Files collision prefix. I reused the shared `audioDisplayName` behavior in `packages/ui` and the Composer display, while keeping the stored filename unchanged for upload paths and Note links.
Author
Owner

Round 2 environment finding: production screenshot capture could not start on this host. COMPOSER_VOICEPILL_ONLY=1 ... bun e2e/composer.mjs exited in mediaSandboxEnv() before launching the server or browser. tests/adversarial/prepare-media-runtime.sh reported too many local threads for the bounded media sandbox test: 6754; its configured limit is 4090. The per-job cargo build -p calternal-server also remained active for nearly an hour without producing a binary amid concurrent builds, so I stopped it and tried the fresh server binary from a current sibling job. The same preflight failure prevented screenshot capture. The branch is unchanged by these failed attempts. I am running the web gates and will report the missing screenshot evidence as a gap.

Round 2 environment finding: production screenshot capture could not start on this host. `COMPOSER_VOICEPILL_ONLY=1 ... bun e2e/composer.mjs` exited in `mediaSandboxEnv()` before launching the server or browser. `tests/adversarial/prepare-media-runtime.sh` reported `too many local threads for the bounded media sandbox test: 6754`; its configured limit is 4090. The per-job `cargo build -p calternal-server` also remained active for nearly an hour without producing a binary amid concurrent builds, so I stopped it and tried the fresh server binary from a current sibling job. The same preflight failure prevented screenshot capture. The branch is unchanged by these failed attempts. I am running the web gates and will report the missing screenshot evidence as a gap.
Author
Owner

Round 2 screenshot attempt: after reusing the worktree's staged media runtime and a current server binary, the production SPA and VoicePill appeared. The first matrix case (390 px, Paper/light) clicked “Record voice memo”, but [data-voice-pill][data-state="recording"] did not appear within 30 seconds. Playwright stopped at apps/web/e2e/composer.mjs:734 with TimeoutError; it captured no updated screenshot. The test reported 0 CSP reports. The earlier preflight failure and this microphone-state timeout prevented the requested refreshed 390/820/1440 light/dark set and the local VoicePill performance profile. bun run check now passes; bun run test is running.

Round 2 screenshot attempt: after reusing the worktree's staged media runtime and a current server binary, the production SPA and VoicePill appeared. The first matrix case (390 px, Paper/light) clicked “Record voice memo”, but `[data-voice-pill][data-state="recording"]` did not appear within 30 seconds. Playwright stopped at `apps/web/e2e/composer.mjs:734` with `TimeoutError`; it captured no updated screenshot. The test reported 0 CSP reports. The earlier preflight failure and this microphone-state timeout prevented the requested refreshed 390/820/1440 light/dark set and the local VoicePill performance profile. `bun run check` now passes; `bun run test` is running.
Author
Owner

Round 2 implementation is committed on job/voicepill-617.

Head: a0cc3dd8d162122abf5125f71b934aab87ad2321
Commits:

  • 31e9929c9 Normalize voice memo display names (#617)
  • a0cc3dd8d Polish VoicePill waveform and announcements (#617)

Built:

  • Reused audioDisplayName across Calendar and Composer. The chip now uses the Calendar format Voice Memo, HH:MM; the indexed filename stays intact for paths and Note links.
  • Reused shared VoiceWaveform in the recording pill at 24 × 24 px. The action buttons use --toolbar-gap; VoicePill no longer defines local focus rings. Its height still uses the toolbar token.
  • Added a polite, atomic screen-reader status that changes with the one-second timer; the visible timer is not a live region. Reduced-motion CSS leaves waveform bars static. Updated DESIGN §38.
  • Added regression coverage and macOS platform assertions to the responsive screenshot flow.

UX gaps closed: Calendar and Composer names now agree; the waveform uses the shared, larger meter; stop/discard controls have the standard gap and shared focus treatment; the timer has a polite announcement cadence.

UX gaps left: The refreshed 390/820/1440 light/dark screenshot set was not captured or attached. The real-server browser run timed out waiting for the first recording to enter recording state after 30 seconds. The reduced-motion/live-region browser assertions and the #617 performance profile therefore did not run. No new screenshots are attached.

Gates and verification:

bun run check completed with exit 0. Output:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

Focused tests passed before commit: Test Files 2 passed (2), Tests 7 passed (7).

bun run test was stopped with exit 130 after it continued running past the job timebox. It did not produce a final Vitest summary. These failure lines were emitted before interruption:

 ❯ |component| src/lib/calendar/TimeGrid.svelte.test.ts (8 tests | 1 failed) 21478ms
   ❯ TimeGrid all-day overflow (2)
     × opens the shared anchored surface with every all-day event and task in a 7-day view 16355ms
 ❯ |component| src/lib/components/analytics/widgets/StatRow.svelte.test.ts (13 tests | 1 failed) 20877ms
   ❯ StatRow summary cards (13)
     × uses the shared type roles and never mono 7166ms
 ❯ |component| src/lib/components/analytics/RangeBar.svelte.test.ts (7 tests | 2 failed) 19937ms
   ❯ RangeBar pills and picker (7)
     × Next keeps focus when it turns onto the last page, and then does nothing 7545ms
     × turning pages constructs no new Intl.DateTimeFormat 7413ms
 ❯ |component| src/lib/calendar/agenda.svelte.test.ts (18 tests | 2 failed) 25724ms
   ❯ Agenda list (18)
     × pages automatically only after the end approaches from outside the viewport 6708ms
     × reveals a Log in a deferred day 6945ms
 ❯ |component| src/lib/calendar/ItemPreview.svelte.test.ts (10 tests | 1 failed) 14154ms
   ❯ Calendar item preview labels (10)
     × uses Journal for a Log entry source 11270ms
 ❯ |unit| src/lib/webmcp/generated.test.ts (9 tests | 1 failed) 32767ms
   × contract writes need confirmation and recheck Apps revocation 30143ms
 ❯ |unit| src/lib/themes.test.ts (74 tests | 1 failed) 35262ms
   ❯ rendered theme contrast contract (22)
     × keeps glass materials in the shared role tokens 11662ms
 ❯ |unit| src/lib/styles/font-weight-token.test.ts (3 tests | 1 failed) 9796ms
   ❯ font weight token guard (3)
     × rejects numeric font-weight declarations outside tokens and font faces 9772ms

git diff --check completed with no output. cargo clean output: Removed 1306 files, 590.7MiB total. No Rust source changed.

The required git fetch origin && git merge origin/dev returned Already up to date. at c4a61e8cf090170f35b1bed3350d9de20c83ecd5. During later checks, the shared origin/dev tracking ref advanced to 440e19dce23040ac8ebaae88f0469b6535b1afcb (fix(test): let local media sandbox start on a busy shared host); this branch is now one commit behind. I did not run a second merge.

Decisions recorded for owner confirmation: use the shared Calendar short label format for Composer recordings; size the pill waveform at 24 × 24 px while preserving the toolbar height; announce the timer through a polite status at the displayed one-second cadence. DESIGN §38 now records the accessibility cadence and reduced-motion behavior.

Round 2 implementation is committed on `job/voicepill-617`. Head: `a0cc3dd8d162122abf5125f71b934aab87ad2321` Commits: - `31e9929c9` Normalize voice memo display names (#617) - `a0cc3dd8d` Polish VoicePill waveform and announcements (#617) Built: - Reused `audioDisplayName` across Calendar and Composer. The chip now uses the Calendar format `Voice Memo, HH:MM`; the indexed filename stays intact for paths and Note links. - Reused shared `VoiceWaveform` in the recording pill at 24 × 24 px. The action buttons use `--toolbar-gap`; VoicePill no longer defines local focus rings. Its height still uses the toolbar token. - Added a polite, atomic screen-reader status that changes with the one-second timer; the visible timer is not a live region. Reduced-motion CSS leaves waveform bars static. Updated DESIGN §38. - Added regression coverage and macOS platform assertions to the responsive screenshot flow. UX gaps closed: Calendar and Composer names now agree; the waveform uses the shared, larger meter; stop/discard controls have the standard gap and shared focus treatment; the timer has a polite announcement cadence. UX gaps left: The refreshed 390/820/1440 light/dark screenshot set was not captured or attached. The real-server browser run timed out waiting for the first recording to enter `recording` state after 30 seconds. The reduced-motion/live-region browser assertions and the #617 performance profile therefore did not run. No new screenshots are attached. Gates and verification: `bun run check` completed with exit 0. Output: ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` Focused tests passed before commit: `Test Files 2 passed (2), Tests 7 passed (7)`. `bun run test` was stopped with exit 130 after it continued running past the job timebox. It did not produce a final Vitest summary. These failure lines were emitted before interruption: ```text ❯ |component| src/lib/calendar/TimeGrid.svelte.test.ts (8 tests | 1 failed) 21478ms ❯ TimeGrid all-day overflow (2) × opens the shared anchored surface with every all-day event and task in a 7-day view 16355ms ❯ |component| src/lib/components/analytics/widgets/StatRow.svelte.test.ts (13 tests | 1 failed) 20877ms ❯ StatRow summary cards (13) × uses the shared type roles and never mono 7166ms ❯ |component| src/lib/components/analytics/RangeBar.svelte.test.ts (7 tests | 2 failed) 19937ms ❯ RangeBar pills and picker (7) × Next keeps focus when it turns onto the last page, and then does nothing 7545ms × turning pages constructs no new Intl.DateTimeFormat 7413ms ❯ |component| src/lib/calendar/agenda.svelte.test.ts (18 tests | 2 failed) 25724ms ❯ Agenda list (18) × pages automatically only after the end approaches from outside the viewport 6708ms × reveals a Log in a deferred day 6945ms ❯ |component| src/lib/calendar/ItemPreview.svelte.test.ts (10 tests | 1 failed) 14154ms ❯ Calendar item preview labels (10) × uses Journal for a Log entry source 11270ms ❯ |unit| src/lib/webmcp/generated.test.ts (9 tests | 1 failed) 32767ms × contract writes need confirmation and recheck Apps revocation 30143ms ❯ |unit| src/lib/themes.test.ts (74 tests | 1 failed) 35262ms ❯ rendered theme contrast contract (22) × keeps glass materials in the shared role tokens 11662ms ❯ |unit| src/lib/styles/font-weight-token.test.ts (3 tests | 1 failed) 9796ms ❯ font weight token guard (3) × rejects numeric font-weight declarations outside tokens and font faces 9772ms ``` `git diff --check` completed with no output. `cargo clean` output: `Removed 1306 files, 590.7MiB total`. No Rust source changed. The required `git fetch origin && git merge origin/dev` returned `Already up to date.` at `c4a61e8cf090170f35b1bed3350d9de20c83ecd5`. During later checks, the shared `origin/dev` tracking ref advanced to `440e19dce23040ac8ebaae88f0469b6535b1afcb` (`fix(test): let local media sandbox start on a busy shared host`); this branch is now one commit behind. I did not run a second merge. Decisions recorded for owner confirmation: use the shared Calendar short label format for Composer recordings; size the pill waveform at 24 × 24 px while preserving the toolbar height; announce the timer through a polite status at the displayed one-second cadence. DESIGN §38 now records the accessibility cadence and reduced-motion behavior.
Author
Owner

Final report addendum (supersedes the earlier head SHA in my previous comment): final head is b6177e846dccb03d6a3d0b92fbe57b9c8f87faef.

Final review found that the existing VoicePill performance profile still waited for 24 .voice-pill-wave-bar nodes from the previous implementation. The shared pill waveform now renders five bars and exposes its bounded analyser level. I updated bench/voicepill-617.mjs to wait for a positive --voice-level and describe the current hot path. node --check bench/voicepill-617.mjs and git diff --check passed. I did not run the profile because its microphone flow hit the same recording-state timeout as the screenshot flow; no performance numbers are available.

Final report addendum (supersedes the earlier head SHA in my previous comment): final head is `b6177e846dccb03d6a3d0b92fbe57b9c8f87faef`. Final review found that the existing VoicePill performance profile still waited for 24 `.voice-pill-wave-bar` nodes from the previous implementation. The shared pill waveform now renders five bars and exposes its bounded analyser level. I updated `bench/voicepill-617.mjs` to wait for a positive `--voice-level` and describe the current hot path. `node --check bench/voicepill-617.mjs` and `git diff --check` passed. I did not run the profile because its microphone flow hit the same recording-state timeout as the screenshot flow; no performance numbers are available.
Author
Owner

Round 3 started on voicepill-617 at b6177e846. Merged origin/dev; retained the updated §57 keyboard motion rule. Investigating actual microphone capture and collecting macOS-emulated production screenshots at 390/820/1440 in Light and Dark. Full suites and performance measurements are reserved for the merge round by the 2026-10-02 verification policy.

Round 3 started on voicepill-617 at b6177e846. Merged origin/dev; retained the updated §57 keyboard motion rule. Investigating actual microphone capture and collecting macOS-emulated production screenshots at 390/820/1440 in Light and Dark. Full suites and performance measurements are reserved for the merge round by the 2026-10-02 verification policy.
Author
Owner

Round 3 evidence: microphone capture now starts on the production SPA with a compatible prebuilt server, CALTERNAL_E2E_ASSET_OVERRIDE=1, microphone permission and a known PCM input file. The phone recording passed changing-analyser, reduced-motion and polite one-second live-region checks. bun run check reports 0 errors and 0 warnings; focused VoicePill/recorder/attachments/Composer tests passed (4 files, 40 tests). The stronger movement assertion found that an over-loud fixture saturates the meter, so the shared fixture uses a lower changing amplitude. Committed fixture/profile wiring at 2ff6a4901. Capturing the saved Calendar attachment and the complete matrix next.

Round 3 evidence: microphone capture now starts on the production SPA with a compatible prebuilt server, CALTERNAL_E2E_ASSET_OVERRIDE=1, microphone permission and a known PCM input file. The phone recording passed changing-analyser, reduced-motion and polite one-second live-region checks. bun run check reports 0 errors and 0 warnings; focused VoicePill/recorder/attachments/Composer tests passed (4 files, 40 tests). The stronger movement assertion found that an over-loud fixture saturates the meter, so the shared fixture uses a lower changing amplitude. Committed fixture/profile wiring at 2ff6a4901. Capturing the saved Calendar attachment and the complete matrix next.
Author
Owner

Round 3 product finding: the actual saved Agenda card rendered Voice memo 2026-10-02 20.22.webm, while the Composer chip rendered Voice Memo, 20:22. The shared attachment view model only normalizes audio-family files; generated WebM voice files can have a container MIME of video/webm. A new regression failed with expected video to be audio. The narrow fix recognizes generated recorder WebM names as audio in the shared helper and retains video classification for ordinary WebM clips. The new focused regression now passes (1 file, 5 tests). Rebuilding and capturing the corrected saved state next.

Round 3 product finding: the actual saved Agenda card rendered `Voice memo 2026-10-02 20.22.webm`, while the Composer chip rendered `Voice Memo, 20:22`. The shared attachment view model only normalizes audio-family files; generated WebM voice files can have a container MIME of video/webm. A new regression failed with `expected video to be audio`. The narrow fix recognizes generated recorder WebM names as audio in the shared helper and retains video classification for ordinary WebM clips. The new focused regression now passes (1 file, 5 tests). Rebuilding and capturing the corrected saved state next.
Author
Owner

Round 3 second product finding: the 1440 px 3x crop clipped Discard. The focused Note recording then timed out because the adjacent Open in the editor button intercepted Discard clicks. The recording width was only 4.5 × the hit token, less than its waveform + timer + two buttons + three gaps on desktop. I replaced that multiplier with those CSS token dimensions and reserve five timer characters. The phone action group also exceeded its viewport during recording, so below 600 px the active VoicePill temporarily replaces the other Add controls. Stop/Discard and Send remain in one row; the Add controls return at idle. Added focused browser bounds assertions; rerunning affected checks and the capture matrix after this CSS change.

Round 3 second product finding: the 1440 px 3x crop clipped Discard. The focused Note recording then timed out because the adjacent `Open in the editor` button intercepted Discard clicks. The recording width was only 4.5 × the hit token, less than its waveform + timer + two buttons + three gaps on desktop. I replaced that multiplier with those CSS token dimensions and reserve five timer characters. The phone action group also exceeded its viewport during recording, so below 600 px the active VoicePill temporarily replaces the other Add controls. Stop/Discard and Send remain in one row; the Add controls return at idle. Added focused browser bounds assertions; rerunning affected checks and the capture matrix after this CSS change.
Author
Owner

Round 3 finished on job/voicepill-617 at 4b9232890630813364077a41b5de31b6e4ff87c6.

Merged origin/dev once, at c4faf184df726a9375ae0c13bdfb6018ac2cf57e, including the busy-host media preflight fix. No push, deploy or merge into dev.

Built and fixed:

  • Added a shared, changing PCM microphone fixture for the browser proof and the VoicePill profile. Chromium uses fake-device, fake-permission and fake-audio-file flags; contexts grant microphone access. The production asset override serves this branch's web build. Recording now starts and the real analyser changes its waveform level.
  • Fixed a real saved-label mismatch. The browser showed Voice memo 2026-10-02 20.22.webm after save. Generated recorder WebM files now use the audio family even when container MIME is video/webm, so Calendar and Composer both show Voice Memo, HH:MM. A new regression failed before the change and passed afterward. Ordinary WebM clips remain video.
  • Fixed desktop Discard overlap. The former fixed width clipped Discard and let the adjacent Note editor action intercept clicks. Width now reserves waveform, a five-character timer, both buttons and all shared gaps. Browser bounds checks pass.
  • On phones, the active recording uses the Add capsule's space. The other Add controls return at idle. Stop, Discard and Send fit inside the viewport.
  • Extended production captures to recording, stopped, attached and saved Journal states, with 3x row crops. Each case uses macOS platform signals. Saved cards open through touch on phone/tablet and Enter on desktop; real media playback advances and Escape closes it.
  • Moved host-side morph image encoding after Undo, so encoding cannot consume the real eight-second action window. Updated the profile to name its recorded baseline.

Files changed in this round: apps/web/e2e/voice-fixture.mjs, apps/web/e2e/composer.mjs, bench/voicepill-617.mjs, packages/ui/src/components/VoicePill.svelte, packages/ui/src/components/calendar/attachments.ts, apps/web/src/lib/composer/Composer.svelte, apps/web/src/lib/calendar/attachments.test.ts. The merge kept origin/dev's corrected DESIGN motion rule.

Evidence: all six production capture cases passed. Reduced motion keeps waveform bars static. The atomic polite status updates at the one-second cadence; the visible timer has aria-live off. The 3x crops were checked for icon/timer alignment and complete controls. Claude remains the visual reviewer.

Width Theme Recording Stopped Attached Saved Journal item 3x row
390 Light recording stopped attached saved row-3x
390 Dark recording stopped attached saved row-3x
820 Light recording stopped attached saved row-3x
820 Dark recording stopped attached saved row-3x
1440 Light recording stopped attached saved row-3x
1440 Dark recording stopped attached saved row-3x

Morph frame strip.

Gate output, verbatim:

cargo fmt --check produced no output. No Rust source changed, so Rust clippy/test gates are not applicable in this job.

bun run check:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

Final affected-file command: cd apps/web && bunx vitest run src/lib/composer/VoicePill.svelte.test.ts src/lib/composer/Composer.svelte.test.ts src/lib/calendar/attachments.test.ts --maxWorkers=2. Full output:


 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web

Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method

 Test Files  3 passed (3)
      Tests  37 passed (37)
   Start at  20:29:59
   Duration  36.49s (transform 64%, environment 23%, import 8%, tests 4%, setup 1%)

  Transform  |component| transforming modules took 39.20s · 61% of tracked time, re-done on every run
             persist transforms across runs with fsModuleCache: true
             learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns

Earlier focused recorder/attachment/component checks also passed (4 files, 40 tests); the new container regression passed (1 file, 5 tests). Those complete logs are attached. The production build completed with exit 0. Node syntax checks and git diff --check passed. Module comments in all touched files were re-read.

Browser output for the complete screenshot matrix, verbatim:

PASS VoicePill 390 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 390 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 820 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 820 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 1440 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 1440 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
CSP REPORTS composer: 0 across 7 pages

The tail-only run then reported, verbatim:

PASS VoicePill focused controls: morph, Stop, Undo, Discard, keyboard Note save and control containment
Capacity fixture states: [
  "Attached", "Attached", "Attached", "Attached", "Attached", "Attached", "Attached", "Attached",
  "Attached", "Attached", "Uploading 100%", "Uploading 100%", "Uploading 0%", "Uploading 0%",
  "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%",
  "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%",
  "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%"
]

Known gaps and UX gaps left:

  • The 32-file capacity fixture timed out at its unchanged 30-second wait: 10 files were Attached, 2 were Uploading 100%, and 20 were Uploading 0%. No error state was reported. This is a SLOW verification gap. The retained-file retry tail was not reached; its existing expectation was kept. The focused recording/morph/Stop/Undo/Discard/keyboard Note-save and bounds checks passed before that fixture.
  • The exact cause of the previous round's recording-state timeout cannot be reconstructed from its missing media diagnostics. It no longer reproduces with the current production assets, prebuilt server, granted permission and known fake audio. New startup diagnostics report secure-context, media-device and microphone-policy state on failure.
  • Saved WebM cards still use the existing container-derived file glyph. Audio card presentation belongs to the #622 integration; this is cosmetic. The saved card opens and plays.
  • No WebKit/native Mac run, full web suite or adversarial matrix was run under the latest verification policy.

UX gaps closed: real-browser capture proof; changing waveform; reduced-motion and live-region proof; consistent saved names; reachable desktop Discard; phone control containment; touch/keyboard opening and real playback of saved memos.

Decisions for owner confirmation:

  • Reserve the five-character timer and all control widths with CSS tokens, with no runtime layout measurement.
  • Below 600 px, the active VoicePill takes the Add capsule; other Add controls return when idle.
  • Treat generated recorder WebM names as audio for Calendar display when container sniffing reports video/webm. Keep other WebM files as video.
  • CURRENT was not available. Used a private copy of the prebuilt photopw-849 server binary (built 2026-10-02 20:13) with this branch's production SPA. All API writes and media reads remained real.

For the merge round:

  • cd apps/web && bun run test — full web regression suite.
  • cd apps/web && COMPOSER_VOICEPILL_ONLY=1 CALTERNAL_E2E_ASSET_OVERRIDE=1 COMPOSER_SHOTS=../../artifacts/voicepill-617-merge bun e2e/composer.mjs with CALTERNAL_SERVER_BIN set to the combined build — complete retained-file capacity/retry coverage.
  • cd apps/web && bun run test:e2e:composer — all Composer write, keyboard and error flows on the combined branch.
  • tests/adversarial/run.sh — combined-server cross-plugin and robustness round.
  • The latest verification policy reserves performance measurements for the merge round and performance issues, even when an older brief asks for them. No new benchmark numbers are claimed. On the perf VM, stage the production build and export the shared release binary as CALTERNAL_SERVER_BIN, then run flock /root/perf.lock bash -c 'uptime; PERF_VM_LOCK=1 PERF_COMMIT=$(git rev-parse HEAD) bun bench/voicepill-617.mjs --json artifacts/voicepill-617/profile.json'. Compare CPU, RSS, average and full-tray burst results with docs/perf/baseline.json: metrics.scenarios.voicepill_617. The baseline average p50/p95 is 580.35/792.39 ms at 390 and 455.15/634.50 ms at 1440; burst p50/p95 is 467.05/582.77 and 461.02/697.31 ms.

Cleanup output, verbatim:

     Removed 1 file, 356B total

Web build output and the private server copy were removed. Git status is clean. Review artifacts remain ignored in this worktree and are attached to this issue.

Round 3 finished on `job/voicepill-617` at `4b9232890630813364077a41b5de31b6e4ff87c6`. Merged `origin/dev` once, at `c4faf184df726a9375ae0c13bdfb6018ac2cf57e`, including the busy-host media preflight fix. No push, deploy or merge into dev. Built and fixed: - Added a shared, changing PCM microphone fixture for the browser proof and the VoicePill profile. Chromium uses fake-device, fake-permission and fake-audio-file flags; contexts grant microphone access. The production asset override serves this branch's web build. Recording now starts and the real analyser changes its waveform level. - Fixed a real saved-label mismatch. The browser showed `Voice memo 2026-10-02 20.22.webm` after save. Generated recorder WebM files now use the audio family even when container MIME is video/webm, so Calendar and Composer both show `Voice Memo, HH:MM`. A new regression failed before the change and passed afterward. Ordinary WebM clips remain video. - Fixed desktop Discard overlap. The former fixed width clipped Discard and let the adjacent Note editor action intercept clicks. Width now reserves waveform, a five-character timer, both buttons and all shared gaps. Browser bounds checks pass. - On phones, the active recording uses the Add capsule's space. The other Add controls return at idle. Stop, Discard and Send fit inside the viewport. - Extended production captures to recording, stopped, attached and saved Journal states, with 3x row crops. Each case uses macOS platform signals. Saved cards open through touch on phone/tablet and Enter on desktop; real media playback advances and Escape closes it. - Moved host-side morph image encoding after Undo, so encoding cannot consume the real eight-second action window. Updated the profile to name its recorded baseline. Files changed in this round: `apps/web/e2e/voice-fixture.mjs`, `apps/web/e2e/composer.mjs`, `bench/voicepill-617.mjs`, `packages/ui/src/components/VoicePill.svelte`, `packages/ui/src/components/calendar/attachments.ts`, `apps/web/src/lib/composer/Composer.svelte`, `apps/web/src/lib/calendar/attachments.test.ts`. The merge kept origin/dev's corrected DESIGN motion rule. Evidence: all six production capture cases passed. Reduced motion keeps waveform bars static. The atomic polite status updates at the one-second cadence; the visible timer has aria-live off. The 3x crops were checked for icon/timer alignment and complete controls. Claude remains the visual reviewer. | Width | Theme | Recording | Stopped | Attached | Saved Journal item | 3x row | |---|---|---|---|---|---|---| | 390 | Light | [recording](https://git.kayg.org/attachments/505749a3-4178-4a85-a7b3-9ad53e6717a7) | [stopped](https://git.kayg.org/attachments/7dc4c6ab-d8a3-4324-bd25-bdc41f5ea258) | [attached](https://git.kayg.org/attachments/2211f7ff-ca40-4e80-a550-8e3b52d8f407) | [saved](https://git.kayg.org/attachments/0c58e7c6-16e9-4aac-9efa-9cc3b1e009da) | [row-3x](https://git.kayg.org/attachments/21cee230-ecf9-4b8f-a890-e0c05131c8db) | | 390 | Dark | [recording](https://git.kayg.org/attachments/5de6ca3a-2132-4142-b00d-9265fe3e3709) | [stopped](https://git.kayg.org/attachments/447dcba5-4feb-4756-bfe6-774d29e7e076) | [attached](https://git.kayg.org/attachments/b5bccdf0-1e9d-42af-b31e-e4e1259626f5) | [saved](https://git.kayg.org/attachments/e0cb8bbe-9889-444b-8f13-91a40553a13c) | [row-3x](https://git.kayg.org/attachments/b77da825-8c9d-428b-b17b-b6521c5f28ba) | | 820 | Light | [recording](https://git.kayg.org/attachments/72ec9f6e-c5a4-40f7-8685-a6a9ea807020) | [stopped](https://git.kayg.org/attachments/95f7ce9a-5c4d-4ec4-96cc-44a554d75810) | [attached](https://git.kayg.org/attachments/889960a0-51d3-46d4-8541-6f29c1996263) | [saved](https://git.kayg.org/attachments/0c1f5835-6d57-4525-a63b-4804c6f9de6d) | [row-3x](https://git.kayg.org/attachments/df10ee17-5def-42e5-a7a1-1dce49742ea1) | | 820 | Dark | [recording](https://git.kayg.org/attachments/8d843437-cfd1-40b2-b70d-64cca1474a1e) | [stopped](https://git.kayg.org/attachments/e06cfeb2-a184-41a7-8856-ccfb450eda72) | [attached](https://git.kayg.org/attachments/7823eff0-a1af-4f87-b541-757e23fb4fb6) | [saved](https://git.kayg.org/attachments/8319311e-8823-4a60-8fea-2ad13344b4c2) | [row-3x](https://git.kayg.org/attachments/f16bb269-092a-433a-b7bf-57d0e10d6671) | | 1440 | Light | [recording](https://git.kayg.org/attachments/ee1e87de-bcec-42b2-9f9a-5c0fa52af744) | [stopped](https://git.kayg.org/attachments/41e282ba-d5f9-4588-be66-d02046a38f0d) | [attached](https://git.kayg.org/attachments/e805d80e-b0d6-4487-800e-ed53f19b8167) | [saved](https://git.kayg.org/attachments/06a6dc2d-e85d-4c48-8fab-03f1a01c9586) | [row-3x](https://git.kayg.org/attachments/7fba6bf1-b478-4b1a-b293-80e56efb478f) | | 1440 | Dark | [recording](https://git.kayg.org/attachments/e7b20543-7c3d-4312-845f-77edeaf9706c) | [stopped](https://git.kayg.org/attachments/f21ea7e3-d027-4608-ab6f-2600e757b89c) | [attached](https://git.kayg.org/attachments/6a6c6c5d-4364-4406-ba48-9870fabedcd9) | [saved](https://git.kayg.org/attachments/b40092fc-628a-469f-88f3-b634e0033f35) | [row-3x](https://git.kayg.org/attachments/257ba22e-a7c2-422b-b69f-f85a4c8d2f41) | [Morph frame strip](https://git.kayg.org/attachments/10ef35f4-685f-409b-a01f-b2a72f9c0483). Gate output, verbatim: `cargo fmt --check` produced no output. No Rust source changed, so Rust clippy/test gates are not applicable in this job. `bun run check`: ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` Final affected-file command: `cd apps/web && bunx vitest run src/lib/composer/VoicePill.svelte.test.ts src/lib/composer/Composer.svelte.test.ts src/lib/calendar/attachments.test.ts --maxWorkers=2`. Full output: ```text RUN v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Test Files 3 passed (3) Tests 37 passed (37) Start at 20:29:59 Duration 36.49s (transform 64%, environment 23%, import 8%, tests 4%, setup 1%) Transform |component| transforming modules took 39.20s · 61% of tracked time, re-done on every run persist transforms across runs with fsModuleCache: true learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns ``` Earlier focused recorder/attachment/component checks also passed (4 files, 40 tests); the new container regression passed (1 file, 5 tests). Those complete logs are attached. The production build completed with exit 0. Node syntax checks and `git diff --check` passed. Module comments in all touched files were re-read. Browser output for the complete screenshot matrix, verbatim: ```text PASS VoicePill 390 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 390 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 820 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 820 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 1440 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 1440 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop CSP REPORTS composer: 0 across 7 pages ``` The tail-only run then reported, verbatim: ```text PASS VoicePill focused controls: morph, Stop, Undo, Discard, keyboard Note save and control containment Capacity fixture states: [ "Attached", "Attached", "Attached", "Attached", "Attached", "Attached", "Attached", "Attached", "Attached", "Attached", "Uploading 100%", "Uploading 100%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%", "Uploading 0%" ] ``` Known gaps and UX gaps left: - The 32-file capacity fixture timed out at its unchanged 30-second wait: 10 files were Attached, 2 were Uploading 100%, and 20 were Uploading 0%. No error state was reported. This is a SLOW verification gap. The retained-file retry tail was not reached; its existing expectation was kept. The focused recording/morph/Stop/Undo/Discard/keyboard Note-save and bounds checks passed before that fixture. - The exact cause of the previous round's recording-state timeout cannot be reconstructed from its missing media diagnostics. It no longer reproduces with the current production assets, prebuilt server, granted permission and known fake audio. New startup diagnostics report secure-context, media-device and microphone-policy state on failure. - Saved WebM cards still use the existing container-derived file glyph. Audio card presentation belongs to the #622 integration; this is cosmetic. The saved card opens and plays. - No WebKit/native Mac run, full web suite or adversarial matrix was run under the latest verification policy. UX gaps closed: real-browser capture proof; changing waveform; reduced-motion and live-region proof; consistent saved names; reachable desktop Discard; phone control containment; touch/keyboard opening and real playback of saved memos. Decisions for owner confirmation: - Reserve the five-character timer and all control widths with CSS tokens, with no runtime layout measurement. - Below 600 px, the active VoicePill takes the Add capsule; other Add controls return when idle. - Treat generated recorder WebM names as audio for Calendar display when container sniffing reports video/webm. Keep other WebM files as video. - CURRENT was not available. Used a private copy of the prebuilt photopw-849 server binary (built 2026-10-02 20:13) with this branch's production SPA. All API writes and media reads remained real. For the merge round: - `cd apps/web && bun run test` — full web regression suite. - `cd apps/web && COMPOSER_VOICEPILL_ONLY=1 CALTERNAL_E2E_ASSET_OVERRIDE=1 COMPOSER_SHOTS=../../artifacts/voicepill-617-merge bun e2e/composer.mjs` with `CALTERNAL_SERVER_BIN` set to the combined build — complete retained-file capacity/retry coverage. - `cd apps/web && bun run test:e2e:composer` — all Composer write, keyboard and error flows on the combined branch. - `tests/adversarial/run.sh` — combined-server cross-plugin and robustness round. - The latest verification policy reserves performance measurements for the merge round and performance issues, even when an older brief asks for them. No new benchmark numbers are claimed. On the perf VM, stage the production build and export the shared release binary as `CALTERNAL_SERVER_BIN`, then run `flock /root/perf.lock bash -c 'uptime; PERF_VM_LOCK=1 PERF_COMMIT=$(git rev-parse HEAD) bun bench/voicepill-617.mjs --json artifacts/voicepill-617/profile.json'`. Compare CPU, RSS, average and full-tray burst results with `docs/perf/baseline.json: metrics.scenarios.voicepill_617`. The baseline average p50/p95 is 580.35/792.39 ms at 390 and 455.15/634.50 ms at 1440; burst p50/p95 is 467.05/582.77 and 461.02/697.31 ms. Cleanup output, verbatim: ```text Removed 1 file, 356B total ``` Web build output and the private server copy were removed. Git status is clean. Review artifacts remain ignored in this worktree and are attached to this issue.
Author
Owner

Send-back round started on job/voicepill-617.

  • Current branch head: 4b9232890630813364077a41b5de31b6e4ff87c6
  • Branch base: c4faf184df726a9375ae0c13bdfb6018ac2cf57e
  • Reported worktree status: clean

I am tracing the pill geometry, requesting state, waveform layout, and the timed-out 32-attachment retained retry before editing. I will recapture the requested 390/820/1440 light/dark Mac-rendered production screenshots and run focused verification after the fixes.

Send-back round started on `job/voicepill-617`. - Current branch head: `4b9232890630813364077a41b5de31b6e4ff87c6` - Branch base: `c4faf184df726a9375ae0c13bdfb6018ac2cf57e` - Reported worktree status: clean I am tracing the pill geometry, requesting state, waveform layout, and the timed-out 32-attachment retained retry before editing. I will recapture the requested 390/820/1440 light/dark Mac-rendered production screenshots and run focused verification after the fixes.
Author
Owner

Finding from the reviewed production screenshots and current CSS:

  • VoicePill.svelte sets the pill height to --control-h-compact but sets each Pill icon size to --control-h. Pill.svelte uses that value for both button width and height. The shared tokens resolve to 30/24 px on a fine pointer and 44/36 px on a coarse pointer, which matches the circles extending beyond the pill in the attached 1440 and 390 3x crops. verifyVoiceControlBounds() currently checks only horizontal containment.
  • The requesting state reserves 3.3 * --hit for an ellipsized text label plus Cancel; the existing morph strip shows the label clipped to Wai... / Waiting....
  • VoiceWaveform renders five 2 px bars in a 24 px region, while the recording slot is 0.9 * --hit; the screenshots show the unused gap before the timer.

I will add production geometry assertions for both axes and waveform spread, then adjust the pill controls, request indicator and waveform against those checks. The 32-file retained retry already exists in the focused real-server e2e; I will run it separately after reproducing its timeout.

Finding from the reviewed production screenshots and current CSS: - `VoicePill.svelte` sets the pill height to `--control-h-compact` but sets each `Pill` icon size to `--control-h`. `Pill.svelte` uses that value for both button width and height. The shared tokens resolve to 30/24 px on a fine pointer and 44/36 px on a coarse pointer, which matches the circles extending beyond the pill in the attached 1440 and 390 3x crops. `verifyVoiceControlBounds()` currently checks only horizontal containment. - The requesting state reserves `3.3 * --hit` for an ellipsized text label plus Cancel; the existing morph strip shows the label clipped to `Wai...` / `Waiting...`. - `VoiceWaveform` renders five 2 px bars in a 24 px region, while the recording slot is `0.9 * --hit`; the screenshots show the unused gap before the timer. I will add production geometry assertions for both axes and waveform spread, then adjust the pill controls, request indicator and waveform against those checks. The 32-file retained retry already exists in the focused real-server e2e; I will run it separately after reproducing its timeout.
Author
Owner

The 390/820/1440 light/dark production screenshot matrix and focused VoicePill controls passed. In the 32-file retry case, the test waited for the retried upload to report Attached before looking for Undo; the subsequent Undo lookup timed out after 30 seconds. I changed the fixture to cancel queued uploads before opening the slot and to wait for the specific “Voice memo attached” toast before checking the settled upload state. I am rerunning this sequence now.

The 390/820/1440 light/dark production screenshot matrix and focused VoicePill controls passed. In the 32-file retry case, the test waited for the retried upload to report Attached before looking for Undo; the subsequent Undo lookup timed out after 30 seconds. I changed the fixture to cancel queued uploads before opening the slot and to wait for the specific “Voice memo attached” toast before checking the settled upload state. I am rerunning this sequence now.
Author
Owner

Completed

  • Sized Stop and Discard to fit the VoicePill capsule with a 1 px inset. The shared Pill hit area stays available for touch.
  • Replaced the truncated microphone-request text with a mic icon and a polite, visually hidden live status.
  • Spread the shared 24-bar waveform across its reserved lane up to the timer.
  • Added production geometry assertions for pill containment, control centering, waveform span and timer spacing.
  • The 32-entry capacity scenario now cancels queued fixtures before waiting for one real file. It proves the memo stays available after refusal, retries into a freed slot, uploads, and can be undone.

UX gaps closed

  • Control circles remain inside the pill at phone, tablet and desktop widths.
  • The request state has no clipped or ellipsized visible label, and assistive technology receives its status.
  • The waveform uses the full lane and the timer follows at the shared gap.
  • Capacity refusal and retry complete with the retained voice memo and Undo action.

UX gaps left

None found in the #617 scope. Screenshots use Playwright macOS platform emulation.

Decisions

DESIGN §38 does not set these exact details. The action size uses --control-h-compact - 2px to leave a 1 px inset while retaining the shared hit target. The requesting state shows only the microphone icon and announces “Waiting for microphone…” through a hidden polite status. The pill uses the first 24 bars from the shared waveform data and spans the full reserved lane.

Files

  • packages/ui/src/components/VoicePill.svelte
  • packages/ui/src/components/composer/VoiceWaveform.svelte
  • apps/web/src/lib/composer/VoicePill.svelte.test.ts
  • apps/web/e2e/composer.mjs

Head

844277e621d3648df00f01bf4871929038503812

Verification

  • cargo fmt --check: exit 0; no stdout.
  • No Rust crate changed, so per-crate clippy/test gates did not apply. The local server build used for E2E completed with: Finished dev profile [unoptimized + debuginfo] target(s) in 35m 51s.
  • Production bun run build succeeded. The final screenshots came from that production SPA.

bun run check output:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bunx vitest run src/lib/composer/VoicePill.svelte.test.ts --maxWorkers=2 output:

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web


 Test Files  1 passed (1)
      Tests  4 passed (4)
   Start at  10:37:24
   Duration  16.99s (transform 50%, environment 29%, import 17%, tests 1%)

  Transform  |component| transforming modules took 8.25s · 50% of tracked time, re-done on every run
             persist transforms across runs with fsModuleCache: true
             learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns

node --check apps/web/e2e/composer.mjs: exit 0; no stdout.

Production E2E output:

PASS VoicePill 390 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 390 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 820 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 820 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 1440 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 1440 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill focused controls: morph, Stop, Undo, Discard, keyboard Note save and control containment
composer voice pill e2e: record, attach, Undo, discard, Note save and retained-file retry passed; screenshots in ../../artifacts/voicepill-617-sendback
CSP REPORTS composer: 0 across 7 pages

Evidence

The production screenshot set is attached to this issue. The six groups cover recording, stopped, attached, saved and 3× row-crop states at 390/820/1440 px in light and dark. The macOS-rendered morph strip is here.

Remaining checks

The full web suite, full E2E suite and adversarial matrices remain for the merge round under the shared verification policy. No performance measurement was run for this visual/accessibility issue; the existing bench/voicepill-617.mjs profile remains available.

## Completed - Sized Stop and Discard to fit the VoicePill capsule with a 1 px inset. The shared Pill hit area stays available for touch. - Replaced the truncated microphone-request text with a mic icon and a polite, visually hidden live status. - Spread the shared 24-bar waveform across its reserved lane up to the timer. - Added production geometry assertions for pill containment, control centering, waveform span and timer spacing. - The 32-entry capacity scenario now cancels queued fixtures before waiting for one real file. It proves the memo stays available after refusal, retries into a freed slot, uploads, and can be undone. ## UX gaps closed - Control circles remain inside the pill at phone, tablet and desktop widths. - The request state has no clipped or ellipsized visible label, and assistive technology receives its status. - The waveform uses the full lane and the timer follows at the shared gap. - Capacity refusal and retry complete with the retained voice memo and Undo action. ## UX gaps left None found in the #617 scope. Screenshots use Playwright macOS platform emulation. ## Decisions DESIGN §38 does not set these exact details. The action size uses `--control-h-compact - 2px` to leave a 1 px inset while retaining the shared hit target. The requesting state shows only the microphone icon and announces “Waiting for microphone…” through a hidden polite status. The pill uses the first 24 bars from the shared waveform data and spans the full reserved lane. ## Files - `packages/ui/src/components/VoicePill.svelte` - `packages/ui/src/components/composer/VoiceWaveform.svelte` - `apps/web/src/lib/composer/VoicePill.svelte.test.ts` - `apps/web/e2e/composer.mjs` ## Head `844277e621d3648df00f01bf4871929038503812` ## Verification - `cargo fmt --check`: exit 0; no stdout. - No Rust crate changed, so per-crate clippy/test gates did not apply. The local server build used for E2E completed with: `Finished `dev` profile [unoptimized + debuginfo] target(s) in 35m 51s`. - Production `bun run build` succeeded. The final screenshots came from that production SPA. `bun run check` output: ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bunx vitest run src/lib/composer/VoicePill.svelte.test.ts --maxWorkers=2` output: ```text RUN v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Test Files 1 passed (1) Tests 4 passed (4) Start at 10:37:24 Duration 16.99s (transform 50%, environment 29%, import 17%, tests 1%) Transform |component| transforming modules took 8.25s · 50% of tracked time, re-done on every run persist transforms across runs with fsModuleCache: true learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns ``` `node --check apps/web/e2e/composer.mjs`: exit 0; no stdout. Production E2E output: ```text PASS VoicePill 390 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 390 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 820 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 820 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 1440 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 1440 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill focused controls: morph, Stop, Undo, Discard, keyboard Note save and control containment composer voice pill e2e: record, attach, Undo, discard, Note save and retained-file retry passed; screenshots in ../../artifacts/voicepill-617-sendback CSP REPORTS composer: 0 across 7 pages ``` ## Evidence The production screenshot set is attached to this issue. The six groups cover recording, stopped, attached, saved and 3× row-crop states at 390/820/1440 px in light and dark. The macOS-rendered morph strip is [here](https://git.kayg.org/attachments/f604b5c3-e905-4d63-a5fc-741fc6ac9229). - **390 light:** [recording](https://git.kayg.org/attachments/9c5f7201-2824-4926-8c2c-4391df9f2557) · [stopped](https://git.kayg.org/attachments/e0b44a07-c898-4f77-8cba-79d172d0f164) · [attached](https://git.kayg.org/attachments/c8d0ec99-e440-4dc8-82d3-ae494f21e899) · [saved](https://git.kayg.org/attachments/d0e3c45c-6642-4d97-8115-dfedaa0e6294) · [3× row crop](https://git.kayg.org/attachments/6ae2e2b6-db52-412a-9e59-0b112280c122) - **390 dark:** [recording](https://git.kayg.org/attachments/3a1207a2-3efd-4511-a6e3-b0a1bffadd08) · [stopped](https://git.kayg.org/attachments/ac62c45f-ca74-454c-a507-5ffedb7a2175) · [attached](https://git.kayg.org/attachments/eeefab76-aed3-474b-9d33-0d70473cf24d) · [saved](https://git.kayg.org/attachments/96536552-2640-47b1-b09a-3630f10ebfb7) · [3× row crop](https://git.kayg.org/attachments/bbdaa465-27d3-4c00-99fd-3de303e1c5c4) - **820 light:** [recording](https://git.kayg.org/attachments/bc6bc005-38a8-4148-984d-8c8bcff426a7) · [stopped](https://git.kayg.org/attachments/9b3db03c-91f6-441a-a08d-bcc599f3d1c8) · [attached](https://git.kayg.org/attachments/f58aa136-490b-4424-910d-980af39120db) · [saved](https://git.kayg.org/attachments/24e07981-fd17-4451-9403-404577477e33) · [3× row crop](https://git.kayg.org/attachments/93a40005-25f8-47b2-9504-9f453cc2cd1f) - **820 dark:** [recording](https://git.kayg.org/attachments/955c0977-8bbc-4db7-a67e-15d055f0e30a) · [stopped](https://git.kayg.org/attachments/e5d8b8f2-1dd1-499f-9ec4-da9131dd2c0e) · [attached](https://git.kayg.org/attachments/64c13d0b-4c8f-4015-8df9-2dbbcec46b34) · [saved](https://git.kayg.org/attachments/d1418331-d643-49d1-9b2c-19146bb67e53) · [3× row crop](https://git.kayg.org/attachments/1878a35d-ebb7-4606-8ee5-56d5c5d9a6a8) - **1440 light:** [recording](https://git.kayg.org/attachments/cd950e55-24cc-4340-bf61-0afd1534ce32) · [stopped](https://git.kayg.org/attachments/45b0305f-9531-4d1a-9472-ad919e4b5e49) · [attached](https://git.kayg.org/attachments/2f1c82aa-43fe-4fba-afc2-dbdcac3764fc) · [saved](https://git.kayg.org/attachments/c47af6ed-4565-419a-b44b-281d0beb1059) · [3× row crop](https://git.kayg.org/attachments/cf4cf3aa-4593-4153-a99d-9020b4820756) - **1440 dark:** [recording](https://git.kayg.org/attachments/a10b1c1a-7e4c-4890-8ca5-7850f6ac64bb) · [stopped](https://git.kayg.org/attachments/81035ded-6289-4765-949a-38df2b1daa8e) · [attached](https://git.kayg.org/attachments/63bda5bd-1934-4807-8918-65150befc7e8) · [saved](https://git.kayg.org/attachments/2889eb1e-c4be-4dad-8fd5-0eca78a0cd72) · [3× row crop](https://git.kayg.org/attachments/a1a28892-b64c-4f50-aed7-41fec326ec43) ## Remaining checks The full web suite, full E2E suite and adversarial matrices remain for the merge round under the shared verification policy. No performance measurement was run for this visual/accessibility issue; the existing `bench/voicepill-617.mjs` profile remains available.
Author
Owner

Starting SEND-BACK round 2 on branch job/voicepill-617.

Base SHA (origin/dev merge base): 4a871b383864dad0d6c87bcd22b7f0d589e3506e. Current HEAD: 844277e621d3648df00f01bf4871929038503812.

I am adjusting the visible Stop and Discard circles to have equal top, bottom and trailing insets, preserving their shared touch targets. I will strengthen the existing e2e geometry assertion, then capture the 390/820/1440 light/dark set and 3x row crops.

Starting SEND-BACK round 2 on branch `job/voicepill-617`. Base SHA (`origin/dev` merge base): `4a871b383864dad0d6c87bcd22b7f0d589e3506e`. Current HEAD: `844277e621d3648df00f01bf4871929038503812`. I am adjusting the visible Stop and Discard circles to have equal top, bottom and trailing insets, preserving their shared touch targets. I will strengthen the existing e2e geometry assertion, then capture the 390/820/1440 light/dark set and 3x row crops.
Author
Owner

Finding: the previous bounds helper allowed Stop and Discard to touch the recording capsule because it only checked containment with a 1px tolerance; the component sized each icon control to --control-h-compact - 2px, which filled the capsule's inner height. It did not assert a shared inset, an inner radius, or the visible gap. I am extending that check to cover those measurements and the retained shared hit area.

Finding: the previous bounds helper allowed Stop and Discard to touch the recording capsule because it only checked containment with a 1px tolerance; the component sized each icon control to `--control-h-compact - 2px`, which filled the capsule's inner height. It did not assert a shared inset, an inner radius, or the visible gap. I am extending that check to cover those measurements and the retained shared hit area.
Author
Owner

Finding: after reducing the painted circles, the old capsule width left spare space. The production geometry run showed space-between distributing that space into the waveform-to-timer gap, which violated the existing shared-gap assertion. I have refit the recording capsule width around the smaller circles and equal insets so the existing waveform gap behavior stays intact; the original assertion remains unchanged.

Finding: after reducing the painted circles, the old capsule width left spare space. The production geometry run showed `space-between` distributing that space into the waveform-to-timer gap, which violated the existing shared-gap assertion. I have refit the recording capsule width around the smaller circles and equal insets so the existing waveform gap behavior stays intact; the original assertion remains unchanged.
Author
Owner

Completed SEND-BACK round 2 for #617.

Head SHA: e4f2a8f007d60d4f9600d0c146ba6083b2a43e66
Branch: job/voicepill-617

Built

  • The Stop and Discard circles now have equal --island-pad insets at the top and bottom; Discard has the same inset at the trailing edge.
  • The inner radius uses --radius-pill - --island-pad. Stop and Discard keep a visible gap, and the shared Pill pseudo-element still provides the full --hit target.
  • The recording capsule now fits the smaller controls, so the existing waveform-to-timer gap stays at its shared value.
  • The production e2e bounds check covers all three insets, the inner radius, visible gap, --hit target, and waveform gap.

Files

  • packages/ui/src/components/VoicePill.svelte
  • apps/web/e2e/composer.mjs

Screenshots

All files are attached to this issue. Each screenshot uses macOS platform signals.

Width Theme Recording Stopped Attached Saved 3x row crop
390 Light recording stopped attached saved row-3x
390 Dark recording stopped attached saved row-3x
820 Light recording stopped attached saved row-3x
820 Dark recording stopped attached saved row-3x
1440 Light recording stopped attached saved row-3x
1440 Dark recording stopped attached saved row-3x

Morph strip: five-frame strip.

Gate output

Production build completed with exit 0. Its final output was:

✓ built in 35.64s
> Using @sveltejs/adapter-static
  Wrote site to "build"
  ✔ done

The server build was needed because /mnt/hdd/targets/shared-server/CURRENT was absent:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 91m 27s

bun run check:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

Focused component test, verbatim:

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web


 Test Files  1 passed (1)
      Tests  4 passed (4)
   Start at  13:43:27
   Duration  9.06s (transform 74%, environment 15%, import 6%, setup 4%, tests 1%)

  Transform  |component| transforming modules took 6.62s · 74% of tracked time, re-done on every run
             persist transforms across runs with fsModuleCache: true
             learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns

Production VoicePill e2e output, verbatim:

PASS VoicePill 390 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 390 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 820 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 820 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 1440 light: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill 1440 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop
PASS VoicePill focused controls: morph, Stop, Undo, Discard, keyboard Note save and control containment
composer voice pill e2e: record, attach, Undo, discard, Note save and retained-file retry passed; screenshots in /home/kayg/Developer/calternal-wt/voicepill-617/artifacts/voicepill-617-round2
CSP REPORTS composer: 0 across 7 pages

Cleanup output:

Removed 7237 files, 4.6GiB total

No Rust source changed, so Rust format, clippy and test gates were not applicable. node --check e2e/composer.mjs and git diff --check passed.

UX gaps closed

The circle outlines no longer meet the capsule outline. Stop and Discard stay separated, vertically concentric, and touchable at phone, tablet and desktop sizes. The responsive screenshot set covers light and dark themes.

Known gaps and UX gaps left

No VoicePill-specific gaps remain. Vite printed existing use client directive notices from vendored analytics files; the production build completed.

Decisions

DESIGN §38 does not set a precise control inset. I used --island-pad as the inset and reduced the inherited pill radius by the same value, as the review requested. I refit the shell width to preserve the existing shared waveform gap after the controls became smaller.

For the merge round

Run the full web Vitest and combined e2e suites under the merge-round policy. This job ran the focused VoicePill component test and production e2e only.

Completed SEND-BACK round 2 for #617. Head SHA: `e4f2a8f007d60d4f9600d0c146ba6083b2a43e66` Branch: `job/voicepill-617` ## Built - The Stop and Discard circles now have equal `--island-pad` insets at the top and bottom; Discard has the same inset at the trailing edge. - The inner radius uses `--radius-pill - --island-pad`. Stop and Discard keep a visible gap, and the shared `Pill` pseudo-element still provides the full `--hit` target. - The recording capsule now fits the smaller controls, so the existing waveform-to-timer gap stays at its shared value. - The production e2e bounds check covers all three insets, the inner radius, visible gap, `--hit` target, and waveform gap. ## Files - `packages/ui/src/components/VoicePill.svelte` - `apps/web/e2e/composer.mjs` ## Screenshots All files are attached to this issue. Each screenshot uses macOS platform signals. | Width | Theme | Recording | Stopped | Attached | Saved | 3x row crop | |---:|---|---|---|---|---|---| | 390 | Light | [recording](https://git.kayg.org/attachments/e1a0a702-bd6f-4dba-b8cc-8e06257091f9) | [stopped](https://git.kayg.org/attachments/aa08d89f-1c83-49ac-b2b7-9168cde28d13) | [attached](https://git.kayg.org/attachments/051696e6-a94b-49eb-a3dc-ee8eec05598b) | [saved](https://git.kayg.org/attachments/4f744a0a-be42-4404-bbce-061cd973a877) | [row-3x](https://git.kayg.org/attachments/3b73f37c-d97d-4783-a026-9813df46b2fc) | | 390 | Dark | [recording](https://git.kayg.org/attachments/88f08832-46a5-43d8-bc8e-596c96db8060) | [stopped](https://git.kayg.org/attachments/a6e9cd82-fe9c-4fb4-aef3-361a95468642) | [attached](https://git.kayg.org/attachments/fb2f5770-6da7-44ca-8c68-87170fa8c4ef) | [saved](https://git.kayg.org/attachments/081de67a-18b6-4c89-93ca-b217ae272b76) | [row-3x](https://git.kayg.org/attachments/f54125e0-d3d3-4c61-9ca7-19bfa7527e07) | | 820 | Light | [recording](https://git.kayg.org/attachments/d4afc043-bdf5-4c7b-85db-dfaed4325e93) | [stopped](https://git.kayg.org/attachments/71cb2434-382b-4592-b4fc-7e7aa8b9a080) | [attached](https://git.kayg.org/attachments/06d7469e-7994-4f87-99af-87796b7d7049) | [saved](https://git.kayg.org/attachments/9cbbf3ee-0d10-4fee-960d-638595eee49f) | [row-3x](https://git.kayg.org/attachments/e1049fc6-248c-418a-ae29-34c7a2d3b401) | | 820 | Dark | [recording](https://git.kayg.org/attachments/b48268d7-2e96-4676-af89-c97cc39a05d5) | [stopped](https://git.kayg.org/attachments/2a29fcde-6c49-4ab0-a7e4-285897b31193) | [attached](https://git.kayg.org/attachments/7cadf61f-e660-4090-938f-d39b0605187d) | [saved](https://git.kayg.org/attachments/8554055d-5d04-4ed9-bf70-4467af41975b) | [row-3x](https://git.kayg.org/attachments/48ff5161-0f36-48ed-b76e-94dc1fbf1a05) | | 1440 | Light | [recording](https://git.kayg.org/attachments/ce63a4ed-8529-471e-8a24-4e256a215d5b) | [stopped](https://git.kayg.org/attachments/3c5d0a61-2c9e-478b-a16d-2ed75b9fcdc2) | [attached](https://git.kayg.org/attachments/eb6cb8d4-ba73-4cd1-8c83-abeee296c6a0) | [saved](https://git.kayg.org/attachments/cd815159-97f1-4cc7-a589-057fefac6dad) | [row-3x](https://git.kayg.org/attachments/981cd7ee-e6ba-4e5a-b6b8-989c8e64acc2) | | 1440 | Dark | [recording](https://git.kayg.org/attachments/6a170743-20dd-4fb5-a3b1-8b0d32c50cfb) | [stopped](https://git.kayg.org/attachments/df2b2aa6-6764-4316-9651-0fcbdd331fd4) | [attached](https://git.kayg.org/attachments/37cc47bb-da53-44ac-919f-5be5e97ab4e1) | [saved](https://git.kayg.org/attachments/bb598a83-8359-4eef-83e8-52541551f1f0) | [row-3x](https://git.kayg.org/attachments/51870a68-d45f-4d42-a34e-cd1d99bb60a7) | Morph strip: [five-frame strip](https://git.kayg.org/attachments/393f94b0-a9f8-4ea5-9497-017924e79cde). ## Gate output Production build completed with exit 0. Its final output was: ```text ✓ built in 35.64s > Using @sveltejs/adapter-static Wrote site to "build" ✔ done ``` The server build was needed because `/mnt/hdd/targets/shared-server/CURRENT` was absent: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 91m 27s ``` `bun run check`: ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` Focused component test, verbatim: ```text RUN v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Test Files 1 passed (1) Tests 4 passed (4) Start at 13:43:27 Duration 9.06s (transform 74%, environment 15%, import 6%, setup 4%, tests 1%) Transform |component| transforming modules took 6.62s · 74% of tracked time, re-done on every run persist transforms across runs with fsModuleCache: true learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns ``` Production VoicePill e2e output, verbatim: ```text PASS VoicePill 390 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 390 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 820 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 820 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 1440 light: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill 1440 dark: moving recording, stopped, attached, durable playable Journal item and 3x crop PASS VoicePill focused controls: morph, Stop, Undo, Discard, keyboard Note save and control containment composer voice pill e2e: record, attach, Undo, discard, Note save and retained-file retry passed; screenshots in /home/kayg/Developer/calternal-wt/voicepill-617/artifacts/voicepill-617-round2 CSP REPORTS composer: 0 across 7 pages ``` Cleanup output: ```text Removed 7237 files, 4.6GiB total ``` No Rust source changed, so Rust format, clippy and test gates were not applicable. `node --check e2e/composer.mjs` and `git diff --check` passed. ## UX gaps closed The circle outlines no longer meet the capsule outline. Stop and Discard stay separated, vertically concentric, and touchable at phone, tablet and desktop sizes. The responsive screenshot set covers light and dark themes. ## Known gaps and UX gaps left No VoicePill-specific gaps remain. Vite printed existing `use client` directive notices from vendored analytics files; the production build completed. ## Decisions DESIGN §38 does not set a precise control inset. I used `--island-pad` as the inset and reduced the inherited pill radius by the same value, as the review requested. I refit the shell width to preserve the existing shared waveform gap after the controls became smaller. ## For the merge round Run the full web Vitest and combined e2e suites under the merge-round policy. This job ran the focused VoicePill component test and production e2e only.
Author
Owner

Starting send-back round 3 on branch job/voicepill-617. Base is origin/dev merge base f06679b11c; current HEAD is e4f2a8f007. I will correct the production pill geometry and extend its responsive E2E proof to all requested widths, themes and DPRs.

Starting send-back round 3 on branch job/voicepill-617. Base is origin/dev merge base f06679b11cde29cc0b7120fdab5f721389caf695; current HEAD is e4f2a8f007d60d4f9600d0c146ba6083b2a43e66. I will correct the production pill geometry and extend its responsive E2E proof to all requested widths, themes and DPRs.
Author
Owner

Finding: the existing responsive loop calls verifyVoiceControlBounds for 390/820/1440 in both themes, but the proof only measures waveform bars relative to their own lane. It never measures the first bar from the capsule's leading edge, so the 1440 dark crop can visibly touch the border while passing. The width matrix also samples only 3x/2x/1x once each, not both requested DPRs.

Finding: the existing responsive loop calls verifyVoiceControlBounds for 390/820/1440 in both themes, but the proof only measures waveform bars relative to their own lane. It never measures the first bar from the capsule's leading edge, so the 1440 dark crop can visibly touch the border while passing. The width matrix also samples only 3x/2x/1x once each, not both requested DPRs.
Author
Owner

Finding: the strengthened recording-capsule assertion passed the complete responsive matrix at 390/820/1440, light/dark, 1x/2x DPR (12 production cases). The subsequent focused interaction flow sampled the pill 300 ms after recording began and failed an old whole-control containment check while the shell was still moving. I am making that probe wait for the width/morph transitions to settle; this does not add a production loading wait.

Finding: the strengthened recording-capsule assertion passed the complete responsive matrix at 390/820/1440, light/dark, 1x/2x DPR (12 production cases). The subsequent focused interaction flow sampled the pill 300 ms after recording began and failed an old whole-control containment check while the shell was still moving. I am making that probe wait for the width/morph transitions to settle; this does not add a production loading wait.
Author
Owner

Round 3 complete for #617.

Built

  • VoicePill keeps the shared token gap fixed and lets the waveform lane shrink before the timer or action controls. This keeps the waveform and Discard inside the capsule when fractional widths round short.
  • The production E2E proof measures the first waveform bar and Discard against the vertical token with a 0.5 px tolerance. It runs at 390, 820 and 1440 px, in light and dark, at 1x and 2x DPR. The geometry probe waits for its CSS motion to finish before reading bounds.
  • The final run measured 3 px on all three edges at 390/820, and 2 px at 1440, for both themes and DPRs.

Files

  • packages/ui/src/components/VoicePill.svelte
  • apps/web/e2e/composer.mjs

Screenshots

The 12 fresh 3x row crops are in artifacts/voicepill-617-round3/ with names voicepill-{390|820|1440}-{1x|2x}-{light|dark}-row-3x.png. They are ignored artifacts and are not committed. The installed fj v0.6.0 has no issue attachment command, so I could not upload them to Forgejo; the shared worktree path is available for review.

UX gaps closed

  • The leading waveform and trailing Discard now keep the same inset as the vertical controls at every tested size and density.
  • The interaction proof no longer reads containment during the pill morph.

UX gaps left

  • The screenshot files are not attached to Forgejo; see the worktree path above. No other gap was found in this scope.

Decisions

  • DESIGN §38 sets the VoicePill behavior and shared tokens but does not choose a layout strategy for fractional-width shortfalls. I kept a fixed token gap and made the waveform lane the flexible child, preserving control size and edge insets.

Head SHA

1488cbedf31bb5aa111d02189bf3821c29ddb061

Gates and evidence

cargo fmt --check exited 0 with no stdout or stderr. No Rust crate changed, so Rust clippy and unit-test gates were not applicable.

bun run check output:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bunx vitest run src/lib/composer/VoicePill.svelte.test.ts --maxWorkers=2 output:

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web

 Test Files  1 passed (1)
      Tests  4 passed (4)
   Start at  15:29:44
   Duration  17.15s (transform 72%, environment 15%, import 7%, setup 4%, tests 2%)

  Transform  |component| transforming modules took 12.21s · 72% of tracked time, re-done on every run
             persist transforms across runs with fsModuleCache: true
             learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns

The production VoicePill E2E matrix passed all 12 combinations. Each geometry line logged equal leading, vertical and trailing insets: 3 px at 390/820 and 2 px at 1440. The focused control flow also passed (morph, Stop, Undo, Discard, keyboard Note save and retained-file retry). CSP reports: 0 across 13 pages.

The full web suite remains for the merge round under the shared verification policy.

Round 3 complete for #617. **Built** - VoicePill keeps the shared token gap fixed and lets the waveform lane shrink before the timer or action controls. This keeps the waveform and Discard inside the capsule when fractional widths round short. - The production E2E proof measures the first waveform bar and Discard against the vertical token with a 0.5 px tolerance. It runs at 390, 820 and 1440 px, in light and dark, at 1x and 2x DPR. The geometry probe waits for its CSS motion to finish before reading bounds. - The final run measured 3 px on all three edges at 390/820, and 2 px at 1440, for both themes and DPRs. **Files** - `packages/ui/src/components/VoicePill.svelte` - `apps/web/e2e/composer.mjs` **Screenshots** The 12 fresh 3x row crops are in `artifacts/voicepill-617-round3/` with names `voicepill-{390|820|1440}-{1x|2x}-{light|dark}-row-3x.png`. They are ignored artifacts and are not committed. The installed `fj v0.6.0` has no issue attachment command, so I could not upload them to Forgejo; the shared worktree path is available for review. **UX gaps closed** - The leading waveform and trailing Discard now keep the same inset as the vertical controls at every tested size and density. - The interaction proof no longer reads containment during the pill morph. **UX gaps left** - The screenshot files are not attached to Forgejo; see the worktree path above. No other gap was found in this scope. **Decisions** - DESIGN §38 sets the VoicePill behavior and shared tokens but does not choose a layout strategy for fractional-width shortfalls. I kept a fixed token gap and made the waveform lane the flexible child, preserving control size and edge insets. **Head SHA** `1488cbedf31bb5aa111d02189bf3821c29ddb061` **Gates and evidence** `cargo fmt --check` exited 0 with no stdout or stderr. No Rust crate changed, so Rust clippy and unit-test gates were not applicable. `bun run check` output: ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bunx vitest run src/lib/composer/VoicePill.svelte.test.ts --maxWorkers=2` output: ```text RUN v5.0.1 /home/kayg/Developer/calternal-wt/voicepill-617/apps/web Test Files 1 passed (1) Tests 4 passed (4) Start at 15:29:44 Duration 17.15s (transform 72%, environment 15%, import 7%, setup 4%, tests 2%) Transform |component| transforming modules took 12.21s · 72% of tracked time, re-done on every run persist transforms across runs with fsModuleCache: true learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns ``` The production VoicePill E2E matrix passed all 12 combinations. Each geometry line logged equal leading, vertical and trailing insets: 3 px at 390/820 and 2 px at 1440. The focused control flow also passed (morph, Stop, Undo, Discard, keyboard Note save and retained-file retry). CSP reports: 0 across 13 pages. The full web suite remains for the merge round under the shared verification policy.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#617
No description provided.