Skip to content

More Demo Fixes Overall#279

Merged
farhan-navas merged 7 commits into
mainfrom
farhan-navas/san-144-more-demo-fixes
May 31, 2026
Merged

More Demo Fixes Overall#279
farhan-navas merged 7 commits into
mainfrom
farhan-navas/san-144-more-demo-fixes

Conversation

@farhan-navas
Copy link
Copy Markdown
Contributor

@farhan-navas farhan-navas commented May 30, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Added client- and server-side validation to prevent invalid cap hierarchies (per-call > daily/monthly) with clear error messages.
    • Buy flow now validates merchant contact email (returns 400 with missing_field when profile fields are missing) and distinguishes recovery steps: sign out/in for missing email, set phone/address in dashboard.
    • Discovery now skips products missing or invalid prices and rejects matches with empty names.
  • Documentation

    • Clarified what is forwarded to merchants: account email, operator phone + shipping address, and chosen SKU/quantity; discovery intent/context is never sent.
    • Discovery guidance updated to allow extra JSON keys.

@linear
Copy link
Copy Markdown

linear Bot commented May 30, 2026

SAN-144

@farhan-navas farhan-navas self-assigned this May 30, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

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: 400bf05e-f178-41a8-b0df-9ae37129ae57

📥 Commits

Reviewing files that changed from the base of the PR and between b46aa0e and 1edf8f1.

📒 Files selected for processing (7)
  • backend/.env.example
  • backend/buyHandlers/common.go
  • backend/buyHandlers/discover.go
  • backend/config/sangriaInternal.go
  • backend/dbEngine/discoveries.go
  • backend/dbEngine/models.go
  • backend/sangriamerchant/client.go

📝 Walkthrough

Walkthrough

Moves operator email into the canonical User record, normalizes and upserts it, removes operator.email usage, derives merchant billing email for /v1/buy, adds cap-hierarchy validation (backend + frontend), defers merchant client init using a new internal key config, and updates docs for buy/discover skills.

Changes

Email Architecture and Validation Changes

Layer / File(s) Summary
Database schema and model contracts
dbSchema/schema.ts, backend/dbEngine/models.go, backend/sangriamerchant/client.go
Adds nullable users.email, removes agent_operators.email; updates User, AgentOperator, and DiscoveryMatch to use nullable fields for merchant-provided metadata.
User email persistence and normalization
backend/dbEngine/users.go
Adds userColumns/scanUser, centralizes upsert SQL, normalizes email on UpsertUser/UpsertUserTx, and returns email from GetUserByWorkosID.
Agent operator email removal
backend/dbEngine/agentOperators.go
Removes email from scans/columns; drops email parameter from CreateAgentOperator/CreateAgentOperatorTx; operator insert no longer writes email.
Operator email lookup for purchase flow
backend/dbEngine/users.go
Implements GetUserEmailForOperator joining agent_operators→organization_members(is_admin)→users.email; returns empty string when absent or NULL.
Auth, org creation, and operator wiring
backend/auth/workos.go, backend/dbEngine/organizations.go, backend/dbEngine/users.go
CreateUser now passes email to UpsertUserTx; CreateOrganization signature drops creatorEmail and stops passing email into agent operator creation; EnsurePersonalOrganizationTx drops email param.
Buy handler integration and docs
backend/buyHandlers/buy.go, backend/buyHandlers/common.go, SANGRIA_BUY_SKILL.md, SANGRIA_DISCOVER_SKILL.md
/v1/buy fetches user email via GetUserEmailForOperator, treats lookup errors as internal_error, returns invalid_request if email empty, sets merchant BuyRequest.Email from fetched email, defers merchant client init, and docs clarify forwarded fields and troubleshooting guidance.
Dashboard and admin handler updates
backend/adminHandlers/merchants.go, backend/clientHandlers/context.go
CreateMerchantAPIKey upserts user with email; resolveOperator/CreateAgentOperator calls no longer include email argument.
Backend cap-hierarchy validation
backend/clientHandlers/apiKeys.go
Adds validateCapHierarchy to enforce per-call ≤ daily ≤ monthly (unlimited treated as +∞) and uses it in CreateAPIKey and UpdateAPIKeySettings returning 400 on invalid hierarchies.
Frontend cap-hierarchy validation
frontend/app/(client)/client/settings/ClientSettingsContent.tsx, frontend/components/CardSettingsModal.tsx, frontend/components/CreateAgentKeyModal.tsx
Adds cross-field checks to reject per-run/per-call caps that exceed daily or monthly caps before submission.
Merchant client init and internal key config
backend/config/sangriaInternal.go, backend/buyHandlers/common.go, backend/sangriamerchant/client.go, backend/main.go
Adds SangriaInternal loader for SANGRIA_INTERNAL_KEY; New(internalKey string) client stores internalKey and sets Authorization on Buy when present and HTTPS; InitMerchantClient defers construction until config loaded at startup.
Facilitator HTTPS guard
backend/x402Handlers/facilitator.go
Suppresses CDP JWT auth for non-HTTPS facilitator requests and logs a warning.
Discover candidate and match validation
backend/buyHandlers/discover.go, backend/dbEngine/discoveries.go
Skips catalog products with nil PriceUSD and logs warnings; validateDiscoveryMatches rejects empty-trimmed Name fields.
Skill documentation updates
SANGRIA_BUY_SKILL.md, SANGRIA_DISCOVER_SKILL.md
Clarifies /v1/buy validation uses account email + operator phone + full shipping address; specifies forwarded fields to merchants and updates troubleshooting guidance; clarifies discover context allowances.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • GTG-Labs/sangria#119: Related edits to CreateMerchantAPIKey and dbengine.UpsertUser inputs.
  • GTG-Labs/sangria#277: Overlapping WorkOS/user persistence and agent-operator creation changes that this PR builds upon.
  • GTG-Labs/sangria#271: Prior discovery/discovery-match work related to discover model and endpoint behavior.

Suggested reviewers

  • rahulmllvrp
  • simantak-dabhade

Poem

🐰 I hopped through tables, tidy and bright,

Moved emails to users, set validations right.
Caps now obey order, no run can exceed,
Buys whisper only contacts merchants need.
A carrot-toast for the clean deploy tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'More Demo Fixes Overall' is vague and generic, using non-descriptive terms that don't convey meaningful information about the substantial changes in this pull request. Use a more specific title that reflects the main changes, such as 'Refactor email handling and add cap validation for API keys' or 'Move operator email to user model and implement merchant authentication.'
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 farhan-navas/san-144-more-demo-fixes

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
dbSchema/schema.ts (1)

53-69: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Enforce canonical email values in the schema.

UpsertUserTx normalizes email, but users.email still accepts blank, untrimmed, or mixed-case values if any writer bypasses that helper. Since this column is now the canonical merchant-contact source, the table should reject non-canonical values instead of relying only on application code.

Suggested schema guard
-export const users = pgTable("users", {
-  workosId: text("workos_id").primaryKey(),
-  owner: text().notNull(),
-  email: varchar({ length: 320 }), // RFC 5321 max
-  createdAt: timestamp("created_at", { withTimezone: true })
-    .notNull()
-    .defaultNow(),
-  updatedAt: timestamp("updated_at", { withTimezone: true })
-    .notNull()
-    .defaultNow(),
-});
+export const users = pgTable(
+  "users",
+  {
+    workosId: text("workos_id").primaryKey(),
+    owner: text().notNull(),
+    email: varchar({ length: 320 }), // RFC 5321 max
+    createdAt: timestamp("created_at", { withTimezone: true })
+      .notNull()
+      .defaultNow(),
+    updatedAt: timestamp("updated_at", { withTimezone: true })
+      .notNull()
+      .defaultNow(),
+  },
+  (table) => [
+    check(
+      "chk_users_email_canonical",
+      sql`${table.email} IS NULL OR (
+        ${table.email} = lower(btrim(${table.email}))
+        AND length(btrim(${table.email})) > 0
+      )`,
+    ),
+  ],
+);
As per coding guidelines, "Enforce correctness at the database layer (NOT NULL, unique, FK, CHECK constraints). Never rely on caller discipline."
🤖 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 `@dbSchema/schema.ts` around lines 53 - 69, The users.email column currently
allows blank/untrimmed/mixed-case values; add DB-level guards so the table
enforces canonical emails: make users.email NOT NULL and add a CHECK constraint
on users (email = lower(trim(email)) AND email <> '') (optionally also validate
format with an appropriate regex if desired). Update any migration/DDL that
defines users (and reference UpsertUserTx behavior) so the new NOT NULL + CHECK
are applied atomically during migration to avoid violating existing rows (e.g.,
backfill/trim existing null/blank values first).
🤖 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.

Outside diff comments:
In `@dbSchema/schema.ts`:
- Around line 53-69: The users.email column currently allows
blank/untrimmed/mixed-case values; add DB-level guards so the table enforces
canonical emails: make users.email NOT NULL and add a CHECK constraint on users
(email = lower(trim(email)) AND email <> '') (optionally also validate format
with an appropriate regex if desired). Update any migration/DDL that defines
users (and reference UpsertUserTx behavior) so the new NOT NULL + CHECK are
applied atomically during migration to avoid violating existing rows (e.g.,
backfill/trim existing null/blank values first).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 264160b6-8ef0-4102-b93c-25d98db6cc50

📥 Commits

Reviewing files that changed from the base of the PR and between 7745fae and 75d2b49.

📒 Files selected for processing (15)
  • SANGRIA_BUY_SKILL.md
  • SANGRIA_DISCOVER_SKILL.md
  • backend/adminHandlers/merchants.go
  • backend/auth/workos.go
  • backend/buyHandlers/buy.go
  • backend/clientHandlers/apiKeys.go
  • backend/clientHandlers/context.go
  • backend/dbEngine/agentOperators.go
  • backend/dbEngine/models.go
  • backend/dbEngine/organizations.go
  • backend/dbEngine/users.go
  • dbSchema/schema.ts
  • frontend/app/(client)/client/settings/ClientSettingsContent.tsx
  • frontend/components/CardSettingsModal.tsx
  • frontend/components/CreateAgentKeyModal.tsx
💤 Files with no reviewable changes (1)
  • backend/clientHandlers/context.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@backend/sangriamerchant/client.go`:
- Around line 274-281: The Buy flow currently attaches the shared secret
(c.internalKey) to req.Header in client.go regardless of the buyURL scheme; fix
this by resolving the buy URL via DeriveBuyURL (or using the already-resolved
buyURL) and only setting Authorization: Bearer <c.internalKey> when the URL
scheme is "https" — otherwise omit the header (or return an error/reject the
request) to avoid sending secrets over plaintext HTTP; update the logic around
c.internalKey and req.Header.Set to inspect the resolved URL.Scheme before
attaching the header.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ae0641be-d13e-4414-bf1e-41bbcd4565de

📥 Commits

Reviewing files that changed from the base of the PR and between 75d2b49 and c87bf7c.

📒 Files selected for processing (4)
  • backend/buyHandlers/common.go
  • backend/config/sangriaInternal.go
  • backend/main.go
  • backend/sangriamerchant/client.go

Comment thread backend/sangriamerchant/client.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@backend/x402Handlers/facilitator.go`:
- Around line 145-153: The current substring check using
strings.Contains(facilitatorURL, "api.cdp.coinbase.com") is too permissive and
can leak JWTs; instead parse facilitatorURL with url.Parse (or use net/url) and
do an exact hostname comparison (u.Hostname() == "api.cdp.coinbase.com"), handle
parse errors by returning nil, and keep the existing scheme check using
req.URL.Scheme != "https" (or additionally verify the parsed URL's Scheme if
appropriate) before attaching the CDP Authorization header in facilitator.go.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ab9a7aba-b06e-4fe9-a595-84956f404afa

📥 Commits

Reviewing files that changed from the base of the PR and between c87bf7c and 440cbc5.

📒 Files selected for processing (2)
  • backend/sangriamerchant/client.go
  • backend/x402Handlers/facilitator.go

Comment thread backend/x402Handlers/facilitator.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
backend/buyHandlers/discover.go (1)

108-131: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider skipping products with empty Name for consistency with price validation.

The new validation in validateDiscoveryMatches (discoveries.go lines 142-144) will reject matches with empty Name. However, the handler only checks PriceUSD before building matches. If a merchant product has a valid price but an empty name, it will pass the handler's checks, be added to matches, and then cause CreateDiscovery to fail with ErrInvalidMatchShape—resulting in a 500 for the entire request even if other matches are valid.

Adding a similar skip for empty Name would maintain consistent behavior: bad catalog entries are skipped with a warning, and valid matches can still proceed.

♻️ Proposed fix to add Name validation
 		for _, cand := range top {
 			if cand.Product.PriceUSD == nil {
 				slog.Warn("merchant product has invalid price; skipping candidate",
 					"sku", cand.Product.SKU, "price_usd", "null")
 				continue
 			}
+			if strings.TrimSpace(cand.Product.Name) == "" {
+				slog.Warn("merchant product has empty name; skipping candidate",
+					"sku", cand.Product.SKU)
+				continue
+			}
 			subtotal, ok := toMicrounitsSafe(*cand.Product.PriceUSD)
🤖 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 `@backend/buyHandlers/discover.go` around lines 108 - 131, The handler
currently only skips candidates with invalid PriceUSD; add a check to also skip
candidates with an empty product name to avoid creating invalid matches that
later cause CreateDiscovery/validateDiscoveryMatches to return
ErrInvalidMatchShape. In the loop that builds matches (where toMicrounitsSafe is
called and matches = append(matches, dbengine.DiscoveryMatch{...})), verify
cand.Product.Name != "" (or trim and check length) before computing subtotal; if
empty, log a warning similar to the price checks (include "sku" and "name"
fields) and continue so only matches with non-empty Name and valid price are
appended.
🤖 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.

Outside diff comments:
In `@backend/buyHandlers/discover.go`:
- Around line 108-131: The handler currently only skips candidates with invalid
PriceUSD; add a check to also skip candidates with an empty product name to
avoid creating invalid matches that later cause
CreateDiscovery/validateDiscoveryMatches to return ErrInvalidMatchShape. In the
loop that builds matches (where toMicrounitsSafe is called and matches =
append(matches, dbengine.DiscoveryMatch{...})), verify cand.Product.Name != ""
(or trim and check length) before computing subtotal; if empty, log a warning
similar to the price checks (include "sku" and "name" fields) and continue so
only matches with non-empty Name and valid price are appended.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: aaa9d0f3-d9d8-48e2-8420-a9319f5de9b7

📥 Commits

Reviewing files that changed from the base of the PR and between b46aa0e and ca42250.

📒 Files selected for processing (4)
  • backend/buyHandlers/discover.go
  • backend/dbEngine/discoveries.go
  • backend/dbEngine/models.go
  • backend/sangriamerchant/client.go

@farhan-navas farhan-navas force-pushed the farhan-navas/san-144-more-demo-fixes branch from ca42250 to 50f8fc6 Compare May 31, 2026 01:12
@farhan-navas farhan-navas merged commit 9c45a30 into main May 31, 2026
1 check passed
@farhan-navas farhan-navas deleted the farhan-navas/san-144-more-demo-fixes branch May 31, 2026 03:49
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