diff --git a/docs/qa/platform-checklist/RUNNER.md b/docs/qa/platform-checklist/RUNNER.md index c0e1e82b94..62e3c61a5c 100644 --- a/docs/qa/platform-checklist/RUNNER.md +++ b/docs/qa/platform-checklist/RUNNER.md @@ -338,6 +338,14 @@ contradicts it, and correct it here when it does. | `wrong-panel` | feature looks missing on a sibling surface | item's `steps` name the exact surface; check it | | `wrong-persona` | admin privileges mask a guard | run guard checks as the non-privileged persona | | `absence-inference` | a missing flag/key/script read as a missing capability | follow the forwarding chain to where the default is actually decided, before writing the finding down. A scaffold's bare `objectstack dev` still serves the console: `dev` forwards `--ui` only when set and never forwards a negation, and the default is decided one hop downstream by `serve`'s own `ui` flag (`default: true, allowNo: true`) — so absence means ON. ⛔ The off switch `--no-ui` is `serve`'s and `start`'s; `dev` has no such flag and rejects it (environment facts above) | +| `auth-state-leak` | a per-persona matrix scored against ONE identity — the previous persona's credentials survived the switch, so a gate reads as holding (or as fallen open) for a caller that never made the request | prove the identity server-side before each cell (the session row, or `GET /auth/get-session`), never from the gesture that was supposed to switch it; give each persona its own session. ⚠️ Both halves have to go: the console's `localStorage` bearer token survives a `clearCookies()` gesture and a form sign-in ALSO sets `better-auth.session_token` (environment facts above), so an "anonymous" or "non-admin" cell can silently still be the admin's | +| `cache-staleness` | a read answered from a cache instead of re-resolved: the row this run just planted or edited reads back unchanged, or one persona's body is replayed for the next | before scoring, confirm the response carries something this run set (the planted spelling, the new `audience`); re-fetch with a client of its own per persona rather than reusing a response. ⚠️ Two independent read doors disagreeing may be one door's cache rather than the drift the item is hunting | +| `eventual-consistency` | a deferred write read before it settles — an effect that has not landed yet reads as a refusal, and a state the reconciler is about to fill reads as durable | prove the state from its own record (the session row / session payload), never from the symptom you are about to test; prefer the durable producer over the racy one and record which the verdict rests on; re-read after a settle window before writing an absence down | +| `clock-skew` | a relative window resolved against a different instant than the expectation was computed from — a run that spans a rollover, or a server clock that is not the runner's | record the wall clock BEFORE the first request and derive every expectation from that one instant; reconcile each preset against a literal-window query computed from the same clock, and re-run any window whose boundary the run crossed | +| `timezone-boundary` | rows near midnight or a period edge landing on the other side of a calendar window, because the window is anchored in a different zone than the runner assumed | record the zone alongside the clock and compute the expected window in the zone the door resolves in; score against the literal-window query, never against an intuition about "today" | +| `silent-coercion` | a 200 that reads as the contract holding while the input was converted across types — or the predicate dropped entirely, so the request succeeds meaning something other than what was authored | never score on status alone: compare what came back against what was sent, and the returned row count against the UNFILTERED count — a dropped predicate answers over the full table, which is the dangerous direction | +| `destructive-in-place` | a clean re-run — the first run already rewrote the input it is judged against, so the second observation describes a different tree | run against a scratch copy / a database you own; checksum (or snapshot) the input before and after and cite the pair; check which arm actually writes before replaying — a preview arm is read-only only until `--apply` | +| `first-boot-cold-start` | a 404 / empty body / white screen from a first boot still doing its one-time work, read as the first-run-only defect the item exists to hunt | read the boot log and wait for the server's own serving line before the first probe: a never-built tree compiles on the way up (`dev` spawns `os compile` when no `dist/objectstack.json` exists) and seeds admin on a zero-user DB, neither of which a warm re-boot repeats. ⛔ Do not write a first-run defect down before re-probing a warm boot | ## Run records — the GitHub issue is the report