authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-25 17:16:05-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-26 13:20:36-08:00
log35f39435f3feb5bc6b44d25d069cf29c98e99ed5
tree38a3499019302342055f389573f1c888543e5090
parente7743632ecc89ddec30dc01d9553353a1f062c83

cleanup plan


1 files changed, 0 insertions(+), 185 deletions(-)

.plans/16-frontend-contract-schema-validation-alignment.md deleted-185
......@@ -1,185 +0,0 @@
1# Plan: Frontend Contract Schema Validation Alignment
2
3## Summary
4Align frontend input/output validation with `@t3tools/contracts` schemas so the web client stops relying on ad-hoc checks and unsafe casts at critical boundaries.
5
6## Goals
71. Decode all WebSocket envelopes with shared contract schemas.
82. Decode push payloads before handing them to app logic.
93. Replace duplicated frontend range/input checks with contract schema decoding.
104. Validate project search params with shared constraints before dispatch.
115. Validate script keybinding updates with `KeybindingRule` schema before RPC.
126. Propagate branded IDs (`ThreadId`, `ProjectId`, etc.) through typed app flow after single ingress conversion.
13
14## Non-Goals
15- No protocol shape changes.
16- No server behavior changes unless uncovered by failing tests.
17- No UI redesign; only validation/transport boundary hardening.
18
19## Phase 1: WebSocket Envelope Decoding
20
21### Issue
22`apps/web/src/wsTransport.ts` parses JSON and performs manual object/cast checks (`as WsPush`) instead of decoding with contracts.
23
24### Changes
251. Decode inbound messages with contract schema union (`WsResponse`).
262. Remove manual push/response shape checks that duplicate schema logic.
273. Convert decode failures into safe no-op handling with structured debug logging.
28
29### Target Files
30- `apps/web/src/wsTransport.ts`
31- `apps/web/src/wsTransport.test.ts`
32
33### Tests
341. Accept valid push envelope and route to channel listener.
352. Accept valid response envelope and resolve pending request.
363. Reject malformed envelope without crashing transport.
37
38## Phase 2: Push Payload Decoding at API Boundary
39
40### Issue
41`apps/web/src/wsNativeApi.ts` forwards unknown push payloads and uses direct casts (`data as WsWelcomePayload`).
42
43### Changes
441. Decode `server.welcome` payload with `WsWelcomePayload`.
452. Decode `terminal.event` payload with `TerminalEvent`.
463. Decode `orchestration.domainEvent` payload with `OrchestrationEvent`.
474. Skip invalid payloads safely and keep subscriptions alive.
48
49### Target Files
50- `apps/web/src/wsNativeApi.ts`
51- `apps/web/src/wsNativeApi.test.ts`
52
53### Tests
541. Valid welcome payload is delivered and cached.
552. Invalid welcome payload is ignored.
563. Valid terminal/orchestration events are forwarded.
574. Invalid push data does not invoke consumers.
58
59## Phase 3: Replace Duplicated Checkpoint Range Validation
60
61### Issue
62`apps/web/src/lib/providerReactQuery.ts` duplicates `fromTurnCount`/`toTurnCount` validation rules already represented in contracts.
63
64### Changes
651. Use `OrchestrationGetTurnDiffInput` / `OrchestrationGetFullThreadDiffInput` decoding before RPC calls.
662. Remove bespoke integer/range checks where schema decoding can be authoritative.
673. Preserve current UX behavior (`enabled` gating + retry policy).
68
69### Target Files
70- `apps/web/src/lib/providerReactQuery.ts`
71- `apps/web/src/lib/providerReactQuery.test.ts`
72
73### Tests
741. Valid range decodes and calls expected RPC.
752. Invalid range fails fast and avoids RPC call.
763. `fromTurnCount === 0` path still routes to `getFullThreadDiff`.
77
78## Phase 4: Project Search Input Validation from Contracts
79
80### Issue
81`apps/web/src/lib/projectReactQuery.ts` forwards search inputs without enforcing `ProjectSearchEntriesInput` constraints, and `ChatView` can pass empty query strings.
82
83### Changes
841. Decode project search input with `ProjectSearchEntriesInput` before request dispatch.
852. Gate query execution when query is empty after trim.
863. Replace magic search limit usage with shared constant (`PROJECT_SEARCH_ENTRIES_MAX_LIMIT`) or schema-safe value.
87
88### Target Files
89- `apps/web/src/lib/projectReactQuery.ts`
90- `apps/web/src/components/ChatView.tsx`
91- `apps/web/src/lib/projectReactQuery.test.ts`
92
93### Tests
941. Empty query does not dispatch request.
952. Over-limit values are rejected or clamped via schema-safe path.
963. Valid queries still return prior placeholder behavior.
97
98## Phase 5: Script/Keybinding Validation Consistency
99
100### Status
101Complete
102
103### Issue
104`ProjectScriptsControl` and `ChatView` rely mostly on local non-empty checks while sending keybinding data into a contract-validated endpoint (`server.upsertKeybinding`).
105
106### Changes
1071. Add pre-submit decode for keybinding payload using `KeybindingRule`.
1082. Normalize and validate keybinding values via schema before calling `upsertKeybinding`.
1093. Keep existing script ID command generation logic, but ensure command + key are schema-valid before mutation.
110
111### Target Files
112- `apps/web/src/components/ProjectScriptsControl.tsx`
113- `apps/web/src/components/ChatView.tsx`
114- (optional helper) `apps/web/src/projectScripts.ts`
115- `apps/web/src/components/ProjectScriptsControl.test.tsx` (or logic test file)
116
117### Tests
1181. Invalid keybinding string surfaces UI validation error and blocks submission.
1192. Valid keybinding updates still persist and refresh cached server config.
1203. Editing existing script preserves valid keybinding mapping behavior.
121
122## Phase 6: Branded ID Flow (Single Ingress Branding)
123
124### Issue
125Frontend currently converts plain strings to branded IDs repeatedly (`asThreadId`, `asProjectId`, etc.) at many callsites, which spreads unsafe conversions across app logic.
126
127### Desired Model
1281. Convert/decode to branded IDs once at ingress boundaries.
1292. Keep IDs branded through store state, selectors, and command/query builders.
1303. Avoid repeated `*.makeUnsafe` wrappers in feature logic.
131
132### Boundary Rules
1331. Router/search params (string) -> decode to branded IDs at route boundary.
1342. Storage/network payloads (untyped JSON) -> decode with contract schemas at transport/query boundary.
1353. ID generation (`newThreadId`, `newProjectId`, etc.) remains branded at source.
1364. UI components should consume typed IDs from store/hooks, not raw strings.
137
138### Changes
1391. Introduce a thin typed ID layer in web app (or reuse contracts decoders directly) for safe `unknown -> branded` conversion at boundaries.
1402. Refactor web store and thread/project domain types to carry branded IDs where feasible.
1413. Replace repeated `asThreadId`/`asProjectId` callsites in:
142 - `apps/web/src/components/ChatView.tsx`
143 - `apps/web/src/components/Sidebar.tsx`
144 - `apps/web/src/components/BranchToolbar.tsx`
145 - `apps/web/src/lib/providerReactQuery.ts`
1464. Keep unavoidable casts isolated in one boundary module (not feature components).
1475. Minimize/remove broad `asXId` helpers from `apps/web/src/lib/orchestrationIds.ts` once flow is typed.
148
149### Target Files
150- `apps/web/src/lib/orchestrationIds.ts`
151- `apps/web/src/types.ts`
152- `apps/web/src/store.ts`
153- `apps/web/src/components/ChatView.tsx`
154- `apps/web/src/components/Sidebar.tsx`
155- `apps/web/src/components/BranchToolbar.tsx`
156- `apps/web/src/lib/providerReactQuery.ts`
157- route boundary files (`apps/web/src/routes/*`) as needed
158
159### Tests
1601. Type-level regression checks (compile-time) proving command builders accept branded IDs directly.
1612. Route/load path tests verifying invalid ID ingress is rejected/fails safely.
1623. Existing behavior tests still pass for thread/project operations after refactor.
163
164## Cross-Cutting Execution Notes
1651. Prefer a small shared frontend utility for Effect schema decoding to avoid repeated `decodeUnknownSync` boilerplate.
1662. Use `Schema.decodeUnknownEither` or equivalent non-throwing path where event stream robustness matters.
1673. Keep failure behavior fail-soft at stream boundaries (drop invalid events, keep transport running).
1684. Brands are TypeScript-level constraints; JSON boundaries still require decode/re-brand at ingress.
169
170## Validation Checklist
1711. `bun run lint`
1722. `bun run test --filter=@t3tools/web` (or equivalent web test command in repo)
1733. Manual smoke checks:
174 - App boot + welcome sync
175 - Terminal stream still renders
176 - Diff panel loads checkpoint ranges
177 - Path autocomplete search remains responsive
178 - Script add/edit with keybinding works in Electron flow
179
180## Done Criteria
1811. No unsafe push/event casts remain at core WebSocket boundaries.
1822. Frontend no longer duplicates contract input validation for checkpoint range and project search.
1833. Keybinding submission path is schema-validated before RPC.
1844. Branded IDs flow through frontend feature code without repeated `asThreadId`/`asProjectId` wrappers.
1855. Updated tests cover malformed payload and invalid input regressions.