PERF: Money adopts shared revision, snapshot, mutation and delta contracts (#663) #673

Open
opened 2026-10-02 05:36:17 +00:00 by kayg · 2 comments
Owner

Context: #663 matrix, DESIGN §58 rules 3–6. This is the Money adapter issue. The only shared owners are #665 revision cache/ETag, #666 view snapshots, #667 optimistic client IDs/Undo receipts and #668 change stream/deltas. Depend on their public contracts; do not implement competing primitives.

Evidence at c4a61e8cf0:

  • R3: GET /api/v1/money/budgets/{id}/accounts; apps/web/src/lib/money/api.ts:18. no-store reads; one last account answer retained, not revision-keyed register/body cache with conditional reads.
  • R4: PUT /api/v1/money/budgets/{id}/transactions/{transaction_id}/cleared; apps/web/src/routes/money/[budget]/accounts/[[account]]/+page.svelte:133. Waits for state write then full load. No durable receipt/client-ID inverse and synchronous cross-view selection update.
  • R5: GET /api/v1/money/budgets/{id}/accounts; apps/web/src/lib/money/store.svelte.ts:87. refresh re-reads account answers after local write; no cross-Installation changed-since stream/delta for register or Budget.
  • R6: GET /api/v1/money/budgets/{id}/accounts; apps/web/src/lib/money/store.svelte.ts:57. Budget switch sets accounts=null; one account set retained, no shared register/cursor/scroll LRU. Active #641 bounded neighbour work must be reused.

Expected:

  • Adopt #665 for list/detail revisions and header-complete rows. Cached open is synchronous, keeps object identity and makes zero requests. Authorize before conditional 304; User switch/revoke/plugin disable removes affected retained data.
  • Adopt #666 for a byte/row-bounded complete view snapshot. Restore before await, then one bounded catch-up; no blank/spinner over already-seen data. Deep-link intent and keyboard focus override a retained view when needed.
  • Adopt #667 for create/edit/delete/clear a transaction and change an Assignment; preserve exact integer arithmetic and all source bytes outside the edit; restore Budget/month/account/register selection. Changes publish to every visible/retained representation in one frame. Client IDs survive lost acknowledgement; Undo uses the durable receipt and cannot erase an intervening change. Definite rejection rolls back; timeout remains pending until receipt reconciliation. Do not add offline editing.
  • Adopt #668: one per-User wake-up, coalesced capped deltas, stable unchanged objects and deletion tombstones. Stop full-refetch fan-out and timer refreshes only after the delta covers their correctness duties.

Tests:

  • Production API/e2e for the listed actions and views, including stale 304, lost acknowledgement, duplicate ID, Undo after reconnect/restart, obsolete response, empty/error/offline state and cross-view consistency. Existing status/assertion expectations stay unchanged.
  • Two Users, session switch, share revoke and plugin disable cannot restore denied rows. Keep authorization/step-up and server-only writes through calternal-fs.
  • Pointer/touch (≥44 px), keyboard focus/Enter/Space/Escape, screen-reader name/role/state, Copy link and shortcuts work. Keyboard motion keeps shared durations (#611).
  • Extend #549/#641 profiles rather than create another harness. ≥5 locked production/HDD cold and warm samples; median/p95/max, CPU/RSS and large realistic data plus burst. Cached open ≤100 ms, accepted action ≤150 ms, warm return ≤100 ms, first usable view ≤1.5 s at 10k items. Warm blaze: zero incomplete frames; collect identity/unpainted/long-task/heap/RSS metrics.

Reuse and active work: #462 format/maths rules and active #641 job/blaze-surfaces account traversal/cancellation/prefetch. This issue adopts shared primitives after that work; no parallel replacement of its account navigation.

Measurement scope: the production/HDD read table on #663 supplies a representative endpoint result, not proof that a complete Money view meets all budgets. Rule 7/9 findings remain with #641/#549 and the existing surface jobs. This follow-up integrates their results and adds shared-contract acceptance after they land.

Representative measurement from the audit (not a full-view budget result):
/api/v1/money/budgets/{id}/accounts, five serial requests after fixture readiness, all HTTP 200. Median/p95/max 44.7/50.8/50.8 ms; five-request burst median/p95/max 45.1/45.4/45.4 ms. Serial window server CPU 80 ms, RSS 444309504 bytes; no ETag on these sampled responses. Load inside lock 3.66/2.1/0.93.
Shared release server source cc25c441b7a974185622a1dee853cf38686d2b67, binary SHA-256 2f3567d91c34839851247bc0acbc25a56aaacd14dca269b8f0342ddf83447ed9; embedded production SPA; Chromium browser on the build host through SSH/HTTPS. Server/Home/Index on perf VM HDD emulator: direct-I/O loop, 8 ms read/write dm-delay, 200 IOPS and 150 MiB/s caps. Every measured phase held flock -w 14400 /root/perf.lock. Qualification QD1 115.3 IOPS/8.028 ms median, QD16 200.7 IOPS/96.993 ms. Fixture: 366 Daily notes, 10,980 Logs, 100 Files/Photos, 20 Notes/Tasks, three Budgets and 100 transactions; Mail empty, Admin one User.
Structural source evidence above is the newer audit base, not the measured binary revision. No claim that these revisions are equivalent. The baseline in docs/perf/baseline.json uses another fixture/build/transport; no regression ratio is valid here. See #663 for matching baseline endpoint values and coverage gaps.

Context: #663 matrix, DESIGN §58 rules 3–6. This is the Money adapter issue. The only shared owners are #665 revision cache/ETag, #666 view snapshots, #667 optimistic client IDs/Undo receipts and #668 change stream/deltas. Depend on their public contracts; do not implement competing primitives. Evidence at c4a61e8cf090170f35b1bed3350d9de20c83ecd5: - R3: `GET /api/v1/money/budgets/{id}/accounts`; `apps/web/src/lib/money/api.ts:18`. no-store reads; one last account answer retained, not revision-keyed register/body cache with conditional reads. - R4: `PUT /api/v1/money/budgets/{id}/transactions/{transaction_id}/cleared`; `apps/web/src/routes/money/[budget]/accounts/[[account]]/+page.svelte:133`. Waits for state write then full load. No durable receipt/client-ID inverse and synchronous cross-view selection update. - R5: `GET /api/v1/money/budgets/{id}/accounts`; `apps/web/src/lib/money/store.svelte.ts:87`. refresh re-reads account answers after local write; no cross-Installation changed-since stream/delta for register or Budget. - R6: `GET /api/v1/money/budgets/{id}/accounts`; `apps/web/src/lib/money/store.svelte.ts:57`. Budget switch sets accounts=null; one account set retained, no shared register/cursor/scroll LRU. Active #641 bounded neighbour work must be reused. Expected: - Adopt #665 for list/detail revisions and header-complete rows. Cached open is synchronous, keeps object identity and makes zero requests. Authorize before conditional 304; User switch/revoke/plugin disable removes affected retained data. - Adopt #666 for a byte/row-bounded complete view snapshot. Restore before await, then one bounded catch-up; no blank/spinner over already-seen data. Deep-link intent and keyboard focus override a retained view when needed. - Adopt #667 for create/edit/delete/clear a transaction and change an Assignment; preserve exact integer arithmetic and all source bytes outside the edit; restore Budget/month/account/register selection. Changes publish to every visible/retained representation in one frame. Client IDs survive lost acknowledgement; Undo uses the durable receipt and cannot erase an intervening change. Definite rejection rolls back; timeout remains pending until receipt reconciliation. Do not add offline editing. - Adopt #668: one per-User wake-up, coalesced capped deltas, stable unchanged objects and deletion tombstones. Stop full-refetch fan-out and timer refreshes only after the delta covers their correctness duties. Tests: - Production API/e2e for the listed actions and views, including stale 304, lost acknowledgement, duplicate ID, Undo after reconnect/restart, obsolete response, empty/error/offline state and cross-view consistency. Existing status/assertion expectations stay unchanged. - Two Users, session switch, share revoke and plugin disable cannot restore denied rows. Keep authorization/step-up and server-only writes through calternal-fs. - Pointer/touch (≥44 px), keyboard focus/Enter/Space/Escape, screen-reader name/role/state, Copy link and shortcuts work. Keyboard motion keeps shared durations (#611). - Extend #549/#641 profiles rather than create another harness. ≥5 locked production/HDD cold and warm samples; median/p95/max, CPU/RSS and large realistic data plus burst. Cached open ≤100 ms, accepted action ≤150 ms, warm return ≤100 ms, first usable view ≤1.5 s at 10k items. Warm blaze: zero incomplete frames; collect identity/unpainted/long-task/heap/RSS metrics. Reuse and active work: #462 format/maths rules and active #641 job/blaze-surfaces account traversal/cancellation/prefetch. This issue adopts shared primitives after that work; no parallel replacement of its account navigation. Measurement scope: the production/HDD read table on #663 supplies a representative endpoint result, not proof that a complete Money view meets all budgets. Rule 7/9 findings remain with #641/#549 and the existing surface jobs. This follow-up integrates their results and adds shared-contract acceptance after they land. Representative measurement from the audit (not a full-view budget result): `/api/v1/money/budgets/{id}/accounts`, five serial requests after fixture readiness, all HTTP 200. Median/p95/max 44.7/50.8/50.8 ms; five-request burst median/p95/max 45.1/45.4/45.4 ms. Serial window server CPU 80 ms, RSS 444309504 bytes; no ETag on these sampled responses. Load inside lock 3.66/2.1/0.93. Shared release server source `cc25c441b7a974185622a1dee853cf38686d2b67`, binary SHA-256 `2f3567d91c34839851247bc0acbc25a56aaacd14dca269b8f0342ddf83447ed9`; embedded production SPA; Chromium browser on the build host through SSH/HTTPS. Server/Home/Index on perf VM HDD emulator: direct-I/O loop, 8 ms read/write dm-delay, 200 IOPS and 150 MiB/s caps. Every measured phase held `flock -w 14400 /root/perf.lock`. Qualification QD1 115.3 IOPS/8.028 ms median, QD16 200.7 IOPS/96.993 ms. Fixture: 366 Daily notes, 10,980 Logs, 100 Files/Photos, 20 Notes/Tasks, three Budgets and 100 transactions; Mail empty, Admin one User. Structural source evidence above is the newer audit base, not the measured binary revision. No claim that these revisions are equivalent. The baseline in docs/perf/baseline.json uses another fixture/build/transport; no regression ratio is valid here. See #663 for matching baseline endpoint values and coverage gaps.
Author
Owner

Client runtime audit for #663; keep with #673/#688.

origin/dev c4a61e8cf0: money/api.ts:47–51 requests transactions without a page cursor/limit and bypasses cache. money/[budget]/accounts/account/+page.svelte:80–90 loads transactions with a month view in parallel, then :291 renders every returned transaction with no virtual range. Cleared/save/delete handlers (:119–144) await the server and afterWrite full reload before displaying refreshed data.

Adopt the existing bounded window, snapshots and mutation receipts; use a shared virtual collection with stable transaction/account identity. Test a large register and repeated cleared actions: bounded mounted rows, no whole-list re-fetch on each accepted action, and correct Undo/cross-view totals. No financial data or values were used in this audit. This is code evidence, not a measured budget failure.

Client runtime audit for #663; keep with #673/#688. origin/dev c4a61e8cf090170f35b1bed3350d9de20c83ecd5: money/api.ts:47–51 requests transactions without a page cursor/limit and bypasses cache. money/[budget]/accounts/[[account]]/+page.svelte:80–90 loads transactions with a month view in parallel, then :291 renders every returned transaction with no virtual range. Cleared/save/delete handlers (:119–144) await the server and afterWrite full reload before displaying refreshed data. Adopt the existing bounded window, snapshots and mutation receipts; use a shared virtual collection with stable transaction/account identity. Test a large register and repeated cleared actions: bounded mounted rows, no whole-list re-fetch on each accepted action, and correct Undo/cross-view totals. No financial data or values were used in this audit. This is code evidence, not a measured budget failure.
Author
Owner

Independent read-only review of d00c9eed4 for #844 found a remaining Money
cache-adoption defect. This adds evidence to #673; no new issue is needed.

P2: an authoritative missing/denied month response does not remove the warm
report. apps/web/src/lib/money/store.svelte.ts:131–138 rejects the read
through RevisionCache but leaves #months and its userStorage value.
store.svelte.ts:96–110 restores them on later visits.
apps/web/src/routes/money/[budget]/[month]/+page.svelte:81–86 catches all
refresh errors and keeps the old report on screen.

Example: warm a month, remove its Budget files through another Installation,
then return. A 404 never removes the retained report. This is a static code
trace, not a live reproduction.

Fix: on authoritative 403/404, remove both retained copies and clear the
mounted report. Use the existing missing/error state. Retain the report only
on a transport failure. Bind access and Plugin invalidation to all retained
copies. DESIGN §58 rules 3 and 6 and #673 require this.

Regression test: warm the real route, return 404 on refresh, and assert no
report in the route, memory or browser storage after revisit and reload.
Cover 403 and Plugin disable. Keep the offline report on network failure.

No build or test ran. The LIGHT review contract forbids them.

Independent read-only review of `d00c9eed4` for #844 found a remaining Money cache-adoption defect. This adds evidence to #673; no new issue is needed. P2: an authoritative missing/denied month response does not remove the warm report. `apps/web/src/lib/money/store.svelte.ts:131–138` rejects the read through RevisionCache but leaves `#months` and its userStorage value. `store.svelte.ts:96–110` restores them on later visits. `apps/web/src/routes/money/[budget]/[month]/+page.svelte:81–86` catches all refresh errors and keeps the old report on screen. Example: warm a month, remove its Budget files through another Installation, then return. A 404 never removes the retained report. This is a static code trace, not a live reproduction. Fix: on authoritative 403/404, remove both retained copies and clear the mounted report. Use the existing missing/error state. Retain the report only on a transport failure. Bind access and Plugin invalidation to all retained copies. DESIGN §58 rules 3 and 6 and #673 require this. Regression test: warm the real route, return 404 on refresh, and assert no report in the route, memory or browser storage after revisit and reload. Cover 403 and Plugin disable. Keep the offline report on network failure. No build or test ran. The LIGHT review contract forbids them.
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#673
No description provided.