AI-assisted reward builder - #4277
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds validated AI reward schemas, authorized streaming generation, shared AI usage accounting, typed presets, a client-side builder with preview and review flows, and integration into non-referral reward forms. ChangesAI reward builder
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Partner
participant AIRewardInput
participant useAIRewardBuilder
participant generateReward
participant AIRewardPreviewFrame
participant RewardForm
Partner->>AIRewardInput: enter prompt or select preset
AIRewardInput->>useAIRewardBuilder: start reward generation
AIRewardInput->>useAIRewardBuilder: select preset
useAIRewardBuilder->>generateReward: submit workspaceId, event, prompt
generateReward-->>useAIRewardBuilder: stream structured reward draft
useAIRewardBuilder->>AIRewardPreviewFrame: update preview state
AIRewardPreviewFrame-->>Partner: show preview and review actions
Partner->>RewardForm: accept draft
RewardForm-->>Partner: update reward fields
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (4)
apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx (3)
936-937: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExplain why submit is disabled during AI review.
The submit button is disabled while
aiBuilder.isReviewingis true, butdisabledTooltiponly covers the upsell cases. A user who does not notice the Accept and Discard buttons sees a disabled button with no explanation. Add a tooltip branch that instructs the user to accept or discard the AI preview.🤖 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 `@apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx` around lines 936 - 937, Update the submit button’s disabledTooltip logic near the isUpdating and aiBuilder.isReviewing conditions to add a branch for AI review state, instructing users to accept or discard the AI preview before submitting. Preserve the existing upsell tooltip branches and apply this message when aiBuilder.isReviewing disables submission.
683-776: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the duplicated description editor.
The description and tooltip editor block is repeated verbatim in the non-referral branch (Lines 683-776) and the referral branch (Lines 790-883). The two copies must stay in sync for every future change to the description UI, the max lengths, or the animation.
Extract one local component, for example
RewardDescriptionEditor, that readsdescriptionandtooltipDescriptionand callssetValue. Render it in both branches.Also applies to: 790-883
🤖 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 `@apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx` around lines 683 - 776, The description and tooltip editor markup is duplicated across the non-referral and referral branches. Extract the shared block into a local RewardDescriptionEditor component that receives or accesses description, tooltipDescription, and setValue, including the existing animation, controls, max lengths, and clearing behavior, then render that component in both branches and remove both duplicated implementations.
968-980: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the unused
classNameprop fromRewardSheetCard. Both call sites omitclassName, so the prop is not used in this file.🤖 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 `@apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx` around lines 968 - 980, Remove the unused className prop from the RewardSheetCard component’s PropsWithChildren type, parameter destructuring, and cn call while preserving its existing default styling. Leave both call sites unchanged since neither passes className.apps/web/ui/partners/rewards/ai-reward-builder.tsx (1)
205-260: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winCancel the streamed draft when the builder unmounts.
The
for awaitloop keeps callingapplyDraftafter the sheet closes or after a new generation starts. Track a generation token or a mounted ref, and stop applying partials when the token no longer matches. This also prevents an old stream from overwriting a newer one.🤖 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 `@apps/web/ui/partners/rewards/ai-reward-builder.tsx` around lines 205 - 260, Update generate and the streamed readStreamableValue loop to track a mounted state and per-generation token, invalidating the token on unmount and before starting a new generation. Stop consuming or applying partial drafts when the token is no longer current, so stale streams cannot call applyDraft or overwrite newer generation results.
🤖 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/lib/ai/ai-reward-schema.ts`:
- Around line 71-75: Constrain the amount field in aiRewardModifierSchema with
nonnegative validation, and add object-level validation so percentage modifiers
cannot exceed 100 while preserving the existing unrestricted flat-dollar
behavior.
In `@apps/web/lib/ai/generate-reward.ts`:
- Around line 59-68: Update generateReward to use inputSchema.safeParse and
throw a controlled Error message from validation failures before accessing
parsed data. After obtaining the authenticated session and normalized
workspaceId, call assertRateLimit with [session.user.id, workspaceId] and the
shared policy before invoking streamText, preserving the existing authorization
and generation flow.
- Around line 99-121: The streamText call in the async generation flow must
propagate AI SDK generation failures and limit output size: add an onError
handler that passes the provided error to stream.error, and set maxOutputTokens
to 2000 alongside the existing model options.
In `@apps/web/ui/partners/rewards/ai-reward-builder.tsx`:
- Around line 783-787: Update the children wrapper in the AI reward builder
preview to apply the React 19 inert attribute whenever chromeMounted is true,
alongside the existing pointer-events-none and select-none classes. Preserve
normal keyboard and pointer interaction when chromeMounted is false.
- Around line 149-161: Update applyDraft to preserve the form’s original
defaults when applying next, so the AI-generated changes remain dirty and
unsaved-change detection continues to work; keep discard’s existing
reset(snapshotRef.current) behavior unchanged so discarding restores the
original state.
- Around line 84-108: Update the modifier mapping in the reward builder to
assign the resolved modifierType to the returned type field instead of
modifier.type, ensuring omitted AI types are populated before validateReward
processes the payload.
---
Nitpick comments:
In `@apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx`:
- Around line 936-937: Update the submit button’s disabledTooltip logic near the
isUpdating and aiBuilder.isReviewing conditions to add a branch for AI review
state, instructing users to accept or discard the AI preview before submitting.
Preserve the existing upsell tooltip branches and apply this message when
aiBuilder.isReviewing disables submission.
- Around line 683-776: The description and tooltip editor markup is duplicated
across the non-referral and referral branches. Extract the shared block into a
local RewardDescriptionEditor component that receives or accesses description,
tooltipDescription, and setValue, including the existing animation, controls,
max lengths, and clearing behavior, then render that component in both branches
and remove both duplicated implementations.
- Around line 968-980: Remove the unused className prop from the RewardSheetCard
component’s PropsWithChildren type, parameter destructuring, and cn call while
preserving its existing default styling. Leave both call sites unchanged since
neither passes className.
In `@apps/web/ui/partners/rewards/ai-reward-builder.tsx`:
- Around line 205-260: Update generate and the streamed readStreamableValue loop
to track a mounted state and per-generation token, invalidating the token on
unmount and before starting a new generation. Stop consuming or applying partial
drafts when the token is no longer current, so stale streams cannot call
applyDraft or overwrite newer generation results.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 2880a52e-828d-467b-99d4-97cf60845cdb
📒 Files selected for processing (7)
apps/web/lib/ai/ai-reward-schema.tsapps/web/lib/ai/generate-reward.tsapps/web/ui/partners/rewards/add-edit-reward-sheet.tsxapps/web/ui/partners/rewards/ai-reward-builder.tsxapps/web/ui/partners/rewards/reward-presets.tspackages/ui/src/icons/nucleo/index.tspackages/ui/src/icons/nucleo/sparkle3-fill.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/lib/ai/ai-reward-schema.ts (1)
24-28: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winRequire
metadataFieldfor metadata conditions.The schema accepts a condition with
attribute: "metadata"and nometadataField. That condition cannot identify the metadata key to evaluate. Add an object refinement that requires a non-emptymetadataFieldwhenattributeismetadata.🤖 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 `@apps/web/lib/ai/ai-reward-schema.ts` around lines 24 - 28, Update the condition schema object around metadataField to add a refinement requiring metadataField to be non-empty whenever attribute is "metadata"; preserve optional metadataField behavior for all other attribute values.apps/web/lib/ai/generate-reward.ts (1)
135-150: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winValidate the draft against the selected event.
aiRewardSchemaaccepts percentage and lifetime rewards, plus all condition entities, for every event. The selectedeventonly affects the prompt. A model response can therefore passOutput.objectwhile producing a percentage or lifetime click/lead reward, or a condition that is not supported for that event.Create an event-bound output schema or add an event-specific refinement before forwarding the draft. Validate base fields, inherited modifier fields, and each condition entity and attribute against
REWARD_CONDITIONS[event].🤖 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 `@apps/web/lib/ai/generate-reward.ts` around lines 135 - 150, Update the reward generation flow around aiRewardSchema and partialObject before stream.update to validate drafts against the selected event, not only the base schema. Build an event-bound schema or refinement using REWARD_CONDITIONS[event] that validates base and inherited modifier fields, rejects unsupported percentage or lifetime click/lead rewards, and rejects unsupported condition entities or attributes before forwarding the draft.
🤖 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/lib/api/links/usage-checks.ts`:
- Around line 57-69: Update the atomic update in the usage-check flow to compare
aiUsage against the persisted Project.aiLimit rather than the potentially stale
workspace.aiLimit. Preserve the existing increment behavior only when the stored
limit has not been reached, using a database-supported conditional update or a
transaction that locks and rechecks the project row.
---
Outside diff comments:
In `@apps/web/lib/ai/ai-reward-schema.ts`:
- Around line 24-28: Update the condition schema object around metadataField to
add a refinement requiring metadataField to be non-empty whenever attribute is
"metadata"; preserve optional metadataField behavior for all other attribute
values.
In `@apps/web/lib/ai/generate-reward.ts`:
- Around line 135-150: Update the reward generation flow around aiRewardSchema
and partialObject before stream.update to validate drafts against the selected
event, not only the base schema. Build an event-bound schema or refinement using
REWARD_CONDITIONS[event] that validates base and inherited modifier fields,
rejects unsupported percentage or lifetime click/lead rewards, and rejects
unsupported condition entities or attributes before forwarding the draft.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 853d182b-ddf1-4b64-9675-bd388e40724c
📒 Files selected for processing (5)
apps/web/lib/ai/ai-reward-schema.tsapps/web/lib/ai/generate-partner-network-invite-email.tsapps/web/lib/ai/generate-reward.tsapps/web/lib/api/links/usage-checks.tsapps/web/ui/partners/rewards/ai-reward-builder.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/ui/partners/rewards/ai-reward-builder.tsx
|
@coderabbitai full review. any breaking changes or security issues? |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx (3)
683-775: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftExtract the duplicated reward description block.
Lines 683-775 and lines 790-882 are the same markup: the
Pen2toggle, the animatedmotion.div,InlineBadgePopoverInputfordescription,InlineBadgePopoverRichTextAreafortooltipDescription, and the clear button. Only the title body and the cardcontentdiffer between the referral and non-referral branches. Two copies of about 95 lines must now be kept in sync.Extract the block into a local component (for example
RewardDescriptionFields) that readsdescriptionandtooltipDescriptionfrom the form context, then render it in both branches.🤖 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 `@apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx` around lines 683 - 775, The referral and non-referral branches duplicate the reward description editor markup. Extract the shared Pen2 toggle, animated description/tooltip fields, and clear-button logic into a local RewardDescriptionFields component that reads and updates description and tooltipDescription through the form context, then render that component in both branches while leaving their distinct titles and card content unchanged.
323-330: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueStabilize the
getValuesandresetadapters.Both props are new function instances on every render.
useAIRewardBuilderlists them in the dependency arrays ofensureSnapshot,applyDraft, andgenerate, so all of those callbacks are rebuilt on every render ofRewardSheetContent, including on every streamed chunk. Wrapping the adapters inuseCallbackrestores the memoization.♻️ Proposed refactor
const aiEvent = selectedEvent === "referral" ? "sale" : selectedEvent; + const getAIFormValues = useCallback( + () => getValues() as Record<string, unknown>, + [getValues], + ); + const resetAIFormValues = useCallback( + ( + values: Record<string, unknown>, + options?: { keepDefaultValues?: boolean }, + ) => reset(values as FormData, options), + [reset], + ); const aiBuilder = useAIRewardBuilder({ event: aiEvent, - getValues: () => getValues() as Record<string, unknown>, - reset: (values, options) => reset(values as FormData, options), + getValues: getAIFormValues, + reset: resetAIFormValues, });Add
useCallbackto the React import if it is not already present.🤖 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 `@apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx` around lines 323 - 330, Stabilize the getValues and reset adapter props passed to useAIRewardBuilder in RewardSheetContent by wrapping each in useCallback with appropriate dependencies. Add the React useCallback import if needed, while preserving their current behavior and type conversions so ensureSnapshot, applyDraft, and generate retain memoized callbacks across renders.
970-982: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the unused
classNameprop fromRewardSheetCard. No caller passesclassName, so this prop adds unused API surface.🤖 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 `@apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx` around lines 970 - 982, Remove the optional className prop from the RewardSheetCard component props and eliminate its use in the cn call, preserving the existing card styles and all other props.apps/web/ui/partners/rewards/ai-reward-builder.tsx (1)
269-276: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winEach streamed chunk resets the whole form and regenerates modifier ids.
applyDraftruns on every partial object.buildRewardFormValuesFromDraftassigns a newuuid()to each modifier on line 99, soresetproduces newmodifiers[].idvalues on every chunk.RewardsLogickeys itsuseFieldArrayrows by that id, so every condition row unmounts and remounts many times during a single generation. This causes visible churn in the preview and wasted renders.Consider reusing ids by index across chunks, or throttling partial application (for example, apply partials only when the modifier count or scalar fields change).
🤖 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 `@apps/web/ui/partners/rewards/ai-reward-builder.tsx` around lines 269 - 276, Update the streamed partial handling in the generation loop and the modifier conversion used by buildRewardFormValuesFromDraft so existing modifier ids are reused by index across chunks instead of generating a new uuid for every reset. Preserve stable ids for unchanged rows while assigning uuids only to newly added modifiers, preventing RewardsLogic field-array rows from remounting during streaming.
🤖 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/lib/ai/ai-reward-schema.ts`:
- Around line 5-28: Update aiRewardSchema, aiRewardModifierSchema, and
aiRewardConditionSchema to enforce event-specific rules using
REWARD_CONDITIONS[event] (or validate the completed draft before returning it).
Require click and lead rewards, including modifiers, to be flat with
amountInCents and duration 0, and reject conditions whose entity, attribute, or
operator is not permitted for the selected event. Ensure generate-reward
structured output cannot pass invalid combinations through safeParse to the
builder.
In `@apps/web/ui/partners/rewards/ai-reward-builder.tsx`:
- Around line 452-457: Update the onKeyDown handler in the AI reward builder to
avoid calling generate() when Enter is pressed during IME composition, while
preserving the existing preventDefault and submission behavior for non-composing
Enter without Shift. Use the keyboard event’s composition-state indicator before
invoking generate().
- Around line 827-834: Update the “Try again” Button’s onClick behavior to
preserve the current prompt when calling requestExit, allowing the user to
resend it without retyping; alternatively, rename the button text to “Dismiss”
if discarding the prompt is the intended behavior.
- Around line 442-451: Update the focus management around the textarea and
action row in the AI reward builder to track focus on their shared container
rather than the textarea alone. Use the wrapper’s onFocus/onBlur and
relatedTarget containment check to keep focused true while focus remains inside,
preventing the presets and Generate button from unmounting during keyboard
navigation; retain the delayed close only when focus leaves the container.
---
Nitpick comments:
In `@apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx`:
- Around line 683-775: The referral and non-referral branches duplicate the
reward description editor markup. Extract the shared Pen2 toggle, animated
description/tooltip fields, and clear-button logic into a local
RewardDescriptionFields component that reads and updates description and
tooltipDescription through the form context, then render that component in both
branches while leaving their distinct titles and card content unchanged.
- Around line 323-330: Stabilize the getValues and reset adapter props passed to
useAIRewardBuilder in RewardSheetContent by wrapping each in useCallback with
appropriate dependencies. Add the React useCallback import if needed, while
preserving their current behavior and type conversions so ensureSnapshot,
applyDraft, and generate retain memoized callbacks across renders.
- Around line 970-982: Remove the optional className prop from the
RewardSheetCard component props and eliminate its use in the cn call, preserving
the existing card styles and all other props.
In `@apps/web/ui/partners/rewards/ai-reward-builder.tsx`:
- Around line 269-276: Update the streamed partial handling in the generation
loop and the modifier conversion used by buildRewardFormValuesFromDraft so
existing modifier ids are reused by index across chunks instead of generating a
new uuid for every reset. Preserve stable ids for unchanged rows while assigning
uuids only to newly added modifiers, preventing RewardsLogic field-array rows
from remounting during streaming.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: c6aaf745-638f-40b5-963a-0782e574d101
📒 Files selected for processing (10)
apps/web/lib/ai/ai-reward-schema.tsapps/web/lib/ai/generate-partner-network-invite-email.tsapps/web/lib/ai/generate-reward.tsapps/web/lib/api/links/usage-checks.tsapps/web/lib/upstash/ratelimit-policies.tsapps/web/ui/partners/rewards/add-edit-reward-sheet.tsxapps/web/ui/partners/rewards/ai-reward-builder.tsxapps/web/ui/partners/rewards/reward-presets.tspackages/ui/src/icons/nucleo/index.tspackages/ui/src/icons/nucleo/sparkle3-fill.tsx
|
@coderabbitai final review. any breaking changes or security issues? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- Changed files ---'
git diff --name-status origin/main...HEAD
echo '--- Diff stat ---'
git diff --stat origin/main...HEAD
echo '--- Outlines ---'
for file in \
apps/web/lib/ai/ai-reward-schema.ts \
apps/web/lib/ai/generate-reward.ts \
apps/web/lib/api/links/usage-checks.ts \
apps/web/lib/upstash/ratelimit-policies.ts \
apps/web/ui/partners/rewards/ai-reward-builder.tsx \
apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx \
apps/web/ui/partners/rewards/reward-presets.ts
do
if [ -f "$file" ]; then
echo "### $file"
ast-grep outline "$file" --items all || true
fi
done
echo '--- Full changed-file diffs ---'
git diff --no-ext-diff --unified=80 origin/main...HEAD -- \
apps/web/lib/ai/ai-reward-schema.ts \
apps/web/lib/ai/generate-reward.ts \
apps/web/lib/api/links/usage-checks.ts \
apps/web/lib/upstash/ratelimit-policies.ts \
apps/web/ui/partners/rewards/ai-reward-builder.tsx \
apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx \
apps/web/ui/partners/rewards/reward-presets.tsLength of output: 113986 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- Reward action and schema locations ---'
fd -t f . apps/web | rg '(create-reward|update-reward|rewards\.ts|reward.*schema|partner.*reward)' | head -80
echo '--- Reward action call and validation contexts ---'
rg -n -C 8 --type ts --type tsx \
'(createRewardAction|updateRewardAction|createOrUpdateRewardSchema|rewardConditionsArraySchema|amountInCents)' \
apps/web/lib/actions apps/web/lib/zod apps/web/ui/partners/rewards 2>/dev/null | head -500
echo '--- AI credit helper callers and AI usage model fields ---'
rg -n -C 6 --type ts \
'\b(reserveAIUsageCredit|refundAIUsageCredit|throwIfAIUsageExceeded|aiUsage|aiLimit)\b' \
apps/web | head -500
echo '--- Changed invite-email implementation and base version ---'
sed -n '1,280p' apps/web/lib/ai/generate-partner-network-invite-email.ts
echo '--- BASE ---'
git show origin/main:apps/web/lib/ai/generate-partner-network-invite-email.ts | sed -n '1,280p'
echo '--- Exact changed file line numbers ---'
nl -ba apps/web/lib/ai/generate-reward.ts | sed -n '60,175p'
nl -ba apps/web/lib/ai/ai-reward-schema.ts | sed -n '1,180p'
nl -ba apps/web/ui/partners/rewards/ai-reward-builder.tsx | sed -n '30,140p'Length of output: 3825 🏁 Script executed: #!/bin/bash
set -euo pipefail
for file in \
apps/web/lib/actions/partners/create-reward.ts \
apps/web/lib/actions/partners/update-reward.ts \
apps/web/lib/zod/schemas/rewards.ts \
apps/web/lib/ai/generate-partner-network-invite-email.ts \
apps/web/lib/api/links/usage-checks.ts
do
echo "### OUTLINE $file"
ast-grep outline "$file" --items all || true
done
echo '--- reward actions ---'
cat -n apps/web/lib/actions/partners/create-reward.ts
cat -n apps/web/lib/actions/partners/update-reward.ts
echo '--- reward schema relevant declarations ---'
rg -n -C 10 \
'(createOrUpdateRewardSchema|rewardConditionBaseSchema|rewardConditionsSchema|rewardConditionsArraySchema|amountInCents)' \
apps/web/lib/zod/schemas/rewards.ts
echo '--- shared helper call sites ---'
rg -n -C 10 \
'(reserveAIUsageCredit|refundAIUsageCredit|throwIfAIUsageExceeded)' \
apps/web
echo '--- invite generator: current then base ---'
cat -n apps/web/lib/ai/generate-partner-network-invite-email.ts
echo '--- BASE ---'
git show origin/main:apps/web/lib/ai/generate-partner-network-invite-email.ts | cat -n
echo '--- generated-draft form conversion ---'
sed -n '30,135p' apps/web/ui/partners/rewards/ai-reward-builder.tsx | cat -nLength of output: 68346 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- Reward payload conversion ---'
nl -ba apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx | sed -n '120,210p'
echo '--- Persisted reward validation ---'
fd -t f 'validate-reward.ts' apps/web -x sh -c 'echo "### $1"; cat -n "$1"' sh {}
echo '--- Amount schema units ---'
nl -ba apps/web/lib/zod/schemas/rewards.ts | sed -n '330,430p'
echo '--- Complete reward condition validation ---'
nl -ba apps/web/lib/zod/schemas/rewards.ts | sed -n '316,372p'Length of output: 874 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- Reward payload conversion ---'
sed -n '120,210p' apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx | cat -n
echo '--- Persisted reward validation ---'
fd -t f -a 'validate-reward.ts' apps/web -x sh -c 'echo "### $1"; cat -n "$1"' sh {}
echo '--- Amount schema units ---'
sed -n '330,430p' apps/web/lib/zod/schemas/rewards.ts | cat -n
echo '--- Complete reward condition validation ---'
sed -n '316,372p' apps/web/lib/zod/schemas/rewards.ts | cat -nLength of output: 17368
I found no blocking breaking changes or security issues in PR The reviewed paths enforce workspace membership, role permissions, plan access, rate limits, atomic AI-credit reservation, and schema validation. Generated rewards still pass through the existing reward validation before persistence. Flat reward amounts use dollar values in the form and convert to cents in I did not run runtime tests in this review environment. You are interacting with an AI system. |
|
@coderabbitai final review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
base_ref="origin/main"
echo '--- Current commits and changed files ---'
git log --oneline "${base_ref}..HEAD"
git diff --name-status "${base_ref}...HEAD"
echo '--- Changed-file diff: server and validation paths ---'
git diff --no-ext-diff --unified=60 "${base_ref}...HEAD" -- \
apps/web/lib/ai/generate-reward.ts \
apps/web/lib/ai/ai-reward-schema.ts \
apps/web/lib/api/links/usage-checks.ts \
apps/web/lib/upstash/ratelimit-policies.ts \
apps/web/lib/ai/generate-partner-network-invite-email.ts
echo '--- Structure map: client reward flow ---'
for file in \
apps/web/ui/partners/rewards/ai-reward-builder.tsx \
apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx \
apps/web/ui/partners/rewards/reward-presets.ts \
apps/web/lib/actions/partners/create-reward.ts \
apps/web/lib/actions/partners/update-reward.ts \
apps/web/lib/api/rewards/validate-reward.ts
do
if [ -f "$file" ]; then
echo "### $file"
ast-grep outline "$file" --items all || true
fi
done
echo '--- Exact security and persistence contexts ---'
for file in \
apps/web/lib/ai/generate-reward.ts \
apps/web/lib/api/links/usage-checks.ts \
apps/web/lib/actions/partners/create-reward.ts \
apps/web/lib/actions/partners/update-reward.ts \
apps/web/lib/api/rewards/validate-reward.ts \
apps/web/ui/partners/rewards/ai-reward-builder.tsx \
apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx
do
if [ -f "$file" ]; then
echo "### $file"
cat -n "$file"
fi
done
echo '--- AI generator call sites and form submission flow ---'
rg -n -C 7 --type ts --type tsx \
'\b(generateReward|reserveAIUsageCredit|refundAIUsageCredit|getRewardPayload|createRewardAction|updateRewardAction|validateReward)\b' \
apps/webLength of output: 162702 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- Prisma Project fields and table mapping ---'
fd -t f -a 'schema.prisma' . -x sh -c '
echo "### $1"
rg -n -A130 -B8 "^model Project\\b|\\b(aiUsage|aiLimit)\\b|@@map" "$1" | head -260
' sh {}
echo '--- Reward-condition schema and event catalog ---'
sed -n '1,430p' apps/web/lib/zod/schemas/rewards.ts | cat -n
echo '--- AI reward schema test ---'
cat -n apps/web/tests/misc/ai-reward-schema.test.ts
echo '--- Current UI and schema diff ---'
git diff --no-ext-diff --unified=40 origin/main...HEAD -- \
apps/web/lib/zod/schemas/rewards.ts \
apps/web/ui/partners/rewards/ai-reward-builder.tsx \
apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx \
apps/web/ui/partners/rewards/reward-presets.ts \
apps/web/tests/misc/ai-reward-schema.test.ts
echo '--- Portable call-site and error-flow search ---'
rg -n -C 5 -g '*.ts' -g '*.tsx' \
'\b(generateReward|reserveAIUsageCredit|refundAIUsageCredit|getAIRewardSchema|rewardConditionsArraySchema)\b' \
apps/web
echo '--- Raw Project SQL conventions ---'
rg -n -C 4 -g '*.ts' -g '*.tsx' \
'(\$executeRaw|UPDATE[[:space:]]+Project|aiUsage|aiLimit)' \
apps packagesLength of output: 180095
I found no blocking breaking changes or security issues. The AI reward action verifies authentication, workspace membership, user role, plan capability, rate limits, and AI usage limits. The AI usage reservation uses an atomic conditional update. Concurrent requests cannot exceed the persisted The streamed output receives event-specific schema validation. The existing reward create and update actions apply their server-side validation before persistence. The client conversion correctly converts flat reward amounts and currency condition values from dollars to cents. The new schema tests cover the reward-condition catalog, metadata requirements, percentage limits, and click/lead constraints. I did not run runtime tests in this read-only review environment. 🧠 Learnings usedYou are interacting with an AI system. |
Summary by CodeRabbit