mirror of
https://github.com/Comfy-Org/comfy-typescript-sdk.git
synced 2026-11-04 23:47:27 +00:00
[PR #167] [MERGED] fix(parity): track the Python SDK's refactors, and retry the sdk-parity fetch #167
Labels
No labels
audit:ai-readiness
audit:community
audit:github
audit:health-scoring
audit:policy
audit:release
audit:supply-chain
bug
cursor-review-pin
effort:medium
effort:small
effort:trivial
priority:nice-to-have
priority:recommended
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/comfy-typescript-sdk#167
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/comfy-typescript-sdk/pull/167
Author: @mattmillerai
Created: 9/20/2026
Status: ✅ Merged
Merged: 10/9/2026
Merged by: @mattmillerai
Base:
main← Head:matt/sdk-parity-track-python-refactors📝 Commits (2)
c1fc9c2fix(parity): track the Python SDK's refactors instead of reading them as driftfb4d665fix(ci): retry the sdk-parity fetch so a network blip is not read as drift📊 Changes
6 files changed (+457 additions, -56 deletions)
View changed files
📝
AGENTS.md(+26 -16)📝
parity/python-surface.json(+17 -3)📝
scripts/python-surface.mjs(+81 -11)📝
scripts/sync-python-surface.mjs(+65 -10)📝
src/sdk/surface-parity.test.ts(+171 -16)➕
src/sdk/sync-python-surface.test.ts(+97 -0)📄 Description
ELI-5
The
sdk-parityjob checks that this SDK and the Python SDK still look alike. It does that by reading the Python SDK's source over the network and comparing names.It has been red for four runs, and neither cause was a real difference between the two SDKs:
Both are fixed here: the reader now understands the new arrangement, and the download retries before giving up. Along the way the refreshed comparison surfaced three real differences, which are now written down instead of being invisible.
What was actually broken
1.
RouterRunResultwas read as a third namespacemodels.pygrewRouterRunResult— the envelopeModels.run_detailedreturns.extractModelsMethods's guard exists to catch a third namespace class appearing, which a result record is not, so it failed hard.Adding it to
PYTHON_MODELS_CLASSESwould have been wrong (it has no methods, so the extractor's own "yielded zero public methods" guard would fire). Data classes are recognized separately now, viaPYTHON_MODELS_DATA_CLASSES, with a rot guard so an entry the Python module no longer defines fails rather than lingering.2. The extractor couldn't see re-exports — and the miss looked like drift
exceptions.pystopped definingComfyError,Forbidden,InsufficientCreditsandUnauthorized, and started re-exporting them from_errors/router_exceptions. That is deliberate on the Python side — it is how the two modules came to share one class object per name instead of two classes wearing one name.extractExportedErrorClassesonly readclassstatements, so it missed all four. The failure mode is the important part: it did not look broken, it looked like the Python root had dropped four error classes — the same shape a real divergence takes. The extractor now follows relative re-exports too.Three real differences this surfaced
Refreshing the snapshot made three names visible for the first time, so they go in the allowlist as what they are. Three new fields, pointing the opposite way to the existing LEAD fields:
run_detailedpythonOnlyModelsMethodsrunalready returns an envelope, so the disclosure rides the ordinary return value. A second method would be the same data behind a longer name.RouterErrorpythonOnlyExportedErrorClassesrouterErrors.RouterError. The guard proves it is really there.AlreadyCompleted,CancelRefusedexportedErrorClassesBehindPythonrouterErrors.That last row is a real gap worth a follow-up:
RequestHandle.cancelexists here, but its refusals surface untyped, so a caller cannot tell "already finished" from "refused" without reading a status. It is recorded rather than filtered so it stays visible, and its guard is the strictest of the three — it insists the name is missing from both addresses, so the entry can never decay into a general exemption.Also deletes
queue-error-buckets-land-first-in-typescript: its rot guard fired on the refreshed snapshot because the Python twin landed. That is the mechanism working, not a gap.The retry
Second commit, separable if you'd rather split it — kept together because both changes serve the same property: a red from
sdk-parityshould mean the sibling SDK moved.The 2026-09-20 run died on
TypeError: fetch failed/ECONNRESETfromraw.githubusercontent.com, which is indistinguishable from a real rename at a glance.fetchSourcenow makes four attempts over a widening backoff (under two seconds on a healthy run). A transport failure or a 5xx retries; a 4xx does not — that is a definite answer the caller needs promptly, and retrying a 404 just spends four attempts arriving at the same "the file moved" while making it look flaky.To test this rather than assert it,
fetchSourceis exported andawait main()is guarded so the module only self-runs as a CLI — importing it must not reach the network or rewrite the snapshot. CLI behavior is unchanged.Consumer impact
None. Nothing under
src/ships differently: the onlysrc/changes are a test file and a new test file. No export, type, or default changes. This is tooling, tests and docs, so per CONTRIBUTING.md there is no CHANGELOG entry.Test plan
Every gate the CI runs, locally, on Node 26:
pnpm lintpnpm format:checkpnpm typecheckpnpm check:spec-driftpnpm check:sdk-parity— now OK againstcomfy-python-sdk@mainpnpm test— 650 passed, 5 skipped (was 645; +5 new retry tests)pnpm buildnode scripts/check-public-repo-hygiene.mjsNew tests cover the happy path with no retry, riding out three transport blips, giving up after the fourth attempt, retrying a 503, and not retrying a 404 — the transport failure uses the real
undicishape (TypeError: fetch failedwith anECONNRESETcause).One note on process: the
router-errors-namespacedentry originally claimed all three ofRouterError,AlreadyCompletedandCancelRefusedwere namespaced. The new rot guard rejected that claim, which is how the lag in row three got found rather than papered over.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.