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.
Anthony Green2026-06-10 18:55:16 -0400 2f52ba38e28247cce870f7bc031ba3da497cc41f