Skip to content

feat(wallet): worker withdrawal flow + auto-release cron - #19

Merged
shaiksohelll merged 14 commits into
mainfrom
feat/wallet-withdrawal-and-autorelease-cron
May 23, 2026
Merged

shaiksohelll merged 14 commits into
mainfrom
feat/wallet-withdrawal-and-autorelease-cron

Conversation

@shaiksohelll

@shaiksohelll shaiksohelll commented May 18, 2026

Copy link
Copy Markdown
Owner

Summary

Ships worker wallet withdrawal end-to-end and daily auto-release of escrow funds that have passed their review window. Both flows are RPC-backed, idempotent at the application layer, and observable via the existing escrow ledger. PR also rolls in a focused round of state-machine hardening on the escrow flow surfaced during review.

What's in this PR

🏦 Worker withdrawal flow

  • New RPC withdraw_wallet(p_user_id, p_amount, p_idempotency_key) — locks the wallet row, validates available balance, debits balance, and emits a single escrow_ledger row inside a nested BEGIN..EXCEPTION block scoped to the INSERT.
  • unique_violation on the ledger insert (i.e. retry with the same p_idempotency_key) is the only swallowed error path — every other UNIQUE violation propagates. Pattern adopted after review round-11.
  • Worker-facing UI: WithdrawDialog with Zod-validated amount input, balance display, mapped RPC errors via mapWalletRpcError (typed against PostgrestError).
  • Withdrawals appear in the worker's transaction history via the existing ledger query.

⏰ Auto-release cron

  • pg_cron job auto_release_escrows scheduled at 30 20 * * * (20:30 UTC / 02:00 IST, daily), calling a release_eligible_escrows() SQL function.
  • Releases all escrow_in_review milestones whose review window has elapsed and have no active dispute. Transitions state to released and credits the worker wallet.
  • Idempotent re-runs are safe — state guard in the function prevents double-release.
  • Documented in adr/0004-auto-release.md.

🛡️ State-machine hardening

  • Tightened guards on fund_escrow, submit_milestone, approve_milestone, dispute_milestone against out-of-order transitions; previously-possible "double-fund" and "submit-after-release" paths now error explicitly.
  • Captured in adr/0002-escrow.md and adr/0003-dispute.md.

🔑 Client-side idempotency (application-layer half)

  • New helper getOrCreateIdempotencyKey(actionName, milestoneId) shipped in src/lib/idempotency.ts. Generates a stable per-intent UUID, rotated only on success, so retries reuse the same key.
  • Wired into all four milestone actions (fund, submit, approve, dispute) on the client side.
  • ⚠️ Server-side RPC enforcement (p_idempotency_key uuid parameter + UNIQUE constraint) for the four escrow RPCs is tracked in a follow-up "Phase 4.5 escrow idempotency PR". The client-side key is the application-layer half of the contract; the RPC signatures don't currently accept the parameter. Forwarding it through the action today would have nothing to bind to.

🔐 Auth-hydration handling

  • Three list/detail surfaces (worker-milestones.tsx, client-milestones.tsx, client-job-detail.tsx) now redirect unauthenticated users to /login via a useEffect-driven router.replace, with <Skeleton /> rendered during hydration and the navigation tick. Avoids both stale-render and render-phase side-effect issues.
  • worker/account/page.tsx:
    • Destructures error from supabase.auth.getUser(); transient auth/network failures now show a retryable error card instead of redirecting signed-in users to /login.
    • Clears local profile/worker state before redirect to prevent stale data flash.
    • load() is guarded by a requestIdRef race-condition pattern so concurrent triggers (initial mount, Retry, onSaved, onChanged) can't have an older request overwrite fresher state.

📜 ADRs

  • adr/0001-auth.md — auth flow design
  • adr/0002-escrow.md — escrow state machine
  • adr/0003-dispute.md — dispute flow
  • adr/0004-auto-release.md — daily auto-release cron contract
  • adr/0005-realtime-contract.md — Realtime channel ownership and auth propagation via singleton client
  • adr/0006-nav-shell.md — navigation shell pattern
  • adr/0007-rpc-pattern.md — RPC error mapping and contract conventions

🗃️ Migrations

  • 202604260004_create_escrow_functions.sql — escrow RPC suite
  • 202604260005_harden_state_machine.sql — state-guard hardening
  • 20260518135600_withdraw_wallet.sql + 20260518181500_withdraw_wallet_fix_ambiguous_column.sql — withdrawal RPC + ambiguity fix
  • 20260518225500_schedule_auto_release_cron.sqlpg_cron job registration (canonical cron source)
  • 20260521080000_pr19_review_fixes.sql — consolidated review-fix pack covering rounds 5–6. Section headers (A/C/D/F/I/E/H) document the per-concern split internally. Future migrations follow one-per-logical-change per repo guidelines.

Out of scope / deferred

Item Tracked in
Server-side p_idempotency_key enforcement on fund_escrow / submit_milestone / approve_milestone / dispute_milestone Phase 4.5 escrow idempotency PR (next)
Sentry.captureException() wiring for the 10+ // TODO: Sentry markers in escrow.ts, worker-milestones.tsx, worker/account/page.tsx PR #20 — Sentry observability
Canonical async-dialog migration for apply-modal, change-phone-dialog, delete-account-dialog, edit-profile-dialog, worker-form, withdraw-dialog Zod PR #22 — Canonical Dialog Migration
Per-file .setAuth() calls on Realtime channels Not needed — singleton client handles auth propagation via eager-prime + onAuthStateChange. Tracked under PR #23 only to verify all .channel() sites use the singleton.
GHA CI workflow PR #21 — CI workflow (Sprint 2, Phase 5/5.5)
acceptWorkerAction transactional RPC + ADR Phase 5 jobs hardening PR

Testing

Check Result
pnpm lint ✅ 0 errors (2 pre-existing unrelated warnings on mount effects)
pnpm typecheck ✅ clean
pnpm test ✅ 13/13 passed
pnpm build ✅ 23/23 pages compiled

Manual verification:

  • Withdrawal happy path + retry path (idempotency replay returns the same ledger row, no double-debit).
  • Auto-release cron dry-run via select release_eligible_escrows(); against seeded escrow rows in escrow_in_review past their review window.
  • Auth-hydration redirect verified on all three list surfaces (worker milestones, client milestones, client job detail) + worker/account.

Risk

  • Low-to-medium. Withdrawal flow is new; auto-release runs daily and is reversible via ledger inspection if needed. Worst-case operational mitigation: disable the pg_cron job (select cron.unschedule('auto_release_escrows');) — escrows simply remain in escrow_in_review until manually released or the job re-enabled.
  • No breaking changes to existing escrow RPCs — only new parameters added with safe defaults, and tighter state guards on transitions that should never have been reachable.

Review history

This PR was through 12 review iterations across CodeRabbit, GitHub Copilot, and CodeAnt. All Critical/High/Major findings against in-scope changes are resolved. Remaining open threads are intentional deferrals tracked in the table above; a consolidated summary review comment documents per-thread disposition.

Copilot AI review requested due to automatic review settings May 18, 2026 18:18
@codeant-ai

codeant-ai Bot commented May 18, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@vercel

vercel Bot commented May 18, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
pakka Ready Ready Preview, Comment May 23, 2026 11:42am

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds DB withdraw and auto-release migrations, deterministic RPC error mapping and a server withdraw action, client WithdrawDialog and top-up idempotency via generateUuid(), plus many formatting, docs, and seed script updates.

Changes

All changes (single cohort)

Layer / File(s) Summary
Database migrations and cron
supabase/migrations/*
Defines/overwrites withdraw_wallet, adds per-user idempotency index, fixes ambiguous-column issues, updates guard triggers, defines/updates auto_release_milestones, schedules pg_cron job, and applies PR19 review fixes.
Server actions and RPC error mapping
src/app/_actions/wallet.ts, src/app/_actions/escrow.ts
Adds mapWalletRpcError and withdrawWalletAction (validation, worker-role check, RPC call, idempotency normalization, revalidation) and mapEscrowRpcError used by escrow actions to return deterministic user-facing messages and structured RPC logging.
Client withdraw & top-up UI
src/components/features/withdraw-dialog.tsx, src/components/features/topup-dialog.tsx, src/components/features/wallet-view.tsx
New WithdrawDialog component with client-side validation, idempotency key handling, mutation/invalidation and toasts; top-up dialog uses dialog-scoped generateUuid() idempotency; WalletView renders dialogs by role.
Client pages, milestones, job details
src/app/client/jobs/..., src/app/client/...
React Query keys gated by user?.id, fetchers scoped to user, realtime subscription rewrites to .on("postgres_changes", ...), and multiple generateUuid() substitutions in action handlers.
Worker views & realtime
src/app/worker/...
Realtime subscription rewrites, query gating, generateUuid() usage, and minor error handling/formatting changes across worker-facing pages.
UUID util, wallet schema, and types
src/lib/uuid.ts, src/lib/schemas/wallet.ts, src/lib/types/database.ts
Adds generateUuid() secure UUIDv4 generator, new withdrawWalletSchema/type, and small type/schema formatting updates used by server and client flows.
Edge functions & Supabase server fixes
supabase/functions/*, src/lib/supabase/server.ts
Edge function 500-response formatting changes and SSR cookie-store loop fix.
Seeds, docs, ADRs, formatting
scripts/*, docs/*, adr/*, many UI files
Large seed script reformatting, ADR/document formatting edits, README/AGENTS/CLAUDE tweaks, and numerous stylistic/formatting updates across UI components and utilities.

Sequence Diagram

sequenceDiagram
  participant Client
  participant WithdrawDialog
  participant ServerAction as withdrawWalletAction
  participant DB as withdraw_wallet_RPC
  participant Wallet as wallets_table
  participant Ledger as escrow_ledger_table
  Client->>WithdrawDialog: open, enter amount, submit (idempotency_key)
  WithdrawDialog->>ServerAction: call withdrawWalletAction(amount, idempotency_key)
  ServerAction->>DB: CALL withdraw_wallet(p_amount, p_idempotency_key)
  DB->>Ledger: lookup or insert idempotent escrow_ledger row (withdraw)
  DB->>Wallet: SELECT ... FOR UPDATE and debit available_balance (if new)
  DB->>ServerAction: return available_balance, ledger_id
  ServerAction->>WithdrawDialog: return success/result
  WithdrawDialog->>Client: invalidate wallet queries, show toast, close
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

"I'm a rabbit with a ledger and a key,
UUIDs hop bright for idempotency,
Cron hums nightly, RPCs reply,
Wallets balance safe beneath the sky,
Hooray — transactions snug and free!" 🐇✨

✨ 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 feat/wallet-withdrawal-and-autorelease-cron

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label May 18, 2026
Comment thread src/app/_actions/wallet.ts
Comment thread src/components/features/topup-dialog.tsx Outdated
Comment thread src/components/features/withdraw-dialog.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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/app/_actions/wallet.ts`:
- Around line 95-109: The current error handling in the block that inspects
error.message is fragile; update the logic to inspect error.code (the
Postgres/RPC errcode) instead of parsing error.message: in the same
function/block in src/app/_actions/wallet.ts replace the message-substring
checks with a mapping/switch on error.code (e.g., check error.code ===
'INVALID_AMOUNT_CODE' / 'INSUFFICIENT_BALANCE_CODE' / 'NOT_AUTHENTICATED_CODE' /
'WALLET_NOT_FOUND_CODE' and return the same user-facing messages) and keep a
default fallback returning the generic withdraw error; ensure you reference the
same error variable and preserve existing returned shapes ({ success: false,
error: ... }).

In `@supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql`:
- Around line 30-33: The NULL guard for p_amount was removed so NULL now
bypasses validation; update the conditional to explicitly check for NULL and low
values by changing the block that currently reads "if p_amount < 100 then" to
include an IS NULL check (e.g. "if p_amount IS NULL OR p_amount < 100 then") so
that NULL values raise the same exception 'invalid_amount' with errcode '22023'.

In `@supabase/migrations/20260518225500_schedule_auto_release_cron.sql`:
- Around line 101-106: The current IF check against public.wallets (the clause
referencing w.profile_id = v_milestone.client_id and w.locked_balance >=
v_milestone.amount) silently skips milestones when balance is insufficient;
update the procedure that processes v_milestone to explicitly handle this
branch: either INSERT a log entry (into a milestones_log/audit table) or UPDATE
the milestone row (e.g., set status = 'failed' or add a failure_reason like
'insufficient_locked_balance' and updated_at), and/or call your notification
procedure (e.g., notify_client_of_insufficient_balance) so the worker and client
are informed; make the change around the IF ... THEN block that references
v_milestone so the insufficient-balance path is not a silent no-op but records
the condition and triggers notification or a documented state transition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 03bf56e5-2255-480f-98e7-32076a9b096c

📥 Commits

Reviewing files that changed from the base of the PR and between d8534c4 and 9ba6c92.

📒 Files selected for processing (10)
  • src/app/_actions/wallet.ts
  • src/components/features/topup-dialog.tsx
  • src/components/features/wallet-view.tsx
  • src/components/features/withdraw-dialog.tsx
  • src/lib/schemas/wallet.ts
  • src/lib/uuid.ts
  • supabase/functions/auto_release_milestones/index.ts
  • supabase/migrations/20260518135600_withdraw_wallet.sql
  • supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql
  • supabase/migrations/20260518225500_schedule_auto_release_cron.sql
💤 Files with no reviewable changes (1)
  • supabase/functions/auto_release_milestones/index.ts

Comment thread src/app/_actions/wallet.ts
Comment thread supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql Outdated
Comment thread supabase/migrations/20260518225500_schedule_auto_release_cron.sql
Comment thread supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql Outdated

Copilot AI 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.

Pull request overview

Adds a worker withdrawal RPC + UI flow and replaces the prior Edge Function scaffold with a pure-Postgres auto-release cron job for submitted milestones.

Changes:

  • Introduces withdraw_wallet() RPC (with per-user idempotency) and wires it into a new withdrawWalletAction + <WithdrawDialog /> UI.
  • Adds a UUID helper to support idempotency key generation in insecure dev contexts (LAN/HTTP).
  • Schedules auto_release_milestones() via pg_cron and updates status-guard triggers to accommodate cron-driven status updates.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
supabase/migrations/20260518225500_schedule_auto_release_cron.sql Adds pg_cron scheduling, updates guard triggers, and modifies auto_release_milestones() to support cron execution.
supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql Replaces withdraw_wallet to fix available_balance ambiguity by aliasing table references.
supabase/migrations/20260518135600_withdraw_wallet.sql Introduces initial withdraw_wallet RPC and a partial unique idempotency index.
supabase/functions/auto_release_milestones/index.ts Removes the placeholder Edge Function scaffold.
src/lib/uuid.ts Adds UUID v4 generator with randomUUID()/getRandomValues() fallback.
src/lib/schemas/wallet.ts Adds Zod schema/type for withdrawal inputs.
src/components/features/withdraw-dialog.tsx Adds worker withdrawal dialog with validation, quick amounts, and idempotency key generation.
src/components/features/wallet-view.tsx Role-gates wallet dialogs (TopUp for clients, Withdraw for workers).
src/components/features/topup-dialog.tsx Switches top-up idempotency key generation to the shared UUID helper.
src/app/_actions/wallet.ts Adds withdrawWalletAction server action calling withdraw_wallet with error mapping and revalidation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread supabase/migrations/20260518225500_schedule_auto_release_cron.sql
Comment thread supabase/migrations/20260518225500_schedule_auto_release_cron.sql
Comment thread supabase/migrations/20260518135600_withdraw_wallet.sql Outdated
Comment thread supabase/migrations/20260518135600_withdraw_wallet.sql Outdated
Comment thread supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql Outdated
Comment thread supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql Outdated
Comment thread supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql Outdated
Comment thread src/lib/uuid.ts Outdated
Comment thread src/components/features/withdraw-dialog.tsx
Comment thread supabase/migrations/20260518135600_withdraw_wallet.sql Outdated
Comment thread supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql Outdated
Comment thread supabase/migrations/20260518181500_withdraw_wallet_fix_ambiguous_column.sql Outdated
@codeant-ai

codeant-ai Bot commented May 18, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

- withdraw_wallet: add is_worker() check (A), p_amount NULL guard (F),
  p_idempotency_key NULL guard (C), insert-first/catch unique_violation
  pattern mirroring topup (D), public. schema qualifier (I), w. table
  alias to disambiguate available_balance vs RETURNS TABLE column
- worker-form: replace as any cast with typed cast to fix pre-existing
  lint error surfaced during PR #19 pipeline
- guard_milestones_status + guard_jobs_status: replace GUC bypass with
  direct session_user='postgres' check (E)
- auto_release_milestones: drop set_config GUC line (E), add RAISE NOTICE
  on insufficient-locked-balance skip (H)
- wallet.ts: mapWalletRpcError helper routing by error.code + exact
  RAISE EXCEPTION token (G), defense-in-depth is_worker() pre-check in
  withdrawWalletAction (A), raw error logging before generic fallback
- topup-dialog + withdraw-dialog: stable idempotencyKey via useState
  initializer + useEffect reset on close (B), normalize 4-space to
  2-space indent on withdraw-dialog (Copilot #9)
@codeant-ai

codeant-ai Bot commented May 21, 2026

Copy link
Copy Markdown

CodeAnt AI is running Incremental review


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels May 21, 2026
@codeant-ai

codeant-ai Bot commented May 21, 2026

Copy link
Copy Markdown

CodeAnt AI Incremental review completed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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/app/_actions/wallet.ts`:
- Around line 90-97: The unmapped RPC error branches and catch blocks in the
wallet server actions (e.g., inside topupWalletAction and the other try/catch
paths referenced) currently only log and return a generic error; add
Sentry.captureException(error) in each of those branches before returning to
ensure exceptions are recorded, and import Sentry if not already present; ensure
you call Sentry.captureException(error) (or Sentry.withScope if you want to add
context/tags) in the unmapped RPC error block and inside each catch block around
lines referenced (including the other error branches at 115-119, 162-169,
187-190) so every Server Action try/catch path captures the error.

In `@src/lib/uuid.ts`:
- Around line 25-30: The fallback UUID generator using Math.random() in
src/lib/uuid.ts (the "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" replace block) must
be removed and converted to a fail-fast when secure crypto is unavailable:
detect secure randomness via globalThis.crypto?.getRandomValues (or similar) at
the start of the UUID function and if not present throw an explicit error (or
return a clear failure) instead of using Math.random; replace the existing
replace(...) fallback block with that fail-fast behavior and ensure the function
documents/propagates the thrown error so idempotency callers (wallet idempotency
key flows) cannot silently get weaker non-cryptographic IDs.

In `@supabase/migrations/20260521080000_pr19_review_fixes.sql`:
- Around line 152-265: The migration modifies the protected SECURITY DEFINER
function auto_release_milestones(), which violates policy; revert any changes to
that existing function and instead implement the new behavior as a separate RPC
(e.g., public.rpc_auto_release_milestones) or create an ADR explicitly approving
this exact modification; update the migration to remove the CREATE OR REPLACE
for auto_release_milestones and add a new migration that defines the new RPC
wrapper (or include the ADR file reference) so the original SECURITY DEFINER
function remains unchanged unless an ADR is accepted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 88f528ef-4a4b-43ca-8134-219553f8128a

📥 Commits

Reviewing files that changed from the base of the PR and between 9ba6c92 and 60ad8da.

📒 Files selected for processing (67)
  • AGENTS.md
  • CLAUDE.md
  • README.md
  • adr/0001-auth.md
  • adr/0002-escrow.md
  • adr/0003-dispute.md
  • adr/0004-auto-release.md
  • adr/0005-realtime-contract.md
  • adr/0006-nav-shell.md
  • adr/0007-rpc-pattern.md
  • adr/template.md
  • docs/adr/0002-rls-and-security-definer-as-only-escrow-path.md
  • docs/adr/0003-auth-state-hygiene.md
  • docs/adr/0004-migration-discipline.md
  • docs/data-model.md
  • docs/state-machine.md
  • scripts/seed-demo-users.ts
  • src/app/_actions/jobs.ts
  • src/app/_actions/wallet.ts
  • src/app/client/account/page.tsx
  • src/app/client/jobs/[id]/client-job-detail.tsx
  • src/app/client/jobs/[id]/fund/page.tsx
  • src/app/client/jobs/[id]/milestones/client-milestones.tsx
  • src/app/client/jobs/client-job-list.tsx
  • src/app/client/jobs/new/post-job-form.tsx
  • src/app/client/layout.tsx
  • src/app/login/actions.ts
  • src/app/onboarding/actions.ts
  • src/app/onboarding/worker/worker-form.tsx
  • src/app/page.tsx
  • src/app/worker/account/page.tsx
  • src/app/worker/applications/worker-applications.tsx
  • src/app/worker/feed/worker-feed.tsx
  • src/app/worker/jobs/[id]/apply-modal.tsx
  • src/app/worker/jobs/[id]/milestones/worker-milestones.tsx
  • src/app/worker/jobs/[id]/worker-job-detail.tsx
  • src/app/worker/layout.tsx
  • src/components/account/change-phone-dialog.tsx
  • src/components/account/delete-account-dialog.tsx
  • src/components/account/edit-profile-dialog.tsx
  • src/components/account/sign-out-button.tsx
  • src/components/features/topup-dialog.tsx
  • src/components/features/wallet-view.tsx
  • src/components/features/withdraw-dialog.tsx
  • src/components/nav/client-nav-shell.tsx
  • src/components/nav/worker-nav-shell.tsx
  • src/components/ui/alert-dialog.tsx
  • src/components/ui/badge.tsx
  • src/components/ui/dialog.tsx
  • src/components/ui/progress.tsx
  • src/components/ui/separator.tsx
  • src/components/ui/skeleton.tsx
  • src/components/ui/status-badge.tsx
  • src/components/ui/textarea.tsx
  • src/hooks/use-user.ts
  • src/lib/__tests__/escrow.property.test.ts
  • src/lib/escrow-machine.ts
  • src/lib/format.ts
  • src/lib/schemas/jobs.ts
  • src/lib/schemas/onboarding.ts
  • src/lib/schemas/wallet.ts
  • src/lib/supabase/client.ts
  • src/lib/supabase/server.ts
  • src/lib/types/database.ts
  • src/lib/uuid.ts
  • supabase/functions/auto-release/index.ts
  • supabase/migrations/20260521080000_pr19_review_fixes.sql
💤 Files with no reviewable changes (2)
  • src/lib/format.ts
  • src/app/login/actions.ts
✅ Files skipped from review due to trivial changes (48)
  • adr/template.md
  • adr/0006-nav-shell.md
  • src/components/ui/separator.tsx
  • src/app/client/jobs/[id]/fund/page.tsx
  • src/app/worker/jobs/[id]/apply-modal.tsx
  • README.md
  • src/app/client/layout.tsx
  • src/lib/supabase/client.ts
  • docs/data-model.md
  • docs/adr/0003-auth-state-hygiene.md
  • supabase/functions/auto-release/index.ts
  • src/lib/tests/escrow.property.test.ts
  • src/components/nav/client-nav-shell.tsx
  • src/components/account/change-phone-dialog.tsx
  • src/components/ui/skeleton.tsx
  • adr/0005-realtime-contract.md
  • src/components/ui/status-badge.tsx
  • src/app/client/jobs/client-job-list.tsx
  • CLAUDE.md
  • src/app/onboarding/actions.ts
  • src/components/ui/progress.tsx
  • src/lib/schemas/onboarding.ts
  • src/app/worker/layout.tsx
  • docs/adr/0004-migration-discipline.md
  • adr/0002-escrow.md
  • src/lib/types/database.ts
  • src/components/account/sign-out-button.tsx
  • src/lib/escrow-machine.ts
  • adr/0003-dispute.md
  • src/app/worker/feed/worker-feed.tsx
  • src/app/_actions/jobs.ts
  • src/components/nav/worker-nav-shell.tsx
  • src/app/worker/account/page.tsx
  • src/hooks/use-user.ts
  • src/app/page.tsx
  • docs/state-machine.md
  • src/app/client/jobs/new/post-job-form.tsx
  • src/app/client/account/page.tsx
  • src/components/ui/textarea.tsx
  • src/app/worker/applications/worker-applications.tsx
  • adr/0001-auth.md
  • src/components/account/edit-profile-dialog.tsx
  • adr/0007-rpc-pattern.md
  • src/components/ui/badge.tsx
  • adr/0004-auto-release.md
  • src/components/ui/dialog.tsx
  • docs/adr/0002-rls-and-security-definer-as-only-escrow-path.md
  • src/components/account/delete-account-dialog.tsx

Comment thread src/app/_actions/wallet.ts
Comment thread src/lib/uuid.ts Outdated
Comment thread supabase/migrations/20260521080000_pr19_review_fixes.sql
@shaiksohelll

Copy link
Copy Markdown
Owner Author

@copilot review

Copilot AI commented May 21, 2026

Copy link
Copy Markdown

@copilot review

Reviewed and re-validated this PR state. The review feedback is already addressed in commit 60ad8da (latest on this branch), and no additional code changes were required from this new comment. No new UI changes were made in this follow-up pass.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • fonts.googleapis.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/Pakka/Pakka/node_modules/.pnpm/next@15.5.15_@babel&#43;core@7.29.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

- uuid.ts: remove Math.random fallback, throw if secure crypto API
  unavailable; collision resistance is a correctness requirement for
  wallet idempotency keys (CodeRabbit follow-up #2)
- docs/adr/0005: ADR approving in-place modification of
  auto_release_milestones per Copilot #1 (drop GUC bypass) and
  CodeRabbit #3 (RAISE NOTICE on silent skip), as required by ADR 0004
- pr19_review_fixes.sql: add ADR reference comment above
  auto_release_milestones rewrite for discoverability (CodeRabbit
  follow-up #3)

Deferred to PR #20: Sentry.captureException wiring (CodeRabbit
follow-up #1) — Sentry SDK installation is the dedicated scope of
PR #20 per the roadmap; adding capture calls now would not compile.
@shaiksohelll

Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai

codeant-ai Bot commented May 21, 2026

Copy link
Copy Markdown

CodeAnt AI is running the review.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels May 21, 2026
@shaiksohelll

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 23, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

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.

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@shaiksohelll

Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented May 23, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

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.

@shaiksohelll

Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai

codeant-ai Bot commented May 23, 2026

Copy link
Copy Markdown

CodeAnt AI is running the review.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels May 23, 2026
@codeant-ai

codeant-ai Bot commented May 23, 2026

Copy link
Copy Markdown

Sequence Diagram

This PR adds a worker-only wallet withdrawal path with idempotent RPC handling and a database cron that auto-releases eligible milestones, updating wallets and ledger entries while emitting notifications.

sequenceDiagram
    participant Worker
    participant App
    participant Database
    participant Cron

    Worker->>App: Submit withdraw request
    App->>Database: Call withdraw_wallet via server action
    Database->>Database: Validate worker, idempotency, balance and write ledger
    Database-->>App: Return new available balance and ledger id
    App-->>Worker: Show updated wallet balance

    Cron->>Database: Run auto_release_milestones on schedule
    Database->>Database: Release due milestones and update wallets and ledger
    Database-->>App: Create notifications for client and worker
Loading

Generated by CodeAnt AI

Comment thread src/app/client/jobs/[id]/client-job-detail.tsx Outdated
Comment thread src/app/client/jobs/[id]/milestones/client-milestones.tsx
Comment thread src/app/client/jobs/[id]/milestones/client-milestones.tsx Outdated
Comment thread src/app/worker/account/page.tsx
Comment thread src/app/worker/account/page.tsx
… race guard (round-12)

Fix 1 - worker-milestones.tsx, client-milestones.tsx, client-job-detail.tsx:
move router.replace("/login") out of the render path and into a
useEffect([isAuthLoading, user, router]). Render guard collapses back to
the combined `isAuthLoading || !user?.id` -> <Skeleton /> shape, so the
user sees a skeleton both during auth hydration and during the
post-resolution navigation tick. No flash of stale UI; no React
render-phase side-effect warning. Dependency array uses `user` (whole
object) rather than `user?.id` to avoid the exhaustive-deps lint warning
about optional chaining in deps. Closes the regression from round-11
Fix 2/3.

Fix 2 - worker/account/page.tsx: clear local profile and worker state
(setProfile(null), setWorker(null)) before router.replace("/login") in
load()'s !user branch, so stale private data cannot flash between
sign-out and navigation.

Fix 3 - worker/account/page.tsx: add a requestIdRef race-condition guard
to load(). Increments at call start; checks before every setState after
each await (post-getUser, post-Promise.all). Concurrent triggers
(initial mount, Retry, onSaved, onChanged) can no longer have an older
in-flight request overwrite fresher state. Pattern is dependency-free;
no AbortController used because supabase-js v2 doesn't support signal
forwarding on .from() queries.

Refs round-12 review: CodeAnt Major x4 (3 render-phase redirects, 1
stale state, 1 race condition; the render-phase finding was applied
proactively to worker-milestones.tsx as well even though CodeAnt only
flagged it on the two client files).

Round-12 CodeAnt CRITICAL x1 (client-milestones.tsx:238 fund
idempotency forwarding) reply-dismissed and conversation resolved in
PR thread: see Phase 4.5 escrow idempotency PR.
@shaiksohelll

Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai

codeant-ai Bot commented May 23, 2026

Copy link
Copy Markdown

CodeAnt AI is running the review.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai

codeant-ai Bot commented May 23, 2026

Copy link
Copy Markdown

Sequence Diagram

This diagram shows the new worker withdrawal flow that debits wallets via a secure RPC with idempotent keys, and the pg_cron-driven auto_release_milestones job that automatically releases eligible escrowed milestones and updates wallets.

sequenceDiagram
    participant Worker
    participant WebApp
    participant ServerAction
    participant Database
    participant CronScheduler

    Worker->>WebApp: Submit withdraw request (amount)
    WebApp->>ServerAction: Call withdrawWalletAction
    ServerAction->>Database: Verify worker role and validate amount
    ServerAction->>Database: RPC withdraw_wallet with idempotency key
    Database-->>ServerAction: Updated balance and ledger entry
    ServerAction-->>WebApp: Return success and revalidate wallet view

    CronScheduler->>Database: Run auto_release_milestones daily
    Database->>Database: Auto-release eligible milestones and update wallets
    Database-->>CronScheduler: Return count of released milestones
Loading

Generated by CodeAnt AI

Comment thread src/app/worker/jobs/[id]/milestones/worker-milestones.tsx
@codeant-ai

codeant-ai Bot commented May 23, 2026

Copy link
Copy Markdown

CodeAnt AI finished running the review.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@shaiksohelll shaiksohelll left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

PR #19 — Final Review Summary

This PR went through 12 review rounds (rounds 5–12 documented in commit history). All Critical/High/Major findings against in-scope changes have been addressed. Remaining open threads fall into the following intentional-deferral buckets:

Deferred to Phase 4.5 — Escrow Idempotency PR

RPC-level uniqueness enforcement on p_idempotency_key for fund_escrow, submit_milestone, approve_milestone, dispute_milestone. The client-side per-intent key shipped in round-8 is the application-layer half of the contract; the RPC signatures don't currently accept the parameter, so wiring it through the server action would have nothing to bind to. Tracked separately.
Threads: worker-milestones submit handler; client-milestones fund/approve/dispute handlers.

Deferred to PR #20 — Sentry Observability Sweep

All // TODO: Sentry.captureException() markers across the codebase will land together once Sentry is wired in. Markers were intentionally left as TODOs rather than partial wiring.
Threads: escrow.ts (8 sites); worker-milestones.tsx; worker/account/page.tsx (round-11 authError marker).

Deferred to PR #22 — Canonical Async-Dialog Migration

Migration of apply-modal, change-phone-dialog, delete-account-dialog, edit-profile-dialog, worker-form, and withdraw-dialog Zod schema to the canonical async-dialog pattern (reference: src/components/features/topup-dialog.tsx). Three new server actions (updatePhoneAction, requestAccountDeletion, updateProfile) accompany.
Threads: all *-dialog files flagged for pattern migration.

Deferred to PR #23 (downgraded) — Realtime Auth Propagation

Originally flagged: per-file .setAuth() calls on Realtime channels. Resolution: the singleton client at src/lib/supabase/client.ts handles auth propagation to Realtime via eager-prime + onAuthStateChange listener; no per-file action needed. PR #23 scope reduced to verifying all .channel() sites use the singleton (not fresh createClient()).
Threads: 3 Realtime setAuth comments.

Intentional Design Choices (Not Bugs)

  • Migration consolidation (20260521080000_pr19_review_fixes.sql): consolidated round-5 fix-pack for review feedback against the original three feature migrations. Section headers (A/C/D/F/I/E/H) document the per-concern split internally. Future migrations follow one-per-logical-change.
  • Step4 skillInput draft tag clearing on nav-away: by design.

Bot Convergence Status

  • CodeRabbit: converged at round-9 (last finding: Realtime setAuth, dismissed per above).
  • Copilot: converged at round-11 (last findings: cron mismatch + exception scope, both fixed).
  • CodeAnt: active; remaining re-flags are repeats of the Phase 4.5 finding above.

Ready to merge pending final CI green light.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants