Skip to content

feat(sdk): Hybrid Link tier transitions (resolution, merge, candidates) - #304

Merged
purpshell merged 5 commits into
devfrom
t3code/hybrid-tier-transitions
Oct 2, 2026
Merged

purpshell merged 5 commits into
devfrom
t3code/hybrid-tier-transitions

Conversation

@purpshell

@purpshell purpshell commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Adds TypeScript SDK support for Hybrid Link tier transitions: resolving a Hybrid Link Number that leaves Pro, merging a same-number pair on an upgrade to Pro, and listing merge candidates.

  • Client.sessions.quoteTierChange accepts hybridResolution (discriminated keep / split) or hybridMerge (absorbNumberId). NumberTierQuoteRequest makes them mutually exclusive at the type level. The method also rejects both at runtime, before any request, with PolymorfaValidationError.
  • NumberTierChange.quote.hybridTransition is a union discriminated on action (keep / split / merge), with survivingNumberId, status (scheduled | running | completed | failed | cancelled), failureReason, newNumberId, metaDisconnectRequired and effectiveAtMs.
  • New Client.projects.listHybridMergeCandidates(projectId) for GET /platform/projects/{projectId}/hybrid-merge-candidates (ledger clientPath projects.listHybridMergeCandidates). It returns HybridMergeCandidate[].
  • POLYMORFA_ERROR_CODES adds hybrid_choice_required and hybrid_transition_ineligible. Both are 409 responses, so they surface as PolymorfaConflictError.
  • New exported types: HybridTransport, HybridResolution, HybridKeepResolution, HybridSplitResolution, HybridMerge, NumberHybridTransition (+ keep/split/merge variants), NumberHybridTransitionStatus, HybridMergeCandidate, HybridMergeCandidateNumber, HybridMergeIneligibleReason.

Contract pin

This branch merges SDK dev through origin/dev as of 2026-10-02, which pins API dev e818ba6. SDK dev already types campaign send windows, so this branch dropped its own duplicate sendWindow fields in favour of dev's.

Both snapshots are byte-identical to polymorfa/polymorfa@fcf7b21015dd185e987b5aa3580e8af5f4c7f1a4 on branch t3code/hybrid-pro-downgrade (API PR #404). That commit is not merged to API dev, so source.json records published: false. The Messaging snapshot is byte-identical to SDK dev's pin.

Platform changes against SDK dev:

  • The Hybrid Link contract, unchanged since 4be41e2b4.

  • Four reviewed fingerprint changes: quoteSessionTierChange, getSessionTierChange, setSessionTierOverride, and the excluded Console updateProject.

  • Six operations from API dev (API PR #428, hosted Flow endpoints):

    • GET /platform/flows/{flowId}/endpoint
    • PUT /platform/flows/{flowId}/endpoint
    • DELETE /platform/flows/{flowId}/endpoint
    • GET /platform/flows/{flowId}/endpoint/receipts
    • GET /platform/flow-encryption-keys
    • POST /platform/flow-encryption-keys/rotate

    No open SDK PR covers these. The ledger excludes them with that reason; they need a separate SDK change.

Strict ledger: 557 total, 421 covered, 136 excluded, 0 missing, 0 partial, 0 changed. Repin to the merged API dev commit before merging this PR.

Contract changes since the first revision:

  • d831bf9c6: each candidate Number has a required canBeAbsorbed, false with hosted message storage, and that Number must be kept. ineligibleReason adds transition_in_progress, pairing_in_progress and hms_enabled. The candidate list returns 403 feature_unavailable without live Hybrid Link access, which surfaces as PolymorfaAuthorizationError.
  • 4be41e2b4: the missing-choice code is hybrid_choice_required, the code API PR #381 already ships, replacing hybrid_resolution_required. A queued change that is rejected for that reason reports status: "rejected" with failureReason: "hybrid_choice_required"; this is documented on the type, in the README and in a test. The candidate operation's description now states the 403 and the survivor rule.

Contract notes / ambiguities

  • The two new codes are documented only in operation prose, not in the PlatformAccessPublicError/PublicError enums. The error-code contract test counts them as "documented in prose", the same way it handles payg_required/addon_required.
  • hybrid_transition_ineligible carries its reason only in the message (Hybrid Link change unavailable: <reason>). The error envelope has no structured reason field, so the SDK does not parse it.
  • The candidate ineligibleReason enum (deletion_in_progress, transition_in_progress, pairing_in_progress, hms_enabled, not_coexistence, different_customer, connection_disabled, not_connected) differs from the design's list of quote-time reasons. The SDK types follow the schema.
  • The spec places transition status at quote.hybridTransition.status. The design doc says NumberTierChange.hybridTransition.status. The SDK follows the spec and the implementation. The spec also adds cancelled, which the design does not list.
  • NumberHybridTransition in the schema is one flat object. The SDK narrows it to an action union that matches the API's hybridPlanView.

Six-part assessment

Part Status Evidence
API Companion, unmerged polymorfa/polymorfa#404 @ fcf7b21 owns handlers, authority, and the source spec.
SDKs Updated TypeScript server SDK types, methods, error codes, snapshot, ledger, and tests. The browser package is unaffected: tier changes and project reads are organization-credential Platform routes. No other language SDK lives in this repo.
CLI Updated in companion polymorfa/cli#35. It adds session tier quote/confirm/status and session hybrid-merge-candidates, and pins CI to this SDK commit (6271f47) until publication.
Docs Updated Root README resource list, packages/typescript/README.md "Hybrid Link Numbers", contracts/README.md pin notes, and a CHANGELOG entry for the tier transitions. Public Mintlify docs belong to the API PR.
Feature releases Unaffected here The SDK changes no flag, enrollment, or availability. The API gates merge on messaging.hybrid-link and keeps the downgrade guard ungated.
Admin controls Unaffected here The SDK has no staff surface. The API change extends the read-only admin-hybrid-link view.

Verification (Node 24.15, at 6271f47)

  • npm run typecheck, npm run build:workspaces: pass
  • npm test with --testTimeout=60000 (the host was heavily loaded): 131 files, 1445 tests pass
  • npm run check:coverage (strict), check:functions, check:names, format:check: pass
  • npm run lint: 0 errors, 2 existing warnings in errors.ts

No live API, deployment, or package publication was exercised. Publishing is required before the CLI can move from the source pin to a registry pin.

Type hybridResolution (keep/split) and hybridMerge on tier quotes,
quote.hybridTransition with its status, Client.projects.listHybridMergeCandidates,
and the hybrid_resolution_required / hybrid_transition_ineligible codes.
Pin the Platform snapshot and ledger to API commit feaf0734a.
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 3d742f30-518b-4afd-89e1-e5ca4a91adac

📥 Commits

Reviewing files that changed from the base of the PR and between aa47e7e and 71a7d2f.

📒 Files selected for processing (16)
  • CHANGELOG.md
  • README.md
  • contracts/README.md
  • contracts/coverage.json
  • contracts/openapi.platform.json
  • contracts/source.json
  • packages/typescript/README.md
  • packages/typescript/src/errors.ts
  • packages/typescript/src/index.ts
  • packages/typescript/src/platform/projects.ts
  • packages/typescript/src/platform/sessions.ts
  • packages/typescript/src/platform/types.ts
  • packages/typescript/test/coverage-reconciliation.test.ts
  • packages/typescript/test/coverage.test.ts
  • packages/typescript/test/error-body.test.ts
  • packages/typescript/test/hybrid-tier-transitions.test.ts
 ______________________________
< torvalds@linux:~$ git review >
 ------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@purpshell

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Candidate Numbers report canBeAbsorbed, ineligibleReason gains
transition_in_progress, pairing_in_progress and hms_enabled, and the list
returns 403 feature_unavailable without Hybrid Link access. Cloud credential
operations carried in from API dev are excluded until SDK PR #308 merges.
The missing-choice code is hybrid_choice_required, which a rejected queued
change also reports as its failureReason. The carried API dev revision adds
campaign send windows, typed here, and Official API template, service-window
and pricing reads excluded until SDK PR #308 merges.
SDK dev already types campaign send windows, so the duplicate fields from
this branch are dropped in favour of dev's. The hosted Flow endpoint and Flow
encryption key operations carried from API dev (PR #428) have no SDK owner
yet and are excluded.
@purpshell
purpshell marked this pull request as ready for review October 2, 2026 01:56
@purpshell
purpshell merged commit 6d50b43 into dev Oct 2, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant