Skip to content

T-F31Q-migrate-site-to-bun

Status: closed/done · Impact: medium · Complexity: small

The Astro/Starlight docs site under site/ is the last npm-managed corner of an otherwise Bun-on-root workspace, and it carries two lockfiles — a live package-lock.json and a stale pnpm-lock.yaml. D-0012-monorepo-tooling unifies the JS/TS ecosystem on Bun under the moon runner; moon prefers a single package manager per workspace. Move site/ onto Bun, collapse to one lockfile, and register it as a moon project so the workspace has one PM end to end.

LocationRole today
site/package.jsonAstro site manifest; npm-style scripts (astro dev/build/preview) run under npm
site/package-lock.jsonlive npm lockfile (~220 KB) — the dependency manager of record for site/
site/pnpm-lock.yamlstale pnpm lockfile (~128 KB) left from the original scaffold; no pnpm in use
site/.gitignoreignores node_modules/, dist/, .astro/; no lockfile entries
package.jsonroot workspace manifest, Bun-managed (engines.bun >=1.2.0)
bun.lockroot Bun lockfile — the single PM of record everywhere except site/
README.md:78”Documentation site” quickstart instructs npm --prefix site install / npm --prefix site dev
sdlc.yaml#worktree_initruns bun install at root on worktree creation; never touches site/

The site content root site/src/content/docs/ is generated by sdlc docs generate site (plugin/lib/services/docs/site.ts), a Bun-run sdlc CLI verb that writes markdown directly and is package-manager-agnostic — it never shells out to npm or astro. astro build (Starlight) consumes that content; sharp is pulled in as the Astro image-optimization dependency.

site/ is a Bun project: its dependencies are installed and locked by Bun (site/bun.lock), its scripts run under Bun (bunx astro …), and it is registered as a moon project via site/moon.yml on the node/bun toolchain. Exactly one lockfile remains in site/ (site/bun.lock); both site/package-lock.json and the stale site/pnpm-lock.yaml are gone. astro build and sdlc docs generate site both still succeed, and moon run site:build drives the build through the workspace runner.

  1. Delete site/package-lock.json and site/pnpm-lock.yaml.
  2. Run bun install inside site/, producing site/bun.lock. Confirm sharp resolves and installs under Bun — it ships prebuilt platform binaries, but Bun’s resolver is the one real risk here (see Out of scope note); if the prebuilt binary is missing for the host, allow Bun’s postinstall to fetch it rather than working around it.
  3. Rewrite site/package.json scripts to invoke astro through Bun: each script becomes bunx astro <cmd> (dev, start, build, preview, astro).
  4. Add site/moon.yml registering the site as a moon project on the node/bun toolchain, with at minimum a build task running bunx astro build (so moon run site:build resolves). Align task wiring with the workspace .moon/ config landed by T-RVMG.
  5. Add site/bun.lock to git (it is the new lockfile of record) and confirm site/.gitignore does not ignore it; leave the existing node_modules/ / dist/ / .astro/ ignores as-is.
  6. Update README.md’s “Documentation site” quickstart from npm --prefix site … to the Bun equivalent (bun install --cwd site / bunx --bun astro dev or bun run --cwd site dev).
  7. Verify: bunx astro build succeeds; sdlc docs generate site --check exits zero (no drift); moon run site:build succeeds.
LocationKindChange
site/package-lock.jsondeleteremove the npm lockfile
site/pnpm-lock.yamldeleteremove the stale pnpm lockfile
site/package.jsonmodifyrewrite scripts to run astro under Bun (bunx astro …)
README.mdmodifyswap the npm --prefix site … quickstart for the Bun equivalent
site/moon.ymlnewregister site as a moon project (node/bun toolchain) with a build task
site/bun.locknewBun lockfile produced by bun install in site/
  • AC-1: site/package-lock.json and site/pnpm-lock.yaml no longer exist; site/bun.lock is the only lockfile under site/.
  • AC-2: bunx astro build (run in site/) exits zero and writes site/dist/.
  • AC-3: sdlc docs generate site --check exits zero against the migrated site (generator still writes site/src/content/docs/ unchanged).
  • AC-4: moon run site:build exits zero, driving the Astro build through the workspace runner.
  • AC-5: no npm --prefix site invocation remains in README.md.
  • CI workflow for the site (lint/build/deploy) — manual build is sufficient, as in T-6D5U-add-astro-docs-site.
  • The .moon/workspace.yml / .moon/toolchains.yml workspace scaffold and the apps/ + packages/ trees — owned by T-RVMG.
  • Extending sdlc.yaml#worktree_init to bun install inside site/ — the site is not part of this repo’s test corpus; defer unless a later task needs it.
  • sharp risk note (not a deliverable, a watch item): sharp is Astro’s image dep and the one component historically finicky under Bun’s installer. If bun install cannot resolve its prebuilt binary on the host, fetch the platform binary rather than reverting to npm; do not pin away from Bun.
  • T-RVMG — adopts moon as the workspace runner and lands the .moon/ config (workspace + node/bun toolchain) plus the apps//packages/ trees. site/moon.yml has nothing to register against until that workspace exists, and the single-PM decision it enforces is the reason this migration runs. Hard dependency; recorded in depends_on:.

Captured by /sdlc:task-work on 2026-06-19. PR: pending.

  • AC-1: agent-manual — ls site/ confirms only site/bun.lock remains; both package-lock.json and pnpm-lock.yaml deleted.
  • AC-2: agent-manual — cd site && bunx astro build exit 0 (599 pages, wrote site/dist/).
  • AC-3: agent-manual — sdlc docs generate site --check exit 0 (wrote: false, drift: []).
  • AC-4: agent-manual — moon run site:build exit 0 via moon 1.41.8 (see friction re: PATH moon 2.0-rc).
  • AC-5: auto — grep -n 'npm --prefix site' README.md returns no matches.
  • sharp resolved cleanly under Bun 1.2.21 with prebuilt darwin-arm64 binaries — no postinstall fallback needed, contra the task’s watch-item risk.
  • The migration was mechanical (lockfile swap, bunx astro scripts, site/moon.yml); all three verification builds passed first try.
  • project-check-task-state-origin blocked every branch commit because Step 5a/5b’s task-state commits (verify-stamp, start-commit) were on local main but not on a stale origin/main; had to push origin/main mid-flow to unblock — already tracked as T-6R73-precommit-hooks-consult-quality-baseline (promoted from B-8YI7). → T-6R73-pre-commit-drift-hooks-gate-unconditionally-forcing-no
  • Step 7’s quality run from the worktree defaulted --baseline-dir to the worktree-local .sdlc/ and could not find the Step 3a baseline (written to the superproject .sdlc/quality-baselines/); had to pass --baseline-dir <superproject> explicitly — the baseline-dir-superproject resolution tracked by T-44OO-plugin-scripts-self-discover-project-root. → T-44OO-plugin-scripts-self-discover-project-root
  • Step 7 false-flagged 1 new-drift on the rumdl summary line’s (63ms) timing token; the 9 underlying MD013 issues (in untouched T-XBJY-…md) all subtracted as pre-existing — normalizeFinding does not mask timing tokens. Exactly T-BQRU-quality-normalize-ports-pids-timings. → T-BQRU-quality-normalize-ports-pids-timings
  • The moon on PATH is 2.0.0-rc.0, which fails the workspace’s versionConstraint: '>=1.41.0' (.moon/workspace.yml); moon run site:build only works with an explicitly-installed 1.41.x. The repo pins no moon version in .prototools/mise — pin moon 1.41.x (or bump the constraint) so the default-PATH moon can drive the workspace runner. → T-FFHB-pin-moon-version-in-prototools

T-RVMG


← Back to Tasks