The generated C, amalgams, vendored runtime pins, and compiled binaries from the Claude Code era are removed from the worktree. The El sources survive; this tree is now source-only for the first-principles rebuild. Per Principal direction 2026-08-19.
8.6 KiB
CLAIMS — cycle 15, no namespacing at all
Source doc: docs/v1/experiments/cycles/15-no-namespacing-at-all.md
This is the measurement that bears hardest on the hypothesis: if every El symbol
binds to a name already resolved by the C linker, the partition question is not
being asked yet. It was captured with correspondingly more care than the rest of
the set — the linking claim is demonstrated as a six-step chain
(0010–0015), not asserted.
Pinned commits
| role | sha | subject |
|---|---|---|
| parent / before-state | bb040ad2c |
record the numeric literals answer: a bare number has no axis |
| experiment (system under test) | 79f6cb79850b5affd7735fcc418ede2242b46e5b |
ANSWER: if the partition is a neighbourhood, does linking survive? |
| land (merge) | f23cb2b948396eae6a44daac5d56f5e0e6338758 |
answer the module question: the partition is a path, and there is no namespacing |
79f6cb7 touches codegen.el, definitions_query.sh and definitions.sh
only. compiler.el — which holds resolve_imports — is unchanged between
bb040ad and 79f6cb7, so the import/linking measurements below hold at both.
Verdicts
| # | claim | artifact | commit | verdict |
|---|---|---|---|---|
| A1 | import is textual inlining (resolve_imports), guarded against double inclusion by a __elc_imp__:<path> state key |
0016-resolve-imports-is-textual-inlining.out |
79f6cb7 |
REPRODUCED (exact) |
| A2 | when a .elh header exists the header is inlined instead and the .el is marked seen |
0016, 0010, 0011, 0012 |
79f6cb7 |
REPRODUCED |
| A3 | so symbols resolve at C link time — linking IS real, delegated to C | 0012, 0013-link-D-unresolved-at-c-link-time.out, 0014, 0015 |
79f6cb7 |
REPRODUCED (demonstrated end to end) |
| A4 | two modules defining helper emit two C functions into one translation unit |
0003, 0004-pre-two-c-functions-one-TU.out |
bb040ad |
REPRODUCED (exact) |
| A5 | cc does catch the collision, but reports four errors with the user's own function FOURTH: __el_body_helper, __env_helper, __thunk_helper, helper |
0005-pre-cc-collision-error-order.out |
bb040ad |
REPRODUCED (exact, in order) |
| A6 | repaired at El level with the message duplicate definition: 'helper' is defined 2 times — El has no namespacing, so imported modules share one global scope |
0006-post-el-level-duplicate-message.out |
79f6cb7 |
REPRODUCED (verbatim) |
| A7 | 4/4 definitions_query.sh |
0007-post-definitions-query-harness.out |
79f6cb7 |
REPRODUCED |
| A8 | 104/104 native | 0009-post-native-suite.out |
79f6cb7 |
REPRODUCED (exact) |
| A9 | the compiler itself reports clean | 0017-compiler-self-check-clean.out |
79f6cb7 |
REPRODUCED |
| A10 | fixpoint ok | 0008-post-fixpoint.out |
79f6cb7 |
REPRODUCED |
| A11 | LIMIT: textual inlining destroys file provenance, so the message can name the symbol but not the files | 0006 |
79f6cb7 |
REPRODUCED |
| A12 | "the first three are generated symbols introduced by the unconditional-wrapper pass earlier today — before it, there was one clear message" | — | — | NOT-CAPTURED |
| A13 | "El's partition is a FILESYSTEM PATH, not a neighbourhood" / "whether linking survives a neighbourhood partition cannot be asked yet" | — | — | NOT-CAPTURED (interpretive) |
| A14 | both compilers build | 0001, 0002 |
bb040ad, 79f6cb7 |
REPRODUCED |
A3 — the linking chain, in full
This is the load-bearing measurement. Six captures, one per step. Fixtures
fixtures/m.el (defines mhelper) and fixtures/link_main.el (imports it).
A — no header present. resolve_imports inlines the module body; the
definition and the call land in one translation unit (0010):
el_val_t mhelper(void);
static el_val_t __el_body_mhelper(void) { return EL_STR("from module m"); …}
…
el_val_t mhelper(void) { … } <- the DEFINITION is here
int main(…) { println(mhelper()); }
B — emit a header. elc --emit-header m.el writes (0011):
// auto-generated by elc --emit-header — do not edit
extern fn mhelper() -> String
C — recompile with the header present. The .el is marked seen and the
header text is inlined in its place. The whole module body is gone (0012):
#include "el_runtime.h"
el_val_t mhelper(void); <- a DECLARATION, and nothing else
int main(int _argc, char** _argv) {
el_runtime_init_args(_argc, _argv);
println(mhelper());
return 0;
}
D — link that translation unit alone. El has produced a program with an
unresolved symbol, and says nothing about it. The C linker is what refuses
(0013, exit 1):
Undefined symbols for architecture arm64:
"_mhelper", referenced from:
E — link the module's own translation unit in as well (0014, exit 0):
LINK-OK
F — run it (0015, exit 0):
from module m
The symbol is genuinely unbound at the El layer and genuinely bound by ld.
"Linking IS real, delegated to C" is not a characterisation here; it is the
observed behaviour of the toolchain at the pinned commit. The El compiler never
resolves mhelper — it emits a C declaration and lets the system linker
succeed or fail.
A5 — the collision, and the ordering, exactly as recorded
Fixtures fixtures/a.el, fixtures/b.el, fixtures/main.el — two modules each
defining helper, one program importing both.
Emitted into one translation unit (0004) — the generated helper trio appears
twice, then the user's function twice:
5: el_val_t helper(void);
6: el_val_t helper(void);
8: static el_val_t __el_body_helper(void) {
12: struct __env_helper { char __e0; };
13: static el_val_t __thunk_helper(void* __v) {
17: el_val_t helper(void) {
25: static el_val_t __el_body_helper(void) { <- second copy begins
29: struct __env_helper { char __e0; };
30: static el_val_t __thunk_helper(void* __v) {
34: el_val_t helper(void) {
cc at bb040ad (0005, exit 1) reports, in this order:
mod_pre.c:25:17: error: redefinition of '__el_body_helper'
mod_pre.c:29:8: error: redefinition of '__env_helper'
mod_pre.c:30:17: error: redefinition of '__thunk_helper'
mod_pre.c:34:10: error: redefinition of 'helper'
Four errors. The user's own function is the fourth. The first three name symbols the user never wrote. Exact match, including order.
A6 / A11 — the repair, and its stated limit
At 79f6cb7 the collision is caught at El level from recorded relations
(0006, checker exit 1):
duplicate definition: 'helper' is defined 2 times — El has no namespacing, so imported modules share one global scope
verbatim to the record. And the limit is visible in the same output: it names
which name collides and not which files, because by the time codegen runs
resolve_imports has produced one source string. That limit is what cycle 16
goes on to remove.
A1 — the mechanism, verbatim from source
0016, compiler.el at 79f6cb7:
416:fn resolve_imports(src_path: String) -> String {
417: let seen_key: String = "__elc_imp__:" + src_path
420: state_set(seen_key, "1")
455: let imp_elh_path: String = str_slice(imp_path, 0, str_len(imp_path) - 3) + ".elh"
456: let imp_elh: String = fs_read(imp_elh_path)
457: if !str_eq(imp_elh, "") {
460: let seen_imp_key: String = "__elc_imp__:" + imp_path
461: state_set(seen_imp_key, "1")
462: let prefix_chunks = native_list_append(prefix_chunks, imp_elh)
Textual inlining; __elc_imp__:<path> as the double-inclusion guard; header
preferred over body and the .el marked seen. Every clause of A1 and A2 present
in the code, and demonstrated in behaviour by the A–F chain.
Suite results at 79f6cb7
104 tests, 104 passed, 0 failed, 249 assertions (0009)
4 assertions, 4 passed, 0 failed definitions_query (0007)
FIXPOINT-OK gen2 == gen3 (0008)
definitions: clean exit=0 (compiler's own source) (0017)
All four figures match the record.
Provenance notes
Every artifact in this cycle was captured from a dedicated worktree —
/tmp/rerun-v1b-15-pre (bb040ad) or /tmp/rerun-v1b-15-post (79f6cb7) —
with capture.sh invoked by absolute path from inside it, so the recorded commit
is the system under test's, and fixtures and build products were kept outside the
worktree so the tree stayed clean. No DIRTY-TREE flags in this cycle; every
artifact is reproducible by checking out its recorded commit.
One capture (0013) was taken a first time with the working directory left in
the shared main checkout, which recorded the wrong commit. It was deleted, its
manifest row removed, and the measurement re-run from inside the worktree. The
result was unchanged.