Build a permit review queue for two reviewers whose allowed case sets do not overlap. The first reviewer receives 47 cases in the first server page, searches the list, types a private note, and requests approval for case 62. Begin with a plain POST form that works when JavaScript is disabled. The owner must preserve the note under case 62 through insertion and sorting, show pending state without treating it as a committed server result, and return one approval when a lost response causes the same operation to be replayed. A second reviewer must never see the first reviewer's case IDs in HTML or serialized data. A saved 29-row browser preference may apply after hydration, once the initial page has matched the server's 47-row view.
Project: Svelte permit review and approval action
Build contract
- Use one authorized source snapshot and phrase; derive matching rows and count without copying them into writable state.
- Key case cards by immutable ID and route approval intent through a parent callback rather than child prop mutation.
- Validate the action's fields and server identity, check case permission, and deduplicate a stable operation ID atomically.
- Fetch per reviewer in server load, return a bounded payload, and delay browser-only preferences until after first render.
Implementation checkpoint
function mayCommitPermitApproval(operationId, committedOperations) {
return !committedOperations.has(operationId);
}
const committedOperations = new Set(["permit-62-reviewer-47-retry-81"]);
console.log(mayCommitPermitApproval("permit-62-reviewer-47-retry-81", committedOperations));
// Output: falseCost and boundaries
Local filtering scans n summaries per changed phrase and may allocate O(n) matching references. Keep the rendered first page bounded even if the server knows about 47,000 cases. Stable row keys retain view state, so private draft storage grows with the number of retained case IDs and needs logout and expiry cleanup. An operation deduplication table grows with retained commands and must be transactional with the approval mutation; a client Set alone is only an illustration of the replay decision, not the production authority. Each server load spends permission and query work for its viewer. Shared caching is safe only when the full visibility boundary is part of the key and invalidation rule. An enhanced form can improve pending feedback, but it costs browser code and must preserve the ordinary POST path.
Failure drill
Insert case 29 above a drafted case 62 and reverse sort; the note must remain with 62. Reject approval for case 62 after the card reports intent and confirm every status display returns to the server result. Commit approval, lose the response, and replay the same operation ID; one audit notification may remain. Tamper with the hidden case ID and sign in as a reviewer without permission: the action must reject both paths. Run two concurrent server loads and scan both responses for cross-reviewer case IDs. Hydrate with a stored 29-row preference and a different time zone; the first browser tree must match the server before personalization updates it. Disable scripts and submit the form again.
Acceptance checks
- Search results and count describe one authorized snapshot; no stale copied count is displayed.
- Every draft, pending command, and committed status remains attached to the intended case ID.
- An authorized approval replay yields one logical write; malformed or forbidden requests yield no write.
- Server responses and the initial browser tree stay isolated and consistent across reviewers and preferences.
Common Mistakes
- Persisting an entire private case snapshot in browser storage.
- Letting a card change its received case object.
- Treating a hidden form field as permission.
- Sharing mutable load state between requests.
Related lessons
Svelte Reactivity and Server Boundaries; Svelte Runes: Source State, Derived Views, and Effect Cleanup; Svelte Props, Callbacks, and Keyed Editor Ownership; SvelteKit Form Actions, Validation, and Mutation Replay; SvelteKit Request-Scoped Load and Hydration State.
