You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TenantPlanSchema's doc block claims cloud-side readers fold unknown plan codes to free — that became true only on 2026-08-17, and the citation is wrong #9345
Filed by the repo:cloud whole-repo PM seat (objectstack#6026, session session_0137TnZzVmkSjXxoSVgPFS6S) as item 3 of the maintainer ruling on cloud#1380 (2026-08-16), which assigns the filing to the cloud seat and the work to the spec seat — packages/spec is the spec seat's surface, so this card is filed unassigned rather than claimed here.
Spec correction (same round, cross-repo): objectstack's TenantPlanSchema doc block (#7513) is corrected so its "cloud-side readers fold unknown to free" sentence becomes measured truth with an accurate citation — the cloud seat files the objectstack card (spec text surface) with Blocked-by: this card's A landing.
Blocked-by
objectstack-ai/cloud#1380 — specifically its item 1 ("A"), landing as objectstack-ai/cloud PR #1417. ⛔ Do not correct the sentence before that PR merges: until it does, the claim is false on the tenant-runtime side and a "correction" would make the doc block wrong in the opposite direction.
What is wrong today
TenantPlanSchema's doc block asserts that cloud-side readers fold an unrecognized plan code onto free. As of the ruling, that was true of one of the two mirrors:
reader
before 2026-08-17
after cloud#1380 A
service-cloudplanKey (control plane)
folds unknown → free
unchanged
objectos-runtimeisFreePlan (tenant runtime)
granted PAID for unknown
folds unknown → free
So the sentence described the control plane and generalized it to "cloud-side readers". The disagreement between those two mirrors is the entire subject of cloud#1380 — a spec doc block asserting they agreed is what let it go unnoticed.
The citation attached to the sentence is also inaccurate; the correction should point at the ruling and at the code that actually implements the fold, not at the earlier reference.
What the corrected text has to say
Unrecognized codes fold to free on both mirrors, as of the cloud#1380 ruling (2026-08-16) — with the accurate citation.
The fold is at the entitlement layer, not in normalization. sys_environment.plan keeps the raw value modulo case, so "a tier I do not know" stays distinguishable from "the free tier" for any reader, log line, or operator. (Sinking it into normalization would have erased that distinction before anything could observe it — the reason cloud#1389 / PR chore: version packages #1393 states normalize the spelling, never the vocabulary.)
The ruling's operational corollary, because it is the premise the fold rests on: new tiers are minted rarely, and images roll before a new tier is sold. If that discipline changes, this ruling's premise changes with it. It now lives in isFreePlan's docstring on the cloud side; the spec text should not contradict it.
⚠️ Do not write the sentence as if the two vocabularies are now one list. They are not — A landed over a copy, guarded by an element-for-element equality pin. Unifying them is objectstack-ai/cloud#1418 (ruled, not yet done), and a SHA-pinned image can predate a vocabulary entry even after that lands.
Scope
packages/spec text surface only — a doc block correction, no schema or behaviour change. ⛔ This card carries no authority to change what TenantPlanSchema validates.
Related
objectstack-ai/cloud#1380 (the ruling and A) · objectstack-ai/cloud PR #1417 (A as landed) · objectstack-ai/cloud#1418 (item 2, the shared vocabulary) · objectstack-ai/cloud#1389 (the spelling red line) · objectstack#7513.
Triage: lands in packages/spec (TenantPlanSchema doc block) — pm:blocked + domain:spec, type Task. Text face: acceptance is byte-identical, prose-only correction.
Blocked-by: objectstack-ai/cloud#1380
Verified at triage time: cloud PR #1417 (ruling item A) is an open draft, not merged — the card's ⛔ stands; correcting the sentence today would make it false in the opposite direction. Unlock: when cloud#1417 merges, re-verify this card's surface on the merged ref, then queue.
Dispatch note for the spec seat: the diff will touch packages/spec/src/**, so the clause-② enqueue gate will flag it by path even though this is doc-block-only — either dispatch at the contract-review tier or plan for the needs:contract-review loop before enqueue.
This comment is from the triage seat Routine — session session_01Xusz85Crfxkq8sWGHjtby6.
Unlock scan: upstream cloud PR #1417 is MERGED (2026-08-17 16:01Z), so the Blocked-by is discharged — pm:blocked → pm:queue. Premise re-verified on origin/main before returning to queue: packages/spec/src/cloud/tenant.zod.ts:47-50 still attributes the free-tier fold to "the cloud distribution's normalization" — which is now doubly wrong: the fold landed at the entitlement layer precisely because folding in normalization was ruled out (cloud#1389: normalize the spelling, never the vocabulary). The card's three content requirements stand as written; note for the spec seat: cloud#1418 (shared vocabulary) is ruled but NOT yet landed, so the corrected text must not describe the two vocabularies as one list.
Claim: session session_01Fs18A2DdXLVN2h8PaaFBcP (the domain:spec execution seat, #6017), branch claude/issue-9345-tenantplan-docblock. Text face (acceptance byte-identical, prose-only) — dispatched at sonnet per the #9361 precedent; the clause-② enqueue gate applies mechanically by path at ACCEPT (needs:contract-review on, triage clears, then landing). Content contract: the three requirements in the card plus the unlock comment's three do-not-write traps (fold is at the ENTITLEMENT layer, never "normalization" — cloud#1389 red line; the two vocabularies are NOT merged — cloud#1418 ruled-not-landed; carry the operational premise sentence consistent with cloud's isFreePlan docstring). Generated-artifacts coupling declared: wave-8 siblings #9463/#9406/#9447 — landings serialize, later landers run os-regen-merge.
{
"issue": 9345,
"outcome": "delivered",
"pr": 9492,
"branch": "claude/issue-9345-tenantplan-docblock",
"head": "102813d6ba6df58dd91d546be5edf8234a6ea600",
"before_after": "packages/spec/src/cloud/tenant.zod.ts:47-50 rewritten: the sentence attributing the free-tier fold to 'the cloud distribution's normalization' is replaced with a paragraph stating the fold is at the entitlement layer (isFreePlan), citing cloud#1380 (ruled 2026-08-16) and cloud PR #1417 (merged 2026-08-17), stating both mirrors now fold, and adding two new paragraphs: the two vocabularies stay unmerged (cloud#1418 ruled-not-landed, SHA-pinned mirrors can predate an entry), and the operational premise (new tiers minted rarely; images roll before a new tier sells) consistent with cloud's isFreePlan docstring. Doc-block prose only; TenantPlanSchema still accepts any string, acceptance byte-identical.",
"traps_checked": [
"1. never wrote 'normalized to free' -- attributed the fold explicitly to the entitlement layer / isFreePlan, stated sys_environment.plan keeps the raw value (case-normalized only), cited cloud#1389's red line by name",
"2. never described the two vocabularies as merged/one list -- stated cloud#1380 lands over a pinned copy, cloud#1418 is ruled but not landed, and a SHA-pinned mirror can predate a vocabulary entry even after #1418 lands",
"3. carried the operational premise (new tiers minted rarely; images roll before a new tier goes on sale; premise changes if that discipline changes) worded to be consistent with cloud's isFreePlan docstring per the unlock comment's re-verified quote (measurement of record; this repo cannot read the cloud repo directly)"
],
"files": [
"packages/spec/src/cloud/tenant.zod.ts",
".changeset/tenant-plan-docblock-entitlement-fold.md"
],
"gates": [
"pnpm --filter @objectstack/spec build -- clean",
"pnpm --filter @objectstack/spec check:generated -- all 13 generated artifacts up to date (the JSDoc block is not read by build-docs.ts; only .describe() strings are, and those are unchanged)",
"pnpm --filter @objectstack/spec typecheck -- clean",
"pnpm --filter @objectstack/spec test -- 409 test files / 10915 tests passed, 0 failed",
"pnpm check:cross-package-test-inputs -- OK",
"pnpm --filter @objectstack/lint check:doc-formula-expressions -- OK (required building @objectstack/formula's dist first)",
"pnpm check:spec-parsed-alias -- OK",
"pnpm check:type-source-resolution -- OK",
"pnpm check:merge-driver -- OK",
"pnpm --filter @objectstack/spec check:empty-state -- OK",
"pnpm --filter @objectstack/spec check:variant-docs -- OK",
"node scripts/check-dev-prereqs.mjs --self-test -- OK (the CI-run form; lint.yml invokes only --self-test, never the full-workspace scan, per the script's own header)",
"node scripts/docs-audit/check-affected-docs.mjs -- OK (its wrapper is self-test-only by design; docs-drift-check.yml never installs pnpm)",
"grep -naP control-byte scan over changed files -- clean"
],
"deviations": [
"check:strictness-ledger and check:liveness are covered inside check:generated's run (both reported green there) rather than invoked as separate commands.",
"check:dev-prereqs was run in its --self-test form rather than the full existence scan: the full scan needs the entire workspace built (65+ packages), which this doc-block-only change does not warrant, and CI itself (lint.yml) only ever runs --self-test for this gate -- confirmed by reading the workflow file."
]
}
ACCEPT — review of record against PR #9492 (head 102813d6b), full diff read (2 files: the doc-block rewrite + a patch changeset).
All three do-not-write traps verified in the landed prose, each by name: (1) the fold is attributed to the entitlement layer with sys_environment.plan keeping the raw value and cloud#1389's red line quoted (normalize the spelling, never the vocabulary); (2) the two mirrors' vocabularies are stated as NOT merged — cloud#1380 lands over a pinned copy, cloud#1418 ruled-not-landed, and the SHA-pinned-image-predates-entry caveat carried; (3) the operational premise (tiers minted rarely, images roll before a tier sells, premise changes if that discipline changes) worded consistently with cloud's isFreePlan docstring. Bonus accuracy: the pre-ruling asymmetry (control-plane planKey folded while tenant-runtime isFreePlan granted paid) is recorded as history. TenantPlanSchema remains z.string() — acceptance byte-identical; check:generated 13/13 current; spec suite 10915 green; gate deviations (strictness/liveness inside check:generated; dev-prereqs --self-test = CI's own invocation) accepted as reported.
Clause-② label applied (sonnet dispatch touching packages/spec/src/**): needs:contract-review now on this card per the #9254/#9361 flow. ⛔ PR #9492 stays draft and does not enqueue until the triage seat's contract-tier review clears it; this seat lands after clearance + green CI, in the wave-8 serial-relay order.
needs:contract-review clearance — PASS (triage seat, session session_019gCKd9EZfQ6MbGTnMHHJvW, reviewing at CONTRACT_REVIEW_TIER = claude-fable-5 per the single-source constant on origin/main; not the dispatching seat).
Reviewed the contract increment of PR #9492: the diff touches only the JSDoc block above TenantPlanSchema in packages/spec/src/cloud/tenant.zod.ts plus a patch changeset — the z.string() schema expression and its describe() text are byte-identical, so the acceptance set is unchanged. Content checked against the card: the corrected prose states the fold on both mirrors as of cloud#1380/PR #1417, places it at the entitlement layer (not normalization, per cloud#1389's red line), keeps the two vocabularies explicitly un-merged (cloud#1418 pending), and carries the operational premise. Label cleared; the card may enqueue — remaining flow (ready flip, CI context refresh) is the spec seat's.
Activity
os-zhuang commentedon Aug 17, 2026
Triage: lands in
packages/spec(TenantPlanSchema doc block) —pm:blocked+domain:spec, type Task. Text face: acceptance is byte-identical, prose-only correction.Blocked-by: objectstack-ai/cloud#1380
Verified at triage time: cloud PR #1417 (ruling item A) is an open draft, not merged — the card's ⛔ stands; correcting the sentence today would make it false in the opposite direction. Unlock: when cloud#1417 merges, re-verify this card's surface on the merged ref, then queue.
Dispatch note for the spec seat: the diff will touch
packages/spec/src/**, so the clause-② enqueue gate will flag it by path even though this is doc-block-only — either dispatch at the contract-review tier or plan for theneeds:contract-reviewloop before enqueue.This comment is from the triage seat Routine — session
session_01Xusz85Crfxkq8sWGHjtby6.Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
Blocked-by:解除 —— objectstack-ai/cloud#1380 的 A 已落地objectstack-ai/cloud PR #1417 已合并,cloud#1380 已关。⇒ 本卡的前置条件满足,
TenantPlanSchema那句话现在可以被改成实况。落地形态,供改文案时引用(以合并后的分支复核过,非转引 PR 正文):
⇒ 两侧镜像现在都把未识别 plan 码折叠为 free。裁定日期 2026-08-16,裁定记录在 objectstack-ai/cloud#1380 的评论 id
5307572840。sys_environment.plan保留原值(仅大小写归一),所以「我不认识的档位」与「free 档」对任何读者、日志、运维仍然可区分。⛔ 不要写成「未识别码被归一化为 free」—— 那正是 cloud#1389 的红线所禁止的(归一化拼写,永不归一化词表),也是这个缺陷当初得以被测量和裁定的原因。isFreePlan的 docstring;spec 文本 ⛔ 不应与它矛盾。⛔ 本卡仍归 spec 座位(
packages/spec文本面),repo:cloud座位只负责立卡与解锁通知,不实施。Generated by Claude Code
os-zhuang commentedon Aug 18, 2026
Unlock scan: upstream cloud PR #1417 is MERGED (2026-08-17 16:01Z), so the Blocked-by is discharged —
pm:blocked→pm:queue. Premise re-verified onorigin/mainbefore returning to queue:packages/spec/src/cloud/tenant.zod.ts:47-50still attributes the free-tier fold to "the cloud distribution's normalization" — which is now doubly wrong: the fold landed at the entitlement layer precisely because folding in normalization was ruled out (cloud#1389: normalize the spelling, never the vocabulary). The card's three content requirements stand as written; note for the spec seat: cloud#1418 (shared vocabulary) is ruled but NOT yet landed, so the corrected text must not describe the two vocabularies as one list.Triage seat routine — session
session_pm_triage_20260818T0104Z.Generated by Claude Code
os-steve commentedon Aug 18, 2026
Claim: session
session_01Fs18A2DdXLVN2h8PaaFBcP(thedomain:specexecution seat, #6017), branchclaude/issue-9345-tenantplan-docblock. Text face (acceptance byte-identical, prose-only) — dispatched at sonnet per the #9361 precedent; the clause-② enqueue gate applies mechanically by path at ACCEPT (needs:contract-reviewon, triage clears, then landing). Content contract: the three requirements in the card plus the unlock comment's three do-not-write traps (fold is at the ENTITLEMENT layer, never "normalization" — cloud#1389 red line; the two vocabularies are NOT merged — cloud#1418 ruled-not-landed; carry the operational premise sentence consistent with cloud'sisFreePlandocstring). Generated-artifacts coupling declared: wave-8 siblings #9463/#9406/#9447 — landings serialize, later landers run os-regen-merge.Generated by Claude Code
docs(spec): TenantPlanSchema doc block states the entitlement-layer f…
os-steve commentedon Aug 18, 2026
os-dev-report
{ "issue": 9345, "outcome": "delivered", "pr": 9492, "branch": "claude/issue-9345-tenantplan-docblock", "head": "102813d6ba6df58dd91d546be5edf8234a6ea600", "before_after": "packages/spec/src/cloud/tenant.zod.ts:47-50 rewritten: the sentence attributing the free-tier fold to 'the cloud distribution's normalization' is replaced with a paragraph stating the fold is at the entitlement layer (isFreePlan), citing cloud#1380 (ruled 2026-08-16) and cloud PR #1417 (merged 2026-08-17), stating both mirrors now fold, and adding two new paragraphs: the two vocabularies stay unmerged (cloud#1418 ruled-not-landed, SHA-pinned mirrors can predate an entry), and the operational premise (new tiers minted rarely; images roll before a new tier sells) consistent with cloud's isFreePlan docstring. Doc-block prose only; TenantPlanSchema still accepts any string, acceptance byte-identical.", "traps_checked": [ "1. never wrote 'normalized to free' -- attributed the fold explicitly to the entitlement layer / isFreePlan, stated sys_environment.plan keeps the raw value (case-normalized only), cited cloud#1389's red line by name", "2. never described the two vocabularies as merged/one list -- stated cloud#1380 lands over a pinned copy, cloud#1418 is ruled but not landed, and a SHA-pinned mirror can predate a vocabulary entry even after #1418 lands", "3. carried the operational premise (new tiers minted rarely; images roll before a new tier goes on sale; premise changes if that discipline changes) worded to be consistent with cloud's isFreePlan docstring per the unlock comment's re-verified quote (measurement of record; this repo cannot read the cloud repo directly)" ], "files": [ "packages/spec/src/cloud/tenant.zod.ts", ".changeset/tenant-plan-docblock-entitlement-fold.md" ], "gates": [ "pnpm --filter @objectstack/spec build -- clean", "pnpm --filter @objectstack/spec check:generated -- all 13 generated artifacts up to date (the JSDoc block is not read by build-docs.ts; only .describe() strings are, and those are unchanged)", "pnpm --filter @objectstack/spec typecheck -- clean", "pnpm --filter @objectstack/spec test -- 409 test files / 10915 tests passed, 0 failed", "pnpm check:cross-package-test-inputs -- OK", "pnpm --filter @objectstack/lint check:doc-formula-expressions -- OK (required building @objectstack/formula's dist first)", "pnpm check:spec-parsed-alias -- OK", "pnpm check:type-source-resolution -- OK", "pnpm check:merge-driver -- OK", "pnpm --filter @objectstack/spec check:empty-state -- OK", "pnpm --filter @objectstack/spec check:variant-docs -- OK", "node scripts/check-dev-prereqs.mjs --self-test -- OK (the CI-run form; lint.yml invokes only --self-test, never the full-workspace scan, per the script's own header)", "node scripts/docs-audit/check-affected-docs.mjs -- OK (its wrapper is self-test-only by design; docs-drift-check.yml never installs pnpm)", "grep -naP control-byte scan over changed files -- clean" ], "deviations": [ "check:strictness-ledger and check:liveness are covered inside check:generated's run (both reported green there) rather than invoked as separate commands.", "check:dev-prereqs was run in its --self-test form rather than the full existence scan: the full scan needs the entire workspace built (65+ packages), which this doc-block-only change does not warrant, and CI itself (lint.yml) only ever runs --self-test for this gate -- confirmed by reading the workflow file." ] }Generated by Claude Code
os-steve commentedon Aug 18, 2026
ACCEPT — review of record against PR #9492 (head
102813d6b), full diff read (2 files: the doc-block rewrite + a patch changeset).All three do-not-write traps verified in the landed prose, each by name: (1) the fold is attributed to the entitlement layer with
sys_environment.plankeeping the raw value and cloud#1389's red line quoted (normalize the spelling, never the vocabulary); (2) the two mirrors' vocabularies are stated as NOT merged — cloud#1380 lands over a pinned copy, cloud#1418 ruled-not-landed, and the SHA-pinned-image-predates-entry caveat carried; (3) the operational premise (tiers minted rarely, images roll before a tier sells, premise changes if that discipline changes) worded consistently with cloud'sisFreePlandocstring. Bonus accuracy: the pre-ruling asymmetry (control-planeplanKeyfolded while tenant-runtimeisFreePlangranted paid) is recorded as history.TenantPlanSchemaremainsz.string()— acceptance byte-identical;check:generated13/13 current; spec suite 10915 green; gate deviations (strictness/liveness inside check:generated; dev-prereqs --self-test = CI's own invocation) accepted as reported.Clause-② label applied (sonnet dispatch touching
packages/spec/src/**):needs:contract-reviewnow on this card per the #9254/#9361 flow. ⛔ PR #9492 stays draft and does not enqueue until the triage seat's contract-tier review clears it; this seat lands after clearance + green CI, in the wave-8 serial-relay order.Generated by Claude Code
os-support-ai commentedon Aug 18, 2026
needs:contract-reviewclearance — PASS (triage seat, sessionsession_019gCKd9EZfQ6MbGTnMHHJvW, reviewing atCONTRACT_REVIEW_TIER=claude-fable-5per the single-source constant onorigin/main; not the dispatching seat).Reviewed the contract increment of PR #9492: the diff touches only the JSDoc block above
TenantPlanSchemainpackages/spec/src/cloud/tenant.zod.tsplus a patch changeset — thez.string()schema expression and itsdescribe()text are byte-identical, so the acceptance set is unchanged. Content checked against the card: the corrected prose states the fold on both mirrors as of cloud#1380/PR #1417, places it at the entitlement layer (not normalization, per cloud#1389's red line), keeps the two vocabularies explicitly un-merged (cloud#1418 pending), and carries the operational premise. Label cleared; the card may enqueue — remaining flow (ready flip, CI context refresh) is the spec seat's.Generated by Claude Code
docs(spec): TenantPlanSchema doc block states the entitlement-layer f…