Skip to content

fix(bc-evts): engi tap-back race, one edit-time rule for every score editor; e2e harness re-verified (bc-e2rv) - #473

Merged
gitrgoliveira merged 31 commits into
mainfrom
bc-evts-pr0-e2rv
Oct 15, 2026
Merged

gitrgoliveira merged 31 commits into
mainfrom
bc-evts-pr0-e2rv

Conversation

@gitrgoliveira

@gitrgoliveira gitrgoliveira commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • A flag taken back while its add is still saving now stays taken back (engi editor). The engi editor compared its counts by value only, so tapping + then − before the + save's echo returned read as "no change", and the echo put the flag back. It now holds its board against a server copy stamped before the operator's last unsent edit, the rule the individual editor got for the same race in test(bc-e2eh, mp-yqxn.2): e2e harness and a clumsy-operator review of knockout and mixed #442 (bc-ltap). This is the first phase (PR0) of bc-evts; the event-log phases follow in their own PRs.
  • The edit-time rule is now the same in all three score editors. The stamp is taken only while the match is running (only then is the edit autosaved, and only a landed autosave clears it), and is cleared when the match leaves running. Before, a net-zero fiddle on a scheduled match or a correction left a stamp nothing cleared, and the editor refused another device's earlier copy.
  • A refused autosave now takes the server's copy. No broadcast follows a refusal, so the board used to keep the refused value while every other surface showed the server's. A queued answer keeps the stamp (it lands later), and a clock_skew answer keeps the tap for the next save to carry with a corrected stamp, as recordScore intends.
  • Folder import: a competition row with no date takes the tournament's date (bc-imdt). It imported, then could never be drawn ("Cannot start: invalid date"). POST /competitions and the import now share one rule (defaultCompetitionDate); a row the tournament cannot date is refused on its own row.
  • Edit tournament details no longer logs a console error when no logo is set (bc-lgnf). It probed HEAD /api/branding/logo, which answers 404 on purpose; the tournament's theme now carries a read-only hasLogo.
  • A pool tie still waiting for its tie-break bout now reads as a tie (bc-tbdg). Two competitors level on every criterion showed as 2nd and 3rd, the 2nd with the advancing mark, as if the order were decided. Both now read =2nd, with a badge naming the tie-break bout that will decide it, and neither carries the advancing mark while the tie decides who goes through. A tie inside the advancing places (level for 1st when two advance) reads =1st and keeps the mark; a tie wholly below the qualifying places shares its place with no bout.
  • A tie over an extra qualifying place now gets its tie-break bout (bc-xqtb, bc-fbtb). The bout was held only within Winners per pool, so a tie over the extra place an oversized pool sends (Oversized send +1) or over a drafted pool's 2nd (Fit the knockout) got none, and whoever sorted higher took the knockout place. The cut is now read off the stored draw: the places the bracket's "Pool X-Nth" placeholders seat, the same labels the qualifier seating fills, so standard, larger-pools and fill-bracket follow one rule, and qualification waits for the bout. The competition detail payload carries the per-pool count (poolQualifiers), so the standings badge and advancing mark follow the server instead of re-deriving it.
  • The e2e harness is re-planned and re-verified against main (bc-e2rv): review fixes across the fixtures (bounded and failing mark clearing, response waits for lineup saves, settle-before-read sleeps replaced by condition waits, one owner each for the two-tap commit, correction and coarse-pointer helpers, three fixmes tightened to their beads), the README checked line by line against the code, and seven new journeys: engi, kachinuki in pools, organiser roster (check-in, seeds, participant edit and replace), organiser admin (results workbook, folder import, logo and sponsor, password reset), locked mode, pools tie-break (minimum pool size, a tie-break bout, and ties over a larger-pools extra place and a drafted 2nd; bc-e2tb) and a spectator journey on the phone (public) project (bc-e2pb).

Why

Operator rulings 2026-10-12/13: bc-evts' plan puts this engi fix first (PR0); bc-e2rv asked for the harness to be re-planned and re-checked from main after #442; "fix and polish all detected gaps", with the coverage journeys in this PR except Swiss and except the journeys an open mp-yqxn UX-review bead owns (league mp-yqxn.3, self-run public mp-yqxn.5, engi review mp-yqxn.6, settings mp-yqxn.8; a Swiss journey is filed as bc-e2sw). The plan is on bc-e2rv's design field (rev 3). The pools tie-break journey found the tie display gap (bc-tbdg), and its review found the larger-pools and fill-bracket tie-break gaps (bc-xqtb, bc-fbtb); each was ruled "bead, fix it here" (2026-10-14).

Reviewed by Claude review rounds and independent rounds by another model family (Antigravity, Gemini) after each change set, the last two on the tie-break cut. Each round's critical and major findings were fixed, or dismissed with the code that decides them, until none remained.

Files changed

File What
web-mobile/js/admin_scoring_engi.jsx edit-time stamp for the flag counts; running-only; cleared on leaving running; refused autosave takes the server's counts
web-mobile/js/admin_scoring_individual.jsx running-only stamp, cleared on leaving running; refused autosave takes the server's score
web-mobile/js/admin_scoring_team.jsx row and pick stamps running-only, cleared on leaving running; refused autosave takes the server's rows and picks (takeServerRowsRef)
web-mobile/js/admin_scoring_autosave.jsx landedAutosave gains onRefused; clock_skew and queued answers call neither callback
web-mobile/js/write_result.jsx writeWasRefused comment names its callers
web-mobile/js/admin_branding.jsx reads theme.hasLogo instead of probing HEAD /api/branding/logo
internal/state/models.go Theme.MarshalJSON emits a derived read-only hasLogo
internal/mobileapp/handlers_branding.go comments for the derived field
internal/mobileapp/handlers_competition.go defaultCompetitionDate, shared by POST /competitions and the import
internal/mobileapp/handlers_import.go a dateless row takes the tournament's date or is refused on its own row
internal/mobileapp/handlers_import_date_test.go new: the import's date rule
internal/mobileapp/handlers_branding_haslogo_test.go new: hasLogo on the wire, a PUT can neither set nor lose it
internal/mobileapp/handlers_import{,_format,_kind,_extra_qualifiers}_test.go their tournaments get a date (a dateless row under a dateless tournament is now refused)
specs/openapi.yaml import date rule; Tournament.theme with read-only hasLogo; poolQualifiers on the competition detail
.claude/rules/match-results-and-marks.md "The edit-time adopt rule": owners, running-only, refusal, clock_skew, limits
web-mobile/js/__tests__/render/engi_editor_tap_back_kept.render.test.jsx new: the engi race, both directions, the edit counter, queued, scheduled, boundary, leaving running
web-mobile/js/__tests__/render/individual_engi_refused_autosave_takes_server.render.test.jsx new: refused, queued and clock_skew autosaves, late refusal after a finish
web-mobile/js/__tests__/render/team_editor_stamps_only_while_running.render.test.jsx new: team sheet running-only stamps, refused/queued/clock_skew
web-mobile/js/__tests__/render/admin_branding_no_logo_probe.render.test.jsx new: no request that can 404
web-mobile/js/__tests__/render/{individual_editor_tap_back_kept,team_editor_row_stamp_cleared_on_landed_save,autosave_every_scoring_edit,changed_groups_every_editor,ippon_double_tap,kachinuki_cleared_bout_reaches_server,team_fusensho_keeps_points}.render.test.jsx landed answers instead of undefined (now a refusal); new cases
internal/engine/tiebreaker.go poolTieBreakCut, the one cut every pool tie path asks, read off the stored draw (drawnQualifierPlaces); PoolQualifierCuts for the payload
internal/engine/{chusen,daihyosen,pool_requalify}.go chusen candidates, team daihyosen and the requalification wait ask the same cut
internal/engine/tiebreak_extra_qualifier_test.go new: larger-pools and fill-bracket ties (bout owed, none below the cut, team daihyosen, chusen, qualification waits)
internal/engine/pool_daihyosen_bandaware_test.go test helper takes competition options
internal/mobileapp/handlers_viewer.go, handlers_viewer_test.go poolQualifiers on the competition detail payload
web-mobile/js/viewer_standings.jsx tiedPlaces: tied rows share their place (=2nd), badge title names the bout, advancing mark withheld while the tie decides it; qualifiersForPool reads the served count
web-mobile/js/api_serializers.jsx normalizeCompetitionDetail puts each pool's served count on the pool
web-mobile/js/__tests__/render/pools_tied_place.render.test.jsx, viewer_pools_viewer.test.jsx, api.test.jsx new and updated: tied places, larger-pools and drafted-2nd ties, the served count
docs/user-guide/organisers/run-tournament.md how a tie shows (=2nd) and when it gets a tie-break bout, including the extra qualifying places
scripts/e2e/journeys/pools-tiebreak.spec.mjs new: minimum pool size, a tie-break bout, a larger-pools extra-place tie, a drafted-2nd tie (three competitions, three courts)
scripts/e2e/journeys/spectator.public.spec.mjs, scripts/e2e/fixtures/public.mjs new: the spectator journey on the phone project
scripts/e2e/journeys/engi.spec.mjs new: an engi match on the court console and the public phone; the tap-back race held in flight, proven red without the fix
scripts/e2e/journeys/kachinuki-pools.spec.mjs new: kachinuki in pools, a level pool draw, the tables on admin and phone, the winners in the final
scripts/e2e/journeys/organiser-roster.spec.mjs new: check-in, seeds (proven red without seeding), participant edit and replace, duplicate refusal
scripts/e2e/journeys/organiser-admin.spec.mjs new: results workbook (proven red against a pre-bout export), folder import incl. a dateless row, logo and sponsor, reset; bc-lblg fixme
scripts/e2e/journeys/organiser-locked.spec.mjs new: locked mode sign-in, no Forgot password, /reset disabled
scripts/e2e/journeys/{knockout-mixed-individual,knockout-mixed-kachinuki,knockout-mixed-team,moved-match,shared-shiaijo,smoke}.spec.mjs condition waits, real assertions for swallowed probes, settled reads, listeners released in finally, fixmes bc-adna/bc-seqc/bc-pubq tightened, kachinuki totals follow paced strikes
scripts/e2e/fixtures/{engi,held-write,pools,roster,organiser}.mjs new fixtures for the new surfaces and the held-save guard
scripts/e2e/fixtures/{audit,clumsy,competition,individual,kachinuki,pace,pointer,scoring,setup,shared-court,shots,team,test,wizard}.mjs review fixes; one owner per helper; serverOptionsFor; console allowlist (an unreadable 409 only when the page closed; the duplicate participant edit)
scripts/screenshots/lib/server.mjs optional { args, env } for a spec's server (capture harness callers unchanged)
scripts/e2e/README.md checked against the code; new journeys, fixtures, waits, server options
Makefile the e2e comment's Playwright-release claim made accurate

Screenshots

All from the e2e journeys' real-browser runs at the PR head (iPad context, 1180x820).

Engi: a flag taken back while its add was saving stays taken back (court console; the add's echo has arrived, the board still shows 0 and is Synced):

engi flag taken back

The same after a reload: the server holds 0 (the save of the take-back named the flags as changed):

engi after reload

Folder import: a row with no date took the tournament's date (28 Nov 2026) and can be drawn (before this PR it showed "Cannot start: invalid date in Settings tab" and Generate draw was disabled):

import no date

Edit tournament details with an uploaded logo (the page now reads theme.hasLogo; with no logo it sends no request that answers 404):

edit details logo

A pool tie waiting for its tie-break bout (admin Pools tab): Kei Takagi and Mina Abara are level on every criterion in Pool B, both read =2nd, neither carries the advancing mark, and the bout that decides them is still to be fought:

pools tie level

A tie over the extra place an oversized pool sends (Oversized send +1, public phone view): Pool A sends two; Joan Mercer and Lars Nilsen are level for 2nd, both read =2nd, and their tie-break bout (7) is scheduled. Before this PR no bout was held and whoever sorted higher took the place:

extra place tie

A tie over a drafted 2nd (Fit the knockout, admin): Pool A was drafted to send its 2nd, so its level pair gets a tie-break bout (4, on court C); Pool B was not, so its identical tie shares =2nd with no bout:

fit the knockout tie

Test plan

  • make go/test passes (lint + security scan + tests) at the PR head
  • New/updated unit tests cover the change: every behavioural change has a render or Go test shown red without the change and green with it (the tie-break cut also by a review run of 1080 real draws, standard and larger-pools, with no pool's cut changed)
  • Manual browser verification (for web-mobile/ changes): the e2e journeys drive the real app in Chromium (iPad and phone contexts); the engi tap-back race was reproduced in the browser with the fix reverted and passes with it; the folder import of a dateless row and the logo flow were walked the same way
  • make e2e green locally (79 tests in 13 files): at the PR head (a2b7444) default workers 70 passed, 9 skipped (the 9 fixmes of open beads); at de5d22a (the head before the workbook reader's CodeQL fix, which only organiser-admin uses and which passed twice on its own) default workers, --workers=1 and a fresh clone (make e2e/deps, make e2e) each 70 passed, 9 skipped
  • Screenshots added above (real browser, from the journeys)
  • Docs updated under docs/ for any user-facing change: run-tournament.md describes the tied place and when a tie gets a bout (no docs page describes the import manifest; the editors' visible behaviour matches docs already); make docs/build passes
  • Docs screenshots and videos recaptured: none changed (no change to what a user sees on a captured screen)
  • No new console errors or warnings: the console policy fails any unallowed error; the one new allowlist entry is the documented duplicate-participant refusal the roster journey triggers on purpose

Follow-ups filed: bc-lblg (lobby logo as an optional setting; its fixme is in organiser-admin). Not in this PR, by operator decision: league, self-run public and engi-across-formats journeys (mp-yqxn.3, .5, .6); Swiss (filed as bc-e2sw); the engi cross-device lost flag (bc-evts loss 2, removed by the event-log phases, engi in PR4).

Closes bc-e2rv, bc-imdt, bc-lgnf, bc-e2tb, bc-e2pb, bc-tbdg, bc-xqtb, bc-fbtb. Part of bc-evts (PR0 of its plan; the bead stays open).

🤖 Generated with Claude Code

gitrgoliveira and others added 20 commits October 13, 2026 16:52
…ve is out

The engi editor compared flag counts by value only, so taking a flag back to
the count the server still showed read as untouched, and the earlier save's
echo then wiped the tap (the race bc-ltap fixed on the individual editor).
It now holds its counts against a server copy stamped before the operator's
last unsent edit, cleared by landedAutosave once a save built after that edit
has landed. The rule and its three owners are recorded in the match results
rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new journey seeds an engi (kata pairs) competition through the wizard,
scores and finishes a match on the court console and sees it on a public
phone. Its second test holds a flag's save in flight, takes the flag back,
and checks the echo does not restore it, in both directions; with the engi
fix reverted it fails. The held-save route moves to a fixture shared with
the bc-ltap test, and the wizard fixture learns the Engi option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the re-verification review of the e2e harness after PR #442:
- audit rows find their spec from a path with spaces or parentheses;
- mark-clearing loops are bounded (clearMarks) and fail with a message;
- lineup saves wait for the save's response, not a button or a toast;
- an allowlisted 409 whose body could not be read is not an error;
- the fighter list closes with Escape, not a tap at a fixed point;
- a harness-owned startMatch waits for Start, taps it and fails if absent;
- journeyShots refuses a name that would empty the output root;
- one enterTournament owner; one expect timeout constant; rosterOf bounds.
The README and the Makefile comment now say what the code does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew round 1

The edit-time stamp holds a score editor's board against the echo of its own
autosave, and only a landed autosave clears it. On a match that is not
running nothing is autosaved, so a net-zero fiddle left a stamp nothing
cleared and the editor refused another device's earlier copy. The engi and
individual editors now take the stamp only while the match is running.

Tests now model a landed save, and pin the edit counter (a save built before
the latest tap clears nothing), a queued or refused save keeping the stamp,
the scheduled match, and the stamp boundary.

Harness: an unreadable 409 body is allowed only when the page had closed,
under its own reason; the held-save guard asserts its answer reached the page
before the next write was sent, and fails on an unreleased hold; lineup saves
are matched to their team and lineup; a refused panel save sends nothing;
clearMarks fails when a removed mark comes back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew round 2

A finish or send back made elsewhere inside the autosave window drops the
pending write with no answer, so no landed save cleared the stamp; both
editors now clear it once the match is no longer running. The rules note
scopes the running-only stamp to the individual and engi editors and names
the team sheet's stamps as the open question. Harness: a page close is
detected from its close event as well; held-write's stop() only cleans up
and assertNoSafetyRelease() runs after a passing body, so it never hides the
body's own failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…engi journey

The held-write guard now waits for the follow-up write and fails when it is
never sent, clears its timers, and forgets a pending hold on stop. The engi
journey waits for the running editor or Up next's Start, whichever shows,
and checks every flag tap landed. Comment and rules wording corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rom the starting count

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e takes the server's copy

The team sheet now takes its row and pick stamps only while the match is
running and clears them when it leaves running, as the individual and engi
editors do. In all three editors a refused autosave (landedAutosave's new
onRefused) clears the stamp and takes the server's current copy, since no
broadcast follows a refusal to bring the board back; a queued answer keeps
the stamp. Tests that answered an autosave with nothing now answer a landed
one, since nothing handed back reads as a refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five teams, kachinuki, pools of 3 and 2 feeding a final: winner stays on, a
hikiwake retiring both, a team exhausted, a level pool encounter recorded as a
draw with no daihyosen offered, the pool tables' W/L/T, IV/IL/IT and PW/PL on
admin and the public viewer, and the pool winners seated in the final.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… edit, replace)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…set, locked mode)

Adds organiser-admin.spec.mjs (file mode: results workbook download, import
from a folder, logo and sponsor, forgotten-password reset) and
organiser-locked.spec.mjs (--lock-password with a hash from the app's own
hash-password command), with fixtures/organiser.mjs. A spec file may declare
its server's extra args and env with serverOptionsFor; start() takes optional
{ args, env }, unchanged when absent. One narrow console allowlist entry for
the Edit details HEAD probe of /api/branding/logo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A manifest row without a date imported as a success, yet the competition
could never be drawn ("Cannot start: invalid date in Settings tab"). The
import now gives it the tournament's Day 1 through defaultCompetitionDate,
the rule POST /competitions applies (extracted, not respelled), and refuses a
row whose date still cannot be resolved with a per-row message. The
organiser-admin journey's fixme becomes a step asserting Generate draw is
enabled and the date it took; the other fixme is renamed to its bead, bc-lblg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BrandingManager asked HEAD /api/branding/logo whether a logo was set, and the
server answers 404 on purpose while none is, so the browser logged a console
error on every load. The tournament's theme now carries a derived, read-only
hasLogo (omitted when false; the disk filename stays off the wire) and the page
reads it, following the tournament when it changes. The HEAD and GET routes are
untouched for other callers. The e2e console allowlist entry for the 404 is
removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hter fixmes

Settle-before-read sleeps become condition waits: a state, a write's answer
(nextMatchWrite/writeLanded), or, where the outcome is the thing under audit,
the new rendered/untilStable settle helpers (pace.mjs). The sleeps that stay
(scenario time, and negatives where an absence is the evidence) are marked
"Deliberate:". The swallowed-assert probes in smoke and the team journey are
real assertions. One owner each: armTwoTap (finishTeam, endMatch, armFinish,
saveEngiResult), ipponOnRow (boutIppon and awardBoutIppon), correctCompleted
with a live-bout reader (correctAcross), ensureCoarse (pointer.mjs). 15 s
literals use EXPECT_TIMEOUT_MS. The bc-adna, bc-seqc and bc-pubq fixmes are
tightened to their beads' acceptance (still fixme). The kachinuki correction
step paces its strikes past each side's bounce window, which makes bout 1 M K
again and the encounter total PW 8-3 (7-3 after the reopened bout 8), where an
unpaced pair left it at M and the expectations had followed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tions and allowlist entry

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
landedAutosave read a clock_skew answer as a refusal and took the server's
copy, wiping a tap that recordScore deliberately leaves on the board for the
next save to carry with a corrected stamp; it now keeps the stamp and the
board, as for a queued answer. The individual and engi refusal adopt also
requires the stamp still to be set, so a correction tapped after the match
finished elsewhere is not wiped by a late refusal. The rules note states the
team sheet's scope and the queued-then-refused limit. Smaller: a Go doc
comment back on its function, the hasLogo test proves a PUT cannot lose a
logo, and writeWasRefused names its callers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ook that must hold the result, settled reads

- organiser-roster: the seeds test reads the unseeded draw, discards it, seeds
  two competitors it put in one pool, and asserts the redraw separates them
  (red when the seeding step is skipped).
- organiser-admin: the Excel export is compared with one taken before the bout
  (winner carried into the next round, waza letter in a score cell); the
  bc-imdt step uses a tournament day that is not today and the page's own
  formatDate; the TV board's no-sponsor negative waits for the tournament
  payload that carries the sponsor; the bc-lblg fixme is written against an
  optional setting, on and off. The dated import row takes a tournament day.
- untilStable takes `from` (the settled read before the tap); the probes that
  read after a tap use it, wait for a known target, or wait for the write.
- retapWhileSaving waits for every write the taps caused before returning.
- organiser-locked waits for auth-config; kachinuki-pools claims only what it
  tests; liveIndividualBout's 1 s read is only in the toBeNull poll; the
  Registration desk confirm's dwell starts after it rendered; a stale toast
  cannot satisfy the withdrawn competitor's Finish; the sync-pill reason in
  scoring.mjs and the README is corrected; small nits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…poll, listeners released in finally

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…window

A pick from a fighter name list stamps the team sheet's bout list, which
ignores every click inside it for TAP_BOUNCE_MS; the J3 repair pick landed on
that boundary and was dropped about one run in five with --workers=1, and the
step's deliberate wrong pick was always dropped. tapNameOption paces option
taps from the previous one, so the wrong pick now really lands and the repair
always does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 14, 2026 02:10

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread scripts/e2e/fixtures/organiser.mjs Fixed
Comment thread scripts/e2e/fixtures/organiser.mjs Fixed
Comment thread scripts/e2e/fixtures/organiser.mjs Fixed
gitrgoliveira and others added 7 commits October 14, 2026 08:22
The fixme now checks each part of bc-adna on its own (soft assertions, the
network ones read in finally): Finish reads plain Finish; the Scores tab sends
no start by itself (a start is a /score write carrying startOnly), after
Finish or after a fusenpai; and no start is refused court_busy. Three README
listing lines reflowed to 80 columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eak bout

Individual Pools + Knockout, seven entrants, pool size a minimum of 3: the draw
makes pools of 4 and 3 (a maximum of 3 would make 3, 2, 2). One pool is scripted
so two competitors end level on W, L, D, PW and PL for the advancing 2nd place:
the app adds the tie-break bout (Pool X-TB-N), shown as the only bout left to
score on the court console and Scores tab, the table marks both tied until it is
fought, the knockout holds both places, then the winner (picked from the one the
table badged lower) ranks above the loser and is seated, on admin and public
viewer. The other pool is split on points lost alone, with no tie-break bout.

New helpers: playerRowFigures (pools.mjs), markDraw (scoring.mjs),
playScriptedBout and ROSTER_7 (individual.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first journey to run on the public project: an officiated tournament
seeded and scored from a second (operator) context through the interface,
two individual competitions with prefixes K (12 competitors) and M (6).
The phone walks the home, a competition page's tabs and the schedule;
checks the bc-nsrc number rule on the watch picker, the schedule's
filter and its text filter ('k' lists the K draw, 'k1' is K1 alone,
bare digits find nobody, names and dojos by substring); follows a
competitor and sees the watchlist hero and the match card follow the
court without a reload; and opens the match card from the Home NOW list,
the schedule, the Pools tab and the Overview while the operator keeps
scoring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ic journeys

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…titors as tied

While two pool competitors are level on every criterion and the tie decides
who advances, the pool table badged one 2nd in the advancing style and the
other 3rd, by draw order. tiedPlaces (viewer_standings.jsx, the one owner for
the admin Pools tab and the public viewer) gives the group one shared place,
shown as =2nd, with no advancing mark on any member until the tie-break bout
is scored; a tie inside the advancing places keeps the mark, since it cannot
change who goes through. The pools-tiebreak journey asserts it on admin and
phone, and the organiser guide says it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…round 5

tiedPlaces now takes the cut the server holds a tie-break bout against
(effectivePoolWinners, mirroring EffectivePoolWinners) beside the advancing
cut, so a larger-pools tie over the extra place no longer reads as waiting
for a bout that is never scheduled; the tooltip names the bout (or the
representative bout for teams) only when one is owed. New render cases: the
larger-pools tie and a 3-way tie across the cut. The organiser guide's tie
paragraph now says what tiebreaker.go does. Harness: the spectator journey
follows the competitor actually in both queued bouts and polls its reads; the
bc-adna fixme drops dead bookkeeping; markDraw takes a Locator; the
pools-tiebreak bracket read waits for the seated qualifiers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s tie-break bout

The engine held tie-break bouts (and team daihyosen, chusen candidates and
the requalification wait) against EffectivePoolWinners, so a tie over the
extra place an oversized pool sends under Oversized send +1 got no bout and
its qualifier was seated on the standings' arbitrary order. poolTieBreakCut
is now the one cut, the pool's own QualifiersForPool, read by every tie
path. The standings badge follows the same single cut, the docs paragraph
names the extra place, and the pools-tiebreak journey plays a larger-pools
tie over the 2nd place on court B.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gitrgoliveira and others added 2 commits October 14, 2026 11:13
…e-break bout

Fill-bracket drafts some pools to send their 2nd too, but the tie cut
(poolTieBreakCut) read Competition.QualifiersForPool, which cannot say
which pools were drafted, so a tie for a drafted 2nd got no bout and whoever
sorted 2nd took the knockout place. The cut is now read off the stored draw
(drawnQualifierPlaces: the highest place of the pool the bracket's draw-time
placeholders seat), the same labels the qualifier seating fills, so it covers
standard, larger-pools and fill-bracket by one rule and cannot drift from the
bracket when courts or seeds change after the draw. Requalification asks the
same cut. The viewer detail payload carries it per pool (poolQualifiers), and
the standings page reads it instead of re-deriving the larger-pools count.
The pools-tiebreak journey adds a fill-bracket competition on court C.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…raw seats

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 14, 2026 10:50

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…eads <t> runs

CodeQL flagged the e2e fixture's XML reading: entities were decoded one
after another, so "&amp;lt;" became "<", and string text was what was left
after a regex stripped tags. Decode in one pass, and read a shared or inline
string's <t> runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 14, 2026 10:56

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…t tests; harness polish

A corrected pool result after the knockout started is now pinned to wait
for the tie-break bout over a larger-pools extra place and a fill-bracket
drafted 2nd (both red with the requalification cut reverted to Winners per
pool). The team sheet's refused-autosave adopt of the representative bout's
score and pick is pinned (red with that branch returning the local row).
Harness: a single empty-marks read waits for the card's four slots, the
spectator step title names the followed competitor's side correctly, the
first pools-tiebreak bracket read counts roster names only, and three audit
settles in knockout-mixed-individual are marked Deliberate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wyy8S51EvuQx3shdiTPwkp
@gitrgoliveira
gitrgoliveira merged commit a06ad56 into main Oct 15, 2026
13 checks passed
@gitrgoliveira
gitrgoliveira deleted the bc-evts-pr0-e2rv branch October 15, 2026 10:52
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