Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | 1x 8x 2x 2x 4x | /**
* Single source of truth for the dashboard placeholder-phase prep-state
* discriminants (issue #275).
*
* Mirrors `spec/programming.allium:27`
* enum PrepState { in_flight | replanning | stalled | unknown }
* and the SECURITY DEFINER `get_dashboard_prep_state` SQL function
* (`supabase/migrations/20260516153245_add_get_dashboard_prep_state.sql`), whose
* priority order is stalled > replanning > in_flight > unknown (first match wins).
*
* Why a runtime tuple, not just a type union: the dashboard render branch
* (`+page.svelte`), the load-function validation (`+page.server.ts`), and the
* Playwright drift guard (`e2e/dashboard-prep-states.test.ts`) all derive from
* this one list. A `type`-only union is erased at runtime and cannot be
* iterated, so the drift guard could not assert "every discriminant has a
* seeded render assertion". The `as const` tuple gives both a compile-time
* exhaustiveness anchor (see `prepView`) and a runtime-iterable set.
*
* Conformance with the spec enum and the SQL function is by-eye — there is no
* codegen from the Allium spec. If the spec enum gains a member, add it here:
* `prepView`'s exhaustiveness guard then forces a render-branch decision at
* compile time, and the #275 drift guard forces a seeded E2E assertion.
*/
export const PREP_STATES = ['in_flight', 'replanning', 'stalled', 'unknown'] as const;
export type PrepState = (typeof PREP_STATES)[number];
/**
* Render category for the placeholder phase. Collapses the four discriminants
* to the three branches the dashboard actually renders:
* - `stalled` — terminal; oncall owns recovery (no Check-now, polling stops).
* - `replanning` — transient refusal, in-band retry in flight (keep Check-now).
* - `optimistic` — in_flight | unknown; the generating / preparing copy. The
* split between the pre-60s "generating" notice and the
* post-60s "preparing" fallback is time-based (the durable
* >60s threshold), NOT discriminant-based, so both share this
* category.
*/
export type PrepView = 'stalled' | 'replanning' | 'optimistic';
/**
* Map a `PrepState` discriminant to the render category the dashboard branches
* on. The `default` arm's `never` assignment is the exhaustiveness guard:
* adding a discriminant to `PREP_STATES` without a case here is a compile error
* (`pnpm check` / `tsc`), so a new state can never silently collapse to the
* optimistic copy.
*/
export function prepView(state: PrepState): PrepView {
switch (state) {
case 'stalled':
return 'stalled';
case 'replanning':
return 'replanning';
case 'in_flight':
case 'unknown':
return 'optimistic';
default: {
const _exhaustive: never = state;
return _exhaustive;
}
}
}
|