Skip to content

Add expert level to The Accessibility Nightmare - #94

Merged
sinduri-g merged 8 commits into
off-on-dev:mainfrom
sindurigf:feat/accessibility-nightmare-expert
Sep 23, 2026
Merged

sinduri-g merged 8 commits into
off-on-dev:mainfrom
sindurigf:feat/accessibility-nightmare-expert

Conversation

@sindurigf

Copy link
Copy Markdown
Contributor

The Compliance Engine asks why a gate that has been green for months never caught the barrier the company is being audited over. The answer is in two parts: it only ever scanned one of the four pages a customer passes through, and the fault it was meant to find is behavioural, so no amount of scanning would have reached it.

Players extend scanner coverage, discover that doing so finds nothing, diagnose the silence with a virtual screen reader, repair the routing, and produce a compliance report that records what each check proved.

Verification plants a fault on the payment page and requires the scan to go red, so coverage is measured rather than assumed — a suite that stays green never opened that page.

Adds check_playwright_tests_detect to lib/scripts/node.sh, which asserts a tagged suite fails. Purely additive; no existing caller changes.

Also removes the rewards block from the adventure index, as the adventure has no rewards.

What does this PR do?

Closes #

Type of PR

  • 💡 Adventure Idea
  • 🗺️ New Adventure Level
  • 📖 Solution Walkthrough
  • 🐛 Bug fix / 📝 Documentation improvement / Other

💡 Adventure Idea

  • Idea file is placed in ideas/ (not ideas/.implemented/)

🗺️ New Adventure Level

  • Devcontainer tested from scratch in a new Codespace
  • verify.sh passes when the challenge is solved
  • verify.sh fails with helpful error messages when not solved
  • Docs complete: story, objectives, hints, no spoilers
  • All links in docs work
  • Idea file moved to ideas/.implemented/ (first level PR only)
  • Tracking issue linked above (Part of # for earlier levels, Closes # for the last one)

📖 Solution Walkthrough

  • External link (blog post, video, etc.) — linked from the adventure's solutions page
  • In-repo markdown (adventures/XX-.../docs/solutions/level.md)
  • The challenge deadline has passed
  • Walkthrough explains why things work, not just what to do

📝 Other

  • Commits are focused and minimal
  • Documentation updated if needed

sindurigf and others added 8 commits September 19, 2026 21:43
The Compliance Engine asks why a gate that has been green for months
never caught the barrier the company is being audited over. The answer
is in two parts: it only ever scanned one of the four pages a customer
passes through, and the fault it was meant to find is behavioural, so
no amount of scanning would have reached it.

Players extend scanner coverage, discover that doing so finds nothing,
diagnose the silence with a virtual screen reader, repair the routing,
and produce a compliance report that records what each check proved.

Verification plants a fault on the payment page and requires the scan
to go red, so coverage is measured rather than assumed — a suite that
stays green never opened that page.

Adds check_playwright_tests_detect to lib/scripts/node.sh, which asserts
a tagged suite fails. Purely additive; no existing caller changes.

Also removes the rewards block from the adventure index, as the
adventure has no rewards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: sindurigf <sinduri.g@gmail.com>
Check 3 plants a fault in the payment page and requires the player's
scan to catch it. Three ways that could go wrong:

The recovery that undoes a crashed probe lived inside the branch taken
only when the scan was already green, so a run killed mid-probe left the
planted fault in place and then failed check 2 — for exactly the player
who had widened their scan to reach that page. Recovery now runs before
any check, and self-heals.

The trap was armed before the backup was known good, so a short write
could leave a truncated backup that the handler then moved over the
original. The backup is now compared byte for byte before it is relied
on, and the handler chains loader.sh's own cleanup rather than replacing
it.

The inverted check treated any non-zero exit as "fault caught", so
Ctrl-C scored a pass and the run carried on. Only exit code 1, a real
test failure, counts now; anything else reports that the check could not
be measured.

Also: drop the python3 dependency the prerequisite check never declared,
resolve the report path against the vite server root rather than cwd,
read test outcomes after retries rather than from the first attempt,
ignore the probe backup, add set -e to the devcontainer scripts to match
the sibling levels, and stop pointing players at a localhost URL that
does not resolve in a Codespace.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: sindurigf <sinduri.g@gmail.com>
A cold read of the docs and the reference page found the defect
identifiable before the player ran anything, which leaves objective 2
with nothing to find.

The reference table paired "the document has a title" with "route
change announces the new page" as two halves of one criterion, and
highlighted both. That was the sharpest statement of the lesson and also
the answer, printed before any investigation. The row is now the fault
class rather than this app's instance of it, and the highlight is gone.

The WCAG links named the two criteria outright; they now point at the
guideline and at the quick reference, so the player still has somewhere
to look but has to work out which criterion their tests verify.

Two smaller tells removed: the listen-panel instructions said to read
what does not arrive after the marker, and the @transition hint said to
watch what arrives after the address changes. Both named the conclusion.
They now describe how to read the panel and leave the judgement open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: sindurigf <sinduri.g@gmail.com>
The route marker was printed after awaiting a drain. That await yields
the event loop, React commits its re-render, and the new page's
announcement reaches the log in time to be flushed above the marker it
belongs below — so the panel showed the arrival before the navigation
that caused it.

The marker is now queued on hashchange and printed at the end of the
drain that call kicks off. React commits after the handler returns, so
that drain still reads a log ending at the previous page: phrases spoken
before the navigation land above the marker, the arrival lands below it.

Overlapping drains are also blocked. Two calls could both slice from the
same index and print the same phrases twice, which the 200ms poll made
likely during navigation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: sindurigf <sinduri.g@gmail.com>
The level could be passed by announcing a fixed string. That satisfies a
naive "something was announced" assertion and is broken twice over: it
never tells the customer which page they reached, and because the text
never changes there is no mutation for the browser to announce, so it
goes silent from the second navigation onward. Announcing assertively
passed too, though it interrupts whatever the user was listening to.

A new check drives the repaired storefront directly rather than trusting
the player's own tests, which prove only what the player chose to
assert. It moves between two pages in a row and requires each arrival to
be announced, the two to differ, and neither to interrupt.

Verified to discriminate: unfixed fails, a fixed string fails, an
assertive live region fails, naming the destination politely passes.

An over-announcing check was tried first and dropped. React bails out
when the text is unchanged, so a missing dependency array produced no
extra announcement and the test would have passed for everyone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: sindurigf <sinduri.g@gmail.com>
A tone pass against beginner and intermediate, which set the house
voice. Removed the consultancy phrasing ("which tool bridges the gap"),
the unearned adjective on axe-core, the benefit claim about a report
that "stands up to audit", and two lines that graded the player's work
before they had done any.

Cut "that is the lesson, not a bug", which congratulated the challenge
on its own twist and pre-empted the one thing the player is meant to
notice for themselves. Deleted a duplicated instruction to assert the
outcome rather than the mechanism, which appeared twice within four
lines.

Added the storefront opener both siblings carry and this level was
missing: where the Ports tab is, that Codespaces serves from
.app.github.dev with no localhost to visit, and how to start the server
by hand. The level referenced that tab without ever introducing it.

Dropped the status-politeness binding from the coverage reference. No
doc, hint or spec ever asked for that annotation, so the row was
permanently unsatisfiable; politeness is enforced by the new check
instead. Also stopped one verify hint from restating the annotation type
the docs already give.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: sindurigf <sinduri.g@gmail.com>
The coverage hint opened with "Something is announced, but...", which is
false in the state every player meets first: nothing is announced at
all. It now describes what the two arrivals have to do, which reads
correctly whether the player has heard silence, a fixed string, or an
announcement that interrupts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: sindurigf <sinduri.g@gmail.com>
Checks 2 to 4 reach the storefront through playwright.config.js, which
starts the server if it is not already up. Check 5 drives the page
itself, so nothing starts it — and a dead port produced the hint about
an announcement that does not survive a second navigation, sending the
player to debug a fix that was never the problem.

It now probes the port first and says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: sindurigf <sinduri.g@gmail.com>

@KatharinaSick KatharinaSick left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!!! 😊

@sinduri-g
sinduri-g merged commit 07395d2 into off-on-dev:main Sep 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants