Uh oh!
There was an error while loading. Please reload this page.
fix(client): derive invitation status from the spec's InvitationStatus union - #8076
Conversation
…tus union (#7781) `organizations.invitations.list()` hand-wrote its row `status` as 'pending' | 'accepted' | 'rejected' | 'canceled' — missing `expired`, ObjectStack's own terminal state driven by `expiresAt`. `listMine()` was worse: a bare `string`. Both are two more hand-copied spellings of the vocabulary InvitationStatus (@objectstack/spec/identity) already owns, same divergence family as #7726. Both methods now type `status: InvitationStatus`, imported from the spec, so a future value added to the enum reaches the SDK by construction instead of silently diverging again. Added a type-level + runtime pin (invitation-status-vocabulary.test.ts) that fails to compile if either method is ever re-literalized — reverse-verified by temporarily re-literalizing list()'s status back to the old 4-value union and confirming `pnpm --filter @objectstack/client typecheck` fails with a type error in the pin file, then restoring the fix and confirming green again. Types-only, no wire change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B3Kurx8qufrDzNjk4rag7V
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 11 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also reference the affected code. These are read-only:
|
hotlong
commented
Aug 12, 2026
PM review — |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#7781
What
organizations.invitations.list()'s rowstatuswas hand-written as'pending' | 'accepted' | 'rejected' | 'canceled'— missingexpired,ObjectStack's own terminal state driven by
expiresAt.listMine()was worse:a bare
string. Both are two more hand-copied spellings of the vocabularyInvitationStatus(packages/spec/src/identity/organization.zod.ts) alreadyowns — same divergence family as #7726, which had already widened the spec
side to five values (adding
canceled) while this file stayed at four anddrifted the other way.
Per the ruling on the issue: derive, don't extend. Both methods now type
status: InvitationStatus, imported from@objectstack/spec/identity(
@objectstack/clientalready depends on@objectstack/specand alreadyimports several other types from it, e.g.
ApprovalStatus), so a future valueadded to the spec enum reaches the SDK by construction instead of silently
diverging again.
Both directions checked
The card only reported
expiredmissing. I also checked the other direction —whether every value the SDK's old hand-written literal listed is a real spec
value. All four (
pending,accepted,rejected,canceled) exist in thespec enum, so there's no SDK-only dead surface to report.
The pin
packages/client/src/invitation-status-vocabulary.test.ts:Assert<Eq<...>>, same pattern aspackages/spec/src/api/meta-item-response-shapes.test.ts) asserting eachmethod's declared row
statustype is exactlyInvitationStatus— not aliteral that merely lists the same values today. Read by
tscviatsconfig.test.json(pnpm --filter @objectstack/client typecheck);vitestdoes not type-check.plus a check that the SDK's old four values are all real spec values (the
"other direction" above), and a refusal check for a near-miss spelling.
Reverse-verification, both directions:
Removed
expiredfrom the spec enum, rebuilt@objectstack/spec, ranpnpm --filter @objectstack/client typecheck— stayed green. This isexpected, not a gap: since the fix derives the client's type directly from
InvitationStatusrather than restating it, the two are the same type byconstruction and cannot diverge from a spec-side change alone — that's the
fix working as intended. Restored the spec file and rebuilt.
The regression this pin actually guards against is someone reverting the
derivation. Verified that directly: temporarily re-literalized
list()'sstatusback to the old hand-written 4-value union (leaving the pin testuntouched) and re-ran typecheck:
Restored the fix and confirmed green again (
check:test-typecheck: OK — 0 file(s) / 0 error(s)).Types-only, no wire change — the value already arrived off the wire regardless
of what the annotation said. Changeset included.
Tests
pnpm --filter @objectstack/client typecheck— clean (tsc --noEmit+check:test-typecheck, 0 debt entries).pnpm --filter @objectstack/client test -- --maxWorkers=2— 22 test files /287 tests passed, including the 3 new pin tests.
node scripts/check-nul-bytes.mjs— OK.Generated by Claude Code