PERF: one revision cache and strong ETag/304 contract for UI reads (#663) #665

Open
opened 2026-10-02 05:20:39 +00:00 by kayg · 5 comments
Owner

Context: #663 instant-interaction audit. No product change is part of the audit job.

Own rule 3 and the shared read contract in DESIGN §58. This issue is the only owner of the revision cache and ETag/304 machinery; Tab adoption is separate.

Evidence at c4a61e8cf0:

  • apps/web/src/lib/files/api.ts:41 caches first pages by path/visibility and time, not body revision or bytes.
  • apps/web/src/lib/search/SearchPreview.svelte:3 keeps 80 entries by result ID, with no revision in the key.
  • apps/web/src/lib/money/api.ts:18 requests no-store reads.
  • Files thumbnails already implement conditional reads (crates/plugins/files/src/lib.rs:3648); reuse that behavior. Notes detail GET (crates/plugins/notes/src/lib.rs:3568) returns a JSON ETag field, not a shared conditional body-cache contract.

Expected:

  • One typed cache keyed by User ID, stable item ID, revision and representation variant. Variants include zone, query/filter and remote-content policy when they alter bytes. Bound bytes, entries, pending reads and neighbour prefetch. LRU eviction releases large bodies.
  • Strong ETags describe the exact representation; If-None-Match returns 304 with no body. Authorize before 304. Share revoke, plugin disable, sign-out, 401 and User switch clear or invalidate retained data. Reuse userStorage #555 rather than add browser storage. Keep secrets and raw Admin configuration out of persistent caches.
  • A current cached open is synchronous, makes zero requests and does not publish a new model. Deduplicate one revalidation per key; obsolete generations cannot publish. Return unchanged objects by identity.
  • Define a bounded offline read state within DESIGN §15; do not add offline editing.

Tests:

  • Cached open has zero requests and stable identity; changed revision misses; representation variants never alias; byte/entry caps hold after a 500-step run.
  • Real-server ETag/304, mutation invalidation, share revocation and two-User/session-switch isolation. Retain existing conditional-write tests unchanged.
  • Extend #641 and #549 profiles, production/HDD/locked VM, ≥5 cold and warm samples, median/p95/max, CPU/RSS, cached open ≤100 ms.

Reuse and dependencies: #555 userStorage; #549 route caches; #641 harness; #640 Mail cache work and #642 Settings resources. Audit those branch results before implementation. Do not take ownership of their layouts, traversal handlers or benchmarks.

Measurement evidence: see the production/HDD matrix posted on #663. Latencies of sampled endpoints do not prove the shared contract is complete.

Locked production measurement evidence:
Nine representative list/security endpoints were checked in five successful serial requests each (Notes uses the corrected phase). None returned an HTTP ETag: 0/9 sampled endpoint types. Files list returned 2,756 bytes; Notes 12,660 bytes; Calendar about 258k bytes. These are sampled responses, not a universal absence proof. The source paths above establish the missing shared contract separately.
Runtime server source cc25c441b7, binary SHA-256 2f3567d91c34839851247bc0acbc25a56aaacd14dca269b8f0342ddf83447ed9, shared release binary with embedded production SPA. The source audit uses the newer c4a61e8 base; no code equivalence claim. Perf VM HDD emulator uses bench/hdd-emu.sh (direct-I/O/ext4, 8 ms read/write delay, 200 IOPS and 150 MiB/s caps) and flock -w 14400 /root/perf.lock for every measured phase. First valid qualification QD1 115.3 IOPS/8.028 ms median, QD16 200.7 IOPS/96.993 ms; load 0.10/0.18/0.08 and 0.20/0.20/0.09. Fixture: 366 Daily notes, 10,980 Logs, 100 Files/Photos, 20 Notes/Tasks, three Budgets and 100 transactions; Mail empty and Admin one User. No matching transport/HDD/fixture baseline exists, so no regression ratio is claimed. Full raw scopes, failure retention and endpoint baseline references are on #663.

Context: #663 instant-interaction audit. No product change is part of the audit job. Own rule 3 and the shared read contract in DESIGN §58. This issue is the only owner of the revision cache and ETag/304 machinery; Tab adoption is separate. Evidence at c4a61e8cf090170f35b1bed3350d9de20c83ecd5: - `apps/web/src/lib/files/api.ts:41` caches first pages by path/visibility and time, not body revision or bytes. - `apps/web/src/lib/search/SearchPreview.svelte:3` keeps 80 entries by result ID, with no revision in the key. - `apps/web/src/lib/money/api.ts:18` requests no-store reads. - Files thumbnails already implement conditional reads (`crates/plugins/files/src/lib.rs:3648`); reuse that behavior. Notes detail GET (`crates/plugins/notes/src/lib.rs:3568`) returns a JSON ETag field, not a shared conditional body-cache contract. Expected: - One typed cache keyed by User ID, stable item ID, revision and representation variant. Variants include zone, query/filter and remote-content policy when they alter bytes. Bound bytes, entries, pending reads and neighbour prefetch. LRU eviction releases large bodies. - Strong ETags describe the exact representation; If-None-Match returns 304 with no body. Authorize before 304. Share revoke, plugin disable, sign-out, 401 and User switch clear or invalidate retained data. Reuse userStorage #555 rather than add browser storage. Keep secrets and raw Admin configuration out of persistent caches. - A current cached open is synchronous, makes zero requests and does not publish a new model. Deduplicate one revalidation per key; obsolete generations cannot publish. Return unchanged objects by identity. - Define a bounded offline read state within DESIGN §15; do not add offline editing. Tests: - Cached open has zero requests and stable identity; changed revision misses; representation variants never alias; byte/entry caps hold after a 500-step run. - Real-server ETag/304, mutation invalidation, share revocation and two-User/session-switch isolation. Retain existing conditional-write tests unchanged. - Extend #641 and #549 profiles, production/HDD/locked VM, ≥5 cold and warm samples, median/p95/max, CPU/RSS, cached open ≤100 ms. Reuse and dependencies: #555 userStorage; #549 route caches; #641 harness; #640 Mail cache work and #642 Settings resources. Audit those branch results before implementation. Do not take ownership of their layouts, traversal handlers or benchmarks. Measurement evidence: see the production/HDD matrix posted on #663. Latencies of sampled endpoints do not prove the shared contract is complete. Locked production measurement evidence: Nine representative list/security endpoints were checked in five successful serial requests each (Notes uses the corrected phase). None returned an HTTP ETag: 0/9 sampled endpoint types. Files list returned 2,756 bytes; Notes 12,660 bytes; Calendar about 258k bytes. These are sampled responses, not a universal absence proof. The source paths above establish the missing shared contract separately. Runtime server source cc25c441b7a974185622a1dee853cf38686d2b67, binary SHA-256 2f3567d91c34839851247bc0acbc25a56aaacd14dca269b8f0342ddf83447ed9, shared release binary with embedded production SPA. The source audit uses the newer c4a61e8 base; no code equivalence claim. Perf VM HDD emulator uses bench/hdd-emu.sh (direct-I/O/ext4, 8 ms read/write delay, 200 IOPS and 150 MiB/s caps) and flock -w 14400 /root/perf.lock for every measured phase. First valid qualification QD1 115.3 IOPS/8.028 ms median, QD16 200.7 IOPS/96.993 ms; load 0.10/0.18/0.08 and 0.20/0.20/0.09. Fixture: 366 Daily notes, 10,980 Logs, 100 Files/Photos, 20 Notes/Tasks, three Budgets and 100 transactions; Mail empty and Admin one User. No matching transport/HDD/fixture baseline exists, so no regression ratio is claimed. Full raw scopes, failure retention and endpoint baseline references are on #663.
Author
Owner

Starting #665 on job/perf-cache-665, based on c4a61e8cf090170f35b1bed3350d9de20c83ecd5 (origin/dev). I will bring the missing #663 §58 decision commits from job/instant-663, then build the shared revision cache and strong ETag/304 contract with a single proving adoption. I will audit existing #555 userStorage, #549 route cache, and #641 benchmark work before changing files. No other mode adoption is in scope.

Starting #665 on `job/perf-cache-665`, based on `c4a61e8cf090170f35b1bed3350d9de20c83ecd5` (`origin/dev`). I will bring the missing #663 §58 decision commits from `job/instant-663`, then build the shared revision cache and strong ETag/304 contract with a single proving adoption. I will audit existing #555 userStorage, #549 route cache, and #641 benchmark work before changing files. No other mode adoption is in scope.
Author
Owner

Finding: GET /api/v1/notes/{id} currently returns a JSON etag field but no HTTP ETag or conditional 304 contract (crates/plugins/notes/src/lib.rs, get_note). That field hashes the Markdown source, so it does not cover the full JSON representation when identity/path-derived fields change. I added calternal-plugin::http_cache::conditional_json to hash the exact serialized bytes and return an empty 304. The Notes handler resolves the User and Note before checking the validator. Per-User browser storage already exists in apps/web/src/lib/userStorage.ts; this work uses bounded in-memory entries and does not add persistent storage.

Finding: `GET /api/v1/notes/{id}` currently returns a JSON `etag` field but no HTTP `ETag` or conditional 304 contract (`crates/plugins/notes/src/lib.rs`, `get_note`). That field hashes the Markdown source, so it does not cover the full JSON representation when identity/path-derived fields change. I added `calternal-plugin::http_cache::conditional_json` to hash the exact serialized bytes and return an empty 304. The Notes handler resolves the User and Note before checking the validator. Per-User browser storage already exists in `apps/web/src/lib/userStorage.ts`; this work uses bounded in-memory entries and does not add persistent storage.
Author
Owner

The production Notes E2E reached the new #665 screenshots but theme verification showed html[data-mode] stayed system after setting paper mode. The shared E2E theme seeder gated its in-place update on a #app selector that the current app does not use. Updated the gate to .app-frame, the mounted route shell, so both persisted storage and the live document receive the requested theme.

The production Notes E2E reached the new #665 screenshots but theme verification showed `html[data-mode]` stayed `system` after setting paper mode. The shared E2E theme seeder gated its in-place update on a `#app` selector that the current app does not use. Updated the gate to `.app-frame`, the mounted route shell, so both persisted storage and the live document receive the requested theme.
Author
Owner

The cache invalidation listener removed shared entries on calternal:access-changed, but an already-mounted NoteView still held its parsed body in component state. While offline, that left the private body visible after access was revoked. NoteView now clears and reloads on shared invalidation signals, and clears its body immediately on session end/expiry. The real-server browser regression dispatches the access-change event while offline, confirms the body disappears, then confirms the online retry fetches a fresh 200. The same run passed the cross-User ETag 404, malformed and 8 KiB validators, Unicode and encoded-path IDs, and cached/no-cache offline flows.

The cache invalidation listener removed shared entries on `calternal:access-changed`, but an already-mounted NoteView still held its parsed body in component state. While offline, that left the private body visible after access was revoked. NoteView now clears and reloads on shared invalidation signals, and clears its body immediately on session end/expiry. The real-server browser regression dispatches the access-change event while offline, confirms the body disappears, then confirms the online retry fetches a fresh 200. The same run passed the cross-User ETag 404, malformed and 8 KiB validators, Unicode and encoded-path IDs, and cached/no-cache offline flows.
Author
Owner

Issue #665 final report

Branch: job/perf-cache-665
Head: b88bc6ac888fd18e7e8a256f0b5b65ecaeed92c2

Built

  • Added a shared authenticated JSON response helper that hashes the exact serialized body with BLAKE3, emits a strong ETag, accepts weak/list/wildcard If-None-Match validators, returns an empty 304, and sets Cache-Control: private, no-store.
  • Added conditional reads to GET /api/v1/notes/{id}. The handler resolves the current User and Note access before evaluating the validator. OpenAPI and the generated API client now describe 200/304 and ETag.
  • Added the bounded in-memory revision cache and adopted it in Notes as the proving mode. Keys include User, stable Note ID, representation variant and revision. Loads coalesce, stale completions are rejected, and invalidation aborts pending reads.
  • Note detail opens now reuse a warm representation immediately. Offline cached Notes are complete and read-only, with an explicit retry when online. A cold offline open shows an error and retry action. Access, session, User, plugin and Note change events clear cached and rendered private bodies.
  • Added adversarial route checks for owner 200/304, empty 304, cross-User owner-validator denial (404), malformed and 8 KiB validators, Unicode IDs and encoded /etc/passwd.
  • Added bench/revision-cache-665.mjs and stored the five-sample VM result in docs/perf/runs/2026-10-02T075227Z-1a20b4726-revision-cache-665.json.

Files

CLAUDE.md; Cargo.lock; apps/web/e2e/harness.mjs; apps/web/e2e/notes.mjs; apps/web/src/lib/api/revision-cache.ts; apps/web/src/lib/api/revision-cache.test.ts; apps/web/src/lib/files/api.ts; apps/web/src/lib/notes/NoteView.svelte; apps/web/src/lib/notes/api.ts; apps/web/src/lib/notes/noteIndex.svelte.ts; apps/web/src/lib/notes/revision-cache.test.ts; bench/revision-cache-665.mjs; contracts/openapi.json; crates/calternal-plugin/Cargo.toml; crates/calternal-plugin/src/http_cache.rs; crates/calternal-plugin/src/lib.rs; crates/plugins/notes/src/lib.rs; docs/DESIGN.md; docs/perf/runs/2026-10-02T075227Z-1a20b4726-revision-cache-665.json; packages/api-client/src/generated.ts.

UX gaps closed

  • Warm online opens show the cached Note before the live room joins; offline opens keep the complete body visible and prevent editing.
  • Retry works after reconnect, retains object identity on 304, and obtains a fresh 200 after an access reset. The UI drops the rendered body when access or session state changes.
  • The Notes production flow was captured at 390, 820 and 1440 px in light and dark for online, cached-offline and no-cache-offline states. The E2E run ended with notes revision cache e2e: ok and CSP REPORTS notes: 0 across 2 pages.

UX gaps left

  • Notes remain read-only while offline. Offline edits, mentions, reminders and live collaboration are unavailable until an explicit online retry; this follows the memory-only offline read decision in DESIGN §15.
  • An earlier separate NOTES_E2E_COLLAB_ONLY=1 run timed out waiting for .collaboration-carets__caret after opening the editor. The targeted #665 production E2E passed; that separate collaboration flow remains unverified here.

Decisions not specified before this change

  • The shared cache defaults are 128 entries, 8 MiB of response bodies, 16 pending reads and one prefetch. The benchmark exercises the 50 × 16 KiB average case and the full 128 × 64 KiB / 8 MiB bound with a 16-read burst.
  • Strong representation tags use BLAKE3 over the exact JSON bytes sent to the client. This includes identity and path fields, so a changed response representation changes its tag.
  • Offline Notes are memory-only, read-only and explicitly retried after reconnect. DESIGN §§15 and 58 now record this decision.

Performance

Measured on perf-test under /root/perf.lock, with five samples and the production web build (295 assets, 8,627,142 bytes). The lock-held load average was 1.61, 2.07, 1.86; the recorded before/after averages were 1.62, 2.08, 1.87. docs/perf/baseline.json has no #665 metric, so this run establishes the profile rather than claiming a baseline comparison.

  • Average, 50 Notes × 16 KiB: cache-load p50/p95 0.0023/0.0171 ms; warm read p50/p95 0.0005/0.0011 ms; CPU 0.0561 s; retained RSS 76,636,160 bytes.
  • Worst case, 128 Notes × 64 KiB (8 MiB), 16-read burst: cache-load p50/p95 0.0022/0.0090 ms; warm read p50/p95 0.0005/0.0009 ms; burst p50/p95 0.4076/1.3082 ms; CPU 0.079 s; retained RSS 76,898,304 bytes.

Gates and evidence

The output excerpts below are verbatim. cargo fmt --check exited 0 with no output.

cargo clippy -p calternal-plugin --all-targets -- -D warnings
    Checking calternal-plugin v0.0.1 (/home/kayg/Developer/calternal-wt/perf-cache-665/crates/calternal-plugin)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 45.36s

cargo test -p calternal-plugin
test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.33s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings
    Blocking waiting for file lock on package cache
    Checking calternal-plugin-notes v0.0.1 (/home/kayg/Developer/calternal-wt/perf-cache-665/crates/plugins/notes)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 47s

cargo test -p calternal-plugin-notes
thread 'tests::daily_and_composer_preserve_unrelated_bytes' (3721393) panicked at crates/plugins/notes/src/lib.rs:9508:9:
assertion `left == right` failed
  left: 404
 right: 200
test result: FAILED. 167 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 106.13s

cargo test -p calternal-plugin-notes daily_and_composer_preserve_unrelated_bytes
test tests::daily_and_composer_preserve_unrelated_bytes ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 167 filtered out; finished in 0.58s

cargo clippy -p calternal-server --all-targets -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 10m 24s

cargo test -p calternal-server
test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 20.17s

bun run --cwd apps/web check
svelte-check found 0 errors and 0 warnings

bun run --cwd apps/web test
 Test Files  155 passed (155)
      Tests  1066 passed (1066)
   Start at  10:28:53
   Duration  107.70s (transform 49%, environment 19%, import 17%, tests 12%, setup 4%)

The Notes full-suite 404 occurred in the pre-existing Journal test only under the suite run; its isolated rerun passed. No test expectation was changed. cargo clean removed 16,897 files / 9.1 GiB, and apps/web/build was removed after verification. The worktree is clean.

Screenshots attached

State Theme 390 px 820 px 1440 px
Online Paper 390 820 1440
Online Tokyo Night 390 820 1440
Cached offline Paper 390 820 1440
Cached offline Tokyo Night 390 820 1440
No-cache offline Paper 390 820 1440
No-cache offline Tokyo Night 390 820 1440

No push, deploy or merge was performed.

# Issue #665 final report Branch: `job/perf-cache-665` Head: `b88bc6ac888fd18e7e8a256f0b5b65ecaeed92c2` ## Built - Added a shared authenticated JSON response helper that hashes the exact serialized body with BLAKE3, emits a strong `ETag`, accepts weak/list/wildcard `If-None-Match` validators, returns an empty `304`, and sets `Cache-Control: private, no-store`. - Added conditional reads to `GET /api/v1/notes/{id}`. The handler resolves the current User and Note access before evaluating the validator. OpenAPI and the generated API client now describe `200`/`304` and `ETag`. - Added the bounded in-memory revision cache and adopted it in Notes as the proving mode. Keys include User, stable Note ID, representation variant and revision. Loads coalesce, stale completions are rejected, and invalidation aborts pending reads. - Note detail opens now reuse a warm representation immediately. Offline cached Notes are complete and read-only, with an explicit retry when online. A cold offline open shows an error and retry action. Access, session, User, plugin and Note change events clear cached and rendered private bodies. - Added adversarial route checks for owner `200`/`304`, empty `304`, cross-User owner-validator denial (`404`), malformed and 8 KiB validators, Unicode IDs and encoded `/etc/passwd`. - Added `bench/revision-cache-665.mjs` and stored the five-sample VM result in `docs/perf/runs/2026-10-02T075227Z-1a20b4726-revision-cache-665.json`. ## Files `CLAUDE.md`; `Cargo.lock`; `apps/web/e2e/harness.mjs`; `apps/web/e2e/notes.mjs`; `apps/web/src/lib/api/revision-cache.ts`; `apps/web/src/lib/api/revision-cache.test.ts`; `apps/web/src/lib/files/api.ts`; `apps/web/src/lib/notes/NoteView.svelte`; `apps/web/src/lib/notes/api.ts`; `apps/web/src/lib/notes/noteIndex.svelte.ts`; `apps/web/src/lib/notes/revision-cache.test.ts`; `bench/revision-cache-665.mjs`; `contracts/openapi.json`; `crates/calternal-plugin/Cargo.toml`; `crates/calternal-plugin/src/http_cache.rs`; `crates/calternal-plugin/src/lib.rs`; `crates/plugins/notes/src/lib.rs`; `docs/DESIGN.md`; `docs/perf/runs/2026-10-02T075227Z-1a20b4726-revision-cache-665.json`; `packages/api-client/src/generated.ts`. ## UX gaps closed - Warm online opens show the cached Note before the live room joins; offline opens keep the complete body visible and prevent editing. - Retry works after reconnect, retains object identity on `304`, and obtains a fresh `200` after an access reset. The UI drops the rendered body when access or session state changes. - The Notes production flow was captured at 390, 820 and 1440 px in light and dark for online, cached-offline and no-cache-offline states. The E2E run ended with `notes revision cache e2e: ok` and `CSP REPORTS notes: 0 across 2 pages`. ## UX gaps left - Notes remain read-only while offline. Offline edits, mentions, reminders and live collaboration are unavailable until an explicit online retry; this follows the memory-only offline read decision in DESIGN §15. - An earlier separate `NOTES_E2E_COLLAB_ONLY=1` run timed out waiting for `.collaboration-carets__caret` after opening the editor. The targeted #665 production E2E passed; that separate collaboration flow remains unverified here. ## Decisions not specified before this change - The shared cache defaults are 128 entries, 8 MiB of response bodies, 16 pending reads and one prefetch. The benchmark exercises the 50 × 16 KiB average case and the full 128 × 64 KiB / 8 MiB bound with a 16-read burst. - Strong representation tags use BLAKE3 over the exact JSON bytes sent to the client. This includes identity and path fields, so a changed response representation changes its tag. - Offline Notes are memory-only, read-only and explicitly retried after reconnect. DESIGN §§15 and 58 now record this decision. ## Performance Measured on `perf-test` under `/root/perf.lock`, with five samples and the production web build (295 assets, 8,627,142 bytes). The lock-held load average was 1.61, 2.07, 1.86; the recorded before/after averages were 1.62, 2.08, 1.87. `docs/perf/baseline.json` has no #665 metric, so this run establishes the profile rather than claiming a baseline comparison. - Average, 50 Notes × 16 KiB: cache-load p50/p95 0.0023/0.0171 ms; warm read p50/p95 0.0005/0.0011 ms; CPU 0.0561 s; retained RSS 76,636,160 bytes. - Worst case, 128 Notes × 64 KiB (8 MiB), 16-read burst: cache-load p50/p95 0.0022/0.0090 ms; warm read p50/p95 0.0005/0.0009 ms; burst p50/p95 0.4076/1.3082 ms; CPU 0.079 s; retained RSS 76,898,304 bytes. ## Gates and evidence The output excerpts below are verbatim. `cargo fmt --check` exited 0 with no output. ```text cargo clippy -p calternal-plugin --all-targets -- -D warnings Checking calternal-plugin v0.0.1 (/home/kayg/Developer/calternal-wt/perf-cache-665/crates/calternal-plugin) Finished `dev` profile [unoptimized + debuginfo] target(s) in 45.36s cargo test -p calternal-plugin test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.33s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings Blocking waiting for file lock on package cache Checking calternal-plugin-notes v0.0.1 (/home/kayg/Developer/calternal-wt/perf-cache-665/crates/plugins/notes) Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 47s cargo test -p calternal-plugin-notes thread 'tests::daily_and_composer_preserve_unrelated_bytes' (3721393) panicked at crates/plugins/notes/src/lib.rs:9508:9: assertion `left == right` failed left: 404 right: 200 test result: FAILED. 167 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 106.13s cargo test -p calternal-plugin-notes daily_and_composer_preserve_unrelated_bytes test tests::daily_and_composer_preserve_unrelated_bytes ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 167 filtered out; finished in 0.58s cargo clippy -p calternal-server --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 10m 24s cargo test -p calternal-server test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 20.17s bun run --cwd apps/web check svelte-check found 0 errors and 0 warnings bun run --cwd apps/web test Test Files 155 passed (155) Tests 1066 passed (1066) Start at 10:28:53 Duration 107.70s (transform 49%, environment 19%, import 17%, tests 12%, setup 4%) ``` The Notes full-suite 404 occurred in the pre-existing Journal test only under the suite run; its isolated rerun passed. No test expectation was changed. `cargo clean` removed 16,897 files / 9.1 GiB, and `apps/web/build` was removed after verification. The worktree is clean. ## Screenshots attached | State | Theme | 390 px | 820 px | 1440 px | | --- | --- | --- | --- | --- | | Online | Paper | [390](https://git.kayg.org/attachments/587df0d6-203f-4710-a256-a7ee0105ae51) | [820](https://git.kayg.org/attachments/bcefa14c-e18a-4fba-a498-927906c69403) | [1440](https://git.kayg.org/attachments/354200d2-e4a4-4039-a06e-d865a119bca2) | | Online | Tokyo Night | [390](https://git.kayg.org/attachments/eb72c25b-d9d7-4b50-9392-c5248e71ce26) | [820](https://git.kayg.org/attachments/0bf9f109-b7cd-462d-8f58-69de5d8ce9bf) | [1440](https://git.kayg.org/attachments/15743ef5-fc9b-45c0-874b-ddbc1db8de29) | | Cached offline | Paper | [390](https://git.kayg.org/attachments/0129fd20-f085-4bbe-9fba-10e0b672a751) | [820](https://git.kayg.org/attachments/e843c13e-ea2f-47c1-af5f-b4900161ec4b) | [1440](https://git.kayg.org/attachments/a1133a06-9103-412c-bcd3-81969789588d) | | Cached offline | Tokyo Night | [390](https://git.kayg.org/attachments/ed790143-d6b0-4c74-bff1-d552eb56c271) | [820](https://git.kayg.org/attachments/05281341-b335-4226-8002-3c7561ac9369) | [1440](https://git.kayg.org/attachments/b946534d-b1e1-42ae-8f97-5187449bd746) | | No-cache offline | Paper | [390](https://git.kayg.org/attachments/959c58c3-5057-4ff3-9733-17c2da0a5e4c) | [820](https://git.kayg.org/attachments/987c3e18-1aa5-48cb-97dd-f3ceab0325a9) | [1440](https://git.kayg.org/attachments/b0b994ac-c776-4ff5-8aa4-43407dacc870) | | No-cache offline | Tokyo Night | [390](https://git.kayg.org/attachments/5abac086-496a-40f0-9331-e718e4489858) | [820](https://git.kayg.org/attachments/0bce63bb-0356-499e-997d-2fc84a934f46) | [1440](https://git.kayg.org/attachments/ae2b485d-c326-4404-aba1-4b3f4937cfa9) | No push, deploy or merge was performed.
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#665
No description provided.