From 102813d6ba6df58dd91d546be5edf8234a6ea600 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 04:43:32 +0000 Subject: [PATCH] docs(spec): TenantPlanSchema doc block states the entitlement-layer fold, not normalization (#9345) TenantPlanSchema's doc block claimed unrecognized plan codes fold to free via "the cloud distribution's normalization." That was wrong on the mechanism and the citation as of the cloud#1380 ruling (2026-08-16), landed in cloud PR #1417 (merged 2026-08-17): - The fold happens at the entitlement layer (isFreePlan), not in normalization -- sys_environment.plan keeps the raw value (case- normalized only), so an unrecognized tier stays distinguishable from the free tier to any reader, log line, or operator (cloud#1389's red line: normalize the spelling, never the vocabulary). - Before the ruling, only the control-plane planKey reader folded unknown to free; the tenant-runtime isFreePlan reader granted paid access. As of cloud PR #1417 both mirrors fold. - The two mirrors' vocabularies are not merged into one list (cloud#1380 lands over a pinned copy; unifying them is cloud#1418, ruled but not landed, and a SHA-pinned image can predate a vocabulary entry even after that lands). - Carries the ruling's operational premise -- new tiers are minted rarely, images roll before a new tier goes on sale -- consistent with cloud's isFreePlan docstring. Doc-block prose only. No schema, validation, or behaviour change -- TenantPlanSchema still accepts any string; acceptance is byte-identical. Fixes #9345 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Fs18A2DdXLVN2h8PaaFBcP --- .../tenant-plan-docblock-entitlement-fold.md | 31 +++++++++++++++++++ packages/spec/src/cloud/tenant.zod.ts | 26 +++++++++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 .changeset/tenant-plan-docblock-entitlement-fold.md diff --git a/.changeset/tenant-plan-docblock-entitlement-fold.md b/.changeset/tenant-plan-docblock-entitlement-fold.md new file mode 100644 index 0000000000..f9d6484beb --- /dev/null +++ b/.changeset/tenant-plan-docblock-entitlement-fold.md @@ -0,0 +1,31 @@ +--- +"@objectstack/spec": patch +--- + +docs(spec): `TenantPlanSchema` doc block states the entitlement-layer fold, not normalization (#9345) + +`TenantPlanSchema`'s doc block claimed that an unrecognized plan code is folded +to the free tier by "the cloud distribution's normalization." That was +measured wrong on two counts as of the cloud#1380 ruling (2026-08-16, landed +in cloud PR #1417, merged 2026-08-17): + +- The fold happens at the **entitlement layer** (e.g. `isFreePlan`), never in + normalization — `sys_environment.plan` keeps the raw value (case-normalized + only), so an unrecognized tier stays distinguishable from the free tier to + any reader, log line, or operator. Writing it as normalization is exactly + what cloud#1389's red line forbids: normalize the spelling, never the + vocabulary. +- Before the ruling landed, only the control-plane `planKey` reader folded + unknown codes to free; the tenant-runtime `isFreePlan` reader granted paid + access to an unrecognized code. As of cloud PR #1417 both mirrors fold. + +The corrected doc block also states, explicitly, what it must not say: the two +mirrors' vocabularies are not merged into one list (cloud#1380 lands a +pinned *copy*; unifying them is cloud#1418, ruled but not yet landed, and a +SHA-pinned image can predate a vocabulary entry even after that lands), and +it carries the ruling's operational premise (new plan tiers are minted +rarely, images roll before a new tier goes on sale) so the spec text does not +contradict cloud's `isFreePlan` docstring, which states the same premise. + +Doc-block prose only — `TenantPlanSchema` still accepts any string and +enforces no vocabulary; acceptance behavior is unchanged. diff --git a/packages/spec/src/cloud/tenant.zod.ts b/packages/spec/src/cloud/tenant.zod.ts index 5c6e0e7758..57153b5cdb 100644 --- a/packages/spec/src/cloud/tenant.zod.ts +++ b/packages/spec/src/cloud/tenant.zod.ts @@ -44,10 +44,28 @@ export type TenantDatabaseStatus = z.input; * `planAllowsAiStudio`) interpret specific values, and they own that * interpretation independently of this schema. * - * Convention (not enforced here): an empty or unrecognized value is treated - * as the free tier by cloud-side readers. Spec accepts any string, including - * the empty one — the free-tier fallback is the cloud distribution's - * normalization, not a spec-level default. + * Convention (not enforced here): as of the cloud#1380 ruling (2026-08-16, + * landed in cloud PR #1417, merged 2026-08-17), an empty or unrecognized + * value folds to the free tier on **both** cloud-side mirrors (the + * control-plane `planKey` reader and the tenant-runtime `isFreePlan` + * reader). The fold happens at the **entitlement layer**, not in + * normalization: `sys_environment.plan` keeps the raw value (case-normalized + * only), so an unrecognized tier stays distinguishable from the free tier to + * any reader, log line, or operator — cloud#1389's red line is normalize the + * spelling, never the vocabulary. Spec accepts any string, including the + * empty one — the free-tier fallback is a cloud-side entitlement decision, + * not a spec-level default. + * + * The two mirrors' vocabularies are **not** merged into one list: cloud#1380 + * lands A over a copy of the vocabulary, pinned to the source by an + * element-for-element equality guard. Unifying them is cloud#1418 (ruled, + * not yet landed) — and even once it lands, a SHA-pinned image can still + * predate a vocabulary entry, so the two lists can disagree either way. + * + * The fold's premise is operational, not structural: new plan tiers are + * minted rarely, and images roll before a new tier goes on sale. If that + * discipline changes, this premise changes with it (see the cloud + * distribution's `isFreePlan` docstring, which carries the same premise). */ export const TenantPlanSchema = lazySchema(() => z.string().describe( 'Opaque plan/tier identifier. The vocabulary is control-plane config owned by the '