Files
booth/tests/mutations/antislop.toml
T
vh d4f64fd7ec fix(as-S5a): every control named, one h1 and a skip link, rings and hit areas
The markup and CSS half of the anti-slop interaction work. The in-place
client is untouched (that is S5b).
- Glyph-only controls carry a name: the withdraw ×s, downloads, open full
  page, the viewers' ✕, the board's pin, copy and remove, the bench's
  remove, the 1:1 toggle ("1:1, natural pixels"). Film-strip and tray
  frames carry the file's name as sr-only text instead of reading "01".
  A Desk row's wipe names its booth.
- Fields are named by aria-label, not by their placeholder.
- The inline ask's options are a radiogroup labelled by the prompt; a
  single-question fieldset gets an sr-only legend; a titled ask's title
  takes bk-ask-<id>-title (it duplicated the question's id).
- One h1 per page (sr-only on the Desk, review and compare), a skip link
  to <main id="main">, theme-color for light and dark.
- The review tape is one picture (role=img); its segments leave the tab
  order (the film strip holds the same links, named).
- Wipe now uses the Desk's delegated prompt, moved to base.html: it names
  the booth and asks the kept-booth question for a kept booth.
- Embed focus rings of its own; rings drawn inside clipping containers;
  the withdraw × at least 24px, 44px under a coarse pointer;
  touch-action:manipulation; strips contain their overscroll; a long
  slug wraps on a phone.
- A truncated why carries its full text in title; a countdown of 48h or
  more reads in days.
Two r2_flow.toml rows for the confirm helper now name base.html, where
the helper moved (anchors unchanged; the gate found them drifted).

Contract: as_antislop S5a. Falsifiers: antislop.toml 86/86 proved (S1-S6, S5a);
all 12 tables 366/366 proved on this tree.
2026-09-28 13:30:31 -07:00

646 lines
28 KiB
TOML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# The anti-slop fix slices: every falsifier the contract claims
# (docs/contracts/as_antislop.contract.md), and the change each forbids.
unit = "anti-slop fix slices (the house clock, and the slices after it)"
# ---- S1: the house clock
[[mutation]]
label = "S1 clock renders HH:MM"
file = "booth/app.py"
test = "tests/test_antislop.py::test_clock_forms"
old = ''' hhmm = time.strftime("%H%M", time.localtime(epoch))'''
new = ''' hhmm = time.strftime("%H:%M", time.localtime(epoch))'''
[[mutation]]
label = "S1 clock raises on a stamp it cannot read"
file = "booth/app.py"
test = "tests/test_antislop.py::test_clock_never_raises"
old = ''' if epoch is None:
return value if isinstance(value, str) else ""'''
new = ''' if epoch is None:
raise ValueError(value)'''
[[mutation]]
label = "S1 byline passes an address through"
file = "booth/app.py"
test = "tests/test_antislop.py::test_byline_hides_addresses"
old = ''' return "" if _is_address(bare) else who'''
new = ''' return who'''
[[mutation]]
label = "S1 the date tooltips keep HH:MM"
file = "booth/app.py"
test = "tests/test_antislop.py::test_date_stamp_is_house_form"
old = ''' return time.strftime("%Y-%m-%d %H%M", time.localtime(epoch))'''
new = ''' return time.strftime("%Y-%m-%d %H:%M", time.localtime(epoch))'''
[[mutation]]
label = "S1 a pick's answer line shows the raw stamp"
file = "booth/templates/_marks.html"
test = "tests/test_antislop.py::test_rendered_marks_use_the_house_clock"
old = '''{{ a.answer.answered_at|clock }}'''
new = '''{{ a.answer.answered_at }}'''
[[mutation]]
label = "S1 a memo line shows the raw stamp"
file = "booth/templates/_marks.html"
test = "tests/test_antislop.py::test_rendered_marks_use_the_house_clock"
old = '''{{ a.created|clock }}'''
new = '''{{ a.created }}'''
[[mutation]]
label = "S1 a memo line shows the poster's address"
file = "booth/templates/_marks.html"
test = "tests/test_antislop.py::test_rendered_marks_use_the_house_clock"
old = '''{% if a.by|byline %} · {{ a.by|byline }}{% endif %}'''
new = '''{% if a.by %} · {{ a.by }}{% endif %}'''
[[mutation]]
label = "S1 an answer line shows the answerer's address"
file = "booth/templates/_marks.html"
test = "tests/test_antislop.py::test_rendered_marks_use_the_house_clock"
old = '''{% if a.answer.answered_by|byline %} · {{ a.answer.answered_by|byline }}{% endif %}'''
new = '''{% if a.answer.answered_by %} · {{ a.answer.answered_by }}{% endif %}'''
[[mutation]]
label = "S1 the inline ask's state tag shows the raw stamp"
file = "booth/templates/_ask_inline.html"
test = "tests/test_antislop.py::test_embed_fragment_uses_the_house_clock"
old = '''✓ answered <time datetime="{{ a.answer.answered_at }}">{{ a.answer.answered_at|clock }}</time>'''
new = '''✓ answered {{ a.answer.answered_at }}'''
[[mutation]]
label = "S1 a board row shows HH:MM"
file = "booth/templates/booth.html"
test = "tests/test_antislop.py::test_board_rows_use_the_house_clock"
old = '''{{ e.when|clock }}'''
new = '''{{ e.when }}'''
# ---- S1, folded from the heid bug-hunt (thread 01M3MGPFKWBX0SJK5HFE0P3AFM)
[[mutation]]
label = "S1 clock raises OverflowError on an int past float range (Q1)"
file = "booth/app.py"
test = "tests/test_antislop.py::test_clock_never_raises"
old = ''' try:
f = float(value)
except (OverflowError, ValueError):
return None
return f if math.isfinite(f) else None'''
new = ''' return float(value) if math.isfinite(value) else None'''
[[mutation]]
label = "S1 a date-only stamp prints an invented 0000 (Q8)"
file = "booth/app.py"
test = "tests/test_antislop.py::test_clock_forms"
old = ''' if isinstance(value, str) and ":" not in value:'''
new = ''' if False:'''
[[mutation]]
label = "S1 an address with a port prints (Q7)"
file = "booth/app.py"
test = "tests/test_antislop.py::test_byline_hides_addresses"
old = ''' if s.count(":") == 1:
candidates.add(s.split(":", 1)[0])'''
new = ''''''
[[mutation]]
label = "S1 a zero-width character disguises an address (Q7)"
file = "booth/app.py"
test = "tests/test_antislop.py::test_byline_hides_addresses"
old = ''' bare = "".join(ch for ch in who if unicodedata.category(ch) not in ("Cf", "Cc")).strip()'''
new = ''' bare = who.strip()'''
[[mutation]]
label = "S1 a board row's author is not bylined (Q5)"
file = "booth/templates/booth.html"
test = "tests/test_antislop.py::test_board_rows_use_the_house_clock"
old = '''{% if e.who|byline %}<span class="board-who">{{ e.who|byline }}</span>{% endif %}'''
new = '''{% if e.who %}<span class="board-who">{{ e.who }}</span>{% endif %}'''
# ---- S2: legibility
[[mutation]]
label = "S2 the review arrows' chip lets a light stage through (60%)"
file = "booth/templates/view.html"
test = "tests/test_antislop.py::test_review_arrows_hold_over_a_white_stage"
old = '''background:oklch(0.17 0.01 250 / .82);'''
new = '''background:oklch(0.17 0.01 250 / .6);'''
[[mutation]]
label = "S2 the filmstrip numbers back at 9.5px"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_film_numbers_meet_the_label_floor"
old = ''' .film-ord{position:absolute;left:3px;top:3px;padding:1px 4px;border-radius:var(--radius-sm);
font:600 var(--size-micro)/1.2 var(--font-mono);background:oklch(0.17 0.01 250 / .85);'''
new = ''' .film-ord{position:absolute;left:3px;top:3px;padding:1px 4px;border-radius:var(--radius-sm);
font:600 9.5px/1.2 var(--font-mono);background:oklch(0.17 0.01 250 / .85);'''
[[mutation]]
label = "S2 the marks' state stamp back at 10.5px"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_mark_state_meets_the_label_floor"
old = ''' .mark-state{font-family:var(--font-mono);font-size:var(--size-micro);'''
new = ''' .mark-state{font-family:var(--font-mono);font-size:10.5px;'''
[[mutation]]
label = "S2 the Desk's section rules back at the label size"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_hint_lines_meet_the_sentence_floor"
old = ''' .desk-rule{font-size:var(--size-caption);font-weight:400;'''
new = ''' .desk-rule{font-weight:400;'''
[[mutation]]
label = "S2 the board note back at 11px"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_hint_lines_meet_the_sentence_floor"
old = ''' .board-note{font-family:var(--font-mono);font-size:var(--size-caption);'''
new = ''' .board-note{font-family:var(--font-mono);font-size:var(--size-micro);'''
[[mutation]]
label = "S2 the bench note back at 11px"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_hint_lines_meet_the_sentence_floor"
old = ''' .bench-note{font-family:var(--font-mono);font-size:var(--size-caption);'''
new = ''' .bench-note{font-family:var(--font-mono);font-size:var(--size-micro);'''
[[mutation]]
label = "S2 retired benches faded again"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_retired_benches_are_not_faded"
old = ''' .bench-row.is-retired .bench-link,.bench-row.is-retired .bench-url{color:var(--text-muted)}'''
new = ''' .bench-row.is-retired{opacity:.5}'''
[[mutation]]
label = "S2 an answered ask's option details faded again"
file = "booth/static/embed.js"
test = "tests/test_antislop.py::test_embed_fades_nothing"
old = ''' ".bk-ask-det{font-size:.82rem}",'''
new = ''' ".bk-ask-det{font-size:.82rem;opacity:.68}",'''
[[mutation]]
label = "S2 the recorded line faded again"
file = "booth/static/embed.js"
test = "tests/test_antislop.py::test_embed_fades_nothing"
old = ''' ".bk-ask-was{margin:.15rem 0 .55rem;font-size:.86rem}",'''
new = ''' ".bk-ask-was{margin:.15rem 0 .55rem;font-size:.86rem;opacity:.8}",'''
[[mutation]]
label = "S2 the notes placeholder back to the browser's grey"
file = "booth/static/embed.js"
test = "tests/test_antislop.py::test_embed_fades_nothing"
old = ''' ".bk-ask-notes::placeholder{color:inherit;opacity:.75}",'''
new = ''''''
[[mutation]]
label = "S2 the inline ask's state tag back at 10.5px"
file = "booth/static/embed.js"
test = "tests/test_antislop.py::test_the_ask_tag_meets_the_label_floor"
old = """ "font:600 11px/1.3 'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;","""
new = """ "font:600 10.5px/1.3 'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;","""
# ---- S2, folded from the heid bug-hunt guard grid: what the COMPUTED style shows
[[mutation]]
label = "S2 the ask title fades (Q9)"
file = "booth/static/embed.js"
test = "tests/test_antislop_s2_browser.py::test_nothing_fades"
old = """ "letter-spacing:.12em;text-transform:uppercase}","""
new = """ "letter-spacing:.12em;text-transform:uppercase;opacity:.62}","""
[[mutation]]
label = "S2 a later rule shrinks the film numbers (cascade, not shorthand)"
file = "booth/templates/base.html"
test = "tests/test_antislop_s2_browser.py::test_labels_and_sentences_render_at_their_floors"
old = """ .film-ord{position:absolute;left:3px;top:3px;padding:1px 4px;border-radius:var(--radius-sm);
font:600 var(--size-micro)/1.2 var(--font-mono);background:oklch(0.17 0.01 250 / .85);color:oklch(0.91 0.008 216)}"""
new = """ .film-ord{position:absolute;left:3px;top:3px;padding:1px 4px;border-radius:var(--radius-sm);
font:600 var(--size-micro)/1.2 var(--font-mono);background:oklch(0.17 0.01 250 / .85);color:oklch(0.91 0.008 216);font-size:1px}"""
[[mutation]]
label = "S2 the details go transparent without any opacity"
file = "booth/static/embed.js"
test = "tests/test_antislop_s2_browser.py::test_nothing_fades"
old = """ ".bk-ask-det{font-size:.82rem}","""
new = """ ".bk-ask-det{font-size:.82rem;color:transparent}","""
[[mutation]]
label = "S2 the placeholder vanishes (opacity 0) while keeping color:inherit"
file = "booth/static/embed.js"
test = "tests/test_antislop_s2_browser.py::test_nothing_fades"
old = """ ".bk-ask-notes::placeholder{color:inherit;opacity:.75}","""
new = """ ".bk-ask-notes::placeholder{color:inherit;opacity:0}","""
[[mutation]]
label = "S2 retired benches greyed by a filter instead of opacity"
file = "booth/templates/base.html"
test = "tests/test_antislop_s2_browser.py::test_nothing_fades"
old = """ .bench-row.is-retired .bench-link,.bench-row.is-retired .bench-url{color:var(--text-muted)}"""
new = """ .bench-row.is-retired .bench-link,.bench-row.is-retired .bench-url{color:var(--text-muted)}
.bench-row.is-retired{filter:grayscale(.6) opacity(.5)}"""
[[mutation]]
label = "S2 the flagged tray's number back at 10px"
file = "booth/templates/base.html"
test = "tests/test_antislop_s2_browser.py::test_labels_and_sentences_render_at_their_floors"
old = ''' .tray-ord{position:absolute;left:3px;bottom:3px;padding:1px 4px;border-radius:var(--radius-sm);
font:600 var(--size-micro)/1.2 var(--font-mono);'''
new = ''' .tray-ord{position:absolute;left:3px;bottom:3px;padding:1px 4px;border-radius:var(--radius-sm);
font:600 10px/1.2 var(--font-mono);'''
[[mutation]]
label = "S2 the tile's flagged stamp back at 10.5px"
file = "booth/templates/base.html"
test = "tests/test_antislop_s2_browser.py::test_labels_and_sentences_render_at_their_floors"
old = ''' font:600 var(--size-micro)/1.3 var(--font-mono);letter-spacing:var(--tracking-caps);text-transform:uppercase;'''
new = ''' font:600 10.5px/1.3 var(--font-mono);letter-spacing:var(--tracking-caps);text-transform:uppercase;'''
[[mutation]]
label = "S2 compare's A/B badge back at 9.5px"
file = "booth/templates/base.html"
test = "tests/test_antislop_s2_browser.py::test_labels_and_sentences_render_at_their_floors"
old = ''' font:700 var(--size-micro)/1.2 var(--font-mono);background:var(--accent);color:var(--accent-contrast)}'''
new = ''' font:700 9.5px/1.2 var(--font-mono);background:var(--accent);color:var(--accent-contrast)}'''
# ---- S3: phone layouts (measured in a real browser at 390x844)
[[mutation]]
label = "S3 bench rows squeeze instead of wrapping"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_bench_rows_do_not_overprint_on_a_phone"
old = ''' .bench-row{flex-wrap:wrap;row-gap:4px}'''
new = ''' .bench-row{row-gap:4px}'''
[[mutation]]
label = "S3 the board head's phone rule gone (the count wraps)"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_board_head_stays_compact_on_a_phone"
old = ''' @media (max-width:600px){.board-head{flex-wrap:wrap}.board-title{white-space:nowrap}'''
new = ''' @media (max-width:1px){.board-head{flex-wrap:wrap}.board-title{white-space:nowrap}'''
[[mutation]]
label = "S3 the doc bar squeezes the name again"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_doc_name_keeps_a_readable_line_on_a_phone"
old = ''' @media (max-width:600px){.doc-inline > .doc-bar{flex-wrap:wrap;row-gap:4px}'''
new = ''' @media (max-width:1px){.doc-inline > .doc-bar{flex-wrap:wrap;row-gap:4px}'''
[[mutation]]
label = "S3 a file tile's link starts under the number again"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_file_tile_number_clears_the_download_link"
old = ''' .item > .ord ~ .dl{padding-top:44px}'''
new = ''''''
[[mutation]]
label = "S3 the review keeps its tagline on a phone"
file = "booth/templates/view.html"
test = "tests/test_antislop_browser.py::test_review_header_is_one_line_on_a_phone"
old = '''{% block body_attrs %} class="page-stage"{% endblock %}'''
new = ''''''
[[mutation]]
label = "S3 the tagline hidden on every page (the Desk's negative control)"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_review_header_is_one_line_on_a_phone"
old = ''' @media (max-width:600px){body.page-stage .topbar .tagline{display:none}}'''
new = ''' @media (max-width:600px){body .topbar .tagline{display:none}}'''
[[mutation]]
label = "S3 the benches head stacks its count again"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_board_head_stays_compact_on_a_phone"
old = """ .bench-head{flex-wrap:wrap}.bench-title{white-space:nowrap}.bench-note{flex:1 1 100%}}"""
new = """ }"""
# ---- S4: reading measure
[[mutation]]
label = "S4 prose runs the full width again"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_doc_prose_reads_at_a_book_measure"
old = """ .markdown-body > :is(p,ul,ol,blockquote,dl,h1,h2,h3,h4,h5,h6){max-width:72ch}"""
new = """"""
[[mutation]]
label = "S4 h3 flat against its body again (1.08em)"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_doc_headings_step_by_size"
old = """.markdown-body h3{font-size:1.2em;font-weight:600}"""
new = """.markdown-body h3{font-size:1.08em;font-weight:600}"""
# ---- S6: the operator's rulings
[[mutation]]
label = "S6 the old tagline copy"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_the_tagline_is_a_sentence"
old = """ <span class="tagline">held for review · wipes in {{ ttl_hours }}h unless kept</span>"""
new = """ <span class="tagline">ephemeral media · auto-wipes {{ ttl_hours }}h · kept boards don't</span>"""
[[mutation]]
label = "S6 the tagline in tracked capitals again"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_the_tagline_is_a_sentence"
old = """ .tagline{font-family:var(--font-mono);font-size:var(--size-caption);font-weight:500;color:var(--text-muted)}"""
new = """ .tagline{font-family:var(--font-mono);font-size:var(--size-caption);font-weight:500;text-transform:uppercase;color:var(--text-muted)}"""
[[mutation]]
label = "S6 the needs-you side stripe back"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_needs_you_rows_carry_no_side_stripe"
old = """ .desk-row.is-needs{box-shadow:var(--shadow-sm)}"""
new = """ .desk-row.is-needs{box-shadow:inset 3px 0 0 var(--warning),var(--shadow-sm)}"""
[[mutation]]
label = "S6 the brand dot glows again"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_the_brand_dot_is_matte"
old = """ .brand .dot{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--accent)}"""
new = """ .brand .dot{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--accent);box-shadow:var(--glow-armed)}"""
[[mutation]]
label = "S6 Wipe now paints the stripe on its own background again"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_the_hazard_stripe_is_a_pseudo_element"
old = """ position:relative;overflow:hidden;background:transparent;
border:1px solid var(--danger);color:var(--danger-text);-webkit-backdrop-filter:none;backdrop-filter:none}"""
new = """ position:relative;overflow:hidden;background:var(--hazard-stripe) top / 100% 3px no-repeat,transparent;
border:1px solid var(--danger);color:var(--danger-text);-webkit-backdrop-filter:none;backdrop-filter:none}"""
[[mutation]]
label = "S6 the stripe layer lost (no ::before)"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_the_hazard_stripe_is_a_pseudo_element"
old = """ .wipe-lg button::before,.board-del-sel:not(:disabled)::before{content:"";position:absolute;"""
new = """ .wipe-lg button::after,.board-del-sel:not(:disabled)::after{content:"";position:absolute;"""
# ---- S5a: accessibility plumbing (names, landmarks, focus, hit areas)
[[mutation]]
label = "S5a the withdraw-note × unnamed again"
file = "booth/templates/_marks.html"
test = "tests/test_antislop.py::test_every_control_has_a_word_for_a_name"
old = """<button type="submit" class="mark-x" title="withdraw this note" aria-label="withdraw this note">×</button>"""
new = """<button type="submit" class="mark-x" title="withdraw this note">×</button>"""
[[mutation]]
label = "S5a a film frame named only by its number"
file = "booth/templates/view.html"
test = "tests/test_antislop.py::test_every_control_has_a_word_for_a_name"
old = """ <span class="sr-only">{{ x.name }}</span>{%- if x.kind == 'image' %}<img loading="lazy" decoding="async" src="{{ x.thumb or x.url }}" alt="">{% else %}<span class="film-kind">"""
new = """ {%- if x.kind == 'image' %}<img loading="lazy" decoding="async" src="{{ x.thumb or x.url }}" alt="">{% else %}<span class="film-kind">"""
[[mutation]]
label = "S5a a flagged-tray item named only by its number"
file = "booth/templates/_marks.html"
test = "tests/test_antislop.py::test_every_control_has_a_word_for_a_name"
old = """<span class="sr-only">{{ it.name }}</span>{%- if it.kind"""
new = """{%- if it.kind"""
[[mutation]]
label = "S5a the 1:1 button read as digits"
file = "booth/templates/view.html"
test = "tests/test_antislop.py::test_every_control_has_a_word_for_a_name"
old = """ aria-label="1:1, natural pixels" title="natural pixels — drag to pan a large picture">"""
new = """ title="natural pixels — drag to pan a large picture">"""
[[mutation]]
label = "S5a the doc's open-full-page glyph unnamed"
file = "booth/templates/booth.html"
test = "tests/test_antislop.py::test_every_control_has_a_word_for_a_name"
old = """ title="open full page" aria-label="open {{ it.name }} full page">⤢</a>"""
new = """ title="open full page">⤢</a>"""
[[mutation]]
label = "S5a compare's close glyph unnamed"
file = "booth/templates/compare.html"
test = "tests/test_antislop.py::test_every_control_has_a_word_for_a_name"
old = """ title="back to the review of A (Esc)" aria-label="back to the review of A">✕</a>"""
new = """ title="back to the review of A (Esc)">✕</a>"""
[[mutation]]
label = "S5a the board's pin star unnamed"
file = "booth/templates/booth.html"
test = "tests/test_antislop.py::test_every_control_has_a_word_for_a_name"
old = """ aria-pressed="{{ 'true' if e.pinned else 'false' }}" aria-label="pin {{ e.desc }}""""
new = """ aria-pressed="{{ 'true' if e.pinned else 'false' }}""""
[[mutation]]
label = "S5a the board's remove × unnamed"
file = "booth/templates/booth.html"
test = "tests/test_antislop.py::test_every_control_has_a_word_for_a_name"
old = """ title="remove this link" aria-label="remove the link {{ e.desc }}""""
new = """ title="remove this link""""
[[mutation]]
label = "S5a a Desk wipe that does not say which booth"
file = "booth/templates/index.html"
test = "tests/test_antislop.py::test_desk_row_controls_name_their_booth"
old = """aria-label="wipe the booth {{ b.name }}""""
new = """aria-label="wipe booth""""
[[mutation]]
label = "S5a the note field named only by its placeholder"
file = "booth/templates/_marks.html"
test = "tests/test_antislop.py::test_fields_are_named"
old = """<textarea name="text" rows="2" aria-label="a note on this booth, for the session that posted it" placeholder"""
new = """<textarea name="text" rows="2" placeholder"""
[[mutation]]
label = "S5a the bench URL field unnamed"
file = "booth/templates/booth.html"
test = "tests/test_antislop.py::test_fields_are_named"
old = """<input type="url" name="url" aria-label="https://host:port/" placeholder"""
new = """<input type="url" name="url" placeholder"""
[[mutation]]
label = "S5a the embed's notes field unnamed"
file = "booth/templates/_ask_inline.html"
test = "tests/test_antislop.py::test_fields_are_named"
old = """ aria-label="{{ a.notes_label }}" placeholder="{{ a.notes_label }} (optional)">"""
new = """ placeholder="{{ a.notes_label }} (optional)">"""
[[mutation]]
label = "S5a the inline ask's options not a named group"
file = "booth/templates/_ask_inline.html"
test = "tests/test_antislop.py::test_radio_groups_are_named_and_ids_are_unique"
old = """<div class="bk-ask-opts" role="radiogroup" aria-labelledby="""
new = """<div class="bk-ask-opts" aria-labelledby="""
[[mutation]]
label = "S5a the group names a prompt id that is not there"
file = "booth/templates/_ask_inline.html"
test = "tests/test_antislop.py::test_radio_groups_are_named_and_ids_are_unique"
old = """<p class="bk-ask-prompt" id="bk-ask-{{ a.id }}{% if q.key %}-{{ q.key }}{% endif %}-prompt">"""
new = """<p class="bk-ask-prompt">"""
[[mutation]]
label = "S5a a titled ask's title reuses the question's id"
file = "booth/templates/_ask_inline.html"
test = "tests/test_antislop.py::test_radio_groups_are_named_and_ids_are_unique"
old = """<p class="bk-ask-title" id="bk-ask-{{ a.id }}-title">"""
new = """<p class="bk-ask-title" id="bk-ask-{{ a.id }}">"""
[[mutation]]
label = "S5a a single-question fieldset without a legend"
file = "booth/templates/_marks.html"
test = "tests/test_antislop.py::test_radio_groups_are_named_and_ids_are_unique"
old = """{% else %}<legend class="sr-only">{{ q.prompt }}</legend>{% endif %}"""
new = """{% endif %}"""
[[mutation]]
label = "S5a the Desk without an h1"
file = "booth/templates/index.html"
test = "tests/test_antislop.py::test_every_page_has_one_h1_and_a_skip_link"
old = """<h1 class="sr-only">Desk</h1>
"""
new = """"""
[[mutation]]
label = "S5a the review without an h1"
file = "booth/templates/view.html"
test = "tests/test_antislop.py::test_every_page_has_one_h1_and_a_skip_link"
old = """<h1 class="sr-only">{{ file }}</h1>
"""
new = """"""
[[mutation]]
label = "S5a compare without an h1"
file = "booth/templates/compare.html"
test = "tests/test_antislop.py::test_every_page_has_one_h1_and_a_skip_link"
old = """<h1 class="sr-only">compare {{ sides.a.rel }} and {{ sides.b.rel }}</h1>
"""
new = """"""
[[mutation]]
label = "S5a no skip link"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_every_page_has_one_h1_and_a_skip_link"
old = """<a class="skip-link" href="#main">Skip to content</a>
"""
new = """"""
[[mutation]]
label = "S5a the skip link points at nothing"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_every_page_has_one_h1_and_a_skip_link"
old = """<main id="main">"""
new = """<main>"""
[[mutation]]
label = "S5a no dark theme-color"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_theme_color_for_both_schemes"
old = """<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#15191d">
"""
new = """"""
[[mutation]]
label = "S5a the tape is a list of links again"
file = "booth/templates/view.html"
test = "tests/test_antislop.py::test_the_tape_is_one_picture"
old = """<div class="tape" data-region="tape" role="img" aria-label="""
new = """<div class="tape" data-region="tape" aria-label="""
[[mutation]]
label = "S5a the tape's segments back in the tab order"
file = "booth/templates/view.html"
test = "tests/test_antislop.py::test_the_tape_is_one_picture"
old = """ title="{{ num(x.ordinal) }} {{ x.name }}" tabindex="-1" aria-hidden="true"></a>"""
new = """ title="{{ num(x.ordinal) }} {{ x.name }}"></a>"""
[[mutation]]
label = "S5a Wipe now back on an inline anonymous prompt"
file = "booth/templates/booth.html"
test = "tests/test_antislop.py::test_wipe_now_asks_by_name"
old = """ data-booth="{{ name }}" data-confirm="{{ 'wipe-kept' if kept else 'wipe' }}">"""
new = """ onsubmit="return confirm('Wipe this booth now?')">"""
[[mutation]]
label = "S5a a kept booth's Wipe now asks the ordinary question"
file = "booth/templates/booth.html"
test = "tests/test_antislop_browser.py::test_wipe_now_asks_by_name_in_the_browser"
old = """data-confirm="{{ 'wipe-kept' if kept else 'wipe' }}""""
new = """data-confirm="wipe""""
[[mutation]]
label = "S5a the shared prompt does not reach a booth page"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_wipe_now_asks_by_name_in_the_browser"
old = """ var form = ev.target.closest ? ev.target.closest('form[data-confirm]') : null;"""
new = """ var form = ev.target.closest ? ev.target.closest('.desk form[data-confirm]') : null;"""
[[mutation]]
label = "S5a hours never roll up to days"
file = "booth/app.py"
test = "tests/test_antislop.py::test_human_dur_rolls_up_to_days"
old = """ if h >= 48:"""
new = """ if h >= 4800:"""
[[mutation]]
label = "S5a the embed's submit button has no ring of its own"
file = "booth/static/embed.js"
test = "tests/test_antislop.py::test_embed_chrome_draws_its_own_focus_rings"
old = """ ".bk-ask-go:focus-visible{outline:2px solid var(--bk-accent);outline-offset:2px}",
"""
new = """"""
[[mutation]]
label = "S5a an embed option has no ring"
file = "booth/static/embed.js"
test = "tests/test_antislop.py::test_embed_chrome_draws_its_own_focus_rings"
old = """ ".bk-ask-opt:has(input:focus-visible){outline:2px solid var(--bk-accent);outline-offset:1px}",
"""
new = """"""
[[mutation]]
label = "S5a rings drawn outside a clipping container"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_focus_rings_are_drawn_inside_clipping_containers"
old = """ .desk-panel a:focus-visible{outline-offset:-2px}"""
new = """ .desk-panel a:focus-visible{outline-offset:2px}"""
[[mutation]]
label = "S5a a truncated why without its full text"
file = "booth/templates/_provenance.html"
test = "tests/test_antislop.py::test_a_truncated_why_carries_its_full_text"
old = """<span class="prov-why" title="{{ m.why }}">"""
new = """<span class="prov-why">"""
[[mutation]]
label = "S5a the withdraw × at its old size on a desktop"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_withdraw_buttons_are_big_enough_to_hit"
old = """ .mark-x{min-width:24px;min-height:24px}"""
new = """ .mark-x{}"""
[[mutation]]
label = "S5a the withdraw × not enlarged under a finger"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_withdraw_buttons_are_big_enough_to_hit"
old = """ @media (pointer:coarse){.mark-x,.flagtoggle button"""
new = """ @media (pointer:coarse){.flagtoggle button"""
[[mutation]]
label = "S5a double-tap zoom delay back on controls"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_touch_and_scroll_behaviour"
old = """ a,button,summary,label,input,select,textarea{touch-action:manipulation}"""
new = """ summary,label,input,select,textarea{touch-action:manipulation}"""
[[mutation]]
label = "S5a the film strip hands its scroll to the page"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_touch_and_scroll_behaviour"
old = """ .film,.vcap,.doc-body,.cmp-strip{overscroll-behavior:contain}"""
new = """ .vcap,.doc-body,.cmp-strip{overscroll-behavior:contain}"""
[[mutation]]
label = "S5a a long slug runs off a phone"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_touch_and_scroll_behaviour"
old = """ @media (max-width:600px){.h1-slug{white-space:normal;overflow-wrap:anywhere}}"""
new = """ @media (max-width:600px){.h1-slug{}}"""