# P4-club-admin — findings

| | |
| --- | --- |
| Reviewer | Zayed |
| Reviewed | — |
| Status | draft |
| Supersedes | — |
| Superseded by | — |

**This prototype is a draft and its outcome column is empty on purpose.** Every
decision below offers two defensible answers, both actually built and both
switchable from the bar under the prototype header. The choice follows the
reader from screen to screen and is written into the address, so every
screenshot named here is reproducible by pasting its URL back.

## Outcome per decision

| Id | Decision | Options built | Outcome | Evidence | Writes back to |
| --- | --- | --- | --- | --- | --- |
| **D1** | **Where the season is chosen.** The structure, the roster, the entries and the squad list are all season-scoped. Is the season chosen **once for the club area**, in the head that already carries the club's name and status, or **re-chosen on each screen**? | a) once for the area, remembered across it — `season=area` b) a control on each screen, not remembered — `season=screen` | | `screenshots/structure--default--ar--season-area.png`, `screenshots/structure--default--ar--season-screen.png`, `screenshots/roster--default--ar--season-area.png`, `screenshots/roster--default--ar--season-screen.png`, `screenshots/index--default--ar--season-area.png`, `screenshots/contract--default--ar--season-area.png` | (c) a convention in the web repository |
| D2 | **The structure page**: one **tree**, or **season-filtered tables**? | a) a tree — `structure=tree` b) tables — `structure=tables` | | `screenshots/structure--default--ar--tree.png`, `screenshots/structure--default--ar--tables.png`, `screenshots/structure--default--en--tables.png`, `screenshots/structure--partial--ar.png` | (a) the club-structure epic |
| **D3** | **How a squad refusal is shown, and where a documented exception is entered.** Three of the four rules refuse a named person on this roster today. **Inline on the row**, or **a rules panel** beside the list? | a) on the person's row — `refusal=row` b) a rules panel — `refusal=panel` | | `screenshots/squad--default--ar--refusal-row.png`, `screenshots/squad--default--ar--refusal-panel.png`, `screenshots/squad--default--en--refusal-panel.png`, `screenshots/squad--partial--ar.png`, `screenshots/squad--error--ar.png` | (a) the entries-and-squads epic, and (c) a convention |
| D4 | **Attach or create.** **One search box** that offers creation when it finds nothing, or **two separate paths** chosen up front? | a) one box — `attach=one` b) two paths — `attach=two` | | `screenshots/attach--default--ar--attach-one.png`, `screenshots/attach--default--ar--attach-two.png`, `screenshots/attach--empty--ar--attach-one.png`, `screenshots/attach--empty--ar--attach-two.png`, `screenshots/attach--partial--ar.png` | (a) the roster epic, and (b) a note on the profile-claims epic |

### The epic's first decision was already answered, and what replaced it

The owning epic's D1 reads: "the club shell: a dedicated `/club` area with its
own navigation vs club screens inside the main navigation."

**S1 locked that on 18 September 2026**, as S3: the club is an area with a tab
strip, its name and its status at the head of every screen inside it — and not
as a preference. Under the flat answer `club-structure.html` and
`club-roster.html` lost the club's status band completely, so a pending club
reached its roster with nothing telling it that it could not transact, which
broke that ticket's own condition. That condition is the spine of this
prototype: `club=pending` is a switch here precisely because a club's standing
governs every screen in the area.

A locked decision is superseded by a new prototype and never reopened inside
one, so **this prototype renders the locked answer and does not offer the
rejected one as a switch.** If the reviewer wants it reopened, that is a new
`P<n>` superseding S1, not a decision inside this one.

**What replaced it is the question the locked answer leaves open: where the
season is chosen** — recorded above as D1 and built both ways, with the
difference in behaviour and not in decoration. Choose 2025/26 on the structure
screen and move to the roster: under `season=area` you are still in 2025/26, and
under `season=screen` you are back in the current season with nothing having
said so.

### Secondary questions the same screens answer

| Question | Where it is visible | Writes back to |
| --- | --- | --- |
| **An empty state must never tell somebody to do a thing his account cannot do.** Every empty state here has two readings, one for an approved club and one for a club under review, and the second asks for nothing at all. Is that a rule for every empty state in the product, or only where an entitlement gates the action? | `screenshots/roster--empty--ar--pending.png`, `screenshots/roster--empty--ar--approved.png`, `screenshots/structure--empty--ar--pending.png`, `screenshots/entries--empty--ar--pending.png` | (c) a convention in the web repository |
| **The signed-in club page.** The S1 review found the signed-in club directory opening the anonymous visitor page. The data is the same on both — that is locked — so is the signed-in twin one route rendered in two shells, or two routes? | `screenshots/club--default--ar.png`, `screenshots/club--partial--ar.png`, `screenshots/club--empty--ar.png` | (b) a note on the public-directory epic, shared with P6 |
| A **contract change is a new record**, and the record it replaces stays readable with its dates and its author. Does a **correction** — a mistyped date rather than a changed agreement — take the same shape, or is it an edit with an audit line? | `screenshots/contract--default--ar.png`, `screenshots/contract--error--ar.png` | (a) the roster-and-contracts epic — **it changes what the contract endpoint accepts** |
| **Injury status is club-internal** and appears on no public club page and no published squad list. Does it reach another club at the private tier while that club is about to approach the person? | `screenshots/contract--default--ar.png`, `screenshots/club--default--ar.png`, `screenshots/roster--default--ar.png` | (b) a note on the injury epic |
| A club that **creates** a person rather than finding one produces an unverified stub that lands, later, in the platform's profile-claims queue. Should creating require at least one identifying document, given the cost falls on somebody else's queue? | `screenshots/attach--partial--ar.png`, `screenshots/attach--empty--ar--attach-two.png` | (b) a note on the profile-claims epic |
| **An entry is never deleted**, and the same rule refuses an administrator deleting an edition that holds entries. Is closing an edition with an end date the right escape hatch? | `screenshots/catalogue--error--ar.png`, `screenshots/entries--default--ar.png` | (a) the catalogue-admin epic |
| A **tournament edition with no registration window** is visible to every club and enterable by none, and no club screen can say why. Should the catalogue refuse to publish an edition until a window is tied to it? | `screenshots/catalogue--partial--ar.png` | (a) the catalogue-admin epic |
| The platform carries **no under-19 and no under-17 competition**, so a club's youth teams have nothing to enter. Is that a gap to fill in the catalogue, or is youth competition outside version one? | `screenshots/entries--default--ar.png`, `screenshots/catalogue--default--ar.png` | (a) the catalogue-admin epic |

### The review pattern, taken from P3 and used here

P3 settled the review pattern and wrote it down in five points. The squad list is
this prototype's queue of decisions and is built from those five points **without
changing them**: a queue of candidates with no count that is not the length of
the list; the evidence on each row; a decision that requires a reason; a decided
row that stays readable with its reason, its author and its date; and the
decision recorded against a person who stays in the provenance column.

**What P4 hands back to P3 as a question**: here the refusal comes from a *rule*
and not from a reviewer, so the reason is written before anybody reads it. D3 is
about where that pre-written reason goes, and P3's answer does not cover it —
P3's reason is typed by a person into a field, and this one is a sentence the
server already knows. Whether these are one pattern with two sources of text, or
two patterns, is worth five minutes of the review.

---

# Write-back

Three destinations, one section each, filled in after the review.

**No requirement id from any parked requirement document may appear anywhere
below.** Restate it in the project's own words or drop it.

## (a) New stories — appended

Destination epics, from the owning ticket: registration and members to the
**club registration and members** epic; structure to the **club structure**
epic; the roster, contracts and injury to the **roster** and **injury** epics;
entries and squad lists to the **entries and squads** epic; the catalogue to the
**admin reference-data** epic.

```
- **As a <actor>**, I <do>, so that <outcome>.
  - Accept: <criteria>.
```

Two lines, the second indented by two spaces, append-only.

## (b) Notes on existing stories

Written after the review, keyed by 1-based position.

## (c) Rules and conventions for the code repository

| Glob | Title | Note |
| --- | --- | --- |
| — | the season's place in the club area | **D1's answer.** Not a re-opening of "the club is an area with a tab strip", which is locked. |
| — | role-aware empty states | **An empty state never asks for an action the account cannot take.** One reading per entitlement, and the refused reading asks for nothing. |
| — | a contract is never edited | A contract change records a new contract and keeps the one it replaces, with its dates and its author. |
| — | squad refusals | **D3's answer**, plus: every squad refusal names the rule, names the date it changes where one exists, and offers the one thing to do next. |

---

## Rejected options

Filled in after the review.

| Option | Why it was rejected | Evidence |
| --- | --- | --- |

## A screen the S1 review found missing, built here

**The signed-in club page** (`club.html`). The S1 review recorded that the club
directory in the signed-in navigation opened the anonymous visitor page, and
that the two-shells convention decides the frame while the routing was carried
forward. P6 owns the anonymous twin; this is the signed-in one, built inside the
application shell with the rail intact, carrying exactly the data a visitor
reads plus the three things signing in adds: open the profile, add to the
watchlist, send an approach. Its `partial` state is the club whose public
profile module is not active, which is the only way a club ever discovers what
its own page does not say. **It has no `no-permission` state, and that is a
finding rather than an omission**: reading a club's page needs no standing, and
a club under review reads it exactly as an approved one does, because browsing
is precisely what it was given.

## What P4 found in the inherited decisions and in the shared cast

Recorded here rather than acted on, because a locked decision is superseded by a
new prototype and never edited, and because a fixture correction belongs in the
open.

1. **The owning epic's D1 was answered by S1 as S3.** See above. It is rendered,
   not re-asked, and a live question took its place.
2. **The shared cast contradicted itself on the squad list.** `FIXTURES.md` read
   "22 of a maximum 25" for نادي الصحراء's football squad and "twenty-four" for
   its roster — and that roster also holds two people with no contract recorded,
   two basketball players, a sixteen-year-old and a player already entered
   elsewhere. Twenty-two plus six is twenty-eight. The squad list is now **18**,
   the correction is written into `FIXTURES.md` in the open rather than hidden,
   and the arithmetic closes exactly.
3. **The consequence of that correction is a design finding, not bookkeeping.**
   The cap of twenty-five is **not** the rule that binds this club: it has seven
   seats free and still cannot fill three of them, because every remaining
   candidate is refused by a different rule. The screen therefore draws the cap
   as remaining capacity and the other three rules as refusals, rather than
   inventing seven more people so that a fourth refusal could be drawn.
4. **A club under review is owed nothing, and that is a fact rather than a gap.**
   The work-owed count reads 0 for فهد الزهراني because a club that transacts
   with nobody has nobody waiting on it, and his unread count is 1 — the
   acknowledgement that his registration arrived. Both are recorded in
   `FIXTURES.md`.

## Not settled

- **Who may act for a club.** Membership is still one owner flag and nothing
  else, carried from S1. The roster, the squad list and a documented exception
  filed with a federation are three very different writes handed to the same
  undifferentiated "member", and the prototype marks the owner-only actions with
  the locked-field pattern rather than with a permission model.
- **Whether a correction to a contract is a new record or an edit.** Every change
  is drawn as a new record because "entries are never deleted" points that way,
  and a mistyped date is not a changed agreement.
- **Whether a club may create a person with no document at all.** The prototype
  lets it and marks the result unverified.
- **Whether a squad entry follows the person or the club.** بسام الزامل is
  entered for نادي صقور الحجاز in an edition he no longer plays in; nothing here
  says whether that club's entry is amended, and the window means it could not be
  until January in any case.
- **Whether the sporting catalogue belongs to this prototype at all.** It is built
  here because the screens that consume it are here, and it renders P3's answers
  for list shape and for confirming a platform-wide write rather than asking
  again. P3's own findings raise the same question from the other side.

## Not built

- **A permission model for club members.** Named and deliberately not built: the
  prototype has no second club member with different rights, because S1's
  membership is owner-or-member and inventing a third standing would presume the
  answer to an open question.
- **The federation's side of a documented exception.** The exception is
  documented by the club and readable afterwards; whether a federation
  acknowledges it, refuses it or merely receives it is outside this prototype.
