Skip to content

[AISAGE-502] refactor(web-shared): import web-shared by package name, not @/ fallback - #7099

Merged
chrarnoldus merged 4 commits into
mainfrom
resolve-lib-import-alias
Oct 5, 2026
Merged

chrarnoldus merged 4 commits into
mainfrom
resolve-lib-import-alias

Conversation

@chrarnoldus

@chrarnoldus chrarnoldus commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

apps/web, apps/ai-gateway, packages/trpc and Storybook resolved @/* from two roots: the app's own src first, then packages/web-shared/src. #6806 set this up so code could move into web-shared without changing its import specifiers. The result was that two folders behaved as one:

  • 14 lib/ directories (kiloclaw, organizations, kilo-pass, user, trpc, …) exist in both trees, so you can't tell from @/lib/kiloclaw/x which package it is in.
  • A file added under apps/web/src with the same path as a web-shared file would silently replace it inside apps/web. Only AGENTS.md prevented this.
  • The resolution order was copied into 7 places: 4 tsconfigs, Jest, the trpc rollup resolver and the Storybook alias.

This PR imports web-shared code as @kilocode/web-shared/<path under src>, like @kilocode/db/*. @/ now always means the importing app's own src. No file moves and no logic changes.

Commits

# Commit Kind Size
1 Add the @kilocode/web-shared/* mapping alongside the @/ fallback hand-written 8 files, +11
2 Rewrite consumer imports (apps/web, ai-gateway, trpc, storybook, and two service tests that use apps/web's @/) generated 1,693 files, 4,520 specifiers
3 Rewrite web-shared's own imports generated 343 files, 1,596 specifiers
4 Remove the fallback, drop @/ from the web-shared tsconfig, update AGENTS.md hand-written 11 files

Each commit typechecks on its own, because both forms resolve between commits 1 and 4.

Commit 3 is needed because web-shared files are also compiled by the apps. There, @/ meant the app's src first, so web-shared's internal @/ imports only worked because of the fallback.

Verification

  • Ran the script below on the parents of commits 2 and 3, then oxfmt, in a clean worktree. git diff --quiet against each commit passed, so both generated commits are exactly the script's output.
  • next build --experimental-build-mode=compile for apps/web compiled. This checks how Turbopack resolves @kilocode/web-shared/* inside web-shared files.
  • next build for apps/ai-gateway compiled and passed its TypeScript step. Page-data collection then failed because NEXT_PUBLIC_GASTOWN_URL was missing locally; that error came from a web-shared module that had already been imported, which shows the import chain resolved.
  • build-storybook succeeded (webpack alias).
  • tsx --tsconfig tsconfig.scripts.json resolved a web-shared module and its web-shared imports, then stopped at the same missing environment variable.
  • Typecheck passed for web (including the trpc build), ai-gateway, storybook, web-shared (tsconfig.lib.json and tsconfig.json) and trpc.
  • packages/trpc/dist/index.d.ts and dist/mobile.d.ts are byte-identical before and after commit 4.
  • oxlint passed with 0 warnings and 0 errors; pnpm format:check is clean.
  • Full web Jest suite under Node 24: 1,130 of 1,131 suites passed. The remaining one crashed its Jest worker (SIGSEGV) and passes when rerun.
  • Rebuilt on main after fix(models/up): key models by normalized Analytics Engine ID #7097 added an @/lib/dotenvx mock that only resolved through the removed fallback, which broke CI. Commits 2 and 3 are regenerated with the script; commits 1 and 4 are unchanged (git range-diff shows =).
  • CI then caught two tests outside the original scope that use apps/web's @/: services/deploy-infra/builder/test-postgres (runs under the apps/web Jest config) and services/cloud-agent-next/src/telemetry/report-consumer.glanceable.test.ts (mocks the @/ imports of an apps/web module). The script now covers both. kilo-deploy-builder test:postgres passes (11 tests), as does the full cloud-agent-next unit suite (190 files). Both services typecheck.

No visual changes and no runtime behaviour changes, so there was nothing to test manually beyond the builds above.

Visual Changes

N/A

Reviewer Notes

Review commits 1 and 4 by hand, and the script for commits 2 and 3. You don't need to read those two diffs; re-run the script instead:

git checkout <commit 2 or 3>~1
node rewrite-web-shared-imports.mjs consumers   # commit 2
node rewrite-web-shared-imports.mjs web-shared  # commit 3
git diff --name-only -z | xargs -0 pnpm exec oxfmt
git diff --quiet <commit 2 or 3> && echo identical
  • The script uses the old fallback's resolution order: the consumer's own @/ root first, then web-shared. It rewrites every quoted @/… string, so jest.mock, jest.requireActual, import() and backticked file references in comments are updated too.
  • After commit 2, about 600 lines differ from a plain prefix swap. These are imports that went over the 100-character line width, which oxfmt re-wrapped.
  • Two @/ strings were left alone on purpose:
    • apps/web/src/lib/code-indexing/storage.ts has a commented-out import of a file that doesn't exist.
    • A comment in packages/web-shared/src/lib/config.server.ts points at an apps/web file. Commit 4 changes it to the repository path.
  • Open PRs that add @/ imports of web-shared code will stop typechecking after rebasing onto this. Fixing them means changing those imports to @kilocode/web-shared/…, or re-running the script.
rewrite-web-shared-imports.mjs
// Rewrites `@/…` specifiers that resolve to packages/web-shared/src into
// `@kilocode/web-shared/…`, using the same resolution order as the old
// tsconfig fallback: the consumer's own `@/` root first, web-shared second.
//
// Usage (from the repository root):
//   node rewrite-web-shared-imports.mjs consumers   # apps/web, ai-gateway, trpc, storybook, 2 service tests
//   node rewrite-web-shared-imports.mjs web-shared  # packages/web-shared itself
import { execFileSync } from 'node:child_process';
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
import path from 'node:path';

const WEB_SHARED_SRC = 'packages/web-shared/src';
const NEW_PREFIX = '@kilocode/web-shared/';

const ROOTS = {
  consumers: [
    { dir: 'apps/web', localSrc: 'apps/web/src' },
    { dir: 'apps/ai-gateway', localSrc: 'apps/ai-gateway/src' },
    { dir: 'packages/trpc', localSrc: 'apps/web/src' },
    { dir: 'apps/storybook', localSrc: 'apps/web/src' },
    // Tests outside apps/web whose `@/` means apps/web's alias: one runs under
    // the apps/web Jest config, the other mocks the `@/` imports of an apps/web
    // module it loads.
    { dir: 'services/deploy-infra/builder/test-postgres', localSrc: 'apps/web/src' },
    { dir: 'services/cloud-agent-next/src/telemetry', localSrc: 'apps/web/src' },
  ],
  'web-shared': [{ dir: 'packages/web-shared', localSrc: null }],
};

const SOURCE_FILE = /\.(?:[cm]?[jt]sx?|mdx)$/;
const SUFFIXES = [
  '',
  '.ts',
  '.tsx',
  '.d.ts',
  '.js',
  '.jsx',
  '.mjs',
  '.json',
  '/index.ts',
  '/index.tsx',
  '/index.js',
];
// A quoted `@/…` string literal; backtick literals only without interpolation.
const SPECIFIER = /(['"`])@\/([^'"`$\s]+)\1/g;

function resolves(root, subpath) {
  const base = path.join(root, subpath.replace(/\.js$/, ''));
  for (const candidate of [path.join(root, subpath), ...SUFFIXES.map(s => base + s)]) {
    if (existsSync(candidate) && statSync(candidate).isFile()) return true;
  }
  return false;
}

const mode = process.argv[2];
if (!(mode in ROOTS)) {
  console.error(`usage: node ${path.basename(process.argv[1])} <consumers|web-shared>`);
  process.exit(2);
}

let rewritten = 0;
let changedFiles = 0;
const unresolved = [];

for (const { dir, localSrc } of ROOTS[mode]) {
  const files = execFileSync('git', ['ls-files', '-z', dir], { encoding: 'utf8' })
    .split('\0')
    .filter(file => SOURCE_FILE.test(file));

  for (const file of files) {
    const before = readFileSync(file, 'utf8');
    const after = before.replace(SPECIFIER, (match, quote, subpath) => {
      if (localSrc && resolves(localSrc, subpath)) return match;
      if (!resolves(WEB_SHARED_SRC, subpath)) {
        unresolved.push(`${file}: ${match}`);
        return match;
      }
      rewritten += 1;
      return `${quote}${NEW_PREFIX}${subpath}${quote}`;
    });
    if (after !== before) {
      writeFileSync(file, after);
      changedFiles += 1;
    }
  }
}

console.log(`rewrote ${rewritten} specifiers in ${changedFiles} files`);
if (unresolved.length > 0) {
  console.log(`left ${unresolved.length} unresolved @/ strings unchanged:`);
  for (const line of unresolved) console.log(`  ${line}`);
}

@chrarnoldus chrarnoldus self-assigned this Oct 2, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Full re-review of the rebased @/ -> @kilocode/web-shared/* import-alias refactor (2,046 files across 4 commits, head bb6835464) found no functional issues: every rewritten specifier resolves to an existing file, every resolver that previously encoded the @/ -> packages/web-shared/src fallback now handles the new package prefix, and no ambiguous @/ reference to web-shared remains anywhere in the consumer or web-shared trees.

Files Reviewed (2046 files)

Hand-written commits (reviewed line by line):

  • apps/web/tsconfig.json, apps/web/tsconfig.scripts.json, apps/web/jest.config.ts
  • apps/ai-gateway/tsconfig.json, apps/ai-gateway/AGENTS.md
  • apps/storybook/.storybook/main.ts
  • packages/trpc/tsconfig.json, packages/trpc/rollup.config.mjs
  • packages/web-shared/tsconfig.json, packages/web-shared/AGENTS.md, packages/web-shared/src/lib/config.server.ts

Generated commits, verified by resolving every changed specifier against the filesystem:

  • apps/web/** (~1,670 files): 1,063 unique @/... specifiers all resolve under apps/web/src; 208 unique @kilocode/web-shared/... specifiers all resolve under packages/web-shared/src
  • packages/web-shared/** (~345 files): 0 remaining @/ specifiers; all self-imports resolve under packages/web-shared/src
  • apps/ai-gateway/**, apps/storybook/**, packages/trpc/**, and the two service tests (services/deploy-infra/builder/test-postgres, services/cloud-agent-next/src/telemetry) all resolve
Previous Review Summary (commit fcb36a9)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit fcb36a9)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental re-review of the rebased import-alias refactor (HEAD 07a8969ec) found no new issues: hand-written commits 1 and 4 are byte-identical to the previously reviewed versions, and only the generated consumer/web-shared commits were regenerated to extend the mechanical @/ → @kilocode/web-shared/* rewrites to files added on main. Every new rewrite target exists and none shadow a same-path file under apps/web/src.

Files Reviewed (25 files)

Newly covered by the regenerated import commits (each rewritten specifier resolves to an existing packages/web-shared/src module):

  • apps/ai-gateway/src/instrumentation.ts
  • apps/web/src/instrumentation.ts
  • apps/web/src/components/PostHogProvider.tsx
  • apps/web/src/components/shared/usePreferredModels.ts
  • apps/web/src/app/api/cron/cleanup-verified-domain-claims/route.ts, .../route.test.ts
  • apps/web/src/app/api/cron/dispatch-bouncer-credit-event-outbox/route.ts
  • apps/web/src/lib/bouncer/dispatch-credit-event-outbox.ts, .../dispatch-credit-event-outbox.test.ts, .../bouncer-credit-event-outbox.integration.test.ts
  • apps/web/src/lib/kilo-pass/checkout-session.test.ts
  • apps/web/src/lib/organizations/verified-domain-cleanup.ts, .../verified-domain-cleanup.test.ts
  • packages/web-shared/src/lib/ai-gateway/auto-model/auto-free-config.ts, .../auto-free-config.test.ts
  • packages/web-shared/src/lib/ai-gateway/preferred-models.ts, .../preferred-models.test.ts
  • packages/web-shared/src/lib/bouncer/inference.ts, .../inference.test.ts
  • packages/web-shared/src/lib/observability/node-instrumentation.ts
  • packages/web-shared/src/lib/observability/sentry-server.ts, .../sentry-server.test.ts
  • packages/web-shared/src/lib/sanitize-analytics-url.test.ts

Hand-written commits re-verified unchanged:

  • apps/ai-gateway/tsconfig.json, apps/ai-gateway/AGENTS.md
  • apps/storybook/.storybook/main.ts
  • apps/web/jest.config.ts, apps/web/tsconfig.json, apps/web/tsconfig.scripts.json
  • packages/trpc/rollup.config.mjs, packages/trpc/tsconfig.json
  • packages/web-shared/AGENTS.md, packages/web-shared/tsconfig.json, packages/web-shared/src/lib/config.server.ts

Reviewed by deepseek-v4.1-flash · Input: 66.7K · Output: 22.1K · Cached: 1.1M

Review guidance: REVIEW.md from base branch main

@chrarnoldus
chrarnoldus force-pushed the resolve-lib-import-alias branch 3 times, most recently from fcb36a9 to 07a8969 Compare October 5, 2026 12:00
Map @kilocode/web-shared/* to packages/web-shared/src in every place that
resolves the @/ fallback today: the web, ai-gateway, scripts, trpc and
web-shared tsconfigs, the web Jest config, the @kilocode/trpc rollup
resolver and the Storybook webpack alias. Both specifiers resolve, so the
following import rewrites can land without breaking anything in between.
…mers

Generated. In apps/web, apps/ai-gateway, packages/trpc and apps/storybook,
every '@/…' string that resolved to packages/web-shared/src through the
tsconfig fallback now reads '@kilocode/web-shared/…'. Strings that resolve
to the consumer's own src are untouched. The rewrite uses the fallback's
resolution order: the consumer's '@/' root first, web-shared second.

Two service tests use '@/' with the apps/web meaning and are rewritten the
same way: the deploy-builder Postgres test, which runs under the apps/web
Jest config, and a cloud-agent-next test that mocks the '@/' imports of an
apps/web module it loads.

Reproduce on the parent commit with the script from the PR description:

  node rewrite-web-shared-imports.mjs consumers
  git diff --name-only -z | xargs -0 pnpm exec oxfmt

Formatting only rewraps imports that now exceed the line width.
Generated. Inside packages/web-shared, every '@/…' string now reads
'@kilocode/web-shared/…'. These files are also compiled by apps/web and
apps/ai-gateway, where '@/' means the app's own src first, so they must not
rely on '@/' once the fallback is removed.

Reproduce on the parent commit with the script from the PR description:

  node rewrite-web-shared-imports.mjs web-shared
  git diff --name-only -z | xargs -0 pnpm exec oxfmt
Every import of web-shared code now uses @kilocode/web-shared/…, so '@/'
maps to the importing app's own src only. Drop the second '@/' target from
the web, ai-gateway, scripts and trpc tsconfigs, the web Jest config, the
@kilocode/trpc rollup resolver and the Storybook webpack alias, and drop
'@/' from the web-shared tsconfig so a stray '@/' import there fails its
typecheck.

A file under apps/web/src can no longer shadow a web-shared file with the
same path, so the rule that a path exists in only one tree goes away.
@chrarnoldus
chrarnoldus force-pushed the resolve-lib-import-alias branch from 07a8969 to bb68354 Compare October 5, 2026 12:06
@chrarnoldus
chrarnoldus merged commit 1127c40 into main Oct 5, 2026
31 checks passed
@chrarnoldus
chrarnoldus deleted the resolve-lib-import-alias branch October 5, 2026 13:10
chrarnoldus added a commit that referenced this pull request Oct 5, 2026
Resolve the models/up import conflict and switch the branch's new web-shared imports (dotenvx, trpc/init) to the @kilocode/web-shared package name, since #7099 removed the @/ fallback.
@chrarnoldus chrarnoldus changed the title refactor(web-shared): import web-shared by package name, not @/ fallback [AISAGE-502] refactor(web-shared): import web-shared by package name, not @/ fallback Oct 19, 2026
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