Skip to content

Make the duplicate-code check stricter and remove the duplication it finds - #1795

Merged
stefan-burke merged 2 commits into
mainfrom
claude/jscpd-min-tokens-dedup-jrx2qj
Jul 13, 2026
Merged

Make the duplicate-code check stricter and remove the duplication it finds#1795
stefan-burke merged 2 commits into
mainfrom
claude/jscpd-min-tokens-dedup-jrx2qj

Conversation

@stefan-burke

@stefan-burke stefan-burke commented Jul 12, 2026

Copy link
Copy Markdown
Member

What this does

The project has an automatic check that fails the build if the same code appears in two places. This makes that check a little stricter — it now notices repeated blocks one step smaller than before (20 tokens instead of 21).

Turning the check up surfaced 159 spots where the same code lived in two places. This PR removes every one of them, so the build passes at the new, stricter setting.

How the duplication was removed

The rule is: two copies of the same code become one shared piece that both places use. Never a trick to make the checker look the other way while leaving the two copies in place. Some of the bigger consolidations:

  • Booking now has one shared way to decide which child items can still be booked, one way to work out day counts, and one availability check used by both the public pages and the API.
  • The database layer reuses shared helpers for the common "look these rows up by id" reads, and a single shared mechanism for the login/token rate-limit lockouts that used to be written twice.
  • Admin pages share one set of building blocks for the "check you're allowed, load the thing, show the form" pattern instead of repeating it per page.
  • The listings dashboard had a large table-rendering block copied into the servicing page; that now lives in one place both use.
  • Small shared UI pieces were pulled out and reused: a "read more in a new tab" link, a titled section, a scrollable table wrapper, a labelled table row, question inputs, and the payment-provider settings block. The three payment providers (Stripe, Square, SumUp) now share one heading-and-hint intro block.
  • News posts and site pages — which are almost the same kind of thing — now share one form, one public web route, and one page layout.
  • The debug page is now built from a simple list of rows rather than the same block written out many times.

Review follow-ups included

Several points raised in review are also fixed here:

  • Two spots that quietly turned a missing value into a blank one now check for it properly and fail loudly.
  • A "recalculate" link is hidden when the site is in read-only mode so it can't lead to a blocked page.
  • The last hand-written "flatten every parent's children" spot in the booking handler now reuses the one shared helper, and the three payment-provider intros were merged into one.
  • Some wording moved into the shared text file where all the site's words live — the SumUp form hints and buttons, and the "1 listing / 2 listings" count on the service-events list.
  • A few shared helpers were given clearer, explicit types, and one tricky lookup got a short note explaining why it can never miss.

A handful of other points raised in review are real but were already true before this change (not caused by it) — for example some update-page and API-docs wording that still lives in the code rather than the shared text file. Those are written up in TODO.md for a focused follow-up rather than changed here, so this stays a duplicate-removal change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DTrbBujCcBu8CUC56dy4tQ

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change set lowers the jscpd threshold and consolidates repeated logic into shared helpers across scripts, booking and API flows, database access, authentication, deployment, and UI templates. Related tests, translations, documentation comments, and configuration are updated.

Changes

Shared refactors and feature integrations

Layer / File(s) Summary
Shared contracts and utilities
src/shared/...
Adds reusable response, lookup, booking, SQL, encryption, lockout, email, deployment, webhook, parsing, schema, and UI helpers.
Booking and API flows
src/features/api/..., src/features/public/..., src/shared/booking/...
Centralizes date validation, capacity checks, child eligibility, folded-booking completion, answer grouping, and day-count calculations.
Database and migration flows
src/shared/db/...
Refactors query composition, schema probing, CRUD handling, capacity calculations, membership updates, and stock-aware reads.
Admin routes and forms
src/features/admin/...
Adopts shared authentication, feature gates, flash handling, lookup wrappers, deployment reporting, content forms, and tab definitions.
UI components and templates
src/ui/templates/..., src/ui/client/...
Introduces reusable page, form, table, list, link, prose, question-control, settings, and admin-page components, then updates existing templates to use them.
Scripts, tests, and support
scripts/..., test/..., src/locales/en/..., .jscpd.json, TODO.md
Refactors benchmark, mutation, CDN, reporting, and analysis scripts; updates tests and translations; records review findings; and lowers the jscpd threshold from 21 to 20.

Estimated code review effort: 5 (Critical) | ~120 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 accurately summarizes both the stricter duplication threshold and the follow-up refactors that remove duplicated code.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/jscpd-min-tokens-dedup-jrx2qj

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5909c38b1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .jscpd.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 14

🤖 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.

Inline comments:
In `@scripts/mutation/generate.ts`:
- Around line 155-160: Update unaryMutants and UNARY_MUTATIONS so every
supported unary operator has an explicit table entry, using an empty array for
intentionally mutation-free operators; remove the ?? [] fallback so missing
operators fail through the required lookup behavior.

In `@src/features/admin/bulk-email.ts`:
- Around line 19-22: Update sendBulkEmails to clear or mark the bulk email draft
consumed before invoking the non-idempotent external send. Ensure
settings.update.bulkEmailDraft("") occurs before recordContacts and logActivity,
while preserving the existing send behavior and avoiding draft reuse after later
failures.

In `@src/features/admin/database-reset.ts`:
- Around line 52-53: Update handleDemoResetGet and demoResetPage so the flash
value supplied by publicFormPage’s render callback is accepted and passed
through to the demo-reset page renderer. Preserve the existing access wrapper
and ensure the flashed validation error from errorRedirect("/demo/reset", error)
appears on the confirmation page.

In `@src/features/api/booking.ts`:
- Around line 65-70: Update the date validation in the booking flow to inspect
body.date with an explicit type check before converting or validating it. Reject
missing and non-string values through the existing bookingError.invalidDate
response, and only pass a validated string to availableDates.includes and the
returned date; remove the ?? defaulting.

In `@src/features/public/site-page.ts`:
- Around line 58-64: Update the exported publicSlugRoute higher-order function
to declare an explicit return type for the outer function, using the
repository’s established route-handler type or equivalent. Keep the inner
handler’s existing Response | Promise<Response> behavior unchanged and ensure
the public API no longer relies on return-type inference.

In `@src/shared/find-by-id.ts`:
- Around line 14-22: Annotate the exported findByIdThen function with the named
FindByIdThen<R> return type instead of relying on inferred nested generic
function types. Preserve the existing fetch, build, and null-handling behavior
while applying the type to findByIdThen’s own return signature.

In `@src/shared/try-step.ts`:
- Around line 12-15: Update the catch block in try-step to narrow the caught
value before accessing .message: use the Error message for actual Error
instances and a safe string representation for null, undefined, strings, and
other thrown values, while preserving the existing label, error, and ok:false
result shape.

In `@src/ui/templates/admin/guide/components.tsx`:
- Around line 120-124: Annotate the exported renderGuideSections constant with
the repository’s concrete section-renderer type, while preserving its existing
sectionsRenderer callback and rendering behavior. Use the established type
symbol from the surrounding guide rendering code rather than relying on inferred
typing.

In `@src/ui/templates/admin/listings/aggregates.tsx`:
- Around line 66-92: Update ListingRunningTotalsSection so the recalculate
action URL is only passed when isReadOnly() is false, matching
ListingAggregateMismatchRow’s existing gating. Preserve the notice content while
ensuring read-only mode omits actionHref and therefore hides the recalculate
link.

In `@src/ui/templates/admin/settings/payment.tsx`:
- Line 263: Add an English locale entry for the SumUp guide label in the
appropriate message catalog, then update the SumUp PaymentGuideLink usage in the
payment settings component to pass the translated value from t(...). Keep the
displayed text unchanged and follow the existing Stripe and Square translation
patterns.

In `@src/ui/templates/attendee-table.tsx`:
- Around line 293-296: Update the comment immediately before the buildAnswerMaps
call to document that computeVisibilityMap sets visMap.answers true only when
opts.questionData is defined, making the non-null assertion on opts.questionData
safe. Preserve the existing explanation of the empty-array behavior.

In `@src/ui/templates/components/settings-section.tsx`:
- Line 15: Update SettingsSectionDetails and the settingsSectionWith wrapper to
accept and forward id and enctype form attributes, preserving existing behavior
for callers that omit them. Ensure config-driven settings forms can use these
fields through the wrapper.

In `@test/shared/admin-api-example.test.ts`:
- Line 18: Replace the self-derived ADMIN_API_EXAMPLE_ADMIN_LISTING expected
value in the admin API example test with an independently authored AdminListing
fixture or separate schema assertion. Ensure the test no longer derives both the
actual and expected values through toAdminListing(API_EXAMPLE_LISTING), while
preserving the existing comparison behavior.

In `@test/ui/templates/admin/built-sites.test.ts`:
- Line 245: Update the assertion in the missing BUNNY_API_KEY test to expect the
exact host error text, preserving the “its secrets can't be read” wording and
prefix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1243938b-1899-4f8e-b639-6e76c1e2d065

📥 Commits

Reviewing files that changed from the base of the PR and between e5909c3 and 8a01bb0.

📒 Files selected for processing (207)
  • scripts/bench/cold-start/first-request-child.ts
  • scripts/find-unused-src.ts
  • scripts/line-counts.ts
  • scripts/mutation/child-process.ts
  • scripts/mutation/generate.ts
  • scripts/mutation/ignore.ts
  • scripts/mutation/isolation.ts
  • scripts/mutation/operators.ts
  • scripts/mutation/runner.ts
  • scripts/profile-cold-boot.ts
  • scripts/static-cdn.ts
  • scripts/stripe-mock.ts
  • scripts/stripe-mock/install.ts
  • scripts/unit-tests-report.ts
  • src/features/admin/api-groups.ts
  • src/features/admin/api-keys.ts
  • src/features/admin/api.ts
  • src/features/admin/attendee-form-model.ts
  • src/features/admin/attendee-form-routes.ts
  • src/features/admin/attendee-page.ts
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/attendees-edit.ts
  • src/features/admin/attendees-route-helpers.ts
  • src/features/admin/built-sites.ts
  • src/features/admin/bulk-email.ts
  • src/features/admin/catalog-transfer/import.ts
  • src/features/admin/catalog-transfer/schema.ts
  • src/features/admin/confirmation.ts
  • src/features/admin/content-form-fields.ts
  • src/features/admin/database-reset.ts
  • src/features/admin/entity-write-tab.ts
  • src/features/admin/find-group.ts
  • src/features/admin/images.ts
  • src/features/admin/ledger/entries.ts
  • src/features/admin/listing-page.ts
  • src/features/admin/listings-edit.ts
  • src/features/admin/load-listing.ts
  • src/features/admin/news-form.ts
  • src/features/admin/questions.ts
  • src/features/admin/scanner.ts
  • src/features/admin/servicing.tsx
  • src/features/admin/servicing/page.tsx
  • src/features/admin/sessions.ts
  • src/features/admin/settings-helpers.ts
  • src/features/admin/settings-sumup.ts
  • src/features/admin/settings-superuser.ts
  • src/features/admin/settings-wallets.ts
  • src/features/admin/site-content.ts
  • src/features/admin/site-pages-form.ts
  • src/features/admin/site.ts
  • src/features/admin/support.ts
  • src/features/admin/update.ts
  • src/features/admin/users.ts
  • src/features/api/booking.ts
  • src/features/api/folded-booking.ts
  • src/features/api/listings.ts
  • src/features/api/packages.ts
  • src/features/api/payment-processing/classify.ts
  • src/features/api/payment-processing/create.ts
  • src/features/api/public-listing.ts
  • src/features/api/webhooks.ts
  • src/features/auth.ts
  • src/features/join.ts
  • src/features/public/news.ts
  • src/features/public/site-page.ts
  • src/features/public/ticket-form.ts
  • src/features/public/ticket-submit.ts
  • src/features/public/ticket-submit/parse.ts
  • src/features/public/ticket-submit/paths.ts
  • src/features/public/ticket-submit/prepare.ts
  • src/features/tickets/token-utils.ts
  • src/locales/en/attendees.json
  • src/shared/accounting/backfill.ts
  • src/shared/accounting/rows.ts
  • src/shared/admin-api-example.ts
  • src/shared/app-forms.ts
  • src/shared/booking.ts
  • src/shared/booking/fold-tree.ts
  • src/shared/booking/model.ts
  • src/shared/booking/package-cap.ts
  • src/shared/builder.ts
  • src/shared/crypto/encryption.ts
  • src/shared/crypto/keys.ts
  • src/shared/db/attempt-lockout.ts
  • src/shared/db/attendees/capacity.ts
  • src/shared/db/attendees/servicing.ts
  • src/shared/db/capacity.ts
  • src/shared/db/client.ts
  • src/shared/db/common-schema.ts
  • src/shared/db/groups.ts
  • src/shared/db/listing-parents.ts
  • src/shared/db/login-attempts.ts
  • src/shared/db/logistics-agents.ts
  • src/shared/db/migrations.ts
  • src/shared/db/migrations/schema-sync.ts
  • src/shared/db/modifier-resolve.ts
  • src/shared/db/modifiers.ts
  • src/shared/db/query.ts
  • src/shared/db/questions/attendee-answers/save.ts
  • src/shared/db/restore-legacy-columns.ts
  • src/shared/db/table.ts
  • src/shared/db/token-attempts.ts
  • src/shared/deno-deploy-api.ts
  • src/shared/email.ts
  • src/shared/env.ts
  • src/shared/find-by-id.ts
  • src/shared/forms.tsx
  • src/shared/listings-actions.ts
  • src/shared/order/evaluate.ts
  • src/shared/payment-helpers.ts
  • src/shared/payments.ts
  • src/shared/provider-types.ts
  • src/shared/refund-ledger.ts
  • src/shared/response-steps.ts
  • src/shared/rest/crud-api.ts
  • src/shared/rest/resource.ts
  • src/shared/seen-before.ts
  • src/shared/site-secrets.ts
  • src/shared/site-update.ts
  • src/shared/square.ts
  • src/shared/stripe.ts
  • src/shared/sumup-provider.ts
  • src/shared/try-step.ts
  • src/shared/types.ts
  • src/ui/client/admin/payment-test-buttons.ts
  • src/ui/client/order.ts
  • src/ui/templates/admin/activityLog.tsx
  • src/ui/templates/admin/admin-page.tsx
  • src/ui/templates/admin/api-keys.tsx
  • src/ui/templates/admin/attendee-ledger-panel.tsx
  • src/ui/templates/admin/attendee-page.tsx
  • src/ui/templates/admin/attendees.tsx
  • src/ui/templates/admin/bulk-actions.tsx
  • src/ui/templates/admin/catalog-transfer.tsx
  • src/ui/templates/admin/dashboard.tsx
  • src/ui/templates/admin/debug.tsx
  • src/ui/templates/admin/delete-link.tsx
  • src/ui/templates/admin/deliveries.tsx
  • src/ui/templates/admin/expected-actual.tsx
  • src/ui/templates/admin/groups/list.tsx
  • src/ui/templates/admin/groups/overview.tsx
  • src/ui/templates/admin/guide/components.tsx
  • src/ui/templates/admin/guide/integrations.tsx
  • src/ui/templates/admin/ledger/formatting.tsx
  • src/ui/templates/admin/ledger/statement.tsx
  • src/ui/templates/admin/listing-table.tsx
  • src/ui/templates/admin/listings/aggregates.tsx
  • src/ui/templates/admin/listings/attendees.tsx
  • src/ui/templates/admin/listings/capacity-rows.tsx
  • src/ui/templates/admin/listings/edit-panel.tsx
  • src/ui/templates/admin/listings/ledger-section.tsx
  • src/ui/templates/admin/logistics.tsx
  • src/ui/templates/admin/logout.tsx
  • src/ui/templates/admin/money-adjust-section.tsx
  • src/ui/templates/admin/news.tsx
  • src/ui/templates/admin/questions.tsx
  • src/ui/templates/admin/seeds.tsx
  • src/ui/templates/admin/servicing-events.tsx
  • src/ui/templates/admin/sessions.tsx
  • src/ui/templates/admin/settings-advanced.tsx
  • src/ui/templates/admin/settings-nag-banner.tsx
  • src/ui/templates/admin/settings.tsx
  • src/ui/templates/admin/settings/page-shell.tsx
  • src/ui/templates/admin/settings/payment.tsx
  • src/ui/templates/admin/settings/schema-form.tsx
  • src/ui/templates/admin/settings/wallet-settings.tsx
  • src/ui/templates/admin/share-rows.tsx
  • src/ui/templates/admin/site-pages.tsx
  • src/ui/templates/admin/site.tsx
  • src/ui/templates/attendee-table.tsx
  • src/ui/templates/checkin.tsx
  • src/ui/templates/components/aggregate-sections.tsx
  • src/ui/templates/components/inline-form-button.tsx
  • src/ui/templates/components/item-list.tsx
  • src/ui/templates/components/labelled-row.tsx
  • src/ui/templates/components/new-tab-link.tsx
  • src/ui/templates/components/phone-links.tsx
  • src/ui/templates/components/prose-article.tsx
  • src/ui/templates/components/prose-heading.tsx
  • src/ui/templates/components/prose-section.tsx
  • src/ui/templates/components/question-controls.tsx
  • src/ui/templates/components/raw-paragraph.tsx
  • src/ui/templates/components/reorder.tsx
  • src/ui/templates/components/save-form.tsx
  • src/ui/templates/components/settings-section.tsx
  • src/ui/templates/components/table-scroll.tsx
  • src/ui/templates/components/text-fields.tsx
  • src/ui/templates/payment.tsx
  • src/ui/templates/public/content-page.tsx
  • src/ui/templates/public/homepage.tsx
  • src/ui/templates/public/news.tsx
  • src/ui/templates/public/order-gallery.tsx
  • src/ui/templates/public/reservations/child-block.ts
  • src/ui/templates/public/reservations/day-config.ts
  • src/ui/templates/public/reservations/questions.tsx
  • src/ui/templates/public/shared.tsx
  • src/ui/templates/public/site-page.tsx
  • src/ui/templates/tickets.tsx
  • test/features/public/ticket-form.test.ts
  • test/lib/booking-model-day-counts.test.ts
  • test/lib/booking-model-eligibility.test.ts
  • test/lib/server-built-sites-update.test.ts
  • test/shared/admin-api-example.test.ts
  • test/shared/booking/package-cap.test.ts
  • test/shared/seen-before.test.ts
  • test/ui/templates/admin/built-sites.test.ts
  • test/ui/templates/admin/ledger/statement.test.ts
💤 Files with no reviewable changes (1)
  • test/shared/booking/package-cap.test.ts

Comment thread scripts/mutation/generate.ts
Comment thread src/features/admin/bulk-email.ts
Comment thread src/features/admin/database-reset.ts
Comment thread src/features/api/booking.ts Outdated
Comment thread src/shared/find-by-id.ts
Comment thread src/ui/templates/admin/settings/payment.tsx Outdated
Comment thread src/ui/templates/attendee-table.tsx
Comment thread src/ui/templates/components/settings-section.tsx
Comment thread test/shared/admin-api-example.test.ts
Comment thread test/ui/templates/admin/built-sites.test.ts

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a01bb076c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/ui/templates/admin/expected-actual.tsx
@stefan-burke
stefan-burke force-pushed the claude/jscpd-min-tokens-dedup-jrx2qj branch from 8a01bb0 to b8f01ee Compare July 12, 2026 20:48
@stefan-burke stefan-burke changed the title WIP: Tighten the duplicate-code check another notch (min-tokens 20) Make the duplicate-code check stricter and remove the duplication it finds Jul 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/shared/db/attendees/capacity.ts (1)

616-624: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the invariant behind the non-null assertions on loads.get(day)! / m.get(day)!.

These assertions are safe because perDayLoads always emits an entry for every day passed in, but a short comment stating that would make the invariant explicit for future readers. Based on learnings, this repo prefers non-null assertions with a documenting comment over defensive branches when the missing case is genuinely impossible.

📝 Example comment
+    // `loads` is built from `perDayLoads(groupRows, days)`, which always has
+    // an entry for every day in `days` — safe to assert non-null here.
     result.set(
       id,
       new Map(days.map((day) => [day, max_attendees - base - loads.get(day)!])),
     );

Also applies to: 690-699

🤖 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 `@src/shared/db/attendees/capacity.ts` around lines 616 - 624, Add a concise
comment next to the non-null assertions on loads.get(day)! in the capacity
calculation and m.get(day)! at the other affected location, documenting that
perDayLoads always emits an entry for every requested day. Keep the existing
non-null assertions and calculation behavior unchanged.

Source: Learnings

src/features/admin/site-content.ts (1)

45-50: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

siteEntityPost re-implements the pattern formPost was just extracted for.

gatedPost(SITE_FORM)((_session, form) => h(form))(request) is exactly formPost(SITE_FORM)(h)(request). Since formPost already exists in #routes/auth.ts (imported here), this call site should use it instead of duplicating the discard-session pattern inline — directly aligned with this PR's dedup goal.

♻️ Proposed fix
 export const siteEntityPost =
   <T>(load: (id: number) => Promise<T | null>) =>
   (handler: (item: T, form: FormParams) => Promise<Response>) =>
     gatedEntityRoute<FormParams>((request, h) =>
-      gatedPost(SITE_FORM)((_session, form) => h(form))(request),
+      formPost(SITE_FORM)(h)(request),
     )(load, handler);
🤖 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 `@src/features/admin/site-content.ts` around lines 45 - 50, Update
siteEntityPost to replace the inline gatedPost(SITE_FORM) session-discarding
wrapper with the imported formPost(SITE_FORM)(h) helper, preserving the existing
request handling and gatedEntityRoute behavior.
🤖 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.

Inline comments:
In `@src/features/admin/attendees-route-helpers.ts`:
- Around line 174-180: Introduce a named route-params type for the attendee ID
shape, following the existing AttendeeRouteParams and ListingRouteParams
pattern, and use it in both the createAuthedHandler generic and attendeeFormPost
return type. Keep the handler behavior unchanged.

In `@src/features/admin/confirmation.ts`:
- Around line 108-122: Add an explicit return type to the exported
createVerifiedFormRoute helper, matching the return-type annotation used by
createConfirmedHandlers. Preserve the existing generic parameters, verification
flow, and callback behavior while annotating the return value at the function
declaration.

In `@src/features/public/site-page.ts`:
- Around line 62-67: Update publicSlugRoute to use a route-agnostic or generic
typed handler that supports both “GET /page/:slug” and “GET /news/:slug”
registrations. Preserve the existing slug extraction and requirePublicSite
behavior while removing the hard-coded page route contract.

In `@src/shared/db/attempt-lockout.ts`:
- Around line 23-31: Update lockoutActive so expired-row cleanup only deletes
the record when its stored locked-until timestamp still equals the lockedUntil
value observed by this call. Replace the unconditional deleteByField call with
an atomic conditional deletion using the available database helper or query
mechanism, while preserving the existing return behavior.
- Around line 23-31: Update lockoutActive to treat only null or undefined
lockedUntil values as inactive; allow timestamp 0 to proceed through expiration
checking and deleteByField cleanup.
- Around line 15-18: Update the exported curried function clearAttemptsFor to
explicitly annotate the outer function’s return type as the async inner callback
type, while preserving its existing parameters and deleteByField behavior.

In `@src/shared/email.ts`:
- Around line 119-124: Update getActiveEmailConfig to replace the
nullish-coalescing fallback with the repository’s explicit undefined/null check
pattern, matching getHostEmailConfig while preserving the site-config-first
behavior and null result when neither configuration exists.

In `@src/shared/payment-helpers.ts`:
- Around line 694-708: Move the test-only SignedTestWebhook type and
signedTestWebhook helper out of src/shared/payment-helpers.ts into a test
utility module such as test/test-utils/signed-webhook.ts. Update all test
imports to use the relocated helper through the established test-utils alias,
and remove the production exports while preserving the existing signing and
payload behavior.

In `@src/shared/response-steps.ts`:
- Around line 14-17: Update the exported featureGate outer curried function to
declare an explicit named return type, matching the existing convention used by
timedRunner. Preserve the inner action signature and current isOn/blocked
behavior, and reuse an appropriate existing type if available rather than
introducing an anonymous union.

In `@src/shared/rest/resource.ts`:
- Around line 223-228: Update the generic constraint for ResourceConfig/Resource
so Row requires an id: number property, then change runAfterCommit to access
row.id directly and remove the unsafe unknown double cast. Preserve the existing
afterCommit invocation and null/config guards.

In `@src/ui/templates/admin/bulk-actions.tsx`:
- Around line 28-39: Update the BulkActionPage/adminDuplicateGroupPage rendering
flow to accept the submitted duplicate-group values and pass them into the
corresponding TextField/TextFields inputs when re-rendering after an error.
Preserve the existing default values for the initial render, while ensuring
failed POST responses retain the submitted name, find/replace, and date fields.

In `@src/ui/templates/admin/debug.tsx`:
- Around line 172-194: Replace the branch-based dispatch in
AvailabilityStateBadge and StorageBackendBadge with exhaustive Record lookups
keyed by their respective state and backend unions. Store each badge’s variant
and label in the records, then render the selected entry for the incoming value,
preserving all existing labels and variants; retain JSX.Element return types.

In `@src/ui/templates/admin/share-rows.tsx`:
- Line 2: Remove the duplicate local UnavailablePublicUrlRow definition in
groups/overview.tsx and import UnavailablePublicUrlRow alongside
PublicTicketLink from the shared share-rows module. Preserve all existing usages
and behavior.

---

Outside diff comments:
In `@src/features/admin/site-content.ts`:
- Around line 45-50: Update siteEntityPost to replace the inline
gatedPost(SITE_FORM) session-discarding wrapper with the imported
formPost(SITE_FORM)(h) helper, preserving the existing request handling and
gatedEntityRoute behavior.

In `@src/shared/db/attendees/capacity.ts`:
- Around line 616-624: Add a concise comment next to the non-null assertions on
loads.get(day)! in the capacity calculation and m.get(day)! at the other
affected location, documenting that perDayLoads always emits an entry for every
requested day. Keep the existing non-null assertions and calculation behavior
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e9f36a12-1c88-4753-98fa-cb6a9ccd5f1b

📥 Commits

Reviewing files that changed from the base of the PR and between 8a01bb0 and b8f01ee.

📒 Files selected for processing (221)
  • .jscpd.json
  • TODO.md
  • scripts/bench/cold-start/first-request-child.ts
  • scripts/find-unused-src.ts
  • scripts/line-counts.ts
  • scripts/mutation/child-process.ts
  • scripts/mutation/generate.ts
  • scripts/mutation/ignore.ts
  • scripts/mutation/isolation.ts
  • scripts/mutation/operators.ts
  • scripts/mutation/runner.ts
  • scripts/profile-cold-boot.ts
  • scripts/static-cdn.ts
  • scripts/stripe-mock.ts
  • scripts/stripe-mock/install.ts
  • scripts/timed-run.ts
  • scripts/unit-tests-report.ts
  • src/features/admin/api-groups.ts
  • src/features/admin/api-keys.ts
  • src/features/admin/api.ts
  • src/features/admin/attendee-form-model.ts
  • src/features/admin/attendee-form-routes.ts
  • src/features/admin/attendee-page.ts
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/attendees-edit.ts
  • src/features/admin/attendees-route-helpers.ts
  • src/features/admin/backup.ts
  • src/features/admin/built-sites.ts
  • src/features/admin/bulk-email.ts
  • src/features/admin/catalog-transfer/import.ts
  • src/features/admin/catalog-transfer/schema.ts
  • src/features/admin/confirmation.ts
  • src/features/admin/content-form-fields.ts
  • src/features/admin/database-reset.ts
  • src/features/admin/entity-write-tab.ts
  • src/features/admin/find-group.ts
  • src/features/admin/images.ts
  • src/features/admin/ledger/entries.ts
  • src/features/admin/listing-page.ts
  • src/features/admin/listings-edit.ts
  • src/features/admin/load-listing.ts
  • src/features/admin/news-form.ts
  • src/features/admin/questions.ts
  • src/features/admin/scanner.ts
  • src/features/admin/servicing.tsx
  • src/features/admin/servicing/page.tsx
  • src/features/admin/sessions.ts
  • src/features/admin/settings-helpers.ts
  • src/features/admin/settings-logistics.ts
  • src/features/admin/settings-sumup.ts
  • src/features/admin/settings-superuser.ts
  • src/features/admin/settings-wallets.ts
  • src/features/admin/site-content.ts
  • src/features/admin/site-pages-form.ts
  • src/features/admin/site.ts
  • src/features/admin/support.ts
  • src/features/admin/update.ts
  • src/features/admin/users.ts
  • src/features/api/booking.ts
  • src/features/api/folded-booking.ts
  • src/features/api/listings.ts
  • src/features/api/packages.ts
  • src/features/api/payment-processing/classify.ts
  • src/features/api/payment-processing/create.ts
  • src/features/api/public-listing.ts
  • src/features/api/webhooks.ts
  • src/features/auth.ts
  • src/features/join.ts
  • src/features/public/news.ts
  • src/features/public/site-page.ts
  • src/features/public/ticket-form.ts
  • src/features/public/ticket-submit.ts
  • src/features/public/ticket-submit/parse.ts
  • src/features/public/ticket-submit/paths.ts
  • src/features/public/ticket-submit/prepare.ts
  • src/features/public/ticket-submit/pricing.ts
  • src/features/tickets/token-utils.ts
  • src/locales/en/attendees.json
  • src/locales/en/settings.json
  • src/shared/accounting/backfill.ts
  • src/shared/accounting/rows.ts
  • src/shared/admin-api-example.ts
  • src/shared/app-forms.ts
  • src/shared/booking.ts
  • src/shared/booking/fold-tree.ts
  • src/shared/booking/model.ts
  • src/shared/booking/package-cap.ts
  • src/shared/builder.ts
  • src/shared/crypto/encryption.ts
  • src/shared/crypto/keys.ts
  • src/shared/db/attempt-lockout.ts
  • src/shared/db/attendees/capacity.ts
  • src/shared/db/attendees/servicing.ts
  • src/shared/db/capacity.ts
  • src/shared/db/client.ts
  • src/shared/db/common-schema.ts
  • src/shared/db/groups.ts
  • src/shared/db/images.ts
  • src/shared/db/listing-parents.ts
  • src/shared/db/login-attempts.ts
  • src/shared/db/logistics-agents.ts
  • src/shared/db/migrations.ts
  • src/shared/db/migrations/schema-sync.ts
  • src/shared/db/modifier-resolve.ts
  • src/shared/db/modifiers.ts
  • src/shared/db/query.ts
  • src/shared/db/questions/attendee-answers/save.ts
  • src/shared/db/restore-legacy-columns.ts
  • src/shared/db/table.ts
  • src/shared/db/token-attempts.ts
  • src/shared/deno-deploy-api.ts
  • src/shared/email.ts
  • src/shared/env.ts
  • src/shared/find-by-id.ts
  • src/shared/forms.tsx
  • src/shared/listings-actions.ts
  • src/shared/order/evaluate.ts
  • src/shared/payment-helpers.ts
  • src/shared/payments.ts
  • src/shared/provider-types.ts
  • src/shared/refund-ledger.ts
  • src/shared/response-steps.ts
  • src/shared/rest/crud-api.ts
  • src/shared/rest/resource.ts
  • src/shared/seen-before.ts
  • src/shared/site-secrets.ts
  • src/shared/site-update.ts
  • src/shared/square.ts
  • src/shared/stripe.ts
  • src/shared/sumup-provider.ts
  • src/shared/try-step.ts
  • src/shared/types.ts
  • src/ui/client/admin/payment-test-buttons.ts
  • src/ui/client/order.ts
  • src/ui/templates/admin/activityLog.tsx
  • src/ui/templates/admin/admin-page.tsx
  • src/ui/templates/admin/api-keys.tsx
  • src/ui/templates/admin/attendee-ledger-panel.tsx
  • src/ui/templates/admin/attendee-page.tsx
  • src/ui/templates/admin/attendees.tsx
  • src/ui/templates/admin/availability-checker.tsx
  • src/ui/templates/admin/bulk-actions.tsx
  • src/ui/templates/admin/catalog-transfer.tsx
  • src/ui/templates/admin/dashboard.tsx
  • src/ui/templates/admin/debug.tsx
  • src/ui/templates/admin/delete-link.tsx
  • src/ui/templates/admin/deliveries.tsx
  • src/ui/templates/admin/detail-rows.tsx
  • src/ui/templates/admin/expected-actual.tsx
  • src/ui/templates/admin/groups/list.tsx
  • src/ui/templates/admin/groups/overview.tsx
  • src/ui/templates/admin/guide/components.tsx
  • src/ui/templates/admin/guide/integrations.tsx
  • src/ui/templates/admin/ledger/formatting.tsx
  • src/ui/templates/admin/ledger/statement.tsx
  • src/ui/templates/admin/listing-qr.tsx
  • src/ui/templates/admin/listing-table.tsx
  • src/ui/templates/admin/listings/aggregates.tsx
  • src/ui/templates/admin/listings/attendees.tsx
  • src/ui/templates/admin/listings/capacity-rows.tsx
  • src/ui/templates/admin/listings/edit-panel.tsx
  • src/ui/templates/admin/listings/ledger-section.tsx
  • src/ui/templates/admin/logistics.tsx
  • src/ui/templates/admin/logout.tsx
  • src/ui/templates/admin/money-adjust-section.tsx
  • src/ui/templates/admin/news.tsx
  • src/ui/templates/admin/questions.tsx
  • src/ui/templates/admin/seeds.tsx
  • src/ui/templates/admin/servicing-events.tsx
  • src/ui/templates/admin/sessions.tsx
  • src/ui/templates/admin/settings-advanced.tsx
  • src/ui/templates/admin/settings-nag-banner.tsx
  • src/ui/templates/admin/settings.tsx
  • src/ui/templates/admin/settings/page-shell.tsx
  • src/ui/templates/admin/settings/payment.tsx
  • src/ui/templates/admin/settings/schema-form.tsx
  • src/ui/templates/admin/settings/wallet-settings.tsx
  • src/ui/templates/admin/share-rows.tsx
  • src/ui/templates/admin/site-pages.tsx
  • src/ui/templates/admin/site.tsx
  • src/ui/templates/attendee-table.tsx
  • src/ui/templates/checkin.tsx
  • src/ui/templates/components/aggregate-sections.tsx
  • src/ui/templates/components/div-with-class.tsx
  • src/ui/templates/components/inline-form-button.tsx
  • src/ui/templates/components/item-list.tsx
  • src/ui/templates/components/labelled-row.tsx
  • src/ui/templates/components/new-tab-link.tsx
  • src/ui/templates/components/phone-links.tsx
  • src/ui/templates/components/prose-article.tsx
  • src/ui/templates/components/prose-heading.tsx
  • src/ui/templates/components/prose-section.tsx
  • src/ui/templates/components/question-controls.tsx
  • src/ui/templates/components/raw-paragraph.tsx
  • src/ui/templates/components/reorder.tsx
  • src/ui/templates/components/save-form.tsx
  • src/ui/templates/components/settings-section.tsx
  • src/ui/templates/components/table-scroll.tsx
  • src/ui/templates/components/text-fields.tsx
  • src/ui/templates/payment.tsx
  • src/ui/templates/public/content-page.tsx
  • src/ui/templates/public/homepage.tsx
  • src/ui/templates/public/news.tsx
  • src/ui/templates/public/order-gallery.tsx
  • src/ui/templates/public/reservations/child-block.ts
  • src/ui/templates/public/reservations/day-config.ts
  • src/ui/templates/public/reservations/questions.tsx
  • src/ui/templates/public/shared.tsx
  • src/ui/templates/public/site-page.tsx
  • src/ui/templates/tickets.tsx
  • test/features/public/ticket-form.test.ts
  • test/lib/booking-model-day-counts.test.ts
  • test/lib/booking-model-eligibility.test.ts
  • test/lib/i18n-coverage.test.ts
  • test/lib/server-built-sites-update.test.ts
  • test/shared/admin-api-example.test.ts
  • test/shared/booking/package-cap.test.ts
  • test/shared/seen-before.test.ts
  • test/ui/templates/admin/built-sites.test.ts
  • test/ui/templates/admin/expected-actual.test.tsx
  • test/ui/templates/admin/ledger/statement.test.ts
💤 Files with no reviewable changes (1)
  • test/shared/booking/package-cap.test.ts

Comment thread src/features/admin/attendees-route-helpers.ts
Comment thread src/features/admin/confirmation.ts
Comment thread src/features/public/site-page.ts
Comment thread src/shared/db/attempt-lockout.ts
Comment thread src/shared/db/attempt-lockout.ts
Comment thread src/shared/response-steps.ts
Comment thread src/shared/rest/resource.ts
Comment thread src/ui/templates/admin/bulk-actions.tsx
Comment thread src/ui/templates/admin/debug.tsx
Comment thread src/ui/templates/admin/share-rows.tsx

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b8f01eedc0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/shared/site-update.ts
@stefan-burke
stefan-burke force-pushed the claude/jscpd-min-tokens-dedup-jrx2qj branch from b8f01ee to c711218 Compare July 12, 2026 21:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/shared/db/migrations/schema-sync.ts (1)

1-659: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

File exceeds the ~400-line guideline.

schema-sync.ts now spans well past 400 lines. Consider splitting the step definitions (applySchemaChanges, syncIndexes, syncTriggers, backfillListingAttendees/backfillListingAggregates, dropDeprecatedAttendeeColumns, verifyCurrentAppSchema) into focused modules now that stepUsing decouples each from its probe, since this refactor is a natural point to also address file size.

As per coding guidelines, "Keep source and test files under approximately 400 lines; split overloaded files into focused modules and separate pure logic from I/O."

🤖 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 `@src/shared/db/migrations/schema-sync.ts` around lines 1 - 659, The
schema-sync module exceeds the project’s size guideline and contains several
separable migration concerns. Split the step definitions and related logic
around applySchemaChanges, syncIndexes, syncTriggers, backfillListingAttendees,
backfillListingAggregates, dropDeprecatedAttendeeColumns, and
verifyCurrentAppSchema into focused modules, reusing stepUsing and shared
helpers where needed; preserve their existing behavior and exports.

Source: Coding guidelines

♻️ Duplicate comments (3)
src/shared/db/attempt-lockout.ts (1)

23-32: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

TOCTOU race in expired-lockout cleanup still unresolved.

lockoutActive still unconditionally deletes the row by hashedIp once it decides the lockout is expired. A concurrent request could have already written a fresh lockout for the same IP between the read and this delete, and that fresh lockout would be silently discarded, losing rate-limit state. This was flagged on a prior commit and hasn't changed — delete should be conditioned on the stored value still matching what was read (e.g. DELETE ... WHERE ip = ? AND locked_until = ?), ideally in one atomic statement.

🤖 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 `@src/shared/db/attempt-lockout.ts` around lines 23 - 32, The expired-lockout
cleanup in lockoutActive must avoid deleting a newer lockout written after the
read. Replace the unconditional deleteByField call with an atomic conditional
deletion that matches both hashedIp and the previously read lockedUntil value,
using the database helper or query mechanism that supports this predicate.
src/shared/payment-helpers.ts (1)

694-709: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test-only helper still lives in production source.

SignedTestWebhook/signedTestWebhook remain exported from src/shared/payment-helpers.ts despite being documented purely for test use. This was flagged previously and still applies — consider moving it to test/test-utils/.

🤖 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 `@src/shared/payment-helpers.ts` around lines 694 - 709, Move the test-only
SignedTestWebhook type and signedTestWebhook helper out of
src/shared/payment-helpers.ts into test/test-utils/. Update all test imports and
usages to reference the new test utility, and remove their production exports
while preserving the existing payload signing behavior.

Source: Coding guidelines

src/ui/templates/admin/bulk-actions.tsx (1)

34-39: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Duplicate-group form still doesn't refill submitted values on error.

This mirrors the previously flagged, still-unresolved gap: adminDuplicateGroupPage used to accept a values object (per the line-range summary), but this revision drops it entirely in favor of the shared BulkActionPage type, so value={${group.name} (copy)} and the TextFields field arrays are now hardcoded with no way to carry submitted input back in. A failed POST will reset the new-name/find/replace/date-shift inputs even though error is still threaded through correctly.

To fix without breaking the shared BulkActionPage contract used by activateConfirmPage, consider a dedicated params type for the duplicate form (or an optional values field on a page-specific signature) rather than reusing the generic 4-arg type for this page.

Based on learnings, "Admin form pages (including bulk/duplicate-group forms) should re-fill the user's submitted values back into the relevant TextField/TextFields inputs after a failed POST/validation error (flash/form-refill pattern)."

#!/bin/bash
# Check whether the route handler ever forwards submitted values to the render function.
rg -n "adminDuplicateGroupPage" -B3 -A15 src/features/admin/bulk-actions.ts

Also applies to: 174-256

🤖 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 `@src/ui/templates/admin/bulk-actions.tsx` around lines 34 - 39, The
duplicate-group page signature currently cannot receive submitted form values,
so failed submissions reset all fields. Update adminDuplicateGroupPage to use a
dedicated parameters type or page-specific signature that includes values, then
populate the duplicate-name, find/replace, and date-shift TextField/TextFields
inputs from those values while preserving the shared BulkActionPage contract for
other pages such as activateConfirmPage.

Source: Learnings

🤖 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.

Inline comments:
In `@src/features/admin/ledger/entries.ts`:
- Around line 152-171: Update loadedEntryPage to accept a lazy loader function
returning Promise<Loaded | null> instead of a pre-started promise, then invoke
it exactly once when loading the entry. Update all callers to pass loader
thunks, preserving the existing null handling, flash application, and rendering
behavior.

In `@src/features/admin/listings-edit.ts`:
- Line 117: Rename the .find() callback parameter in chosenTemplateOrPicker from
t to a non-conflicting name such as template, and update the callback’s property
access accordingly; preserve the existing template lookup behavior.

In `@src/features/public/ticket-submit/pricing.ts`:
- Line 19: Update the import in pricing.ts so AnswerInfo remains a type-only
import while extractContact is imported as a runtime value, allowing
ReturnType<typeof extractContact> to resolve correctly.

In `@src/shared/accounting/backfill.ts`:
- Around line 87-96: Update the raw SQL in nextPaidAttendeeIds and
stampFromExistingStatement to assign explicit descriptive singular aliases with
AS to listing_attendees and transfers, respectively. Qualify each table-column
reference with its corresponding alias while preserving the existing filtering,
ordering, and pagination behavior.

In `@src/shared/admin-api-example.ts`:
- Line 179: Update the `/api/packages/:slug/book` response example to use the
paid booking example payload instead of `API_BOOK_FREE_EXAMPLE_JSON`, or define
a package-specific paid response payload and reference it in the response
configuration.

In `@src/ui/templates/admin/listings/edit-panel.tsx`:
- Around line 180-191: Replace the hardcoded recalculate URL in the
ListingAggregateMismatchNotice actionHref with the shared adminPath helper,
using the listingRecalculate route and listing.id parameter. Preserve the
existing isReadOnly() gating and undefined behavior for read-only listings.

In `@src/ui/templates/components/aggregate-sections.tsx`:
- Around line 46-53: Update sectionsRenderer to use the curried map utility from
`#fp` instead of the native sections.map method, matching the functional style
used by the other consolidated render helpers while preserving the existing
render order and JSX fragment output.

In `@src/ui/templates/public/site-page.tsx`:
- Around line 9-19: Restore the missing NavNode type import alongside SitePage
in the imports used by sitePagePage, so the items declaration in the
seoContentPage callback type-checks without changing its behavior.

---

Outside diff comments:
In `@src/shared/db/migrations/schema-sync.ts`:
- Around line 1-659: The schema-sync module exceeds the project’s size guideline
and contains several separable migration concerns. Split the step definitions
and related logic around applySchemaChanges, syncIndexes, syncTriggers,
backfillListingAttendees, backfillListingAggregates,
dropDeprecatedAttendeeColumns, and verifyCurrentAppSchema into focused modules,
reusing stepUsing and shared helpers where needed; preserve their existing
behavior and exports.

---

Duplicate comments:
In `@src/shared/db/attempt-lockout.ts`:
- Around line 23-32: The expired-lockout cleanup in lockoutActive must avoid
deleting a newer lockout written after the read. Replace the unconditional
deleteByField call with an atomic conditional deletion that matches both
hashedIp and the previously read lockedUntil value, using the database helper or
query mechanism that supports this predicate.

In `@src/shared/payment-helpers.ts`:
- Around line 694-709: Move the test-only SignedTestWebhook type and
signedTestWebhook helper out of src/shared/payment-helpers.ts into
test/test-utils/. Update all test imports and usages to reference the new test
utility, and remove their production exports while preserving the existing
payload signing behavior.

In `@src/ui/templates/admin/bulk-actions.tsx`:
- Around line 34-39: The duplicate-group page signature currently cannot receive
submitted form values, so failed submissions reset all fields. Update
adminDuplicateGroupPage to use a dedicated parameters type or page-specific
signature that includes values, then populate the duplicate-name, find/replace,
and date-shift TextField/TextFields inputs from those values while preserving
the shared BulkActionPage contract for other pages such as activateConfirmPage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 66f2eccb-a2b5-4f37-9f36-61b3faf650d2

📥 Commits

Reviewing files that changed from the base of the PR and between b8f01ee and c711218.

📒 Files selected for processing (223)
  • .jscpd.json
  • TODO.md
  • scripts/bench/cold-start/first-request-child.ts
  • scripts/find-unused-src.ts
  • scripts/line-counts.ts
  • scripts/mutation/child-process.ts
  • scripts/mutation/generate.ts
  • scripts/mutation/ignore.ts
  • scripts/mutation/isolation.ts
  • scripts/mutation/operators.ts
  • scripts/mutation/runner.ts
  • scripts/profile-cold-boot.ts
  • scripts/static-cdn.ts
  • scripts/stripe-mock.ts
  • scripts/stripe-mock/install.ts
  • scripts/timed-run.ts
  • scripts/unit-tests-report.ts
  • src/features/admin/api-groups.ts
  • src/features/admin/api-keys.ts
  • src/features/admin/api.ts
  • src/features/admin/attendee-form-model.ts
  • src/features/admin/attendee-form-routes.ts
  • src/features/admin/attendee-page.ts
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/attendees-edit.ts
  • src/features/admin/attendees-route-helpers.ts
  • src/features/admin/backup.ts
  • src/features/admin/built-sites.ts
  • src/features/admin/bulk-email.ts
  • src/features/admin/catalog-transfer/import.ts
  • src/features/admin/catalog-transfer/schema.ts
  • src/features/admin/confirmation.ts
  • src/features/admin/content-form-fields.ts
  • src/features/admin/database-reset.ts
  • src/features/admin/entity-write-tab.ts
  • src/features/admin/find-group.ts
  • src/features/admin/images.ts
  • src/features/admin/ledger/entries.ts
  • src/features/admin/listing-page.ts
  • src/features/admin/listings-edit.ts
  • src/features/admin/load-listing.ts
  • src/features/admin/news-form.ts
  • src/features/admin/questions.ts
  • src/features/admin/scanner.ts
  • src/features/admin/servicing.tsx
  • src/features/admin/servicing/page.tsx
  • src/features/admin/sessions.ts
  • src/features/admin/settings-helpers.ts
  • src/features/admin/settings-logistics.ts
  • src/features/admin/settings-sumup.ts
  • src/features/admin/settings-superuser.ts
  • src/features/admin/settings-wallets.ts
  • src/features/admin/site-content.ts
  • src/features/admin/site-pages-form.ts
  • src/features/admin/site.ts
  • src/features/admin/support.ts
  • src/features/admin/update.ts
  • src/features/admin/users.ts
  • src/features/api/booking.ts
  • src/features/api/folded-booking.ts
  • src/features/api/listings.ts
  • src/features/api/packages.ts
  • src/features/api/payment-processing/classify.ts
  • src/features/api/payment-processing/create.ts
  • src/features/api/public-listing.ts
  • src/features/api/webhooks.ts
  • src/features/auth.ts
  • src/features/join.ts
  • src/features/public/news.ts
  • src/features/public/site-page.ts
  • src/features/public/ticket-form.ts
  • src/features/public/ticket-submit.ts
  • src/features/public/ticket-submit/parse.ts
  • src/features/public/ticket-submit/paths.ts
  • src/features/public/ticket-submit/prepare.ts
  • src/features/public/ticket-submit/pricing.ts
  • src/features/tickets/token-utils.ts
  • src/locales/en/attendees.json
  • src/locales/en/settings.json
  • src/shared/accounting/backfill.ts
  • src/shared/accounting/rows.ts
  • src/shared/admin-api-example.ts
  • src/shared/app-forms.ts
  • src/shared/booking.ts
  • src/shared/booking/fold-tree.ts
  • src/shared/booking/model.ts
  • src/shared/booking/package-cap.ts
  • src/shared/builder.ts
  • src/shared/crypto/encryption.ts
  • src/shared/crypto/keys.ts
  • src/shared/db/attempt-lockout.ts
  • src/shared/db/attendees/capacity.ts
  • src/shared/db/attendees/servicing.ts
  • src/shared/db/capacity.ts
  • src/shared/db/client.ts
  • src/shared/db/common-schema.ts
  • src/shared/db/groups.ts
  • src/shared/db/images.ts
  • src/shared/db/listing-parents.ts
  • src/shared/db/login-attempts.ts
  • src/shared/db/logistics-agents.ts
  • src/shared/db/migrations.ts
  • src/shared/db/migrations/schema-sync.ts
  • src/shared/db/modifier-resolve.ts
  • src/shared/db/modifiers.ts
  • src/shared/db/query.ts
  • src/shared/db/questions/attendee-answers/save.ts
  • src/shared/db/restore-legacy-columns.ts
  • src/shared/db/table.ts
  • src/shared/db/token-attempts.ts
  • src/shared/deno-deploy-api.ts
  • src/shared/email.ts
  • src/shared/env.ts
  • src/shared/find-by-id.ts
  • src/shared/forms.tsx
  • src/shared/listings-actions.ts
  • src/shared/order/evaluate.ts
  • src/shared/payment-helpers.ts
  • src/shared/payments.ts
  • src/shared/provider-types.ts
  • src/shared/refund-ledger.ts
  • src/shared/response-steps.ts
  • src/shared/rest/crud-api.ts
  • src/shared/rest/resource.ts
  • src/shared/seen-before.ts
  • src/shared/site-secrets.ts
  • src/shared/site-update.ts
  • src/shared/square.ts
  • src/shared/stripe.ts
  • src/shared/sumup-provider.ts
  • src/shared/try-step.ts
  • src/shared/types.ts
  • src/ui/client/admin/payment-test-buttons.ts
  • src/ui/client/order.ts
  • src/ui/templates/admin/activityLog.tsx
  • src/ui/templates/admin/admin-page.tsx
  • src/ui/templates/admin/api-keys.tsx
  • src/ui/templates/admin/attendee-ledger-panel.tsx
  • src/ui/templates/admin/attendee-page.tsx
  • src/ui/templates/admin/attendees.tsx
  • src/ui/templates/admin/availability-checker.tsx
  • src/ui/templates/admin/bulk-actions.tsx
  • src/ui/templates/admin/catalog-transfer.tsx
  • src/ui/templates/admin/dashboard.tsx
  • src/ui/templates/admin/debug.tsx
  • src/ui/templates/admin/delete-link.tsx
  • src/ui/templates/admin/deliveries.tsx
  • src/ui/templates/admin/detail-rows.tsx
  • src/ui/templates/admin/expected-actual.tsx
  • src/ui/templates/admin/groups/list.tsx
  • src/ui/templates/admin/groups/overview.tsx
  • src/ui/templates/admin/guide/components.tsx
  • src/ui/templates/admin/guide/integrations.tsx
  • src/ui/templates/admin/ledger/formatting.tsx
  • src/ui/templates/admin/ledger/statement.tsx
  • src/ui/templates/admin/listing-qr.tsx
  • src/ui/templates/admin/listing-table.tsx
  • src/ui/templates/admin/listings/aggregates.tsx
  • src/ui/templates/admin/listings/attendees.tsx
  • src/ui/templates/admin/listings/capacity-rows.tsx
  • src/ui/templates/admin/listings/edit-panel.tsx
  • src/ui/templates/admin/listings/ledger-section.tsx
  • src/ui/templates/admin/logistics.tsx
  • src/ui/templates/admin/logout.tsx
  • src/ui/templates/admin/money-adjust-section.tsx
  • src/ui/templates/admin/news.tsx
  • src/ui/templates/admin/questions.tsx
  • src/ui/templates/admin/seeds.tsx
  • src/ui/templates/admin/servicing-events.tsx
  • src/ui/templates/admin/sessions.tsx
  • src/ui/templates/admin/settings-advanced.tsx
  • src/ui/templates/admin/settings-nag-banner.tsx
  • src/ui/templates/admin/settings.tsx
  • src/ui/templates/admin/settings/page-shell.tsx
  • src/ui/templates/admin/settings/payment.tsx
  • src/ui/templates/admin/settings/schema-form.tsx
  • src/ui/templates/admin/settings/wallet-settings.tsx
  • src/ui/templates/admin/share-rows.tsx
  • src/ui/templates/admin/site-pages.tsx
  • src/ui/templates/admin/site.tsx
  • src/ui/templates/attendee-table.tsx
  • src/ui/templates/checkin.tsx
  • src/ui/templates/components/aggregate-sections.tsx
  • src/ui/templates/components/div-with-class.tsx
  • src/ui/templates/components/inline-form-button.tsx
  • src/ui/templates/components/item-list.tsx
  • src/ui/templates/components/labelled-row.tsx
  • src/ui/templates/components/new-tab-link.tsx
  • src/ui/templates/components/phone-links.tsx
  • src/ui/templates/components/prose-article.tsx
  • src/ui/templates/components/prose-heading.tsx
  • src/ui/templates/components/prose-section.tsx
  • src/ui/templates/components/question-controls.tsx
  • src/ui/templates/components/raw-paragraph.tsx
  • src/ui/templates/components/reorder.tsx
  • src/ui/templates/components/save-form.tsx
  • src/ui/templates/components/settings-section.tsx
  • src/ui/templates/components/table-scroll.tsx
  • src/ui/templates/components/text-fields.tsx
  • src/ui/templates/payment.tsx
  • src/ui/templates/public/content-page.tsx
  • src/ui/templates/public/homepage.tsx
  • src/ui/templates/public/news.tsx
  • src/ui/templates/public/order-gallery.tsx
  • src/ui/templates/public/reservations/child-block.ts
  • src/ui/templates/public/reservations/day-config.ts
  • src/ui/templates/public/reservations/questions.tsx
  • src/ui/templates/public/shared.tsx
  • src/ui/templates/public/site-page.tsx
  • src/ui/templates/tickets.tsx
  • test/features/public/ticket-form.test.ts
  • test/lib/booking-model-day-counts.test.ts
  • test/lib/booking-model-eligibility.test.ts
  • test/lib/i18n-coverage.test.ts
  • test/lib/server-built-sites-update.test.ts
  • test/shared/admin-api-example.test.ts
  • test/shared/booking/package-cap.test.ts
  • test/shared/seen-before.test.ts
  • test/shared/try-step.test.ts
  • test/ui/templates/admin/built-sites.test.ts
  • test/ui/templates/admin/expected-actual.test.tsx
  • test/ui/templates/admin/ledger/statement.test.ts
  • test/ui/templates/admin/listings/edit-panel.test.ts
💤 Files with no reviewable changes (1)
  • test/shared/booking/package-cap.test.ts

Comment thread src/features/admin/ledger/entries.ts
Comment thread src/features/admin/listings-edit.ts Outdated
Comment thread src/features/public/ticket-submit/pricing.ts
Comment thread src/shared/accounting/backfill.ts
Comment thread src/shared/admin-api-example.ts
Comment thread src/ui/templates/admin/listings/edit-panel.tsx
Comment thread src/ui/templates/components/aggregate-sections.tsx
Comment thread src/ui/templates/public/site-page.tsx
@stefan-burke
stefan-burke force-pushed the claude/jscpd-min-tokens-dedup-jrx2qj branch from c711218 to 2eae1ae Compare July 13, 2026 03:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ui/templates/admin/settings/payment.tsx (1)

289-291: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

SumUpForm still has hard-coded copy instead of t().

The where_to_find label was fixed to use t(...), but the hint paragraph (Lines 289-291) and testLabel/updateLabel (Lines 302-303) remain literal strings, unlike the equivalent StripeForm fields which all go through t("settings.stripe.*").

♻️ Proposed fix
-          {s.sumupKeyConfigured
-            ? "A SumUp API key is currently configured. Enter new credentials below to replace them."
-            : "No SumUp API key is configured. Enter your SumUp credentials to enable SumUp payments."}
+          {s.sumupKeyConfigured
+            ? t("settings.sumup.configured_hint")
+            : t("settings.sumup.not_configured_hint")}
-        testLabel="Test Connection"
-        updateLabel="Update SumUp Credentials"
+        testLabel={t("settings.sumup.test_connection")}
+        updateLabel={t("settings.sumup.update_key")}

As per coding guidelines, src/**/*.{ts,tsx} files must "Keep user-facing copy in src/locales/en/*.json and access it through t("key"); do not hard-code user-facing strings in templates."

Also applies to: 302-303

🤖 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 `@src/ui/templates/admin/settings/payment.tsx` around lines 289 - 291, Update
SumUpForm’s hint paragraph and testLabel/updateLabel props to use the existing
translation function t() with settings.sumup translation keys, matching the
equivalent StripeForm fields. Add any missing user-facing strings to the English
locale file and preserve the current text and behavior through translations.

Source: Coding guidelines

♻️ Duplicate comments (2)
src/ui/templates/public/site-page.tsx (1)

9-19: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Missing NavNode import — will fail type-checking.

NavNode is used at Line 19 but never imported; only SitePage is imported from #shared/types.ts. This produces TS2304: Cannot find name 'NavNode'. This was already flagged in a prior review pass but does not appear to have been addressed in this version of the file.

🐛 Proposed fix
-import type { SitePage } from "`#shared/types.ts`";
+import type { NavNode, SitePage } from "`#shared/types.ts`";
🤖 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 `@src/ui/templates/public/site-page.tsx` around lines 9 - 19, Import the
NavNode type in the sitePagePage module alongside SitePage from `#shared/types.ts`
so the items declaration resolves during type-checking.
src/features/public/ticket-submit/pricing.ts (1)

19-19: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

extractContact can't stay a type-only import.

ReturnType<typeof extractContact> at lines 42 and 82 requires extractContact as a value binding, but import type { AnswerInfo, extractContact } erases it entirely at compile time — this raises TS1361 ('extractContact' cannot be used as a value because it was imported using 'import type'). Split the import: keep AnswerInfo type-only, import extractContact as a value.

This was already flagged on a previous commit and appears unresolved in the current code.

🐛 Proposed fix
-import type { AnswerInfo, extractContact } from "../ticket-form.ts";
+import type { AnswerInfo } from "../ticket-form.ts";
+import { extractContact } from "../ticket-form.ts";
🤖 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 `@src/features/public/ticket-submit/pricing.ts` at line 19, Update the import
in pricing.ts so AnswerInfo remains a type-only import while extractContact is
imported as a runtime value, allowing the existing ReturnType<typeof
extractContact> usages to compile.
🤖 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.

Inline comments:
In `@src/features/admin/update.ts`:
- Around line 89-95: Update the deployAndReport call in the update flow so
successPrefix uses the localized t(...) lookup instead of the hard-coded
“Updated” string. Add or reuse an appropriate locale key, while leaving
logPrefix unchanged as the internal activity-log label.

In `@src/features/public/ticket-form.ts`:
- Around line 104-114: Define a named exported return type for resolvePageDate,
capturing both the successful date result and the invalid-date error result,
then replace the function’s inline union annotation with that type. Keep the
existing behavior and discriminating ok fields unchanged.

In `@src/features/public/ticket-submit.ts`:
- Line 361: Replace the inline flattening of ctx.childrenByParentId.values() in
the surrounding ticket submission logic with the existing allChildListings
helper, including the children assignment, so all child-listing aggregation uses
the shared implementation.

In `@src/shared/booking/package-cap.ts`:
- Around line 325-332: Add a concise comment immediately before the non-null
assertion in sharedChildGroupLimits, documenting that application invariants
guarantee every groupId from needs is present in ctx.groupRemainingByGroupId.
Keep the existing lookup and assertion unchanged.

In `@src/shared/site-update.ts`:
- Around line 51-75: Update deployAndReport to obtain the success flash message
through the existing localization helper t, using a new admin.update.deployed
key in the appropriate English locale JSON with the required sentence-case
wording. Pass the localized message the existing successPrefix and
result.value.name parameters require, while preserving the current response
flow.

In `@src/ui/templates/admin/api-keys.tsx`:
- Around line 293-308: Replace the hardcoded JSX intro in the admin entry of
endpointDocsSections with t("api_keys.admin_api_note"), and add that key with
the equivalent sentence-case text to the appropriate English locale JSON file.
Keep the public API intro and endpoint configuration unchanged.

In `@src/ui/templates/admin/delete-link.tsx`:
- Around line 11-13: Update the exported rowDeleteLink helper to explicitly
declare the return type of its outer curried function, while preserving the
existing inner callback parameter and JSX.Element return annotations.

In `@src/ui/templates/admin/servicing-events.tsx`:
- Around line 29-45: Update upcomingServicingRow to replace the inline
listing-count pluralization with the existing t() localization helper, using the
pluralized admin.servicing.listing_count key. Add the corresponding
singular/plural entry to admin.json following the format of other admin count
strings, while preserving the current listingCount value and details layout.

---

Outside diff comments:
In `@src/ui/templates/admin/settings/payment.tsx`:
- Around line 289-291: Update SumUpForm’s hint paragraph and
testLabel/updateLabel props to use the existing translation function t() with
settings.sumup translation keys, matching the equivalent StripeForm fields. Add
any missing user-facing strings to the English locale file and preserve the
current text and behavior through translations.

---

Duplicate comments:
In `@src/features/public/ticket-submit/pricing.ts`:
- Line 19: Update the import in pricing.ts so AnswerInfo remains a type-only
import while extractContact is imported as a runtime value, allowing the
existing ReturnType<typeof extractContact> usages to compile.

In `@src/ui/templates/public/site-page.tsx`:
- Around line 9-19: Import the NavNode type in the sitePagePage module alongside
SitePage from `#shared/types.ts` so the items declaration resolves during
type-checking.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 631582ad-ef9f-4495-831c-e94d4b453021

📥 Commits

Reviewing files that changed from the base of the PR and between c711218 and 2eae1ae.

📒 Files selected for processing (224)
  • .jscpd.json
  • TODO.md
  • scripts/bench/cold-start/first-request-child.ts
  • scripts/find-unused-src.ts
  • scripts/line-counts.ts
  • scripts/mutation/child-process.ts
  • scripts/mutation/generate.ts
  • scripts/mutation/ignore.ts
  • scripts/mutation/isolation.ts
  • scripts/mutation/operators.ts
  • scripts/mutation/runner.ts
  • scripts/profile-cold-boot.ts
  • scripts/static-cdn.ts
  • scripts/stripe-mock.ts
  • scripts/stripe-mock/install.ts
  • scripts/timed-run.ts
  • scripts/unit-tests-report.ts
  • src/features/admin/api-groups.ts
  • src/features/admin/api-keys.ts
  • src/features/admin/api.ts
  • src/features/admin/attendee-form-model.ts
  • src/features/admin/attendee-form-routes.ts
  • src/features/admin/attendee-page.ts
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/attendees-edit.ts
  • src/features/admin/attendees-route-helpers.ts
  • src/features/admin/backup.ts
  • src/features/admin/built-sites.ts
  • src/features/admin/bulk-email.ts
  • src/features/admin/catalog-transfer/import.ts
  • src/features/admin/catalog-transfer/schema.ts
  • src/features/admin/confirmation.ts
  • src/features/admin/content-form-fields.ts
  • src/features/admin/database-reset.ts
  • src/features/admin/entity-write-tab.ts
  • src/features/admin/find-group.ts
  • src/features/admin/images.ts
  • src/features/admin/ledger/entries.ts
  • src/features/admin/listing-page.ts
  • src/features/admin/listings-edit.ts
  • src/features/admin/load-listing.ts
  • src/features/admin/news-form.ts
  • src/features/admin/questions.ts
  • src/features/admin/scanner.ts
  • src/features/admin/servicing.tsx
  • src/features/admin/servicing/page.tsx
  • src/features/admin/sessions.ts
  • src/features/admin/settings-helpers.ts
  • src/features/admin/settings-logistics.ts
  • src/features/admin/settings-sumup.ts
  • src/features/admin/settings-superuser.ts
  • src/features/admin/settings-wallets.ts
  • src/features/admin/site-content.ts
  • src/features/admin/site-pages-form.ts
  • src/features/admin/site.ts
  • src/features/admin/support.ts
  • src/features/admin/update.ts
  • src/features/admin/users.ts
  • src/features/api/booking.ts
  • src/features/api/folded-booking.ts
  • src/features/api/listings.ts
  • src/features/api/packages.ts
  • src/features/api/payment-processing/classify.ts
  • src/features/api/payment-processing/create.ts
  • src/features/api/public-listing.ts
  • src/features/api/webhooks.ts
  • src/features/auth.ts
  • src/features/join.ts
  • src/features/public/news.ts
  • src/features/public/site-page.ts
  • src/features/public/ticket-form.ts
  • src/features/public/ticket-payment.ts
  • src/features/public/ticket-submit.ts
  • src/features/public/ticket-submit/parse.ts
  • src/features/public/ticket-submit/paths.ts
  • src/features/public/ticket-submit/prepare.ts
  • src/features/public/ticket-submit/pricing.ts
  • src/features/tickets/token-utils.ts
  • src/locales/en/attendees.json
  • src/locales/en/settings.json
  • src/shared/accounting/backfill.ts
  • src/shared/accounting/rows.ts
  • src/shared/admin-api-example.ts
  • src/shared/app-forms.ts
  • src/shared/booking.ts
  • src/shared/booking/fold-tree.ts
  • src/shared/booking/model.ts
  • src/shared/booking/package-cap.ts
  • src/shared/builder.ts
  • src/shared/crypto/encryption.ts
  • src/shared/crypto/keys.ts
  • src/shared/db/attempt-lockout.ts
  • src/shared/db/attendees/capacity.ts
  • src/shared/db/attendees/servicing.ts
  • src/shared/db/capacity.ts
  • src/shared/db/client.ts
  • src/shared/db/common-schema.ts
  • src/shared/db/groups.ts
  • src/shared/db/images.ts
  • src/shared/db/listing-parents.ts
  • src/shared/db/login-attempts.ts
  • src/shared/db/logistics-agents.ts
  • src/shared/db/migrations.ts
  • src/shared/db/migrations/schema-sync.ts
  • src/shared/db/modifier-resolve.ts
  • src/shared/db/modifiers.ts
  • src/shared/db/query.ts
  • src/shared/db/questions/attendee-answers/save.ts
  • src/shared/db/restore-legacy-columns.ts
  • src/shared/db/table.ts
  • src/shared/db/token-attempts.ts
  • src/shared/deno-deploy-api.ts
  • src/shared/email.ts
  • src/shared/env.ts
  • src/shared/find-by-id.ts
  • src/shared/forms.tsx
  • src/shared/listings-actions.ts
  • src/shared/order/evaluate.ts
  • src/shared/payment-helpers.ts
  • src/shared/payments.ts
  • src/shared/provider-types.ts
  • src/shared/refund-ledger.ts
  • src/shared/response-steps.ts
  • src/shared/rest/crud-api.ts
  • src/shared/rest/resource.ts
  • src/shared/seen-before.ts
  • src/shared/site-secrets.ts
  • src/shared/site-update.ts
  • src/shared/square.ts
  • src/shared/stripe.ts
  • src/shared/sumup-provider.ts
  • src/shared/try-step.ts
  • src/shared/types.ts
  • src/ui/client/admin/payment-test-buttons.ts
  • src/ui/client/order.ts
  • src/ui/templates/admin/activityLog.tsx
  • src/ui/templates/admin/admin-page.tsx
  • src/ui/templates/admin/api-keys.tsx
  • src/ui/templates/admin/attendee-ledger-panel.tsx
  • src/ui/templates/admin/attendee-page.tsx
  • src/ui/templates/admin/attendees.tsx
  • src/ui/templates/admin/availability-checker.tsx
  • src/ui/templates/admin/bulk-actions.tsx
  • src/ui/templates/admin/catalog-transfer.tsx
  • src/ui/templates/admin/dashboard.tsx
  • src/ui/templates/admin/debug.tsx
  • src/ui/templates/admin/delete-link.tsx
  • src/ui/templates/admin/deliveries.tsx
  • src/ui/templates/admin/detail-rows.tsx
  • src/ui/templates/admin/expected-actual.tsx
  • src/ui/templates/admin/groups/list.tsx
  • src/ui/templates/admin/groups/overview.tsx
  • src/ui/templates/admin/guide/components.tsx
  • src/ui/templates/admin/guide/integrations.tsx
  • src/ui/templates/admin/ledger/formatting.tsx
  • src/ui/templates/admin/ledger/statement.tsx
  • src/ui/templates/admin/listing-qr.tsx
  • src/ui/templates/admin/listing-table.tsx
  • src/ui/templates/admin/listings/aggregates.tsx
  • src/ui/templates/admin/listings/attendees.tsx
  • src/ui/templates/admin/listings/capacity-rows.tsx
  • src/ui/templates/admin/listings/edit-panel.tsx
  • src/ui/templates/admin/listings/ledger-section.tsx
  • src/ui/templates/admin/logistics.tsx
  • src/ui/templates/admin/logout.tsx
  • src/ui/templates/admin/money-adjust-section.tsx
  • src/ui/templates/admin/news.tsx
  • src/ui/templates/admin/questions.tsx
  • src/ui/templates/admin/seeds.tsx
  • src/ui/templates/admin/servicing-events.tsx
  • src/ui/templates/admin/sessions.tsx
  • src/ui/templates/admin/settings-advanced.tsx
  • src/ui/templates/admin/settings-nag-banner.tsx
  • src/ui/templates/admin/settings.tsx
  • src/ui/templates/admin/settings/page-shell.tsx
  • src/ui/templates/admin/settings/payment.tsx
  • src/ui/templates/admin/settings/schema-form.tsx
  • src/ui/templates/admin/settings/wallet-settings.tsx
  • src/ui/templates/admin/share-rows.tsx
  • src/ui/templates/admin/site-pages.tsx
  • src/ui/templates/admin/site.tsx
  • src/ui/templates/attendee-table.tsx
  • src/ui/templates/checkin.tsx
  • src/ui/templates/components/aggregate-sections.tsx
  • src/ui/templates/components/div-with-class.tsx
  • src/ui/templates/components/inline-form-button.tsx
  • src/ui/templates/components/item-list.tsx
  • src/ui/templates/components/labelled-row.tsx
  • src/ui/templates/components/new-tab-link.tsx
  • src/ui/templates/components/phone-links.tsx
  • src/ui/templates/components/prose-article.tsx
  • src/ui/templates/components/prose-heading.tsx
  • src/ui/templates/components/prose-section.tsx
  • src/ui/templates/components/question-controls.tsx
  • src/ui/templates/components/raw-paragraph.tsx
  • src/ui/templates/components/reorder.tsx
  • src/ui/templates/components/save-form.tsx
  • src/ui/templates/components/settings-section.tsx
  • src/ui/templates/components/table-scroll.tsx
  • src/ui/templates/components/text-fields.tsx
  • src/ui/templates/payment.tsx
  • src/ui/templates/public/content-page.tsx
  • src/ui/templates/public/homepage.tsx
  • src/ui/templates/public/news.tsx
  • src/ui/templates/public/order-gallery.tsx
  • src/ui/templates/public/reservations/child-block.ts
  • src/ui/templates/public/reservations/day-config.ts
  • src/ui/templates/public/reservations/questions.tsx
  • src/ui/templates/public/shared.tsx
  • src/ui/templates/public/site-page.tsx
  • src/ui/templates/tickets.tsx
  • test/features/public/ticket-form.test.ts
  • test/lib/booking-model-day-counts.test.ts
  • test/lib/booking-model-eligibility.test.ts
  • test/lib/i18n-coverage.test.ts
  • test/lib/server-built-sites-update.test.ts
  • test/shared/admin-api-example.test.ts
  • test/shared/booking/package-cap.test.ts
  • test/shared/seen-before.test.ts
  • test/shared/try-step.test.ts
  • test/ui/templates/admin/built-sites.test.ts
  • test/ui/templates/admin/expected-actual.test.tsx
  • test/ui/templates/admin/ledger/statement.test.ts
  • test/ui/templates/admin/listings/edit-panel.test.ts
💤 Files with no reviewable changes (1)
  • test/shared/booking/package-cap.test.ts

Comment thread src/features/admin/update.ts
Comment thread src/features/public/ticket-form.ts
Comment thread src/features/public/ticket-submit.ts
Comment thread src/shared/booking/package-cap.ts
Comment thread src/shared/site-update.ts
Comment thread src/ui/templates/admin/api-keys.tsx
Comment thread src/ui/templates/admin/delete-link.tsx
Comment thread src/ui/templates/admin/servicing-events.tsx
@stefan-burke
stefan-burke enabled auto-merge July 13, 2026 04:37
@stefan-burke
stefan-burke force-pushed the claude/jscpd-min-tokens-dedup-jrx2qj branch from ae87bdf to a51b88f Compare July 13, 2026 05:19
Lowers the jscpd minimum-match threshold from 21 to 20 tokens and removes
every duplicate the stricter setting surfaces (159 clones across the source
tree). As with the previous notches, each clone was fixed by making the two
copies one thing — a shared helper or a curried factory — never by reshaping
code so the matcher stops matching.

Highlights of the merges:
- Booking: one child-eligibility check, one day-count path, one folded-booking
  finish, one availability check shared by the API and web pages.
- Database access: id→name/column reads and "id set from rows" fold into the
  shared query helpers; hand-rolled group loops use the standard groupBy; a
  shared rate-limit "attempt lockout" mechanism replaces two parallel ones.
- Admin routing: route guards fold onto the existing authed-form factories;
  new shared feature-gate, find-by-id, response-step and try-step helpers.
- Payments: one checkout retrieve/sign path per concept; a shared
  new-tab-link component; the session-metadata type reuses the contact type.
- UI: a listing-table module extracted from the dashboard, plus shared
  prose, table-scroll, labelled-row, settings-section and question-control
  components; the debug page is now a data table rendered by one fold.
- News and site pages unify onto one content-form, one public slug route, and
  one SEO content page; expected-vs-actual recalculation shares one mechanism.
- Scripts: a shared timed-runner and env builder; the real sleep/delay is
  imported instead of re-implemented.

Also folds in review follow-ups: replaces `??`-defaulting with explicit
checks in the booking date and unary-mutation paths, narrows non-Error throws
in try-step, gives the read-only listing view no dead recalculate link, moves
the SumUp guide label into the message catalog, and annotates exported
helpers with explicit return types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTrbBujCcBu8CUC56dy4tQ
@stefan-burke
stefan-burke force-pushed the claude/jscpd-min-tokens-dedup-jrx2qj branch from a51b88f to a032024 Compare July 13, 2026 05:27
Fold the last inlined "flatten every parent's children" spot in the ticket
handler into the shared allChildListings helper, merge the three payment
provider settings intros (Stripe, Square, SumUp) into one ProviderIntro block,
and move the SumUp and service-event-count copy into the message catalog.

Also tightens a few types and adds a missing-lookup invariant note raised in
review; pre-existing copy and behaviour points are recorded in TODO.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTrbBujCcBu8CUC56dy4tQ
@stefan-burke
stefan-burke force-pushed the claude/jscpd-min-tokens-dedup-jrx2qj branch from a032024 to edfbd49 Compare July 13, 2026 05:31
@stefan-burke
stefan-burke added this pull request to the merge queue Jul 13, 2026
Merged via the queue into main with commit f9bbb2e Jul 13, 2026
3 checks passed
@stefan-burke
stefan-burke deleted the claude/jscpd-min-tokens-dedup-jrx2qj branch July 13, 2026 05:39
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