wip(asdf-lite): full mini-clos + asdf-lite + load-op cycle works at REPLVerified
End-to-end working: defsystem → operate 'load-op → component
files actually load. Three bugs surfaced and fixed:
1. Prelude load macro shadowed the function-callable path.
The existing (defmacro load (path) (cons 'progn
(%read-source-file path))) inlines the file at COMPILE TIME
when PATH is a literal. It also fired for variable PATH
expressions, where %read-source-file silently returned NIL —
the expansion became (progn nil), a no-op. No runtime load.
The tree-walker (which expands prelude macros — runs before
*ag-macros-prelude-tail*) doesn't know stringp, so a defmacro
that tries to distinguish literal-string from runtime-expr
can't. Route everything through the runtime loader:
(defmacro load (path)
(list 'load-file-at-runtime path))
(defun load-file-at-runtime (path)
(ag-load-source-action (ag-get-runtime-code) path))
Users who want compile-time inlining use %read-source-file
directly.
2. Cold-static *ag-runtime-code* unreachable from user code.
Warm REPL boot's `(setq *ag-runtime-code* code)' compiles
against cold's *ag-globals* (the defvar's there). User code
reading *ag-runtime-code* at REPL compiles against runtime
*ag-globals* which DOESN'T have cold's defvar — a fresh slot
gets allocated at NIL. Two slots, never coupled.
Bridge: a cold-tier accessor function.
(defun ag-get-runtime-code () *ag-runtime-code*)
The function lookup goes through *ag-fn-table* (one slot
across tiers); the *ag-runtime-code* read inside the function
body resolves at cold-compile time to cold's static vaddr,
the one the boot setq writes to. load-file-at-runtime calls
(ag-get-runtime-code) instead of touching the defvar.
3. asdf-lite's perform-dispatch needed funcall, not apply.
mini-clos's mc-dispatch does `(apply m args)' on the looked-
up method lambda. In funcl today, apply on a defmethod-
lifted lambda doesn't fire the body — funcall does. Worked
around in asdf-lite by adding perform-dispatch that uses
funcall directly. The underlying apply-vs-funcall divergence
is a separate bug needing its own session.
Verified at REPL:
funcl> (load "src/mini-clos.lisp")
funcl> (load "libs/asdf-lite/asdf-lite.lisp")
funcl> (defsystem app :components ((:file "main")))
APP
funcl> (operate 'load-op 'app)
MAIN LOADED ← main.lisp's write-string runs
GREET ← its defun installed
funcl> (greet)
HELLO FROM GREET ← user-defined fn callable
asdf-lite.lisp tracked in libs/asdf-lite/ (force-added — was
.gitignored historically).
Other touches:
- cold-warm-imports.lisp: declare AG-LOAD-SOURCE-ACTION as
a cold→warm import. load-file-at-runtime calls it; moving
ag-load-source-action into cold needs the whole REPL
dispatch (ag-repl-eval-each / ag-repl-eval-form) which cold
doesn't have by design.
- cold-bootstrap-retained.lisp: AG-GET-RUNTIME-CODE and
LOAD-FILE-AT-RUNTIME added (newly BODY-UNREACHABLE; users
can reach them via funcall but cold's main doesn't).
- prelude.lisp: defsystem-with-options returns the system
NAME (was the system instance — system carries backrefs to
components → parent → components → printer loops the REPL).
Test suite: 199/199 GREEN.
KNOWN OPEN ITEMS (this wip branch only — don't land on main
until resolved):
- apply-vs-funcall on defmethod lambdas. asdf-lite's
perform-dispatch is a workaround. Root cause unclear.
- Codex pointed out (2026-06-10) that the reader's
"foo" → (quote "foo") auto-quoting is the root cause of
the load-macro dispatch awkwardness above. A real fix
would make string literals self-evaluating, drop ag-
extract-quoted-bv churn, and split string-vs-byte-vector
APIs cleanly. That's a separate, bigger refactor — wip
branch lives with the current macro until then.