docs(adr0020): release-profile capability slicing + report-tag renameVerified
Two pieces, single commit because the report-tag rename and the
ADR draft both flow from the same codex review (2026-06-10):
1. RENAME STRICT-LINK-{LIVE,DEAD} → BODY-{REACHABLE,UNREACHABLE}.
The old tags overstated what the analyzer proves. Today's
tree-shake analyzer is sound only for direct out-of-line body
reachability — it doesn't follow funcall / (function NAME)
indirection or inline-emitted primitives. Calling its output
"STRICT-LINK-DEAD" misleadingly suggested liveness.
Changes:
- cold-tree-shake.lisp:277-278 emits BODY-REACHABLE /
BODY-UNREACHABLE tags.
- ag-tree-shake-bfs docstring rewritten to describe what
the BFS actually traces (the old text said "Strict-link
BFS from ROOTS through fn-patches + value-patches"
— fn-patches is now redundant since step 1.5 routed
edges through *ag-call-graph-edges* and value-patches
were always excluded).
- tests/bootstrap.el's two tree-shake tests updated to
match the new tags.
- cold-bootstrap-only.lisp + cold-bootstrap-retained.lisp
docstrings updated.
2. DRAFT ADR 0020: release-profile capability slicing.
ADR 0019 step 2.5 reached a dead end: every BODY-UNREACHABLE
name was either (a) inline-emitted primitive, (b) CL stdlib
user-facing, (c) REPL/hosted/save-image runtime helper, or
(d) gensym/trap. Step 2.5's bootstrap-only file is empty
today, and codex's review pointed out why: the criterion "no
user can call indirectly via funcall" makes elimination
impossible by construction. ag-build-fn-installs
(compiler.lisp:3290) emits a symbol-function cell per top-
level defun, so "the user could call it via (symbol-function
'NAME)" is universally true.
The transferable form of SBCL #8 is release-profile capability
slicing, not genesis cleanup. ADR 0020 stages this:
Step 1 — provenance at emit time:
extend *ag-call-graph-edges* with edge-class (direct-call,
function-value-reference, synthetic-install-reference,
inline-semantic-use).
Step 2 — explicit capability tags:
(declaim (capability NAME ...)) recognition; first batch
covers CL Phase-1 stdlib as public-root + REPL entry points
as runtime-feature repl.
Step 3 — profile manifest format + per-profile reports:
docs/release-profiles/{full,no-image,compiler-only}.lisp.
funcl-cold --tree-shake-report --profile NAME runs BFS
with profile capabilities as roots.
Step 4 — gated elimination:
funcl-cold --strict-link --profile NAME emits only defuns
reachable from profile roots OR in profile retain list.
Supersedes ADR 0019 step 3.
The synthetic-install-reference distinction is the key fix
for the "everything is reachable via symbol-function" problem
— synthetic refs tagged at emit time, excluded from
reachability proofs.
ADR 0019 step 3 marked superseded; ADR 0019 steps 1, 1.5,
2, 2.5 remain valid (the analyzer and gate machinery feeds
ADR 0020 step 1).
Verified:
- 197/197 GREEN
- Both tree-shake tests pass with renamed tags
- cold-of-cold reachability set unchanged (1194 reachable /
229 unreachable) — purely a label change
Verified:
- 197/197 GREEN