UI break-it sweep: overlaps, broken states, errors, edge cases across every screen #117

Closed
opened 2026-09-25 20:24:53 +00:00 by kayg · 12 comments
Owner

Owner (2026-09-26 night): 'aggressively try to break the functionality you implement to find out weird spots like text overlaps, broken states, errors, edge cases'. A dedicated sweep of the merged dev build: every route and overlay at 320/390/768/1024/1440/1920 widths, 100/200% zoom, --ui-scale extremes, both themes plus 4 more, touch and mouse, keyboard only; hostile content (very long names, RTL, emoji, zero-width, huge counts, empty states), network failures (offline, 500s, slow responses), rapid interactions (double submits, racing navigation, back/forward during loads), auth edge states (expired session mid-action). Every finding becomes its own Forgejo issue with a screenshot and repro, or a fix with a regression check where no other job owns the area.

Owner (2026-09-26 night): 'aggressively try to break the functionality you implement to find out weird spots like text overlaps, broken states, errors, edge cases'. A dedicated sweep of the merged dev build: every route and overlay at 320/390/768/1024/1440/1920 widths, 100/200% zoom, --ui-scale extremes, both themes plus 4 more, touch and mouse, keyboard only; hostile content (very long names, RTL, emoji, zero-width, huge counts, empty states), network failures (offline, 500s, slow responses), rapid interactions (double submits, racing navigation, back/forward during loads), auth edge states (expired session mid-action). Every finding becomes its own Forgejo issue with a screenshot and repro, or a fix with a regression check where no other job owns the area.
Author
Owner

Started the break-it sweep on branch job/breakit-fixes at 494bf76, based on dev 8400bf2cd9. I am reviewing the 12 existing commits and runner before running web gates and the fresh-server sweep.

Started the break-it sweep on branch job/breakit-fixes at 494bf76, based on dev 8400bf2cd99d3165071e8de8a5f8a59ff261c978. I am reviewing the 12 existing commits and runner before running web gates and the fresh-server sweep.
Author
Owner

Starting ui-batch from job/ui-batch at base SHA 7c1d6c82ea98ac3772117a534fbb923825a60bab (current dev tip). I read CLAUDE.md, CONTEXT.md, and DESIGN.md sections 28, 33, 34, and 38. I am fixing the assigned UI findings with per-issue regression coverage and will report gates and head SHA here when complete.

Starting ui-batch from `job/ui-batch` at base SHA `7c1d6c82ea98ac3772117a534fbb923825a60bab` (current `dev` tip). I read CLAUDE.md, CONTEXT.md, and DESIGN.md sections 28, 33, 34, and 38. I am fixing the assigned UI findings with per-issue regression coverage and will report gates and head SHA here when complete.
Author
Owner

Break-it coverage finding: the existing viewport matrix covered normal browser scale and the simulated 200% zoom, but never set --ui-scale, even though DESIGN §34 defines the 1.0 and 1.15 extremes and this issue asks for scale extremes. I added both values across the six standard widths for the main views, and the sweep now checks the computed custom property before auditing each page. node --check apps/web/e2e/breakit.mjs passed; the full run is pending against the fresh merged build.

Break-it coverage finding: the existing viewport matrix covered normal browser scale and the simulated 200% zoom, but never set `--ui-scale`, even though DESIGN §34 defines the 1.0 and 1.15 extremes and this issue asks for scale extremes. I added both values across the six standard widths for the main views, and the sweep now checks the computed custom property before auditing each page. `node --check apps/web/e2e/breakit.mjs` passed; the full run is pending against the fresh merged build.
Author
Owner

Second sweep-coverage gap: the original 200% zoom profiles covered only 1440 px and 1920 px windows. I added the half-size CSS viewport and DPR 2 profiles for 320, 390, 768, and 1024 px too, so the paper route matrix now runs at both zoom levels for every requested width. The run is pending.

Second sweep-coverage gap: the original 200% zoom profiles covered only 1440 px and 1920 px windows. I added the half-size CSS viewport and DPR 2 profiles for 320, 390, 768, and 1024 px too, so the paper route matrix now runs at both zoom levels for every requested width. The run is pending.
Author
Owner

Runner finding from the fresh build: member setup returned HTTP 400 Invalid request because its fixture display name contained U+202E, which the committed auth hardening refuses. No UI screen ran before the failure. The member fixture now uses valid long Unicode text, and the shared passkey helper reports the failing auth stage and response message instead of dereferencing a missing publicKey. The adversarial display-name probe still checks bidi rejection.

Runner finding from the fresh build: member setup returned HTTP 400 `Invalid request` because its fixture display name contained U+202E, which the committed auth hardening refuses. No UI screen ran before the failure. The member fixture now uses valid long Unicode text, and the shared passkey helper reports the failing auth stage and response message instead of dereferencing a missing `publicKey`. The adversarial display-name probe still checks bidi rejection.
Author
Owner

Fresh-server runner finding: after readyz returned, the break-it harness waited a fixed 300 ms and then read whatever setup-token bytes had arrived. The next setup request returned HTTP 401 Invalid or expired token, before the UI matrix. The harness now waits for both readiness and a complete token log field; startup error output redacts the token. The server endpoint itself accepted the generated token in a separate local probe.

Fresh-server runner finding: after readyz returned, the break-it harness waited a fixed 300 ms and then read whatever setup-token bytes had arrived. The next setup request returned HTTP 401 `Invalid or expired token`, before the UI matrix. The harness now waits for both readiness and a complete token log field; startup error output redacts the token. The server endpoint itself accepted the generated token in a separate local probe.
Author
Owner

The targeted 404 check now passes at 390px and 1440px with 0 findings. It also verifies the app’s “This page does not exist” empty state. I fixed the fresh-server harness to skip waiting for a setup token when it reuses an already registered --keep dataset; before that fix, /readyz was 200 but startup timed out with setup token=false. I also narrowed the console-error allowance to an expected SvelteKit Not found route, while keeping a DOM assertion for the route empty state. Commits: f13a2fa, f7a7480. The 404 screenshots and report are under /home/kayg/Developer/calternal/target/breakit/cont-breakit-117/verify-404/. Starting the full sweep next.

The targeted 404 check now passes at 390px and 1440px with 0 findings. It also verifies the app’s “This page does not exist” empty state. I fixed the fresh-server harness to skip waiting for a setup token when it reuses an already registered `--keep` dataset; before that fix, `/readyz` was 200 but startup timed out with `setup token=false`. I also narrowed the console-error allowance to an expected SvelteKit `Not found` route, while keeping a DOM assertion for the route empty state. Commits: `f13a2fa`, `f7a7480`. The 404 screenshots and report are under `/home/kayg/Developer/calternal/target/breakit/cont-breakit-117/verify-404/`. Starting the full sweep next.
Author
Owner

I interrupted the first unfiltered pass after 68 screens. It showed that deliberately malformed and missing inputs returned expected API 400/404 responses, which the harness incorrectly counted as browser console failures. I changed the collector to allow only expected 4xx responses and to fail on every 5xx. Targeted reruns passed: files-missing 2/2, files-traversal 2/2, search-hostile 2/2 (the latter has only contrast findings). The partial report stays in /home/kayg/Developer/calternal/target/breakit/cont-breakit-117/full/; the corrected full pass will use a separate output directory. Commit 817b8a3.

I interrupted the first unfiltered pass after 68 screens. It showed that deliberately malformed and missing inputs returned expected API 400/404 responses, which the harness incorrectly counted as browser console failures. I changed the collector to allow only expected 4xx responses and to fail on every 5xx. Targeted reruns passed: `files-missing` 2/2, `files-traversal` 2/2, `search-hostile` 2/2 (the latter has only contrast findings). The partial report stays in `/home/kayg/Developer/calternal/target/breakit/cont-breakit-117/full/`; the corrected full pass will use a separate output directory. Commit `817b8a3`.
Author
Owner

The corrected unfiltered run is progressing through the viewport matrix. Current report-only findings dedupe to existing #134 (calendar text contrast) and #136 (page-load CLS), plus one distinct Search UI contrast issue filed as #153 with measurement screenshots. The same findings are recorded in /home/kayg/Developer/calternal/target/breakit/cont-breakit-117/full-final/report.json; the pass is still running.

The corrected unfiltered run is progressing through the viewport matrix. Current report-only findings dedupe to existing #134 (calendar text contrast) and #136 (page-load CLS), plus one distinct Search UI contrast issue filed as #153 with measurement screenshots. The same findings are recorded in `/home/kayg/Developer/calternal/target/breakit/cont-breakit-117/full-final/report.json`; the pass is still running.
Author
Owner

The 200% zoom matrix reproduces the global minimum-width issue at both 320px and 390px physical widths: each becomes a 160px/195px CSS viewport, while the page remains 320px wide due to layout.css. I filed this as #155 with route screenshots. Search muted-text contrast is filed as #153. Neither is being changed in this branch: the zoom minimum is a whole-app layout decision, and the search findings are outside the search-indexing/UI work areas allowed for a local fix.

The 200% zoom matrix reproduces the global minimum-width issue at both 320px and 390px physical widths: each becomes a 160px/195px CSS viewport, while the page remains 320px wide due to `layout.css`. I filed this as #155 with route screenshots. Search muted-text contrast is filed as #153. Neither is being changed in this branch: the zoom minimum is a whole-app layout decision, and the search findings are outside the search-indexing/UI work areas allowed for a local fix.
Author
Owner

Merged into dev and deployed to calternal.kayg.org at 6440a5f (gates: fmt, svelte-check 0 errors, 398 web tests, clippy, workspace tests all pass).

Merged into dev and deployed to calternal.kayg.org at 6440a5f (gates: fmt, svelte-check 0 errors, 398 web tests, clippy, workspace tests all pass).
kayg closed this issue 2026-09-26 10:25:37 +00:00
Author
Owner

ui-batch complete

Branch: job/ui-batch (merged dev before the final gates; no push or deploy).
Head: 1799c134c15b8889348ebb88c0a82d75b3f9d23d.

Child issues

All requested child issues were fixed, commented on with evidence, and closed.

  • #128 — Fixed. Files E2E checked exact repeated spaces in a filename in the Files row and Quick Look; Share E2E checked the public list and gallery caption.
  • #129 — Fixed. Share E2E checked a long public filename does not widen the page and that non-image gallery tiles show names.
  • #130 — Fixed. Notifications E2E checked the desktop inbox stays within the viewport with the bell in the sidebar and that /notifications opens and closes to the calendar.
  • #131 — Fixed. Files E2E checked the Kind column yields at 1024px with the sidebar open and the page fits the viewport.
  • #133 — Fixed. Notes E2E checked a wide Markdown table scrolls inside its wrapper while the phone document stays within the viewport.
  • #135 — Fixed. Composer E2E checked a long line and long tag chip stay inside the phone composer; the tag label ellipsizes and the page stays within 390px.
  • #136 — Fixed. Layout sweep enforced CLS < 0.05 and measured Settings admin at 0.000 and note at 0.009; Share E2E checked public-link CLS < 0.05.
  • #137 — Fixed. Layout sweep's offline probe opened an unloaded mode link with the browser offline and checked the real offline state and explanation.
  • #138 — Fixed. Layout sweep checked the app 404 state on phone and desktop and followed the Files recovery link.
  • #139 — Fixed. Files E2E checked encoded ../../etc shows the folder error without breadcrumbs, file rows, or item/quota summary.
  • #140 — Fixed. API client tests checked GET sharing, caller abort behavior, staggered reads, and invalidation; Files E2E checked duplicate requests.

Files

  • apps/web/e2e/composer.mjs
  • apps/web/e2e/files.mjs
  • apps/web/e2e/layout-sweep.mjs
  • apps/web/e2e/notes.mjs
  • apps/web/e2e/notifications.mjs
  • apps/web/e2e/share.mjs
  • apps/web/src/hooks.client.ts
  • apps/web/src/lib/api/notes.ts
  • apps/web/src/lib/components/LinkResolveState.svelte
  • apps/web/src/lib/composer/Composer.svelte
  • apps/web/src/lib/files/FilesBrowser.svelte
  • apps/web/src/lib/files/PublicLinkPage.svelte
  • apps/web/src/lib/files/model.test.ts
  • apps/web/src/lib/files/model.ts
  • apps/web/src/lib/navigation/clientRouteStatus.svelte.ts
  • apps/web/src/lib/notes/api.ts
  • apps/web/src/lib/search/SavedSearchList.svelte
  • apps/web/src/lib/search/saved.svelte.ts
  • apps/web/src/routes/+error.svelte
  • apps/web/src/routes/+layout.svelte
  • apps/web/src/routes/[...path]/+page.svelte
  • packages/api-client/src/index.test.ts
  • packages/api-client/src/index.ts
  • packages/ui/src/components/files/FileCollection.svelte
  • packages/ui/src/components/files/FileName.svelte
  • packages/ui/src/components/viewer/QuickLook.svelte

Gates

bun run check:

svelte-check found 0 errors and 0 warnings

bun run test:

 Test Files  53 passed (53)
      Tests  418 passed (418)

bun run build:

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

cargo fmt --check: no output; exit 0.

cargo clippy --all-targets -- -D warnings:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 22s

cargo test (workspace exit 0; final doc-test output excerpt):

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

Layout sweep:

checked 180 screens
CLS settings-admin-cold: 0.000
CLS note-cold: 0.009
CLS note: 0.009
CLS admin-configuration: 0.000
WARN settings-account: list "Signed-in devices": 2 rows share an icon
WARN settings-ai: list "Agents": 2 rows share an icon
LAYOUT SWEEP PASSED

Production E2E completion markers:

FILES E2E PASSED
search e2e: ok
SHARE E2E PASSED
composer e2e: all flows passed; screenshots in /home/kayg/Developer/calternal-wt/ui-batch/apps/web/../../target/ui-batch-composer
NOTIFICATIONS E2E PASSED
notes e2e: ok

API-client focused tests:

 7 pass
 0 fail

cargo clean completed:

     Removed 17533 files, 13.6GiB total

Web build output was removed after verification.

Decisions and known gaps

  • docs/DESIGN.md does not set recovery links for a 404. The app error state reuses LinkResolveState and offers Today and Files as stable destinations.
  • docs/DESIGN.md does not specify the message for a failed client-side route chunk. The persistent app shell shows an offline state with a reconnect-and-retry explanation; it does not add offline editing.
  • The request deduper shares a successful, cacheable GET snapshot for 750ms after completion so shell and page readers mounting in adjacent turns can share it. Writes clear snapshots. Each caller gets its own response and abort signal. This closes a timing gap beyond strictly in-flight sharing.
  • The layout sweep has two non-blocking same-icon warnings: Signed-in devices and Agents. The Notes E2E used the shared Playwright 1.63 module explicitly because this checkout has no tests/adversarial/node_modules/playwright install.

Six screenshots from the production build are attached in the job conversation and retained at artifacts/ui-batch-review/ for visual review. The visual review remains with the orchestrator.

# ui-batch complete Branch: `job/ui-batch` (merged `dev` before the final gates; no push or deploy). Head: `1799c134c15b8889348ebb88c0a82d75b3f9d23d`. ## Child issues All requested child issues were fixed, commented on with evidence, and closed. - #128 — Fixed. Files E2E checked exact repeated spaces in a filename in the Files row and Quick Look; Share E2E checked the public list and gallery caption. - #129 — Fixed. Share E2E checked a long public filename does not widen the page and that non-image gallery tiles show names. - #130 — Fixed. Notifications E2E checked the desktop inbox stays within the viewport with the bell in the sidebar and that `/notifications` opens and closes to the calendar. - #131 — Fixed. Files E2E checked the Kind column yields at 1024px with the sidebar open and the page fits the viewport. - #133 — Fixed. Notes E2E checked a wide Markdown table scrolls inside its wrapper while the phone document stays within the viewport. - #135 — Fixed. Composer E2E checked a long line and long tag chip stay inside the phone composer; the tag label ellipsizes and the page stays within 390px. - #136 — Fixed. Layout sweep enforced CLS < 0.05 and measured Settings admin at 0.000 and note at 0.009; Share E2E checked public-link CLS < 0.05. - #137 — Fixed. Layout sweep's offline probe opened an unloaded mode link with the browser offline and checked the real offline state and explanation. - #138 — Fixed. Layout sweep checked the app 404 state on phone and desktop and followed the Files recovery link. - #139 — Fixed. Files E2E checked encoded `../../etc` shows the folder error without breadcrumbs, file rows, or item/quota summary. - #140 — Fixed. API client tests checked GET sharing, caller abort behavior, staggered reads, and invalidation; Files E2E checked duplicate requests. ## Files - `apps/web/e2e/composer.mjs` - `apps/web/e2e/files.mjs` - `apps/web/e2e/layout-sweep.mjs` - `apps/web/e2e/notes.mjs` - `apps/web/e2e/notifications.mjs` - `apps/web/e2e/share.mjs` - `apps/web/src/hooks.client.ts` - `apps/web/src/lib/api/notes.ts` - `apps/web/src/lib/components/LinkResolveState.svelte` - `apps/web/src/lib/composer/Composer.svelte` - `apps/web/src/lib/files/FilesBrowser.svelte` - `apps/web/src/lib/files/PublicLinkPage.svelte` - `apps/web/src/lib/files/model.test.ts` - `apps/web/src/lib/files/model.ts` - `apps/web/src/lib/navigation/clientRouteStatus.svelte.ts` - `apps/web/src/lib/notes/api.ts` - `apps/web/src/lib/search/SavedSearchList.svelte` - `apps/web/src/lib/search/saved.svelte.ts` - `apps/web/src/routes/+error.svelte` - `apps/web/src/routes/+layout.svelte` - `apps/web/src/routes/[...path]/+page.svelte` - `packages/api-client/src/index.test.ts` - `packages/api-client/src/index.ts` - `packages/ui/src/components/files/FileCollection.svelte` - `packages/ui/src/components/files/FileName.svelte` - `packages/ui/src/components/viewer/QuickLook.svelte` ## Gates `bun run check`: ```text svelte-check found 0 errors and 0 warnings ``` `bun run test`: ```text Test Files 53 passed (53) Tests 418 passed (418) ``` `bun run build`: ```text ✓ built in 16.43s > Using @sveltejs/adapter-static Wrote site to "build" ✔ done ``` `cargo fmt --check`: no output; exit 0. `cargo clippy --all-targets -- -D warnings`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 22s ``` `cargo test` (workspace exit 0; final doc-test output excerpt): ```text test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` Layout sweep: ```text checked 180 screens CLS settings-admin-cold: 0.000 CLS note-cold: 0.009 CLS note: 0.009 CLS admin-configuration: 0.000 WARN settings-account: list "Signed-in devices": 2 rows share an icon WARN settings-ai: list "Agents": 2 rows share an icon LAYOUT SWEEP PASSED ``` Production E2E completion markers: ```text FILES E2E PASSED search e2e: ok SHARE E2E PASSED composer e2e: all flows passed; screenshots in /home/kayg/Developer/calternal-wt/ui-batch/apps/web/../../target/ui-batch-composer NOTIFICATIONS E2E PASSED notes e2e: ok ``` API-client focused tests: ```text 7 pass 0 fail ``` `cargo clean` completed: ```text Removed 17533 files, 13.6GiB total ``` Web build output was removed after verification. ## Decisions and known gaps - `docs/DESIGN.md` does not set recovery links for a 404. The app error state reuses `LinkResolveState` and offers Today and Files as stable destinations. - `docs/DESIGN.md` does not specify the message for a failed client-side route chunk. The persistent app shell shows an offline state with a reconnect-and-retry explanation; it does not add offline editing. - The request deduper shares a successful, cacheable GET snapshot for 750ms after completion so shell and page readers mounting in adjacent turns can share it. Writes clear snapshots. Each caller gets its own response and abort signal. This closes a timing gap beyond strictly in-flight sharing. - The layout sweep has two non-blocking same-icon warnings: Signed-in devices and Agents. The Notes E2E used the shared Playwright 1.63 module explicitly because this checkout has no `tests/adversarial/node_modules/playwright` install. Six screenshots from the production build are attached in the job conversation and retained at `artifacts/ui-batch-review/` for visual review. The visual review remains with the orchestrator.
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#117
No description provided.