Skip to content

Make refunds safe to retry, recover, and review - #2065

Merged
stefan-burke merged 243 commits into
mainfrom
claude/m4-pr-a
Aug 17, 2026
Merged

Make refunds safe to retry, recover, and review#2065
stefan-burke merged 243 commits into
mainfrom
claude/m4-pr-a

Conversation

@stefan-burke

@stefan-burke stefan-burke commented Aug 10, 2026

Copy link
Copy Markdown
Member

Why

Refunds used to depend on partial signals: the currently configured provider,
nullable reads, boolean send results, attendee notes, and route-local retry
rules. That left gaps where a lost provider reply or failed local write could
send money twice, hide returned money, or create work with no safe exit.

PR4-A replaces those seams with one durable refund authority and one declared
payment-work lifecycle. The system sends only from exact stored facts, records
what it learns before releasing blockers, and fails closed when it cannot prove
what happened.

What changed

One refund engine

  • Stripe, Square, and SumUp now implement the same exhaustive read and send
    contracts. Missing, unavailable, invalid, completed, accepted, rejected,
    unsent, and uncertain outcomes cannot be collapsed into booleans or nulls.
  • Single Refund, Refund All, Refresh, callbacks, and placeholder recovery use
    the same provider identity, authority, Money recording, and settlement path.
  • Captured currency and amount are explicit. Provider identity is stored with
    the charge and is never guessed from current settings.
  • Returned money prevents another automatic send. Keyed requests may replay the
    same generation; uncertain keyless requests become owner work.
  • Claims are fenced by the attendee revision and complete reference set. Throws
    retain the claim or persist returned-but-unrecorded money before propagating.

Forward-only payment provenance

New attendee rows carry one non-sensitive provenance field:

  • NULL: historical or otherwise unqualified payment history;
  • "": the stored PII was proven to contain no payment id;
  • a session id: the exact canonical processed_payments row that proves the
    encrypted PII payment id.

New paid bookings write this proof in the booking transaction. Placeholder
creation atomically writes the attendee, payment anchor, provenance, ready
refund authority, and terminal checkout outcome. Merge propagates uncertainty;
pruning retains the row named by proven provenance.

Historical/unqualified payments are intentionally not refundable in the app. The
operator refunds those directly in Stripe, Square, or SumUp. There is no
population decrypt, backfill, re-save requirement, compatibility reader, guessed
provider, or legacy refund engine.

Blocking states have reachable exits

  • One exhaustive lifecycle table names what every payment state blocks, what
    clears it, the owner route that reaches that clearer, and whether a real money
    choice is required.
  • One PaymentMoveSnapshot drives attendee actions, Delete confirmation, merge
    preview, and the transactional delete/merge writers. A link is never offered
    when the writer must refuse it; a race still fails inside the transaction.
  • Payment work follows a merge and remains visible even when the target has no
    legacy PII payment id.
  • Owners alone may send refunds or acknowledge money reviews. Managers may only
    refresh existing evidence.

Bounded work and privacy

  • Refund All uses an indexed, PII-free SQL summary. A submission selects one
    candidate with LIMIT 1 and decrypts at most that one attendee.
  • The summary refuses NULL provenance, mismatched provenance, paid bookings
    without a canonical row, and reference drift before any provider call.
  • One attendee may expose at most ten indexed references. Shared-reference
    claims refuse above 100 holders before protected state is opened or written.
  • Raw provider references and new attendee PII use owner-public-key ciphertext.
    Equality uses DB-keyed one-way indexes. Possessing the database and
    DB_ENCRYPTION_KEY alone cannot open those values when the modern wrapped
    owner key is in use.
  • Provider credentials and finite operational failure metadata remain
    DB-key-encrypted; that boundary is explicit rather than presented as PII
    secrecy.

No parallel compatibility path

The durable provider-refund authority is the only live refund engine.
Architecture tests reject ambient provider selection, credential enumeration,
direct adapter calls, extra send-permit minters, and extra authority writers in
refund-facing code.

The remaining pre-cutover whole-checkout selector is confined to callback
session retrieval and authentication. It cannot authorize or send a refund. The
later aggregate cutover must replace and delete it in one release rather than
add a bridge or fallback.

What the tests show

  • Visitor journeys cover booking, owner and manager permissions, pre-send
    checks, delayed/partial/uncertain outcomes, failed local recording, moved
    work, blocked Delete/Merge controls, required owner choices, and historical
    PII-only deposits mixed with new indexed balance payments.
  • Direct tests pin provider schemas, placeholder transaction rollback,
    post-terminal recovery, claim and merge races, pruning, bounded Refund All
    admission, privacy-safe reads, migration/restore parity, and every lifecycle
    exit.
  • The exact CI gate passes with 100% line and branch coverage and 0% duplicated
    code. Targeted mutation detected all non-equivalent changes in the new Refund
    All and lifecycle decision modules (32/32 and 61 kills plus one recorded
    sign-equivalent comparator mutation).

Scope and durable record

This is M4 / PR4 Part A. Stable per-order booking obligations, exact allocation
for shared or merged charges, free package-member cancellation, and the atomic
aggregate cutover remain named future work in PLAN.md and TODO.md.

The temporary PR4_PLAN.md has been removed. Its useful as-built decisions,
deliberate limits, privacy boundary, and future constraints now live in the
permanent plans, code, tests, and reports.

  • branch.html is the database-first five-minute briefing.
  • src-diff-distilled.html removes imports, comments, blanks, moves, renames,
    and file-split noise from the source comparison.

Verification

  • env CI=1 nix develop -c deno task precommit
  • full direct suite with enforced 100% line and branch coverage
  • full Cucumber catalogue
  • zero source, test, spec, or stylesheet duplication
  • focused provider-authority, lifecycle, Refund All, merge/delete, pruning, and
    migration regressions
  • independent combined audit of provenance, atomicity, reachability,
    performance, and encryption boundaries

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Too many files!

This PR contains 666 files, which is 366 over the limit of 300.

To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f52d991e-6c83-40e1-9394-d0f41e59ceb4

📥 Commits

Reviewing files that changed from the base of the PR and between b19c6a8 and 0dcb0a6.

⛔ Files ignored due to path filters (1)
  • deno.lock is excluded by !**/*.lock
📒 Files selected for processing (666)
  • .gitignore
  • AGENTS.md
  • PLAN.md
  • PR3_PLAN.md
  • PR4_PLAN.md
  • TODO.md
  • branch.html
  • e2e-payments/README.md
  • e2e-payments/deno.json
  • e2e-payments/specs/live-payment-providers.feature
  • e2e-payments/src/browser.ts
  • e2e-payments/src/cleanup.ts
  • e2e-payments/src/config.ts
  • e2e-payments/src/cucumber/steps/booking.ts
  • e2e-payments/src/cucumber/steps/pages.ts
  • e2e-payments/src/cucumber/steps/refund.ts
  • e2e-payments/src/cucumber/steps/setup.ts
  • e2e-payments/src/cucumber/support/hooks.ts
  • e2e-payments/src/cucumber/support/journal.ts
  • e2e-payments/src/cucumber/support/world.ts
  • e2e-payments/src/db-fault.ts
  • e2e-payments/src/flow.ts
  • e2e-payments/src/main.ts
  • e2e-payments/src/order-flow.ts
  • e2e-payments/src/providers/shared.ts
  • e2e-payments/src/providers/square.ts
  • e2e-payments/src/providers/stripe.ts
  • e2e-payments/src/providers/sumup-callback.ts
  • e2e-payments/src/providers/sumup.ts
  • e2e-payments/src/providers/types.ts
  • e2e-payments/src/refund-outcome.ts
  • e2e-payments/src/server.ts
  • e2e-payments/src/targets.ts
  • flake.nix
  • scripts/mutation/equivalent-mutants/features.txt
  • scripts/mutation/equivalent-mutants/scripts.txt
  • scripts/mutation/equivalent-mutants/shared-a-l.txt
  • scripts/mutation/equivalent-mutants/shared-db.txt
  • scripts/mutation/equivalent-mutants/shared-m-z.txt
  • scripts/mutation/equivalent-mutants/ui-client.txt
  • scripts/run-tests.ts
  • specs/payments/checking-before-a-refund.feature
  • specs/payments/only-owners-refund.feature
  • specs/payments/recovering-the-money-record.feature
  • specs/payments/refunding-a-booking.feature
  • specs/payments/refunding-everyone-at-once.feature
  • specs/payments/refunding-from-two-windows.feature
  • specs/payments/resolving-uncertain-refunds.feature
  • specs/payments/waiting-for-a-refund.feature
  • src-diff-distilled.html
  • src/features/admin/area-loaders.ts
  • src/features/admin/attendee-delete.ts
  • src/features/admin/attendee-form-lines.ts
  • src/features/admin/attendee-form-routes.ts
  • src/features/admin/attendee-logistics.ts
  • src/features/admin/attendee-page-data.ts
  • src/features/admin/attendee-page.ts
  • src/features/admin/attendee-payment-review.ts
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/attendee-refunds/bulk.ts
  • src/features/admin/attendee-refunds/single-result.ts
  • src/features/admin/attendee-refunds/single.ts
  • src/features/admin/attendees-edit.ts
  • src/features/admin/attendees-list.ts
  • src/features/admin/attendees-merge.ts
  • src/features/admin/attendees-route-helpers.ts
  • src/features/admin/attendees.ts
  • src/features/admin/bulk-email.ts
  • src/features/admin/entity-pages.ts
  • src/features/admin/ledger/statements.ts
  • src/features/admin/listing-page.ts
  • src/features/admin/privacy.ts
  • src/features/admin/refunds/attempt.ts
  • src/features/admin/refunds/authority.ts
  • src/features/admin/refunds/budget.ts
  • src/features/admin/refunds/candidates.ts
  • src/features/admin/refunds/claim.ts
  • src/features/admin/refunds/confirmation.ts
  • src/features/admin/refunds/dispatch.ts
  • src/features/admin/refunds/ledger-findings.ts
  • src/features/admin/refunds/provider-requests.ts
  • src/features/admin/refunds/provider.ts
  • src/features/admin/refunds/readiness-findings.ts
  • src/features/admin/refunds/readiness-problem.ts
  • src/features/admin/refunds/readiness-run.ts
  • src/features/admin/refunds/readiness.ts
  • src/features/admin/refunds/ready-admission.ts
  • src/features/admin/refunds/refresh.ts
  • src/features/admin/refunds/report.ts
  • src/features/admin/refunds/result-findings.ts
  • src/features/admin/refunds/row-reviews.ts
  • src/features/admin/refunds/waves.ts
  • src/features/admin/require-provider.ts
  • src/features/admin/schema-atlas.ts
  • src/features/admin/servicing.tsx
  • src/features/admin/settings-square.ts
  • src/features/admin/sms.ts
  • src/features/api/payment-processing/classify.ts
  • src/features/api/payment-processing/create.ts
  • src/features/api/payment-processing/index.ts
  • src/features/api/payment-processing/pricing.ts
  • src/features/api/payment-processing/recovery.ts
  • src/features/api/payment-processing/refunds.ts
  • src/features/api/payment-processing/store-refund.ts
  • src/features/api/payment-success.ts
  • src/features/api/webhooks.ts
  • src/features/read-json-body.ts
  • src/locales/en/attendees.json
  • src/locales/en/guide-accounts.json
  • src/locales/en/nav.json
  • src/locales/en/privacy.json
  • src/locales/en/schema-atlas.json
  • src/locales/manifest.ts
  • src/shared/accounting/conflicts.ts
  • src/shared/accounting/mappers.ts
  • src/shared/accounting/queries.ts
  • src/shared/accounting/store.ts
  • src/shared/admin-surface/definitions.ts
  • src/shared/admin-surface/nav-routes.ts
  • src/shared/admin-surface/write-routes-a-m.ts
  • src/shared/booking.ts
  • src/shared/bulk-email-targets/listings.ts
  • src/shared/checkout-complete.ts
  • src/shared/currency.ts
  • src/shared/db/attendee-types.ts
  • src/shared/db/attendees/atomic-update.ts
  • src/shared/db/attendees/create-batch.ts
  • src/shared/db/attendees/create.ts
  • src/shared/db/attendees/delete.ts
  • src/shared/db/attendees/dependent-data.ts
  • src/shared/db/attendees/payment-provenance.ts
  • src/shared/db/attendees/pii-write.ts
  • src/shared/db/attendees/pii.ts
  • src/shared/db/attendees/queries.ts
  • src/shared/db/attendees/select.ts
  • src/shared/db/attendees/servicing.ts
  • src/shared/db/attendees/update.ts
  • src/shared/db/client.ts
  • src/shared/db/listing-overview-stats.ts
  • src/shared/db/migrations/2026-07-26_payment_records.ts
  • src/shared/db/migrations/2026-08-10_refund_authority_records.ts
  • src/shared/db/migrations/registry.ts
  • src/shared/db/migrations/schema-sync.ts
  • src/shared/db/migrations/schema/payments/charges.ts
  • src/shared/db/migrations/schema/payments/columns.ts
  • src/shared/db/migrations/schema/payments/index.ts
  • src/shared/db/migrations/schema/payments/refund-confirmations.ts
  • src/shared/db/migrations/schema/tables-attendees.ts
  • src/shared/db/migrations/schema/tables-questions.ts
  • src/shared/db/migrations/schema/types.ts
  • src/shared/db/migrations/schema/version.ts
  • src/shared/db/notes/queries.ts
  • src/shared/db/notes/sealing.ts
  • src/shared/db/notes/types.ts
  • src/shared/db/orphan-attendees.ts
  • src/shared/db/payment-admit-move.ts
  • src/shared/db/payment-anchor/attendee.ts
  • src/shared/db/payment-anchor/reference.ts
  • src/shared/db/payment-anchor/session.ts
  • src/shared/db/payment-claim.ts
  • src/shared/db/payment-claim/scope.ts
  • src/shared/db/payment-claim/take.ts
  • src/shared/db/payment-finalize.ts
  • src/shared/db/payment-reference-rows.ts
  • src/shared/db/payment-reference-store.ts
  • src/shared/db/payment-references.ts
  • src/shared/db/payment-review.ts
  • src/shared/db/processed-payments.ts
  • src/shared/db/provider-refund-authority-change.ts
  • src/shared/db/provider-refund-authority.ts
  • src/shared/db/provider-refund-case-resolution.ts
  • src/shared/db/provider-refund-cases.ts
  • src/shared/db/prune.ts
  • src/shared/db/questions/parsing.ts
  • src/shared/db/refund-all-candidates.ts
  • src/shared/db/refund-confirmations.ts
  • src/shared/existing-payment-provider.ts
  • src/shared/form-data.ts
  • src/shared/ledger-scope.ts
  • src/shared/logger.ts
  • src/shared/logistics-filter.ts
  • src/shared/merge/attendee-merge-types.ts
  • src/shared/merge/attendee-merge.ts
  • src/shared/named-error.ts
  • src/shared/order-select.ts
  • src/shared/payment-dashboard.ts
  • src/shared/payment-helpers.ts
  • src/shared/payment-idempotency.ts
  • src/shared/payment-review.ts
  • src/shared/payment/admit-move.ts
  • src/shared/payment/admit-refund.ts
  • src/shared/payment/checkout-failure.ts
  • src/shared/payment/claim.ts
  • src/shared/payment/conflict.ts
  • src/shared/payment/diagnose.ts
  • src/shared/payment/money.ts
  • src/shared/payment/placeholder-refund.ts
  • src/shared/payment/provider-failures.ts
  • src/shared/payment/provider-read.ts
  • src/shared/payment/provider-reference.ts
  • src/shared/payment/provider-timeout.ts
  • src/shared/payment/refund-attempt.ts
  • src/shared/payment/refund-authority-choice.ts
  • src/shared/payment/refund-authority-lifecycle.ts
  • src/shared/payment/refund-authority-state.ts
  • src/shared/payment/refund-authority.ts
  • src/shared/payment/refund-conflict-decision.ts
  • src/shared/payment/refund-generation.ts
  • src/shared/payment/refund-network.ts
  • src/shared/payment/refund-provider-authorization.ts
  • src/shared/payment/refund-replay-window.ts
  • src/shared/payment/refund-request-identity.ts
  • src/shared/payment/refund.ts
  • src/shared/payment/resource-id.ts
  • src/shared/payment/resources.ts
  • src/shared/payment/review.ts
  • src/shared/payment/row-state.ts
  • src/shared/payment/validated-session.ts
  • src/shared/payments.ts
  • src/shared/provider-refund-cursor.ts
  • src/shared/provider-refunds.ts
  • src/shared/provider-refunds/budget.ts
  • src/shared/provider-refunds/send.ts
  • src/shared/provider-refunds/state.ts
  • src/shared/provider-refunds/target.ts
  • src/shared/provider-refunds/work.ts
  • src/shared/read-json.ts
  • src/shared/refund-ledger.ts
  • src/shared/refund-ledger/log.ts
  • src/shared/refund-ledger/placeholder.ts
  • src/shared/refund-ledger/plan.ts
  • src/shared/refund-ledger/record.ts
  • src/shared/refund-ledger/result.ts
  • src/shared/schema-atlas/index.ts
  • src/shared/schema-atlas/payment-review.ts
  • src/shared/schema-atlas/refund-authority.ts
  • src/shared/schema-atlas/types.ts
  • src/shared/square-provider.ts
  • src/shared/square.ts
  • src/shared/square/api.ts
  • src/shared/square/checkout.ts
  • src/shared/square/client.ts
  • src/shared/square/connection.ts
  • src/shared/square/order.ts
  • src/shared/square/outcomes.ts
  • src/shared/square/payment-outcomes.ts
  • src/shared/square/read.ts
  • src/shared/square/transport.ts
  • src/shared/square/webhook.ts
  • src/shared/stripe-provider.ts
  • src/shared/stripe.ts
  • src/shared/stripe/client.ts
  • src/shared/stripe/endpoints.ts
  • src/shared/stripe/request.ts
  • src/shared/stripe/runtime.ts
  • src/shared/stripe/schemas.ts
  • src/shared/subrequest-budget.ts
  • src/shared/sumup-observation.ts
  • src/shared/sumup-provider.ts
  • src/shared/sumup.ts
  • src/shared/sumup/failures.ts
  • src/shared/sumup/money.ts
  • src/shared/sumup/transaction.ts
  • src/shared/sumup/transport.ts
  • src/shared/sumup/wire.ts
  • src/shared/validation/kind.ts
  • src/shared/validation/number.ts
  • src/shared/validation/string.ts
  • src/ui/client/admin.ts
  • src/ui/client/admin/schema-atlas.ts
  • src/ui/static/style.scss
  • src/ui/templates/admin/attendees.tsx
  • src/ui/templates/admin/attendees/delete-confirm.tsx
  • src/ui/templates/admin/bulk-actions.tsx
  • src/ui/templates/admin/confirm-page.tsx
  • src/ui/templates/admin/privacy.tsx
  • src/ui/templates/admin/provider-refund-cases.tsx
  • src/ui/templates/admin/schema-atlas.tsx
  • src/ui/templates/admin/settings/page-shell.tsx
  • src/ui/templates/components/json-script.tsx
  • src/ui/templates/public/errors.tsx
  • test/e2e-payments/cleanup.test.ts
  • test/e2e-payments/config-log.test.ts
  • test/e2e-payments/db-fault.test.ts
  • test/e2e-payments/parse-target.test.ts
  • test/e2e-payments/refund-outcome.test.ts
  • test/e2e-payments/secrets.test.ts
  • test/e2e-payments/step-coverage.test.ts
  • test/e2e-payments/targets.test.ts
  • test/features/admin/attendee-page-data/refund-actions.test.ts
  • test/features/admin/attendee-page-data/refunds-ui.test.ts
  • test/features/admin/attendee-page.test.ts
  • test/features/admin/attendee-page/actions.test.ts
  • test/features/admin/attendee-page/helpers.ts
  • test/features/admin/attendee-payment-review.test.ts
  • test/features/admin/attendee-refunds/authorization.test.ts
  • test/features/admin/attendee-refunds/balance-payments.test.ts
  • test/features/admin/attendee-refunds/bulk.test.ts
  • test/features/admin/attendee-refunds/bulk/results.test.ts
  • test/features/admin/attendee-refunds/bulk/safety-admission.test.ts
  • test/features/admin/attendee-refunds/pending.test.ts
  • test/features/admin/attendee-refunds/provider-log.test.ts
  • test/features/admin/attendee-refunds/single-result.test.ts
  • test/features/admin/attendee-refunds/single/get.test.ts
  • test/features/admin/attendee-refunds/single/post.test.ts
  • test/features/admin/attendee-refunds/state.test.ts
  • test/features/admin/attendees-edit.test.ts
  • test/features/admin/attendees-route-helpers.test.ts
  • test/features/admin/refunds-helpers.ts
  • test/features/admin/refunds/attempt/outcomes.test.ts
  • test/features/admin/refunds/attempt/unrecorded.test.ts
  • test/features/admin/refunds/budget.test.ts
  • test/features/admin/refunds/candidates.test.ts
  • test/features/admin/refunds/claim-helpers.ts
  • test/features/admin/refunds/claim-settlement.test.ts
  • test/features/admin/refunds/claim.test.ts
  • test/features/admin/refunds/confirmation-fixture.ts
  • test/features/admin/refunds/confirmation.test.ts
  • test/features/admin/refunds/dispatch/budget-lifecycle.test.ts
  • test/features/admin/refunds/dispatch/write-order.test.ts
  • test/features/admin/refunds/provider.test.ts
  • test/features/admin/refunds/provider/batch.test.ts
  • test/features/admin/refunds/provider/batch/budget.test.ts
  • test/features/admin/refunds/provider/batch/claim.test.ts
  • test/features/admin/refunds/provider/batch/ledger-findings.test.ts
  • test/features/admin/refunds/provider/batch/ledger.test.ts
  • test/features/admin/refunds/provider/claim-completion.test.ts
  • test/features/admin/refunds/provider/claim-lifecycle.test.ts
  • test/features/admin/refunds/provider/claim-set-races.test.ts
  • test/features/admin/refunds/provider/claim.test.ts
  • test/features/admin/refunds/provider/dispatch-helpers.ts
  • test/features/admin/refunds/provider/helpers.ts
  • test/features/admin/refunds/provider/ledger-results.ts
  • test/features/admin/refunds/provider/mixed-capability.test.ts
  • test/features/admin/refunds/provider/readiness-helpers.ts
  • test/features/admin/refunds/provider/readiness-integration.test.ts
  • test/features/admin/refunds/provider/waves.test.ts
  • test/features/admin/refunds/readiness-capability.test.ts
  • test/features/admin/refunds/readiness-failure-evidence.test.ts
  • test/features/admin/refunds/readiness-findings/authority-failure.test.ts
  • test/features/admin/refunds/readiness-problem.test.ts
  • test/features/admin/refunds/readiness-run/action-admission.test.ts
  • test/features/admin/refunds/readiness-run/refresh-budget.test.ts
  • test/features/admin/refunds/readiness-run/shared-reference.test.ts
  • test/features/admin/refunds/readiness.test.ts
  • test/features/admin/refunds/readiness/helpers.ts
  • test/features/admin/refunds/refresh/blocking.test.ts
  • test/features/admin/refunds/refresh/helpers.ts
  • test/features/admin/refunds/refresh/returned.test.ts
  • test/features/admin/refunds/refresh/review.test.ts
  • test/features/admin/require-provider.test.ts
  • test/features/admin/schema-atlas/server.test.ts
  • test/features/api/payment-processing/cancel.test.ts
  • test/features/api/payment-processing/classify.test.ts
  • test/features/api/payment-processing/committed-entries.test.ts
  • test/features/api/payment-processing/create.test.ts
  • test/features/api/payment-processing/index/balance.test.ts
  • test/features/api/payment-processing/index/booking.test.ts
  • test/features/api/payment-processing/index/helpers.ts
  • test/features/api/payment-processing/index/refunds.test.ts
  • test/features/api/payment-processing/items.test.ts
  • test/features/api/payment-processing/items/boundaries.test.ts
  • test/features/api/payment-processing/metadata.test.ts
  • test/features/api/payment-processing/recovery.test.ts
  • test/features/api/payment-processing/refunds.test.ts
  • test/features/api/payment-processing/refunds/provider-result.test.ts
  • test/features/api/payment-processing/refunds/rejected-charge.test.ts
  • test/features/api/payment-processing/store-refund-authority.test.ts
  • test/features/api/payment-processing/store-refund-helpers.ts
  • test/features/api/payment-processing/store-refund.test.ts
  • test/features/api/payment-processing/store-refund/balance.test.ts
  • test/features/api/payment-processing/store-refund/shape.test.ts
  • test/features/api/webhooks/callbacks.test.ts
  • test/features/api/webhooks/helpers.ts
  • test/features/api/webhooks/provider.test.ts
  • test/features/api/webhooks/success-details.test.ts
  • test/integration/code-quality.test.ts
  • test/integration/db/attendees/apply-attendee-atomic-edit.test.ts
  • test/integration/db/migration-restore/helpers.ts
  • test/integration/logger/log-error.test.ts
  • test/integration/processed-payments/locking.test.ts
  • test/integration/refund-authority-architecture-fixtures.ts
  • test/integration/refund-authority-architecture.test.ts
  • test/integration/server-balance-webhook.test.ts
  • test/integration/server/attendees/delete-incomplete.test.ts
  • test/integration/server/attendees/delete.test.ts
  • test/integration/server/attendees/merge-payment-busy.test.ts
  • test/integration/server/attendees/payment.test.ts
  • test/integration/server/attendees/placeholder-refresh.test.ts
  • test/integration/server/balance-payment-replay.test.ts
  • test/integration/server/owner-routes.test.ts
  • test/integration/server/package-children.test.ts
  • test/integration/server/package-children/availability.test.ts
  • test/integration/server/package-children/bookings.test.ts
  • test/integration/server/package-children/configuration-drift.test.ts
  • test/integration/server/package-children/helpers.ts
  • test/integration/server/payments-success-refunds.test.ts
  • test/integration/server/payments/cancel.test.ts
  • test/integration/server/payments/confirm.test.ts
  • test/integration/server/payments/purchase.test.ts
  • test/integration/server/payments/replay.test.ts
  • test/integration/server/payments/sales-off-safety.test.ts
  • test/integration/server/payments/success.test.ts
  • test/integration/server/privacy-refund-recovery-active-revision.test.ts
  • test/integration/server/privacy-refund-recovery-helpers.ts
  • test/integration/server/privacy-refund-recovery-race.test.ts
  • test/integration/server/privacy-refund-recovery.test.ts
  • test/integration/server/privacy.test.ts
  • test/integration/server/reservation-edge-cases.test.ts
  • test/integration/server/reservation/deposit-basics.test.ts
  • test/integration/server/reservation/promo-addons.test.ts
  • test/integration/server/settings/square.test.ts
  • test/integration/server/webhook-dual-path.test.ts
  • test/integration/server/webhooks/already-processed-rollback.test.ts
  • test/integration/server/webhooks/can-pay-more-multi-ticket.test.ts
  • test/integration/server/webhooks/can-pay-more-single-ticket.test.ts
  • test/integration/server/webhooks/concurrent-processing.test.ts
  • test/integration/server/webhooks/customisable-days-pricing.test.ts
  • test/integration/server/webhooks/extract-intent-redirect.test.ts
  • test/integration/server/webhooks/item-validation.test.ts
  • test/integration/server/webhooks/modifier-refunds.test.ts
  • test/integration/server/webhooks/multi-ticket-booking.test.ts
  • test/integration/server/webhooks/multi-ticket-refunds.test.ts
  • test/integration/server/webhooks/price-signature-package-overrides.test.ts
  • test/integration/server/webhooks/price-signature-post-commit-recovery.test.ts
  • test/integration/server/webhooks/price-signature-stored-refund-and-ignore.test.ts
  • test/integration/server/webhooks/refund-helper-functions.test.ts
  • test/integration/server/webhooks/refund-logging.test.ts
  • test/integration/server/webhooks/refund-skip-conditions.test.ts
  • test/integration/server/webhooks/registration-closed.test.ts
  • test/integration/server/webhooks/single-ticket-refunds.test.ts
  • test/integration/server/webhooks/square.test.ts
  • test/integration/server/webhooks/sumup.test.ts
  • test/integration/server/webhooks/unrecognized-sessions.test.ts
  • test/integration/servicing/deletion.test.ts
  • test/integration/servicing/purge-edge-cases.test.ts
  • test/integration/stripe-client-parity.test.ts
  • test/integration/stripe/checkout.test.ts
  • test/integration/stripe/core.test.ts
  • test/integration/webhook-price-signature-trusted-and-mismatch.test.ts
  • test/integration/webhook-price-signature/helpers.ts
  • test/scripts/specs/catalog.test.ts
  • test/scripts/specs/fixtures/wait-for-rows.test.ts
  • test/scripts/stripe-mock/lifecycle.test.ts
  • test/shared/accounting/conflicts.test.ts
  • test/shared/accounting/queries/balances.test.ts
  • test/shared/accounting/store.test.ts
  • test/shared/db/attendees/api/create-rollback.test.ts
  • test/shared/db/attendees/balance.test.ts
  • test/shared/db/attendees/create.test.ts
  • test/shared/db/attendees/delete.test.ts
  • test/shared/db/attendees/dependent-data.test.ts
  • test/shared/db/attendees/pii.test.ts
  • test/shared/db/attendees/select-refunded.test.ts
  • test/shared/db/attendees/select.test.ts
  • test/shared/db/client/transaction.test.ts
  • test/shared/db/listing-overview-stats.test.ts
  • test/shared/db/listings/delete.test.ts
  • test/shared/db/migrations/2026-08-10_refund_authority_records.test.ts
  • test/shared/db/migrations/boot.test.ts
  • test/shared/db/migrations/schema/index.test.ts
  • test/shared/db/migrations/schema/payments/charges.test.ts
  • test/shared/db/migrations/schema/payments/columns.test.ts
  • test/shared/db/migrations/schema/payments/schema.test.ts
  • test/shared/db/migrations/schema/tables-questions.test.ts
  • test/shared/db/migrations/schema/triggers/listing-aggregates.test.ts
  • test/shared/db/migrations/schema/version/guard.test.ts
  • test/shared/db/notes/queries.test.ts
  • test/shared/db/orphan-attendees.test.ts
  • test/shared/db/payment-admit-move.test.ts
  • test/shared/db/payment-anchor/attendee.test.ts
  • test/shared/db/payment-anchor/reference.test.ts
  • test/shared/db/payment-claim.test.ts
  • test/shared/db/payment-claim/admission.test.ts
  • test/shared/db/payment-claim/take.test.ts
  • test/shared/db/payment-claim/take/shared-references.test.ts
  • test/shared/db/payment-claim/unrecorded-date.test.ts
  • test/shared/db/payment-reference-rows.test.ts
  • test/shared/db/payment-reference-store.test.ts
  • test/shared/db/payment-references.test.ts
  • test/shared/db/payment-references/readiness.test.ts
  • test/shared/db/payment-references/storage.test.ts
  • test/shared/db/payment-review.test.ts
  • test/shared/db/processed-payments.test.ts
  • test/shared/db/processed-payments/failure-replacement.test.ts
  • test/shared/db/processed-payments/finalize-guard.test.ts
  • test/shared/db/processed-payments/staleness.test.ts
  • test/shared/db/provider-refund-authority-change.test.ts
  • test/shared/db/provider-refund-authority.test.ts
  • test/shared/db/provider-refund-cases-audit.test.ts
  • test/shared/db/provider-refund-cases-validation.test.ts
  • test/shared/db/provider-refund-cases.test.ts
  • test/shared/db/prune/helpers.ts
  • test/shared/db/prune/payments.test.ts
  • test/shared/db/prune/tables.test.ts
  • test/shared/db/refund-all-candidates.test.ts
  • test/shared/db/refund-confirmations.test.ts
  • test/shared/merge/attendee-merge/cleanup.test.ts
  • test/shared/merge/attendee-merge/helpers.ts
  • test/shared/merge/attendee-merge/repoint.test.ts
  • test/shared/payment-dashboard.test.ts
  • test/shared/payment-helpers/dispatch.test.ts
  • test/shared/payment-idempotency.test.ts
  • test/shared/payment-review.test.ts
  • test/shared/payment/admit-move.test.ts
  • test/shared/payment/admit-refund.test.ts
  • test/shared/payment/claim.test.ts
  • test/shared/payment/diagnose.test.ts
  • test/shared/payment/provider-failures.test.ts
  • test/shared/payment/provider-reference.test.ts
  • test/shared/payment/refund-attempt.test.ts
  • test/shared/payment/refund-authority-choice.test.ts
  • test/shared/payment/refund-authority-lifecycle.test.ts
  • test/shared/payment/refund-authority-state.test.ts
  • test/shared/payment/refund-authority/state.test.ts
  • test/shared/payment/refund-conflict-decision.test.ts
  • test/shared/payment/refund-provider-authorization.test.ts
  • test/shared/payment/refund-replay-window.test.ts
  • test/shared/payment/refund-request-identity.test.ts
  • test/shared/payment/refund.test.ts
  • test/shared/payment/resources.test.ts
  • test/shared/payment/row-state.test.ts
  • test/shared/payment/validated-session.test.ts
  • test/shared/provider-refund-cursor.test.ts
  • test/shared/provider-refunds.test.ts
  • test/shared/provider-refunds/budget.test.ts
  • test/shared/provider-refunds/engine-helpers.ts
  • test/shared/provider-refunds/send.test.ts
  • test/shared/provider-refunds/send/outcomes.test.ts
  • test/shared/provider-refunds/state-owner-revision.test.ts
  • test/shared/provider-refunds/state.test.ts
  • test/shared/provider-refunds/state/contracts.test.ts
  • test/shared/provider-refunds/target-conflict.test.ts
  • test/shared/provider-refunds/target.test.ts
  • test/shared/provider-refunds/work.test.ts
  • test/shared/refund-ledger/groups.test.ts
  • test/shared/refund-ledger/helpers.ts
  • test/shared/refund-ledger/log.test.ts
  • test/shared/refund-ledger/placeholder.test.ts
  • test/shared/refund-ledger/plan/partial.test.ts
  • test/shared/refund-ledger/plan/reference-placement.test.ts
  • test/shared/refund-ledger/plan/whole-account.test.ts
  • test/shared/refund-ledger/record/batch.test.ts
  • test/shared/refund-ledger/record/single.test.ts
  • test/shared/schema-atlas/helpers.ts
  • test/shared/schema-atlas/payment-review.test.ts
  • test/shared/schema-atlas/refund-authority.test.ts
  • test/shared/schema-atlas/types.test.ts
  • test/shared/square-provider.test.ts
  • test/shared/square-provider/metadata.test.ts
  • test/shared/square-provider/provider.test.ts
  • test/shared/square-provider/read-outcomes.test.ts
  • test/shared/square-provider/webhook-fields.test.ts
  • test/shared/square-provider/webhook.test.ts
  • test/shared/square/client.test.ts
  • test/shared/square/payment-link-validation.test.ts
  • test/shared/square/payment-link.test.ts
  • test/shared/square/read-order.test.ts
  • test/shared/square/read-payment.test.ts
  • test/shared/square/refund-outcomes.test.ts
  • test/shared/square/refund-payment.test.ts
  • test/shared/square/refund-transport.test.ts
  • test/shared/square/request-init.test.ts
  • test/shared/square/rest-transport.test.ts
  • test/shared/square/retrieve-refund.test.ts
  • test/shared/square/transport-errors.test.ts
  • test/shared/square/webhook.test.ts
  • test/shared/stripe-provider.test.ts
  • test/shared/stripe-provider/operations.test.ts
  • test/shared/stripe-provider/outcomes.test.ts
  • test/shared/stripe-provider/refund-outcomes.test.ts
  • test/shared/stripe-provider/webhook.test.ts
  • test/shared/stripe/client.test.ts
  • test/shared/stripe/endpoints/setup.test.ts
  • test/shared/stripe/refund-header-probe.ts
  • test/shared/stripe/request.test.ts
  • test/shared/stripe/request/errors.test.ts
  • test/shared/stripe/request/fixtures.ts
  • test/shared/stripe/request/retries.test.ts
  • test/shared/stripe/runtime.test.ts
  • test/shared/stripe/schemas.test.ts
  • test/shared/subrequest-budget.test.ts
  • test/shared/sumup-provider.test.ts
  • test/shared/sumup-provider/webhook.test.ts
  • test/shared/sumup.test.ts
  • test/shared/sumup/connection.test.ts
  • test/shared/sumup/create-checkout.test.ts
  • test/shared/sumup/failures.test.ts
  • test/shared/sumup/provider-money.test.ts
  • test/shared/sumup/transaction-transport.test.ts
  • test/shared/sumup/transaction.test.ts
  • test/shared/sumup/transactions.test.ts
  • test/shared/validation/kind.test.ts
  • test/shared/validation/number.test.ts
  • test/specs/steps/bulk-money.ts
  • test/specs/steps/extra-charges.ts
  • test/specs/steps/money-actions.ts
  • test/specs/steps/money-shares.ts
  • test/specs/steps/payment-capacity.ts
  • test/specs/steps/privacy.ts
  • test/specs/steps/refund-safety-permissions.ts
  • test/specs/steps/refund-safety/checking-before.ts
  • test/specs/steps/refund-safety/common.ts
  • test/specs/steps/refund-safety/owner-cases.ts
  • test/specs/steps/refund-safety/partial-return.ts
  • test/specs/steps/refund-safety/purchases.ts
  • test/specs/steps/refund-safety/races.ts
  • test/specs/steps/refund-safety/recovering.ts
  • test/specs/steps/refund-safety/waiting.ts
  • test/specs/steps/refunds.ts
  • test/specs/support/bulk-money.ts
  • test/specs/support/editors.ts
  • test/specs/support/hooks.ts
  • test/specs/support/money-drivers.ts
  • test/specs/support/money.ts
  • test/specs/support/refund-safety/faults.ts
  • test/specs/support/refund-safety/history.ts
  • test/specs/support/refund-safety/journeys.ts
  • test/specs/support/refund-safety/merge-window.ts
  • test/specs/support/refund-safety/permissions.ts
  • test/specs/support/refund-safety/provider-script.ts
  • test/specs/support/refund-safety/state.ts
  • test/specs/support/refund-safety/windows.ts
  • test/specs/support/staff-accounts.ts
  • test/specs/support/world.ts
  • test/test-utils/attendees/helpers.ts
  • test/test-utils/attendees/merge.ts
  • test/test-utils/balance.ts
  • test/test-utils/checkout-failure.test.ts
  • test/test-utils/checkout-failure.ts
  • test/test-utils/checkout.ts
  • test/test-utils/db-contracts.test.ts
  • test/test-utils/db-helpers/attendee-payments.ts
  • test/test-utils/error-paths.test.ts
  • test/test-utils/historical-payment-references.ts
  • test/test-utils/payment-claim.ts
  • test/test-utils/payment-references.ts
  • test/test-utils/payment-session.ts
  • test/test-utils/payment-state.ts
  • test/test-utils/processed-payments.ts
  • test/test-utils/provider-failure-cases.ts
  • test/test-utils/provider-refund-cases.ts
  • test/test-utils/refund-authority.ts
  • test/test-utils/refund-candidates.ts
  • test/test-utils/refund-confirmations.ts
  • test/test-utils/refund-ledger.ts
  • test/test-utils/refund-routes.ts
  • test/test-utils/reservation/helpers.ts
  • test/test-utils/sentry.test.ts
  • test/test-utils/sentry.ts
  • test/test-utils/square/fixtures.ts
  • test/test-utils/square/harness.ts
  • test/test-utils/square/outcomes.ts
  • test/test-utils/square/webhook.ts
  • test/test-utils/stripe/fixtures.ts
  • test/test-utils/stripe/responses.ts
  • test/test-utils/sumup.ts
  • test/test-utils/test-state.ts
  • test/test-utils/webhooks.ts
  • test/test-utils/webhooks/stripe.test.ts
  • test/test-utils/webhooks/stripe.ts
  • test/ui/client/admin/schema-atlas.test.ts
  • test/ui/templates/admin/attendee-page.test.ts
  • test/ui/templates/admin/confirm-page.test.tsx
  • test/ui/templates/admin/provider-refund-cases.test.tsx
  • test/ui/templates/admin/schema-atlas.test.tsx

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This PR replaces boolean refund checks with structured payment evidence. It adds refund admission, durable claims, indexed payment references, protected payment state, partial ledger reconciliation, and safeguards for attendee deletion, merging, pruning, and settlement.

Changes

Payment refund architecture

Layer / File(s) Summary
Payment contracts and provider evidence
src/shared/payment/*, src/shared/payments.ts, src/shared/stripe*, src/shared/square*, src/shared/sumup*
Adds schemas for money, refund resources, conflicts, observations, and resolutions. Providers now return structured captured and refunded money.
Refund admission and processing
src/shared/payment/admit-refund.ts, src/shared/payment-review.ts, src/features/admin/refunds/*, src/features/api/payment-processing/refunds.ts
Classifies refunds before provider calls, suppresses duplicate or conflicting refunds, records outcomes, and tracks withheld or unrecorded results.
Claims and reference persistence
src/shared/payment/{claim,row-state}.ts, src/shared/db/payment-claim.ts, src/shared/db/payment-references.ts, src/shared/db/payment-anchor/*
Adds durable claims, protected-state mirrors, encrypted reference indexes, legacy anchoring, and conditional claim release.
Ledger and move safeguards
src/shared/refund-ledger.ts, src/shared/db/attendees/*, src/shared/merge/*, src/shared/db/prune*
Scopes refunds to bookings, reverses returned ledger groups, blocks unsafe attendee moves, and preserves protected rows.
Validation and regression coverage
test/shared/*, test/features/*, test/integration/*
Adds coverage for provider evidence, refund admission, claims, ledger reconciliation, migrations, protected rows, and attendee operations.

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

Sequence Diagram(s)

sequenceDiagram
  participant RefundBatch
  participant RowClaims
  participant PaymentProvider
  participant PaymentReferences
  participant RefundLedger
  RefundBatch->>RowClaims: claim attendee payment rows
  RowClaims-->>RefundBatch: claimed references and sessions
  RefundBatch->>PaymentProvider: read charge money
  PaymentProvider-->>RefundBatch: refund evidence
  RefundBatch->>PaymentProvider: send admitted refunds
  RefundBatch->>PaymentReferences: mark returned references
  RefundBatch->>RefundLedger: record returned groups
  RefundBatch->>RowClaims: release or retain claims
Loading

Possibly related PRs

🚥 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 summarizes the main changes to make refund processing safe to retry, recover, and review.
✨ 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 claude/m4-pr-a
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/m4-pr-a

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: 3561bbf610

ℹ️ 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/payment/resources.ts Outdated
Comment thread src/shared/payment/resources.ts Outdated
@stefan-burke

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 7

🤖 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/shared/payment/diagnose.ts`:
- Around line 126-127: Update providerCouldNotRefund to accept RefundResolution,
adding that type to the existing type-only import from
`#shared/payment/resources.ts`, and explicitly annotate the predicate’s boolean
return type. Preserve the existing failed-status and provider_failed-reason
checks while relying on the union’s reason vocabulary for compile-time
validation.

In `@src/shared/payment/resources.ts`:
- Around line 177-186: Update the documentation above refundMoneyThatIs to
briefly describe that it sums refund amounts matching the requested status,
including completed and pending refunds; remove the separate pending-only and
aggregate-calculation descriptions while leaving the helper implementation
unchanged.

In `@src/shared/payment/words.ts`:
- Around line 3-10: Replace milestone and historical narration with concise
current-behavior comments: in src/shared/payment/words.ts lines 3-10, document
only the payment vocabulary used by validation; in
src/shared/payment/resources.ts lines 92-96, state that completed and pending
refunds require positive amounts while failed refunds may be zero; in
src/shared/payment/conflict.ts lines 1-9, describe only the current conflict
union without M5/M7 or reference-kind history.

In `@test/shared/payment/diagnose.test.ts`:
- Around line 162-170: Expand the payment diagnosis tests around the
duplicate-charge case to use distinct charge resource IDs when asserting the
multiple-charges conflict, then add cases covering refund_exceeds_capture,
partial_charge, capture_total_mismatch, and per-charge currency_mismatch in
diagnose.ts. Ensure each fixture reaches its intended conflict path without
being intercepted by duplicate IDs or another earlier validation.

In `@test/shared/payment/refund.test.ts`:
- Around line 68-82: Update the refund expectations in the tests at the
referenced cases, including the pending-refund test and the corresponding
assertions around lines 78, 93, and 113, to use toStrictEqual instead of
toEqual. Preserve the expected objects without a refund key so the assertions
verify that named omits the property rather than returning it as undefined.

In `@test/shared/payment/resources.test.ts`:
- Around line 258-269: Update the comparison cases in the sameProviderResource
test to use valid ProviderResource variants, including the required parentId for
the stripe_payment_intent kind, and remove the `as typeof refundResource` cast
when passing changed. Preserve coverage for provider, kind, and id differences.

In `@test/test-utils/payment-state.ts`:
- Around line 109-115: Remove the unused refundedObservation and
partlyRefundedObservation helper definitions from the payment-state test
utilities, leaving all referenced payment observation helpers unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 083b4e12-9235-4d60-a36b-9a1bc9c73aef

📥 Commits

Reviewing files that changed from the base of the PR and between b17ebb8 and 1cce55f.

📒 Files selected for processing (16)
  • src/shared/payment/conflict.ts
  • src/shared/payment/diagnose.ts
  • src/shared/payment/money.ts
  • src/shared/payment/observation.ts
  • src/shared/payment/refund.ts
  • src/shared/payment/resource-id.ts
  • src/shared/payment/resources.ts
  • src/shared/payment/words.ts
  • src/shared/validation/kind.ts
  • test/shared/payment/conflict.test.ts
  • test/shared/payment/diagnose.test.ts
  • test/shared/payment/observation.test.ts
  • test/shared/payment/refund.test.ts
  • test/shared/payment/resources.test.ts
  • test/shared/validation/kind.test.ts
  • test/test-utils/payment-state.ts

Comment thread src/shared/payment/diagnose.ts Outdated
Comment thread src/shared/payment/resources.ts Outdated
Comment thread src/shared/payment/words.ts Outdated
Comment thread test/shared/payment/diagnose.test.ts Outdated
Comment thread test/shared/payment/refund.test.ts Outdated
Comment thread test/shared/payment/resources.test.ts Outdated
Comment thread test/test-utils/payment-state.ts Outdated

@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: d13677b699

ℹ️ 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/payment/refund.ts Outdated
Comment thread src/shared/payment/diagnose.ts Outdated
Comment thread src/shared/payment/diagnose.ts Outdated
Comment thread src/shared/payment/diagnose.ts Outdated
Comment thread src/shared/payment/refund.ts Outdated
Comment thread src/shared/db/migrations/2026-08-10_payment_state_columns.ts Outdated
Base automatically changed from claude/plan-md-review-jarsho to main August 10, 2026 11:18

@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: 86b3612155

ℹ️ 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/payment/diagnose.ts Outdated

@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: 3cb76b3f40

ℹ️ 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/square-provider.ts Outdated

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
PR4_PLAN.md (1)

1192-1196: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Define an explicit anchor-row boundary before using processed_payments for claims.

Lines 1192-1196 store the claim anchor in processed_payments with an attendee ID and encrypted payment reference. The schema documents admin roster, export, and refund-all reads that select rows by attendee_id and payment_reference != '' (src/shared/db/migrations/schema/tables-attendees.ts, Lines 210-218). Those reads can treat the anchor as a real payment. Add a record discriminator and exclude anchors from every payment read, or use a dedicated claims table. Add regression tests before implementing the INSERT-OR-IGNORE flow.

🤖 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 `@PR4_PLAN.md` around lines 1192 - 1196, Define an explicit discriminator for
claim anchor rows before implementing the INSERT-OR-IGNORE flow described in the
plan. Update processed_payments payment reads—including admin roster, export,
and refund-all queries—to exclude anchor records, or use a dedicated claims
table instead; add regression tests covering the boundary before changing the
claim insertion 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 `@PR4_PLAN.md`:
- Around line 122-124: Update the “Not yet built” section in PR4_PLAN.md to
remove the completed admission pre-flight and administrative/API refund wiring,
including src/shared/payment/admit-refund.ts, and add them to the “As built”
inventory. Keep only the remaining claim, discovery and tagging, sweep,
recorder, and owner-action work listed as unfinished.

In `@src/shared/db/migrations/schema/tables-attendees.ts`:
- Around line 199-208: Update every processed_payments write
path—buildFinalizeStatements, finalizeSessionIfUnresolved, and
legacyMergePaymentStatement—to derive and persist payment_reference_index
whenever payment_reference is written, and clear both columns together when a
stale reservation is reused or the reference is removed. Ensure the updates are
atomic and add coverage for finalization, recovery, merge, and stale-reservation
reuse.

In `@src/shared/payment/admit-refund.ts`:
- Around line 101-103: Make refund admission and sending atomic in
src/shared/payment/admit-refund.ts lines 101-103 by acquiring a durable
per-payment-reference claim before provider state is read or refundPayment is
called; non-owners must withhold or reconcile without sending. Add a
deterministic two-caller concurrency regression test in
test/shared/payment/admit-refund.test.ts lines 56-93 asserting only one provider
refund call occurs for the same payment reference.

In `@src/shared/payments.ts`:
- Around line 288-295: Update all payment provider test doubles implementing
PaymentProvider to replace isPaymentRefunded stubs with readChargeMoneyOrNull
implementations. Return a ChargeMoney | null value matching each test scenario,
and remove the obsolete method so the doubles satisfy the current contract.

---

Outside diff comments:
In `@PR4_PLAN.md`:
- Around line 1192-1196: Define an explicit discriminator for claim anchor rows
before implementing the INSERT-OR-IGNORE flow described in the plan. Update
processed_payments payment reads—including admin roster, export, and refund-all
queries—to exclude anchor records, or use a dedicated claims table instead; add
regression tests covering the boundary before changing the claim insertion
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: af444e5c-adab-438f-8955-f2510865a318

📥 Commits

Reviewing files that changed from the base of the PR and between 1cce55f and 3cb76b3.

📒 Files selected for processing (29)
  • AGENTS.md
  • PR4_PLAN.md
  • src/features/admin/attendees-edit.ts
  • src/features/admin/refunds/provider.ts
  • src/features/api/payment-processing/refunds.ts
  • src/shared/db/migrations/2026-08-10_payment_state_columns.ts
  • src/shared/db/migrations/registry.ts
  • src/shared/db/migrations/schema/tables-attendees.ts
  • src/shared/db/migrations/schema/version.ts
  • src/shared/payment/admit-refund.ts
  • src/shared/payment/conflict.ts
  • src/shared/payment/diagnose.ts
  • src/shared/payment/money.ts
  • src/shared/payment/refund.ts
  • src/shared/payment/resources.ts
  • src/shared/payment/words.ts
  • src/shared/payments.ts
  • src/shared/square-provider.ts
  • src/shared/stripe-provider.ts
  • src/shared/stripe/schemas.ts
  • src/shared/sumup-provider.ts
  • src/shared/sumup.ts
  • test/shared/db/migrations/schema/index.test.ts
  • test/shared/db/migrations/schema/version/guard.test.ts
  • test/shared/payment/admit-refund.test.ts
  • test/shared/payment/diagnose.test.ts
  • test/shared/payment/refund.test.ts
  • test/shared/payment/resources.test.ts
  • test/test-utils/payment-state.ts
💤 Files with no reviewable changes (2)
  • src/shared/payment/words.ts
  • test/test-utils/payment-state.ts

Comment thread PR4_PLAN.md Outdated
Comment thread src/shared/db/migrations/schema/tables-attendees.ts Outdated
Comment thread src/shared/payment/admit-refund.ts Outdated
Comment thread src/shared/payments.ts Outdated

@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: 463859c1ee

ℹ️ 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/square-provider.ts Outdated
Comment thread src/shared/payment/admit-refund.ts Outdated
Comment thread src/shared/payment/admit-refund.ts Outdated
Comment thread src/features/api/payment-processing/refunds.ts Outdated

@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: 232c450f60

ℹ️ 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/payment/admit-refund.ts Outdated
Comment thread src/shared/db/payment-claim.ts Outdated
Comment thread src/shared/db/migrations/schema/tables-attendees.ts
Comment thread src/shared/payment/diagnose.ts Outdated
Comment thread src/shared/db/payment-claim.ts Outdated

@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: ffdd053711

ℹ️ 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/db/payment-claim.ts Outdated
Comment thread src/shared/db/payment-finalize.ts Outdated
Comment thread src/features/admin/refunds/provider.ts Outdated

@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

🤖 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 `@PR4_PLAN.md`:
- Line 120: Update the “three state columns” entry in PR4_PLAN.md to state that
payment-claim.ts writes payment_reference_index during finalize, while noting
that only pre-M4 legacy rows may remain empty. Remove the contradictory “NOTHING
WRITES IT YET” wording and keep the migration/schema references unchanged.

In `@src/features/admin/attendee-refunds.ts`:
- Around line 135-141: Remove the duplicate three-line comment in the attendee
refund flow, keeping a single copy directly above the underAttendeeClaim call it
explains; do not retain it above const candidate.

In `@src/features/admin/refunds/provider.ts`:
- Around line 143-150: Update the release handling around the refund workflow’s
try/catch so failures from release do not replace the original refund error or
discard a successful result. Preserve and rethrow the caught error after
attempting release(true), and preserve result after attempting
release(run.lost(result)); report any release failure separately through the
existing logError mechanism, threading listingId via underAttendeeClaim or the
run object as appropriate.

In `@src/shared/db/payment-claim.ts`:
- Around line 84-88: Update the SELECT statements in readRows and the other
processed_payments query to alias the table as processed payment using AS, and
qualify table references as needed while preserving callers’ bare where column
names. Leave the UPDATE statements unchanged because SQLite does not support
aliases there.
- Around line 99-119: Update readClaimableRows so rows with a non-empty
payment_reference but an empty payment_reference_index cannot proceed as
claimable without shared-reference validation. Confirm the payment_state
migration backfills payment_reference_index; if it does not, make
readClaimableRows block or fail such claims instead of returning sharing: []
while preserving normal indexed-reference behavior.

In `@src/shared/db/prune.ts`:
- Around line 57-61: Update the prune flow around the protected_state predicate
and mayReleaseClaim/decideClaim behavior so lost claims without keyed capability
do not remain permanently unprunable. Add the claim timestamp to the plaintext
mirror and apply the existing staleBefore cutoff when deciding whether to skip a
claim, or ensure a sweep settles such claims before pruning; preserve skipping
genuinely live claims while allowing stale ones to be pruned.

In `@test/shared/db/payment-claim.test.ts`:
- Around line 161-163: Add assertions to the empty-release test around
releaseAttendeeRows to verify the durable implementation makes zero database
calls, using countDatabaseCalls and an exact count assertion. Add a separate
test for claimAttendeeRows([]) that likewise verifies no database calls occur,
covering its empty-session early return while keeping each test focused on one
behavior.
- Around line 113-117: Update the “is written by the same statement as the
reference” test to import and compare against the exact deterministic value
returned by paymentReferenceIndex("pi_h"), replacing the non-empty regex
assertion while preserving the existing stored.v lookup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fcc613ff-df4b-499d-afb6-ead6e723a7e7

📥 Commits

Reviewing files that changed from the base of the PR and between 3cb76b3 and 0238e82.

📒 Files selected for processing (44)
  • PR4_PLAN.md
  • TODO.md
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/refunds/provider.ts
  • src/shared/db/payment-claim.ts
  • src/shared/db/payment-finalize.ts
  • src/shared/db/payment-references.ts
  • src/shared/db/processed-payments.ts
  • src/shared/db/prune.ts
  • src/shared/payment/claim.ts
  • src/shared/payment/row-state.ts
  • src/shared/payments.ts
  • src/shared/square-provider.ts
  • src/shared/square.ts
  • src/shared/stripe-provider.ts
  • src/shared/sumup-provider.ts
  • test/features/admin/attendee-refunds/single.test.ts
  • test/features/admin/refunds/provider.test.ts
  • test/features/admin/refunds/provider/batch.test.ts
  • test/features/api/payment-processing/index/refunds.test.ts
  • test/features/api/payment-processing/refunds.test.ts
  • test/features/api/payment-processing/refunds/rejected-charge.test.ts
  • test/features/api/webhooks/helpers.ts
  • test/integration/server/payments-success-refunds.test.ts
  • test/integration/server/payments/replay.test.ts
  • test/integration/server/webhooks/price-signature-package-overrides.test.ts
  • test/integration/stripe/core.test.ts
  • test/integration/webhook-price-signature/helpers.ts
  • test/shared/db/payment-claim.test.ts
  • test/shared/db/processed-payments.test.ts
  • test/shared/db/prune/helpers.ts
  • test/shared/db/prune/payments.test.ts
  • test/shared/payment/claim.test.ts
  • test/shared/payment/row-state.test.ts
  • test/shared/square-provider.test.ts
  • test/shared/square/retrieve-refund.test.ts
  • test/shared/stripe-provider/operations.test.ts
  • test/shared/stripe/request.test.ts
  • test/shared/sumup-provider.test.ts
  • test/shared/sumup/transactions.test.ts
  • test/test-utils/attendees/helpers.ts
  • test/test-utils/payment-state.ts
  • test/test-utils/refund-routes.ts
  • test/test-utils/stripe/responses.ts

Comment thread PR4_PLAN.md Outdated
Comment thread src/features/admin/attendee-refunds.ts Outdated
Comment thread src/features/admin/refunds/provider.ts Outdated
Comment thread src/shared/db/payment-claim.ts Outdated
Comment thread src/shared/db/payment-claim.ts Outdated
Comment thread src/shared/db/prune.ts Outdated
Comment thread test/shared/db/payment-claim.test.ts Outdated
Comment thread test/shared/db/payment-claim.test.ts Outdated

@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

if (outcome !== "refunded") return { candidate, outcome: "errored" };

P1 Badge Keep the keyless claim when marker persistence fails

When a SumUp refund succeeds but markPaymentReferencesProviderRefunded throws, this branch preserves the refunded outcome, so underAttendeeClaim classifies the result as validated and releases the keyless claim. Fresh evidence beyond the earlier lost-provider-response thread is that the single-admin route records its ledger entry only after that release; if the process dies or the ledger write also fails while SumUp's events still lag, an immediate retry can issue a second refund. Treat the failed completion marker as an errored/lost result, or retain the claim until another durable completion record lands.

AGENTS.md reference: AGENTS.md:L73-L79

ℹ️ 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/features/admin/refunds/provider.ts Outdated
Comment thread src/shared/db/payment-claim.ts Outdated

@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: d06920535f

ℹ️ 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/features/admin/refunds/provider.ts Outdated
Comment thread src/shared/db/payment-claim.ts Outdated
Comment thread src/shared/db/payment-claim.ts Outdated

@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: 0a3a5d2ffa

ℹ️ 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/db/payment-claim.ts Outdated
Comment thread src/features/admin/refunds/provider.ts Outdated

@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: 2

🤖 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 `@test/features/admin/refunds/provider.test.ts`:
- Around line 415-446: Extend the existing provider factory to accept a refund
capability option, defaulting to its current keyed behavior, and use that
factory for the keyless provider doubles in the refund tests. Replace the inline
keyless, keylessSaysNo, and alreadyBack objects with factory calls using the
appropriate refunded or alreadyRefunded values and keyless capability,
preserving their current refund outcomes.
- Around line 485-505: Add a test alongside the existing keyless hold tests that
uses a row claim whose release operation rejects. Invoke underAttendeeClaim with
a successful work result, then assert the returned result is unchanged and the
release failure is reported through the established error-reporting mechanism.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5807909d-48ae-4971-99f4-5c6cea26d0ce

📥 Commits

Reviewing files that changed from the base of the PR and between 0238e82 and 0a3a5d2.

📒 Files selected for processing (11)
  • PR4_PLAN.md
  • TODO.md
  • scripts/mutation/equivalent-mutants/shared-a-l.txt
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/refunds/provider.ts
  • src/shared/db/payment-claim.ts
  • src/shared/db/prune.ts
  • test/features/admin/refunds/provider.test.ts
  • test/shared/db/payment-claim.test.ts
  • test/shared/db/prune/helpers.ts
  • test/shared/db/prune/payments.test.ts
💤 Files with no reviewable changes (1)
  • scripts/mutation/equivalent-mutants/shared-a-l.txt

Comment thread test/features/admin/refunds/provider.test.ts Outdated
Comment thread test/features/admin/refunds/provider.test.ts Outdated

@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: 468bc45b88

ℹ️ 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/features/admin/refunds/provider.ts Outdated

@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: a5b36068e8

ℹ️ 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/features/admin/refunds/provider.ts Outdated

@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: 82889d2f34

ℹ️ 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/features/admin/refunds/provider.ts Outdated
Comment thread src/shared/payment/admit-refund.ts

@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: 2

🤖 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 `@test/features/admin/refunds/provider.test.ts`:
- Around line 663-678: Update the assertions after processRefundBatch to verify
counts.refundedCount and counts.errorCount separately, requiring two refunds and
zero errors. Ensure the test setup for attendees 11 and 12 provides
deterministic database rows or a recording double so the exact expectations
remain valid.

In `@test/shared/db/payment-claim.test.ts`:
- Around line 260-284: Add a test alongside the existing returned-money cases
that books two attendees with the same payment reference, marks the second
attendee’s unclaimed row as refunded, then claims the first attendee via
claimAttendeeRows. Assert held.returned contains the paymentReferenceIndex for
that shared reference, confirming stored.sharing contributes to the result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 576e19c3-38af-4268-877e-7707b404e317

📥 Commits

Reviewing files that changed from the base of the PR and between 0a3a5d2 and 743c456.

📒 Files selected for processing (9)
  • TODO.md
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/refunds/provider.ts
  • src/shared/db/payment-claim.ts
  • src/shared/db/payment-references.ts
  • src/shared/payment/admit-refund.ts
  • test/features/admin/refunds/provider.test.ts
  • test/shared/db/payment-claim.test.ts
  • test/test-utils/refund-routes.ts

Comment thread test/features/admin/refunds/provider.test.ts Outdated
Comment thread test/shared/db/payment-claim.test.ts Outdated

@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: 743c456774

ℹ️ 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/db/prune.ts Outdated

Copy link
Copy Markdown
Member Author

Round 26 — four findings, all verified against the code first, all real, all fixed.

Codex P1, prune.ts:76 — a stale keyless claim's row was deletable. Confirmed, and worse than it reads: the retention window is measured from checkout, so a years-old booking refunded this morning is already past it, and the claim ages out five minutes later. The row is the only durable record that money may be on its way back; deleting it took the reference index and the returned-money marker with it, and left a retry to fall back on the attendee's legacy payment_id and send the payout again.

Fixed in 562366d: a row with work on it is never pruned, however long ago the claim was taken. Nothing is stranded — a stale claim is resumable, so the next run for that attendee picks the row up and settles it. The contract already required this (the prune "never deletes a row with live work on it"; merge and delete fail closed "fresh or stale"), so the code had drifted from it. The mirror no longer carries a written-at time, which existed only so the prune could expire a claim. Two regressions pin it, one through claimAttendeeRows end to end; both fail against the old predicate.

CodeRabbit, provider.test.tsrefundedCount + errorCount is a sum, not a contract. Right, and pinning each counter exposed something: both ledger posts were failing, because those two attendees exist only in the candidate list. The counts now say exactly that, so the test can no longer pass for the wrong reason.

CodeRabbit, payment-claim.test.ts — nothing covered the stored.sharing half of returned. Confirmed by deleting ...stored.sharing and watching the suite stay green. There is now a case where the refunded row belongs to another attendee, and it kills that mutant.

CodeRabbit, payment_reference_index writers. The sweep was right and found more than the thread named. Fixed across three commits (e0c4ee9, f375be7, 613bf4f):

  • The merge anchor wrote a reference with no index at all, so every merge-inherited legacy charge was invisible to the shared-money check.
  • The recovery finalize did the same — and that is the only refundable id an admin-added attendee's balance charge has.
  • Reusing an abandoned checkout row cleared the reference but kept the index, so the row named money it no longer carried. The returned-money marker matches on that column, so it would have stamped the reused row as refunded and the real refund would have been skipped as already done.
  • Rows written before the column existed are filled in on the first authenticated refund read. Nothing else can: the index derives from the owner-key-encrypted reference, so no migration could compute it.

Also closed from TODO.md, since the anchor work landed: a charge held only in the attendee's payment_id column now gets a row of its own before the claim, so a run can no longer be told it holds an attendee whose money is on no row at all — which is what let two runs each send a payout against one charge.

CI is 22347 passed, 1 failed; the one failure remains the expected exports from src/ should be used in production code, not just tests, which is the unwired judge this PR is still building toward.


Generated by Claude Code

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
PR4_PLAN.md (1)

138-143: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the claim-coverage status.

Line 138 states that only the admin bulk route claims. The single-attendee route also calls underAttendeeClaim in src/features/admin/attendee-refunds.ts Lines 140-165. State that both admin routes claim, while the callback and refresh routes remain unclaimed.

🤖 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 `@PR4_PLAN.md` around lines 138 - 143, Update the claim-coverage statement in
PR4_PLAN.md to say both admin routes claim via underAttendeeClaim, including the
single-attendee path in src/features/admin/attendee-refunds.ts, while the
callback flow (tryRefund/refundReferenceAtProvider) and refresh-payment route
remain unclaimed.
🤖 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/shared/db/processed-payments.ts`:
- Around line 183-193: Add payment_reference_index to the processed_payments
fixture table definition in the healing test setup so it matches the production
schema and allows the update path using refClause and refParams to execute
successfully.

---

Outside diff comments:
In `@PR4_PLAN.md`:
- Around line 138-143: Update the claim-coverage statement in PR4_PLAN.md to say
both admin routes claim via underAttendeeClaim, including the single-attendee
path in src/features/admin/attendee-refunds.ts, while the callback flow
(tryRefund/refundReferenceAtProvider) and refresh-payment route remain
unclaimed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6d7fa51b-3aa4-4e7b-9a62-d2f3ea5b0feb

📥 Commits

Reviewing files that changed from the base of the PR and between 743c456 and 9fbceef.

📒 Files selected for processing (30)
  • PR4_PLAN.md
  • TODO.md
  • src/features/admin/attendee-refunds.ts
  • src/features/admin/attendees-edit.ts
  • src/features/admin/refunds/provider.ts
  • src/features/api/payment-processing/refunds.ts
  • src/shared/db/payment-anchor/mint.ts
  • src/shared/db/payment-anchor/session.ts
  • src/shared/db/payment-claim.ts
  • src/shared/db/payment-references.ts
  • src/shared/db/processed-payments.ts
  • src/shared/db/prune.ts
  • src/shared/payment-review.ts
  • test/features/admin/attendees-edit.test.ts
  • test/features/admin/refunds/provider.test.ts
  • test/features/admin/refunds/provider/batch.test.ts
  • test/features/api/payment-processing/refunds.test.ts
  • test/shared/db/payment-anchor/mint.test.ts
  • test/shared/db/payment-claim.test.ts
  • test/shared/db/payment-references.test.ts
  • test/shared/db/processed-payments.test.ts
  • test/shared/db/processed-payments/staleness.test.ts
  • test/shared/db/prune/helpers.ts
  • test/shared/db/prune/payments.test.ts
  • test/shared/merge/attendee-merge/repoint.test.ts
  • test/shared/payment-review.test.ts
  • test/shared/refund-ledger/helpers.ts
  • test/test-utils/payment-claim.ts
  • test/test-utils/processed-payments.ts
  • test/test-utils/refund-routes.ts
💤 Files with no reviewable changes (1)
  • TODO.md

Comment thread src/shared/db/processed-payments.ts Outdated

@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: 9fbceefb98

ℹ️ 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/payment/diagnose.ts Outdated

Copy link
Copy Markdown
Member Author

Round 27 — three findings.

CodeRabbit, processed_payments test fixture — real, fixed in 1a0d528. One test drops the table and recreates it, and the recreation was a hand-written copy of the schema that had fallen three columns behind the app: both payment-state columns and the reference index. Nothing fails today, because the database is rebuilt between tests — but any test running after it in the same database would break on a missing column, and the next column added would widen the gap again.

Rather than add the one column back, it now rebuilds from the schema the app itself uses (createTableSql over the processed_payments entry in SCHEMA), so it cannot drift. Verified the rebuilt table carries payment_reference_index, protected_state, and evidence_index — the hand-written one carried none of them.

CodeRabbit, PR4_PLAN.md claim coverage — correct, fixed in the same commit. Both admin routes take the hold now: the bulk wave through processRefundBatch and the single refund through attendee-refunds.ts, each via underAttendeeClaim. The note still said only the bulk route did. The callback and refresh routes remain the unclaimed pair, as it said.

Codex P1, remove the test-only outcomeOf — declining, and this one is a decision rather than a disagreement. The finding is factually right: outcomeOf and the observation/provider-read path around it have no production caller yet, which is exactly why the exports from src/ check is red and has been red on purpose for this whole PR. The repository owner was asked directly whether to trim the unwired judge to make the PR green now, or keep building it here, and chose to keep building. Deleting it would undo that decision and mean re-porting the same slice in the next PR.

It is not left unguarded: the PR description names it, the contract names it as a live fault with the sweeps that will call it, and the check stays red until those land — so the judge cannot quietly become a second authority, because nothing can merge while it has no caller.

CI on 9fbceef: 22358 passed, 1 failed, that same expected export check.


Generated by Claude Code

@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: 1a0d5286cb

ℹ️ 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/features/admin/refunds/provider.ts Outdated

Copy link
Copy Markdown
Member Author

Codex P1 on refundCandidateAtProvider — the race is real. Verified, recorded in 5d49f4c, and the fix needs the owner's call rather than mine.

The mechanism is exactly as described. A merged attendee's references are read and sent concurrently inside refundCandidateAtProvider, so a clean reference can reach sent while a sibling is still being read. If that sibling then refuses with a lasting conflict, combineRefundOutcomes fails the candidate, tallyProviderRefund keeps it out of chunkRefundedAttendees, and the ledger never posts — with real money already gone.

Two corrections to the framing, neither of which rescues it:

  • The refund is not entirely unrecorded. markReturnedReferences persists the returned reference regardless of the combined outcome, so a retry cannot send it twice, and the mismatch is reported through logError when a candidate has more than one reference. What is lost is specifically the money record.
  • It is only lost permanently when the conflict never clears. If it clears, a retry finds the clean reference already returned and posts the whole ledger entry correctly. A partial_refund is persistent by nature, so the permanent case is the realistic one.

Where the suggested fix needs adjusting. "Read and admit the complete reference set first" is the right instinct, but the reason is stronger than stated, and it rules out the alternative I reached for first. I checked whether the ledger could simply record the part that moved: it cannot. coveredRefundGroups uses the references only as a coverage test and then reverses the whole account via accountRefundGroups. Passing just the refunded subset makes the conflicted group uncovered, returns [], and posts nothing. So partial recording is not a narrower call to an existing function — it is a capability the money model does not have.

That leaves two coherent fixes, and they differ in what a buyer actually experiences:

  1. Admit every reference before sending any (your proposal). The provider side becomes all-or-nothing, matching the ledger. Cost: one permanently stuck reference withholds the buyer's clean money too, indefinitely.
  2. Teach the ledger partial reversal. The buyer gets back what can be returned. Cost: a materially larger change to the money model.

I am not choosing between those unattended. It changes when real refunds reach real buyers, and this repo's workflow puts a behaviour contract in front of a human before implementation for exactly this kind of change. It is now recorded in PR4_PLAN.md's live faults with both options and their trade-offs, and in TODO.md.

Thanks — this one was worth the round.


Generated by Claude Code

stefan-burke and others added 17 commits August 17, 2026 10:11
- payOnHostedPage was a function(this: LiveWorld) stored in an array and
  called as pay(this) — losing the this binding in Cucumber's step runner.
  Make it an arrow (world: LiveWorld) => like the other pay handlers.

- testStripeConnection used button.click() which stalls on CI's frame-idle
  renderer. The #stripe-test-btn is type=button (not a submit), so
  actOnControl's requestSubmit fallback doesn't apply. Use a scripted DOM
  click via evaluate to fire the button's JS handler without needing
  Playwright's stability wait.
Upgrading a released site to this branch could not finish. applySchemaChanges
reconciles the whole declared schema, and for a table that already exists it can
only ADD COLUMN. This release redefines payment_charges — the released table has
no `id`, and SQLite refuses to add a PRIMARY KEY column — so the first migration
that swept the schema stopped the upgrade, three entries before the one that
would have rebuilt the table.

None of the five migrations this branch added had run anywhere, so rather than
reorder around the hazard they collapse into one migration that cannot get the
order wrong. It drops the six dormant payment-record tables first, which are
empty on every site and which nothing has ever written, so the apply then
creates each one with a single CREATE carrying every column and never reaches
for an ALTER. A guard refuses to drop a table holding rows.

No declared trigger reads those tables, so dropping them cannot drop one and
the migration does not sync triggers.

The new migration test starts from a released-shape payment_charges and proves
the upgrade completes and the table ends up rebuilt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqLNrpvmNdGSSqUtSafiMd
…er, SumUp currency

- holdFirstAppReturn.capturedUrl now polls: the route handler can fire a
  microtask after awaitReturnToApp sees the intercepted URL, so an instant
  check raced the capture and threw.

- bookComplexOrder waits for the browser to actually leave for the hosted
  checkout before the provider driver settles it. The booking POST lands on
  an app reserved page first; reading the Stripe session id from the URL or
  the Square orderId from the log before the redirect had committed failed.

- classifySubmittedRefund now classifies by the LOCAL page state — the app
  honestly reports what was recorded at submission time. A provider that
  completes a refund moments after the app accepted it as observing does not
  contradict the app; it only makes the observation Refresh-eligible. The
  provider's returned amount is still checked exactly, separately.

- SumUp refund currency falls back to the transaction's own currency: SumUp
  does not reliably repeat it on every REFUND event, and production reads it
  the same way (src/shared/sumup/transaction.ts).
- The complex-order Scenario Outline never configured the payment provider,
  so the whole multi-listing cart priced as a provider-less free reservation
  and completed at /ticket/reserved instead of reaching any checkout. The
  catalog Given now configures the target provider first on paid legs.

- The held return route was page-scoped; a Stripe cross-origin redirect can
  slip past it as a main-frame navigation. Register the hold on the visitor's
  whole browser context (first /payment/success wins, later ones pass
  through) and unregister it once captured so the replay reaches the app.

- The complex-order booking step now carries the hosted-payment step
  allowance; over a fresh tunnel the settle alone can outlast the 120s
  default and killed the Square leg.

- SumUp's transaction history reports money in major units; the shared
  observation compares minor units. Convert per event (the harness only runs
  2-decimal currencies, so a fixed hundredfold is exact).
- classifySubmittedRefund keyed the observing branch on the one-shot flash
  warning, which can be gone by gathering time; SumUp's settling refund then
  fell into the recorded branch and failed on its claims. The Refunded
  status is now the sole positive proof of recorded; everything else is the
  observing state, whose durable safety claims (no Refunded, no send, no
  delete, Refresh reachable) must hold — the flash warning stays a claim of
  the fault scenario only, where the landing page is read directly.

- The held return's Playwright route interception never fired for Stripe's
  cross-origin redirect in CI (both page- and context-scoped). Capture the
  return URL from the visitor's URL bar instead: the moment it lands on
  /payment/success, that URL is the checkout's own return binding. Holding
  is unnecessary for correctness — the processed-payment reservation makes
  the replay idempotent whichever of webhook or return books first.
- 'When the visitor retries the exact return' (the invalidated-checkout
  scenario) had no step definition — the scenario never reached it until
  this run. All three replay wordings now share one table-driven When.

- A provider-side-completed refund whose retirement has not caught up
  safely blocks Delete while the Refunded badge is already up; SumUp hit
  exactly that and the recorded claims failed on Delete availability.
  Delete's return is a retirement property, not a recording property — the
  recorded branch now proves Refunded + no send + no warning, and only the
  Stripe recovery scenario (where retirement completed) asserts Delete
  reachability. The observing branch still demands Delete be absent.
The 'the visitor retries the exact return' step had no definition and
nothing caught it before a live dispatch — the scenario reached it only
after the earlier failures were fixed, then died as undefined. The new
direct test loads the same support code the runner loads (loadSupport) and
matches every step text the Feature carries against the registered Cucumber
expressions, so a missing or renamed step fails in CI, not in the sandbox.

The live-harness files that only the nightly workflow can execute (browser
sessions, servers, tunnels, provider drivers, step bodies) join the coverage
exclusions — the step-coverage test imports them, so a coverage run sees
them loaded but untested. Pure helpers with deterministic behaviour stay
under coverage and are directly tested under test/e2e-payments/.
Two review findings on the live harness:

- SumUp's refund events were counted by event_type alone, so a PENDING or
  FAILED refund's money read as returned. Count only REFUNDED/SUCCESSFUL
  events — the same mapping production applies (src/shared/sumup/money.ts)
  — and throw on FAILED: a rejected refund is a failure, never a pending or
  completed observation.

- observeViaRead converted any read failure (outage, rejected credentials,
  malformed body) into a pending observation, so a nightly run could pass
  green without ever learning what the provider returned. Read errors now
  propagate; pending is reserved for a successful read that honestly
  reports no settled refund yet.
The harness passes its whole environment to each scenario's app server,
including NTFY_URL — so the scenarios' deliberate server errors (the Money
fault, the SumUp refusal probes, the price-change refund) pinged ntfy as if
they were production incidents, making green runs look like failures. The
harness's own failure-only notification is now the only one a run sends.
A partial provider return (some money back, some not) parked the refund as
needs_provider_check whose only offered exit was 'check again' — and that
exit was provably a no-op: answerProviderConflict writes nothing when the
provider money is unchanged, so nothing but returning the rest of the money
at the provider could ever clear the state, permanently blocking the
privacy delete.

A partial return is a settled provider fact, not a pending one, so it now
parks as needs_owner_choice with reason provider_conflict: the owner
confirms the money that came back (the only admitted choice — 'not sent'
would re-arm a send that pays the returned part twice) and the case
completes with the exact partial money, unblocking the delete once
recorded. Only genuinely inconclusive ('wait') evidence remains a provider
check. Partial evidence also stays protected: later provider reads cannot
overwrite the stored decision, and the not-sent choice is refused.

The two deterministic refund-safety scenarios that pinned the old
check-again exit follow the fix: the partial-return case now asks for the
owner's decision, offers only the returned choice, and still contacts no
provider. The attempt outcome for a wait-conflict provider check gains a
direct test, and the authority-architecture heuristic now counts 'wait'
among the authority-result kinds, bringing genuine authority tests into
the reviewed boundary list.

Verified against the reviewing agent's independent finding at head 2140651:
all three legs (dead-end routing, no-op early return, UI offering only the
no-op) stood as described.
The refund case page showed only the captured amount, so an owner closing
a partial return (£4 back of a £25 charge) saw £25 as the only figure,
confirmed 'the provider returned this money', then attested the Money
entry — and posted £25 where the authority had stored £4. Bookkeeping
went wrong by the difference and nothing flagged it afterwards, because
recording the case is exactly what that attestation finishes.

The case now carries the row's returned money alongside the capture, and
the page shows 'Provider returned' and 'Not returned' before both money
surfaces: the returned choice and the Money-recorded confirmation. The
two numbers are the same money the resolution applies — the conflict
decision for a settled return, the stored refunded amount for the Money
attestation — pulled from the same payment_charges columns, so the page
cannot show a number the decision disagrees with.

Regression coverage pins the wiring at both layers: the template test
asserts a partial-return page contains the returned and missing amounts
(and that nothing-extra is claimed when £0 came back), and the recovery
integration test asserts the served page carries £4 returned / £21
missing before the choice is submitted.
Stripe requests already aborted on a 20s timer, but the SumUp and Square
transports passed no signal to their fetches, so a provider that accepted
the connection and then stopped answering held the call open with no
answer. That is sharpest on a refund send, which arms the authority before
the POST: a stuck call leaves the case armed and unknown, holding the
request and its isolate until the platform kills it, and the eventual
error is not one the failure classifiers recognize.

The timeout now lives in one shared constant all three providers use, so
the budget cannot drift between them. A timeout abort reaches the
existing classifiers as a connection timeout: a refund send becomes
'uncertain' — the honest answer, since the POST may have landed — and the
authority observes later, which is the recovery path this branch built.

Both transports gain a regression test that hangs the provider fetch and
fast-forwards the clock: SumUp must answer 'uncertain, timeout' and
Square 'unavailable, timeout' exactly when the shared budget expires.
Threads fixed in 97efb72 / 7bda1f6 / ed3a823 / f86d152 get
resolution replies on the PR. Everything verified-real-but-unfixed is
recorded here so the threads can be answered without losing the work:
two upgrade-blocking P1s (migration apply ordering, Square missing-order
webhook), two parked-outcome P1/P2s (refunded rejection with no Money
target, un-retried final placeholder outcome), and the live-harness
hardening batch.
The refund system's safety lives in a declared state machine, and the
only way to see it has been to read the code. This adds an owner-only
System map page that draws every state a money record can sit in and
every way it can move — 'if the payment is in state X, its next options
are Y and Z', for the whole machine.

The map is derived, never drawn by hand. Each machine module builds its
states with the real production constructors and discovers its edges by
running the real transition functions — a transition that throws is not
an option, so the page updates itself whenever the rules change and
cannot drift from them. Two machines ship: the refund authority (nine
nodes, including the three distinct owner decisions and the one-exit
partial return this PR made safe) and the payment review lifecycle
(reasons and the exact evidence that retires each). A new machine joins
by deriving one module and adding it to the list; the page needs nothing
else.

The page is complete without JavaScript: every state, its meaning, what
the lifecycle says clears it, and its ways forward are rendered as
lists. With JavaScript, the same data (labels resolved server-side,
embedded as JSON) becomes an interactive SVG diagram — click a state and
its own arrows light up, colour-coded by who moves the record: the site,
the provider, or you.

Wiring follows the house patterns: the settingsArticlePage shell (also
adopted by the debug page) and the shared JsonScript component (also
adopted by duplicate-preview), both extracted where cpd found the same
shape written twice. Tests pin the derived maps exactly — edge sets,
actors, key strings, layouts — and each module holds a 100% mutation
kill rate, with the genuinely unkillable mutants (refusal text, a
defined-vs-undefined stamp) recorded as proven equivalents. The
concurrent-rows fixture's poll branch gets the direct test its
timing-dependent coverage was flaking on.
Main now holds comments to twenty lines, and this docblock ran
twenty-two. Same facts, two paragraphs merged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqLNrpvmNdGSSqUtSafiMd
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

The rebase merged TODO.md by union, and each historical rename of the
Refund All entry's heading came back as its own bodiless line above the
real one. The one heading old m4 actually carried stays; a sweep against
both pre-rebase sources confirms no other heading was added or lost.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqLNrpvmNdGSSqUtSafiMd
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@stefan-burke
stefan-burke added this pull request to the merge queue Aug 17, 2026
Merged via the queue into main with commit 2810f7f Aug 17, 2026
3 checks passed
@stefan-burke
stefan-burke deleted the claude/m4-pr-a branch August 17, 2026 10:47
stefan-burke pushed a commit that referenced this pull request Aug 19, 2026
TODO.md holds work that is still open, so a finished entry leaves it instead
of staying in place marked "done". AGENTS.md now says so, and this change
applies the rule to every entry the code already answers:

- the payment-provider split out of `src/shared/db/settings.ts`, done in the
  commit before this one;
- `src/features/api/webhooks.ts` below 400 lines, done by #2065;
- a completed Square webhook whose order reads as missing, retried rather than
  acknowledged since #2106;
- two suites covering the attendees list;
- four feature modules that now have a test at their mirrored path.

One entry was half open. The shared "reasons" shape shipped, but the surfaces
it makes cheap did not, so the entry keeps the four open surfaces and the three
decisions that bound them, and loses the record of what shipped. The stripe-mock
port-steal entry stays: AGENTS.md cites it as the worked example for reading a
written-down diagnosis, so it is documentation, not a job.

The branch mutation gate found four survivors in the moved provider code. Two
were real:

- `changePaymentProvider` defaulted `first` to `false`. No caller reads that
  default, because only the credentials kind reads `first` at all, and that
  caller always passes the flag. The parameter is now optional with no default.
- `settings.square.hasToken` moved into the new module, but its only test
  stayed in `public-api.test.ts`, which is not a direct test of that file. The
  mirror suite now covers the getter on both answers.

The other two are one expression, `provider ?? ""`, and both are recorded in
`scripts/mutation/equivalent-mutants/shared-db.txt` with their proof. No
provider id is the empty string, so `??` and `||` agree. Only the disable kind
takes the fallback, and the disable statement never evaluates a placeholder
bound to that value.

`deno task precommit:mutation` — 197 mutants, 195 killed, 2 suppressed, 100%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EW4n7LWYByACzixWob1X21
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