Skip to content

[0.9.0] Season seats: set_aside_season_block, retrieve_season_block, release_season_block, inventoryMode - #13

Merged
pietechsolution merged 14 commits into
mainfrom
navin/season-block-0.10.0
Nov 11, 2026
Merged

pietechsolution merged 14 commits into
mainfrom
navin/season-block-0.10.0

Conversation

@pietechsolution

@pietechsolution pietechsolution commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Season seats for seatlayer (Python), part of the 0.9.0 release. One release, 0.9.0, carries season 2a (#12) and this PR (decided 10 Nov).

  • Versions: old 0.8.0 (published) → new 0.9.0 (with season-2a, Season 2a server operations (part A) #12). Nothing is published yet: PyPI stops at 0.8.0 (checked 10 Nov).
  • 0.8.0 has the 48 season operations from 0.7.0/0.8.0. It has none of the season-2a calls and none of these.
  • The branch is cut from season-2a-server so it builds on the season-2a methods. Its name still says 0.10.0, from before the decision: renaming a head branch would close the PR.
  • No version bump, tag or publish is in this PR. pyproject.toml and __version__ are untouched.

A webhook problem 0.9.0 ships fixed

webhooks.create(...) and webhooks.update(...) check names against Webhooks._EVENT_NAMES, and raise ValueError for any other.

  • 0.8.0's list has 8 names and no season name. On season-2a-server it is a hand list of 18, with only the three season.ticket.transfer_* season names, so season.plan.published and the rest are refused.
  • WebhookEventName types all of these names, and the API accepts all of them (KNOWN_EVENTS in the app repo's workers/api/src/webhooks.ts).
  • Here _EVENT_NAMES = frozenset(get_args(WebhookEventName)), with a test that every typed name is accepted.
  • Every name WebhookEventName types is one the API accepts (checked against the API's list: 58 of its 63; the 5 guest.* names aren't typed here yet).
  • If the season-2a PR fixes this first, this hunk falls away at the rebase.

What it adds

Built to docs/season-block-2026-11-08/contract.md in the app repo (§2, §5, §6, and §8 "Amendments, 9 Nov"). Method names are the operationIds, snake_cased (AGENTS.md).

  • Methods:

    • seasons.set_aside_season_block(season_key, expected_revision, keep_for_single_tickets=None, idempotency_key=None);
    • seasons.retrieve_season_block(season_key);
    • seasons.release_season_block(season_key, expected_revision, scope, event_keys=None, at=None, reason=None, idempotency_key=None).

    Both writes use header replay, so an Idempotency-Key is generated when none is given, as on refund_season_performance. Each write answers {"job": ..., "block": ...}; follow the job with retrieve_season_bulk_job.

  • inventory_mode ("block" | "per_hold") on create_season and update_season, sent as inventoryMode only when given. "block" opts the season into season seats; the default stays per_hold. Every Season answer carries inventoryMode.

  • Types: SeasonSeatSelection, SeasonBlockReleaseScope, SeasonDateLeavePending. The webhook names are season.block.ready and season.block.released.

  • The 202 while a date leaves every season seat (create_season_amendment cancel, mark_season_performance_past): is_season_date_leave_pending(result). The return types are unchanged (Any).

  • Errors: no new classes or constants. The new codes come through SeatLayerError and its subclasses with code (§8 item 4). The CHANGELOG lists them, now including every code the 3 calls and inventoryMode can send (SEASON_BLOCK_API_BUSINESS_ERRORS in the app repo).

  • Docs: a "Season seats" section at the end of the README's "Fixed Renewable Seasons": opt in with inventory_mode, set the seats aside, follow the job, read the block, release to single sale (now or at a set time), the two webhook names, the 202 guard, refusals through SeatLayerError with code, and partner seats staying out of the block.

  • CHANGELOG: the "Next: 0.9.0" section. The version PR joins it with the season-2a section under ## 0.9.0. Its webhook line now says what 0.8.0 did (it had said "0.9.0 refused…", which stops being true once both ship as 0.9.0).

Files changed

CHANGELOG.md, README.md, src/seatlayer/__init__.py, src/seatlayer/resources.py, src/seatlayer/types.py, tests/test_client.py, tests/test_seasons.py (7 files, +413 −29).

Checks

  • The repo's gate (ruff check src tests, mypy, pytest -q) on Python 3.14, re-run 10 Nov on this commit: green. ruff: all checks passed; mypy: no issues in 7 source files; pytest 133/133.
    • The operation map test goes 87 → 90: paths, bodies, the caller's key and the generated replay key on both writes.
    • A test sends the phase 4 answers through the transport: the 202s, a job of a new kind, and the 409 codes, each a SeatLayerConflictError with its code.
    • inventory_mode on create and update.
    • A webhook test checks that every typed name is accepted.
  • Mutation check (run before this amend; the code is unchanged since): 8 mutants (the 3 paths, both bodies, header replay, the guard, the event names): 8/8 killed.
  • The README sample isn't run by the repo. Its syntax was checked once (ast.parse), and the calls by hand against the signatures in resources.py.
  • Compat matrix: the same fixtures, shaped as the phase 4 API answers, through a stub transport. The script is season-phase4-python-compat.py in the app session's notes; nothing is sent.
Build Result
published 0.8.0 (the PyPI wheel, pip download seatlayer==0.8.0 --no-deps, re-run 10 Nov) all PASS where the method exists, 4 of 14: the 409 code and the 202 on create_season_amendment, a Season answer carrying inventoryMode, and 409 season_block_packages on update_season. The other 10 are n/a: 0.8.0 has neither the season-2a calls nor these
season-2a-server head e3ab392 all PASS (10 Nov run, not re-run): jobs of both new kinds, a mixed job list, the three new 409 codes, the 202 on a cancel and on a past date, inventoryMode and 409 season_block_packages
this build (0.9.0) all PASS, 14/14 (re-run 10 Nov on this commit's src/), including the 3 new calls and 409 season_block_not_enabled
  • Not done yet: a live dev smoke of the 3 calls with the dev test key. The phase 4 API isn't on dev until the stack merges; it is step 3 below.

Release steps for the owner

  1. The season-2a PR (Season 2a server operations (part A) #12) merges to main, with no tag.
  2. Rebase this PR onto main and retarget it to main; the gate green; merge.
  3. Compat matrix again, once the phase 4 API is on dev:
    • run the compat script against published 0.8.0 and against main's src/: both all PASS;
    • the live dev smoke of the 3 calls with the dev test key: opt in, set aside, follow the job to completed, read, release with at, read the release job.
  4. Version PR: 0.9.0 in both pyproject.toml and src/seatlayer/__init__.py, which must agree. Join the CHANGELOG's season-2a section ("Unreleased") and "Next: 0.9.0" under a dated ## 0.9.0. Merge to main.
  5. Publish: only after the phase 4 API (paiteq/seatmap #2059 → #2086 → #2061 → #2062 → #2063 → #2066 → #2092) is live on production and the owner says yes: git tag v0.9.0 && git push origin v0.9.0. release.yml publishes through PyPI Trusted Publishing, and refuses if the tag and either version disagree.
  6. Live re-check:
    • https://pypi.org/pypi/seatlayer/json reads 0.9.0;
    • in a fresh venv, pip install seatlayer==0.9.0 and python -c "import seatlayer; print(seatlayer.__version__)";
    • run the dev smoke again from the installed package.
  7. Rollback (0.9.0 only adds, apart from the webhook names fix): yank 0.9.0 in the PyPI web UI (project › Manage › Releases › Yank). New installs get 0.8.0 again, and exact pins keep working. PyPI never takes the same version twice, so a fix ships as 0.9.1. A rollback also withdraws season-2a: both ship in 0.9.0.

Failure modes

  • Publishing before the phase 4 API is on production: the 3 calls would answer 404, and inventory_mode would be ignored. Step 5 waits for production.
  • A caller skipping is_season_date_leave_pending: it would read the 202 body as the amendment. The return type is Any, so the README and the docstrings point at the guard.
  • Webhook names: with the list built from WebhookEventName, a name typed here but unknown to the API would reach the API and be refused there. Today every typed name is one the API accepts.
  • Old SDKs: 0.8.0 reads the new answers through its generic error and ignores inventoryMode (compat matrix). It can't call the 3 new routes.
  • README drift: the sample isn't run. A later signature change would leave it stale.

Open risks

  • No live dev smoke yet (step 3).
  • The README links to the docs' "Season seats" section (/server-api/seasons/#season-seats), which goes live with paiteq/seatmap #2063. Until then the link lands on the top of the Seasons page.
  • The README's season intro still counts 87 operations (it is 90 with this PR); it was left as it was.
  • A code comment on _EVENT_NAMES still says "0.9.0 listed 18 by hand"; it means the season-2a branch. This amend touched only the README and CHANGELOG.

… settings, all 56 webhook names, events series_key
…tle (2.16c)

createEventSeries takes fromEventKey: an event already set up becomes the first date under its own key and every other date is a copy of it; name and chartKey are then left out. Every date of a series carries the series' plain name. Matches seatmap slice 2.16c.
Adds releaseSeasonLapsedOffers (POST /v1/seasons/{key}/renewal-offers/release-lapsed, no body, header replay, 202 with the job). retrieveSeasonHostedRenewals pages with cursor and limit (1-500) and answers nextCursor, and listSeasonRenewalOffers pages the same way (1-100). createSeasonRenewalOffers answers 201 {offers} for 100 contracts or fewer and 202 {job, batchId} above that. The bring-across (createSeasonHolderImportFromSeason) answers 202 with a holder_import job above 100 holders. Renewal emails take up to 20,000 offer ids; above that, 422 too_many_offer_ids.
…e_abandoned (p2b)

The public API changes in p2b (b88ed794e..ee7df2921): season_date_cancel_void_job_id names the date_cancel_void job a date cancel starts (season.ticket.voided reason date_cancelled); season_holder_import_rows_per_call sizes a create_season_holder_import call by its dates, now 2 to 50 (a call over it raises SeatLayerValidationError invalid_rows with maxRows); update_season documents singleTicketDates.jobRefused and its exact-retry guard; create_event_series documents 409 create_abandoned. Both helpers are module functions exported from seatlayer.
…b audit)

updateEventSeries sends only the prices that change: each date keeps its
own price for every category or tier left out. A season's null transfer
or return cut-off means the club default from Settings > Ticket site, or
until the date starts when the club has none. Holder transfers and
returns through the secret key are not stopped by the club's customer
switches; cut-offs still apply.
…part C)

A date its season cancelled never opens again: moving its sales to
testable or live, or reopening it, answers 409 season_date_cancelled,
and an event series "sales on" leaves it closed as a failed item of the
job (season_date_cancelled; not in failedDates). A cancelled date can't
be moved, postponed, replaced or marked as taken place: 409
performance_cancelled.
@pietechsolution
pietechsolution force-pushed the navin/season-block-0.10.0 branch 2 times, most recently from 3245551 to 1244a02 Compare November 10, 2026 02:31
…release_season_block; season.block.ready/released webhook names; webhooks accept every typed name; the 202 date-leave answer guard. No version bump: publish only once the phase 4 API is on production
@pietechsolution
pietechsolution force-pushed the navin/season-block-0.10.0 branch from 1244a02 to a643e22 Compare November 10, 2026 05:48
@pietechsolution pietechsolution changed the title [0.10.0, NOT for 0.9.0] Season seats: set_aside_season_block, retrieve_season_block, release_season_block [0.9.0] Season seats: set_aside_season_block, retrieve_season_block, release_season_block, inventoryMode Nov 10, 2026
@pietechsolution pietechsolution mentioned this pull request Nov 11, 2026
@pietechsolution
pietechsolution changed the base branch from season-2a-server to main November 11, 2026 10:14
@pietechsolution
pietechsolution marked this pull request as ready for review November 11, 2026 10:14
@pietechsolution
pietechsolution merged commit bc29fe2 into main Nov 11, 2026
4 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.

2 participants