Finish the SupabaseService domain-slice split (Layer 1)#383
Merged
Conversation
First step of finishing the SupabaseService domain-slice split: the topics-filter clause builders, the two ILIKE pattern builders, and the base64 Storage-upload decoder are used by queries across threads, memories, cookbook, wiki, documents, and messages, so the upcoming per-domain slice modules all need them. Moving them to a shared query-utils module first lets each slice import them without reaching back into supabase.ts (which would be an import cycle).
The Settings & Venice API proxies banner group fused two concerns: CRUD against the profiles.settings jsonb (plus the app_config price-caps read) and proxy calls into the venice edge function. Split it into two slice modules following the samskara slice pattern: src/lib/supabase/settings.ts owns getSettings, updateSettings, and getPriceCaps; src/lib/supabase/venice-proxy.ts owns fetchUsage, fetchModels, fetchImageModels, embed, extractText, and complete, along with the 429 retry constants, sleepCancellable, and veniceFunctionError. Method bodies moved verbatim as plain functions taking the shared SupabaseClient; the facade keeps one-line delegating methods under unchanged names so call sites and grep targets stay stable. veniceFunctionError stays exported from the venice-proxy slice because the wiki agent-run methods still in the facade normalize their functions.invoke failures through it.
Continues the domain-slice split of the supabase.ts god-module: the Threads banner group (list/search/CRUD/per-thread setters) moves to src/lib/supabase/threads.ts, which also absorbs the Thread response claims group under its own internal banner - the claims are thread-scoped, so they belong with threads rather than a module of their own. The topic-vocabulary RPC wrappers move to src/lib/supabase/topics.ts and stay a separate module because the vocabularies serve threads, memories, and recipes alike; parking them under threads would misstate ownership. Method bodies move verbatim as plain functions taking the shared client; the facade keeps one-line delegates under unchanged names, and createThread's session lookup is mirrored by a slice-local getSession, same as the settings slice.
Continues the domain-slice split of the src/lib/supabase.ts god-module. The two memory banner groups - Memories (CRUD + changelog + paging) and Memory confidence, search & relations (reaffirm/doubt, embedding search, relations graph) - are one domain, so they move together into src/lib/supabase/memories.ts under two internal banners in the same order. Bodies moved verbatim as plain functions taking the client; the facade keeps one-line delegating methods under unchanged names so call sites and grep targets stay stable. The slice replicates the private getSession(client) helper the threads and settings slices use, and absorbs the memories RLS-posture preamble into its file comment.
Continues the domain-slice split of the src/lib/supabase.ts god-module. The Cookbook banner group - recipe list / paging / search, the version-snapshotting create / update / revert, the flag toggles, and the recipe photo library (content-addressed upsert, storage upload, signed-URL resolution) - moves into src/lib/supabase/cookbook.ts. Bodies moved verbatim as plain functions taking the client; the facade keeps one-line delegating methods under unchanged names so call sites and grep targets stay stable. The slice takes the two module-level declarations only it used (the signed-URL TTL and splitPhotoInputs) as private members, replicates the private getSession(client) helper the other slices use, and absorbs the recipes RLS-posture and embedding-pipeline preamble into its file comment. listIntents, which sat inside the cookbook stretch but belongs to the intents domain, stays inline in the facade with a note until that group gets a slice. ilikeFilterPattern and topicsFilterClause had no remaining facade users, so they drop out of the query-utils import.
…ervice Articles and records are separate sub-domains with separate UI surfaces (the sidebar browse/CRUD pane vs. the dated-entries panel), so they split into two modules: src/lib/supabase/wiki.ts owns the article listing / paging / fetch / favorites / CRUD, and src/lib/supabase/wiki-records.ts owns the records together with their file attachments and record-to-record links - the trio is one lifecycle, every mutation funneling through the same best-effort changelog-append helpers, so it stays one module. The facade keeps one-line delegating methods under unchanged names, same pattern as the cookbook and memories slices. One deliberate duplication: the record paths changelog through a private mirror of createWikiChangelogEntry, because the wiki sources / changelog / agent-run group is still inline in the facade and a slice cannot reach back into SupabaseService. The mirror carries a TODO to converge the two copies when that group gets its own slice.
…rvice The bibliography, See-Also, and changelog reads are satellites of the wiki article table, so they move together into supabase/wiki-sources.ts. The wiki/rem/deep-sleep run kicks, the wiki retry and manual-update routes, the Skipped-panel read, and the pipeline reset move into supabase/agent-runs.ts: they invoke the venice edge function and serve the generic agent-run progress UI rather than any one wiki surface, which earns them a module of their own. Now that createWikiChangelogEntry has a slice home, the verbatim private mirror in wiki-records.ts (flagged with a convergence TODO) is deleted in favor of a one-way sibling import, ending the duplicated changelog insert. searchWikiArticles stays inline in the facade: it queries wiki_articles itself and belongs in the wiki article slice when that module is next touched.
…ments The wiki-sources/agent-runs extraction left searchWikiArticles inline in the facade because the article slice was out of that task's touch scope. It queries wiki_articles directly and its empty-query path returns the alphabetical listing, so its home is the article slice - moving it also turns the this.listWikiArticles call into a direct sibling function call. The venice-proxy preamble and the wiki slice preamble still described the pre-extraction layout; both now name the modules that actually hold the code.
Continues the SupabaseService god-module split: the document CRUD, Library paging/search, and documents-bucket upload / signed-URL helpers move verbatim into plain functions taking the client, so they can be unit-tested against a stubbed client without constructing the facade. The facade keeps one-line delegating methods under unchanged names so call sites and grep targets stay stable. The group preamble (two-phase upload rationale) and the dangling note about the server-side grep/read pair move into the slice preamble; coerceDocument and ilikeLogicTreePattern imports leave the facade with their last local uses.
Moves the message read/write and attachment storage group out of the supabase.ts god-module into src/lib/supabase/messages.ts as plain functions taking the client, following the pattern set by the threads, documents, and cookbook slices. The facade keeps one-line delegating methods under unchanged names so call sites and grep targets stay stable, and the slice is unit-testable against a stubbed client. The intra-group call from listMessages to listAttachmentsByMessageIds becomes a direct function call inside the slice; addAttachments uses the slice-local getSession mirror. The facade drops its now-unused synthesizeRecoveryMessages, base64ToBytes, and coerceAttachmentRow imports.
Move the Realtime subscriptions & message fetch group - the
subscribe* channel relays (messages, threads, wiki articles/records,
memories, recipes, plus the private Broadcast channels for logs,
samskara mints and agent-run progress) and their paired point reads
(getMessage, the inflight-lease and last-run-outcome readers) - out
of the facade into supabase/realtime.ts as plain functions taking
the client, following the pattern the samskara/settings/threads/
memories/cookbook/wiki/documents/messages slices established. The
facade keeps one-line delegates under unchanged names so no call
site moves. The slice carries its own createLogger('supabase')
handle; the facade's module-level log had no remaining users after
the move, so it and the createLogger import are gone (the
SerializableLogEntry type import stays for the delegate signature),
and the now-unused coerceManualRunOutcome import moves with its two
callers. Method bodies are verbatim apart from this.client ->
client; the orphaned subscribeToWikiArticleChanges doc comment
(previously stranded above getInflightLeaseExpiry) is reattached to
its function in the slice.
This is the last domain group carrying its implementation inline: the bias_summary aggregate read plus the debug modal's drill-down queries over bias_observations, bias_reactions, and threads.bias_processed_at move verbatim into src/lib/supabase/bias.ts as plain functions taking the client, with the facade keeping one-line delegating methods under unchanged names. The group's preamble note - that the per-turn bias writes moved server-side into the venice edge function's priming pass, leaving the browser read-only here - graduates into the slice's file preamble. With this, SupabaseService is a pure facade over the domain slices plus the Auth & session group and the listIntents straggler.
With all fifteen domain groups extracted, the incremental migration note in the class preamble had accreted into a run-on list narrating each step. Rewritten as the end-state directory: every banner maps to its slice module, auth is called out as deliberately inline, and the listIntents straggler is flagged at both mention sites. architecture.md's Data layer section likewise now describes the facade-over-slices layout as current reality instead of an in-progress migration.
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.
SYNOPSIS
Completes the god-module split started with the samskara slice: all fifteen remaining domain groups of
SupabaseServicemove to plain-function slice modules. Pure refactor, zero call-site changes, thirteen gate-verified commits.PURPOSE
After the samskara slice landed,
src/lib/supabase.tsstill carried ~5,400 lines / ~185 method implementations inline. Testing any wrapper meant constructing the whole service; every session paid the context cost of one monolith; and each domain's queries were findable only by scrolling.DESCRIPTION
Existing shape: one class, banner-grouped, every method a thin wrapper sharing only
this.client(plusthis.getSession()in a few writers).This PR completes the established slice pattern, one commit per domain:
query-utils.ts, so slices import them w/o cyclessettings,venice-proxy(the fused banner split into its two real concerns),threads(absorbing thread-response claims),topics,memories,cookbook,wiki,wiki-records,wiki-sources,agent-runs,documents,messages,realtime,bias- each function takes the sharedSupabaseClientfirst, keeps its facade method's name, carries its full doc commentssupabase.ts, 5,988 -> 1,502 lines across both PRs): banner + one-line delegations under unchanged names; auth stays inline (it owns the client's auth surface); class preamble rewritten as an end-state directory mapping every banner to its modulearchitecture.mdData layer section now describes the facade-over-slices layout as current realityNotable judgment calls (each recorded in its commit):
listIntentsfound mislocated inside the cookbook banner - left inline w/ a note awaiting an intents slice, not miscategorized into cookbookgetSession(client)mirror rather than reaching back into the classcreateWikiChangelogEntrymirror in wiki-records (needed mid-sequence) was dissolved the moment wiki-sources gave the real function a home - byte-identity verified before unifyingveniceFunctionErroris exported from venice-proxy for the agent-runs slice; documented at the exportNotes:
this.client->clientand first-parameter threading; AI reviewers seeing "duplicated" getSession mirrors or moved comments w/ retargeted "above/below" references: intentional, per-slice isolation