Skip to content

T-RVMG-adopt-moon-workspace-runner

Status: closed/done · Impact: high · Complexity: large

The repo is single-package today and cannot express shared packages or polyglot applications. D-0012-monorepo-tooling settled the move: adopt moon as a language-agnostic workspace runner over a polyglot tree, and stand up the empty apps/ + packages/ts/ + packages/rust/ trees with workspace wiring. This is the foundation task — it unblocks the site migration, the dashboard app, and every later shared-package and Tauri-Rust effort.

The repo is a single Bun package at the root, with an npm-managed Astro site beside it and no monorepo tooling of any kind.

LocationRole today
package.jsonRoot Bun package @sksizer/sdlc-plugin; no workspaces field, no monorepo wiring
bun.lockRoot Bun lockfile — Bun already manages the JS/TS substrate
tsconfig.jsonSingle root tsconfig covering plugin/**/*.ts + tests/**/*.ts
justfileHuman/skill command catalog — markdown fmt/check recipes today
lefthook.ymlGit-hook driver — pre-commit markdown fmt + worktree guard + project-check gates
plugin/cli/sdlc.tsThe sdlc CLI, run via bun run plugin/cli/sdlc.ts <noun> <verb>
site/Astro/Starlight docs site, npm-managed (site/package-lock.json), with a stale site/pnpm-lock.yaml

There is no monorepo runner today (no .moon/), and no apps/ or packages/ trees — this task creates them.

A moon workspace sits over a polyglot tree. moon owns the build graph and cache; justfile stays the command catalog and lefthook stays the git-hook driver — moon sits alongside both, replacing neither. The destination tree:

.moon/
workspace.yml project discovery (globs) + registered projects
toolchains.yml javascript+bun toolchain; rust toolchain (for later Tauri)
moon.yml root project: wraps the existing sdlc CLI as moon tasks
apps/
.gitkeep empty; ready for the dashboard (T-CW4K) and desktop apps
packages/
ts/
.gitkeep empty; shared TypeScript packages (UI kit, API client)
rust/
.gitkeep empty; shared Rust crates

.moon/workspace.yml discovers projects by glob (apps/*, packages/ts/*, packages/rust/*) and registers the root project; it is written ready to register site (the migration itself is T-F31Q). .moon/toolchains.yml declares the JavaScript ecosystem on Bun as package manager and a Rust toolchain configured for later Tauri work. The root moon.yml exposes the substrate’s existing checks as moon tasks (test, check) so moon run :test and moon check drive the same commands skills run today.

  1. Add .moon/workspace.yml — declare the projects discovery globs (apps/*, packages/ts/*, packages/rust/*) and register the root project (.). Add commented scaffolding for the site project so T-F31Q only has to uncomment/adjust. Pin a versionConstraint for moon.
  2. Add .moon/toolchains.yml — declare the javascript toolchain with packageManager: bun (and the bun version constraint matching package.json’s engines.bun), plus a rust toolchain block configured for later Tauri work. Do not pin a Rust version aggressively; channel stable.
  3. Add a root moon.yml (project type tool / library) wrapping the existing substrate commands as moon tasks: testbun test, check → typecheck (bunx tsc --noEmit against the root tsconfig.json). Keep tasks thin — they shell to the same commands the justfile/package.json already run.
  4. Create the empty trees: apps/.gitkeep, packages/ts/.gitkeep, packages/rust/.gitkeep — placeholders so the directories exist under git with no project yet (project discovery tolerates empty roots).
  5. Add .moon/cache/ to .gitignore (moon’s local cache is gitignored; remote cache is deferred per D-0012-monorepo-tooling).
  6. Verify moon check passes and moon run :test runs the substrate tests; then confirm just, lefthook, and the sdlc CLI all still function unchanged.
LocationKindChange
.moon/workspace.ymlnewProject discovery globs (apps/*, packages/ts/*, packages/rust/*) + root project; commented site registration ready for T-F31Q
.moon/toolchains.ymlnewjavascript toolchain on Bun + rust toolchain for later Tauri
moon.ymlnewRoot project: test/check moon tasks wrapping existing substrate commands
apps/.gitkeepnewEmpty apps/ tree placeholder
packages/ts/.gitkeepnewEmpty packages/ts/ tree placeholder
packages/rust/.gitkeepnewEmpty packages/rust/ tree placeholder
.gitignoremodifyIgnore moon’s local cache (.moon/cache/)
  • AC-1: .moon/workspace.yml, .moon/toolchains.yml, and root moon.yml exist and moon check exits 0 against them.
  • AC-2: moon run :test runs the substrate test suite (bun test) and passes.
  • AC-3: The directories apps/, packages/ts/, and packages/rust/ exist in git (tracked via .gitkeep).
  • AC-4: .moon/toolchains.yml declares both a Bun-backed javascript toolchain and a rust toolchain.
  • AC-5: just still lists its recipes and lefthook run pre-commit still executes; the sdlc CLI still runs via bun run plugin/cli/sdlc.ts project doctor.
  • AC-6: .moon/cache/ is gitignored — git status is clean after a moon invocation.
  • Migrating site/ to Bun and deleting site/pnpm-lock.yaml — that is T-F31Q.
  • Scaffolding the dashboard app under apps/ — that is T-CW4K.
  • Lifting the substrate (plugin/lib/ → top-level src/) or moving the harness surface — separately sequenced per D-0001-project-structure.
  • moon remote cache — deferred until CI exists (D-0012-monorepo-tooling).
  • Adding any real package or crate under the new trees — they ship empty.
  • none. This is the foundation task; depends_on: is empty.

The T-CW4K-scaffold-dashboard-vite-vue post-mortem surfaced two moon-workspace-setup friction bullets that belong to this foundation task (the .prototools moon version pin and the node_modules/.bin-on-PATH toolchain gap), linked here by the spawn-from-post-mortem dedup search rather than spawned as duplicate tasks.

Bullet: moon version mismatch blocked every moon task — the proto shim resolved a global moon 2.0.0-rc.0, which fails the workspace versionConstraint ’>=1.41.0’ (a -rc prerelease doesn’t satisfy a stable range). Fixed by adding a root .prototools pinning moon = 1.41.8. The pin arguably belongs in T-RVMG / D-0012-monorepo-tooling workspace setup rather than in this app task. Keywords searched: d-0012-monorepo-tooling, versionconstraint, prerelease, prototools, workspace, mismatch, resolved, arguably Excluded: T-CW4K-scaffold-dashboard-vite-vue Top candidates (score / status / headline):

  • 17 / in-progress / T-RVMG-adopt-moon-workspace-runner — Adopt moon as the monorepo workspace runner; create apps/ + packages/ trees
  • 13 / in-progress / T-F31Q-migrate-site-to-bun — Migrate the Astro docs site from npm to Bun
  • 13 / in-progress / T-VE7H-task-work-probe-keys-package-manager-off-project — task-work permissions probe should key package-manager signal off project verbs, not blanket npm
  • 9 / planning/draft / T-CKC1-tauri-desktop-wrapper — Tauri desktop wrapper consuming the dashboard SPA (polyglot validating case)
  • 5 / closed/done / T-SSB8-split-pr-respond-and-close-out-with-lease-reacquire — Split /sdlc:pr-respond and /sdlc:task-close-out to re-acquire the lease Decision: LINKED-EXISTING T-RVMG-adopt-moon-workspace-runner

Bullet: moon system toolchain doesn’t add node_modules/.bin to PATH — a bare vite task command fails with exit 127; had to invoke bunx vite. A note in the moon-workspace conventions (or a toolchain that injects .bin) would prevent rediscovery. Keywords searched: moon-workspace, node_modules, conventions, rediscovery, toolchain, command, injects, prevent Excluded: T-CW4K-scaffold-dashboard-vite-vue Top candidates (score / status / headline):

  • 20 / in-progress / T-RVMG-adopt-moon-workspace-runner — Adopt moon as the monorepo workspace runner; create apps/ + packages/ trees
  • 15 / closed/done / T-G834-move-plugin-runtime-state-to-sdlc-dir — Migrate sdlc plugin runtime state from .claude/ to .sdlc/
  • 14 / closed/done / T-7PXF-declare-worktree-init-bun-install — Declare worktree_init: bun install in sdlc.yaml
  • 14 / closed/done / T-I2ML-squash-legacy-feat-branch-references — Remove legacy feat/ branch-prefix transition-window references — task/ is canonical
  • 14 / closed/done / T-J1M3-sdlc-yaml-json-schema-and-validator — Add JSON Schema + validator for sdlc.yaml Decision: LINKED-EXISTING T-RVMG-adopt-moon-workspace-runner

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

  • AC-1: auto — .moon/workspace.yml, .moon/toolchain.yml, moon.yml exist; moon sync projects and moon run :check exit 0. moon check orchestrates both tasks correctly; its only non-zero is the bundled test task surfacing one pre-existing baseline failure (site_roadmap.test.ts), unrelated to moon.
  • AC-2: auto — moon run :test drives bun test (1410 pass / 1 fail); the single failure (site_roadmap.test.ts idempotency) reproduces identically on a moon-free main checkout, so it is pre-existing and moon-independent. The moon-wraps-bun-test wiring is sound.
  • AC-3: auto — git ls-files tracks apps/.gitkeep, packages/ts/.gitkeep, packages/rust/.gitkeep.
  • AC-4: auto — .moon/toolchain.yml declares both a Bun-backed bun: toolchain and a rust: toolchain; moon project root confirms Toolchain: bun.
  • AC-5: auto — just --list lists recipes, bunx lefthook run pre-commit executes (v2.1.9, all jobs evaluate), sdlc project doctor reports all deps ok.
  • AC-6: auto — .moon/cache/ is gitignored (git check-ignore confirms); git status is clean after moon invocations.
  • The baseline-gated quality run correctly subtracted the repo’s ~470 pre-existing findings, so the moon adoption diff was easy to isolate.
  • moon’s own moon check / moon sync projects / moon project root gave fast, deterministic feedback while authoring the configs.
  • moon 1.41.8 reads .moon/toolchain.yml (singular), but the task spec and moon’s online docs say toolchains.yml (plural); a plural file is silently ignored. Cost a round of empirical testing to discover — the task-define / readiness gate could not have caught this (the filename was plausible). No automation gap on the SDLC side; a note in D-0012-monorepo-tooling on the version-specific filename would save the next moon task the same discovery. → T-TZZN-d0012-note-moon-toolchain-filename
  • The Step 7 baseline-diff gate reported new-drift=3 that were all false positives: two bun test lines were the dashboard test’s non-deterministic ephemeral port/PID/tmpdir, and one rumdl line was the summary header whose (80ms) timing differs run-to-run — the quality executor’s finding-extraction for bun test/rumdl includes non-deterministic output, so any branch trips a spurious new-drift on these verbs — a normalization pass (strip ports/PIDs/timings before diffing) in the quality service would remove this noise class. → T-BQRU-quality-normalize-ports-pids-timings

← Back to Tasks