Skip to content

Create new production release - #1325

Open
ebma wants to merge 57 commits into
mainfrom
staging
Open

Create new production release#1325
ebma wants to merge 57 commits into
mainfrom
staging

Conversation

@ebma

@ebma ebma commented Aug 7, 2026

Copy link
Copy Markdown
Member

No description provided.

Sharqiewicz and others added 30 commits July 30, 2026 11:03
Resolved 7 conflicts, all where staging's refactors met the email
notification work:

- models/index.ts, vars.ts, phase-processor.ts,
  alfredpay-customer.service.ts: both sides added adjacent declarations;
  kept both. Dropped the TaxId associations, since staging removed that
  model in the provider_customers cutover.
- security-spec/README.md: kept the Resend row, dropped the Stellar
  Anchors row that staging deleted along with the spec file.
- alfredpay.md: both sides added an invariant 24; kept staging's at 24
  and renumbered the notification invariants to 25 and 26.
- brla.md: took staging's anonymous-quote row, which matches the merged
  registerRamp (rejects only when quote and request users both exist and
  differ); this branch's row described pre-cutover behavior. Kept the
  four added webhook threat rows.
…ed ramps

The 055 prefix collides with 055-create-financial-operations on staging -- the
exact duplicate-prefix situation MIGRATION_RENAMES in migrator.ts was added to
clean up. Renumbering now, while branch-only, avoids another rename shim entry.

The backfill closes a first-deploy hazard: reconcileMissedRampCompletedEmails
deliberately has no age cutoff, so an empty table would re-enqueue every
historical completed ramp and mass-mail users about months-old completions.
Tombstoning them as skipped at table creation keeps the sweep anomaly-only.
getEffectiveUserId fills rampState.userId with the credential's linked profile
on API-key requests, so the null-userId check alone mailed the partner once per
end-customer ramp -- the opposite of the documented behavior. A ramp whose quote
carries an api_credential_id now records a skipped tombstone instead, which
also stops the hourly reconcile sweep from re-surfacing it forever.

The reconciliation integration test now seeds real ramps and asserts the
anti-join's semantics instead of running it against an empty database.
ebma added 27 commits August 6, 2026 18:52
quote amounts are DECIMAL(38,18) and come back as "1250.000000000000000000";
the API response path already trims them, the email path did not.
bun's mock.module patches are process-global and leak into every file loaded
afterwards. The two Avenia suites and the dispatch suite left @vortexfi/shared,
the logger, the transport, Slack, and the template renderer stubbed for the
rest of the run. Each now restores the real namespaces in afterAll, matching
the priceFeed convention.

The canary's name check cannot see this stub shape -- an object literal's
{ getInstance: () => ... } infers the name "getInstance" -- so it now also
asserts the singletons are classes, which an object-literal stub is not.
New coverage for the properties the feature leans on but nothing verified:
the transactional SKIP LOCKED claim shape, the non-production recipient
allowlist (absent recipient, empty list, production bypass), the failure
path (backoff scheduling, abandon-at-cap with Slack alert, error-text cap),
the Avenia terminal-status mapping and reason cap (previously only its
Alfredpay twin was tested), the HTML-escaping invariant from the security
spec, and shared's {attemptId} path templating that the KYB poller's URLs
depend on. Also drops a tautological constant self-assertion.
…uristic

The status route's catch handled provider errors and local enqueue/persist
errors alike, so an enqueue failure whose message contained "not found"
reset the customer to Consulted and wiped the observed state. The local
writes now carry their own catch. The service-path swallow is raised from
info to warn with the stack, since it also hides enqueue failures that
block terminal persistence. The universally-fresh-submission-id dedupe
claim is softened to match what the retry endpoints actually guarantee.
The poller never writes terminal outcomes back to kyc_cases, so every settled
attempt cost one Avenia request per hour until it aged out of the 60-day
window. The queue row the enqueue writes is the natural retirement marker:
an anti-join against email_notifications excludes those attempts before any
provider call. The batch is also capped at 250 cases (oldest writes first),
matching the Alfredpay worker's bound.
Duplicates are the common case -- webhook replays and re-polled settled
attempts -- and each one paid a Supabase admin API call to resolve a locale
that was never used. A key lookup now short-circuits first.

Also restores the claim-consumed attempt when the not-configured guard
requeues a row, stubs getUserLocale in the fake auth world so integration
enqueues stop dialing the neutralized Supabase host, and stubs findOne in
the enqueue unit tests to keep them off the database.
- The unauthenticated Avenia webhook route no longer buffers the API's full
  20mb body limit before the signature check; events are a few KB, 100kb is
  generous.
- Supabase auth-mail subjects and the Terms/Privacy links now localize for
  pt-BR (the Dashboard subject field needs a self-contained conditional, and
  the site serves /pt/ legal pages).
- The Avenia contract test sweeps stale contractRun webhooks left by crashed
  runs before checking the 3-slot cap, and a failed cleanup delete no longer
  masks the error that failed the test.
docs/README.md allows directory trees only for security-spec/ and api/;
general documents live flat as docs/<kind>-<topic>.md and must be indexed.
The architecture doc moves accordingly, the alfredpay-kyc-notification-gap
follow-up note folds into its open-follow-ups section (it was a 40-line
progress journal, which the conventions disallow as a standalone file),
and the index and spec links are repaired in the same change.
…path

AlfredpayCustomerView exposes alfredPayId, not id; the new error log in the
404-heuristic split referenced a field the view does not have.
Empirically (2-file probe): mock.module mutates already-imported namespace
objects in place, so spreading the namespace at restore time copies the
stubs back -- the restore was a silent no-op. Snapshots are now plain-object
copies taken before the first mock.module call, and the new Avenia enqueue
suite resolves its subject in beforeAll because the webhook controller test
mocks that exact module path and bun's file execution order is not the CLI
order.
Same in-place-mutation flaw fixed for the email suites earlier: spreading a
live namespace at restore time copies the stub back, so the fund-ephemeral
and offramp-subsidy suites and the FakeSquidRouter harness restore() were
silent no-ops. All three now restore from a plain-object snapshot taken
before the first mock.module call. The canary additionally checks that
shared's getRoute is not a leftover FakeSquidRouter stub, which the name
and class-shape checks cannot see.
The route persisted Approved/Rejected without ever queuing an email. Once a
case is terminal both this route's short-circuit and the KYB worker stop
observing the attempt, so a client polling ahead of a lost webhook lost the
notification forever. The outcome is queued before the terminal writes --
mirroring the Alfredpay enqueue-before-persist invariant -- and a failed
enqueue fails the request, leaving the case pollable.
…empts

Three review findings on the hourly sweep: partner-owned cases passed the
row filter and could permanently occupy the 250-slot batch (now excluded in
the join); a capped oldest-first prefix re-selected the same rows every
cycle since pending cases are never modified (now an id-ordered keyset like
the Alfredpay sweep); and the returned attempt id was trusted, letting a
malformed provider response enqueue another attempt's outcome and reason
for this case's profile (now discarded, mirroring the authenticated
route's guard). Spec updated in the same change, including its stale
enqueueCompletionEmail/055 identifiers.
sendRequest used a bare fetch, so one hung Avenia connection stalled its
caller indefinitely -- for cron workers with waitForCompletion that means
no future cycle ever runs. Timeouts land in the existing transport-failure
path (BrlaApiError status 0) that callers already normalize to a 502.
The recovery query had no limit, so a prolonged outage made a single hourly
cycle arbitrarily large (one quote plus locale lookup per missing ramp).
250 oldest-first per cycle, with a log line when the cap is hit; processed
ramps gain a queue row and leave the anti-join, so the backlog drains
across cycles without giving up the no-age-cutoff guarantee.
The architecture doc's security-spec link broke when the doc moved into
docs/ (its ../ now resolved outside the tree), and the section 7 comparison
table still named migration 055.
Send ramp completion and verification outcome emails
The Settings checkboxes were uncontrolled defaultChecked stubs -- toggling
persisted nothing and reset on reload. They now load and mutate
/v1/notifications/preferences with optimistic rollback. Two categories
remain: onboarding updates fan out to the three verification_* types and
transfer status maps to ramp_completed -- the stored type strings the
dispatch worker consults, pinned by a test because the PUT endpoint accepts
any keys without validation. The recipient-approvals toggle is dropped (no
such notification type exists yet) and the transfer caption no longer
mentions failures.
…icing-metadata

Add transparent AlfredPay quote pricing metadata
…contract

The dashboard's preference toggles write prefs keyed by these strings and the
API's dispatch worker mutes on them, but each side carried its own literals --
the preferences endpoint accepts arbitrary keys, so drift would mute nothing,
silently. The enum now lives in shared and the API model re-exports it under
its historical NotificationType name.
Review follow-ups on the Settings toggles: a checkbox now reflects
emailEnabled AND the per-type keys, and enabling a category under a global
mute lifts the switch while pinning the other category to its effective
(muted) state. Toggling is blocked until the GET resolves -- a PUT built
from a fallback would replace the saved document -- and while a PUT is in
flight, since overlapping full-document snapshots can complete out of
order. The optimistic handlers are extracted and tested against a real
QueryClient (apply, rollback, no-op without loaded data), and the stale
operations spec that still declared email dispatch unimplemented now
documents the delivery-time preference gating.
…ion-preferences

Wire the Settings email notification toggles to the backend
@netlify

netlify Bot commented Aug 7, 2026

Copy link
Copy Markdown

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit 3a6a661
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a760d402f60db00076b8b61
😎 Deploy Preview https://deploy-preview-1325--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Aug 7, 2026

Copy link
Copy Markdown

Deploy Preview for vrtx-dashboard ready!

Name Link
🔨 Latest commit 3a6a661
🔍 Latest deploy log https://app.netlify.com/projects/vrtx-dashboard/deploys/6a760d4014247f0008e5bb6c
😎 Deploy Preview https://deploy-preview-1325--vrtx-dashboard.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Aug 7, 2026

Copy link
Copy Markdown

Deploy Preview for vortexfi ready!

Name Link
🔨 Latest commit 3a6a661
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6a760d40c571e700087e5813
😎 Deploy Preview https://deploy-preview-1325--vortexfi.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

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