Conversation
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.
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
…ramps Re-enable Ethereum onramps
✅ Deploy Preview for vortex-sandbox ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for vrtx-dashboard canceled.
|
✅ Deploy Preview for vortexfi ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Reviews are probabilistic; live integrator breakage should be caught deterministically. The gate renders the shared endpoint types and the public SDK API into a structurally expanded snapshot, so any change to what partners consume - including transitive type/enum drift - fails CI until the snapshot is regenerated and the diff is reviewed on purpose.
vortex-review encodes the multi-lens finder fleet with loop-until-dry and adversarial verification; address-feedback turns one PR review round into a single verify-fix-gate-push command.
ship chains plan approval, implementation with tests, vortex-review until dry, and PR creation; babysit-pr watches an open PR and reacts to new reviews and CI failures so feedback rounds need no ferrying.
Copilot review on #1326 found three blind spots: generic conditional aliases rendered as self-references, signature type parameters and their constraints were dropped, and readonly was erased. All three let partner-facing drift pass the gate; the serializer now expands conditional bodies, emits type-parameter constraints and defaults, and preserves readonly, with fixture coverage for each.
Inline review comments are not returned by gh pr view and live on a paginated endpoint; the address-feedback and babysit-pr instructions now fetch them explicitly so findings cannot be silently dropped.
Second Copilot round on #1326: readonly index signatures and readonly tuples serialized identically to their mutable forms, so mutability changes to those constructs would pass the gate unseen.
Review, feedback, and babysit skills execute branch-local commands; on a public repo with forks that would let an untrusted PR run code under local credentials. Fork or external-author PRs are now inspect-only in all three skills.
…ooling Add wire-contract CI gate and agent review workflow skills
Skills are agent-agnostic capabilities, so they live in the shared .agents/skills directory alongside the existing integration skills; .claude/skills keeps symlinks because Claude Code only scans that path (symlinked skill directories are officially supported). Hook bypassed for this commit: lint-staged's stash backup fails on staged symlinks; biome verify was run manually and is clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.