Both per-file ratchets are at 39/39 — every Appendix A file typechecks clean and reaches codegen. They have never been linked together. Doing so is the actual Phase 0 exit gate, and it is a different question with a different answer.
First link of the whole set: 13 duplicate symbols, 188 undefined symbols.
Tracked by the new scripts/mvk-links.sh ratchet: duplicates must stay at zero, undefined is a ceiling that must fall.
Duplicates — fixed
Every one was a real pair of distinct functions. serial.home and vga.home both export writeChar, writeString, writeHex, writeDec, and init; heap.home exports init too; kernel_shutdown, kernel_reboot, scheduler_init, scheduler_tick, fb_console_putchar, fb_console_write, getStats, printStats all appear twice.
export marks a module's public API, not a global C name — callers reach these as serial.writeChar and vga.writeChar, so the module alias is the namespace. Fixed in home-lang/home@b9d0582 by emitting <module>__<name>, with the boot entry points (kernel_main, main, _start) keeping bare names since hand-written assembly calls them. 13 → 0.
Note that some of those pairs are also genuine source-level duplication — kernel_shutdown and kernel_reboot exist in both main.home and core/kernel_init.home, like the duplicate scheduler_wake found earlier. Module scoping makes them link, but two implementations of one kernel operation is still a decision someone has to make.
Undefined — 188, two kinds
1. Functions that were never written. core/foundation.home is called for atomic_add_u64, cpu_halt, cpu_reboot, get_ticks, get_lapic_id — and defines none of them. Same class as socket.home calling a tcp.tcp_accept that did not exist (fixed in #21f5fdc). Code written against an interface nobody implemented.
Worst offenders by module: core_foundation (14), core_process (14), core_memory (10), core_filesystem (10), security_capabilities (9).
2. Appendix A is not import-closed. The set imports modules it does not contain: drivers/ata, drivers/nvme, drivers/e1000, mm/memcg, mm/swap, net/dhcp, net/dns, lib/pantry, lib/den, lib/craft, block_io/scheduler, perf/async_io. Worst: lib_pantry (28), lib_den (23), lib_craft (10).
The plan states Appendix A's "Known gaps: none — every candidate role resolved to a real file". That is wrong, and it was invisible until the set was linked.
What has to happen
- Decide per module: implement the missing function, or add the module to Appendix A, or cut the call
lib/pantry,lib/den,lib/craftare 61 of the 188 and look out of scope for a minimum viable kernel — the MVK probably should not reach into the package manager or the GUI toolkit at all- Resolve the genuine duplicate implementations (
kernel_shutdown,kernel_reboot,scheduler_init,scheduler_tick) - Correct Appendix A's "known gaps: none" claim
- Then: link, boot, and the Phase 0 gate closes
Blocks #22.