Skip to content

feat: Track D API hygiene (pre-iOS) - #121

Merged
j-cadena-g merged 2 commits into
mainfrom
feat/track-d-api-hygiene
Aug 11, 2026
Merged

feat: Track D API hygiene (pre-iOS)#121
j-cadena-g merged 2 commits into
mainfrom
feat/track-d-api-hygiene

Conversation

@j-cadena-g

Copy link
Copy Markdown
Owner

Summary

  • Normalize API/WS error responses to { error, code } and share Clerk auth option shapes across middleware, clerk routes, and WebSocket upgrades
  • Audit grocery sync mutations (idempotent-safe) plus membership and invite lifecycle writes
  • Soft-delete recurring rules (0019) and filter tombstones from live queries/processor paths
  • Use shared zCurrencyCode in accounts and debts schemas

Test plan

  • pnpm exec vitest run --config vitest.config.ts server/api/route.test.ts worker.test.ts
  • pnpm exec vitest run --config vitest.workers.config.ts server/api/invites.integration.test.ts server/api/members.integration.test.ts
  • Confirm migration 0019_recurring_soft_delete.sql applies cleanly
  • Soft-delete a recurring rule and verify it disappears from list/calendar/dashboard and is not processed by cron
  • Create/revoke invite and change a member role; confirm audit_logs rows are written
  • Offline grocery sync add/toggle/delete still works; failed sync results remain string errors

Normalize error envelopes and Clerk auth options, audit sync and membership mutations, soft-delete recurring rules, and share currency validation.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 02ee071b-7b51-4075-929f-eb64e3727def

📥 Commits

Reviewing files that changed from the base of the PR and between 29dae84 and a8b6cd9.

📒 Files selected for processing (6)
  • apps/web/server/api/audit.ts
  • apps/web/server/api/invites.ts
  • apps/web/server/api/members.ts
  • apps/web/server/api/sync.ts
  • apps/web/server/lib/errors.ts
  • apps/web/server/lib/recurring-processor.ts
💤 Files with no reviewable changes (1)
  • apps/web/server/api/members.ts
📜 Recent review details
🔇 Additional comments (7)
apps/web/server/lib/errors.ts (1)

1-1: LGTM!

Also applies to: 10-19, 30-38

apps/web/server/lib/recurring-processor.ts (2)

237-270: Make occurrence insertion conditional on the live rule.

stillLive and db.insert(transactions) are separate database operations. A soft delete can commit after the second check and before the insert. The tombstoned rule can then create one occurrence.

Use one conditional write or one transaction that checks the recurring rule state when it inserts the occurrence. This is the same TOCTOU condition from the prior comment.


164-164: LGTM!

Also applies to: 190-190

apps/web/server/api/sync.ts (2)

408-420: Make tag replacement conditional on an active grocery item.

stillActive and db.batch are separate database operations. A soft delete can commit after the check and before the batch. The batch can then replace tag relations for a deleted grocery item.

Make the active-item condition part of the tag replacement write. This is the same TOCTOU condition from the prior comment.


297-315: LGTM!

apps/web/server/api/audit.ts (1)

123-123: LGTM!

apps/web/server/api/invites.ts (1)

178-205: LGTM!

Also applies to: 290-327


📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Deleted recurring transactions are retained for history while excluded from active budgets, calendars, dashboards, and processing.
    • Added audit history for invitations, membership changes, and grocery item updates.
    • Standardized currency validation across account and debt setup.
  • Bug Fixes
    • Improved API and WebSocket error responses with clearer error codes.
    • Strengthened authentication and authorization handling for origins, sessions, and permissions.
    • Prevented deleted or concurrently changed recurring transactions from being processed.
  • Tests
    • Expanded coverage for validation, authentication, authorization, and error responses.

Walkthrough

The PR adds soft deletion for recurring transactions, filters deleted rules from reads and processing, expands audit logging for mutations, standardizes Clerk authentication and JSON errors, and reuses shared currency validation for account and debt schemas.

Changes

Platform data and API behavior

Layer / File(s) Summary
Recurring transaction soft deletion
packages/db/..., apps/web/server/api/recurring.ts, apps/web/server/lib/..., apps/web/app/routes/budget.recurring.tsx
Recurring transactions gain a nullable deletedAt field and index. Deletion now marks records deleted and inactive. Related queries and processing exclude deleted rules.
Mutation audit coverage
apps/web/server/api/invites.ts, apps/web/server/api/members.ts, apps/web/server/api/sync.ts
Invite, member, grocery-item, and tag mutations now record audit entries with actor and state data.
Shared authentication and error responses
apps/web/server/lib/clerk-auth-options.ts, apps/web/server/lib/errors.ts, apps/web/server/api/route.ts, apps/web/worker.ts, apps/web/server/middleware/app-context.ts, apps/web/server/api/push.ts
Clerk options are shared across authentication paths. API, WebSocket, and push ownership errors now use mapped codes and jsonError.
Shared currency validation
apps/web/server/api/accounts.ts, apps/web/server/api/debts.ts
Account and debt schemas now use zCurrencyCode instead of local currency lists.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Poem

I’m a rabbit with audit logs bright,
Soft-deleted rules leave active sight.
Errors now share codes in a row,
Auth options use one flow.
Currency checks hop with care—
Cleaner records everywhere!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title identifies the main API-hygiene work and its pre-iOS context, which matches the primary changeset theme.
Description check ✅ Passed The description directly summarizes standardized errors, audit logging, recurring-rule soft deletion, and shared currency validation in the changeset.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/track-d-api-hygiene
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/track-d-api-hygiene

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

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

🤖 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 `@apps/web/server/api/audit.ts`:
- Line 127: Update the recurring rule authorization query around
isNull(recurringTransactions.deletedAt) to allow tombstoned records while
retaining the existing household and visibility checks. Use the established
tombstone-aware authorization pattern for recurring_transactions so deleted
rules remain available for audit log reads instead of producing NOT_FOUND.

In `@apps/web/server/api/invites.ts`:
- Around line 178-205: Remove plaintext email fields from the audit snapshot
newValues objects at apps/web/server/api/invites.ts lines 178-205 and 587-610,
and apps/web/server/api/members.ts lines 592-606 and 711-725. Preserve stable
IDs and required role fields while retaining emails only where an approved
retention and access-control policy explicitly requires them.

In `@apps/web/server/api/sync.ts`:
- Around line 286-314: The toggle update at apps/web/server/api/sync.ts lines
286-314 must add an isNull(groceryItems.deletedAt) predicate and reject the
mutation when no active row is returned, preventing audit updates after soft
deletion. The tag replacement flow at apps/web/server/api/sync.ts lines 373-423
must condition its writes on the grocery item remaining active and reject the
mutation if deletion occurred before replacement.

In `@apps/web/server/lib/errors.ts`:
- Around line 35-37: Update the response body construction in jsonError so extra
is spread before the required error and code fields, ensuring callers cannot
overwrite either field while preserving the existing HTTP status mapping.

In `@apps/web/server/lib/recurring-processor.ts`:
- Line 190: Make occurrence creation atomic with the deletion check by
revalidating the recurring rule’s deletedAt state within the write-time
transaction or conditional insert, before inserting the transaction. Update the
processor flow around candidateRules and advanceRecurringRuleIfCurrent so a rule
deleted after selection cannot create an occurrence, while preserving
advancement behavior for non-deleted rules.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3109485c-d3af-42a1-b573-d713c82b4ae2

📥 Commits

Reviewing files that changed from the base of the PR and between ff26bcd and 29dae84.

📒 Files selected for processing (24)
  • apps/web/app/routes/budget.recurring.tsx
  • apps/web/server/api/accounts.ts
  • apps/web/server/api/audit.ts
  • apps/web/server/api/budgets.ts
  • apps/web/server/api/calendar.ts
  • apps/web/server/api/debts.ts
  • apps/web/server/api/invites.ts
  • apps/web/server/api/members.ts
  • apps/web/server/api/push.ts
  • apps/web/server/api/recurring.ts
  • apps/web/server/api/route.test.ts
  • apps/web/server/api/route.ts
  • apps/web/server/api/sync.ts
  • apps/web/server/lib/clerk-auth-options.ts
  • apps/web/server/lib/dashboard-data.ts
  • apps/web/server/lib/errors.ts
  • apps/web/server/lib/financial-categories.ts
  • apps/web/server/lib/recurring-processor.ts
  • apps/web/server/middleware/app-context.ts
  • apps/web/worker.test.ts
  • apps/web/worker.ts
  • packages/db/migrations/0019_recurring_soft_delete.sql
  • packages/db/migrations/meta/_journal.json
  • packages/db/src/schema/recurring.ts
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-06-21T23:05:02.739Z
Learnt from: j-cadena-g
Repo: j-cadena-g/amigo PR: 64
File: packages/db/migrations/0013_financial_categories.sql:21-31
Timestamp: 2026-06-21T23:05:02.739Z
Learning: In this codebase, tenant/household isolation for DB writes is enforced at the application layer using `scopeToHousehold()` (not via DB-level composite foreign keys or household-bound CHECK constraints). During review of SQL migrations, don’t require DB-layer household isolation constraints for every change as long as the affected write paths are covered by `scopeToHousehold()`. Treat additional DB-layer constraints as defense-in-depth that should be handled via the separately tracked follow-ups (e.g., for `budget_category_mappings`) rather than being mandatory for each migration PR.

Applied to files:

  • packages/db/migrations/0019_recurring_soft_delete.sql
🔇 Additional comments (24)
apps/web/server/api/accounts.ts (2)

2-3: LGTM!


25-27: LGTM!

apps/web/server/api/debts.ts (1)

23-25: LGTM!

apps/web/server/lib/clerk-auth-options.ts (1)

1-18: LGTM!

apps/web/server/lib/errors.ts (1)

1-18: LGTM!

apps/web/server/middleware/app-context.ts (1)

5-5: LGTM!

Also applies to: 25-28

apps/web/server/api/route.ts (1)

11-12: LGTM!

Also applies to: 56-56, 73-79, 108-135

apps/web/worker.ts (1)

12-13: LGTM!

Also applies to: 110-124, 139-147

apps/web/server/api/push.ts (1)

3-3: LGTM!

Also applies to: 108-110, 141-143

apps/web/server/api/route.test.ts (1)

128-128: LGTM!

Also applies to: 266-266, 284-287

apps/web/worker.test.ts (1)

83-83: LGTM!

Also applies to: 102-102

packages/db/migrations/0019_recurring_soft_delete.sql (1)

1-3: LGTM!

apps/web/server/api/budgets.ts (1)

91-92: LGTM!

apps/web/server/lib/dashboard-data.ts (1)

216-216: LGTM!

Also applies to: 395-395

apps/web/server/lib/financial-categories.ts (1)

121-122: LGTM!

apps/web/server/api/members.ts (1)

43-43: LGTM!

Also applies to: 162-177, 375-395, 460-465

apps/web/server/api/invites.ts (1)

27-27: LGTM!

Also applies to: 290-327

apps/web/server/api/sync.ts (1)

16-16: LGTM!

Also applies to: 56-56, 196-196, 252-272, 318-354, 426-429

packages/db/migrations/meta/_journal.json (1)

137-143: LGTM!

packages/db/src/schema/recurring.ts (1)

64-72: LGTM!

apps/web/server/api/recurring.ts (1)

5-5: LGTM!

Also applies to: 85-86, 188-189, 297-298, 324-325, 345-352, 385-386

apps/web/app/routes/budget.recurring.tsx (1)

12-12: LGTM!

Also applies to: 35-36

apps/web/server/api/calendar.ts (1)

162-168: LGTM!

apps/web/server/lib/recurring-processor.ts (1)

6-6: LGTM!

Also applies to: 164-164

Comment thread apps/web/server/api/audit.ts Outdated
Comment thread apps/web/server/api/invites.ts
Comment thread apps/web/server/api/sync.ts
Comment thread apps/web/server/lib/errors.ts
Comment thread apps/web/server/lib/recurring-processor.ts
Allow audit of soft-deleted recurring rules, drop emails from membership audit snapshots, harden sync/processor races, and lock jsonError envelope fields.
@j-cadena-g
j-cadena-g merged commit 9c225d1 into main Aug 11, 2026
8 checks passed
@j-cadena-g
j-cadena-g deleted the feat/track-d-api-hygiene branch August 11, 2026 20:38
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.

1 participant