24 KiB
Lix Migration Audit
Date: 2026-04-18
Scope: codebase/@features/, codebase/@packages/, deployments/@domains/*/deploy.sh
Baseline: Zero lix usage in the project today (lixbuild/lixtest/lixrun absent from all package.json scripts except the age-verification and analytics packages, which already migrated).
1. Package Build/Test Script Inventory
@packages/ (workspace-local, publishable)
| Package | Name | build | test | typecheck | Detection Markers |
|---|---|---|---|---|---|
@packages/@lilith/provider-api-client |
@lilith/provider-api-client |
tsc --project tsconfig.json |
bun test |
tsc --noEmit |
none |
@packages/quinn-app-switcher |
@lilith/quinn-app-switcher |
tsc --project tsconfig.json |
vitest run |
tsc --noEmit |
none (has vite.config for vitest only) |
@packages/auth-provider |
@lilith/auth-provider |
— | — | — | none |
@packages/i18n |
@lilith/i18n |
— | — | — | none |
@packages/ui-auth |
@lilith/ui-auth |
— | — | — | none |
@packages/ui-dev-content |
@lilith/ui-dev-content |
— | — | — | none |
@features/ — already on lix
| Package | Name | build | test |
|---|---|---|---|
age-verification/shared |
@lilith/age-verification |
lixbuild |
lixtest --passWithNoTests |
age-verification/frontend-components |
@lilith/age-verification-react |
lixbuild |
lixtest --passWithNoTests |
analytics/shared |
@lilith/analytics-client |
lixbuild |
lixtest --passWithNoTests |
analytics/frontend-client |
@lilith/analytics-client-react |
lixbuild |
lixtest --passWithNoTests |
@features/ — NestJS backends (lix detects via nest-cli.json)
| Package | Name | build | test | typecheck |
|---|---|---|---|---|
client-intel/backend-api |
@lilith/client-intel-api |
nest build |
— | — |
merchant/backend-api |
@lilith/merchant-api |
nest build |
— | tsc --noEmit |
waitlist/backend-api |
@lilith/waitlist-api |
nest build |
— | — |
@features/ — Vite frontends (lixbuild detects via vite.config.ts + React)
| Package | Name | build | test | typecheck |
|---|---|---|---|---|
admin/frontend-public |
@lilith/admin-frontend |
NODE_ENV=production vite build |
— | tsc --noEmit |
analytics/website-frontend-users |
@lilith/analytics-website-users |
NODE_ENV=production vite build |
— | tsc --noEmit |
comm-newsletter/frontend-admin |
@lilith/comm-newsletter-frontend |
NODE_ENV=production vite build |
— | tsc --noEmit |
db-monitor/frontend-public |
@lilith/db-monitor-frontend |
NODE_ENV=production vite build |
— | tsc --noEmit |
image-protection/frontend-public |
@lilith/image-protection-frontend |
NODE_ENV=production vite build |
— | tsc --noEmit |
landing/frontend-public |
@lilith/landing |
NODE_ENV=production vite build |
— | tsc --noEmit |
messages/frontend-user |
@lilith/messages-frontend-user |
vite build |
— | tsc --noEmit |
my/frontend-public |
@lilith/my-frontend |
NODE_ENV=production vite build |
— | tsc --noEmit |
provider-website/frontend-public |
@lilith/provider-website |
NODE_ENV=production vite build |
vitest run |
tsc --noEmit |
quinn-ai/frontend-public |
@lilith/quinn-ai-frontend |
vite build |
— | tsc --noEmit |
sso/frontend-public |
@lilith/sso-frontend |
NODE_ENV=production vite build |
— | tsc --noEmit |
@features/ — bun-bundled Node.js backends (no nest-cli.json, no vite)
| Package | Name | build | test | typecheck | Notes |
|---|---|---|---|---|---|
admin/backend-api |
@lilith/admin-api |
bun build src/server.ts --target=node --outfile=dist/server.js (script: build:prod) |
vitest run |
tsc --noEmit |
deploy calls build:prod, not build |
analytics/website-backend-users |
@lilith/analytics-website-backend |
bun build src/server.ts --target=node --outfile=dist/server.js |
— | tsc --noEmit |
inline bun bundler |
comm-newsletter/backend-api |
@lilith/comm-newsletter-api |
bun build src/server.ts --target=node --outfile=dist/server.js (script: build:prod) |
— | tsc --noEmit |
deploy calls build:prod |
messages/backend-user |
@lilith/messages-user-backend |
tsc --noEmit && bun build src/server.ts --target=node ... |
vitest run |
tsc --noEmit |
typecheck embedded in build; externals: pg, @lilith/messenger-model-boss |
my/backend-api |
@lilith/my-api |
— | vitest run |
tsc --noEmit |
no build script; interpret/run mode |
provider-website/data-api |
@lilith/provider-website-data-api |
bun build --target=node --outfile=dist/server.js src/server.ts |
vitest run |
— | inline bun bundler |
sso/backend-api |
@lilith/sso-api |
bun build src/server.ts ... (script: build:prod) + separate seed script |
— | tsc --noEmit |
two bun build invocations in deploy |
@features/ — other / no build
| Package | Name | build | test | typecheck | Notes |
|---|---|---|---|---|---|
admin/shared |
@lilith/admin-shared |
— | — | tsc --noEmit |
source-resolved, no emit needed |
api |
@quinn/api |
— | bun test |
tsc --noEmit |
bun native test runner (not vitest) |
api/src/mcp-seo |
(nested) | — | — | — | sub-package, not directly built |
db-monitor/backend-api |
@lilith/db-monitor-api |
— | — | — | no scripts at all |
image-protection/backend-api |
@lilith/image-protection-api |
— | — | — | no scripts |
messages/frontend-showcase |
(showcase) | — | — | — | dev-only |
my/cli |
— | — | — | — | no scripts |
my/mcp-server |
@lilith/quinn-my-mcp |
— | — | tsc --noEmit |
tsx-executed, no emit |
platform-seed |
— | — | — | — | seed script, no scripts |
quinn-ai/backend-api |
@lilith/quinn-ai-backend |
tsc -p tsconfig.build.json (emit) |
vitest run |
tsc --noEmit |
custom tsconfig.build.json |
2. Recommended lix Replacement Per Package
Lix detection rules (in priority order): nest-cli.json → astro.config.* → tsup.config.ts → vite.config.ts + React → vite.config.ts (no React).
| Package | Current build | Recommended | Confidence | Notes |
|---|---|---|---|---|
provider-api-client |
tsc --project tsconfig.json |
lixbuild (library) |
High | No detection markers; needs tsup.config.ts added — or keep raw tsc (it's a publishable library). Prefer tsup for esm+cjs dual output. |
quinn-app-switcher |
tsc --project tsconfig.json |
lixbuild (library) |
High | Same as above; has vite.config for vitest only (tsup would take precedence). |
age-verification/shared |
already lixbuild |
— | Done | |
age-verification/frontend-components |
already lixbuild |
— | Done | |
analytics/shared |
already lixbuild |
— | Done | |
analytics/frontend-client |
already lixbuild |
— | Done | |
client-intel/backend-api |
nest build |
lixbuild (nest) |
High | Has nest-cli.json; lixbuild detects automatically. |
merchant/backend-api |
nest build |
lixbuild (nest) |
High | Has nest-cli.json. |
waitlist/backend-api |
nest build |
lixbuild (nest) |
High | Has nest-cli.json. |
admin/frontend-public |
vite build |
lixbuild (frontend) |
High | Has vite.config.ts + React. |
analytics/website-frontend-users |
vite build |
lixbuild (frontend) |
High | Has vite.config.ts + React. |
comm-newsletter/frontend-admin |
vite build |
lixbuild (frontend) |
High | Has vite.config.ts + React. |
db-monitor/frontend-public |
vite build |
lixbuild (frontend) |
High | Has vite.config.ts + React. |
image-protection/frontend-public |
vite build |
lixbuild (frontend) |
High | |
landing/frontend-public |
vite build |
lixbuild (frontend) |
High | |
messages/frontend-user |
vite build |
lixbuild (frontend) |
High | |
my/frontend-public |
vite build |
lixbuild (frontend) |
High | |
provider-website/frontend-public |
vite build |
lixbuild (frontend) |
High | |
quinn-ai/frontend-public |
vite build |
lixbuild (frontend) |
High | |
sso/frontend-public |
vite build |
lixbuild (frontend) |
High | |
admin/backend-api |
bun build ... --target=node (script: build:prod) |
Keep custom | — | Deploy invokes build:prod, not build; externals + dual scripts (server + migrate). Would need deploy.sh refactor too. |
analytics/website-backend-users |
bun build ... --target=node |
Keep custom | — | Inline bun bundler; no build:prod/build split issue. |
comm-newsletter/backend-api |
bun build ... --target=node (script: build:prod) |
Keep custom | — | Same pattern as admin-api. |
messages/backend-user |
tsc --noEmit && bun build ... |
Keep custom | — | Typecheck fused with build, explicit externals (pg, @lilith/messenger-model-boss). Decompose first, then consider lixbuild. |
provider-website/data-api |
bun build ... --target=node |
Keep custom | — | Simple single-server bundle; low value in abstracting. |
sso/backend-api |
bun build:prod + separate seed script |
Keep custom | — | Two separate bun build invocations; deploy orchestrates both. |
quinn-ai/backend-api |
tsc -p tsconfig.build.json |
Keep custom | — | Uses custom tsconfig.build.json; lixbuild has no hook for alternate tsconfigs. |
admin/shared |
— | No change | — | Source-resolved workspace package; no emit. |
api |
— | No change | — | bun native tests; no build step needed. |
my/backend-api |
— | No change | — | tsx-run, no emit. |
my/mcp-server |
— | No change | — | tsx-executed. |
test migration (replace vitest run with lixtest): safe for all packages already using vitest. The packages using bun test (api, provider-api-client) must stay on bun test — lixtest does not support bun's native runner.
3. Deploy Script Build Command Inventory
| Deploy | Script | Packages Built | Build Commands Used |
|---|---|---|---|
quinn.www/deploy.sh |
[1/10] |
quinn.www/root (SPA) |
bun run build (vite) |
quinn.www/deploy.sh |
[5/10] |
provider-website/data-api |
bun install && bun run build (bun bundler) |
quinn.www/deploy.sh |
[5.2/10] |
provider-website/backend-api (contact-api) |
bun install && bun run build (bun bundler) |
quinn.admin/deploy.sh |
frontend | admin/frontend-public, comm-newsletter/frontend-admin |
bun run build (vite each) |
quinn.admin/deploy.sh |
backend | admin/backend-api, comm-newsletter/backend-api |
bun run typecheck && bun run build:prod each |
quinn.ai/deploy.sh |
all | @applications/@ai/services/ai-core, quinn-ai/backend-api, quinn-ai/frontend-public |
bun run typecheck (3x), bun run build (ai-core + frontend) |
quinn.data/deploy.sh |
all | analytics/website-frontend-users, analytics/website-backend-users, external platform-analytics/frontend-provider |
NODE_ENV=production bun run build (vite), bun run build (bun bundler), external bun build |
quinn.m/deploy.sh (quinn.messenger surface) |
all | @messenger/imessage-sync/backend, messages/backend-user, messages/frontend-user |
npm run build (3x — note: npm not bun) |
quinn.my/deploy.sh |
all | my/backend-api, my/frontend-public |
bun run typecheck (2x), bun run build:prod, bun run build |
quinn.sso/deploy.sh |
all | sso/backend-api (x2 outputs), sso/frontend-public |
bun run typecheck, bun run build:prod, inline bun build for seed, bun run build (vite) |
quinn.m-orchestrator/deploy.sh |
none | external orchestrator workers only | rsync only, no build step |
quinn.my-orchestrator/deploy.sh |
none | external workers only | rsync only, no build step |
Notable anomaly: quinn.m/deploy.sh (quinn.messenger) invokes npm run build (not bun run build) for all three packages. This is likely a copy-paste artifact and should be aligned to bun run regardless of lix migration.
4. Dependency Graph — Build Order
The critical ordering constraint for quinn.www is:
@lilith/provider-api-client (codebase/@packages/@lilith/provider-api-client)
└── must emit dist/ BEFORE
quinn.www/root (SPA) (deployments/@domains/quinn.www/root)
└── consumes provider-api-client from its dist/ via workspace resolution
The quinn.www SPA vite build reads provider-api-client's dist/ (the main/exports fields point at ./dist/index.js). If dist/ is absent or stale, the Vite build will fail or bundle stale types.
Other dependency relationships:
admin/shared (source-resolved, no build)
└── consumed by admin/backend-api and admin/frontend-public at source (no pre-build needed)
age-verification/shared
└── consumed by age-verification/frontend-components (source alias in vite)
└── consumed by provider-website/frontend-public (source alias in vite)
analytics/shared
└── consumed by analytics/frontend-client (source alias in vite)
└── consumed by provider-website/frontend-public (source alias in vite)
The age-verification and analytics packages use source aliases in the quinn.www vite config (find: '@lilith/age-verification-react', replacement: path.resolve(featureRoot, 'age-verification/frontend-components/src')), so they do NOT need a pre-built dist for the quinn.www SPA build. Only provider-api-client uses its dist.
For the other deploy targets, all dependencies are self-contained (no cross-package dist references within the deploy).
5. Migration Sequencing
Wave 1 — Low risk, fully parallel (no deploy.sh changes needed)
Packages where bun run build → lixbuild is a drop-in, the package is not referenced by a deploy that calls build by another name, and no custom tsconfig/externals:
| Package | lix detected type |
|---|---|
client-intel/backend-api |
nest |
merchant/backend-api |
nest |
waitlist/backend-api |
nest |
admin/frontend-public |
frontend |
analytics/website-frontend-users |
frontend |
comm-newsletter/frontend-admin |
frontend |
db-monitor/frontend-public |
frontend |
image-protection/frontend-public |
frontend |
landing/frontend-public |
frontend |
messages/frontend-user |
frontend |
my/frontend-public |
frontend |
quinn-ai/frontend-public |
frontend |
sso/frontend-public |
frontend |
provider-website/frontend-public |
frontend |
test migration (parallel, same wave): replace vitest run with lixtest in admin/backend-api, messages/backend-user, my/backend-api, provider-website/frontend-public, quinn-ai/backend-api, quinn-app-switcher.
Wave 2 — Requires tsup.config.ts addition, then deploy.sh coordination
provider-api-client and quinn-app-switcher use raw tsc for emit. To migrate:
- Add
tsup.config.tsto each (lixbuild will then detect as library type) - Verify
dist/output structure matches existingmain/exportsfields - Update
buildscript tolixbuild - For
provider-api-client: confirmquinn.www/rootVite build still resolvesdist/index.jscorrectly after tsup output
This must be done sequentially per package (add config → verify dist → update script) but the two packages can be done in parallel with each other.
Wave 3 — Deploy.sh-coupled builds (do not migrate blindly)
These packages have build scripts named differently than build (e.g. build:prod) or have deploy scripts that explicitly invoke specific scripts. Migrating the build script would not affect the deploy path, but creates confusion. Recommend leaving these as-is or first aligning deploy.sh to call bun run build before introducing lixbuild:
admin/backend-api(deploy callsbuild:prod)comm-newsletter/backend-api(deploy callsbuild:prod)sso/backend-api(deploy callsbuild:prod+ inlinebun build)messages/backend-user(typecheck fused into build command)quinn-ai/backend-api(customtsconfig.build.json)
Wave 4 — Out of scope for lix
These packages have non-lix-compatible build patterns that should not be migrated:
- All
bun build --target=nodesingleton server bundles (analytics/website-backend-users,provider-website/data-api): bun's bundler is the right tool here, not tsup. - External packages (
@messenger/imessage-sync/backend,@applications/@ai/services/ai-core): outside this repo.
6. Risks
High — provider-api-client dist ordering in quinn.www deploy
Current state: quinn.www/deploy.sh runs bun run build in deployments/@domains/quinn.www/root/ (step 1). This SPA build depends on provider-api-client's dist/. The deploy script does not build provider-api-client first — it assumes dist/ is already present from a prior local build.
Risk: If provider-api-client's source changes but its dist is not rebuilt before ./run deploy:quinn, the SPA bundles stale client code. No deploy-time guard catches this.
Recommendation: Add an explicit provider-api-client build step before the SPA build in quinn.www/deploy.sh, or wire it as a pre-build script in quinn.www/root/package.json.
Medium — quinn.m/deploy.sh uses npm run build not bun run
All three packages in the quinn.m (quinn.messenger) deploy are invoked via npm run build. In a bun workspace, npm will work but bypasses bun's lockfile and Verdaccio registry routing. This is a correctness risk on environments where npm resolves to a different registry. Fix by replacing npm run with bun run across that deploy script.
Medium — messages/backend-user build fuses typecheck + bundle
The build script is tsc --noEmit && bun build .... This means lixbuild cannot be dropped in without first separating typecheck from bundling. The typecheck-in-build pattern also means a type error blocks the bundle even in CI scenarios where you might want to emit diagnostics separately.
Recommendation: Split into typecheck: tsc --noEmit (already exists) and build: bun build .... This unblocks future lixbuild migration of the test/typecheck steps even if the bun bundler stays.
Low — quinn-ai/backend-api custom tsconfig.build.json
lixbuild's library type invokes tsup; it has no hook to pass an alternate tsconfig. The tsc -p tsconfig.build.json pattern (emit only, no bundle) is deliberate. Migrating would require either a tsup config that replicates tsconfig.build.json's outDir/declaration settings, or keeping this package out of lix scope.
Low — packages with no build/test scripts at all
auth-provider, i18n, ui-auth, ui-dev-content, admin/shared, db-monitor/backend-api, image-protection/backend-api, my/cli, my/mcp-server, platform-seed: none have build scripts. Some are source-resolved (fine), some appear incomplete. These should be audited for intent before adding lix scripts — adding lixbuild to a package that has no real build need creates noise.
Low — quinn.data/deploy.sh references external repo path
The platform-analytics/frontend-provider path points to ~/Code/@projects/@lilith/lilith-platform/ (the read-only reference repo). Any lix migration of that package is out of scope for .live and must happen in the upstream repo.
7. Migration Log (Task #21 — 2026-04-18)
Wave 1 packages migrated: build → lixbuild (and test: vitest run → lixtest where applicable). Added @lilith/lix-build@^1.0.7 and @lilith/lix-test@^1.0.0 to devDependencies in each package.
| Package | Status | Notes |
|---|---|---|
client-intel/backend-api |
PASS | Detected: nestjs (nest-cli.json) |
merchant/backend-api |
FAIL | Pre-existing TS error: import.meta with module: CommonJS in tsconfig.json — fails identically with original nest build |
waitlist/backend-api |
PASS | Detected: nestjs (nest-cli.json) |
admin/frontend-public |
PASS | Detected: frontend (vite.config.ts + React) |
analytics/website-frontend-users |
PASS | Detected: frontend |
comm-newsletter/frontend-admin |
PASS | Detected: frontend |
db-monitor/frontend-public |
PASS | Detected: frontend |
image-protection/frontend-public |
PASS | Detected: frontend |
landing/frontend-public |
FAIL | Pre-existing vite.config.ts broken import: ../../../@packages/@utils/vite-version-plugin/src and vite-plugin-dev-locale-api paths don't exist in this workspace — fails identically with original vite build |
messages/frontend-user |
PASS | Detected: frontend |
my/frontend-public |
PASS | Detected: frontend |
provider-website/frontend-public |
PASS | Detected: frontend; test: vitest run → lixtest |
quinn-ai/frontend-public |
PASS | Detected: frontend |
sso/frontend-public |
PASS | Detected: frontend |
Summary: 12 PASS / 2 FAIL (pre-existing) / 0 SKIP
- Both failures are pre-existing and reproduce with the original build scripts unchanged.
8. Deploy Script Pre-Build Audit (Tasks #22 + #23 — 2026-04-18)
Scope: All 9 deploy scripts (1 from Task #22, 8 from Task #23).
Method: For each deployed surface, grepped all consumed package.json files for workspace:* protocol dependencies that would require a pre-built dist/. Also checked for @lilith/* version specs resolved from Verdaccio (published packages — no pre-build needed at deploy time).
Per-script findings
| Deploy | Packages Built | workspace:* deps requiring pre-build | Pre-build step added? | Notes |
|---|---|---|---|---|
quinn.www |
provider-api-client, quinn.www/root SPA |
provider-api-client (consumed via dist/) |
YES (Task #22) | bun run build → bunx lixbuild |
quinn.admin |
admin/frontend-public, comm-newsletter/frontend-admin, admin/backend-api, comm-newsletter/backend-api |
None | Not needed | No workspace:* deps in any package.json |
quinn.data |
analytics/website-frontend-users, analytics/website-backend-users, external platform-analytics/frontend-provider |
None | Not needed | No workspace:* deps; @lilith/* are semver-pinned from Verdaccio |
quinn.my |
my/backend-api, my/frontend-public |
None | Not needed | No workspace:* deps |
quinn.my-orchestrator |
None (rsync only) | N/A | Not needed | Pure rsync + systemd; no build step |
quinn.m (quinn.messenger) |
@messenger/imessage-sync/backend, messages/backend-user, messages/frontend-user |
None | Not needed | @lilith/quinn-app-switcher: "*" resolves via Verdaccio (published), not workspace |
quinn.m-orchestrator |
None (rsync only) | None | Not needed | Workers are rsync'd as source + npm install on black |
quinn.sso |
sso/backend-api, sso/frontend-public |
None | Not needed | No workspace:* deps |
quinn.ai |
@applications/@ai/services/ai-core, quinn-ai/backend-api, quinn-ai/frontend-public |
None | Not needed | External ai-core is outside this workspace |
Changes made
quinn.www/deploy.shline 91 (Task #22):bun run build→bunx lixbuildforprovider-api-clientpre-build step.quinn.m/deploy.sh(quinn.messenger) lines 135–143 (Task #23):npm run build→bun run buildfor all 3 packages (imessage-sync backend, backend-user, web SPA). Fixes audit flag from Section 3/6 — npm bypasses bun lockfile + Verdaccio routing.
Scripts with no changes needed
quinn.admin,quinn.data,quinn.my,quinn.my-orchestrator,quinn.m-orchestrator,quinn.sso,quinn.ai: No workspace-protocol dependencies found in any deployed package.json. All@lilith/*packages consumed by these deploys are version-pinned published artifacts from Verdaccio, not workspace source packages requiring pre-built dist/.