Quick 260927-q23 (CR-01), unit coverage that runs under -short.
- bouncer: TestRefreshAudienceForSubject covers active, pre/post cutoff,
missing, nil provider, non-numeric sub, provider error, and proves
token-only refusals never reach the provider
- bouncer: TestJWTGuardTokensValidAfter pins the unchanged "User not found"
message and errors.Is(err, ErrSubjectRejected)
- cabana: TestPhase10Coverage subtest pins cookie expiry on subject
refusals, no cookies over Bearer or on a provider error, and the
post-cutoff success path
Fixes review finding CR-01 (quick 260927-q23): POST {prefix}/api/v1/auth/refresh
minted a new token without loading the admin, so a session kept alive by the
SPA's refresh-on-401 survived admin:reset-password, deactivation and deletion.
This broke Phase 9 truth T-09-04.
- bouncer: extract the JWT guard's subject lookup into subjectPrincipal and
issuedBeforeCutoff (same order and messages), add ErrSubjectRejected
- bouncer: add RefreshAudienceFor, which runs the guard's subject checks
after the token-only checks and before minting; Refresh and
RefreshAudience are unchanged (nil hook)
- cabana: share one lazyBackendUsers provider between the backend guard and
refresh; a cookie refresh refused for its subject expires summer_admin
- test: TestAdminRefreshRevocation (Postgres, real admin:reset-password)
- 10-SECURITY-REVIEW.md: T-10-01..T-10-25 and T-10-SC with mitigation,
test or gate stage, observed result, residual risk and the removal
(mutation) checks behind every high threat
- 10-VALIDATION.md: executed task commands, gate statuses, Wave 0 done,
nyquist_compliant after scripts/check-phase10.sh --all passed
- --hygiene refuses localStorage, sessionStorage, indexedDB or document.cookie
outside admin/src/state/useSidebar.ts (T-10-22)
- --self-test plants each violation with a scratch test import, so the
refusal must come from that rule and not from the untested-module check
- scripts/check-phase10.sh with --self-test, --go, --security, --postgres,
--spa, --openapi, --dist, --hygiene, --evidence and --all
- phase10_detect refuses failed, skipped, zero-test, non-JSON and build-failed
go test runs and named tests that did not pass
- the two known fonoteka parity failures are the only allow-listed ones and
refuse the gate once they pass again
- hygiene enforces the framework/app boundary, SC-4 alias-only API types,
typed-client-only HTTP, no raw HTML, same-origin dist, named lucide imports,
no retired admin prefix routes and a test import for every SPA module
- 41 unit and component suites under admin/tests/{app,state,shell,list,form,relation,views,ui}
covering states and a11y roles; every src module is imported by a test
- typed fixture helper assigns each JSON fixture to its generated OpenAPI type
- fix: iconFor ignores inherited object members such as "constructor"
- fix: field controls import ./control instead of the registry (import cycle
left a renderer unregistered depending on module load order)
- fix: dropdown shows the placeholder for an unknown stored value next to an emptyOption
- fix: list announces a failed schema load even when the rows arrive after it
- tailwind no longer scans admin/tests; boardwalk/dist rebuilt
- useSidebar: collapsed below 1100px (matchMedia) or by the admin's choice,
persisted as a boolean under summer-admin.sidebar; the viewport never
overwrites the stored choice
- SectionPanel collapse and rail expand buttons; SectionFlyout (role menu)
opens on hover, focus, Enter or ArrowDown on a rail item, closes on Esc or
about 200 ms after leaving and returns focus to the rail item
- Breadcrumbs with plugin, controller and record crumbs; UserMenu (Reka
DropdownMenu) with initials, name, role and Wyloguj
- useAuth.logout POSTs /auth/logout, clears user, navigation and settings
and routes to login even when the call fails
- applyColorScheme toggles .dark from prefers-color-scheme (no toggle, A6)
- newest toast first, 200 ms fade and scale for dialogs and toasts
- one relation-manager type constant in the registry
- shell smoke tests; tests default to a desktop, light matchMedia
- relation-manager registered in the field registry; renders only on an
existing record, never on create, and is never part of the save body
- RelationManager: relation schema label and comment, debounced search,
selectable linked list (DataTable relation variant), toolbar buttons in
declared order, confirmed unlink with plural messages and toasts
- RelationPickerModal: Reka Dialog (aria-modal, focus trap, Esc) over the
candidates endpoint five per page, selection kept across pages, Dodaj (N)
POSTs link, focus returns to the opener
- admin OpenAPI documents search, sort, dir, page and per_page on the linked
and candidate relation routes so the SPA sends them typed
- neutral acme.demo.widgets members fixtures and relation smoke tests
- FormTabs groups fields by tab (untabbed fields in the default tab) as a
segmented tablist; a tab holding invalid fields after a 422 shows a
count badge and the form switches to the first invalid field
- switch, checkbox and relation join the renderer registry: toggle cards
(role=switch, 20px checkbox) keep a numeric value numeric; relation
fields are read-only labels from meta.labels, a searchable single
select over fields/{field}/options with emptyOption first, or ordered
removable chips with an appending search (300 ms debounce, 20 per
page, more on scroll or the more action)
- FormView gets the back button, record title and update subtitle, a
sticky footer (Usuń with deleteConfirm then DELETE, Anuluj, Zapisz i
zamknij to the mapped redirectClose, Zapisz), and asks before leaving a
dirty form on any route change plus a beforeunload guard
- Settings: the rail pins Ustawienia to the bottom when /settings is
non-empty; /settings lists pages by category; /settings/:code renders
the settings schema through FormGrid and the registry, PUTs the values
and maps a 422 like the record form
- New backend::lang keys (form.more_options, tab_default, discard,
settings.back); form and settings smoke tests; boardwalk/dist rebuilt