docs(spec): amend §8 severity-map + §6 stories-location + §8.X enforcement
- §8 severity table now covers all 21 values of the 5 mirrored enums (ShiftAssignment/ArtistEngagement/Payment/Person/MatchStatus); drops 3 phantom rows (active/inactive/expired, in no enum). Closes the silent grey-fallback gap for 11 production values found in the Plan 3 brainstorm self-audit. - §6 stories-placement reworded: custom/wrapper components (incl. PrimeVue wrappers) co-locate; only the ~80 PrimeVue catalog + Foundations centralize. Plan 2 misread this. - New §8.X: bidirectional Vitest consistency test (apps/app/tests/unit/utils/statusSeverity.consistency.spec.ts), added in Plan 3 — fails on any unmapped enum value OR orphan key. Plan 3 cleanup tasks (tracked in the Plan 3 plan doc, not here): (a) migrate Plan 2's 6 centralized stories to co-located. (b) refactor AppTopbar to wrap PrimeVue Menubar per RFC AD-3. Background: discovered during Plan 3 (Tier-1 primitives) brainstorm self-audit. Fixes spec-vs-reality drift and two Plan 2 deviations from binding spec/RFC text; prevents recurrence for future enums. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -281,9 +281,28 @@ Custom components are first-class in Storybook exactly like PrimeVue
|
|||||||
ones. crewli-starter `ComponentsPage.vue` (~80 PrimeVue components) is
|
ones. crewli-starter `ComponentsPage.vue` (~80 PrimeVue components) is
|
||||||
the **source of truth for the standard-component catalog**.
|
the **source of truth for the standard-component catalog**.
|
||||||
|
|
||||||
**Story placement:** custom/wrapper stories co-located next to the
|
**Story placement (binding — two disjoint classes):**
|
||||||
`.vue` (moves with the file at cutover); PrimeVue standard catalog and
|
|
||||||
Foundations centralized under `stories/`.
|
1. **Custom/wrapper components.** Every component authored in this repo
|
||||||
|
— shell pieces (`AppSidebar`, `AppTopbar`, `WorkspaceSwitcher`,
|
||||||
|
`RightDrawer`, …), Tier-1 primitives (`StatusTag`, `StatCard`,
|
||||||
|
`StateBlock`, `PageHead`, `TagsInput`, `EnergyDots`, `EnergyPicker`),
|
||||||
|
`DraggableBlock`, and the template layer — **including components
|
||||||
|
that internally wrap a PrimeVue component**. Their `.stories.ts` is
|
||||||
|
**co-located next to the `.vue`** so it moves with the component at
|
||||||
|
cutover and is deleted with it. This is the default for essentially
|
||||||
|
all `components-v2/**` work.
|
||||||
|
2. **PrimeVue standard catalog + Foundations.** The ~80-component
|
||||||
|
pure-PrimeVue demo gallery (source of truth: crewli-starter
|
||||||
|
`ComponentsPage.vue`) and the Foundations stories
|
||||||
|
(Color/Typography/Spacing/Icons/Dark/Density) — neither has an owning
|
||||||
|
`.vue`. These are **centralized under `stories/`**.
|
||||||
|
|
||||||
|
"Wraps a PrimeVue component" does **not** reclassify a component into
|
||||||
|
the catalog — a wrapper is still custom and co-locates. (Plan 2 misread
|
||||||
|
the prior wording and centralized 6 shell stories under
|
||||||
|
`src/stories/v2/`; Plan 3 carries a cleanup task to migrate them — see
|
||||||
|
commit body.)
|
||||||
|
|
||||||
**Story tree:** `Foundations/` (Color, Typography, Spacing, Icons, Dark
|
**Story tree:** `Foundations/` (Color, Typography, Spacing, Icons, Dark
|
||||||
mode, Density) · `PrimeVue/` (~80, grouped as in ComponentsPage) ·
|
mode, Density) · `PrimeVue/` (~80, grouped as in ComponentsPage) ·
|
||||||
@@ -395,18 +414,42 @@ knowledge, not a per-page decision** — it lives in one map
|
|||||||
|
|
||||||
| Status value(s) | `severity` | Semantics |
|
| Status value(s) | `severity` | Semantics |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `approved`, `confirmed`, `completed`, `active` | `success` | terminal-good / active |
|
| `approved`, `completed`, `confirmed`, `contracted`, `paid_in_full` | `success` | terminal-good / fully settled |
|
||||||
| `pending`, `pending_approval`, `invited` | `warn` | awaiting action |
|
| `pending_approval`, `pending`, `applied`, `option`, `offered`, `reverted` | `warn` | organizer action required |
|
||||||
| `rejected`, `cancelled`, `declined` | `danger` | terminal-bad |
|
| `invited`, `requested`, `deposit_paid` | `info` | awaiting external party / in-progress — no viewer action |
|
||||||
| `draft` | `secondary` | not yet live |
|
| `none`, `draft`, `dismissed` | `secondary` | muted — absent / not-yet-live / archived |
|
||||||
| `inactive`, `expired` | `secondary` (muted) | dormant |
|
| `rejected`, `cancelled`, `declined`, `no_show` | `danger` | terminal-bad |
|
||||||
| (unmapped fallback) | `info` | + dev-warn so gaps surface |
|
| (unmapped at runtime) | renders `info` + dev console warn | unreachable in a passing build — §8.X consistency test fails on any gap |
|
||||||
|
|
||||||
Rule: **every** backend status enum mirrored into `src/types/` gets a
|
Rule: **every** backend status enum mirrored into `src/types/` gets a
|
||||||
row here in the same PR that adds the enum (extends the existing
|
row here in the same PR that adds the enum (extends the existing
|
||||||
"mirror backend PHP enums" project rule). `StatusTag` never inlines a
|
"mirror backend PHP enums" project rule). `StatusTag` never inlines a
|
||||||
severity; it always resolves through this map.
|
severity; it always resolves through this map.
|
||||||
|
|
||||||
|
### 8.X Enforcement (binding)
|
||||||
|
|
||||||
|
The §8 severity map is mechanically enforced by a single Vitest unit
|
||||||
|
test, `apps/app/tests/unit/utils/statusSeverity.consistency.spec.ts`,
|
||||||
|
added in Plan 3 alongside `statusSeverity.ts`. It imports the live enum
|
||||||
|
modules (`ShiftAssignmentStatus`, `ArtistEngagementStatus`,
|
||||||
|
`PaymentStatus`, `PersonStatus`, `MatchStatus`) and asserts **both**
|
||||||
|
directions:
|
||||||
|
|
||||||
|
1. **Completeness** — every value of every listed enum resolves to an
|
||||||
|
explicit severity in `statusSeverity.ts`, never the dev-fallback.
|
||||||
|
Guards the failure mode that left 11 values silent-falling to grey
|
||||||
|
`info`.
|
||||||
|
2. **No phantoms** — every key in `statusSeverity.ts` corresponds to a
|
||||||
|
value present in at least one listed enum. Guards the inverse mode
|
||||||
|
(the original table mapped `active`/`inactive`/`expired`, which exist
|
||||||
|
in no enum).
|
||||||
|
|
||||||
|
Adding a new mirrored enum, or a new value on an existing one, requires
|
||||||
|
extending **both** `statusSeverity.ts` and this test's enum-list in the
|
||||||
|
**same commit**. A silent fallback or an orphan key is a test failure
|
||||||
|
that blocks CI, not a convention. This extends the existing "mirror
|
||||||
|
backend PHP enums" rule (CLAUDE.md) with a mechanical gate.
|
||||||
|
|
||||||
**Tier-2 — Smart Filter subsystem (secure generic version now):**
|
**Tier-2 — Smart Filter subsystem (secure generic version now):**
|
||||||
`SmartFilterBar` + `FilterChip` + `FilterPopover` + `AddFilterMenu` +
|
`SmartFilterBar` + `FilterChip` + `FilterPopover` + `AddFilterMenu` +
|
||||||
5 editors (Text / NumberRange / MultiSelect / EnumGrid / Tag) +
|
5 editors (Text / NumberRange / MultiSelect / EnumGrid / Tag) +
|
||||||
|
|||||||
Reference in New Issue
Block a user