fix: UX Core functional audit fixes + OffSec bias cases - #181
Conversation
…ter, timeline) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ail surfaces Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Cybersecurity row and the #offsec hash handler in UXCoreLayout were ungated, so merging would have leaked the dev-only OffSec layer to prod in a half-broken state (row active, modal showing HR). The gate is back as a single shared isOffsecEnabled helper used by layout and modal. initUseUXCoreGlobals had zero callers: every localStorage write of the PM/HR choice was dead weight and each reload reset to PM. It is now idempotent (absolute values, safe on every mount), gates the OffSec flag on isOffsecEnabled, and runs on /uxcore mount with an explicit URL hash winning over the restored state. Also adds the machine-readable font passport to CLAUDE.md (commit gate requirement). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… cache TTL - Back/Forward now lands on the right bias: modal state follows the route prop instead of freezing at mount, so the stuck-dimmed modal is gone. - Use-case switch inside the modal is a shallow no-scroll hash update (was a full ISR refetch + jump to top), and OffSec state writes #offsec instead of mislabeling the URL as #hr. - Share previews: og:image/twitter:image static fallback is absolute (scrapers drop relative URLs), the Strapi ogImage path lost its bogus double .OGTags nesting, twitter:url is the canonical URL (was a hardcoded domain with a double slash), and social share links are URL-encoded so hashes/ampersands in titles survive. - getStrapiBiases: 5-minute TTL instead of a permanent module cache, and an empty/incomplete response is never cached — a transient Strapi failure no longer 404s all bias pages until restart, and ISR revalidation actually picks up content edits. - Rating: thanks-state only after the vote lands (failed vote stays retryable), double-click guard, response.ok check, and a failed geo lookup no longer aborts the vote. - mentionedQuestionsIds parse failure degrades to no mentions instead of crashing the bias page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- A search with zero hits now renders as empty (labels faded, map filtered to nothing) instead of silently falling back to the full library while claiming 'found 0'. useBiasSearch carries an explicit isSearchActive flag so empty-query and zero-hit are distinguishable. - Clearing the input cancels the in-flight debounce, so a pending search can no longer repopulate results over a cleared field. - getSearchLabels indexed a locale-keyed object numerically and always returned undefined: EN plural 's' and RU declensions never rendered. It now takes the locale. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- addPersona/updatePersona/deletePersona built auth headers at module load, so logging in after page load kept sending 'Bearer null'. Headers are now resolved per request, and non-2xx responses throw. - Save/overwrite no longer flip to 'saved' optimistically: the state is confirmed only after the request lands, and a failure surfaces the new localized saveFailed message via the existing error slot. - A failed delete keeps the persona in the list instead of hiding an entry the server still has. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… server - A dropped submit no longer advances the test: the answer is confirmed by the server before moving on. On failure the question stays with a localized retry message and the button re-enables, so a network blip means a retry instead of a wrong score or a test finished with unrecorded answers. - Double submits closed: an in-flight ref guards clicks, and Enter now mirrors the Next button's disabled state instead of firing the same question twice. - Resuming a test uses the server's ongoingTest.isFinal instead of a localStorage flag, so a 30-question final resumed in another browser (or after cleared storage) keeps its real length instead of ending at question 10 with negative numbering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@claude review this PR |
# Conflicts: # src/uxcore/api/biases.ts # src/uxcore/components/UXCoreModal/UXCoreModal.tsx # src/uxcore/hooks/useUXCoreGlobals.ts # src/uxcore/layouts/UXCoreLayout/UXCoreLayout.tsx # src/uxcore/lib/offsec.ts
|
Claude finished @manager's task in 4m 36s —— View job Reviewing PR #181
SummaryReviewed against
Findings (posted inline)
No security, hydration, or SSR issues found (all Note: as the PR description says, the branch is behind |
| {card.items.map((item, i) => ( | ||
| <div | ||
| key={i} | ||
| className={`${styles.timelineItem} ${ |
There was a problem hiding this comment.
Convention: this component builds className with raw template-string concatenation instead of the classnames helper (cn), which AGENTS.md requires for conditional classes ("Always use classnames (imported as cn)"). Applies here and at lines 299, 310–313, and 315.
className={cn(styles.timelineItem, { [styles.timelineItemFlagged]: item.flagged })}| ) => { | ||
| // Geo enrichment is best-effort: a failed /api/user lookup must not | ||
| // abort the vote itself. | ||
| let userData: any = {}; |
There was a problem hiding this comment.
let userData: any = {} reintroduces an explicit any. Since only country, region, city, ip are destructured from it, a small inline type (or a shared TUserGeo type matching /api/user's response) would keep this typed without much overhead.
|
|
||
| .submitError { | ||
| text-align: center; | ||
| color: #fb1717; |
There was a problem hiding this comment.
New hardcoded color #fb1717 for the submit-error text — this file already carries legacy hardcoded hex values, but per AGENTS.md "new code aligns to the keepsimple-style skill" / "Do not invent colors". Worth pulling from the design tokens (or an existing error/crimson variable, e.g. the $ks-crimson family used in OffsecBiasView.module.scss) instead of a fresh literal.
| // the lever is the pattern itself (frequency illusion), not any single | ||
| // artifact. | ||
| export interface OffsecBiasTimelineCard extends OffsecBiasCardCommon { | ||
| kind: 'timeline'; |
There was a problem hiding this comment.
Flagging per repo policy rather than blocking: this extends the OffSec bias-card schema with four new kinds (call, document, poster, timeline) and 10 new bias entries under src/uxcore/data/biasOffsec/. This is local static content (not the canonical Strapi UX Core corpus that CLAUDE.md's "UX Core data is canonical" section governs), and it's correctly gated behind isOffsecEnabled (dev-only) — but since it's still bias-schema content, please confirm this had the explicit sign-off the repo convention calls out for bias data/schema changes before merging to dev.
fix(uxcore): address PR #181 review comments
What this does
Functional audit fixes across UX Core, plus new OffSec bias content, gated to dev preview.
Fixes
#offsecinstead of mislabeling the URL as#hr..OGTagsnesting on the Strapi path,twitter:urlis the canonical URL, share links URL-encoded so hashes and ampersands in titles survive.getStrapiBiasesgets a 5-minute TTL instead of a permanent module cache, and never caches an empty/incomplete response — a transient Strapi failure no longer 404s every bias page until restart, and ISR picks up content edits.response.okcheck, failed geo lookup no longer aborts the vote.getSearchLabelstakes the locale, so EN plurals and RU declensions actually render.mentionedQuestionsIdsparse failure degrades to no mentions instead of crashing the bias page.initUseUXCoreGlobalshad zero callers (every persisted PM/HR choice was dead weight, each reload reset to PM); now idempotent and wired on/uxcoremount, with an explicit URL hash winning over restored state.OffSec
#offsechash handler behind one sharedisOffsecEnabledhelper; a persistedisOffsecViewflag is treated as inactive off-dev, so a prior dev session cannot leak the half-finished layer onto staging or prod.Housekeeping
CLAUDE.md(commit gate requirement).Notes for review
dev; merge or rebase before landing.