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.
| Location | Role today |
|---|---|
site/package.json | Astro site manifest; npm-style scripts (astro dev/build/preview) run under npm |
site/package-lock.json | live npm lockfile (~220 KB) — the dependency manager of record for site/ |
site/pnpm-lock.yaml | stale pnpm lockfile (~128 KB) left from the original scaffold; no pnpm in use |
site/.gitignore | ignores node_modules/, dist/, .astro/; no lockfile entries |
package.json | root workspace manifest, Bun-managed (engines.bun >=1.2.0) |
bun.lock | root 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_init | runs 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.
Proposed
Section titled “Proposed”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.
Approach
Section titled “Approach”- Delete
site/package-lock.jsonandsite/pnpm-lock.yaml. - Run
bun installinsidesite/, producingsite/bun.lock. Confirmsharpresolves 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. - Rewrite
site/package.jsonscripts to invoke astro through Bun: each script becomesbunx astro <cmd>(dev,start,build,preview,astro). - Add
site/moon.ymlregistering the site as a moon project on the node/bun toolchain, with at minimum abuildtask runningbunx astro build(somoon run site:buildresolves). Align task wiring with the workspace.moon/config landed byT-RVMG. - Add
site/bun.lockto git (it is the new lockfile of record) and confirmsite/.gitignoredoes not ignore it; leave the existingnode_modules//dist//.astro/ignores as-is. - Update
README.md’s “Documentation site” quickstart fromnpm --prefix site …to the Bun equivalent (bun install --cwd site/bunx --bun astro devorbun run --cwd site dev). - Verify:
bunx astro buildsucceeds;sdlc docs generate site --checkexits zero (no drift);moon run site:buildsucceeds.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
site/package-lock.json | delete | remove the npm lockfile |
site/pnpm-lock.yaml | delete | remove the stale pnpm lockfile |
site/package.json | modify | rewrite scripts to run astro under Bun (bunx astro …) |
README.md | modify | swap the npm --prefix site … quickstart for the Bun equivalent |
site/moon.yml | new | register site as a moon project (node/bun toolchain) with a build task |
site/bun.lock | new | Bun lockfile produced by bun install in site/ |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
site/package-lock.jsonandsite/pnpm-lock.yamlno longer exist;site/bun.lockis the only lockfile undersite/. - AC-2:
bunx astro build(run insite/) exits zero and writessite/dist/. - AC-3:
sdlc docs generate site --checkexits zero against the migrated site (generator still writessite/src/content/docs/unchanged). - AC-4:
moon run site:buildexits zero, driving the Astro build through the workspace runner. - AC-5: no
npm --prefix siteinvocation remains inREADME.md.
Out of scope
Section titled “Out of scope”- 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.ymlworkspace scaffold and theapps/+packages/trees — owned byT-RVMG. - Extending
sdlc.yaml#worktree_inittobun installinsidesite/— the site is not part of this repo’s test corpus; defer unless a later task needs it. sharprisk note (not a deliverable, a watch item):sharpis Astro’s image dep and the one component historically finicky under Bun’s installer. Ifbun installcannot resolve its prebuilt binary on the host, fetch the platform binary rather than reverting to npm; do not pin away from Bun.
Dependencies
Section titled “Dependencies”T-RVMG— adopts moon as the workspace runner and lands the.moon/config (workspace + node/bun toolchain) plus theapps//packages/trees.site/moon.ymlhas nothing to register against until that workspace exists, and the single-PM decision it enforces is the reason this migration runs. Hard dependency; recorded independs_on:.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-06-19. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: agent-manual —
ls site/confirms onlysite/bun.lockremains; bothpackage-lock.jsonandpnpm-lock.yamldeleted. - AC-2: agent-manual —
cd site && bunx astro buildexit 0 (599 pages, wrotesite/dist/). - AC-3: agent-manual —
sdlc docs generate site --checkexit 0 (wrote: false,drift: []). - AC-4: agent-manual —
moon run site:buildexit 0 via moon 1.41.8 (see friction re: PATH moon 2.0-rc). - AC-5: auto —
grep -n 'npm --prefix site' README.mdreturns no matches.
What worked
Section titled “What worked”sharpresolved 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 astroscripts,site/moon.yml); all three verification builds passed first try.
Friction and automation gaps
Section titled “Friction and automation gaps”project-check-task-state-originblocked every branch commit because Step 5a/5b’s task-state commits (verify-stamp, start-commit) were on local main but not on a staleorigin/main; had to pushorigin/mainmid-flow to unblock — already tracked asT-6R73-precommit-hooks-consult-quality-baseline(promoted from B-8YI7). → T-6R73-pre-commit-drift-hooks-gate-unconditionally-forcing-no- Step 7’s
quality runfrom the worktree defaulted--baseline-dirto 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-drifton the rumdl summary line’s(63ms)timing token; the 9 underlying MD013 issues (in untouchedT-XBJY-…md) all subtracted as pre-existing —normalizeFindingdoes not mask timing tokens. Exactly T-BQRU-quality-normalize-ports-pids-timings. → T-BQRU-quality-normalize-ports-pids-timings - The
moonon PATH is2.0.0-rc.0, which fails the workspace’sversionConstraint: '>=1.41.0'(.moon/workspace.yml);moon run site:buildonly 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-PATHmooncan drive the workspace runner. → T-FFHB-pin-moon-version-in-prototools
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”- T-FFHB-pin-moon-version-in-prototools — spawned (Local); pin moon 1.41.x in a root
.prototoolsso the default-PATH moon satisfies the workspaceversionConstraint. Rides this T-F31Q PR per direction (Local friction in this repo’s own config). - T-6R73-pre-commit-drift-hooks-gate-unconditionally-forcing-no — linked; existing tracker for
the pre-commit drift/task-state-origin gate firing on stale
origin/main. - T-44OO-plugin-scripts-self-discover-project-root — linked; existing tracker for the baseline-dir worktree-vs-superproject silent-fallback (sixth post-mortem to surface it).
- T-BQRU-quality-normalize-ports-pids-timings — linked; existing tracker for
normalizeFindingmasking timing tokens before baseline-diff.
Depends on
Section titled “Depends on”T-RVMG