Skip to content

refactor: extract metro/ folder — Phase 5#947

Merged
thymikee merged 1 commit into
mainfrom
refactor/phase5-metro
Jun 30, 2026
Merged

refactor: extract metro/ folder — Phase 5#947
thymikee merged 1 commit into
mainfrom
refactor/phase5-metro

Conversation

@thymikee

Copy link
Copy Markdown
Member

Summary

Phase 5 (layering) slice from plans/perfect-shape.md §5.5 — extract the metro cluster from src/ root into a src/metro/ folder. Pure path codemod, no behavior change.

§5.5 target: metro/ ← metro* · client-metro*.

Move set (→ src/metro/)

file importers why it moves
metro.ts 4 the metro public surface (rslib metro entry)
metro-types.ts 2 metro-domain types, used only by metro/client-metro
client-metro.ts 10 the metro client driver
client-metro-companion.ts 6 metro companion lifecycle (ensure/stopMetroCompanion)

These four form a cohesive metro domain: metro.tsclient-metro* + metro-types; client-metro.tsclient-metro-companion + metro-types. Moving them together keeps intra-folder imports as ./ (unchanged); only their edges to root infra (contracts, kernel/errors, utils/*, client-companion-tunnel-contract) and their external importers were rewritten.

Deliberately kept OUT (stay at src/ root)

The companion-tunnel cluster — client-companion-tunnel-contract.ts, client-companion-tunnel.ts, client-companion-tunnel-worker.ts, companion-tunnel.ts, client-react-devtools-companion.ts — is a separate domain (the future companion/ slice, not part of metro*/client-metro*). Keeping it at root avoids a reverse/sibling churn and, critically, keeps the worker process entrypoint co-located with its spawner (see below). General-purpose infra (utils, contracts, kernel) also stays out.

Worker-path verification (the critical risk for this slice)

metro.ts is itself an rslib entrypoint (metro: 'src/metro.ts'), and the companion tunnel runs as a detached child process whose entry is src/companion-tunnel.ts (rslib entry internal/companion-tunnel). That worker is not a new Worker(new URL('./worker.ts')) thread — client-companion-tunnel.ts resolves the entry by name relative to its own built location in dist/ and spawns it via runCmdDetached.

  • The spawner (client-companion-tunnel.ts) and the worker entry source (companion-tunnel.ts) both stay at root → entry-name resolution is unchanged.
  • The worker bundle chain reaches the moved metro.ts (companion-tunnel.tsclient-companion-tunnel-worker.tsmetro.ts); that import was rewritten and re-bundled.
  • Verified by npm run build (exit 0), which emits both dist/src/metro.js and dist/src/internal/companion-tunnel.js. The metro output path is keyed by the entry name, so package.json ./metro (→ dist/src/metro.js) is unaffected.

Codemod

A resolve-based node codemod: for every .ts in src/test, each relative import/vi.mock/vi.importActual specifier is resolved to an absolute path against the file's original dir, mapped through the old→new move table, and rewritten relative to the file's new dir (compares resolved paths — no naive string replace). Three non-.ts references were updated to the new paths: the rslib metro entry, the .fallowrc.json entry list, and the fallow-baselines/health.json baseline key. tsconfig uses glob includes (no change).

Verification (all green)

  • tsc -p tsconfig.json --noEmit → exit 0
  • oxfmt --check src test → exit 0; oxlint src --deny-warnings → exit 0
  • npm run build → exit 0 (metro entry + companion-tunnel worker bundle both emitted)
  • fallow audit --base origin/main → CLEAN (no issues, 21 changed files)
  • vitest run (full) → 314 files / 2892 tests passed; affected metro/client tests: 7 files / 58 tests passed
  • Layering Guard (git grep for commands/ imports in daemon/platforms/kernel) → EMPTY
  • No stale references to the old paths anywhere (src/test/json); git tracks all 4 files as renames

Pure path codemod — no runtime behavior change.

Move the metro cluster into src/metro/ per plans/perfect-shape.md §5.5
(`metro/ ← metro* · client-metro*`):

  src/metro.ts                 -> src/metro/metro.ts
  src/metro-types.ts           -> src/metro/metro-types.ts
  src/client-metro.ts          -> src/metro/client-metro.ts
  src/client-metro-companion.ts-> src/metro/client-metro-companion.ts

Pure path codemod, no behavior change. Imports were rewritten by a
resolve-based codemod (compares resolved absolute paths, not naive
string match). Also updated the three non-.ts references to the moved
paths: the rslib `metro` entry, the fallow entry list, and the
fallow health-baseline key.

The companion-tunnel cluster (client-companion-tunnel*, companion-tunnel,
client-react-devtools-companion) stays at src/ root — it is a separate
domain (the future `companion/` slice) and keeps the worker process
entrypoint (companion-tunnel.ts / client-companion-tunnel-worker.ts)
co-located with its spawner (client-companion-tunnel.ts), so the worker
entry resolution is unchanged. `npm run build` emits both
dist/src/metro.js and dist/src/internal/companion-tunnel.js.
@github-actions

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.4 MB 1.4 MB 0 B
JS gzip 455.5 kB 455.5 kB +4 B
npm tarball 561.0 kB 561.2 kB +185 B
npm unpacked 2.0 MB 2.0 MB 0 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 25.4 ms 25.9 ms +0.4 ms
CLI --help 50.1 ms 46.9 ms -3.2 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/1153.js +17.5 kB +6.0 kB
dist/src/cli.js 0 B +1 B
dist/src/9919.js 0 B +1 B

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed against plans/perfect-shape.md §5.5 and ADR 0008/0003 boundaries.

This looks ready for maintainer review. I checked the PR as a pure metro-domain move: metro.ts, metro-types.ts, client-metro.ts, and client-metro-companion.ts moved under src/metro/; external imports were mechanically updated; rslib.config.ts still preserves the public ./metro package entry output as dist/src/metro.js; the companion tunnel worker stays rooted and only its import of the metro protocol moved.

Additional checks I ran locally while reviewing:

  • no stale source references to src/client-metro*, src/metro-types, or src/metro.ts
  • no stale relative imports from the old root metro files
  • git diff --check origin/main...origin/pr/947 clean

CI is green across 21 checks. I did not find an actionable blocker.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Jun 30, 2026
@thymikee thymikee merged commit 3aa43bf into main Jun 30, 2026
21 checks passed
@thymikee thymikee deleted the refactor/phase5-metro branch June 30, 2026 06:22
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-30 06:22 UTC

thymikee added a commit that referenced this pull request Jun 30, 2026
Relocate the central contracts barrel into the kernel/ dependency sink
alongside device/errors/redaction/snapshot (kernel now owns the pure
domain types per plans/perfect-shape.md §5.5).

- src/contracts.ts -> src/kernel/contracts.ts (git rename)
- repoint all 44 internal importers to ../kernel/contracts.ts
- rslib entry keeps key 'contracts' so dist output stays dist/src/contracts.js;
  the public 'agent-device/contracts' subpath is byte-identical (proven by the
  metro precedent in #947 and verified via build + package-exports test)
- update .fallowrc.json entrypoint + fallow-baselines/health.json key

Behaviorless path codemod (49 files, +57/-57). typecheck/lint/build/fallow
audit/public-contract tests all green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant