Repository navigation
Release v0.15.0 - #70
Merged
Merged
Conversation
* chore: re-capture the projection fixture corpus from pipelex dev The shared inputs-template projection corpus moved in `pipelex`, so the copy committed here was stale. This is a pure re-capture: no projection code changed, because the Python projection reads whatever kind the descriptor states and both moves are descriptor-side. The capture adds one pipe, `input_semantics_scaffold.scaffold_open_natives`, which puts the natives whose payload shape the descriptor states openly at slot positions — the one place the corpus never reached. `native.Dynamic` and `native.Composite` are structureless by design, so their node is `unknown` and both renderers agree on the empty mapping. `native.JSON` is not: its pinned blueprint carries a required `json_obj`, which makes the dict inside it the corpus's first required unknown node. That position is what the capture declares as a new divergence class, `unknown-empty-object`. The projection renders the empty object, because the descriptor withholds the payload shape there and inventing one would stop it projecting the descriptor; the reference engine reflects the runtime content class instead and fills the dict with a sample key/value pair whoever fills the template in has to delete. The empty object round-trips through the input shaper cleanly, so the class records a difference of vantage rather than a defect and carries no ledger item. `input_form.json`, `pipe_io_contracts.json` and `inputs_template/manifest.json` move with the new pipe; every pre-existing key in all three is byte-for-byte unchanged. `mthds-js` commits the identical bytes. Advances L-260831-56a78f Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01TEH4cihpjsEwgtKL16nzbK * docs: hold the divergence record's prose half to the manifest Review follow-ups on the open-natives recapture, all outside the capture itself — the fixture bytes are untouched and stay byte-identical to the mthds-js twin. The README beside the corpus explains each divergence class in prose, and that was the one half of the record nothing enforced: the manifest is checked hard, so a class that stops occurring has to be retired deliberately, but a retired class used to leave its bullet behind describing a difference that no longer exists, with the suite still green. `test_inputs_template_projection.py` now holds the README's bullet list to the manifest's own declarations, in both directions — the twin of the check `mthds-js` carries. `input_form.json` and `pipe_io_contracts.json` are one capture taken in one command, but nothing outside the contracts file's own parse test read it, so one half could lose a pipe the other still held with the whole suite green. Deleting the new pipe's contract took tests/unit from 557 passed to 556 passed and no failures; the two payload files now have to name the same pipes. The changelog entry claimed the JSON blueprint's `json_obj` is the corpus's first required unknown node. It is not: `widget.matrix[]`, `opt.matrix[]` and `many[].matrix[]` are required unknown nodes and pre-date this capture. What is new is the position — the first required unknown node at an object-field rather than a list-item slot, which is where the engine's rendering diverges. Two more prose claims the committed bytes contradict: the README's summary of which divergence entries carry a `null` ledger item named two of the three there now, and it still described byte parity as skip-gated on a projection that has since landed under L-260830-e7c5b5, so the job runs. Advances L-260831-56a78f Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01TEH4cihpjsEwgtKL16nzbK --------- Co-authored-by: Claude Opus 5 <[email protected]>
…ut contract (#59) Mirrors the standard's new Output-Form Descriptor page and the `json_schema` member it added to the output contract, which landed in one version because neither is useful without the other. `mthds.protocol.output_form` declares no node type: `field` is `InputFormField`, reused verbatim — an output is a concept reference exactly like an input is, so its kinds, its nesting and its constraints are the same questions with the same answers, and a second node union would be a second place for them to drift. One `field`, not a `fields` list: a pipe has exactly one output. What the module adds is the enforcement of what an output does NOT carry — `presence` and `gating` are optional on the node so a slotless node can exist, not so a producer may fill them in with something plausible, and one that does now fails the parse. `PipeOutputContract.json_schema` is breaking: the shape is closed and the member required, so a payload from an earlier engine fails the parse. It is the PAYLOAD's schema, which is where its rule departs from its input twin's — an input's describes what a caller sends, an output's what comes back, so a plural output's is the list content model rather than a bare array. The parity capture gains an output_form.json and a fourth corpus bundle covering outputs specifically. That bundle exists because the three before it all resolved to Text: a corpus whose every pipe outputs one native scalar can only capture one output descriptor and one output schema, so the plural wrap — the producer obligation that fails silently — would never have appeared in it. Claude-Session: https://claude.ai/code/session_019VJ89z3Gza9CW25pQombFK Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
`tests/fixtures/protocol/` is committed byte-identically here and in `mthds-js`, and until now nothing in either repo checked that. The only comparison lived in `conformance`, where it runs on a `conformance` pull request rather than on the one that moves the corpus — so a recapture landed here without its `mthds-js` twin was caught weeks later by an unrelated change, and the pairing held only because prose in two pull request bodies asked for it. `.github/workflows/protocol-corpus-parity.yml` checks `mthds-js` out on every pull request and compares the two trees, which fails the pull request that would land a one-sided recapture instead of reporting it afterwards. Two properties make it deployable. It needs no secret: both mirrors are public, so the sibling checkout is unauthenticated and the job runs on a pull request from a fork exactly as it does on one from a branch — a gate that needed `conformance` itself, which is internal, could not have done that. And it resolves the sibling ref as this pull request's own branch name when the sibling carries a branch of that name, falling back to `dev`: a recapture lands as a paired pull request in both mirrors at once, and comparing each against the other's `dev` would leave both red until the first merged. The file is a vendored copy of `conformance/tests/mthds/fixtures/protocol_corpus_parity_workflow.yml` and is byte-identical in both mirrors, which is why it derives which repo it is running in from `github.repository` rather than naming one. `make check-fixture-drift` in `conformance` fails when a copy drifts or goes missing, so the coupling cannot be quietly undone. Advances L-260902-61be99 Claude-Session: https://claude.ai/code/session_012E1hEFhVP2Ze394Aic1v58 Co-authored-by: Claude Opus 5 <[email protected]>
…published v0.12.0 (#61) `ab27956` added its three entries under `## [v0.12.0] - 2026-09-01`, a version published the day before it landed. The code they describe is not in that release: $ git cat-file -e v0.12.0:mthds/protocol/output_form.py -> absent $ git cat-file -e main:mthds/protocol/output_form.py -> absent So the released notes claimed a module and a contract change nobody who installed 0.12.0 has, while `[Unreleased]` — the section the next release draws from — carried neither. The breaking one is what makes this worth a commit of its own: `PipeOutputContract.json_schema` is a required member on a closed shape, so a payload from an earlier engine fails to parse, and cutting a release from `[Unreleased]` as it stood would have shipped that break with no note under its own heading. This is a pure move; no wording changes. The new module and its suite go to `### Added`, and the required member on an existing contract to `### Changed`, which is what each of them is. Two entries deliberately stay under v0.12.0. The inputs-template projection, the TOML emitter and the corpus were all in that release — `tests/fixtures/protocol/inputs_template/manifest.json` is present at the tag — so their attribution is correct. The longer restatement of the corpus entry that `ab27956` also added duplicates the shorter one above it, but it describes v0.12.0 content and is not misattributed, so it is left alone. Claude-Session: https://claude.ai/code/session_012E1hEFhVP2Ze394Aic1v58 Co-authored-by: Claude Opus 5 <[email protected]>
Same shape as the mthds-js sync: the only conflict was CHANGELOG.md, and main's side wins — its `## [v0.13.0]` heading is the published record, while dev still carried the same work under `[Unreleased]`. Verified rather than assumed: every topic under dev's `[Unreleased]` appears in main's release section, and the released code is there too (`mthds/protocol/output_form.py`, and `scaffold_open_natives` in the committed fixture corpus).
Two pipes join the corpus from scaffold_bundle.mthds: scaffold_anything_slot, which places native.Anything at an input slot the descriptor states as an unknown node, and scaffold_dynamic_prompt, which pairs native.Dynamic with a structured concept whose text fields are named url and homepage_url. Both add worked sites to the existing text-named-url and optional-field-included divergence classes rather than declaring a new one, so the projection needed no change. The unshapeable record gains the scaffold_anything_slot pair in both shapes, refused with a StuffFactoryError and tracked by L-260902-10eb56. The fixtures README's provenance is corrected alongside: it listed three bundles where the capture has been taken from four since output_bundle.mthds joined, and omitted output_form.json from the files to copy across. Captured from pipelex dev at 551fe3463. mthds-js commits the identical bytes. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AUyEZQcLJ4Qhb9vKoSw6vD
….0.0, each constant declared once (#64) * feature: follow the standard's version cut — MTHDS_STANDARD_VERSION 2.0.0, each constant declared once The MTHDS specification unified its release number with its standard version and cut both at 2.0.0 (mthds#63), a major accounting for the breaking native, manifest, lock and resolution changes it had shipped while MTHDS_STANDARD_VERSION stood still at 1.0.0 since February. This package copies that cut: the standard version becomes 2.0.0, the protocol version stays 0.6.0 — the two numbers are independent and the routes did not change — and each constant now carries a docstring naming https://mthds.ai/spec/versioning/ as the rule that governs it, so following the next cut is one edit per number rather than a search. Adds `is_mthds_version_satisfied`, which evaluates a manifest's `mthds_version` against MTHDS_STANDARD_VERSION and against nothing else. The constraint was validated for spelling here and then evaluated nowhere, so every runtime open-coded the parse-and-match itself. Parsing a manifest still does not evaluate it: a package asking for a standard version this runtime does not implement is a well-formed manifest, and whether that warns, refuses the load or fails validation is the runtime's call. Fixes a pre-existing bug found at that seam: `parse_constraint` handed the string to SimpleSpec untouched, which rejects ">=1.0.0, <2.0.0" over the space alone — while the manifest format documents that exact spelling and `is_valid_version_constraint` accepts it. Any manifest or dependency written that way raised SemVerError from the resolver instead of resolving. The `PipelexRunner.version` TODO stays; it retires when the pipelex CLI exposes `protocol_version` (L-260829-43c082). Adds docs/versioning.md. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_017VEgU3Bn7kfRA4ddJZ48HH * fix: prefix every spec-site link with /latest/, and pin the constraint spellings mthds.ai publishes each release under its own prefix and redirects only the root to /latest/, so a bare https://mthds.ai/spec/<page>/ is a 404 while https://mthds.ai/latest/spec/<page>/ is the page. Verified live: the bare shape returns 404 for input-form-descriptor and pipe-io-contracts, the prefixed shape returns 200 for both. Every link into the site — the docstrings this branch adds, docs/versioning.md, and the ones in docs/inputs-template.md and docs/runners.md that predate it — now carries the prefix. The versioning page is the exception, and it stays a forward reference: it landed on the standard's dev branch in mthds-ai/mthds#63 and has never been published, so it is a 404 at both shapes. mthds.ai's `latest` still aliases 0.4.0, cut before the page existed. Publishing it is L-260906-50cf25. Also pins that is_valid_version_constraint and parse_constraint agree on every constraint spelling, in both directions. Stripping the blocks fixed the spaced compound constraint, and the risk it introduced is the opposite error: an empty block left by a doubled or trailing comma must stay a refusal rather than being silently dropped, which would widen the constraint the manifest asked for. Advances L-260829-5b9a42 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019dKCMzkj6yhetfgNeA4cEm --------- Co-authored-by: Claude Opus 5 <[email protected]>
The skill now points at the workspace release play for the procedure and declares only what is this repo's own: what publish.yml ships to PyPI on the push to main and how the landing verifies it, the pyproject.toml version and the uv.lock that follows it, the gates including the protocol fixture corpus parity with mthds-js, the files the release commit carries, the workflows that gate a release pull request, and the particulars — no pre-release form can reach main, the changelog heading carries the v, and the back-merge is a merge commit. Everything the play does once for every repo is gone: the git status pre-flight that offered to fold uncommitted changes or unpushed commits into the release, the release branch created from the current HEAD in place, the numbered restatement of the bump, changelog, commit, push and pull request steps, and the post-merge reminder. The branch is now cut in _mthds-python--release by wt add, and the merge is landed by /ledger-land. Claude-Session: https://claude.ai/code/session_016F72qvy4QBZHe7XX24QmcT Co-authored-by: Claude Opus 5 <[email protected]>
# Conflicts: # CHANGELOG.md
…ript-blank (#67) Adds `mthds.protocol.method_files` — `MethodFile` and the `serialize_method_files` / `parse_method_files` pair — the Python twin of the `mthds` npm package's `protocol/method_files.ts`, so a Python client reads a stored method's catalog source (`[{ name, content }]`, with `""` as the no-source form) without re-porting the platform's decoder. The port is held to the twin edge for edge: the blank predicate is ECMAScript `trim`'s closed set rather than `str.strip()`, which disagrees at both ends, and the bytes are `JSON.stringify`'s; the suite pins both against output the twin printed, and the docstring names the three places the twin deliberately departs from the platform. Closes L-260907-c34629 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5.1 <[email protected]>
… the request (#68) The run-source rule — which combinations of `pipe_code`, `mthds_contents`, the pipelex-api bundle encodings (`files` / `bundle_b64`), a published address (`method_ref`) and the hosted catalog id (`method_id`) may be sent together — now has one Python definition in `mthds.protocol.options`, the twin of `mthds-js`'s `protocol/options.ts`, instead of living as private helpers inside `pipelex-sdk`'s client where `mthds`' own client saw none of it. Those definitions and their tests are the whole of it: nothing in this package enforces from them, so `MthdsAPIClient` keeps forwarding the keys inside `extra` without interpreting them and no client behaviour changes. The first consumer is `PipelexAPIClient` in `pipelex-sdk`, which pins an older `mthds` and can replace its private copies once a release ships the module. Whether the generic any-runner client should enforce Pipelex extension rules at all, and whether the standard's package is where the hosted selector rules belong, is the open design question L-260912-1b10c1 — the branch no longer depends on its answer. Closes L-260907-f4e16c 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <[email protected]>
…t, serialize the ref (#69) `ConceptAbstract` keeps `code`, `domain_code` and the `concept_ref` property; `description`, `structure_class_name` and `refines` leave it, because none of them is a fact about a stuff — a concept's definition belongs to the library the method loads, and a runtime that needs one of them in memory declares it on its own subclass. `StuffAbstract` gains a field serializer on `concept`, so every dump of a stuff emits the standard's wire form, `{"concept": "<domain>.<Code>", "content": …}`, instead of leaving each runner to reduce the object itself. This is the rule the MTHDS spec landed in the CLI I/O contract's "Stuffs on the Wire" section. It is a deliberate break: a stuff, a working memory or a pipe output can no longer be validated back from its own dump, and a consumer that did so resolves the concept by ref through the loaded library instead — the changelog, the docs and the serializer's docstring all say so. Closes L-260918-8fba7a 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018GodVX8dbGwhAZGWnuz6M3 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Trims `ConceptAbstract` to the reference surface — `code`, `domain_code` and `concept_ref` — and makes `StuffAbstract` serialize `concept` as the ref string, so every dump of a stuff emits the standard wire form `{"concept": "<domain>.<Code>", "content": …}` instead of leaving each runner to reduce the object itself. The definition fields (`description`, `structure_class_name`, `refines`) no longer travel with a concept; they belong to the library the method loads, and a runtime that needs them declares them on its own subclass. The two override points reach different code: `concept_ref` on the concept reaches every reduction including the serializer, while `serialize_concept` on the stuff reaches a dump alone — `DictRunResultExecute.from_pipe_output` reduces by `concept_ref`, so a `serialize_concept` override is not followed there (which point it should honour is filed as L-260918-5f09d8). Closes L-260918-8fba7a. **Breaking changes** - A stuff, working memory, or pipe output can no longer be validated back from its own dump — a consumer that re-validated a dump resolves the concept by ref through the loaded library instead. - Producing a dependency-contributed concept's crate key `<package_address>::<domain>.<Code>` is a subclass's job: override `StuffAbstract.serialize_concept` under that name. <sup>Written for commit 7732e1b. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/mthds-ai/mthds-python/pull/69?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="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Claude Opus 5 <[email protected]>
Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_018GodVX8dbGwhAZGWnuz6M3
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v0.15.0
Bumps version from
0.14.0to0.15.0. Promotesdev→main.Changelog
Added
mthds.protocol.method_files— the catalog serialization of a stored method's source:MethodFile(name+content) and the pairserialize_method_files/parse_method_filesbetween a list of files and the JSON[{ name, content }]string the hosted platform persists for a method's.mthdssource and its custom PipeFuncpython. The empty list serializes to"", the platform's "no source" sentinel, never to"[]"; blank-content entries are dropped in both directions; anything but the named array raisesPipelineRequestError, including the three constants Python'sjsonaccepts andJSON.parserefuses. It mirrors themthdsnpm package'sprotocol/method_files.tsedge for edge — blank is ECMAScript-blank, and the bytes areJSON.stringify's down to both surrogate rules, an unpaired one escaped and an adjacent pair combined into the astral character Python alone can spell two ways — so a Python client reads a stored method's bundle from here rather than re-porting the platform's decoder. Python's own number model is kept out of the way too: integers decode as floats, the twin's single number type, so a literal longer thansys.get_int_max_str_digits()cannot raise a bareValueErrorthrough the typed surface. One divergence from the twin is forced rather than chosen and is documented as such: Python's decoder recurses whereJSON.parseiterates, so a deeply nested source is refused here and accepted there, at a depth that is an interpreter build constant.mthds.protocol.options— the run-source argument surface: predicates that decide which combinations ofpipe_code,mthds_contents, the bundle encodingsfiles/bundle_b64,method_refandmethod_idmay be sent together (assert_exclusive_run_sources,has_bundle_payload,assert_method_ref_pairs_with_nothing), plus the selector normalizationnormalized_selector/run_selector_extensions, which refuses a non-string selector. Nothing in this package calls them, so no client behaviour changes:MthdsAPIClientstill forwards the keys insideextrawithout interpreting them.Changed
ConceptAbstractnames a concept and nothing more, andStuffAbstractserializesconceptas the ref string (Breaking): the protocol's concept model keepscode,domain_codeand theconcept_refproperty —description,structure_class_nameandrefinesare gone, since a concept's definition belongs to the library the method loads and never travels beside a stuff, and a runtime that needs one of them in memory declares it on its own subclass.StuffAbstractnow carries a field serializer onconcept, so every dump of a stuff emits{"concept": "<domain>.<Code>", "content": …}— the wire form the standard's CLI I/O contract requires — instead of leaving each runner to reduce the object itself. The dependency-contributed crate key<package_address>::<domain>.<Code>is a subclass's to produce, by overridingStuffAbstract.serialize_conceptunder that name: the protocol model holds no declaring package address. The dump is one-way, which is the point rather than an oversight — aStuffAbstract, aWorkingMemoryAbstractor aPipeOutputAbstractcan no longer be validated back from its own dump, since the definition such a parse would need is exactly what no longer travels. A consumer that used to re-validate a dump resolves the concept by ref through the library the method loads, the way the input side already does. Which of the two override points a runtime picks decides what this SDK's own output path emits:DictRunResultExecute.from_pipe_outputreduces a stuff byconcept_ref, so aconcept_refoverride reaches it and aserialize_conceptoverride does not.Closes L-260918-414e31
🤖 Generated with Claude Code
https://claude.ai/code/session_018GodVX8dbGwhAZGWnuz6M3
Summary by cubic
Releases v0.15.0, promoting
devtomain(version0.14.0→0.15.0) with two new protocol modules and a breaking trim of the concept model.New Features
mthds.protocol.method_files—MethodFileplusserialize_method_files/parse_method_files— the catalog serialization of a stored method's source, mirroring themthdsnpm package edge for edge.mthds.protocol.options— predicates deciding which run-source combination is legal, plus selector normalization.MthdsAPIClient'sexecuteandstartnow validate run sources through those predicates and raisePipelineRequestErrorfor illegal combinations, where they previously only checked that one source was supplied.Migration
ConceptAbstractkeeps onlycode,domain_codeandconcept_ref;description,structure_class_nameandrefinesare removed, since a concept's definition belongs to the loaded library rather than traveling beside a stuff.StuffAbstractnow serializesconceptas the"<domain>.<Code>"ref on every dump, so stuffs, working memories and pipe outputs can no longer be validated back from their own dumps — resolve the concept by ref through the loaded library instead.<package_address>::<domain>.<Code>is a subclass's job, via overridingStuffAbstract.serialize_concept; note thatDictRunResultExecute.from_pipe_outputreduces byconcept_ref, so aconcept_refoverride reaches it but aserialize_conceptoverride does not.Closes L-260918-414e31.
Written for commit de5bb93. Summary will update on new commits.