feat(pkg): package manager — fetch + MVS resolve + namespace registration (#63)
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 17s
ci / build-and-test (push) Successful in 1m33s
commit-lint / conventional-commits (push) Successful in 2s
docs / build-and-deploy (push) Successful in 25s

Implements the v1 direction decided in the RFC as a set of `x` subcommands
plus a small, contained compiler change.

  * URL-as-identity, no registry — a dependency is named by its git import
    path and a `git tag vX.Y.Z` publishes a version.
  * Minimum Version Selection — a `require` is a minimum; the resolver picks
    the greatest required minimum per module, then the reachable closure at
    those versions. Deterministic, no SAT solver (tools/x/pkg.ludic).
  * Content-addressed global store + per-project links — packages live once in
    ~/.ludic/store keyed by a content hash; each project links them under
    ludic_modules/. package.ludic (manifest) + package.lock.ludic (lock).
  * Namespace registration for source packages via a module-root import
    fallback in the compiler: do_import resolves a non-local, non-absolute
    import under $LUDIC_MODULES (default ludic_modules/), so a fetched
    package's Ludic compiles into the consumer the way the built-in stdlib
    does. Collisions and missing prebuilt targets are hard errors.

Commands: x add / x get / x update / x verify / x vendor. New hermetic suite
`x test-pkg` (stands up throwaway git repos, offline) is gated inside `x test`.

Existing programs compile byte-for-byte identically (the import fallback only
fires when the local path is absent); the C-free bootstrap fixpoint holds and
the seed is regenerated. Full suite: 87 passed, package suite: 12 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-01 07:08:12 +03:00
parent 237e13c95e
commit 2c44bae496
10 changed files with 17409 additions and 16387 deletions

View file

@ -326,6 +326,17 @@ function cmd_test() -> int {
if (rc == 42) { ok("ludic app.ludic -> compiles, runs, forwards exit code") }
else { bad2("ludic run: expected exit 42", `got {string(rc)}`) }
# the package manager (issue #63): fetch + MVS resolve + content-addressed
# store + ludic_modules/ links + build, run as its own hermetic suite (it
# stands up throwaway git repos, so like selfhost-test it runs as a sub-process
# and this stage checks its footer).
print("== package manager (x test-pkg) ==")
run("bin/x test-pkg > /tmp/x_pkg.out 2>&1")
if shq("grep -q '0 failed' /tmp/x_pkg.out") {
let pc = capture_line("grep -c PASS /tmp/x_pkg.out")
ok(`package-manager suite: {pc} checks passed (see: x test-pkg)`)
} else { bad2("x test-pkg", capture_line("grep -i fail /tmp/x_pkg.out | head -1")) }
# docs site generator + guard, in Ludic (no Python). docs-gen emits the whole
# pages payload; docs-check is its coverage/integrity guard; docs-palette is
# the named-colour source of truth (its output is the tracked emit_color.ludic