refactor(frontend): readonly on app-types cache/API shapes#1595
Merged
Conversation
§3 #8 second/final slice (graph.ts was #1592). Adds `readonly` to every field of the hand-written app-types interfaces — Camper, BunkRequest, Constraint, SolverRun, DragItem, BunkWithCampers — with `readonly T[]` on array fields and `Readonly<Record<...>>` on map fields (matching the nested-Record pattern #1592 adopted from CodeRabbit). Pure type-level — no runtime change. The generated PB aliases (Session, Bunk, SavedScenario) are left mutable; their immutability belongs in codegen, not a hand-edit. Why: Camper/BunkRequest are the domain shapes that live in the React Query cache and flow through the board, panels, and drag-and-drop. A stray in-place mutation on a shared cached instance would corrupt every subscriber; readonly makes that a compile error. Cascade: a full-readonly tsc probe surfaced exactly 2 consumer sites (even with Camper used in 105 files) — both the predicted readonly-T[]→mutable-param class, neither a real mutation: - buildCamperRows(campers): widened param to `readonly Camper[]` (it only filters/toSorted/maps — all return fresh arrays). - RequestForm useState: copies `constraint.campers` into a mutable working copy `[...]`; the source stays immutable. Verified: type-check green (both tsconfigs), csvExportHelpers suite 19/19, full lefthook pre-push green. Closes out §3c #8; next live row is #9 (React Query queryOptions/skipToken). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughReadonly immutability is added to six core frontend type interfaces (Camper, BunkRequest, Constraint, SolverRun, DragItem, BunkWithCampers), including deep nested expand and results fields. csvExportHelpers tightens its API contract to accept readonly arrays, and RequestForm adapts by cloning the readonly source. Backlog documentation is updated to mark ChangesType Immutability and API Contracts
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
adamflagg
added a commit
that referenced
this pull request
May 21, 2026
#1598) Closes #1596. Finishes the app-types `readonly` contract opened by #1595 — same latent readonly-leak class as the graph-type follow-ups #1593/#1594 (shipped in #1597). Two consumers were intentionally left out of #1595 to keep that a pure type-only change. ## Changes 1. **`AllCampersView.tsx`** — drop the now-redundant `as Camper[]` cast. #1595 widened `buildCamperRows` to accept `readonly Camper[]` (`csvExportHelpers.ts:73`), and `filteredCampers` is `MergedCamper[]` (`MergedCamper extends Camper`), so it's directly assignable. The standing cast only suppressed future compiler signal if the contract changed. 2. **`mergeMultiSessionCampers.ts`** — mark `AdditionalSession`'s fields and `MergedCamper.additionalSessions` `readonly`, matching the now-`readonly` `Camper` that `MergedCamper` extends. The builder constructs each `AdditionalSession` via `.map()` into fresh objects, so the element type going `readonly` is a clean change. Pure type-only hardening — **no runtime behavior change**, no active mutation existed. ## Tests Adds the first tests for `mergeMultiSessionCampers` (previously untested): - **Behavioral coverage** — multi-enrollment merge produces one entry with `additionalSessions`; single-session camper untouched. - **Readonly contract** — `@ts-expect-error` assertions enforced by `tsc --noEmit`. Verified red→green: before the change the directives are unused (TS2578); after, they type-check. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Added comprehensive test suite for multi-session camper merging functionality, including validation of camper data consolidation and immutability constraints. * **Refactor** * Enhanced type safety in CSV export and camper data handling to prevent potential bugs and ensure data integrity during export operations. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/adamflagg/kindred/pull/1598?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backlog row §3 #8, second/final slice —
types/app-types.ts(graph.ts shipped in #1592). This closes out #8.What it does
Adds
readonlyto every field of the hand-written app-types interfaces —Camper,BunkRequest,Constraint,SolverRun,DragItem,BunkWithCampers— withreadonly T[]on array fields andReadonly<Record<…>>on map fields (matching the nested-Record pattern #1592 picked up from CodeRabbit). Pure type-level — no runtime change.The generated PB aliases (
Session,Bunk,SavedScenario) are left mutable — their immutability belongs in codegen, not a hand-edit.Why
Camper/BunkRequestare the domain shapes that live in the React Query cache and flow through the board, panels, and drag-and-drop. A stray in-place mutation on a shared cached instance would corrupt every subscriber;readonlymakes that a compile error.Cascade — measured, tiny
A full-readonly
tscprobe surfaced exactly 2 consumer sites (even withCamperused in 105 files), both the predictedreadonly T[]→mutable-param class, neither a real mutation:csvExportHelpers.tsbuildCamperRows(campers)readonly Camper[]— it onlyfilter/toSorted/maps (all return fresh arrays)RequestForm.tsxuseState(constraint?.campers ?? [])[...]; source stays immutableVerification
npm run type-check(both tsconfigs): greencsvExportHelpersvitest: 19/19lefthook run pre-push: greenConsistent with #1592's finding: zero in-place mutations of cached data exist today, so
readonlyis forward-looking insurance with near-zero cascade. Marks §3c #8 done (both slices); next live row is #9 (React QueryqueryOptions/skipToken).🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Documentation
Refactor