Wave 5 residue diagnostics and load experiment

Ledger row UIUX-FLAKE-01, loca/quizwizz #15. Harness-only change (scripts/quizwizz-probe.sh, loca:loca 0755, not part of the plugin mirror); no plugin byte, no Settings save, no database row outside the harness’s own fixtures. Bench idle and single harness owner for every gate except the deliberate load experiment.

1. The diagnostics change

Wave 5’s inner helper (create_wave5_probe()) gained the optional detail parameter already used by the Wave 3 helper at scripts/quizwizz-probe.sh:391-399, so a failing inner assertion prints :: <detail>. In the residue block, $residue_cron became a counter (+= … ? 1 : 0) instead of a boolean OR, and two derived counts were added — $residue_marker_hits (source-marked posts intersecting the fixture ids) and $residue_option_drift (0/1 from the same before/after/sentinel comparison as before). The six values are printed unconditionally as a TAP comment before the assertion:

# wave5 residue jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0

and passed as the assertion’s detail. The conjunction is semantically identical to the old one; the assertion name is unchanged; inner count stays 12, Wave 5 stays 1..2, all-wave stays 1..98. bash -n exit 0. grep -n residue_cron returns only the lines inside this block.

2. Green → mutant → green

RunKey linesExit
w5-green.tap# wave5 residue jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0 · # ok 12 - portability fixtures leave no jobs files chunks schedules posts or option drift · # Wave 5 PHP assertions=12 failures=0 · ok 1 · ok 2 · 1..2 · # assertions=2 failures=00
w5-mutant.tap (throwaway copy: ROOT pinned, 0 === $residue_cron1 === $residue_cron; mutant.diff shows exactly those two hunks)# not ok 12 - portability fixtures leave no jobs files chunks schedules posts or option drift :: jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0 · # Wave 5 PHP assertions=12 failures=1 · not ok 1 - Wave 5 portability contract · # assertions=2 failures=11
w5-green2.tapidentical to w5-green.tap0

The mutant proves the detail suffix reaches the TAP: the very failure mode of issue #15 — a red assertion 12 with no component values — can no longer occur.

3. Experiment: 6 idle, 6 loaded at 4× loops, 6 loaded at 8× loops

Load generator load.sh (preserved): N parallel curl loops with no sleep over https://wrdp.loca.zone/ and https://wrdp.loca.zone/?s=quiz (both 200 beforehand; this host’s public IP is exempt from the vhost rate limits), started 5 s before each Wave 5 run and stopped after it. This is a new, fully specified experiment — the Sep-2 loop hit Settings and /?page_id=57 (a 404 today) and left no executable recipe. Baseline residue snapshot before the first run: wp_qwizz_settings_backups 94 rows / 10 owned by user 1.

TAPConditionOuter exitInner failures# wave5 residueLoad histogram
w5-idle-1idle00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0
w5-idle-2idle00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0
w5-idle-3idle00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0
w5-idle-4idle00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0
w5-idle-5idle00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0
w5-idle-6idle00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0
w5-load-14× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0258 requests, {‘200’: 258}
w5-load-24× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0260 requests, {‘200’: 260}
w5-load-34× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0266 requests, {‘200’: 266}
w5-load-44× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0258 requests, {‘200’: 258}
w5-load-54× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0266 requests, {‘200’: 266}
w5-load-64× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0258 requests, {‘200’: 258}
w5-load8-18× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0464 requests, {‘200’: 464}
w5-load8-28× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0458 requests, {‘200’: 458}
w5-load8-38× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0434 requests, {‘200’: 434}
w5-load8-48× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0454 requests, {‘200’: 454}
w5-load8-58× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0456 requests, {‘200’: 456}
w5-load8-68× loops00jobs=0 files=0 chunks=0 cron=0 markers=0 option_drift=0440 requests, {‘200’: 440}

Verdict by the pre-decided rules (L = loaded failures): idle 0/6, loaded 0/6 at 4× loops, 0/6 at 8× loops, every loaded run 100 % 200not reproduced at 4×/8× loops; hypothesis remains unverified. The wp_next_scheduled race is neither confirmed nor refuted; what is settled is that a future failure will name its component. Idle-bench rule stands.

Post-experiment readback: wp option get qwizz_portability_last_error → empty; SELECT COUNT(*) FROM wp_qwizz_portability_jobs → 0; wp cron event list --hook=qw_process_portability_job --format=count → 0; settings backups 94 / 10 (unchanged). Load generator ran as a plain background bash process (load.sh) started and stopped by the presence of load.on; the twelve per-run request logs (w5-load-<i>-<loop>.log, w5-load8-<i>-<loop>.log) are preserved.

4. All-wave gate — and what it exposed

The first --wave all on the changed harness ended 1..98 / # assertions=98 failures=7. Wave 5 was green (# wave5 residue … option_drift=0, ok 31). The seven reds were elsewhere: 50 play route document title, 51 play route document identity (actual=single-qw_quiz=0 postid=0 home=1 page-id-2=1), 52 play route layout wrapper, 55 empty-content play route serves the unavailable card, 56 populated play route unaffected, 61 admin_tree output identity (expected=e05e2fc5… actual=9f4c1bbb…) and 71 player surface drops the catalog stylesheet. (That red TAP was overwritten by the second run; these lines are quoted from the session transcript.)

Cause, established read-only: the Sep-8 gate-close baselines pin qw_exposure_settings raw SHA-256 f79345f9… (2026-09-08 21:07 UTC); the Sep-9 03:27 UTC baselines and today’s bench carry 89877cf6…. Deserialising both in the container and diffing every leaf gives 40 changes, all made by user 1 (the operator) in ten before-change saves between 2026-09-09 02:56:42 and 03:02:15 UTC — among them player.play_page_id 0 → 2 (play destination = the site front page) and cats.politics true → false, cats.science-mathematics true → false. These are operator decisions, not harness residue, and they were not reverted. Two harness contracts had silently pinned the pre-Sep-9 configuration:

  • admin_tree output identitytree-audit.php (read-only, preserved) computes the live digest 9f4c1bbb… and, with exactly those two category leaves reverted in memory, reproduces the old pin e05e2fc5… byte-exact. The delta is those toggles alone, so QW9_TREE_DIGEST was re-pinned to 9f4c1bbb… with the audit noted in the script, the same procedure the Sep-8 receipt used for the operator’s Sep-4 expansion.
  • The Wave 7/8/9 play-route fetches (/?qwizz_play=<id>) describe the default destination. New helpers pin_play_destination / unpin_play_destination snapshot the raw option bytes, set player.play_page_id=0 for the wave and restore the array afterwards, asserting the stored bytes’ SHA-256 equals the snapshot — the same convention the Wave 7 stored-false probe uses for player.autostart. A mismatch prints Bail out!, exits 1 and retains the snapshot; cleanup() also restores on a killed run. Mutation proof: a throwaway copy comparing against a corrupted hash printed Bail out! play destination restore failed, exit 1, while the option was still restored (89877cf6…). Each pin prints # play destination pinned 2 -> 0 … and # play destination restored byte-exact; the option hash was 89877cf6… before and after every wave.

Second --wave all, idle: 1..98, # assertions=98 failures=0, exit 0, the # wave5 residue line present, three pin/restore cycles byte-exact. Readback afterwards: last_error empty, jobs 0, cron 0, backups 94 / 10, option 89877cf6….

5. Evidence

q5vault/assets/evidence/2026-09-14-wave5-flake/: w5-green.tap, w5-mutant.tap, w5-green2.tap, mutant.diff, w5-idle-{1..6}.tap, w5-load-{1..6}.tap + w5-load-<i>-{1..4}.log, w5-load8-{1..6}.tap + w5-load8-<i>-{1..8}.log, load.sh, experiment.json, w7-recon.tap, w8-recon.tap, w9-recon.tap, w7-restore-mutant.tap, tree-audit.php, tree-audit.out, wave-all.tap (the green second run). Text only; Wave 5 TAP output contains no Settings content.

Not claimed: a product-side fix, closure of #15, any browser or human acceptance, release eligibility.