Skip to content

fix(web): stop calling revalidatePath during render on first sign-in - #697

Merged
mthines merged 2 commits into
mainfrom
fix/first-signin-token-revalidate
Oct 17, 2026
Merged

mthines merged 2 commits into
mainfrom
fix/first-signin-token-revalidate

Conversation

@mthines

@mthines mthines commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Why

First sign-in on /lore minted a token inside a Server Component render by calling the 'use server' generate action, whose success path calls revalidatePath — Next 15 forbids that during render. A bare catch {} swallowed the resulting throw, leaving an orphan api_tokens row, no token shown, and a logged lorekit.api_token.generate.failed.

What changed

  • Extracted the mint core (insert, scoping, audit — no revalidate) into a new server-only module, lib/token-mint.ts.
  • /lore's render now calls it through a new pure decision module, lib/first-token.ts (resolveFirstToken), which replaces the bare catch with unstable_rethrow-aware degradation.
  • lib/tokens.ts's generateToken action is now a thin wrapper: mint, then revalidate /lore + /settings layout, on success only — used solely by the dashboard's "Generate token" button.
  • Added lorekit.api_token.origin (onboarding|dashboard) to the generate span so prod can prove the fix.

How to verify

cd packages/web && pnpm exec vitest run --config vitest.config.ts src/lib/onboarding-steps.spec.ts src/lib/first-token.spec.ts src/lib/token-mint.spec.ts src/lib/tokens.spec.ts

onboarding-steps.spec.ts pins the fix itself: it fails if the /lore render goes back to generateToken (mutation-tested). token-mint.spec.ts asserts the lorekit.api_token.origin span attribute.

Notes for reviewers

Manual verification against local Supabase (brand-new user, confirm one token + no orphan row) was attempted but local Supabase's DB container would not come up in this sandbox — recorded inconclusive. The unit specs pin the actual mechanism of the bug (no revalidate during the render-path mint).

Out of scope:

  • Users who already hit the bug keep their orphan "My first token". It shows in Settings, where they can revoke it. Because they now have a token, the auto-mint skips them.
  • A brand-new user opening /lore in two tabs at once can still get two tokens. That was already possible before this change.

No user-facing doc change: internal fix, no behaviour a user configures.

buildOnboardingSteps({ autoGenerateToken: true }) (rendered inside /lore's
Suspense boundary) called the 'use server' generateToken action, whose
success path calls revalidatePath — Next 15 throws during render for that,
the bare catch{} swallowed it, and the user was left with an orphan
api_tokens row and no token on screen.

Split the mint core (insert + scoping + audit, no revalidate) into a new
server-only lib/token-mint.ts. generateToken (lib/tokens.ts) now wraps it
and revalidates only on success, from the action path. The render path
goes through a new lib/first-token.ts (resolveFirstToken), which calls
mintToken directly and never revalidates — the render already returns the
freshly minted record as initialTokens, so nothing cached is stale.

Adds lorekit.api_token.origin (onboarding|dashboard) to the generate span
so prod can prove the fix. Pins both paths with node-env specs.
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

The dashboard preview for this PR — redeployed on each push that changes the web app.

Project Deployment Actions Updated
lorekit Ready Ready Preview Oct 17, 2026 12:05pm UTC

Preview always points at this PR's latest commit; Deployment is the immutable build of 7834620. · Comment /web-preview to force a redeploy. · Workflow logs

@mthines

mthines commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner Author

⚠️ No findings — 1 gate needs attention

Both earlier test gaps are closed: a new onboarding spec pins the render to mintToken, and the origin span attribute is now asserted. No new findings in the delta.

Warnings: How to verify omits onboarding-steps.spec.ts, the new spec that pins the render-path fix

Checked: 6 of 10 changed files read · 6 of 10 dependent files traced · no possible issues found

What this change reaches — 10 changed exports · 10 dependent files · 6 checked · 4 not checked
flowchart LR
  subgraph pr["Changed in this PR"]
    s1["TokenPermission<br/>removed"]:::changed
    s2["ApiToken<br/>removed"]:::changed
    s3["buildOnboardingSteps<br/>body changed"]:::changed
    s4["revokeToken<br/>body changed"]:::changed
    s5["mintToken<br/>added"]:::changed
    s6["ApiToken<br/>added"]:::changed
    smore["+4 more changed exports<br/>listed below"]:::changed
  end
  s1c1["packages/web/src/components/dashboard/ · 2 files<br/>✓ checked"]:::ok
  s1c2["packages/web/src/lib/token-permission.ts<br/>✓ checked"]:::ok
  s2c1["packages/web/src/components/dashboard/ · 2 files<br/>✓ checked"]:::ok
  s3c1["packages/web/src/app/(dashboard)/lore/page.tsx<br/>✓ checked"]:::ok
  s3c2["packages/web/src/lib/onboarding-steps.spec.ts<br/>✓ checked"]:::ok
  s4c1["packages/web/src/components/dashboard/TokenManager.tsx<br/>? not checked"]:::unknown
  s5c1["packages/web/src/lib/ · 4 files<br/>✓ checked"]:::ok
  s6c1["packages/web/src/lib/ · 3 files<br/>? not checked"]:::unknown
  s1 --> s1c1
  s1 --> s1c2
  s2 --> s2c1
  s3 --> s3c1
  s3 --> s3c2
  s4 -.-> s4c1
  s5 --> s5c1
  s6 -.-> s6c1
  classDef changed fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
  classDef ok fill:#e7f6ec,stroke:#16a34a,color:#14532d
  classDef partial fill:#fef9c3,stroke:#ca8a04,color:#713f12
  classDef unknown fill:#f3f4f6,stroke:#9ca3af,stroke-dasharray:4 3,color:#374151
  classDef bad fill:#fde8e8,stroke:#dc2626,color:#7f1d1d
  classDef warn fill:#fff4e5,stroke:#d97706,color:#78350f
  classDef imports fill:#f8fafc,stroke:#64748b,stroke-dasharray:4 3,color:#1e293b
Loading
  • TokenPermission (packages/web/src/lib/tokens.ts) — removed change · 3 consumer files · 3 verified unaffected
  • ApiToken (packages/web/src/lib/tokens.ts) — removed change · 2 consumer files · 2 verified unaffected
  • buildOnboardingSteps (packages/web/src/lib/onboarding-steps.tsx) — body change · 2 consumer files · 2 verified unaffected
  • revokeToken (packages/web/src/lib/tokens.ts) — body change · 1 consumer file · 1 not traced
  • mintToken (packages/web/src/lib/token-mint.ts) — added change · 4 consumer files · 4 verified unaffected
  • ApiToken (packages/web/src/lib/token-mint.ts) — added change · 3 consumer files · 3 not traced
  • resolveFirstToken (packages/web/src/lib/first-token.ts) — added change · 2 consumer files · 1 verified unaffected · 1 not traced
  • applyScoping (packages/web/src/lib/token-mint.ts) — added change · 1 consumer file · 1 not traced
  • TOKEN_COLUMNS (packages/web/src/lib/token-mint.ts) — added change · 1 consumer file · 1 not traced
  • TokenPermission (packages/web/src/lib/token-mint.ts) — added change · 1 consumer file · 1 not traced
Review details

Needs attention

Gate Status Details
Description vs. code ⚠️ How to verify omits onboarding-steps.spec.ts, the new spec that pins the render-path fix.
Prior review feedback ✅ Earlier review comments are resolved.
Documentation ✅ Module headers explain the bug and the fix; key-files.md and otel.md updated.
Self-review signals ✅ No debug logs, leftover TODOs, or unreviewed stubs.
Code review ✅ The multi-lens review found no blocking issues.

Found

Quality — produced 0 → posted inline 0 · cleared 0 · carried forward 0 · deferred 0 · below-bar 0
Optimality — ran — 0 proposals; the server-only mint core is the standard fix for revalidatePath during render
Standards — ran — 2 governing docs scanned, 0 findings
Measurability — ran — web path, no new failure mode in the delta; earlier missing-signal advisories unchanged

Run

full · 86 lines in delta · tier deep · depth checkout
⚠️ no sub-agent dispatch available — the intent finder ran in-context with the other finders at effective thoroughness 0.95, instead of as its own sub-agent — 1 prepare-time anomaly (no --state file supplied — routing computed with lastFullSha=none, incrRunsSinceFull=0 (forces the D6 'no prior deep pass on record' trigger every run); pass --state after reading the LoreKit state record for an accurate routing decision)
Integrations — not activated (no manifest or lockfile in the diff)
Memories — repo::mthines/lorekit + global — 0 lessons matched; 1 knowledge fact + 1 hotspot re-verified, no relevance rule fired

Nothing to report — severity, 0 files skipped.

pr-reviewer · commit 7834620 · full review · how these findings are produced · updated Oct 17, 2026 12:17pm UTC

Comment thread packages/web/src/lib/token-mint.spec.ts Outdated
Comment thread packages/web/src/lib/token-mint.spec.ts
…in span attribute

The revalidatePath assertions in token-mint.spec could not fail (token-mint
never imports next/cache). onboarding-steps.spec now fails if the render goes
back to generateToken, and the origin test reads the span attribute. Also
correct first-token's comment: expected refusals are not logged.
@mthines
mthines marked this pull request as ready for review October 17, 2026 12:51
@mthines
mthines merged commit 25b0dbc into main Oct 17, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant