# P5-requests-offers — 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.

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

The `[D5.0]` ticket asks, as its D1, "the thread shape: chat-like timeline vs a
structured terms table with history". **S1 locked that on 18 September 2026** as
its S4: an approach is **one shape for both sides** — a record with the terms in
the list, and a terms record with its history beneath on the detail — with **the
message riding on the history row**, so the history *is* the thread; the two
sides differ only in the filter each opens on (the club on "waiting on them", the
professional and the agent on "waiting on you"); and **the board by state was
rejected as the primary list** and is welcome only as a view toggle on the club's
list.

P5 **renders that answer and does not offer it as a switch.** `offer.html` is the
record with the terms at the head, the history beneath and the message on the
history row; `inbox.html` is one list read three ways with the filter each side
opens on stated on the screen; there is no `deal=thread` variant anywhere in
`p5.js`.

**In its place, D1 below asks what the locked answer left open: the terms are in
the list, and which terms was never said.** The other candidate — where the
club's rejected board-as-a-view-toggle belongs — is deliberately not rebuilt:
S1 already gave it a placement ("welcome later as a view toggle on the club's
list") and its own evidence for rejecting it as the primary list is a screenshot
of six columns wrapping at 1280 with the rail. Re-drawing it here would be
re-asking a question with a screenshot already behind it.

## Outcome per decision

| Id | Decision | Options built | Outcome | Evidence | Writes back to |
| --- | --- | --- | --- | --- | --- |
| **D1** | **What the record's list row carries when the terms are long.** S1 put the terms in the list and did not say which. A1's standing terms are two years, 2,400,000 riyals and a performance clause; a loan has no transfer fee; a staff contract has neither a fee nor an owning club. | a) a **headline** — the deal type and the one term that changed last, with the state and whose move — `row=headline` b) **fixed columns**, the same set for every deal type, blank where a type has no such term — `row=columns` | | `screenshots/inbox--default--ar--headline.png`, `screenshots/inbox--default--ar--columns.png`, `screenshots/inbox--default--en--columns.png`, `screenshots/inbox--default--ar--pro-headline.png`, `screenshots/deals--default--ar--columns.png`, `screenshots/friendly--default--ar--columns.png` | (a) the offer-lifecycle epic — **it changes the list payload** |
| **D2** | **How a counter is authored.** | a) **edit the terms in place**, one terms object with a history — `counter=inplace` b) **a new terms card beside the old**, two objects and a winner — `counter=beside` | | `screenshots/offer--default--ar--inplace.png`, `screenshots/offer--default--ar--beside.png`, `screenshots/offer--default--en--beside.png` | (a) the offer-thread epic — **it changes the terms payload** |
| **D3** | **Where the owning club's consent appears, for each of the four parties.** **The gap the S1 review named.** Both answers keep it a first-class action with its own refusal reason and both put it in the owning club's work queue; they differ in where the control lives and in what the other three parties read. | a) **on the approach** — a third action block on the record; the owning club gets the buttons, the other parties read the same block as a line — `consent=onrecord` b) **a step of its own** — the approach carries a read-only line for everyone and the decision is taken on `consent.html` — `consent=step` | | `screenshots/offer--partial--ar--onrecord.png`, `screenshots/offer--partial--ar--step.png`, `screenshots/offer--partial--ar--onrecord-agent.png`, `screenshots/consent--default--ar--step.png`, `screenshots/consent--partial--ar--step.png`, `screenshots/consent--default--ar--onrecord.png`, `screenshots/inbox--default--ar--consent-step.png` | (a) the offer-lifecycle **and** the offer-thread epics — the two stories S1 already wrote |
| **D4** | **How the deal-type list and the loan fields adapt per target role.** A player, a coach and a referee are not the same deal. | a) **computed from the target** — the list only offers what the role can be, the loan block exists only where the role can be loaned, and a role with no type gets a stated refusal instead of an empty select — `dealtype=perrole` b) **one common list**, everything always offered, what does not apply disabled with its reason, the refusal on send — `dealtype=common` | | `screenshots/index--default--ar--perrole.png`, `screenshots/index--default--ar--common.png`, `screenshots/index--default--en--perrole.png`, `screenshots/index--no-permission--ar.png` | (a) the offer-lifecycle epic, and (c) a rule for the code repository |

### Secondary questions the same screens answer

| Question | Where it is visible | Writes back to |
| --- | --- | --- |
| **Does the badge on الطلبات count approaches, or work owed?** It reads five for the club; two of those five are approaches and the other three are a need's applications, two expiring contracts and an unanswered membership invitation. The list and the badge do not, and cannot, agree. | `screenshots/inbox--partial--ar.png`, `screenshots/inbox--default--ar--headline.png` | (b) a note on the notification-centre epic — it qualifies S1's "one count from one place" |
| **Nothing waits on the professional in his own approaches inbox**, because his mandate makes his agent the one who answers, and the one thing owed to him is a mandate renewal that lives in notifications. Is a badge that counts a mandate on the الطلبات entry right? | `screenshots/inbox--default--ar--pro-headline.png` | (b) the same note |
| **Does the approach record stretch to something that is not a transfer?** A friendly proposal is built on it here — terms at the head, history beneath, message on the row — with the professional, the window and the owning club all absent, and the required refusal reason falling away with them. | `screenshots/friendly--default--ar--headline.png` | (a) the friendlies epic |
| **Is an unanswered friendly proposal work owed?** One has been waiting on نادي الصحراء since 16 سبتمبر 2026 and the badge does not count it. The prototype did not raise the number, because the count is read from one place. | `screenshots/friendly--default--ar--headline.png` | (b) a note on the notification-centre epic |
| **Does a closed deal leave the approaches list?** It is in both here — a row under the closed filter and a row in the deals list. One record read in two places, or two records? | `screenshots/deals--default--ar--headline.png`, `screenshots/inbox--default--ar--headline.png` | (a) the deal-closure epic |
| **A deal that cannot close** carries a blank closing-date field for as long as the consent is outstanding, and the field stays blank rather than being filled with the date of agreement. | `screenshots/deals--partial--ar.png` | (b) a note on the deal-closure epic |
| **The board publishes no count of applicants**, because how many answered a need is the publishing club's information. The club's own manager does show it. | `screenshots/board--default--ar--pro.png`, `screenshots/needs--default--ar.png` | (c) a convention |
| **A need stays open on a position that was deactivated in the catalogue.** «جناح أيسر» was deactivated on 17 سبتمبر 2026; نادي صقور الحجاز's need on it is open to 31 يناير 2027 and carries a live application. The board says the position is gone and does nothing else. | `screenshots/board--partial--ar.png` | (b) a note on the reference-data epic — it is the other half of P3's D4 |
| **A recipient that cannot be resolved.** سلطان الرشيد's agent's mandate is unconfirmed, so the approach resolves to him alone and the screen names the recipient it could not resolve, before sending rather than after. | `screenshots/index--partial--ar.png` | (a) the send-and-inbox epic |

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

**The published-needs board** (`board.html`), and with it the boundary around the
club's own needs manager. The S1 review recorded that "today's needs" rendered
the club's needs *manager* to a professional: the pull side of the marketplace
had a back office and no front door. Three things are drawn here rather than
described:

1. `board.html` is the front door — a professional and an agent read it and
   answer from it, and nobody manages anything on it.
2. `needs.html?state=no-permission&role=pro` is the refused route, and **it lands
   on the board**, not on the home screen and not on nothing.
3. **The professional's and the agent's empty approaches inbox land on the
   board too.** `inbox.html?state=empty` has one variant per role, and no
   variant offers a club action to an account that is not a club.

Evidence: `screenshots/board--default--ar--pro.png`,
`screenshots/board--default--ar--agent.png`,
`screenshots/needs--no-permission--ar--pro.png`,
`screenshots/inbox--empty--ar--pro.png`, `screenshots/inbox--empty--ar--agent.png`,
`screenshots/inbox--empty--ar--club.png`.

### The six states, and which list holds how many

The footnote on `inbox.html` names six server-enforced states, and **all six
render in this prototype** — which is the check the S1 review failed. No single
list claims to hold all six, because no party is on all seven approaches, and
each list says on the screen how many of the six it holds and where the rest are.

| State | Where it renders |
| --- | --- |
| `مرسل` | A2 — إيفان ماركوفيتش, on the club's list |
| `قيد المراجعة` | A6 — نادي محاربي نجد, on the professional's and the agent's list |
| `عرض مقابل` | A1 — on both lists and on `offer.html?state=default` |
| `مقبول` | A3 — on the club's list and on `offer.html?state=partial` |
| `مرفوض` | A4 — فواز الشمراني, on the club's list |
| `مسحوب` | A5 on the club's list, A7 on the professional's and the agent's |

Evidence: `screenshots/inbox--default--ar--headline.png` (five of the six) and
`screenshots/inbox--default--ar--pro-headline.png` (three of the six, one of
them not in the club's list).

---

# 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, per the ticket: the send screen and the inbox to the
**approach send-and-inbox** epic; the record, the counter and the consent to the
**offer-thread** epic; closure and the deals list to the **deal-closure** epic;
the friendly proposals to the **friendlies** epic; the refusals to the
**refusals** epic; the board, the needs manager and the applications to the
**club-needs** epic.

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

Two lines, the second indented by two spaces, append-only. Never reorder,
renumber or reword a story already there.

## (b) Notes on existing stories

Written after the review, keyed by 1-based position within that epic's
`## Stories` block.

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

| Glob | Title | Note |
| --- | --- | --- |
| — | the approach list row | **D1's answer**, and whether the row states the terms as they stand or as the reader last left them. |
| — | authoring a counter | **D2's answer**, and — if it is two cards — what "the terms on the table" means downstream, on the row, on the consent step and on the deal. |
| — | the owning club's consent | **D3's answer.** Consent is a transition with its own refusal reason, offered to the owning club alone, appearing in that club's work queue, and lapsing the day after the contract it rests on ends. |
| — | deal types per profile type | **D4's answer**, and whether the registry is obliged to say which deal types fit which profile type. |
| — | the pull side of the marketplace | The published board and the club's needs manager are two screens, not one, and the refusal from the manager lands on the board. A screen that refuses an account must offer that account's own next step, not the owner's. |

---

## Rejected options

Filled in after the review.

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

## What P5 found in the inherited decisions

Recorded here rather than acted on, because a locked decision is superseded by a
new prototype and never edited.

**Three approaches in the cast once predated their own negotiation windows,
and the cast has been corrected rather than the screens.** A2, A4 and A5 were
written against contracts ending 30 يونيو 2027, 30 يونيو 2028 and 30 يونيو 2027,
which put all three outside the window the product itself states — the same
class of fault the S1 review caught when a professional held two approaches he
could not lawfully have received. `../FIXTURES.md` now ends those contracts on
28 فبراير 2027, 31 يناير 2027 and 31 ديسمبر 2026, records the correction in the
open, and checks **every** approach against the rule in a table so it cannot
recur. **عبدالله القحطاني is the only person the rule refuses**, and the
refusal on `index.html?state=no-permission` is his.

Pulling those dates in moved three sets of terms, and the terms moved with
them: إيفان ماركوفيتش's loan now runs to the end of the contract it is lent
from rather than four months past it; فواز الشمراني's permanent move starts the
day after his contract ends rather than a month inside it, which is the rule
`P4-club-admin/contract.html` states; and عبدالعزيز الحمدان's was never a loan
at all, because his contract ends before the move begins and there is no club
left to lend him from.

## Fixtures added

Four people and three records, added to `FIXTURES.md` in its own style, and each
one added because the existing cast could not supply it without contradicting
itself. None changes a fact already there.

| Added | Why |
| --- | --- |
| **عماد الغانم** — جناح أيمن — اتحاد الوسام — عقد حتى 31 ديسمبر 2026, متاح للإعارة | The loan form needs a contracted target with an **open** window, and the only one in the cast was فيصل الدوسري, who has three approaches on him and may not have a fourth. |
| **طارق بن حمدي** — مدرب حراس المرمى — نادي محاربي نجد — عقد حتى 31 ديسمبر 2026 | D4 needs a coach who can be approached today; عبدالله القحطاني is the eligibility refusal and is nothing else. |
| **عوض الزهراني** — حكم — القائمة الوطنية — لا نادٍ | The cast had no referee. His deal-type list is empty under both answers to D4, and that emptiness is the finding. |
| **The one closed deal** — نادي الصحراء ← بندر العقيل, عقد عمل, closed 14 أغسطس 2026 | `deals.html` needs a deal that closed, and the cast's only deal in motion is the one that has **not** closed. He was a free coach at the time, so no consent was involved — which is what makes him a clean closure and a useful second deal type. |
| **Three friendly proposals**, one of them awaiting نادي الصحراء | `friendly.html` needs an answer side. The incoming one is recorded in `FIXTURES.md` as **deliberately not one of the club's five**, and whether it should be counted is an open question below rather than a sixth row nobody counted. **A later pass found the screen drawing four, three of them contradicting the cast**, and rebuilt it to F1, F2 and F3; the cast now carries each proposal's match date and ground too, so the screen has nothing left to invent. |

## Added to `shared/base.css`

**Nothing.** Every screen here is built from the primitives already in the kit —
`queue`, `facts`, `table`, `callout`, `card`, `empty`, `locked`, `tabs`,
`field-owned`, `thread`. No new class, no new media query, and the two locked
prototypes render exactly as they were screenshotted.

## Not settled

Questions this prototype surfaced and cannot answer. The first two are the
reviewer's, and a fixture that presumed either answer would have destroyed the
evidence.

- **May a professional apply to a need published by the club he is contracted
  to?** No applicant in the cast is contracted to the club whose need he answers,
  and none was invented. The question is on `board.html` as a question, in both
  the professional's reading and everyone else's. Evidence:
  `screenshots/board--default--ar--pro.png`.
- **Is an application validated against the need's published criteria, or is
  filtering entirely the club's problem?** نايف الشهري is 29 against a published
  term of «حتى 26» and his application is the first row of the table. The screen
  states the term, states his age, and does nothing about it. Evidence:
  `screenshots/need--default--ar.png`.
- **What does a club record with a referee, if anything?** Under both answers to
  D4 the deal-type list for عوض الزهراني is empty; under (a) that is a stated
  refusal and under (b) it is five disabled options. The prototype refuses to
  invent a type. Evidence: `screenshots/index--default--ar--perrole.png`,
  `screenshots/index--default--ar--common.png`.
- **Is an unanswered friendly proposal work owed?** It is not among the club's
  five and the badge does not count it.
- **Does a refused consent close the approach, or return it to the two parties?**
  Both screens say it returns them to the terms rather than closing anything,
  because the prototype had to draw one; the reviewer may reverse it.
- **What happens to a counter's eligibility.** If a counter moves the contract
  start date past the window, nothing here rechecks it, and the check is stated
  to run only before sending.
- **Whether the closing date on a deal awaiting consent should be blank or
  absent.** It is blank here, with the reason in the cell.
- **Withdrawing an application** to a published need, from the professional's
  side. The board shows what he has answered and its state, and no way back out.

## Not built

- The second sport is on the board and never worked through to an approach:
  every approach in the cast is football.
- The form that creates a need. `needs.html` manages needs that already exist;
  publishing one belongs to the club-admin prototype.
- The public twin of the board. It renders inside the signed-in shell with "this
  page is public" stated on it, exactly as S1's visitor page does; which route
  serves a stranger belongs to the public-directory prototype.
