Skip to content

P1: fix(auth): improve OTP resend UX - #220

Merged
aspiers merged 8 commits into
mainfrom
split-pr165/clear-code-on-resend
Aug 4, 2026
Merged

P1: fix(auth): improve OTP resend UX#220
aspiers merged 8 commits into
mainfrom
split-pr165/clear-code-on-resend

Conversation

@aspiers

@aspiers aspiers commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Clear the previously entered verification code after a successful resend. The fresh code now starts with an empty control focused at its first position.

Changes

  • Reset OTP state after resend succeeds
  • Focus the first entry position
  • Add focused render coverage

Testing

  • pnpm format:check
  • pnpm lint
  • pnpm typecheck
  • pnpm test
  • pnpm test:coverage

Screenshots

Before: a successful resend left stale digits in the OTP boxes.

Before: resend retains stale digits

After: the old digits are cleared and focus returns to the first box. (This shot predates the later commits on this branch, so its banner shows the older copy and position; both are covered below.)

After: cleared OTP after resend

Errors at the point of failure

Captured against live Railway previews — PR #229's for the before, which still serves the old layout, and this branch's own for the after.

Before: a rejected code reported itself above the subtitle, several elements from the boxes the user had just typed into, with no way forward but the standalone Resend code button further down.

Before: error above the form

After: the message sits between the boxes and Verify, where the user is already looking. Because this particular failure invalidates the stored code server-side, it also carries an inline Resend code action — there is nothing left to retype. A plain mistyped code keeps the message without the action, since retyping is the right recovery there.

After: error at the point of failure

Resend confirmation

After: the confirmation acknowledges the click, then warns that earlier codes have stopped working and points at the spam folder — the two things most likely to be wrong for anyone who got as far as resending.

After: resend confirmation

Notes

Summary by CodeRabbit

  • Bug Fixes

    • Resending a sign-in code now clears entered digits and refocuses the first code field.
    • Users are informed that only the newest code is valid and should check their spam folder.
    • Sign-in errors appear beside the relevant email or verification-code field.
    • Invalidated or excessive-attempt codes offer an inline resend option.
  • Tests

    • Added coverage for resetting the verification form during expired-code recovery.

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
epds-demo Ready Ready Preview Aug 4, 2026 5:34pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aspiers, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 5 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 215f656a-a2c0-4343-81b7-b32a22bf6c61

📥 Commits

Reviewing files that changed from the base of the PR and between d3bab66 and ce3caef.

⛔ Files ignored due to path filters (2)
  • docs/screenshots/pr-232-before-low-contrast-muted-text.png is excluded by !**/*.png
  • docs/screenshots/pr-232-muted-text-contrast.png is excluded by !**/*.png
📒 Files selected for processing (3)
  • .changeset/sign-in-errors-at-point-of-failure.md
  • packages/auth-service/src/__tests__/login-page.test.ts
  • packages/auth-service/src/routes/login-page.ts
📝 Walkthrough

Walkthrough

The login flow now places errors beside the active field using a shared flash region that moves between email and OTP steps. Invalidated or exhausted OTP errors can offer an inline resend action when the flow is active. A successful resend clears and refocuses the OTP inputs, updates the guidance to state that earlier codes are invalid, and advises checking the spam folder. Unit and end-to-end tests validate these behaviors.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Changes

OTP login recovery

Layer / File(s) Summary
Shared flash region for step-specific errors
packages/auth-service/src/routes/login-page.ts
A single hidden live-region flash message moves between the email and OTP steps. The hidden class controls visibility. The moveFlashTo function reparents the region between step slots.
Detect fresh-code errors and offer resend
packages/auth-service/src/routes/login-page.ts
OTP errors matching "too many attempts" or "invalidated" trigger inline resend actions when the flow remains active. Expired-flow and dead-flow handling remain separately gated.
Reset OTP state and guidance after resend
packages/auth-service/src/routes/login-page.ts, .changeset/clear-otp-boxes-on-resend.md
Successful resends clear the OTP inputs, focus the first input, and state that earlier codes are invalid. The message directs users to check the spam folder.
Test OTP recovery, error placement, and resend behavior
packages/auth-service/src/__tests__/login-page.test.ts, e2e/step-definitions/auth.steps.ts, features/passwordless-authentication.feature, .changeset/sign-in-errors-at-point-of-failure.md
Tests validate configurable render options, flash placement in the active step, ordinary and invalidated OTP errors, resend action rendering, resend success messaging with spam guidance, OTP entry steps, and empty focused inputs after resend.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LoginPage
  participant OTPService
  User->>LoginPage: Submit OTP
  LoginPage->>OTPService: Verify OTP
  OTPService-->>LoginPage: Return invalidated-code or too-many-attempts error
  LoginPage-->>User: Move flash to OTP slot and render inline Resend code action
  User->>LoginPage: Select Resend code
  LoginPage->>OTPService: Request new OTP
  OTPService-->>LoginPage: Confirm resend
  LoginPage-->>User: Clear OTP inputs, focus first input, show spam-folder guidance
Loading

Possibly related PRs

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the pull request's main change: improving the OTP resend experience.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch split-pr165/clear-code-on-resend

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@railway-app

railway-app Bot commented Jul 30, 2026

Copy link
Copy Markdown

🚅 Deployed to the ePDS-pr-220 environment in ePDS

Service Status Web Updated (UTC)
@certified-app/auth-service ✅ Success (View Logs) Web Aug 4, 2026 at 5:12 pm
@certified-app/demo untrusted ✅ Success (View Logs) Web Aug 3, 2026 at 7:22 pm
@certified-app/demo ✅ Success (View Logs) Web Aug 3, 2026 at 7:21 pm
@certified-app/pds-core ❌ Build Failed (View Logs) Web Aug 3, 2026 at 7:21 pm

@changeset-bot

changeset-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ce3caef

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coveralls-official

coveralls-official Bot commented Jul 30, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 30934422228

Coverage increased (+0.06%) to 57.734%

Details

  • Coverage increased (+0.06%) from the base build.
  • Patch coverage: 1 of 1 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 3099
Covered Lines: 1783
Line Coverage: 57.53%
Relevant Branches: 1931
Covered Branches: 1121
Branch Coverage: 58.05%
Branches in Coverage %: Yes
Coverage Strength: 9.5 hits per line

💛 - Coveralls

@aspiers aspiers changed the title fix(auth): clear the OTP after resending P1: fix(auth): clear the OTP after resending Jul 30, 2026
Comment thread packages/auth-service/src/__tests__/login-page.test.ts Outdated
@blacksmith-sh

This comment has been minimized.

@aspiers
aspiers force-pushed the split-pr165/clear-code-on-resend branch from 50f4a5e to 619ae08 Compare July 30, 2026 16:17
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-220 July 30, 2026 16:50 Destroyed
@aspiers
aspiers marked this pull request as ready for review July 30, 2026 16:53
Copilot AI lite review requested due to automatic review settings July 30, 2026 16:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
e2e/step-definitions/auth.steps.ts (1)

628-635: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the old digits before testing the reset.

This makes the scenario prove that the boxes contained the old OTP prefix before resend; otherwise a regression that clears or ignores input before resend could still pass the final empty-state assertion.

Proposed test strengthening
     await otpBoxes.nth(0).fill('1')
     await otpBoxes.nth(1).fill('2')
+    await expect(otpBoxes.nth(0)).toHaveValue('1')
+    await expect(otpBoxes.nth(1)).toHaveValue('2')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/step-definitions/auth.steps.ts` around lines 628 - 635, Update the step
definition “the user enters two digits from the old OTP” to assert that the
first two OTP boxes contain the entered old digits after filling them. Keep the
existing fill behavior and ensure the assertions run before the resend/reset
action.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@e2e/step-definitions/auth.steps.ts`:
- Around line 628-635: Update the step definition “the user enters two digits
from the old OTP” to assert that the first two OTP boxes contain the entered old
digits after filling them. Keep the existing fill behavior and ensure the
assertions run before the resend/reset action.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a80ed2d-8632-428c-bd64-317aa077a525

📥 Commits

Reviewing files that changed from the base of the PR and between 7bce175 and 619ae08.

📒 Files selected for processing (4)
  • .changeset/clear-otp-boxes-on-resend.md
  • e2e/step-definitions/auth.steps.ts
  • features/passwordless-authentication.feature
  • packages/auth-service/src/routes/login-page.ts

aspiers added a commit that referenced this pull request Jul 30, 2026
The "boxes are empty after resend" assertion only proves the reset
happened if the boxes held something beforehand. Without this check a
regression that cleared or ignored input *before* resend would still
satisfy the final empty-state assertion.

Suggested by CodeRabbit in review of #220.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 30, 2026 21:45
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-220 July 30, 2026 21:45 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@aspiers

aspiers commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

(reply generated by Claude Opus 5 via Claude Code)

@coderabbitai re: the nitpick on e2e/step-definitions/auth.steps.ts (the "assert the old digits before testing the reset" suggestion) — agreed and applied in d73d017.

Your reasoning is right: without it, a regression that cleared or ignored input before the resend would still satisfy the final empty-state assertion, so the scenario wouldn't actually be proving the reset.

await otpBoxes.nth(0).fill('1')
await otpBoxes.nth(1).fill('2')
// Prove the digits actually landed, so the later empty-box assertion
// demonstrates that resend cleared them rather than that they were
// never entered.
await expect(otpBoxes.nth(0)).toHaveValue('1')
await expect(otpBoxes.nth(1)).toHaveValue('2')

@aspiers

aspiers commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

(reply generated by Claude Opus 5 via Claude Code)

Re: the Blacksmith report of Cucumber/Expired OTP is rejected, resend recovers the flow failing — that result is stale, and for the record it was a real bug rather than the known flake in #214.

It ran against 50f4a5e, an earlier version of the commit that has since been amended and force-pushed away (it is no longer an ancestor of this branch). The failure was:

Error: expect(locator).toHaveCount(expected) failed
  Expected: 8
  - unexpected value "6"

Cause: my new step asserted toHaveCount(testEnv.otpLength). e2e/support/env.ts:33 defaults otpLength to 8 when OTP_LENGTH is unset in the e2e runner's environment, but the auth service under test renders 6 boxes — so the assertion compared an env-derived default against the real DOM.

Fix, already in 619ae08: assert against the length of the OTP actually captured from the mail trap, which is by definition the number of boxes to expect:

await expect(otpBoxes).toHaveCount(this.otpCode.length)

This mirrors the existing guard at auth.steps.ts:55, which likewise prefers the live box count over testEnv.otpLength.

E2E tests pass on 619ae08 (run 30560777504), and all checks are currently green on the branch tip.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@aspiers Thanks — that closes the coverage gap nicely. The pre-resend value assertions ensure the scenario proves that resend clears existing input, rather than merely observing an already-empty state.

🐇 ✅


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

aspiers added a commit that referenced this pull request Aug 3, 2026
The "boxes are empty after resend" assertion only proves the reset
happened if the boxes held something beforehand. Without this check a
regression that cleared or ignored input *before* resend would still
satisfy the final empty-state assertion.

Suggested by CodeRabbit in review of #220.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aspiers
aspiers force-pushed the split-pr165/clear-code-on-resend branch from d73d017 to fe45b27 Compare August 3, 2026 19:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (1)

packages/auth-service/src/routes/login-page.ts:568

  • The PR title/description focus on OTP resend UX, but this change set also repositions the shared flash/error region between steps (so errors render next to the relevant field) and adds an inline “Resend code” CTA for lockout-style errors (“too many attempts/invalidated”). Please update the PR title/description to reflect these additional user-facing behavior changes (or split them into a separate PR) so the review scope matches what’s actually being shipped.
  // The flash region is a single element shared by both steps, so there
  // is only ever one live region for assistive tech to track. It is
  // server-rendered into whichever step is initially visible and moved
  // between the two slots on step transitions; both transitions call
  // clearError() first, so it is always empty when it moves.
  const flashRegionHtml =
    '<div id="error-msg" class="flash-msg hidden" role="status" aria-live="polite"></div>'

aspiers added a commit that referenced this pull request Aug 4, 2026
The two shots in the PR description predate this branch's later
commits: the "after" still shows "Code resent!" in a banner above the
code boxes, which is neither the current copy nor the current
position. Neither showed the error-placement change at all.

Recapture against live Railway previews rather than a local mock —
PR #229's preview for the before (still serving the old layout) and
this branch's own for the after.

  pr-220-before-error-above-form.png   error above the subtitle,
                                       no inline action
  pr-220-error-at-point-of-failure.png error under the boxes with
                                       the inline Resend
  pr-220-resend-confirmation.png       the new resend copy

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 4, 2026 16:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (1)

.changeset/sign-in-errors-at-point-of-failure.md:11

  • Changeset body structure: after the initial **End users:** paragraph, the following paragraph should also be part of the per-audience section (and consistently labeled). Leaving an unlabeled paragraph makes the audience-specific formatting inconsistent with the repo’s changeset guidelines and can make rendered release notes harder to scan.
When the code is rejected because of too many wrong attempts, the message now carries a **Resend code** link beside it. That case wipes the stored code, so retyping cannot work and a fresh code is the only way forward; the standalone **Resend code** button below the form was easy to overlook. A simple mistyped code is unchanged — the boxes clear and refocus so you can just type it again.

aspiers added a commit that referenced this pull request Aug 4, 2026
Testing the previous commit surfaced the gap: entering a wrong code
shows "Invalid OTP" with no way forward, because the inline action was
gated on the "too many attempts" wording.

That gate was based on a wrong assumption — that "Invalid OTP" means a
typo, which retyping fixes. better-auth 1.4.18 throws it from two
places in email-otp/routes.mjs: the wrong-code comparison, and a
missing stored code. The second is reached in several states where
retyping cannot possibly work:

  - after a lockout. TOO_MANY_ATTEMPTS *deletes* the stored value, so
    it is reported exactly once; every subsequent submit falls through
    to "Invalid OTP". The lockout gate therefore missed the very
    attempts where the user is most stuck.
  - after the code was consumed elsewhere, e.g. sign-in completed in
    another tab.
  - after expiry cleanup deleted the value, so a later submit reads
    "Invalid OTP" rather than "expired".

The branch cannot distinguish those from a typo, so withholding the
action stranded the users who needed it most. Offering it always costs
a typo-ing user nothing — the boxes are cleared and focused for
retyping either way.

Drops needsFreshCode entirely; the gate is now just parLikelyDead(),
matching refreshResendVisibility().

Reported by @aspiers testing #220.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 4, 2026 17:10
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-220 August 4, 2026 17:10 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 9 changed files in this pull request and generated no new comments.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These do not belong in the repo, only in the PR description.

aspiers and others added 2 commits August 4, 2026 18:24
Testing the previous commit surfaced the gap: entering a wrong code
shows "Invalid OTP" with no way forward, because the inline action was
gated on the "too many attempts" wording.

That gate was based on a wrong assumption — that "Invalid OTP" means a
typo, which retyping fixes. better-auth 1.4.18 throws it from two
places in email-otp/routes.mjs: the wrong-code comparison, and a
missing stored code. The second is reached in several states where
retyping cannot possibly work:

  - after a lockout. TOO_MANY_ATTEMPTS *deletes* the stored value, so
    it is reported exactly once; every subsequent submit falls through
    to "Invalid OTP". The lockout gate therefore missed the very
    attempts where the user is most stuck.
  - after the code was consumed elsewhere, e.g. sign-in completed in
    another tab.
  - after expiry cleanup deleted the value, so a later submit reads
    "Invalid OTP" rather than "expired".

The branch cannot distinguish those from a typo, so withholding the
action stranded the users who needed it most. Offering it always costs
a typo-ing user nothing — the boxes are cleared and focused for
retyping either way.

Drops needsFreshCode entirely; the gate is now just parLikelyDead(),
matching refreshResendVisibility().

Reported by @aspiers testing #220.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Screenshots for a pull request are review ephemera. They have no value
to the codebase, they are binaries that git keeps forever, and GitHub
already hosts images attached to a PR body for free.

f4ff99f introduced docs/screenshots/ for #232 and it holds nothing but
those two files, so removing them takes the directory with it. The
three added for #220 were dropped from that branch before merge.

The blobs stay reachable in history; purging them would mean rewriting
main, which is not worth it for ~100KB.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 4, 2026 17:32
@aspiers
aspiers force-pushed the split-pr165/clear-code-on-resend branch from 09bc80f to ce3caef Compare August 4, 2026 17:32
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-220 August 4, 2026 17:33 Destroyed
@sonarqubecloud

sonarqubecloud Bot commented Aug 4, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (2)

packages/auth-service/src/routes/login-page.ts:1122

  • The comment in clearError() looks accidentally truncated ("Clearing after the" → "region is hidden …"), which makes the accessibility rationale harder to follow. Reword it into a single grammatical sentence across the wrapped lines.
        // Empty the region before hiding it. Clearing after the
        // region is hidden would mutate one that is already out of
        // the accessibility tree, which some assistive tech reports
        // as a stale announcement.

packages/auth-service/src/routes/login-page.ts:1332

  • PR description says only failures that invalidate the stored code should show an inline Resend code action, while a plain mistyped code should keep the message without the action. This branch adds an inline resend for all non-expired verify failures when the PAR is still alive (the else if (!parLikelyDead()) branch). Either update the PR description to match the implemented behavior, or narrow this branch to only the error cases where resending is the intended recovery.
            } else if (!parLikelyDead()) {
              // Every other verify failure gets the same inline
              // shortcut, for the same reason as the expired path: the
              // standalone Resend button sits below the form and is
              // easy to miss.
              //

@aspiers
aspiers merged commit ce5de14 into main Aug 4, 2026
23 checks passed
@aspiers
aspiers deleted the split-pr165/clear-code-on-resend branch August 4, 2026 17:58
aspiers added a commit that referenced this pull request Aug 4, 2026
Two unreleased changesets from #220 name the button as "Resend code".
Since this PR renames it, they would ship in the same release
describing a control that no longer exists by that name.

Also drops "the confirmation message that replaces 'Code resent!'" —
that phrasing only made sense as a diff against the previous release,
and the string it names is gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

2 participants