Skip to content

Release v0.25.0 - #101

Merged
lchoquel merged 2 commits into
mainfrom
release/v0.25.0
Oct 18, 2026
Merged

lchoquel merged 2 commits into
mainfrom
release/v0.25.0

Conversation

@lchoquel

@lchoquel lchoquel commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Release v0.25.0

Bumps version from 0.24.0 to 0.25.0. Promotes dev → main.

Changelog

Added

  • The core error registry, generated: mthds.protocol.problem.registry is generated from the standard's core error registry 8.0.0, vendored byte for byte from the standard's v8.0.0 release in vendor/mthds-error-registry/ with its source tag and commit, and gives a constant per core code (CoreCode), a typed parameters model per code, reading a number strictly as JsonInteger and JsonNumber (mthds.protocol.problem.json_types) do, never from a string or a boolean, each code's entry (CORE_CODE_SPECS) and the readings a client falls back on; CORE_ERROR_REGISTRY_VERSION in mthds.protocol.protocol declares the version, and make check-error-registry fails when the vendored files, the declared version and the generated module disagree.
  • Problem and ProblemItem: mthds.protocol.problem.models reads the protocol's problem document as received, its undeclared members on model_extra, with effective_domain and effective_step_kind reading a value the protocol does not list by the code's family, effective_retry reading an unknown verdict as no, is_retryable true only for a retry of yes, outcome_is_unknown, params_as to read the parameters as the code's model, and each fault of errors and its suggested_fix typed; make_client_problem in mthds.protocol.problem.client_problems states a client's own problem from the registry.

Changed

  • Standard version 8.0.0 and Protocol 2.0.0 (Breaking): MTHDS_STANDARD_VERSION moved from 7.0.0 to 8.0.0 and PROTOCOL_VERSION from 1.0.0 to 2.0.0, the release of the standard in which every error is a problem document, so a manifest's mthds_version is evaluated against 8.0.0, and ^7.0.0, ~7.0.0, 7.* or an exact 7.0.0 no longer pass.
  • Every request error carries a problem (Breaking): PipelineRequestError(message, *, problem) requires the failure as a Problem, read on exc.problem, and every error this package raises carries one under a registered code: an argument refused before any request leaves, by options, method_files or the client, carries client.argument_invalid naming the argument and the reason, and RunStillRunningError, raised on a 202 from execute that names its run, carries client.follow_failed with the run as the problem's pipeline_run_id, while a 202 naming no run raises ApiResponseError carrying client.outcome_unknown, since the run may exist. BlobUploadError, raised when an upload target refuses a blob, carries the target's own problem document, or else the problem its status names, its message never quoting the target's body, which an object store's refusal such as S3's SignatureDoesNotMatch fills with the presigned signature and query that grant the write, while response_body keeps it whole, and an upload that gets no usable answer raises it too, with status, status_text and response_body None, carrying client.unreachable or client.timeout as a request that creates nothing does, since a blob's upload is safe to repeat, or client.response_unreadable when the target is malformed, no URL the client can send to or a header it cannot write, such as a value not in ASCII or holding a line break, its headers never quoted, and a missing_blobs answer naming a blob the request did not list raises ApiResponseError carrying client.response_unreadable, as any success that is not its route's answer does. A subclass built elsewhere passes a problem to the constructor, and every error, the PipelineRequestError that make_argument_invalid_error raises for a refused argument included, ends str(exc) on the problem's next step, on a line of its own.
  • ApiResponseError reads every answer as a problem by its code (Breaking): a failed answer raises ApiResponseError carrying the runtime's problem document, with status and retry_after filled from the answer when the body leaves them out, and an answer that is no problem document reads as client.outcome_unknown when it is a 5xx on execute or start, where a gateway may stand in front of a run that started, as the problem its status names for 503 on a route that creates nothing and for 405 on any route, and else as client.response_unreadable. A success whose body is not the route's answer reads as client.response_unreadable on a route that creates nothing, and on execute or start, where the runtime accepted the request and the run may exist, as client.outcome_unknown, naming the run as its pipeline_run_id when the body gives the run's id as a non-empty string; an execute or start answer whose pipeline_run_id is empty names no run, so it reads as client.outcome_unknown naming none, as a 202 on execute does. Neither raises a pydantic or JSON error, and str(exc) then quotes the start of the body the server sent, except a missing_blobs success's, whose upload URLs carry the permission to write and which only response_body keeps. An error body is decoded refusing NaN, Infinity, a number past the range of a double and nesting deeper than MAX_BODY_NESTING (512 levels, checked before parsing), and reads an integral number such as 2.0 as the integer 2, as a JavaScript client reads it, while a success body is decoded from its bytes as Response.json() decodes it (decode_success_json): strictly as UTF-8, so that a body that is not UTF-8 is unreadable rather than read with its bytes replaced, refusing NaN and Infinity, and otherwise within Python's own limits, so that a run's output nested deeper or holding an integer past a double's range reads whole and its numbers stay as written, a pipe output's 10.0 reaching the caller as a float. The members type_uri, title, instance, server_message, error_type, error_domain, retryable, user_action and validation_errors are gone, read from exc.problem instead, and so are ProblemDocument and UserAction, while request_id stays, the problem's own request_id member or else the X-Request-ID header; mthds.runners.api.problem.read_error_answer reads an answer as an AnswerReading, its problem and whether that problem is the body's own, as mthds.protocol.problem.answers.read_answer does, and the protected _raise_api_response_error receives the problem and problem_from_body.
  • A request that gets no answer raises ApiTransportError (Breaking): httpx's transport and decoding exceptions no longer escape a route; the request raises ApiTransportError, carrying client.unreachable when no request left, client.timeout when none came back in time, and on execute and start client.outcome_unknown once the request has left, since the run may exist. An answer whose body cannot be read, such as one whose content encoding cannot be decoded, is no usable answer either, whatever its status: it raises ApiTransportError carrying client.outcome_unknown on execute and start and client.unreachable on any other route, as the TypeScript client reads it. The client never repeats a request by itself.
  • ClientNotConfiguredError replaces ClientAuthenticationError (Breaking): the client raises it, a PipelineRequestError carrying client.not_configured and the setting, when no API key or base URL resolves at construction, when the API key holds a character an HTTP header cannot carry, such as a line break or a space at its end, which httpx would refuse to write at every request, when the base URL does not make a valid URL or does not start with http:// or https://, or when request_timeout_seconds is set to anything but an int or a float, never a bool, holding a finite number of seconds a float can hold, never negative, each reading as a setting to fix rather than as an unreachable runtime to retry; the key is never quoted, and neither is a user:password@ written in the base URL, which this error's message and client.unreachable name without its userinfo, query and fragment.
  • The validate verdict's items are problem items (Breaking): InvalidValidationReport.errors replaces validation_errors, each fault a ProblemItem with its code, detail, params, location and suggested_fix, both arms carry advisory warnings in the same schema, and the invalid arm's message is optional; the reports are generic over the item type, and ValidationDiagnostic is gone.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MTPBJkbpj7cQMqEfePgfDU


Summary by cubic

Promotes dev to main for the v0.25.0 release: bumps the version to 0.25.0 and implements the MTHDS standard 8.0.0 and Protocol 2.0.0, in which every error is a problem document. This is a breaking release.

Breaking changes

  • PipelineRequestError now requires a problem; every error this package raises carries one under a registered code.
  • ApiResponseError reads any answer as a problem by code; run routes that get a 202 naming no run, or an unreadable success, raise client.outcome_unknown.
  • Requests that get no usable answer raise ApiTransportError (client.unreachable, client.timeout, or client.outcome_unknown) instead of leaking httpx exceptions.
  • ClientAuthenticationError is replaced by ClientNotConfiguredError, carrying client.not_configured.
  • A validate verdict's validation_errors become errors of ProblemItems; ProblemDocument and UserAction are removed.
  • A manifest's mthds_version is now evaluated against 8.0.0, so ^7.0.0, ~7.0.0, 7.*, and 7.0.0 no longer pass.

New

  • Vendors the core error registry 8.0.0 and generates mthds.protocol.problem.registry: a constant (CoreCode) and typed parameters model per code, strict JsonInteger/JsonNumber reading, and a make check-error-registry gate that fails when the vendored files, declared version, and generated module disagree.
  • Problem and ProblemItem read documents as received, keeping undeclared members on model_extra.
  • BlobUploadError now carries problems too: an object store's refusal, an unrequested blob in a missing_blobs answer, or an upload target the client cannot send to.

Written for commit 8b8a905. Summary will update on new commits.

View guided diff Turn on auto-fix

lchoquel and others added 2 commits October 18, 2026 21:27
mthds-python vendors the MTHDS core error registry's snapshot, generates
its codes, parameters models and constants from it, and reads every
runtime answer as a problem document by code, so `MthdsAPIClient` raises
one coded problem for every failure, including the answers that are no
problem document and the transport failures. It agrees with the
TypeScript twin on a shared case file of answers and their readings, and
records the breaking changes in the changelog.

Closes L-261015-339eba

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
`mthds` now implements MTHDS standard 8.0.0 and protocol 2.0.0, vendors
the core error registry from the v8.0.0 release, and reads every request
failure as a problem document by code, so `MthdsAPIClient` raises
`PipelineRequestError` subclasses that always carry a `problem`.

**New Features**
- Vendors the core error registry 8.0.0 byte for byte from the
standard's `v8.0.0` tag and generates `mthds.protocol.problem.registry`:
a constant per core code, a typed parameters model per code, each code's
entry with its fallback family and status readings, the strict
`JsonInteger` and `JsonNumber` types, and a `make check-error-registry`
gate that fails when the vendored files, the declared version, and the
generated module disagree.
- `read_answer` reads any answer as a problem: a problem document as
received, and HTML pages, empty or non-JSON bodies, and mistyped members
under `client.response_unreadable` or the status readings. Success
bodies decode from bytes as `Response.json()` does, keeping numbers as
written (`10.0` stays a float) and refusing only NaN and Infinity, so a
deeply nested or huge-integer output succeeds and non-UTF-8 bytes read
as `client.outcome_unknown` on run routes.
- Client-state failures (`client.argument_invalid`,
`client.unreachable`, `client.timeout`, `client.outcome_unknown`,
`client.not_configured`) are built from the registry with their
parameters filled in; a base URL or API key HTTP cannot carry, or a time
limit that is no finite number of seconds, is refused as
`client.not_configured` at construction, and an answer whose body cannot
be decoded reads as `client.outcome_unknown` on run routes,
`client.unreachable` on the others.
- The client's answers are pinned against a shared case file that the
TypeScript twin also reads, which the merge from dev extended to the
blob and files routes.

**Breaking changes**
- `ClientAuthenticationError` becomes `ClientNotConfiguredError`; a 202
or unreadable success on `execute` or `start`, including one with an
empty run id, now raises `client.outcome_unknown`, naming the run when
the body gives one.
- A `/validate` verdict's `validation_errors` become `errors` of
`ProblemItem`s; `ProblemDocument` and its lenient member reading are
removed in favor of `Problem`, which reads a document as received and
keeps undeclared members on `model_extra`.
- `BlobUploadError` and the errors of the blob routes now carry a
problem too: a runner's refusal reads as `request.invalid`, an object
store's page, an unrequested blob named by a `missing_blobs` answer, or
an upload target the client cannot send to, by URL or by its headers, as
`client.response_unreadable`, and a refused `files` or `data` argument
as `client.argument_invalid`; an upload that got no usable answer raises
`BlobUploadError` with `client.unreachable` or `client.timeout`. An
upload error's message names the blob and target only, never quoting a
`missing_blobs` success or an object store's refusal, since their bodies
and URLs can carry the permission to write.
- Base URLs are written into problems and messages without their
userinfo, query, or fragment.
- Only the API runner raises these problem-carrying errors; the local
`PipelexRunner` keeps its own `PipelexRunnerError`.

<sup>Written for commit e44e7d2.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/mthds-ai/mthds-python/pull/94?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="View guided diff"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<a
href="https://www.cubic.dev/action/auto-fix/pr/mthds-ai/mthds-python/94?returnTo=https%3A%2F%2Fgithub.com%2Fmthds-ai%2Fmthds-python%2Fpull%2F94&source=description"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"><img
alt="Turn on auto-fix"
src="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review 🔄 Running since 2026-10-18T19:45:23.622662Z 8b8a905 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@lchoquel
lchoquel merged commit fc7e3f6 into main Oct 18, 2026
21 checks passed
@lchoquel
lchoquel deleted the release/v0.25.0 branch October 18, 2026 19:46
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant