anomalyco / anomalyco/opencode
feat(client): carry schema ID brands through codegen into the frontend
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Problem
@opencode-ai/schema already brands every domain ID (SessionID, Agent.ID, Form.ID, Event.ID, Integration.ID, …), but httpapi-codegen flattens them all to plain string in the generated client types (packages/client/src/promise/generated/types.ts — e.g. SessionInfo.id: string, SessionInfo.projectID: string). The frontend (solid data layer, TUI, app) then passes bare strings everywhere:
data.session.get(sessionID: string)accepts a message ID, a project ID, or any string at all.- Optimistic-admission bookkeeping (
outbox,sessionOutbox,creating,sendinginpackages/client/src/solid/data.ts) keys sets/maps by unbranded strings — a message ID in a session-keyed map typechecks fine. - Sync keys are hand-built template literals (
`session:${id}`,`session.family:${id}`) with no compile-time relation between the prefix and the ID kind.
Mixing up ID kinds is exactly the class of bug the recent optimistic-session-creation work (#43687) had to guard against with careful naming and comments instead of types.
Direction
- Codegen: teach
httpapi-codegento emit the branded type (imported from@opencode-ai/schema) wherever the protocol schema carries a brand, instead of widening tostring. The protocol schemas already have the information; this is a projection fix. - Frontend surfaces: narrow the data-layer signatures (
session.get(id: SessionID),prompt(input)withsessionID: SessionID, map/set keys) once the generated types carry brands.Schema.brandtypes are assignable tostring, so this can land incrementally — brand producers first (server responses,SessionID.create()), consumers tighten over time. - Sync keys (optional follow-up): replace template-literal keys with typed constructors (e.g.
SyncKey.session(id)) so a key's prefix and ID kind can't disagree.
Scope notes
- Runtime behavior is unchanged — brands are compile-time only.
- The big win is in
packages/client/src/solid/data.tsand the TUI, where session/message/project/inbox IDs all flow through the same string-typed plumbing.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in httpapi-codegen and inspect how branded protocol schema types become packages/client/src/promise/generated/types.ts, using SessionInfo.id and projectID as examples. Then review packages/client/src/solid/data.ts and the TUI for signatures and map/set keys that should preserve those brands. Done means generated client types retain schema brands and the identified frontend surfaces accept the corresponding branded IDs; typed sync-key constructors are optional follow-up work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100