Audit: real empty, error and loading states on every screen (no fake data) #869

Open
opened 2026-10-02 16:47:42 +00:00 by kayg · 20 comments
Owner

CLAUDE.md 'No fake data' and the owner's UX-completeness rule: every screen shows real data or a real empty state; every failure has a real error state with a way forward; loading never waits for motion and shows cached content at once. For every route and every list/panel component, check: empty state exists and is not sample data; error state exists (network down, 403, 404 deep link, 5xx) with plain words; loading state does not block on animation. Also check touch: no hover-only affordances, 44 px targets for row actions. File issues per screen group with file:line.

Read-only job (LIGHT class)

This job runs at the lowest CPU priority on a shared, overloaded build host. Do not build or test: no cargo build/test/clippy/check, no bun install/build/test/run check, no servers, no browsers. Use rg, sed, git, scripts/fj, and reading. Write findings to audit-findings.md at the worktree root as you go (append per finding) and commit it on your branch. For each real defect: search existing issues first (scripts/fj --host https://git.kayg.org issue search --repo kayg/calternal "<terms>"), then either add evidence to the existing issue or create one self-contained issue (title, evidence with file:line, owner rule or DESIGN section, expected behaviour, test idea) that a later build job can own. Group findings that share one fix into one issue (one owner per shared fix). No product code changes. Post a summary with the list of issues on your own issue. Docs use ASD-STE100 Simplified Technical English.

CLAUDE.md 'No fake data' and the owner's UX-completeness rule: every screen shows real data or a real empty state; every failure has a real error state with a way forward; loading never waits for motion and shows cached content at once. For every route and every list/panel component, check: empty state exists and is not sample data; error state exists (network down, 403, 404 deep link, 5xx) with plain words; loading state does not block on animation. Also check touch: no hover-only affordances, 44 px targets for row actions. File issues per screen group with file:line. ## Read-only job (LIGHT class) This job runs at the lowest CPU priority on a shared, overloaded build host. **Do not build or test**: no `cargo build/test/clippy/check`, no `bun install/build/test/run check`, no servers, no browsers. Use `rg`, `sed`, `git`, `scripts/fj`, and reading. Write findings to `audit-findings.md` at the worktree root as you go (append per finding) and commit it on your branch. For each real defect: search existing issues first (`scripts/fj --host https://git.kayg.org issue search --repo kayg/calternal "<terms>"`), then either add evidence to the existing issue or create one self-contained issue (title, evidence with file:line, owner rule or DESIGN section, expected behaviour, test idea) that a later build job can own. Group findings that share one fix into one issue (one owner per shared fix). No product code changes. Post a summary with the list of issues on your own issue. Docs use ASD-STE100 Simplified Technical English.
Author
Owner

Started the #869 audit on branch job/states-audit, based on 440e19dce23040ac8ebaae88f0469b6535b1afcb (origin/dev). I am mapping routes and shared list/panel components, then I will record file-and-line evidence and check for existing issues before filing grouped findings. This is a read-only audit; no product code or tests will be changed or run.

Started the #869 audit on branch `job/states-audit`, based on `440e19dce23040ac8ebaae88f0469b6535b1afcb` (`origin/dev`). I am mapping routes and shared list/panel components, then I will record file-and-line evidence and check for existing issues before filing grouped findings. This is a read-only audit; no product code or tests will be changed or run.
Author
Owner

Audit result

Completed a static source audit of route and panel loading, empty and error states. No product code changed. The findings are in audit-findings.md and are filed here:

Files and branch

  • File: audit-findings.md
  • Branch: job/states-audit
  • Head: b3975244d12a7775229ac9de2e2053da154a5c08
  • git fetch origin && git merge origin/dev: Already up to date.

Gates and cleanup

The read-only job instruction prohibited builds, tests, checks, servers and browsers. Cargo and Bun gates were not run.

Cargo cleanup output:

Removed 1 file, 356B total

UX gaps left

The filed issues describe the missing or misleading states. I did not implement fixes or run interactive checks, screenshots or responsive visual review because this job prohibited app builds, servers and browsers.

Decisions

  • I treated only successful empty responses as empty states. Transient list failures need an error state and a recovery action.
  • I left the Money landing page without a loading state because DESIGN.md §48 F5 explicitly says it has no loading state.
  • I added the Analytics and Composer lazy-chunk evidence to #423 because it already tracks missing chunk recovery.
## Audit result Completed a static source audit of route and panel loading, empty and error states. No product code changed. The findings are in `audit-findings.md` and are filed here: - New issues: #871, #872, #873, #874, #875, #876, #877, #883, #885, #886, #887, #892 and #912. - Added related evidence to #423 for the Analytics and Composer lazy imports. Added Notes sidebar tree evidence to #872. ## Files and branch - File: `audit-findings.md` - Branch: `job/states-audit` - Head: `b3975244d12a7775229ac9de2e2053da154a5c08` - `git fetch origin && git merge origin/dev`: `Already up to date.` ## Gates and cleanup The read-only job instruction prohibited builds, tests, checks, servers and browsers. Cargo and Bun gates were not run. Cargo cleanup output: ```text Removed 1 file, 356B total ``` ## UX gaps left The filed issues describe the missing or misleading states. I did not implement fixes or run interactive checks, screenshots or responsive visual review because this job prohibited app builds, servers and browsers. ## Decisions - I treated only successful empty responses as empty states. Transient list failures need an error state and a recovery action. - I left the Money landing page without a loading state because DESIGN.md §48 F5 explicitly says it has no loading state. - I added the Analytics and Composer lazy-chunk evidence to #423 because it already tracks missing chunk recovery.
Author
Owner

Started issue #869 on branch job/errstates at base 440e19dce2. I am reading the listed screen issues and existing shared UI states before implementation.

Started issue #869 on branch job/errstates at base 440e19dce23040ac8ebaae88f0469b6535b1afcb. I am reading the listed screen issues and existing shared UI states before implementation.
Author
Owner

Finding for #883: PhotoTimelineData.#run caught a failed day request and then immediately called #pump; because the day stayed eligible, an offline API could be hit repeatedly while the timeline kept showing skeletons. The day now enters a failed set that excludes it from automatic requests. A Retry clears only that day and starts one new request. Regression coverage: photos-units.test.ts confirms the failed day remains idle until Retry and then loads successfully. Commit: 04883295f.

Finding for #883: `PhotoTimelineData.#run` caught a failed day request and then immediately called `#pump`; because the day stayed eligible, an offline API could be hit repeatedly while the timeline kept showing skeletons. The day now enters a failed set that excludes it from automatic requests. A Retry clears only that day and starts one new request. Regression coverage: `photos-units.test.ts` confirms the failed day remains idle until Retry and then loads successfully. Commit: `04883295f`.
Author
Owner

Found a screenshot harness cleanup issue: Playwright did not remove an API route when received a newly-created predicate function, so earlier failure intercepts could remain registered between scenarios. The runner now reuses the same matcher for registration and removal before the full screenshot pass.

Found a screenshot harness cleanup issue: Playwright did not remove an API route when received a newly-created predicate function, so earlier failure intercepts could remain registered between scenarios. The runner now reuses the same matcher for registration and removal before the full screenshot pass.
Author
Owner

Correction to the preceding evidence comment: the route cleanup method is Playwright's unroute call. It received a newly created predicate, so it did not remove the registered handler. The runner now reuses the same matcher for registration and removal. The fix is committed as c5aece961.

Correction to the preceding evidence comment: the route cleanup method is Playwright's unroute call. It received a newly created predicate, so it did not remove the registered handler. The runner now reuses the same matcher for registration and removal. The fix is committed as c5aece961.
Author
Owner

Review of the screenshot runner showed that generic surfaces waited for any alert, without checking that the expected error copy appeared. The runner now checks the exact user-facing alert on each surface before saving a screenshot or activating Retry. Commit: 7fd0e7acc.

Review of the screenshot runner showed that generic surfaces waited for any alert, without checking that the expected error copy appeared. The runner now checks the exact user-facing alert on each surface before saving a screenshot or activating Retry. Commit: 7fd0e7acc.
Author
Owner

The Inspector error screenshot initially had no loaded version row, so it could not verify the #887 action target. The runner now creates one replaced Files fixture through the real API; the first Versions read fails, Retry reaches the local server, and the test checks the restored version action carries the shared touch-hit target. Commit: e04517b5d.

The Inspector error screenshot initially had no loaded version row, so it could not verify the #887 action target. The runner now creates one replaced Files fixture through the real API; the first Versions read fails, Retry reaches the local server, and the test checks the restored version action carries the shared touch-hit target. Commit: e04517b5d.
Author
Owner

The focused production screenshot run saw two GET failures for /api/v1/files/recent before any Retry action (the runner counted 2 where it expected 1). I am checking whether Recent starts the same load twice and will keep the error state calm with a regression check.

The focused production screenshot run saw two GET failures for /api/v1/files/recent before any Retry action (the runner counted 2 where it expected 1). I am checking whether Recent starts the same load twice and will keep the error state calm with a regression check.
Author
Owner

Correction to the Recent finding: the second read is a valid live-file refresh. Recent subscribes to Files changes, and the real upload fixture's pending SSE event schedules one debounced refresh after 300 ms. It is not an automatic retry. The screenshot runner now lets that refresh settle, checks the error stays calm, and verifies Retry adds exactly one request. Commit: 07063a0e0.

Correction to the Recent finding: the second read is a valid live-file refresh. Recent subscribes to Files changes, and the real upload fixture's pending SSE event schedules one debounced refresh after 300 ms. It is not an automatic retry. The screenshot runner now lets that refresh settle, checks the error stays calm, and verifies Retry adds exactly one request. Commit: 07063a0e0.
Author
Owner

The Inspector screenshot fixture replaced its file immediately after upload, and the local server returned 503 with Index is busy; retry shortly. The runner now retries only that known response, with five total attempts and bounded backoff; other failures still stop the run. Commit: eb831093b.

The Inspector screenshot fixture replaced its file immediately after upload, and the local server returned `503` with `Index is busy; retry shortly`. The runner now retries only that known response, with five total attempts and bounded backoff; other failures still stop the run. Commit: eb831093b.
Author
Owner

The full screenshot run reached the Money month case, but its deep link redirected to /calendar/today/2026-10-03 because new Users start with Money disabled. The runner now enables Money through the real per-User Plugin API before navigating, while still mocking only the failing month read. Commit: 70f560425.

The full screenshot run reached the Money month case, but its deep link redirected to `/calendar/today/2026-10-03` because new Users start with Money disabled. The runner now enables Money through the real per-User Plugin API before navigating, while still mocking only the failing month read. Commit: 70f560425.
Author
Owner

The focused Money screenshot run exposed a malformed injected API response: it used { code, message } at the top level, while the generated API contract requires { error: { code, message } }. The client therefore showed its fallback Request failed (500) instead of the plain-language Service unavailable message. I corrected the shared screenshot failure fixture to use the documented ErrorEnvelope; the Money surfaces already display the server's plain-language message and a Retry action. I am rerunning both Money captures against that fixture.

The focused Money screenshot run exposed a malformed injected API response: it used `{ code, message }` at the top level, while the generated API contract requires `{ error: { code, message } }`. The client therefore showed its fallback `Request failed (500)` instead of the plain-language `Service unavailable` message. I corrected the shared screenshot failure fixture to use the documented `ErrorEnvelope`; the Money surfaces already display the server's plain-language message and a Retry action. I am rerunning both Money captures against that fixture.
Author
Owner

The first full screenshot matrix caught a delayed real Files change event in the Files Recent fixture. Its refresh increased the request count after the previous 800 ms wait, so the runner mistook the event-driven refresh for an automatic retry. I added a bounded 5-second quiet-window check: one event refresh may settle, while continued requests fail the check. The focused Files Recent run then passed all six phone/tablet/desktop and light/dark combinations, including a working manual Retry.

The first full screenshot matrix caught a delayed real Files change event in the Files Recent fixture. Its refresh increased the request count after the previous 800 ms wait, so the runner mistook the event-driven refresh for an automatic retry. I added a bounded 5-second quiet-window check: one event refresh may settle, while continued requests fail the check. The focused Files Recent run then passed all six phone/tablet/desktop and light/dark combinations, including a working manual Retry.
Author
Owner

The full run reached the Tags sidebar error but found its alert inside the closed phone navigation sheet. The DOM showed the alert text, while Playwright correctly reported zero visible alert matches because the sheet is inert until opened. The runner now opens the phone sidebar for Tags and Saved searches as it already did for Mail; the focused Tags sidebar phone/light capture passed with the alert visible and Retry checked.

The full run reached the Tags sidebar error but found its alert inside the closed phone navigation sheet. The DOM showed the alert text, while Playwright correctly reported zero visible alert matches because the sheet is inert until opened. The runner now opens the phone sidebar for Tags and Saved searches as it already did for Mail; the focused Tags sidebar phone/light capture passed with the alert visible and Retry checked.
Author
Owner

The Search screenshot runner sent Meta+K as soon as DOMContentLoaded fired. At that point the page had no open dialogs or Search field because Search registers the shortcut from onMount; the key arrived before hydration. I added a short hydration settle before the shortcut and close the phone navigation sheet with Escape after its Retry check, so the next route starts without a modal focus trap. Focused Search and Saved searches phone/light runs both passed.

The Search screenshot runner sent Meta+K as soon as `DOMContentLoaded` fired. At that point the page had no open dialogs or Search field because Search registers the shortcut from `onMount`; the key arrived before hydration. I added a short hydration settle before the shortcut and close the phone navigation sheet with Escape after its Retry check, so the next route starts without a modal focus trap. Focused Search and Saved searches phone/light runs both passed.
Author
Owner

Correction to the prior Files Recent test note: a 500 ms quiet window still missed a later SSE replay in the complete run. The focused six-variant check now gives Files Recent a bounded 10-second window with 2 seconds of quiet before Retry, and records the request baseline only after it settles. All six phone/tablet/desktop and light/dark captures passed; the check still fails if requests keep repeating through the bound.

Correction to the prior Files Recent test note: a 500 ms quiet window still missed a later SSE replay in the complete run. The focused six-variant check now gives Files Recent a bounded 10-second window with 2 seconds of quiet before Retry, and records the request baseline only after it settles. All six phone/tablet/desktop and light/dark captures passed; the check still fails if requests keep repeating through the bound.
Author
Owner

The desktop-dark Search capture had a second query from the access-state refresh before the user pressed Retry, so a fixed total of two requests was too strict. The runner now waits for Search requests to settle, permits at most one startup refresh, and checks that manual Retry adds exactly one request. The focused Search run passed all six phone/tablet/desktop and light/dark combinations; continued automatic requests still fail the bounded quiet-window check.

The desktop-dark Search capture had a second query from the access-state refresh before the user pressed Retry, so a fixed total of two requests was too strict. The runner now waits for Search requests to settle, permits at most one startup refresh, and checks that manual Retry adds exactly one request. The focused Search run passed all six phone/tablet/desktop and light/dark combinations; continued automatic requests still fail the bounded quiet-window check.
Author
Owner

#869 complete

Branch: job/errstates
Head: 04e9642251693fdc06cae84dd512a22fe3dc7ed9

Built

Added the shared RequestState loading/error treatment and adopted it across Files Recent, Trash, Inspector sections and Public Link folders; Notes and mention sections; Journal; Tags, tag results and sidebar; Money month and register; Mail folders; Search results and preview; Photos day pages; Saved searches; and Ask settings. Errors stay distinct from real empty states, preserve stale content where available, announce through role=alert, and expose named Retry actions. Photos stops automatic retries after a failed day page and offers user-controlled Retry. Files Inspector version actions meet the 44 px touch target.

The focused component and surface tests cover the shared state, Mail, Journal, Search, mention cards and Photos. The production screenshot runner covers 18 screens (17 error states plus the real missing-budget state), all at 390, 820 and 1440 px, light and dark, with macOS platform emulation. It checks touch, pointer and keyboard Retry behavior.

Files

Shared UI: packages/ui/src/components/RequestState.svelte, packages/ui/src/index.ts, packages/ui/src/components/notes/NoteMentionsCard.svelte.

Adoption: apps/web/src/lib/components/app-sidebar.svelte; Files InfoPanel.svelte, PublicLinkPage.svelte, RecentView.svelte, TrashView.svelte; MailSidebar.svelte; Notes NoteView.svelte, NotesExplorer.svelte, route /notes; Journal route and JournalPage.svelte; Tags route, tag-detail route and sidebar; Search search-dialog.svelte, SearchPreview.svelte, SavedSearchList.svelte and its access/registry/window helpers; Photos PhotoTimeline.svelte, PhotosView.svelte, timeline.svelte.ts; Money month and register routes; Ask route; Settings status call sites now use the shared component.

Coverage: apps/web/e2e/request-state-869.mjs and focused tests for RequestState, MailSidebar, JournalPage, Search registry, NoteMentionsCard and Photos timeline.

UX gaps closed

Failures no longer look like empty lists or stay as endless skeletons. Retry is named, announced, keyboard operable and touch sized. Sidebar errors are visible when the phone sheet opens. Search distinguishes a failed query from no results. Public Link failures do not imply an empty folder. Inspector version actions meet the touch target.

UX gaps left

The 108 PNGs are in ignored artifacts/request-state-869/ and were not committed. scripts/fj issue comment --help has no attachment option, so they are not attached to this issue; they are available in the shared worktree for orchestrator visual review. Codex did not judge screenshot quality.

Decisions

RequestState lives in packages/ui because it is shared by app surfaces and the package-level mention card; it extends the existing Settings GroupStatus treatment. For Photos, a failed day stops automatic retries and waits for a User action, which is one of the options in #883. Surfaces use the API error envelope's plain-language message with a contextual title and Retry.

No Rust source or API contract changed. Rust gates were not run. No performance profile or measurement was run because #869 is a functional UI issue; the 2026-10-02 verification policy limits performance measurements to performance issues.

Gate output

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/errstates/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

Focused Vitest (--maxWorkers=2):

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

 Test Files  5 passed (5)
      Tests  16 passed (16)
   Start at  02:30:44
   Duration  16.60s (transform 64%, environment 17%, import 9%, tests 6%, setup 4%)

  Transform  |component| transforming modules took 19.82s · 64% 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 focused Photos test also passed (1 file, 9 tests). Screenshot run:

request state #869: 18 screens captured and Retry checked at phone, tablet, desktop in light and dark

node --check apps/web/e2e/request-state-869.mjs and git diff --check passed with no output. cargo clean:

     Removed 7237 files, 4.6GiB total

Full bun run test remains for the merge round, per the 2026-10-02 verification policy.

#869 complete Branch: `job/errstates` Head: `04e9642251693fdc06cae84dd512a22fe3dc7ed9` ## Built Added the shared `RequestState` loading/error treatment and adopted it across Files Recent, Trash, Inspector sections and Public Link folders; Notes and mention sections; Journal; Tags, tag results and sidebar; Money month and register; Mail folders; Search results and preview; Photos day pages; Saved searches; and Ask settings. Errors stay distinct from real empty states, preserve stale content where available, announce through `role=alert`, and expose named Retry actions. Photos stops automatic retries after a failed day page and offers user-controlled Retry. Files Inspector version actions meet the 44 px touch target. The focused component and surface tests cover the shared state, Mail, Journal, Search, mention cards and Photos. The production screenshot runner covers 18 screens (17 error states plus the real missing-budget state), all at 390, 820 and 1440 px, light and dark, with macOS platform emulation. It checks touch, pointer and keyboard Retry behavior. ## Files Shared UI: `packages/ui/src/components/RequestState.svelte`, `packages/ui/src/index.ts`, `packages/ui/src/components/notes/NoteMentionsCard.svelte`. Adoption: `apps/web/src/lib/components/app-sidebar.svelte`; Files `InfoPanel.svelte`, `PublicLinkPage.svelte`, `RecentView.svelte`, `TrashView.svelte`; `MailSidebar.svelte`; Notes `NoteView.svelte`, `NotesExplorer.svelte`, route `/notes`; Journal route and `JournalPage.svelte`; Tags route, tag-detail route and sidebar; Search `search-dialog.svelte`, `SearchPreview.svelte`, `SavedSearchList.svelte` and its access/registry/window helpers; Photos `PhotoTimeline.svelte`, `PhotosView.svelte`, `timeline.svelte.ts`; Money month and register routes; Ask route; Settings status call sites now use the shared component. Coverage: `apps/web/e2e/request-state-869.mjs` and focused tests for RequestState, MailSidebar, JournalPage, Search registry, NoteMentionsCard and Photos timeline. ## UX gaps closed Failures no longer look like empty lists or stay as endless skeletons. Retry is named, announced, keyboard operable and touch sized. Sidebar errors are visible when the phone sheet opens. Search distinguishes a failed query from no results. Public Link failures do not imply an empty folder. Inspector version actions meet the touch target. ## UX gaps left The 108 PNGs are in ignored `artifacts/request-state-869/` and were not committed. `scripts/fj issue comment --help` has no attachment option, so they are not attached to this issue; they are available in the shared worktree for orchestrator visual review. Codex did not judge screenshot quality. ## Decisions `RequestState` lives in `packages/ui` because it is shared by app surfaces and the package-level mention card; it extends the existing Settings `GroupStatus` treatment. For Photos, a failed day stops automatic retries and waits for a User action, which is one of the options in #883. Surfaces use the API error envelope's plain-language message with a contextual title and Retry. No Rust source or API contract changed. Rust gates were not run. No performance profile or measurement was run because #869 is a functional UI issue; the 2026-10-02 verification policy limits performance measurements to performance issues. ## Gate output `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/errstates/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` Focused Vitest (`--maxWorkers=2`): ```text RUN v5.0.1 /home/kayg/Developer/calternal-wt/errstates/apps/web Test Files 5 passed (5) Tests 16 passed (16) Start at 02:30:44 Duration 16.60s (transform 64%, environment 17%, import 9%, tests 6%, setup 4%) Transform |component| transforming modules took 19.82s · 64% 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 focused Photos test also passed (1 file, 9 tests). Screenshot run: ```text request state #869: 18 screens captured and Retry checked at phone, tablet, desktop in light and dark ``` `node --check apps/web/e2e/request-state-869.mjs` and `git diff --check` passed with no output. `cargo clean`: ```text Removed 7237 files, 4.6GiB total ``` Full `bun run test` remains for the merge round, per the 2026-10-02 verification policy.
Author
Owner

#869 nit fixed on job/errstates (not pushed).

Commits:

  • eeb2525fe fix(ui): give titled request failures a next step, not the title again
  • 14fcdfadc test(ui): capture request states from the current build

Change:

  • RequestState exports REQUEST_NEXT_STEP ("Check your connection, then try again."). Titled callers (Notes, Journal, Tags, Tag results, Photos, Money month, Money register) pass it as the message, or keep "Your session ended. Sign in again." when sign-in is the next step.
  • Files Recent, Trash and the Files folder error use cardErrorMessage() in lib/files/api.ts: only the generic "Could not … Try again." becomes the next step; specific causes (offline, permission, sign-in) stay.
  • Safety net: if a message still repeats its title, the component shows the next step instead.
  • Inline states without a title keep their "X could not be loaded." sentence (no heading to repeat).
  • e2e request-state-869.mjs now asserts the heading text is not repeated in the alert, and uses routeCurrentBuild so a prebuilt server shows the worktree build (CALTERNAL_E2E_ASSET_OVERRIDE=1).

Gates: bun run check 0 errors, 0 warnings (1991 files). Vitest: RequestState, JournalPage, files/api, MailSidebar, NoteMentionsCard: 5 files, 22 tests passed.
e2e (prebuilt merge-round-7a server, current assets): notes, files-trash, money-register surfaces passed at phone/tablet/desktop in light and dark. Screenshots: artifacts/request-state-869/{notes,files-trash,money-register}-*-mac.png in the worktree.

#869 nit fixed on `job/errstates` (not pushed). Commits: - `eeb2525fe` fix(ui): give titled request failures a next step, not the title again - `14fcdfadc` test(ui): capture request states from the current build Change: - `RequestState` exports `REQUEST_NEXT_STEP` ("Check your connection, then try again."). Titled callers (Notes, Journal, Tags, Tag results, Photos, Money month, Money register) pass it as the message, or keep "Your session ended. Sign in again." when sign-in is the next step. - Files Recent, Trash and the Files folder error use `cardErrorMessage()` in `lib/files/api.ts`: only the generic "Could not … Try again." becomes the next step; specific causes (offline, permission, sign-in) stay. - Safety net: if a message still repeats its title, the component shows the next step instead. - Inline states without a title keep their "X could not be loaded." sentence (no heading to repeat). - e2e `request-state-869.mjs` now asserts the heading text is not repeated in the alert, and uses `routeCurrentBuild` so a prebuilt server shows the worktree build (`CALTERNAL_E2E_ASSET_OVERRIDE=1`). Gates: `bun run check` 0 errors, 0 warnings (1991 files). Vitest: RequestState, JournalPage, files/api, MailSidebar, NoteMentionsCard: 5 files, 22 tests passed. e2e (prebuilt merge-round-7a server, current assets): notes, files-trash, money-register surfaces passed at phone/tablet/desktop in light and dark. Screenshots: `artifacts/request-state-869/{notes,files-trash,money-register}-*-mac.png` in the worktree.
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#869
No description provided.