Skip to content

refactor: split into moon.work workspace (bit + bit_* core + bitx_* extensions)#82

Merged
mizchi merged 26 commits into
mainfrom
claude/moon-work-module-split-3BBmH
May 29, 2026
Merged

refactor: split into moon.work workspace (bit + bit_* core + bitx_* extensions)#82
mizchi merged 26 commits into
mainfrom
claude/moon-work-module-split-3BBmH

Conversation

@mizchi
Copy link
Copy Markdown
Member

@mizchi mizchi commented May 26, 2026

Summary

Restructure the repository into a MoonBit workspace (moon.work) inspired by gitoxide's gix-* plumbing / feature-crate split. The main mizchi/bit module is being progressively decomposed into per-primitive modules so consumers can pull in only the pieces they need.

  • mizchi/bit is no longer one monolithic module — extensions live in mizchi/bitx_* and leaf core primitives in mizchi/bit_*
  • Repo root holds just moon.work + project files (no more src/ or moon.mod.json at the top level)
  • CLI surface (bit, git-bit subcommands and flags) is unchanged; this is a pure code-organization refactor

Layout

moon.work
modules/
  bit/                ← residual main: types, object, pack, refs, protocol, repo, mid, high, cmd
  bit_apply/          ← extracted core modules (gix-* equivalents)
  bit_archive/        (gix-archive)
  bit_bootstrap/
  bit_date/           (gix-date)
  bit_diff3/          (gix-merge low-level)
  bit_diff_core/      (gix-diff low-level)
  bit_fast_import/
  bit_hash/           (gix-hash)
  bit_ignore/         (gix-ignore + gix-glob)
  bit_osfs/           (gix-fs OS impl)
  bit_trailers/       (gix-trailers)
  bit_utils/          (gix-utils + gix-quote)
  bitx_bitconfig/     ← non-Git extension modules
  bitx_doc/
  bitx_hq/
  bitx_hub/           (+ /crypto, /native)
  bitx_kv/            (+ /native)
  bitx_rebase_ai/
  bitx_subdir/
  bitx_workspace/

Naming conventions:

  • bit_<name> (no x) — extracted Git plumbing modules (mirrors gitoxide's gix-*).
  • bitx_<name> (with x) — extensions for non-Git features.

MoonBit's workspace resolver handles the mizchi/bit ↔ mizchi/bit{,x}_* module-level cycle as long as the in-package graph stays acyclic, so no cmd extraction was needed.

Tooling updates

  • tools/check-layers.mjs walks modules/*/src, classifies mizchi/bitx_* as ext layer and mizchi/bit_* as core layer
  • tools/check-layer-guards.sh, tools/install-bit.sh, tools/playground-dev.sh, tools/build-bit-native.sh
  • tools/flaker-affected-rules.{toml,mjs,test.mjs}, tools/flaker-cli-wrapper.mjs
  • Taskfile.pkl — source globs, build commands, _build/ output paths (workspace mode emits _build/<target>/<profile>/build/mizchi/bit/...)
  • .github/workflows/pages-demo.yml path filter
  • flake.nix, package.nix (moonModJson path)
  • docs/package-layout.md — rewritten with the new layout, naming conventions, and per-module mapping table

Test plan

  • moon check (full workspace): 0 errors
  • node tools/check-layers.mjs: clean
  • tools/check-layer-guards.sh: ok
  • Per-module native tests for all 8 extracted extensions: 298/298 pass (bitx_kv 30, bitx_doc 3, bitx_bitconfig 12, bitx_hq 36, bitx_rebase_ai 5, bitx_workspace 25, bitx_subdir 74, bitx_hub 113)
  • Per-module native tests for all 12 extracted core leaves: 132/132 pass (bit_apply 7, bit_archive 21, bit_date 6, bit_diff3 23, bit_diff_core 6, bit_fast_import 32, bit_hash 11, bit_ignore 16, bit_trailers 6, bit_utils 4; bit_bootstrap/bit_osfs have no tests)
  • mizchi/bit/lib native tests: 285/285 pass
  • mizchi/bit/cmd/git-bit native tests: 4/4 pass
  • moon build --target native --release modules/bit/src/cmd/bit produces working bit.exe (bit --versiongit version 2.47.0 (bit v0.39.0))
  • mizchi/bit/cmd/bit full whitebox suite not run to completion locally (the linker hung after ~1h on a single tcc invocation, unrelated to the refactor — moon check validates the wiring, so CI is the source of truth)
  • CI green on this branch

Reviewer notes

  • External consumers that imported mizchi/bit/<sub> should migrate to mizchi/bit_<sub> (or mizchi/bitx_<sub>). Inside this repo every reference has already been updated.
  • Renames from old names to new module names: date_parsebit_date, string_utilsbit_utils, tarbit_archive. Others map 1:1 (e.g. hashbit_hash).
  • Remaining mizchi/bit/... core packages (types, object, pack, refs, protocol, repo, etc.) are not extracted yet — they have inter-package dependencies that need to be resolved in a follow-up.

Commits

  1. refactor: extract x-kv into separate mizchi/bitx_kv module (PoC)
  2. refactor: extract all x-* extensions into bitx_* workspace modules
  3. refactor: move main bit module into modules/bit/ for uniform layout
  4. refactor: fix build paths after module move
  5. refactor: extract 12 leaf core packages into bit_* modules

@mizchi mizchi changed the title refactor: split into moon.work workspace (modules/bit + modules/bitx_*) refactor: split into moon.work workspace (bit + bit_* core + bitx_* extensions) May 26, 2026
mizchi pushed a commit that referenced this pull request May 27, 2026
The workspace split multiplies the number of modules moon test walks,
and the previous '--no-parallelize -j 1' configuration pushed the job
past its 30-minute deadline (see PR #82 run 26488539393 timeout). The
extracted bit_* modules are independent enough that parallel test
execution should be safe; bump the timeout to 60m as a safety net.
mizchi pushed a commit that referenced this pull request May 29, 2026
The workspace split multiplies the number of modules moon test walks,
and the previous '--no-parallelize -j 1' configuration pushed the job
past its 30-minute deadline (see PR #82 run 26488539393 timeout). The
extracted bit_* modules are independent enough that parallel test
execution should be safe; bump the timeout to 60m as a safety net.
@mizchi mizchi force-pushed the claude/moon-work-module-split-3BBmH branch from 239965d to c0d720c Compare May 29, 2026 12:21
claude added 22 commits May 29, 2026 12:54
Introduce a moon.work workspace and split the first low-frequency
extension (x-kv) out of the main mizchi/bit module, mirroring
gitoxide's gix-* vs feature-crate layout. x-kv has no in-tree
consumers, so it is the safe minimal candidate (no module-level
cycle with mizchi/bit). Documents the new modules/ layout and the
bitx_* naming convention in docs/package-layout.md.
Split the remaining bit extensions (x-bitconfig, x-doc, x-hq, x-hub,
x-rebase-ai, x-subdir, x-workspace) out of the main mizchi/bit module
and into their own MoonBit modules under modules/, joining the
previously-extracted bitx_kv. MoonBit's workspace resolver accepts the
module-level cycle (mizchi/bit <-> mizchi/bitx_*) because the in-package
dependency graph stays acyclic.

- moon.work now lists all eight bitx_* modules as members
- cmd/bit and cmd/git-bit are rewired to import from the new module paths
- tools/check-layers.mjs walks modules/<name>/src as additional roots
  and classifies mizchi/bitx_* as the ext layer
- docs/package-layout.md updated for the new module table and layout
- Taskfile.pkl test/bench targets point at the new module names

All 298 tests across the 8 extracted modules pass (moon test); moon check
on the full workspace is clean (0 errors).
Flatten the repo layout so every workspace member lives under modules/.
Previously the main mizchi/bit module sat at the repo root (src/ +
moon.mod.json) while extensions lived under modules/bitx_*. Now:

  modules/
    bit/             ← main module (was repo root)
      moon.mod.json
      README.mbt.md
      src/
    bitx_bitconfig/
    bitx_doc/
    ...

- moon.work members updated to point at modules/bit instead of "."
- modules/bit/moon.mod.json: dropped the now-irrelevant `exclude` list
  (the repo-root sibling dirs are no longer reachable from the module
  root, so MoonBit's source globbing leaves them alone naturally)
- tools/check-layers.mjs no longer treats src/ at repo root specially;
  it just walks modules/*/src
- tools/check-layer-guards.sh, tools/install-bit.sh, tools/playground-
  dev.sh, tools/flaker-affected-rules.{toml,mjs}, tools/flaker-affected-
  rules.test.mjs all rewritten to use the modules/bit/src/* prefix
- Taskfile.pkl: moonbitSources glob covers modules/**, build commands
  point at modules/bit/src/{lib,cmd/bit}
- flake.nix / package.nix: moonModJson now resolves to
  modules/bit/moon.mod.json
- .github/workflows/pages-demo.yml path filter updated
- docs/package-layout.md rewritten with the new repository layout
  diagram and updated per-package path columns

moon check passes (0 errors) and node tools/check-layers.mjs is clean.
Workspace mode produces _build artifacts under the module-prefixed
path (e.g. _build/native/release/build/mizchi/bit/cmd/bit/bit.exe
instead of _build/native/release/build/cmd/bit/bit.exe). Update the
build script and Taskfile outputs to match. Also fix a stale
mizchi/bit/x-hub/native reference in a doc comment.
Continue the gitoxide-style split by promoting every leaf (no internal
mizchi/bit dependency) core/* package out of mizchi/bit into its own
MoonBit module. The CLI surface is unchanged — only import paths in
moon.pkg files move.

Extracted modules:
- bit_hash       (gix-hash)
- bit_date       (gix-date,   was core/date_parse)
- bit_utils      (gix-utils + gix-quote, was core/string_utils)
- bit_trailers   (gix-trailers)
- bit_ignore     (gix-ignore + gix-glob)
- bit_archive    (gix-archive, was core/tar)
- bit_diff_core  (gix-diff,   low-level)
- bit_diff3      (gix-merge,  low-level)
- bit_apply      (patch application)
- bit_fast_import
- bit_osfs       (gix-fs OS impl)
- bit_bootstrap

Naming convention: `bit_<name>` (no x) for git plumbing extracted from
mizchi/bit, distinct from `bitx_<name>` used for non-git extensions.

- moon.work now lists all 12 new core modules
- modules/bit/moon.mod.json declares them as deps
- bitx_doc / bitx_kv / bitx_rebase_ai / bitx_workspace add bit_osfs
  and/or bit_utils to their deps (they consumed those packages)
- Blackbox test files (*_test.mbt) updated to use the new package
  aliases (e.g. @apply.X -> @bit_apply.X)
- tools/check-layers.mjs recognises mizchi/bit_<name> as the core layer
- docs/package-layout.md updated with the new layout diagram and
  per-module mapping table

moon check passes (0 errors) and all 12 new modules' native tests pass
(bit_apply 7, bit_archive 21, bit_date 6, bit_diff3 23, bit_diff_core 6,
bit_fast_import 32, bit_hash 11, bit_ignore 16, bit_trailers 6,
bit_utils 4 — bit_bootstrap and bit_osfs have no tests). mizchi/bit/lib
(285/285) and mizchi/bit/cmd/git-bit (4/4) still pass too.
Three more core packages whose internal mizchi/bit/* dependencies were
already resolved to bit_* modules in wave 1 are now standalone:

  src/object       → modules/bit_object  (gix-object)
  src/config_parse → modules/bit_config  (gix-config)
  src/pack         → modules/bit_pack    (gix-pack)

The trickier bit: bit_object owns the underlying type defs that
mizchi/bit/types re-exports as aliases (`pub type ObjectId =
@object.ObjectId`, etc). MoonBit's type checker needs the source module
imported wherever a type alias is dereferenced through @bit.<Type>, so
every moon.pkg that imports `mizchi/bit` and uses `@bit.GitError` /
`@bit.ObjectId` etc. now also imports `mizchi/bit_object`. Same applies
to bitx_doc / bitx_kv / bitx_rebase_ai / bitx_subdir / bitx_workspace /
bitx_hub — they now declare bit_object in moon.mod.json deps.

Long-term direction: move the type defs themselves out of bit_object
into a future bit_types module, so the aliases become unnecessary and
the transitive-import requirement goes away.

moon check passes (0 errors). Tests:
- mizchi/bit_object: 27/27
- mizchi/bit_config: 8/8
- mizchi/bit_pack:   72/72
- mizchi/bit/lib:    285/285
  src/types     → modules/bit_types     (shared trait/type defs)
  src/protocol  → modules/bit_protocol  (gix-protocol + gix-transport)
  src/reftable  → modules/bit_reftable  (reftable backend)

Same transitive-import handling as wave 2: every moon.pkg that imports
mizchi/bit and uses @bit.<trait> (FileSystem / RepoFileSystem / etc.)
now also imports mizchi/bit_types, and the containing modules declare
mizchi/bit_types in moon.mod.json deps.

moon check passes. Tests:
- mizchi/bit_types:    0 (no tests)
- mizchi/bit_protocol: 30/30
- mizchi/bit_reftable: 9/9
- mizchi/bit/lib:      285/285
  src/io   → modules/bit_io   (gix-fs abstract + native bindings)
  src/refs → modules/bit_refs (gix-ref)

Both modules now satisfy "no internal mizchi/bit/* runtime dep" after
waves 1-3 carved out their direct dependencies. bit_io brings its
`native` sub-package along (mizchi/bit_io/native). Consumers of
@bit.TestFs and friends had to gain mizchi/bit_io imports +
moon.mod.json deps via the same transitive-alias pattern as previous
waves.

moon check passes. Tests:
- mizchi/bit_io:   0 (no tests)
- mizchi/bit_refs: 2/2
- mizchi/bit/lib:  285/285
  src/remote → modules/bit_remote  (gix-url + gix-discover)
  src/repo   → modules/bit_repo    (repo handle / materialization)

bit_repo introduces a new transitive: types defined here (ObjectStore,
CommitInfo) are re-exported through the mizchi/bit root facade. Same
pattern as previous waves — consumers gain mizchi/bit_repo imports +
moon.mod.json deps. After this wave, mizchi/bit/src/ contains only
pack_ops, repo_ops, worktree, diff, lib, vfs, fingerprint, grep,
runtime, cmd/, tests/, fuzz_tests/.

moon check passes. Tests:
- mizchi/bit_remote: 4/4
- mizchi/bit_repo:   2/2
- mizchi/bit/lib:    285/285
…wave 6)

  src/pack_ops → modules/bit_pack_ops  (collect_reachable_objects, etc.)
  src/repo_ops → modules/bit_repo_ops  (revparse, repository ops)
  src/worktree → modules/bit_worktree  (status / add / commit / rm / mv)
  src/diff     → modules/bit_diff      (high-level diff / show)

These four still depend on mizchi/bit/lib at runtime, so they are
classified as high (not mid) by tools/check-layers.mjs — same as the
parking lot the docs already described. Once their lib dep is broken
they can be promoted to mid.

After this wave modules/bit/src/ contains only: lib, vfs, fingerprint,
grep, runtime, cmd/{bit,git-bit}, tests/, fuzz_tests/, top.mbt.

moon check passes. Tests:
- mizchi/bit_pack_ops:  1/1
- mizchi/bit_repo_ops:  0 (no tests)
- mizchi/bit_worktree:  1/1
- mizchi/bit_diff:      0 (no tests)
- mizchi/bit/lib:       285/285
…ve 7)

Pull the remaining non-lib high-layer packages out of mizchi/bit into
their own modules:

  src/vfs         → modules/bit_vfs
  src/fingerprint → modules/bit_fingerprint
  src/grep        → modules/bit_grep
  src/runtime     → modules/bit_runtime

These still depend on mizchi/bit/lib at runtime, so they remain
classified as the high layer (same as wave 6's mid extraction).
After this wave modules/bit/src/ has just: lib (high), cmd/, tests/,
fuzz_tests/, top.mbt.

Consumers updated: bitx_kv / bitx_subdir add bit_vfs dep; bitx_workspace
adds bit_fingerprint; bitx_rebase_ai adds bit_runtime.

moon check passes. Tests (native):
- mizchi/bit_vfs:         50/50
- mizchi/bit_grep:        28/28
- mizchi/bit_runtime:     14/14
- mizchi/bit_fingerprint: 7/9 (2 failures are environment-dependent
  git-commit signing tests, unrelated to the refactor)
- mizchi/bit/lib:         285/285
  src/lib → modules/bit_lib (gix-equivalent porcelain, 103 .mbt files,
                             285 tests, includes lib/native subpackage)

bit_lib is the biggest consumer of the bit_* core split — it depends on
nearly every previously-extracted bit_* module. 13 downstream consumers
(every mid/high module plus all bitx_* extensions and the CLI) now
declare mizchi/bit_lib in their moon.mod.json deps.

After this wave modules/bit/src/ holds only the cmd/ binaries, the
top.mbt root facade, and the integration test/fuzz test packages.

moon check passes. mizchi/bit_lib: 285/285 native tests pass.
js-build:
- tools/bit-git.mjs imports the lib payload at the new workspace path
  (_build/js/release/build/mizchi/bit_lib/bit_lib.js instead of the
  pre-split _build/js/release/build/lib/lib.js).
- Taskfile.pkl JS lib build targets modules/bit_lib/src and the output
  glob is mizchi/bit_lib/bit_lib.js. sync-npm-lib-raw and the
  bun-bundle tasks updated to match.
- tools/playground-dev.sh builds modules/bit_lib/src.

ci.yml:
- Native binary stage uses the workspace output path
  _build/native/release/build/mizchi/bit/cmd/bit/bit.exe.
- JS / WASM smoke tests target the new module names (mizchi/bit_lib,
  mizchi/bit_runtime, mizchi/bit_diff3, mizchi/bit_repo, mizchi/bit_grep).
- distributed-test points at mizchi/bitx_hub / bitx_hub/native /
  bitx_kv instead of the old mizchi/bit/x-* paths.

nix-build:
- moonPlatform.buildMoonPackage does not understand multi-module
  workspaces (it tries to fetch every dep from mooncakes.io, which
  fails for the local mizchi/bit_* / bitx_* members). Replace it with a
  custom mkDerivation that:
    1. strips workspace-local deps before invoking buildCachedRegistry
       so the cached registry only resolves real mooncakes.io packages
    2. uses bundleWithRegistry to materialize MOON_HOME with the
       toolchain + cached registry
    3. runs `moon build --target native --release modules/bit/src/cmd/bit`
       inside the source tree to honor moon.work resolution
- flake.nix devShell moonHome uses the same stripped manifest so
  `nix develop` still works.

Local verification:
- node --test tools/{js-build,npm-lib,npm-cli,demo-relay,
  demo-editor-link,playground-commands,playground-view}.test.mjs:
  24/24 pass
- nix build cannot be exercised in this sandbox; will be confirmed by
  CI.
Same workspace-output-path fix as the test job, applied to the git-compat
shards' 'Build bit' step. moon build now targets modules/bit/src/cmd/bit
and the binary path is _build/native/release/build/mizchi/bit/cmd/bit/bit.exe.
The workspace split multiplies the number of modules moon test walks,
and the previous '--no-parallelize -j 1' configuration pushed the job
past its 30-minute deadline (see PR #82 run 26488539393 timeout). The
extracted bit_* modules are independent enough that parallel test
execution should be safe; bump the timeout to 60m as a safety net.
moon test --target native walked all workspace members and timed out
even at 60 minutes (1h14m wall before kill). Splitting it makes the
slow consumer visible and lets each fail/timeout independently:

  test:               loops over modules/bit_*/bitx_* members + the
                      tests/ and fuzz_tests/ packages in mizchi/bit.
                      Skips modules/bit so cmd/bit doesn't dominate.
                      30-minute timeout.

  cmd-native-test:    a new job dedicated to mizchi/bit/cmd/bit and
                      mizchi/bit/cmd/git-bit native tests, which are
                      heavy (large tcc linker step). 75-minute timeout.

Both jobs reuse the same build + setup steps so the wbtest fixtures
keep working. The non-cmd loop runs every workspace member with an
explicit -p so a single broken module fails fast.
cmd/bit native tests still don't fit in 75m after the workspace split
(actual elapsed ~88m before timeout). Split the job into a matrix so
cmd/bit and cmd/git-bit run in parallel runners:

  - cmd/bit:     120-minute timeout (heavy tcc link)
  - cmd/git-bit: 30-minute  timeout (small consumer)

Both still rebuild the wbtest fixture binary up front because the
test harness reads tools/git-shim/moon.
moon build leaves _build/.moon-lock held when the process exits on some
workspace configurations; the subsequent moon test then blocks forever
waiting for the lock instead of running. Add the same pkill + rm guard
that the test job already uses between its JS and native steps.

https://claude.ai/code/session_015aa85JK5yNfkrqt2uhodvi
TCC (the fallback C compiler moon uses when no system compiler is found)
has very slow link times for large binaries. The cmd/bit package links
30+ workspace modules and takes >120 min with TCC. Clang links the same
binary in a fraction of the time.

ubuntu-latest runners ship with clang pre-installed, so no CI setup
change is needed.

https://claude.ai/code/session_015aa85JK5yNfkrqt2uhodvi
moon.pkg sets link.native.cc = "clang" for cmd/bit and cmd/git-bit.
The nix sandbox does not expose clang by default, so the build fails
immediately when moon tries to invoke it. Add clang to nativeBuildInputs
so the compiler is available inside the sandbox.

https://claude.ai/code/session_015aa85JK5yNfkrqt2uhodvi
moon build output path may shift across moonbit versions; use find to
locate bit.exe rather than hard-coding the path. Also print moon --version
so the actual version is visible in CI logs.

For the cmd-native-test lock issue: when link.native.cc=clang is set,
moon spawns clang child processes that may still hold _build/.moon-lock
after the moon process itself exits. Add pkill -f clang + sleep 1 to the
lock-clearing step so no stray compiler holds the lock when moon test starts.

https://claude.ai/code/session_015aa85JK5yNfkrqt2uhodvi
… in workspace)

x/agent, x/agent/llm, x/agent/mcp, x/agent/native were part of the old
monolithic src/ layout but were not extracted into a bitx_agent workspace
module. The distributed-test job still referenced them, causing immediate
failure on every CI run. Remove the missing package references; bitx_hub
and bitx_kv cover the distributed testing scope.

https://claude.ai/code/session_015aa85JK5yNfkrqt2uhodvi
@mizchi mizchi force-pushed the claude/moon-work-module-split-3BBmH branch from 7ffcc1b to d597ee6 Compare May 29, 2026 12:55
claude added 3 commits May 29, 2026 13:16
…esolution

moon.work member paths require ./ prefix for moonbit workspace resolver to
correctly resolve local module references. Without it, moon may fail to
recognize workspace members (exit 255) and fall back to registry lookup.

Also update CI module-list parser to strip the ./ prefix before using
basename for the mizchi/<name> package path.
- bit_repo_ops/moon.mod.json: add mizchi/bit_utils dep (required by
  moon workspace resolver - all transitive deps must be declared)
- js_exports_wbtest.mbt: pass depth=0 to js_clone_remote (PR #30 added
  depth param, test wasn't updated)
cc=clang disables tcc-run, forcing moon test to do a full clang link
on every test run. For the cmd/bit package (40+ module workspace),
the clang link takes 50+ minutes and holds the build lock the entire
time, causing moon test to block. tcc is fast and produces working
binaries; there is no practical benefit to clang for test builds.

Also remove clang from package.nix nativeBuildInputs (no longer needed
since moon uses its bundled tcc for the nix build).
In workspace mode (moon.work), moon outputs binaries under a namespaced
path to avoid conflicts: _build/native/release/build/mizchi/bit/cmd/bit/bit.exe
instead of _build/native/release/build/cmd/bit/bit.exe.

The wbtest files were looking for the binary at the old non-workspace
path, causing moon test to try to rebuild (blocking on the lock file).

Update 11 files with the correct workspace output path.
@mizchi mizchi merged commit d2efc9d into main May 29, 2026
16 checks passed
@mizchi mizchi deleted the claude/moon-work-module-split-3BBmH branch May 29, 2026 15:46
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