▶️ 2026-08-26 play surface repair
Historical record — 2026-08-26
Preserved as observed. Cluster 1 of the ordered series delivered by the 2026-08-27 canonical handoff; the four findings it recorded as not fixed have since been closed, per the dated status lines below. Current truth: Program Masterplan.
Scope: WRDP bench, 2026-08-26, Cluster 1 of the play-flow masterplan. Citations are against the installed source under wp/wp-content/plugins/quizwizz/. Every figure on this page was measured this session; nothing is carried over from a plan, changelog, or prior page.
Defects
- operator-reported:
- clicking Play landed on
/?qwizz_play=<id>and showed a dead intermediate screen, a hint plus a singleStart quizbutton, before the quiz began - the quiz surface rendered a dark card floating inside a theme-white page with 56px gutters and no site chrome
- clicking Play landed on
- adjacent defects on the same route, in scope by operator decision:
- the route emitted no
<title>element at all, so the browser tab was blank body classreportedhome … page-id-2while rendering quiz 99
- the route emitted no
End state reached: the play route opens question 1 with no second gesture; a non-site palette paints the whole standalone surface with no white frame and no card seam; the route is a singular document titled after the quiz it renders. All three Play entry points were exercised as real guest gestures, not typed URLs.
Files changed (5)
All bench-local, releaseEligible: false.
includes/Frontend/QuizEmbed.phprender()now ORs inself::is_play_route( $quiz_id )after the access checks- new private helper
is_play_route()reads$_GET['qwizz_play']and compares it to the rendered quiz id
includes/Frontend/QuizQueryViewer.php- explicit main-query state:
global $wp_query;plusposts,post,post_count,found_posts,max_num_pages,queried_objectandqueried_object_id, withis_singularandis_singletrue andis_home,is_front_page,is_page,is_404false - one
<title><?php echo esc_html( wp_get_document_title() ); ?></title>after the viewport meta and beforewp_head() - block output wrapped in
<main class="qwizz-single-quiz" id="qwizz-content">
- explicit main-query state:
assets/js/qwizz-player.jsapplyPresentation()mirrors a non-sitetheme ontodocument.body.dataset.qwizzThemeand deletes it otherwise, guarded bydocument.body.classList.contains('qwizz-has-player')- diff is exactly 6 added lines;
prepareIntro()and its resume branch are byte-identical
assets/css/qwizz-tokens.css- selector-only extension: each of the 6 palettes plus the generic
[data-theme]block also matchesbody[data-qwizz-theme…], andbody[data-qwizz-theme]joins the@media (forced-colors: active)selector list - zero declaration changes; the
--qw-card:occurrence count is identical to the pristine copy
- selector-only extension: each of the 6 palettes plus the generic
assets/css/qwizz-player.css- two appended rules:
body.qwizz-has-player[data-qwizz-theme]{background:var(--qw-card);color:var(--qw-ink)}andbody.qwizz-has-player[data-qwizz-theme] .qwizz-player__card{background:transparent;border-radius:0}
- two appended rules:
Deliberately not changed
- the
bool $autostart = falsedefault inQuizEmbed::render()is unchanged, so passive embeds keep asking - no
<h1>was added to the play route: the player owns the title there, and adding one would restore the duplicate-title defect closed 2026-08-25 .qwizz-single-quizpadding untouched- no
min-heightadded to body - no palette values duplicated in
assets/css/qwizz-tokens.css; the palette work is selector-only
Canonical handoff
Three artifacts sit beside this page in q5vault/audits/qwizz/:
2026-08-26-cluster1-source.patch— 7302 bytes, sha256644de6e5d6e88f974c3262ee2963b8fea19ac9096c1e92c728c349f85b6c18ea2026-08-26-cluster1-preimage.sha256— 5 pre-fix digests, plugin-root-relative2026-08-26-cluster1-postimage.sha256— 5 post-fix digests, plugin-root-relative
Apply from the plugin root, never with --reject:
git apply -p1 2026-08-26-cluster1-source.patchIntegrity, all measured:
- generated with
git diff --no-index --binary --no-prefix a b, exit 1, a nonempty diff being the success condition git diff --no-index --checkreturned 1 with empty whitespace output- header audit: exactly 5
diff --gitstanzas over the 5 allowlisted relative paths; zeronew file mode,deleted file mode,rename,old mode,new mode, or binary stanzas - 50 added lines, 8 removed lines
- forward round-trip:
git apply -p1 --checkrc 0, apply rc 0,diff -r verify brc 0, byte-identical - reverse round-trip:
git apply -p1 -R --checkrc 0, apply rc 0,diff -r revert arc 0, byte-identical to pristine, so the rollback path is proven rather than assumed sha256sum -cof the postimage sidecar against the live plugin tree: 5/5 OK. The preimage sidecar correctly does not match live; it is the before-state record.- superseded 2026-08-26 for two of the five files: Cluster 2 changed
includes/Frontend/QuizQueryViewer.phpandincludes/Frontend/QuizEmbed.phpagain, sosha256sum -cof this postimage sidecar against the live tree now reports 3/5 OK and 2 mismatches. That is the expected chain, not drift: the Cluster 2 preimage digests for those two paths equal the Cluster 1 postimage digests recorded here, and the live tree matches the Cluster 2 postimage. Verify the chain, not this sidecar alone — the playable-quiz repair record.
Measured before/after
| check | before (pre-edit, live) | after (live) |
|---|---|---|
/?qwizz_play=99 intro | present, Start quiz | null, question 1 direct |
data-autostart | "" | "1" |
<title> count | 0 | 1, Geografie-Weltreise – QuizWizz |
| body class | home wp-singular page-template-default page page-id-2 … | wp-singular qw_quiz-template-default single single-qw_quiz postid-99 … |
| body background | rgb(255,255,255) | rgb(23,24,29), body.dataset.qwizzTheme === 'dark' |
.qwizz-player__card background | rgb(23,24,29) | rgba(0,0,0,0) |
.qwizz-player__card border-radius | 18px | 0px |
| white-painting elements between card and viewport edge | card gutters white | [], full-DOM sweep, both surfaces, both viewports |
Before-state provenance: the pre-edit values come from curl and browser reads taken before any file was installed, plus the 2026-08-26-cluster1-preimage.sha256 digest record. NOT MEASURED: no pre-fix screenshot exists. The before-state is textual and computed-style evidence plus the preimage digest record, and nothing on this page claims a pixel comparison.
Verification
Shell and lint:
php -lin-container clean on both changed PHP files, at every install and re-install- standing gate
./scripts/quizwizz-probe.sh --wave all→1..51,assertions=51 failures=0, up from1..46and 46 before this cluster; the 5 new assertions are Wave 7 logs/debug.log38536 → 38544 lines across the whole session. All 8 added lines are WordPressAutomatic updates starting/completecron blocks at 2026-08-26 08:00:36 and 19:49:17 UTC. Zero fatals, zero warnings, zero notices from any changed path.
Wave 7, new in scripts/quizwizz-probe.sh, the host-owned harness rather than plugin source:
ok 47 - play route autostart—autostart=1 gesture=0ok 48 - passive embed still asks—players=1 asks=1 autostarts=0, via a wp-clido_shortcoderender that creates no post and no attempt rowok 49 - play route document title—titles=1 quiz-titled=1ok 50 - play route document identity—single-qw_quiz=1 postid=1 home=0 page-id-2=0ok 51 - play route layout wrapper—wrapper=1 h1=0- every assertion is theme-independent: the served play-route markup carries no theme attribute, and the palette is applied client-side from the REST payload
Mutation proof. The five assertions were proved to defend their contracts, not merely to pass:
- counterfactual pass: a throwaway local HTTP server served textually mutated copies of the real play-route document, and each mutation failed exactly its own assertion and no other —
m1autostart,m3title removed,m4home body class,m5wrapper replaced by an<h1> - live pass: both changed PHP files were installed back to their pristine pre-fix bytes under an ERR-trap,
php -lclean, and--wave 7then reportedassertions=5 failures=4. Assertions 47, 49, 50 and 51 failed with the exact pre-fix shapes, the identity detail readingactual=single-qw_quiz=0 postid=0 home=1 page-id-2=1. Assertion 48 correctly stayedok, because the passive default was never part of the fix. Files were then restored,php -lclean, postimage digests 5/5 OK, and--wave 7green again. - assertion 48 was proved non-vacuous by rendering the shortcode with a play-route signal present,
$_GET['qwizz_play']=99, which is behaviourally identical to a flipped default:players=1 asks=0 autostarts=1, not the expected shape. It was deliberately not proved by flipping the real default, because a flipped default would POST/quizwizz/v1/quiz/<id>/startand INSERT attempt rows on a page view.
Browser, cache disabled, https://wrdp.loca.zone:
- logged-out guest in a separate incognito browser context at 1600×1000 and 375×812: no admin bar,
--wp-admin--admin-bar--heightunset, question 1 direct,titles=1, body class correct, bodyrgb(23,24,29), cardrgba(0,0,0,0), zero white-painting elements,scrollWidth - clientWidth = 0so no horizontal overflow, REST nonce empty confirming the true guest path, andelementFromPoint(2, viewportHeight-3)resolving toMAIN.qwizz-single-quiz, so the surface reaches the bottom edge - all three Play entry points exercised as real gestures rather than typed URLs:
- library fixture page 1289 as a guest: clicked a
Play quizcard → landed?qwizz_play=1284→ question 1 directly (How many bits are there in one byte?), no intro,titles=1,postid-1284, dark full-bleed - craft wizard on page 57 as a guest: selected
Geography, clickedPlay→ craft created quiz 11081 and redirected to?qwizz_play=11081→ question 1 directly, no intro, no failure card,autostart=1,titles=1,postid-11081, dark full-bleed, zero page errors and zero failed requests - wp-admin dashboard
?page=qw-dashboard: itsPlaylinks do emit?qwizz_play=<id>and the route honoured them, but all five links pointed at empty auto-drafts; see the not-fixed section below
- library fixture page 1289 as a guest: clicked a
- all six palettes verified on the page host by setting
body.dataset.qwizzThemeand reading computed styles: lightrgb(255,255,255)with inkrgb(33,29,49), darkrgb(23,24,29), retrorgb(239,230,208), cyberrgb(14,11,30), lovergb(255,241,245), aquargb(234,247,251). Each matches its--qw-card, each keeps its own--qw-ink, and the card is transparent with radius 0 in every case. - unthemed control, attribute removed: body returns to
rgb(255,255,255)and the card returns torgb(23,24,29)with18pxradius, proving both new rules are scoped strictly to the themed-body case and nothing leaks sitetheme control:sitewas temporarily enabled inqw_exposure_settings, quiz 1284 then resolved tosite, and/?qwizz_play=1284showed nodata-qwizz-themeon body, the Twenty Twenty-Five white page background, and the card keeping its own surface. The setting was restored and the option value md5 is identical before and after,cf4ea924e3fd29c2405b1073ddfad73c, withenabled_theme_ids()back to["dark"].- forced-colors emulation via
Emulation.setEmulatedMediawithforced-colors: active:--qw-cardresolves toCanvasand--qw-inktoCanvasText, and body paints system white on black. After disabling emulation--qw-cardreturns to#17181d. - passive embed: a scratch page carrying
[quizwizz_quiz id="99"]rendered the intro with exactly oneStart quizbutton,data-autostart="", body unthemed and white, andbody.qwizz-has-playerabsent. The attempt table read 69 before and 69 after that page view. The scratch page was deleted with--force. - resume path intact: answered one question on
/?qwizz_play=99, reloaded, and the intro returned readingResume question 2 of 10/Start over
Access control re-checked, because the craft redirect carries a qwizz_token:
- private craft quiz 11081, anonymous cookieless request: HTTP 404
- same URL with
qwizz_token=bogus: HTTP 404 - so the crafted quiz was readable only inside the crafting session and there is no access regression. As measured on 2026-08-26 the 404 branch rendered the site home template under a 404 status, a soft-404; that was pre-existing behavior at
QuizQueryViewer.phpand unrelated to this cluster. Closed 2026-08-27: the branch is nowQuizQueryViewer::emit_404()(:64-82), which clears the main query, callsset_404()and renders the theme’s own 404 template — the 2026-08-27 not-found and token-scope record.
Screenshots
Guest, cache disabled, in cluster1-2026-08-26/.

play-dark-desktop-guest.png, 1600×1000- independently confirmed in this capture: a single dark colour fills all four edges, no white band or gutter, no separate rounded card seam around the content, no admin bar and no site header, nav, or footer chrome, and no horizontally clipped content

play-dark-mobile-guest.png, 390×844- independently confirmed in this capture: the same findings as the desktop shot — a single dark colour on all four edges, no white band or gutter, no card seam, no admin bar and no site chrome, no horizontally clipped content

library-play-gesture-guest.png, 1600×1000- what this capture is: the surface the Play gesture starts from. The independent screenshot inspection covered only the two play captures above; the gesture’s own outcome was measured in the browser, recorded under the entry-point bullets, not read off this image.
Bench state left behind
Disclosed, not hidden.
- scratch page for the passive-embed check: created and deleted with
--force; page count 8 before and 8 after - craft-created quiz 11081: created by the real Play gesture and deleted with
--force;SELECT COUNT(*) WHERE ID=11081returns 0 wp_qwizz_attemptsis harness-managed test data. It read 68 before this session’s verification, 69 mid-session, and 21 after the standing gate ran its own cleanup. No manual row purge was performed: deleting rows would touch child answer rows for no benefit on a bench whose gate already manages this table.qw_exposure_settingsrestored byte-equivalent, md5cf4ea924e3fd29c2405b1073ddfad73cbefore and after- pristine pre-fix copies retained at
/tmp/qwfix-pristine/and/tmp/qw-c1/a/; the durable record is the preimage digest sidecar plus the reversible patch
Not fixed — Cluster 2 candidates
New findings from this session. None is authorized, none is scheduled, and none was touched.
Status added 2026-08-26 after Cluster 2: findings 1 and 2 are closed, findings 3 and 4 remain open. The paragraph above records the state when this page was written and is not restated.
Status added 2026-08-27 after Cluster 5, superseding the line above: finding 3 is closed; finding 4 is partly closed, and its one remaining stale site lies outside this page’s file scope. Per-finding closure lines are recorded under each finding below.
- Dashboard Play links all point at empty auto-drafts.
- root cause:
Admin/DashboardPresenter.php:308-341.recent_quizzes()queriespost_status => array('publish','private','draft','pending','future')ordered bymodifiedwithRECENT_LIMITrows, and emits a Play link wheneverQuizAccess::can_read()passes. - measured symptom: the bench holds 109
draftquizzes titledAutomatischer EntwurfwithLENGTH(post_content) = 0, and they dominateorderby=modified, so all 5 dashboard Play links resolved to content-less drafts and zero published quiz was reachable from the dashboard.?qwizz_play=10989returns 200 with a correct<title>andpostid-10989body class but no player at all,NO CARD, on a white page. - correction 2026-08-27: that symptom line conflates two populations. Re-measured, the bench held 109 drafts in TOTAL, of which only 15 carried zero questions, and only 5 of those 15 were titled
Automatischer Entwurf. The dashboard symptom itself is unaffected — zero-question drafts did dominateorderby=modifiedand all 5 slots were dead — but the count 109 never described the zero-question population. All 15 zero-question rows were deleted on 2026-08-27; see finding 4 on the playable-quiz repair record. - closed 2026-08-26:
recent_quizzes()now requiresqw_question_count > 0in both the query and the Play CTA, so no dashboard slot offers a dead Play link — the playable-quiz repair record.
- root cause:
- The Play route renders an empty document for a content-less quiz instead of the
QuizEmbed::unavailable()contract.- root cause: the route’s
do_blocks()call inincludes/Frontend/QuizQueryViewer.php; no line number was recorded for this finding.do_blocks()on emptypost_contentemits nothing, so the route bypasses the unavailable card entirely. - measured symptom: after this cluster the blank page at least carries a correct title and a singular body class, but it is still a dead end.
- closed 2026-08-26: the route now precomputes the block render and substitutes the
QuizEmbed::unavailable()card when it is empty — the playable-quiz repair record.
- root cause: the route’s
- The route’s not-found branch is a soft-404.
- root cause:
status_header( 404 ); return;inincludes/Frontend/QuizQueryViewer.phpfalls through to the theme; no line number was recorded for this finding. - measured symptom: an inaccessible quiz id serves the full site home page, 121 KB, under a 404 status instead of a not-found surface.
- closed 2026-08-27 by Cluster 5: the branch is now
QuizQueryViewer::emit_404()(:64-82), called from the not-a-quiz guard (:21) and from the access-denied branch (:30). It clears$wp_query, callsset_404(), sends the status and renders the theme’s 404 template. All three not-found conditions now serve 404 at 55,655 bytes rather than 121,285, and two of the three had also been answering HTTP 200 rather than 404 — the 2026-08-27 not-found and token-scope record.
- root cause:
- Doc drift created by this cluster’s Wave 7 addition, listed for the owners.
- root cause: the wave-count and current-result statements at
AGENTS.md:82,.omp/skills/quizwizz-probe-harness/SKILL.mdlines 10, 26, 27, 48 and 49,.omp/agents/probe-runner.mdlines 21 and 54,.omp/commands/probe.md:10,q5vault/audits/qwizz/index.md:19, and the current-result statement inq5vault/audits/qwizz/probes.md. - measured symptom: each names Wave 1–6 or the
1..46result, which the standing gate no longer reports. The two q5vault entries in that list are corrected in the same change set as this page; theAGENTS.mdand.omp/**entries lie outside this page’s file scope and belong to their owners. - status 2026-08-27, verified by reading the cited files today: every site in this finding is now corrected.
AGENTS.md:82,.omp/commands/probe.md:10and.omp/agents/probe-runner.md:21,54name--wave 1|2|3|4|5|6|7|8|9|10|alland the1..76standing gate;.omp/skills/quizwizz-probe-harness/SKILL.md:10,26-27,48-49names waves 1-10 and1..76, the:10site having been the last to be fixed because it wrote the range with an ASCII hyphen (Waves 1-7) and so escaped an en-dash sweep. None of those files is in this page’s file scope; the corrections belong to their owners.
- root cause: the wave-count and current-result statements at
Release boundary
Every plugin change here is releaseEligible: false. The bench runtime package is a development artifact, not the canonical release repository: no vendor/, build/, release ZIP, provenance output, QUIZWIZZ_VERSION, or db_data/ was modified, and no version bump, build, or release happened. The release owner applies these source changes through the locked pipeline in BUILDING.md, using the patch and the two digest sidecars above. The harness contract this cluster extended is recorded in the probe harness page.