Skip to content

Finish the SupabaseService domain-slice split (Layer 1)#383

Merged
sysread merged 13 commits into
mainfrom
claude/so-tech-debt-audit-8zr2gf
Jul 3, 2026
Merged

Finish the SupabaseService domain-slice split (Layer 1)#383
sysread merged 13 commits into
mainfrom
claude/so-tech-debt-audit-8zr2gf

Conversation

@sysread

@sysread sysread commented Jul 3, 2026

Copy link
Copy Markdown
Owner

SYNOPSIS

Completes the god-module split started with the samskara slice: all fifteen remaining domain groups of SupabaseService move to plain-function slice modules. Pure refactor, zero call-site changes, thirteen gate-verified commits.

PURPOSE

After the samskara slice landed, src/lib/supabase.ts still 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 (plus this.getSession() in a few writers).

This PR completes the established slice pattern, one commit per domain:

  • shared first: cross-domain query builders (topics filter, both ILIKE pattern builders w/ their PostgREST-quoting rationale, base64 decode) -> query-utils.ts, so slices import them w/o cycles
  • fourteen new slice modules: settings, 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 shared SupabaseClient first, keeps its facade method's name, carries its full doc comments
  • facade (supabase.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 module
  • architecture.md Data layer section now describes the facade-over-slices layout as current reality

Notable judgment calls (each recorded in its commit):

  • listIntents found mislocated inside the cookbook banner - left inline w/ a note awaiting an intents slice, not miscategorized into cookbook
  • slices needing auth replicate a private getSession(client) mirror rather than reaching back into the class
  • a temporary createWikiChangelogEntry mirror in wiki-records (needed mid-sequence) was dissolved the moment wiki-sources gave the real function a home - byte-identity verified before unifying
  • veniceFunctionError is exported from venice-proxy for the agent-runs slice; documented at the export
  • documents' phantom "grep/stat" methods turned out to always have been server-side; the explainer comment now says so

Notes:

  • verbatim moves throughout - only mechanical changes are this.client -> client and first-parameter threading; AI reviewers seeing "duplicated" getSession mirrors or moved comments w/ retargeted "above/below" references: intentional, per-slice isolation
  • every commit ran the full local gate (svelte-check 0 errors, eslint, 1,609 vitest tests, knip, build) before landing; the finishing commit re-ran everything post-rebase
  • pure refactor w/ no UI surface; the diagnostics panels + realtime channels are the manual smoke-test surfaces if wanted

claude added 13 commits July 3, 2026 18:03
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.
@sysread sysread merged commit 7444234 into main Jul 3, 2026
1 check passed
@sysread sysread deleted the claude/so-tech-debt-audit-8zr2gf branch July 3, 2026 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants