The E3 driver flip makes build_one_sep the sole path; it writes
.sepwork/*.unit.ww, not <stem>.combined.ww, so the flip stops producing
combined.ww. Migrate its consumers to sep-feed first, while both build
paths still exist (reversible; all 5 stage binaries stay byte-identical):
- 990_selfhost: resolveunit drives `ww build --sep` and reads
<stem>.sepwork/__root.unit.ww; probe_cgen_match gains err.ww (sep
inlines fmt's .wwi only, so cs==ww holds); probe_ww_links re-expressed
as `ww_ww build --sep`->run; probe_bootstrap_fixed_point removed (the
monolith path it tested is deleted by E4 -- the self-application axis
lives in `make bootstrap`, the cs==ww axis in 995).
- 994_w6c_ww: drop the combined-fed emit_eq + corpus; keep the live
per-package cs-sep==ww-sep oracle (cmp_sepwork + diff_one + bad[]).
- #110 combined_ww_fresh gate removed (its regen-vs-committed premise
dies with its producer).
Non-vacuity proven: a one-line emitter mutation reddens 994's per-package
cs!=ww check across 15 packages; revert restores green.
With intermediates following the output stem, concurrent wwstage-driver
builds no longer share paths - 950/990-997 join the xargs -P group.
Measured: 202.7s -> 153.8s full-test wall (x2 runs race-clean); the
serial tail (57.7% of wall) is gone, remainder is CPU-bound. CLAUDE.md
rule 14 documents the new reality: NNN range no longer a correctness
constraint, still keys phase membership / skip set / cache set.
The 990-997/950/989_lib_byteid gates are pure functions of (stage
binaries, tracked sources, fixtures, harness, Makefile) and eat 87% of
full-test wall (measured, .ai/testperf-baseline.md). test-commit hashes
exactly those inputs; a match with the last green full run reports the
gates as 'cached' (counted separately, never silently) and runs
everything else - 25s vs 212s on a hit, full run on any miss. A red
run never records the key; make test itself is never cached and stays
the pre-push bar (rule 14 updated). Key gap caught in review:
examples/ feeds 996_dyn_ww - included.
Emits a timing block (durations, phase walls, total) before the summary
line; the ok/FAIL and 'all N passed' contract is unchanged and the only
consumer (make's exit-code gate) is unaffected. Measured baseline at
4ff79bf: 211.6s wall, 990-995 self-compile gates = 87% of wall — the
content-keyed skip tier (T1) targets exactly that block. Baseline data:
.ai/testperf-baseline.md.
A test/wcc/NNN_*.c with no Makefile-wired binary used to write a SKIP
status, count toward "all N tests passed", and leave MAKE_TEST_EXIT=0.
Two live instances: 953_arrlit_slice_run skipped under a green gate for
weeks (committed unwired at bf1037d, wired in the previous commit), and
a 938 hit the same hole mid-gate. The runner now treats a missing binary
as FAIL naming the file and the target to add, touches the .fail marker,
and so flips the summary + exit code. Closed by construction: make test
builds every $(TESTS) target before the runner walks test/wcc/*.c, so
the missing-binary path is reachable only by an unwired file — there is
no legitimate missing-binary SKIP to preserve. Intentional skips keep
their existing visible forms (UNIT-mode non-enumeration; in-test per-row
skip messages), neither of which folds into the pass count.
Count math: the summary N is unchanged (a SKIP already incremented ran);
what changes is honesty — pre-fix "all N passed" could include silent
skips, post-fix every counted test actually executed. A bare sh test/run
without the make-built binaries now fails loud instead of green-skipping
the whole suite.
Phase-2 (950/990-997) was blanket-serial only because ww_ww writes build intermediates next to source (#15), so gates race on selfhost/cmd/<tool>/main.* stems. Per-gate FS-footprint audit (ken): the reader group {990,991,992,994,996,997} writes only /tmp or disjoint tracked stems (smoke/mandelbrot) — parallelise race-free via the Phase-1 xargs -P machinery; the source-tree writers {993,995,950} stay a serial tail; combined_ww_fresh last. Verdict set identical (byte-id-neutral — parallelism alters scheduling, not emitted bytes); 2 stable green runs (237 each). make test ~459s -> ~390s.
test/run fans out test/wcc/*.c except 950 and 990-997 across $(nproc)
xargs workers, then runs the byte-id gates sequentially in phase 2.
Each worker writes <prefix>.status into a mktemp results dir; the
collector emits them in glob order for deterministic output. Phase 2
sequentiality sidesteps the race introduced by ww_ww writing
intermediates next to every traversed source (#15) — concurrent reads
of selfhost/cmd/<tool>/main.{combined.ww,s,o} would see partial bytes
(#16).
`make test-unit` (UNIT=1) skips phase 2 entirely for a 3.8s inner-loop
check; `make test` runs the full 132 in ~8:30. test-unit goal aligns
with the selfhost-bootstrap project goal: 990-997 + 950 are the
toolchain-rebuild gates, fast feedback is for unit work below them.
990-995 wrap every ww/w6c/w6a/w6l/wwdump invocation in `timeout 180`;
995's hardcoded /tmp/ww_d_hello.ww is now pid-keyed. 995 forks its 5
ww_ww builds concurrently (waitpid for collection) so its solo wall
drops from 3:15 to 1:32. Phase-2 split (parallel readers + sequential
writers) deferred to #17, post #15.
Wall: 9:59 → 8:32 (full) / 3.8s (test-unit, ~158× from baseline).
test/run fans test/wcc/*.c across $(nproc) workers via xargs -0 -n2 -P
$JOBS; each worker writes <prefix>.status and an optional <prefix>.fail
sentinel into a mktemp results dir. The driver collects in glob order so
output stays deterministic across runs.
Wraps every ww/w6c/w6a/w6l/wwdump invocation in 990-995 with
`timeout 180`, bounding orphan compilers under 8-way contention. The
direct runwait(exe) calls for fresh-built test binaries get the same
treatment via a "timeout 180 %s" snprintf hop. Motivation: pid 2101 was
a w6c_ww that ran 42h on /tmp/wcas_asm_1326_15.ww until we killed it
during this session; timeout makes that impossible.
993's /tmp/ww_d_hello.ww is now pid-keyed (snprintf %d getpid); without
it concurrent runs (or future shards of 993 itself) would race on the
staging file.
Wall: 9m59s → 4m31s on 8 cores. Same 132/132 status.
Plan 9-style w-prefix on the per-arch tools, disambiguating from the
real Plan 9 6c/6a/6l in ref/plan9front/:
cmd/wwc/ → cmd/wcc/ libwwc.a → libwcc.a
cmd/6{c,a,l} → cmd/w6{c,a,l} binary names too
test/wwc/ → test/wcc/ 6 test files w/ w6 prefix
selfhost/cmd mirror in lockstep
bootstrap/amd64/{w6c,w6a,w6l} snapshot binaries (gitignored)
WW_6{C,A,L} → WW_W6{C,A,L} env-var overrides
Plan 9 source-tree refs ("Plan 9 6c shape", ref/plan9front/, etc.)
preserved. Hare-style driver, both C and ww sides:
ww test [path] discover *_test.ww in a directory module, run
each; single-file mode for `ww test foo.ww`
Module-by-name `ww build foo` resolves to foo.ww or foo/foo.ww
via search path (cwd : -I dirs : $WW_LIB)
Default-to-cwd `ww build` / `ww test` build the cwd module
Run pass-through `ww run path arg1 arg2` reaches the program
lib/os: getcwd (79) and getdents64 (217) syscalls power `.` resolution
and directory enumeration on the ww side.
Makefile: wwstage tool deps now include lib/os/os.ww (+ lib/strconv
for wwdump_ww) so lib/* edits force their rebuild instead of leaving
stale binaries — surfaced when test 995 first failed against a stale
w6c_ww built before the lib/os additions.
Test 993 byte-identical parity gate (C-side ww vs ww-side ww_ww on a
build corpus) stays green; all 19 tests pass.