import shared pure modules from server/lib instead of copying them into client/src/lib - #6395
Merged
Conversation
atomantic
force-pushed
the
claim/issue-6364
branch
from
September 6, 2026 15:12
300aaa4 to
b5d66d9
Compare
…to client/src/lib The same need — one pure module used by both runtimes — was served three ways: 25 declared byte-for-byte "server mirror" copies with 20 parity tests policing them, a growing set of files that just import the server leaf (NAV_COMMANDS since the server loaded client source. The copies drifted anyway (#6303 accepted `https:foo` on one side only), and per #6363 their parity tests were unreachable from a scoped PR, so the drift reached main. Settle on one direction: the client imports pure server/lib leaves, the server never imports client source. - Convert all 25 declared mirrors in client/src/lib into named re-exports of the server leaf. Client-only extras stay (INGEST_OPTIONS, appendTriggerWords, sceneShotWarnings, the shot-grammar labels, clampInt/summarizeLengthProfile). - catalogTypes and slashdoCatalog become projections rather than re-exports: the client registry carries UI-only editor layout / button tones, so it decorates the server list instead of restating it. The hand-copied BIBLE_LIMITS caps in editableListFields are gone. - Split three leaves so the client can import them at all: bibleLimits.js out of storyBible.js (crypto + fileUtils), portosUrls.js out of ports.js (module-scope process.env), youtubeUrlAssert.js out of youtubeUrl.js (ServerError → events). - Flip both reverse edges: personaTraitBlend inlines its own clamp, and avatarStyles moves to server/lib with a client re-export. - Delete the 20 parity tests whose only assertion was declaration equality, plus mirrorParity's README-driven coverage guard. Behaviour those tests uniquely covered moves to plain unit tests (isSafeHref's scheme matrix, new youtubeUrl/tribeCadence suites, the lora_effect_probe.py version lockstep, and a client loraEffect suite for the badge table). - Add two always-run guards: scripts/client-server-import-purity.test.js walks the import graph of every server/lib module the client imports and fails on a Node built-in or an out-of-tree import; scripts/server-imports-no-client.test.js fails on any server module importing client source, with the 17 remaining test-side cross-imports frozen in a list that may only shrink. The import budget rises 89,425 → 89,639: the four new leaves are extra nodes on paths that already existed, not new eager edges into a heavy subtree. Closes #6364
atomantic
force-pushed
the
claim/issue-6364
branch
from
September 6, 2026 15:42
b5d66d9 to
75d354f
Compare
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.
Summary
One pure module used by both runtimes was served three different ways: 25 declared byte-for-byte "server mirror" copies in
client/src/libpoliced by 20 parity tests, a growing set of files that simply import the server leaf (NAV_COMMANDSsince #5053,instanceFeatureRegistry, 12 FableLoom files), and two reverse imports where the server loaded client source. The copies drifted anyway (#6303 — the server acceptedhttps:foo, the client rejected it), and per #6363 their parity tests were unreachable from a scoped PR, so the drift reachedmain.This settles on one direction: the client imports pure
server/libleaves; the server never imports client source.client/src/libbecome named re-exports of the server leaf. Client-only extras stay put (INGEST_OPTIONS,appendTriggerWords,sceneShotWarnings, the shot-grammar label maps,clampInt/summarizeLengthProfile).catalogTypesandslashdoCatalogbecome projections rather than plain re-exports — both client registries carry UI-only fields (editorSections/editorFields, per-button Tailwind tones) the server has no use for, so each decorates the server list instead of restating it. That also retires the hand-copiedBIBLE_LIMITSnumbers ineditableListFields.bibleLimits.jsout ofstoryBible.js(crypto+fileUtils),portosUrls.jsout ofports.js(module-scopeprocess.env),youtubeUrlAssert.jsout ofyoutubeUrl.js(ServerError→ Node'sevents).personaTraitBlendinlines its ownclamp, andavatarStylesmoves intoserver/libwith a client re-export.mirrorCoverageguard are gone. Behaviour they uniquely covered moved to plain unit tests:isSafeHref's scheme matrix, newyoutubeUrl/tribeCadencesuites, thelora_effect_probe.pyversion lockstep, and a clientloraEffectsuite for the badge table.scripts/client-server-import-purity.test.jswalks the import graph of everyserver/libmodule the client imports and fails on a Node built-in or an out-of-tree import;scripts/server-imports-no-client.test.jsfails on any production server module importing client source, with the 17 remaining test-side cross-imports frozen in a list that may only shrink.Two deliberate behaviour changes
LENGTH_PROFILEScarried shorterdescriptionstrings than the server profiles it claimed to mirror; the dropdown now shows the server wording. The row has no truncation, so a longer string wraps.loraEffect.formatLoraEffectwas never a mirror. The server returns a sentence for its log line; the client returnsnullwhen the badge already says everything (or the card prints "Unreadable — unreadable"). OnlyLORA_EFFECT_STATUSEScrosses now, and the client function is renamedloraEffectDetailso one name no longer means two things.The server-suite import budget rises 89,425 → 89,639: the four new leaves are extra nodes on paths that already existed, not new eager edges into a heavy subtree.
Test plan
cd server && npm test— 1,997 files / 39,873 tests pass.cd client && npm test— 869 files / 10,564 tests pass.cd client && npm run build— succeeds (the browser bundle now resolves every re-exported server leaf).cd client && npm run lint— clean.import { randomUUID } from 'crypto'toserver/lib/appIdentity.jsfails the purity walk with the offending chain; the direction guard's detector is pinned by bypass probes for a multi-line named import, anexport … from, a bareimport, a same-root import, and areadFileSyncof a cross-root path.Closes #6364