Lane 11: a sheet's rank takes a name as well as an ordinal, as the evaluator reads it (#737) #966

Merged
claude-bot merged 3 commits from fix/lane11-rank-names into main 2026-09-30 10:43:57 +00:00
Collaborator

A follow-up to #964 (lane 11 PR 3), found by PR 6.

The server's write validation refused rank names that the evaluator accepts. For example, "skills.athletics.prof": "proficient" on a 5e sheet got a 422 "is a rank from 0 to 2", but PR 2's rank() reads either an ordinal or a name on the field's scale, and the built-in 5e fixtures store names.

Fix (sheet_values.check, the rank branch):

  • Accepted: a name that exactly matches the field's scale (the evaluator's rule) is accepted and stored as given; so is a whole ordinal from 0 to the top of the scale.
  • Refused: anything else gets the same 422 shape, naming both forms: "is a rank: 0–2 or one of none, proficient, expertise".

Tests (test_sheets_server.py): run for a PF2e-style scale and a 5e-style scale.

  • A name is accepted, stored and evaluated (expert → 7, proficient → 5), and so is an ordinal.
  • A name in the wrong case, an unknown name, an out-of-range ordinal, −1 and 1.5 are each refused with the exact body.
  • tests/sheets passes (478), and ruff (including alembic/) is clean.

No user-visible change yet (the sheet page arrives with PR 9), so there's no changelog fragment.

🤖 Generated with Claude Code

A follow-up to #964 (lane 11 PR 3), found by PR 6. The server's write validation refused rank **names** that the evaluator accepts. For example, `"skills.athletics.prof": "proficient"` on a 5e sheet got a 422 "is a rank from 0 to 2", but PR 2's `rank()` reads either an ordinal or a name on the field's scale, and the built-in 5e fixtures store names. **Fix** (`sheet_values.check`, the rank branch): - **Accepted:** a name that exactly matches the field's scale (the evaluator's rule) is accepted and stored as given; so is a whole ordinal from 0 to the top of the scale. - **Refused:** anything else gets the same 422 shape, naming both forms: "is a rank: 0–2 or one of none, proficient, expertise". **Tests** (`test_sheets_server.py`): run for a PF2e-style scale and a 5e-style scale. - A name is accepted, stored and evaluated (`expert` → 7, `proficient` → 5), and so is an ordinal. - A name in the wrong case, an unknown name, an out-of-range ordinal, −1 and 1.5 are each refused with the exact body. - `tests/sheets` passes (478), and ruff (including `alembic/`) is clean. No user-visible change yet (the sheet page arrives with PR 9), so there's no changelog fragment. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Lane 11 PR 3: a rank is an ordinal or a name on its scale, as the evaluator reads it (#737)
Some checks failed
CI / Backend lint (ruff) (pull_request) Successful in 34s
CI / Feedback site tests (pull_request) Successful in 49s
CI / Bot/backend version sync (pull_request) Successful in 35s
CI / Does this change touch the recording path? (pull_request) Successful in 35s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Docker image build (pull_request) Successful in 1m16s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m26s
CI / Bot tests and audit (pull_request) Successful in 4m9s
CI / Frontend (shard 1/2) (pull_request) Successful in 10m17s
CI / End-to-end journeys (desktop) (pull_request) Successful in 8m26s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 8m25s
CI / Frontend (shard 2/2) (pull_request) Successful in 15m43s
CI / End-to-end journeys (phone) (pull_request) Successful in 17m2s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
0efb756e99
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Author
Collaborator

This PR now carries more than the rank-name fix. The sheet page's render check (PR 9) wrote the built-in fixtures through these routes and found two more places where the server refused data the evaluator accepts:

  • <resource>.max as data, for a resource whose maximum is typed by hand (a creature's hp.max). This is PR 7's rule, taken exactly (legacy.spec_for), so the two branches merge cleanly. .current and .temp stay out of data.
  • Entry-row overrides accept any JSON value, objects included (PF2e runes), capped at 20,000 bytes each. EntryAdd also takes overrides.

The new test: tests/sheets/test_fixtures_through_the_server.py writes every built-in fixture (all 21: PF2e, 5e and generic) through the routes: POST, then PATCH overrides, then resources, with gone rows made genuinely gone. It asserts the server's evaluated values equal each fixture's expectations. It would have caught all three problems, and it keeps the server's validation and the fixtures from drifting apart.

Checks: tests/sheets plus the lore-restore and wiki-page tests pass (578), and ruff is clean, including alembic/.

This PR now carries more than the rank-name fix. The sheet page's render check (PR 9) wrote the built-in fixtures through these routes and found two more places where the server refused data the evaluator accepts: - **`<resource>.max` as data**, for a resource whose maximum is typed by hand (a creature's `hp.max`). This is PR 7's rule, taken exactly (`legacy.spec_for`), so the two branches merge cleanly. `.current` and `.temp` stay out of `data`. - **Entry-row overrides accept any JSON value**, objects included (PF2e runes), capped at 20,000 bytes each. `EntryAdd` also takes `overrides`. **The new test:** `tests/sheets/test_fixtures_through_the_server.py` writes **every built-in fixture (all 21: PF2e, 5e and generic)** through the routes: POST, then PATCH overrides, then resources, with gone rows made genuinely gone. It asserts the server's evaluated values equal each fixture's expectations. It would have caught all three problems, and it keeps the server's validation and the fixtures from drifting apart. **Checks:** `tests/sheets` plus the lore-restore and wiki-page tests pass (578), and ruff is clean, including `alembic/`.
Lane 11: the server keeps what the built-in fixtures hold — a hand-typed resource max, object overrides on entry rows; every fixture through the routes (#737)
Some checks failed
CI / Bot/backend version sync (pull_request) Successful in 2m34s
CI / Docker image build (pull_request) Successful in 2m34s
CI / Does this change touch the recording path? (pull_request) Successful in 2m29s
CI / Backend lint (ruff) (pull_request) Successful in 2m38s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m46s
CI / Feedback site tests (pull_request) Successful in 3m0s
CI / End-to-end journeys (phone) (pull_request) Failing after 13m9s
CI / End-to-end journeys (keyboard) (pull_request) Failing after 13m10s
CI / End-to-end journeys (desktop) (pull_request) Failing after 13m11s
CI / Bot tests and audit (pull_request) Failing after 15m49s
CI / Frontend (shard 2/2) (pull_request) Failing after 15m51s
CI / Frontend (shard 1/2) (pull_request) Failing after 15m52s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
570080217c
<resource>.max is accepted exactly as PR 7 does it (legacy.spec_for, taken
from build/lane11-7-convert as-is so the branches merge cleanly).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into fix/lane11-rank-names
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m23s
CI / Bot tests and audit (pull_request) Successful in 4m24s
CI / Backend lint (ruff) (pull_request) Successful in 1m11s
CI / Feedback site tests (pull_request) Successful in 1m50s
CI / Bot/backend version sync (pull_request) Successful in 58s
CI / Docker image build (pull_request) Successful in 36s
CI / Does this change touch the recording path? (pull_request) Successful in 40s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Frontend (shard 2/2) (pull_request) Successful in 14m29s
CI / Frontend (shard 1/2) (pull_request) Successful in 19m43s
CI / End-to-end journeys (phone) (pull_request) Successful in 15m59s
CI / End-to-end journeys (desktop) (pull_request) Successful in 18m4s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 18m41s
CI / Backend migration, tests, and audit (pull_request) Successful in 49m27s
828caf72b7
Sign in to join this conversation.
No description provided.