Files
Syphon_Filter_3/tools
Christopher Williams d2cf1b8f10 phase12: sf3_cc's maspsx stage NEVER RAN, then doubled the output — fixed (worker B's finding)
Worker B measured this and was right, and the diagnosis is exact. The call was

    maspsx.py --aspsx-version=2.56 "$OUT/$BASE.s" "$OUT/$BASE.ms.s"

but maspsx takes ONE positional (an INPUT) and writes to STDOUT -- it has no output-file
argument, which is precisely why sf3_match runs it as a stdin->stdout filter (run_filter).
So maspsx tried to open the OUTPUT path as its input, failed, and the failure was hidden
TWICE: by `2>/dev/null`, and by a `|| cp` fallback that quietly copied the raw cc1 output
over `.ms.s`.

Measured on src/func_8006B5F0.c before the fix:

  run 1, fresh scratch : .ms.s IDENTICAL to .s (71 lines) -- maspsx NEVER APPLIED
  run 2, scratch exists: 133 lines = TWO body copies with 6 `addu`, because maspsx now read
                         the STALE .ms.s as input, succeeded, printed its real output STRAIGHT
                         TO STDOUT (unredirected), and `cat` printed the stale raw copy after it

After the fix: 62 lines both runs, IDENTICAL to each other, stdout == .ms.s, and .ms.s differs
from .s for the right reason (the stage actually runs).

**So the tool was wrong on the first run and wrong-and-doubled on every later one, from its
Phase 11 promotion (b29963e) until now.** Worker F built it for spelling sweeps and its whole
premise is "one spelling costs ~0.1 s"; the cost was fine, the output was not. Cookbook 185's
description of this tool was wrong for as long as the bug existed.

AND I EDITED THIS VERY FILE EARLIER TODAY to fix `--help` WITHOUT NOTICING, while writing a
LIMIT paragraph about a maspsx stage that was not running at all. Three of four workers had
already tripped on the `--help` path, so I fixed the thing that was visible and documented the
thing I assumed, which is the same failure shape as cookbook 190 an hour earlier: I described a
contract instead of reading the tool that enforces it.

The fix is the correct filter form plus the removal of BOTH things that hid the failure. There
is deliberately NO fallback: a maspsx failure must be loud. A fallback that converts a hard error
into plausible output is worse than no fallback -- it is exactly how a broken tool survives a
whole phase of use by four workers and enters the documentation as working.

Five tests pin it, and two of them are the ones that would have caught this on day one:
  * maspsx must CHANGE the cc1 output (if .ms.s == .s, the stage did not run);
  * two runs into the SAME scratch must agree (catches the stale-read + stdout doubling);
plus exactly one body label emitted, stdout == the staged file, and no `2>/dev/null`/`|| cp`
in the executable body (comments excluded -- the fix's own explanation quotes both patterns).

Concrete cost, per B: it read a cc1-only `jal`/`lw` as a filled delay slot and nearly re-derived
a row that in fact matched. Only `sf3_match range` settled it.

  make test   306 tests, OK   (from 301)
2026-09-24 17:08:29 -04:00
..