Files
booth/tests/mutations/r2_flow.toml
T
vh 436d234ca0 feat(desk): the Desk row, booth dates, and the theme toggle (r2b merge 2: D1 + D1b + D3)
Operator rulings, 2026-09-23.

D1, the Desk row:
- Kept vs ephemeral reads at a glance: an always-visible lifetime pill in
  the right column (sage ★ kept, amber held, ◷ counting down).
- The facts line is facts only.
- zip / keep|release / wipe are one cluster, with zip out of the middle.
  Where a real hover exists it floats over the preview strip (covering
  pictures, never information), appears on hover or keyboard focus, and
  takes no room. Anywhere else (touch, any coarse pointer) it is the
  row's last line, visible, with 32px controls. × hides too (the operator
  answered yes).
D1b: "created 12 Sep" (filesystem birth time; nothing when unknown) and
  "updated 5d ago" (the content clock), as <time> facts on the row and in
  the booth header, from one macro and one clock per page.
D3, the theme toggle: System · Light · Dark in the top bar.
- Stored in localStorage and applied in <head> before any stylesheet.
- System removes data-theme, so the OS query follows the OS live, with
  no listener.
- The token sheet is re-vendored at the same SVOS SHA with a scoping-only
  transform (155 declarations, the same set, both directions), so forced
  themes win over the OS and high contrast follows the theme in effect.
- The ask chrome inside verbatim pages follows the choice through
  data-bk-theme on our own fragments, live across tabs. The host page's
  <html> is never touched.

Declared test changes:
- two row tests replaced;
- the wipe-dialog test hovers first;
- four r2_flow rows retired, with successors in r2b.toml (45/45).
785 passed.
2026-09-23 19:25:32 -07:00

213 lines
8.6 KiB
TOML

# R2 — the review flow: falsifiers the round claims, and the change each forbids.
#
# Every row was proved RED under its mutation in the session that wrote it,
# then committed here so the proof is an artifact rather than scrollback. The
# browser rows need the Playwright Chromium the browser tests already use.
#
# Deliberately ABSENT: single guards inside a defence in depth, each of which
# stays green when removed alone because another layer still holds — so a row
# for any one of them would be a vacuous proof, and this table's own first run
# said so. `.seen`'s O_NOFOLLOW, O_NONBLOCK and S_ISREG (the FIFO/symlink test
# covers them together); and `flagged_targets`' `error is None`, since
# hydration already strips the target from a damaged mark.
#
# RETIRED (r2b D1, 2026-09-23): four rows proving the facts-line row controls
# (visible at rest, compact, on the facts line) — the operator ruled those
# controls hover-revealed over the preview strip, and their tests were replaced
# as declared in r2b's contract. Their successors are in r2b.toml.
#
# The serialization row is only a falsifier because its test HOLDS the first
# refresh in the client: localhost alone never lost the race, and the first
# draft of that test stayed green with serialization deleted.
unit = "the Desk, the lightbox, the review, and the in-place client"
[[mutation]]
label = 'C1 ordinals count from 0, not 1'
file = "booth/items.py"
test = "tests/test_flow.py::test_a_filtered_tile_keeps_its_number_in_the_whole_set"
old = '''ordinal=len(items) + 1,'''
new = '''ordinal=len(items),'''
[[mutation]]
label = 'C2 .seen: a nested-too-deep marker escapes the never-raises read'
file = "booth/items.py"
test = "tests/test_flow.py::test_a_deeply_nested_seen_marker_reads_as_nothing_seen"
old = '''except (UnicodeDecodeError, ValueError, RecursionError):'''
new = '''except (UnicodeDecodeError, ValueError):'''
[[mutation]]
label = 'C3 a non-finite q is accepted as a q-value'
file = "booth/app.py"
test = "tests/test_flow.py::test_a_non_finite_q_is_malformed"
old = ''' raise ValueError("non-finite q")'''
new = ''' pass'''
[[mutation]]
label = 'C3 204 on an explicit JSON Accept becomes the 303'
file = "booth/app.py"
test = "tests/test_flow.py::test_an_explicit_json_accept_gets_204_and_the_write_still_lands"
old = ''' return Response(status_code=204)'''
new = ''' pass'''
[[mutation]]
label = 'C3 back=view lands on the review for a doc too (ring check dropped)'
file = "booth/app.py"
test = "tests/test_flow.py::test_back_view_lands_on_the_review_only_for_a_media_item"
old = ''' if f in ring:'''
new = ''' if True:'''
[[mutation]]
label = 'C4 the Desk counts orphan flags'
file = "booth/app.py"
test = "tests/test_flow.py::test_a_flag_on_a_file_that_is_gone_stays_visible_and_withdrawable"
old = '''"flags": len(flagged_targets(marks) & {it.rel for it in items}),'''
new = '''"flags": len(flagged_targets(marks)),'''
[[mutation]]
label = 'C4 landed_at follows symlinks'
file = "booth/app.py"
test = "tests/test_flow.py::test_the_content_clock_reads_the_booth_not_what_its_links_point_at"
old = ''' st = p.lstat()'''
new = ''' st = p.stat()'''
[[mutation]]
label = 'C4 one unreadable entry reads the whole booth as landed NOW'
file = "booth/app.py"
test = "tests/test_flow.py::test_one_unreadable_entry_costs_that_entry_not_the_booth"
old = '''pin it in "new" forever.
continue'''
new = '''pin it in "new" forever.
return time.time()'''
[[mutation]]
label = 'C4 a non-web bookmark URL becomes a link'
file = "booth/templates/index.html"
test = "tests/test_flow.py::test_the_desk_never_makes_a_non_web_url_clickable"
old = '''{% set web = e.url.lower().startswith(('http://', 'https://')) %}'''
new = '''{% set web = true %}'''
[[mutation]]
label = 'C4 a non-web bench URL becomes a link'
file = "booth/templates/index.html"
test = "tests/test_flow.py::test_the_desk_never_makes_a_non_web_url_clickable"
old = '''{% set web = b.url.lower().startswith(('http://', 'https://')) %}'''
new = '''{% set web = true %}'''
[[mutation]]
label = 'C5 audio/video tiles lose their review link'
file = "booth/templates/booth.html"
test = "tests/test_flow.py::test_a_sound_only_booth_can_open_the_review"
old = '''{% if it.kind in ('video', 'audio') %}<a class="rv-link"'''
new = '''{% if false %}<a class="rv-link"'''
[[mutation]]
label = 'C6 a NUL in ?f escapes as a 500'
file = "booth/app.py"
test = "tests/test_flow.py::test_a_nul_in_the_review_path_is_a_404_not_a_500"
old = ''' except (OSError, ValueError):
# ValueError: an embedded NUL.'''
new = ''' except OSError:
# ValueError: an embedded NUL.'''
[[mutation]]
label = 'C3 client: no busy guard (a double-click writes twice)'
file = "booth/templates/base.html"
test = "tests/test_flow_browser.py::test_an_unsaved_choice_survives_a_save_elsewhere_and_a_double_click_writes_once"
old = ''' if (form.__busy) return;
'''
new = ''''''
[[mutation]]
label = 'C3 client: an unsent radio is not carried across a swap'
file = "booth/templates/base.html"
test = "tests/test_flow_browser.py::test_an_unsaved_choice_survives_a_save_elsewhere_and_a_double_click_writes_once"
old = ''' if (el.checked !== el.defaultChecked) t.checked = el.checked;'''
new = ''''''
[[mutation]]
label = 'C3 client: saves are not serialized'
file = "booth/templates/base.html"
test = "tests/test_flow_browser.py::test_quick_successive_flags_all_show"
old = ''' queue = queue.then(function () { return run(form, data); })'''
new = ''' queue = run(form, data)'''
[[mutation]]
label = 'C3 the standalone marks page has no region'
file = "booth/templates/marks.html"
test = "tests/test_flow_browser.py::test_the_standalone_marks_page_updates_in_place"
old = '''<div class="marks-panel" data-region="marks-panel">'''
new = '''<div class="marks-panel">'''
[[mutation]]
label = "C6 the next arrow's rail offset applies at phone width"
file = "booth/templates/view.html"
test = "tests/test_flow_browser.py::test_the_next_arrow_clears_the_rail_only_beside_it"
old = ''' .vprev{left:0}.vnext{right:0}
@media (min-width:901px){.vnext{right:360px}}
'''
new = ''' .vprev{left:0}.vnext{right:360px}
'''
[[mutation]]
label = "resolver: an entry that cannot be stat'd raises out of booth_items"
file = "booth/items.py"
test = "tests/test_items.py::test_a_folder_that_lists_but_cannot_be_searched_costs_its_files_not_the_index"
old = '''
if not p.is_file():
continue
except OSError:
continue'''
new = '''
if not p.is_file():
continue
except FileNotFoundError:
continue'''
[[mutation]]
label = "the stacked Desk column is a bare 1fr (content sets its minimum)"
file = "booth/templates/base.html"
test = "tests/test_flow_browser.py::test_the_desk_never_scrolls_sideways_at_any_width"
old = '''
@media (max-width:1000px){.desk{grid-template-columns:minmax(0,1fr)}}'''
new = '''
@media (max-width:1000px){.desk{grid-template-columns:1fr}}'''
[[mutation]]
label = "a long unbreakable install path in <code> scrolls the page sideways"
file = "booth/templates/base.html"
test = "tests/test_flow_browser.py::test_the_desk_never_scrolls_sideways_at_any_width"
old = '''
padding:1px 6px;border-radius:var(--radius-sm);border:1px solid var(--border-subtle);overflow-wrap:anywhere}'''
new = '''
padding:1px 6px;border-radius:var(--radius-sm);border:1px solid var(--border-subtle)}'''
[[mutation]]
label = "the row's text column cannot shrink (the 700-1000px window overflows)"
file = "booth/templates/base.html"
test = "tests/test_flow_browser.py::test_the_desk_never_scrolls_sideways_at_any_width"
old = '''
.desk-main{flex:1 1 auto;min-width:0}'''
new = '''
.desk-main{flex:1 1 auto}'''
[[mutation]]
label = "the wipe dialog shows the agent-made name raw (bidi, newline)"
file = "booth/templates/index.html"
test = "tests/test_flow_browser.py::test_the_wipe_dialog_shows_what_is_being_wiped_and_never_fails_open"
old = '''
if (!confirm(word(shown(form.getAttribute('data-booth') || '')))) ev.preventDefault();'''
new = '''
if (!confirm(word(form.getAttribute('data-booth') || ''))) ev.preventDefault();'''
[[mutation]]
label = "an unknown data-confirm word submits with no prompt (fail open)"
file = "booth/templates/index.html"
test = "tests/test_flow_browser.py::test_the_wipe_dialog_shows_what_is_being_wiped_and_never_fails_open"
old = '''
var word = WORDS[form.getAttribute('data-confirm')] || ASK;
if (!confirm('''
new = '''
var word = WORDS[form.getAttribute('data-confirm')];
if (word && !confirm('''