Single-source the brain wire types shared by server and web - #976
Merged
Conversation
apps/web/src/hooks/use-brain.ts hand-mirrored seven record shapes from
apps/server/src/brain/store.ts. Extract the store's type block into a new
dependency-free apps/server/src/brain/types.ts and have web import it, so
the shapes have one definition.
store.ts re-exports every moved name, so all server-side importers are
untouched. The BrainList & { itemCount } composition the list endpoints
return is now named BrainListWithItemCount instead of being restated at
each of its four use sites.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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.
Tech-debt run: sub-item (c) of the server↔web wire-type duplication cluster (the pattern shipped in #856, #965, #971).
What was duplicated
apps/web/src/hooks/use-brain.tshand-restated seven record shapes thatapps/server/src/brain/store.tsalready declares —BrainProject,BrainCollectionSummary,BrainObject,BrainList,BrainListItem,BrainEntryType,BrainEvent. Six duplicated 7-line windows, found by the 2026-08-14 audit's block scan.What changed (3 files, +51 / −146)
apps/server/src/brain/types.ts(102 lines, zero imports) holds the brain record shapes lifted verbatim out ofstore.ts.store.tsnowexport type { … } from "./types.js"for all 14 moved names, plus a pairedimport typebecause a bare re-export does not bind the names in local scope (the trap that bit Derive the remaining within-server agent-type tables from shared #950 and Consolidate the icon-color palette onto one shared module #961). Every server-side importer —routes/brain.ts,shared/mcp/brain-tools.ts,shared/mcp/server.ts,routes/mcp.ts,server.ts, and four test files — is untouched.use-brain.tsimports the leaf directly and keeps every previously-exported name as a local alias, so its importers are untouched too.BrainList & { itemCount: number }composition the list endpoints return is now namedBrainListWithItemCountintypes.ts, replacing four separate restatements of it (BrainAgentActivity, thelistListssignature, and web's copy).Why the new file (deviation from the last three runs)
The plan was to import
brain/store.tsdirectly, as #965 and #971 did with their leaves. That does not compile.apps/web's tsconfig setsuseDefineForClassFields: truewithtarget: ES2020, andstore.tsdeclares error classes with initialized properties — pulling it into web's TS program fails withTS2401: A 'super' call must be a root-level statement…atstore.ts:130. The leaf-extraction fix from #863 applies instead, which is why this diff touches the server. The probe was run before committing to a shape, not after.Drift found: none
Every field matched the server declaration exactly. The one apparent difference — web's
BrainListcarried an extraitemCount— is not drift:BrainStore.listLists()returnsPromise<(BrainList & { itemCount: number })[]>, so web was mirroring the wire shape correctly. That composition is now the named type both sides use.Deliberate exclusions (near-misses left alone)
BRAIN_ENTRY_TYPESexists as a runtime const in bothstore.ts(derived fromENTRY_TYPE_TABLES) anduse-brain.ts(derived fromENTRY_TYPE_COUNT_KEYS). Each derives from a different local lookup table that is meaningful only on its own side, and sharing it would mean importing runtime code from server into the web bundle — a different and much larger change than a type-only import. Left as-is.useBrainListItems's inline{ items, totalCount, revision }matchesgetListItems' return type, but that type is anonymous on the server side too. Naming it would be adding a type, not removing a duplicate.ScopeTotalsinuse-brain.tsis already derived (Pick<BrainCollectionSummary, …>), so it now derives from the server type transitively. No change needed.agents/reviews.ts:297'sitemCountis a review item count, unrelated to brain lists despite the shared field name.Validation
pnpm run check:webalone passes withapps/server/src/generatedabsent — proof the web program does not depend on generated server assets. (pnpm run checkcreates that directory as a side effect, so the clean probe was run first.)pnpm run check✅ ·pnpm run finalize:web✅ (production build succeeds — theimport typeis fully erased by esbuild, zero runtime change)createdByAgentIdacrossapps e2e bin scripts) returns exactly one declaration site outside test fixtures:brain/types.ts.Next run
Sub-item (d) of the same cluster:
apps/server/src/routes/activity/history-routes.tsvsapps/web/src/hooks/use-agent-history.ts(5 windows). A route module is even more likely thanstore.tsto need the leaf-extraction shape — probe withcheck:webfirst.🤖 Generated with Claude Code