test/xmod/collide_test.ww replaces 989_fnptrcollide_run.c and
989_barefn_collide_run.c with every assertion preserved (build-reject
both stages; run-exit 9 + exactly-one TEXT main.run/aa.run + cs==ww).
Makefile grows the XMOD_WW_TESTS block mirroring SEP_WW_TESTS, wired
into test-compiler.
944_alias_cgen_b5_run.c -> alias_g73_heapfill (//ww:compile), joining
its migrated alias_* siblings. The carrier's stated fixture blocker
('//ww:compile links') is stale: the COMPILE cell runs the bare
frontend, no link. The live #24 field(SB) deref-read leak enters
DATABYTEID_DIVERGED, upgrading the carrier's silent byte-id waiver
with a graduation tripwire.
839_xmod_nominal_typeeqast.c -> r839_xmod_nominal_match (run-exit 42),
r839_xmod_a6_concrete (run), r839_xmod_incompat_concrete (error
'not assignable'), r839_xmod_distinct_alias (error 'ambiguous
without nominal layout').
r839_xmod_nominal_match enters DATABYTEID_DIVERGED: the carrier waived
byte-id for the pre-existing cross-module tagged-return spill
divergence; the DIVERGED row makes it loud-on-fix instead. The
typeeqast-layer discriminator itself stays with the lib byteid gate
(test/byteid/libbyteid_test.ww), as the carrier documented.
732_def_const_fold.c -> r732_def_xmod_fold (run-exit 42; stronger than
the carrier's DATA-row grep — links AND reads the folded cross-module
value), r732_def_xmod_cycle (error 'def value: reference chain too
deep', upgrading the needle-less exit!=0 check).
The five same-module exec rows and both same-module fail-loud rows were
already owned by the r732_def_* fixtures; their cs-vs-ww byte identity
rides test-data-byteid, which now also covers the xmod fold row. The
timeout-124 hang distinction devolves to the wwfixture harness timeout.
696_modtype_leaf_collision.c -> r696_modtype_leaf_pos (run-exit 26),
r696_modtype_leaf_neg (staged error; w6c 'no field', w6c_ww
asserttyped: dot — the old no-wwstage-checkfile comment is stale)
697_samemod_prefer.c -> r697_samemod_prefer (compile)
699_use_promote_alias.c -> r699_usepromote_{type,fn,def,var}
(run-exit 42 each)
Single-file multi-package form folds the modcollision/samemodprefer/
usepromote data trees into the fixtures; the trees retire with their
carriers. The dir-tree .wwi transport of colliding type decls is owned
by test/byteid/wwi_test.ww. Coverage upgrades: 697 and the 699 rows
gain the wwstage leg the carriers gated or skipped, and all positive
rows enter the blanket test-data-byteid net.
m3sep_test.ww drives the raw w6c/w6a/w6l toolchains (+_ww twins) over
hand-composed //ww:module units splicing produced .wwi bytes: the
bodies==.wwi keystone on both stages per package (mid/root/smid) with
determinism, rule-10 and the value-global DATA guard; the end-to-end
i32 chain (exit 59) with final-exe byte-id; and the #49 str-label /
#53 export-leaf link legs (exits 160/140) with their .s needle
proofs. Each @test re-produces its .wwi cold instead of sharing the
carrier's single scratch dir (same claims, sharper isolation).
septest_test.ww keeps all five rows (pass/fail run-exits, the
userman + badsig @test rejects, the collide_run user-fn-run coexist)
plus the full-sepwork .s/.wwi byte-id sweep and the -T synth
TEXT-main / CALL-test.run presence needles, both driver stages. The
pass/fail run-exit rows overlap package_test ownership but stay:
retiring them would drop audit-listed assertions from this gate. The
attest_* data fixtures remain in test/wcc/data (also consumed by the
911_attest_* carriers).
sepscratch_test.ww pins the #59 build_one_sep scratch ownership on
both driver stages: build -o keeps <stem>.sepwork, `run` leaves no
/tmp/ww_run_<pid> root, and a foreign pre-existing .sepwork is
refused with exit exactly 1, sentinel intact. The C fork/execl pid
capture becomes `sh -c 'echo $$ ...; exec <drv> run ...'` -- exec
keeps the shell's pid and passes the full driver path as argv[0].
seplink_test.ww carries the .a substrate legs (root-as-.o layout, .a
byte-id + 3x determinism, the #31 masked-dup PASS-3 reject with its
non-vacuity flip), the loud dependency-cycle reject with byte-equal
cs/ww stderr, and the w6l/w6l_ww bare-main duplicate-symbol reject
(achievable substring parity per the filed message divergence).
sepimport_test.ww re-hosts the #98 colo srcd-shadow `ww test` gate,
the #99 imported-dep bare-main mangle count, the #23/#30 modfn
coexist byte-id (incl. the cross-layout vbu order pin), and the
#58/#50 scopelookuptype mod-preference rows over the checked-in and
synthesized module trees. slttypepref's ww_ww-absent skip gate is
dropped: the Make target declares both drivers (mangle_test
precedent).
Birth test/sep/ (single-file ww observers on test/testenv, run by the
new SEP_WW_TARGETS pattern rule under test-compiler) with
sepbuild_test.ww: the build_one_sep real-chain gate incl. the
bodies==.wwi keystone recompile, the #69 root-without--I contract, the
#70 aggregate-def .wwi prototypes, and the #95 spread-marker
round-trip. Every carrier assertion has a successor row; the C
cleanup-accounting ledgers collapse into testenv.clean.
Evaluate the call pre-grow into a per-site scratch, receiving by the
N_LET matrix (sret / float / odd-tail / GP), then grow, slot, sized
ladder. Both stages, byte-identical. The two reject pins graduate to
16B GP accept rows; three new fixtures cover 24B GP, 3B/4B tails,
16B float, and 40B sret.
Four ww tests carry the last eight native byteid carriers' assertions:
asmwindow (753 convwrap beta/main/alpha order + window polarity, 754
slice stride + negative scale scan, 755 amp-dot-idx four rows, 758
first-CALL-line extraction + tab-framed disp literals, direct
w6c/w6c_ww never ww build), freenoop (930 byte-id strengthened to
per-stream compare + negative 'free' grep), structabi (946 param/ret
MOVSD windows with polarity tables and SEND/RECV agreement), mangle
(989_m1mangle needles + concat byte-id, glob order strengthened to
byte-lexicographic). Dead want/stage_mask row fields documented, not
invented into runtime legs; the w6c_ww-absent skip gates drop because
the Make target declares the tools.
BYTEID_WRAPPER_SOURCES, its bins, and test-native-byteid are deleted —
the native byte/artifact partition is EMPTY (11 -> 0 this session);
docs counts move to 123 carriers.
25 renames (git mv, content untouched). _test.ww is what the package
coordinator's test detection and the sep loader's canonical exclusion
key on; the old *test.ww spellings survived only through the
line-leading-@test compatibility scan. Consumers updated in place:
LIBRARY_TESTS, the libbyteid roster, the 901/974/975/976 carriers that
copy or invoke these files, and the check.c/check.ww + path/ftos
comments that cite them. Closes the open-driver-work migration bullet.
test/byteid/wwi_test.ww carries every assertion of both carriers: the
M2 producer gate (ascii/strings/getopt driver-combined positives with
byte-id + wwdump -a re-parse, the synth decl-kind/type-node sweep with
the #47 @symbol round-trip, the #48 types export-def gate, and the
negative private-nominal leak with both-stage identical reject
diagnostics), plus the #11 wwileaf decl-less-leaf rows (package-line
leaf, cross-stage .wwi byte-id, importer resolution + .s byte-id).
Byteid carriers 10 -> 8; docs counts move; the byteid ww target gains
the wwdump prereq its re-parse legs invoke.
test/testenv (package testenv) is the shared plumbing for ww-native
test drivers — subprocess launch with captured output, file IO, string
search, byte-sorted directory listing, scratch ownership — lifted from
the proven package_test.ww idiom; a utility, not a framework.
test/byteid/libbyteid_test.ww carries every assertion of the retired C
carrier: the 44-entry roster (fixtures + sentinel-guarded import probes
+ the zero-dep root-only build), both-stage sep builds with the
resolved-unit proof, per-package .s concat compare with the empty-concat
guard, the ID/DIVERGE/WWREJECT pin discipline, and the lib/ corpus
completeness scan (negative-verified against a planted un-enrolled
module). Concat order strengthened from shell-glob to explicit
byte-lexicographic. The roster fills by cursor-driven field writes
because append() rejects struct-call-result sources (#34).
Byteid carriers 11 -> 10; docs counts move; test-byteid runs the ww
test via the wwtest/ pattern with WW_TEST_REPO.
One phony target per test file so -jN parallelizes both suites, each
file building in its own persistent workdir so only changed packages
recompile. Tests always execute — the workdir caches builds, never
results. Lang tests keep their exact historical invocation (no -I);
library tests keep -I lib/ww. Warm -j4: test-library 19.0s -> 1.0s,
test-lang 19.8s -> 2.0s.
Cold populate, full-skip rerun, surgical staleness (one package's
artifacts deleted), and a 0-byte .o poison drill each build the w6a
graph through a persistent workdir and must be byte-identical to a
classic fresh-scratch build, on both driver stages, plus the
cross-stage clean compare. Wired into test-all; the inner-loop targets
stay untaxed.
Per-binary source lists corrected against the real import closures
(strace of each cold sep build): every wwstage tool gains
lib/types/types.ww, wwdump_ww/w6c_ww gain lib/math/{floats,math}.ww and
drop the no-longer-imported lib/fmt/fmt.ww, ww_ww drops
lib/strconv/strconv.ww, and WWTEST_SRC gains the strconv split plus
math. Wrapper test binaries link only libwcc.a — the runtime tools they
exec move to the run targets that launch them, so a .ww edit no longer
relinks 111 carrier binaries. test-compiler-smoke declares the
w6a_ww/w6l_ww/ww_ww fleet its WW cell actually drives (latent
fresh-clone gap); test-package replaces the blanket all with its exact
tool set; test-lang/test-library name the cstage subset they invoke.
The five wwstage tools, wwtest, wwfixture, both exec-test binaries, and
the package-test binary move off the per-invocation mktemp workspace to
one persistent ww-build workdir per target under out/wwbuild/, publishing
into out/bin by one atomic mv. Package artifacts now survive across
rebuilds and reuse on exact content identity, so a toolchain-source
touch rebuilds in seconds instead of five from-scratch sep builds; the
per-target split keeps -jN recipe concurrency race-free. Rebuilt
binaries verified byte-identical to the mktemp-path builds for all ten
targets.
A -w DIR workdir replaces the fresh .sepwork scratch with a caller-owned
persistent package-artifact tree. A package is reused only when its
freshly composed unit byte-equals the committed unit and byte copies of
the compiler/assembler recorded in the dir equal the live tools — pure
content identity, no mtimes, no hashes, every decision reproducible
with cmp against plain files. Recompiles stage at .new names and commit
by rename, unit strictly last, so an interrupted build forces a
recompile and can never leave a committed unit vouching for uncommitted
artifacts; .o/.a additionally reject zero size (ELF/ar are never
empty), while .s/.wwi accept legitimate empties (FFI-only rt). A mode
stamp pins the -T/-S shape and the artifact protocol revision. Classic
scratch keeps its exact acquire/refuse/cleanup contract; run rejects
-w; dir-mode test rejects -w; both driver stages implement identical
behavior and wording.
Ten of the thirteen were already fully twinned — their runtime rows
migrated in earlier waves (r56_def_*, r79_*, r78_*, r926_*,
r989_m1union_*) and the carriers survived only for a cstage-vs-wwstage
byte-compare the blanket test-data-byteid comparator owns once the
source is a corpus fixture. The other three needed their remaining
inline sources added first: r756_* (4, alias-chain unwrap),
r844_bid_src (size(u64) untyped-int compare), r989_structframe_* (3,
struct-local frame layout). Every new fixture was verified both-stage:
compile exit 0 twice, .s byte-identical, runtime exit as pinned.
The five ww_ww-driver-leg carriers among them (785/786/789/790/
m1union) follow the 815/940/951 precedent: content identity owned by
their corpus twins, driver-leg identity owned by 989_lib_byteid's
sweep. Byte/artifact partition 24 -> 11; carriers 147 -> 134; corpus
pin 1495/2990; the stale 915 sweep figure in the docs corrected to
the true 1,157 non-error count.
fnptrcalleetfn's N_DOT arm required an N_IDENT base, so a.b.cb(...)
fell to the name registry with an empty module hint and emitted
CALL cb(SB) (undefined symbol; cstage calls the stamped ptr indirect).
dotbasestructinfo resolves the base chain through the struct registry
— each link a struct- or *struct-typed field — and the single-dot
path routes through the same resolver unchanged. Closes the last open
shape of the #59.8 name-keyed callee family. Fixture fnptrfield_chain;
corpus pin 1487/2974.
installparams normalizes a decl's `T...` param lhs to []T in place
(cstage instead sets the resolved tp->type and never mutates AST), so
typeeqast compared a decl-synthesized fn type's []i64 against a fn
TYPE expr's surface i64 and confidently rejected
`let f: fn(args: i64...) void = sum;` — cstage accepts and runs. The
normalization wrapper now carries an op marker and typeeqast peels
exactly it, so both sides compare at the declared element type without
admitting genuinely different element depths. Fixture fnptrlet_variadic;
corpus pin 1486/2972.
The last frontend-gap pin: wwstage had no Hare struct embedding
(struct { hash.hash, ... }), rejecting lib/crypto/sha256 at parse.
- parse.ww: the three member forms (named / anonymous struct / bare
dotted-ident embed), consume-then-branch since this parser has no
peek; embeds carry f.str == "" and the type in f.lhs.
- check.ww N_TSTRUCT flatten: promote the inner struct's flattened
fields at base+src.offset (check.c:961-990); the embed is one
nested-struct unit in the slot ladder; the resolved inner AST is
planted on the TFIELD rhs for cgen.
- check.ww walkers: astoffset / exprtype N_DOT / #251 struct-lit
field lookups descend embeds through shared helpers; the collision
and non-struct-embed rejects live in validatestructfields (the
once-per-decl diagnostic site).
- cgenutil.ww registerstruct: regfieldrun walks the AST against the
flattened tfield cursor, descending embeds via the planted inner
AST so promoted fieldinfo entries keep the inner field's own name
and type node.
- wwi printers unchanged (both stages already emit nameless fields).
sha256_test compiles byte-identically end to end and its 6 tests
pass; 989_lib_byteid is now 44 id / 0 divergent / 0 wwreject.
Fixtures r5913_* (promoted rw, offset shift, anonymous embed,
two-level embed + promoted fn-ptr callee, three rejects); corpus pin
1485/2970.
fnptrcalleetfn's N_DOT arm accepted only a bare N_TFN field tnode, so
a call through a field declared `cb: *fn(...)` missed, fell through to
the name registry, and emitted CALL cb(SB) — an undefined symbol
(loud at link; cstage calls the stamped ptr-to-fn indirectly). Same
TPTR peel the N_IDENT arm already had. Fixture fnptrfield_call covers
the by-value and via-pointer shapes; corpus pin 1478/2956.
The migrate-and-retire audit left the surviving carriers as the only
files still describing the retired combined.ww amalgamator as live:
m2wwi/m3sep headers claimed .wwi was dead code, four byteid carriers
carried dead cleanup of never-written .combined.ww intermediates, and
sepbuild's enumeration mirror plus lib_byteid's completeness scan
still skipped the retired artifact form (the same silent
accommodation just removed from both drivers — dropping it here
surfaced and flushed 39 stale untracked amalgamator outputs across
lib/, which the gates now reject loudly). Remaining mentions are
past-tense history or quoted diagnostics.
Audit-driven migrate-and-retire wave (one read-only auditor per
carrier batch, verdicts row-checked through the full corpus gate):
every retired carrier's assertions are now owned by declarative
fixtures or an existing owner. 238 new fixtures land (corpus 1,239 ->
1,477; cells 2,954), covering enum/str/slice/tagged array elements,
inferred-length arrays, global slice/str zero-init and literals,
global array/pointer field reads and stores, struct-literal slice
fields, struct returns, float arithmetic, size/int limits, sort and
log-vstream behavior, place-addressed stores, and append places.
Rejection rows STRENGTHEN the old nonzero-exit checks to required
diagnostic fragments; all byte-id claims fold into the blanket
test-data-byteid sweep (now 1,142 compared, 0 pinned-divergent).
Four carriers were fully redundant with existing fixtures/lang tests
(749, 722, 765, and 771's byteid rows) and retire without new rows.
Native carriers 171 -> 147 (residual 135 -> 111).
PROJECT_PLAN.md was a historical disclaimer wrapping five still-open
T1 driver items — those move to docs/test-system-v2.md (Open driver
work); the rest is dated 2026-08-05 design history, archived in git.
Also retired-mechanism cleanup: both drivers drop the *.combined.ww
enumeration skip (the amalgamator is gone; leftover debris now fails
loudly instead of being silently accommodated), the consumer-less
m4_combined_epoch.md5 pin is deleted, and the wwi.ww/ww.h/check.ww
comments that still described combined.ww as the live path are
re-cited to the sep reality.
The wwstage variadic call classification was name-keyed: a fn-ptr
FIELD call whose local base shadowed the current module name
(lib/log's log.println(log, args...)) picked the module fn's
signature — nfixed off by one, the fixed arg boxed into the gather,
the spread emitted as zeros — and a no-collision fn-ptr callee missed
the registry entirely, leaking the raw N_SPREAD as a single $0 word
(SIGSEGV / exit 255 in 8 of 11 logtest tests on the wwstage leg).
fnptrcalleetfn resolves a local fn-ptr callee (bare local or struct
field) to its N_TFN once, shared by the CALL-target choice,
callee_variadic_param (with the []T wrap registry params get from
installparams), calleecvariadic, and the widening param lookup, so
target and arg prep can never disagree. Graduates the #59.8 logtest
pin — DATABYTEID_DIVERGED-era M_DIVERGE count is now zero.
A TYPE name used as a VALUE (an error-singleton `return too_long;`)
stamped the flattened BODY type, so structurally identical !void
singletons in one union were indistinguishable and flatvariantidxt
loud-rejected the ambiguity — the real cause of the lib/path wwstage
reject (the pinned #120/#29 global-slice-const blame was stale; that
family had drained). The N_IDENT arm resolves through a synthesized
TNAME (the #66 N_STRUCTLIT precedent); the module-qualified N_DOT twin
reads the sym's cached NAMED. Graduates the path M_WWREJECT pin (#142).
An enum OR-fold under `as` ((m.A|m.B) as u32) was never typed:
scruttype resolves IDENT/DOT only, and the post-order restamp then
re-derived the folded member literals as untyped_int. The unstamped
operand missed cgtypeassert's #27b enum-reinterpret gate and lowered
as a phantom tagged assert — unconditional exit 1 at runtime on the
wwstage leg. checkisas now types an N_BIN lhs and the N_BIN restamp
preserves an existing enum stamp. Graduates the #59.9 stattest pin.