Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions atmn/src/compose/models/planModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const PlanItemSchema = z.object({
}),
tiers: z.array(UsageTierSchema).optional().meta({
description:
"Tiered pricing. Each tier's 'to' does NOT include included amount. Either 'amount' or 'tiers' is required.",
"Tiered pricing. Either 'amount' or 'tiers' is required.",
}),
tier_behavior: z.union([z.literal("graduated"), z.literal("volume")]).optional(),

Expand Down Expand Up @@ -245,7 +245,7 @@ type PriceWithAmount = PriceBaseFields & {
tiers?: never;
};

// Price with graduated tiered pricing (no flat amount per tier)
// Price with graduated tiered pricing (each tier's rate applies only to units within that tier)
type PriceWithGraduatedTiers = PriceBaseFields & {
/** Cannot have flat amount when using tiers */
amount?: never;
Expand All @@ -255,14 +255,19 @@ type PriceWithGraduatedTiers = PriceBaseFields & {
tierBehavior: "graduated";
};

// Price with volume tiered pricing (flat amount per tier)
// Volume tier: at least one of amount or flatAmount must be present
type VolumeTier =
| { to: number | "inf"; amount: number; flatAmount?: number }
| { to: number | "inf"; amount?: number; flatAmount: number };

@cubic-dev-ai cubic-dev-ai Bot Mar 4, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: The new VolumeTier type allows flatAmount-only tiers, but runtime Zod schema still requires amount, causing type-safe code to fail validation at runtime.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At atmn/src/compose/models/planModels.ts, line 261:

<comment>The new `VolumeTier` type allows `flatAmount`-only tiers, but runtime Zod schema still requires `amount`, causing type-safe code to fail validation at runtime.</comment>

<file context>
@@ -255,14 +255,19 @@ type PriceWithGraduatedTiers = PriceBaseFields & {
+// Volume tier: at least one of amount or flatAmount must be present
+type VolumeTier =
+  | { to: number | "inf"; amount: number; flatAmount?: number }
+  | { to: number | "inf"; amount?: number; flatAmount: number };
+
+// Price with volume tiered pricing (the tier the total usage falls into applies to all units)
</file context>
Fix with Cubic


// Price with volume tiered pricing (the tier the total usage falls into applies to all units)
type PriceWithVolumeTiers = Omit<PriceBaseFields, "billingMethod"> & {
/** Volume pricing does not support usage_based billing β€” use 'prepaid' */
billingMethod: Exclude<BillingMethod, "usage_based">;
/** Cannot have flat amount when using tiers */
amount?: never;
/** Volume tiered pricing: the tier the total usage falls into applies to all units */
tiers: Array<{ to: number | "inf"; amount: number; flatAmount?: number }>;
tiers: Array<VolumeTier>;
/** Volume: the rate of the tier the total usage falls into applies to all units */
tierBehavior: "volume";
};
Expand Down
1 change: 1 addition & 0 deletions typegen/genUtils/TypeGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ ${imports}
OnDecrease: ['prorate', 'refund_immediately', 'no_action'],
FreeTrialDuration: ['day', 'month', 'year'],
TierBehaviours: ['graduated', 'volume'],
TierBehavior: ['graduated', 'volume'],
ApiFeatureType: ['static', 'boolean', 'single_use', 'continuous_use', 'credit_system'],
FeatureType: ['boolean', 'metered', 'credit_system'],
};
Expand Down
29 changes: 24 additions & 5 deletions typegen/genUtils/atmnTypeHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,35 @@ type PriceWithAmount = PriceBaseFields & {
tiers?: never;
};

// Price with tiered pricing (no flat amount)
type PriceWithTiers = PriceBaseFields & {
// Price with graduated tiered pricing (each tier's rate applies only to units within that tier)
type PriceWithGraduatedTiers = PriceBaseFields & {
/** Cannot have flat amount when using tiers */
amount?: never;
/** Tiered pricing structure based on usage ranges */
/** Graduated tiered pricing: each tier's amount applies only to units within that tier */
tiers: Array<{ to: number | "inf"; amount: number }>;
/** Required when tiers is defined: how tiers are applied */
tierBehaviour: "graduated" | "volume";
/** Graduated: each tier's rate applies only to usage within that tier */
tierBehavior: "graduated";
};

// Volume tier: at least one of amount or flatAmount must be present
type VolumeTier =
| { to: number | "inf"; amount: number; flatAmount?: number }
| { to: number | "inf"; amount?: number; flatAmount: number };
Comment on lines +248 to +250

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The VolumeTier type template (lines 248–250) correctly enforces that at least one of amount or flatAmount must be present. However, the Zod schema that backs runtime validation (UsageTierSchema in planModels.ts lines 7–10) does not reflect this constraint β€” it requires amount unconditionally and lacks flatAmount entirely.

Since atmnTypeHelpers.ts is the source template for the TypeScript type portion of planModels.ts, the corresponding Zod schema in the generator (TypeGenerator.ts line 334) must be updated in parallel to keep types and runtime validation in sync. Without this, consumers will write TypeScript code that passes the type checker but fails at runtime.

See the companion issue in TypeGenerator.ts line 334 for the schema update needed.

Prompt To Fix With AI
This is a comment left during a code review.
Path: typegen/genUtils/atmnTypeHelpers.ts
Line: 248-250

Comment:
The `VolumeTier` type template (lines 248–250) correctly enforces that at least one of `amount` or `flatAmount` must be present. However, the Zod schema that backs runtime validation (`UsageTierSchema` in `planModels.ts` lines 7–10) does not reflect this constraint β€” it requires `amount` unconditionally and lacks `flatAmount` entirely.

Since `atmnTypeHelpers.ts` is the source template for the TypeScript type portion of `planModels.ts`, the corresponding Zod schema in the generator (`TypeGenerator.ts` line 334) must be updated in parallel to keep types and runtime validation in sync. Without this, consumers will write TypeScript code that passes the type checker but fails at runtime.

See the companion issue in `TypeGenerator.ts` line 334 for the schema update needed.

How can I resolve this? If you propose a fix, please make it concise.


// Price with volume tiered pricing (the tier the total usage falls into applies to all units)
type PriceWithVolumeTiers = Omit<PriceBaseFields, "billingMethod"> & {
/** Volume pricing does not support usage_based billing β€” use 'prepaid' */
billingMethod: Exclude<BillingMethod, "usage_based">;
/** Cannot have flat amount when using tiers */
amount?: never;
/** Volume tiered pricing: the tier the total usage falls into applies to all units */
tiers: Array<VolumeTier>;
/** Volume: the rate of the tier the total usage falls into applies to all units */
tierBehavior: "volume";
};

type PriceWithTiers = PriceWithGraduatedTiers | PriceWithVolumeTiers;

// Price must have either amount OR tiers (not both, not neither)
type PriceAmountOrTiers = PriceWithAmount | PriceWithTiers;

Expand Down