One unmeasured candidate refuses "Complete my squad" for everyone (409 on the live app today) #35

Closed
opened 2026-09-13 14:46:02 +02:00 by solvreven · 2 comments
Owner

Found 2026-09-13 while replaying the app's own picks against a real entry (scripts/backtest_app_vs_entry.py, 84fd8d7).

optimize.autocomplete calls _require_trusted_pool(candidates), which raises UnmeasuredValueError if any candidate's availability provenance is untrusted. /api/autocomplete turns that into a 409 for the whole solve.

  • Today: POST /api/autocomplete {"locks": []} answers 409: element 655: availability provenance is POSITION_PRIOR, which is not measured. That is one player (Gboho, Coventry, start_prob 0.36) in a pool of ~400.
  • On 21 August (GW1 snapshot): 102 of the 478 candidates were new signings on position priors. The builder could not complete a squad for anyone in the week the squad mattered most.

The guard is right about the player and wrong about its blast radius. It is the same shape as the 2026-08-30 armband fix in CLAUDE.md: the armband refuses; the XI is still served — ask what else a refusal takes with it. The docstring's argument ("anything in the pool can be chosen") is satisfied just as well by removing the untrusted from the pool as by refusing the pool.

Suggested direction:

  • Build the pool from trusted candidates only, and return the excluded players by name (excluded_unmeasured) so the Transfers/builder surface can say who was left out and why.
  • Keep refusing when the user LOCKS an untrusted player; that is an explicit request the model cannot honour.
  • Do not trust them or substitute a value.
  • Tests: the refusal still fires for a locked untrusted player, an unlocked untrusted player never appears in ev.squad or floor.squad, and the excluded list is rendered on the page (drive the real page).

For scale: the best of the 102 on 21 August projected 8.7 points over six rounds, nowhere near a squad place. So excluding them changes no squad, while refusing changed every squad into none.

**Found 2026-09-13** while replaying the app's own picks against a real entry (`scripts/backtest_app_vs_entry.py`, `84fd8d7`). `optimize.autocomplete` calls `_require_trusted_pool(candidates)`, which raises `UnmeasuredValueError` if **any** candidate's availability provenance is untrusted. `/api/autocomplete` turns that into a 409 for the whole solve. - **Today:** `POST /api/autocomplete {"locks": []}` answers `409: element 655: availability provenance is POSITION_PRIOR, which is not measured`. That is one player (Gboho, Coventry, start_prob 0.36) in a pool of ~400. - **On 21 August (GW1 snapshot):** 102 of the 478 candidates were new signings on position priors. The builder could not complete a squad for anyone in the week the squad mattered most. The guard is right about the player and wrong about its blast radius. It is the same shape as the 2026-08-30 armband fix in CLAUDE.md: *the armband refuses; the XI is still served — ask what else a refusal takes with it*. The docstring's argument ("anything in the pool can be chosen") is satisfied just as well by removing the untrusted from the pool as by refusing the pool. **Suggested direction:** - Build the pool from trusted candidates only, and return the excluded players by name (`excluded_unmeasured`) so the Transfers/builder surface can say who was left out and why. - Keep refusing when the user LOCKS an untrusted player; that is an explicit request the model cannot honour. - Do not trust them or substitute a value. - Tests: the refusal still fires for a locked untrusted player, an unlocked untrusted player never appears in `ev.squad` or `floor.squad`, and the excluded list is rendered on the page (drive the real page). For scale: the best of the 102 on 21 August projected 8.7 points over six rounds, nowhere near a squad place. So excluding them changes no squad, while refusing changed every squad into none.
Author
Owner

Fixed in a3346e3 on feat/captaincy-board.

  • /api/autocomplete now leaves an unmeasured candidate out of the pool instead of refusing the whole solve, and serves him in excluded_unmeasured with his provenance.
  • "Complete my squad" prints the line: Left out of the pool: Gboho (Coventry City, MID) — no measured minutes, only a position prior…
  • A locked untrusted player still refuses, as before.
  • optimize._require_trusted_pool is unchanged and still sees only trusted candidates.

Verified on the live app: POST /api/autocomplete {"locks": []} → 200, 15-man squad, Gboho named; screenshot checked. Six cuts, six red, including a note that is computed and not rendered.

Still open, same shape, not fixed here: the page sends every current pick as a lock. A squad holding one player with no projection at all (start_prob < 0.05, e.g. a 4.0 third keeper) is therefore still refused whole ("no projection for locked player(s): Palmer (301)"). Entry 6261's own squad hits this today. The likely fix mirrors this one: keep such a pick in the squad, bar him from the XI and bench credit, and name him, rather than refuse the request.

Closing this issue for the pool-wide refusal.

**Fixed in `a3346e3`** on `feat/captaincy-board`. - `/api/autocomplete` now leaves an unmeasured candidate out of the pool instead of refusing the whole solve, and serves him in `excluded_unmeasured` with his provenance. - "Complete my squad" prints the line: *Left out of the pool: Gboho (Coventry City, MID) — no measured minutes, only a position prior…* - A **locked** untrusted player still refuses, as before. - `optimize._require_trusted_pool` is unchanged and still sees only trusted candidates. Verified on the live app: `POST /api/autocomplete {"locks": []}` → 200, 15-man squad, Gboho named; screenshot checked. Six cuts, six red, including a note that is computed and not rendered. **Still open, same shape, not fixed here:** the page sends every current pick as a lock. A squad holding one player with **no projection at all** (start_prob < 0.05, e.g. a 4.0 third keeper) is therefore still refused whole ("no projection for locked player(s): Palmer (301)"). Entry 6261's own squad hits this today. The likely fix mirrors this one: keep such a pick in the squad, bar him from the XI and bench credit, and name him, rather than refuse the request. Closing this issue for the pool-wide refusal.
Author
Owner

The part left open above is fixed too, in aa91f71.

A locked pick with no projection (start_prob < 0.05) is now kept and not valued instead of refusing the request.

  • His ev is None, never 0.0.
  • optimize._solve bars a None from the XI and gives his bench slot no credit, so projected players come on first.
  • He is named in kept_unprojected, and the page prints Kept as picked, not valued: … no projection.
  • A lineup that could only be legal by starting him refuses.
  • A locked untrusted pick still refuses.

Verified on the live app with entry 6261's fifteen picks all locked: 200, a 3-5-2 XI, Palmer (Ipswich, 4.0) kept last off the bench. Six cuts, six red. Full suite green in a clean worktree (1739 passed).

**The part left open above is fixed too, in `aa91f71`.** A locked pick with no projection (start_prob < 0.05) is now **kept and not valued** instead of refusing the request. - His `ev` is None, never 0.0. - `optimize._solve` bars a None from the XI and gives his bench slot no credit, so projected players come on first. - He is named in `kept_unprojected`, and the page prints *Kept as picked, not valued: … no projection*. - A lineup that could only be legal by starting him refuses. - A locked **untrusted** pick still refuses. Verified on the live app with entry 6261's fifteen picks all locked: 200, a 3-5-2 XI, Palmer (Ipswich, 4.0) kept last off the bench. Six cuts, six red. Full suite green in a clean worktree (1739 passed).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
solvreven/FPL#35
No description provided.