Skip to content

Commit f2c84e5

Browse files
committed
docs(evidence): record the deployed v1.8.250 schema regeneration evidence
RED staleness list, per-language surface delta against the published packages, alias activation and deserialisation proofs, Go apidiff classification, MCP tools/list from the built wheel, gate results, publication guard receipt and release consequences for the regeneration from Sendmux/docs f7925f2.
1 parent fcb524a commit f2c84e5

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Deployed v1.8.250 schema regeneration (docs f7925f2)
2+
3+
Status: coded, not merged. PR open on `agent/v1-8-250-regen`, branched from `agent/nullable-ref-normaliser-and-aliases` at `0fcff5c` (PR #277, unchanged and included) on `origin/main` `86d7174`; code head `fcb524a86c8bb97f5793c53bb4230c3bec8b6df4`, plus this evidence commit. Supersedes #277 (its commits ride here) and #276 (the transitional v1.8.249 regeneration, not reused). No merge, publish, tag or bot-branch push.
4+
5+
## Inputs
6+
7+
- SDK base: `origin/agent/nullable-ref-normaliser-and-aliases` `0fcff5c` (#277: normaliser rule for `anyOf: [{ $ref }, { type: "null" }]` and the data-driven deprecated-alias tables), itself on `origin/main` `86d7174`.
8+
- OpenAPI source: `Sendmux/docs` main `f7925f2` (`docs(api): publish the v1.8.250 OpenAPI snapshot`), read from a local checkout through `OPENAPI_INPUT_DIR` on every generator run.
9+
- `openapi-app.json` SHA-256 `b42ce87003ae8d4ac80024ce9796cd96db97c5439c5e99d957682f6b6a1f5999` (raw bytes; the file is stored in the canary's `stableStringify` form, so raw and canonical hashes coincide). The live `https://app.sendmux.ai/api/v1/openapi.json`, fetched before the RED run, has the same canonical form (`02-canonical-hash-docs-vs-live.log`; the publication guard below is the same comparison). The figure quoted for this document elsewhere, `5721133e…`, is `json.dumps(document, sort_keys=True, separators=(",", ":"))` of the same bytes — reproduced for docs and live. `openapi-sending.json` unchanged at `c1f82f9b…`. The vendored MCP snapshot before this change was `b901839a…` (the v1.8.248 shape `main`'s packages were generated from).
10+
- Schema delta versus that vendored snapshot (191 components both, no path or operation change): 15 nullable-reference sites are `anyOf: [{ $ref }, { type: "null" }]` against the canonical component (`from` on `MailboxMessage`, `MailboxMessageSummary`, `MailboxRealtimeMessage`, `MailboxMessageContent.participants`; `last_message` on `MailboxThread`/`MailboxThreadSummary`; `MailboxBatchGetItem.content`/`raw_body`; `MailboxSubmissionEnvelope.mail_from`; `MailboxCreateResult.credential`; `ProviderQuotas.per_*`; `SharedAmazonSesLimitRequestPage.pending_request`), no duplicate null branches, no `…OrNull` component; `MailboxMessage` gains the required `message_id`, `in_reply_to`, `references` (`string[]`) and `reply_to` (`MailboxAddress[]`); `MailboxCredential`, `MailboxMessageContent`, `MailboxRawBody`, `MailboxSubmissionEnvelopeAddress` and `ProviderQuotaRange` are plain objects at definition; `MailboxRealtimeMessage`, `MailboxSendScope` and `MailboxSubmissionEnvelope` stay nullable at definition (`02-snapshot-structure.log`). Wire format unchanged: the four fields were always returned and every nullable field stays nullable.
11+
- Toolchain: node v24.21.0, pnpm 10.22.0, Go 1.23.4 (`GOTOOLCHAIN=go1.27.1` for golangci-lint and apidiff, as CI `verify`), PHP 8.4.10 / Composer 2.8.9, Python 3.12.8, Ruby 3.4.1, Rust 1.82.0 floor + stable, Java 17 for openapi-generator.
12+
13+
## RED
14+
15+
`OPENAPI_INPUT_DIR=<docs> pnpm drift:check` on `0fcff5c`: `normalize:check` and every generator ran clean — the #277 alias planner reported no pending or missing replacement — and `verify:sdk-staleness` failed with "Generated SDK package directories are stale." listing 62 entries (50 modified, 12 deleted, 0 untracked): `go/{mailbox,management}` 4 + 4, PHP mailbox 9 / management 5, Python mailbox 11 / management 8, MCP 2, Ruby mailbox 10 / management 7, TS mailbox 1 / management 1. Sending, CLI, Rust and `go/sending` did not change; nothing outside the generated directories changed. Log `.claude/artifacts/l17f/01-drift-check-red.log`; stale list `01-red-stale-list.txt`; tree state `02-git-status-after-regen.txt`, `02-git-diff-stat.txt`.
16+
17+
MCP RED (published 2.1.2 contract): 13 `allOf` branches typed `["object","null"]` beside a closed object (a JSON `null` cannot satisfy them) and `mailbox_get_message` result.data with 18 properties, without the four fields (`04-mcp-analysis-vs-2.1.2.log`).
18+
19+
## Surface delta versus the published packages
20+
21+
Baselines are the published tags (`ts-mailbox-v2.0.1`, `ts-management-v2.0.1`, `go/v2.0.0`, `python-mailbox-v2.0.1`, `python-management-v2.0.0`, `ruby-mailbox/v2.0.1`, `ruby-management/v2.0.0`); PHP against `origin/main` `86d7174`, the split repositories' `v2.1.0`/`v2.1.1` already differing from it in unrelated files. Extraction = L17c's per-language type/field extractor (`05-surface-baseline-published.json`, `05-surface-tree.json`, delta `05-surface-delta-vs-published.txt`). Every number equals L17c's B3 prediction.
22+
23+
| Package (published) | Types removed / added · fields retyped / added | Detail | Verdict | Commit |
24+
| --- | --- | --- | --- | --- |
25+
| `@sendmux/mailbox` 2.0.1 | 0 / 0 · 3 / 8 | `MailboxMessage` + the four fields; `MailboxBatchGetItem.content`/`raw_body` and `MailboxSubmissionEnvelope.mail_from` carry `\| null` themselves while `MailboxMessageContent`, `MailboxRawBody`, `MailboxSubmissionEnvelopeAddress` drop it from their own definitions (compile-time only for code annotating a nullable field with those names); `& { [key: string]: unknown }` wrappers on the content/raw-body/thread-content `data` and `rcpt_to` gone; `from: MailboxAddress \| null`, `send_scope` unchanged | additive; no export removed | `fix(ts-mailbox)` `3c7ca7e` |
26+
| `@sendmux/management` 2.0.1 | 0 / 0 · 5 / 1 | `MailboxCreateResult.credential`, `ProviderQuotas.per_*` carry `\| null`; `MailboxCredential`, `ProviderQuotaRange` plain; `MailboxAppPasswordResult.credential: MailboxCredential`; `pending_request: SharedAmazonSesLimitRequest \| null` unchanged | additive; no export removed | `fix(ts-management)` `2f4a156` |
27+
| `sendmux.ai/go/v2` 2.0.0 | mailbox 25 / 0 · 4 / 4; management 18 / 0 · 1 / 8 (+8 union members renamed) | all 43 removed identifiers are declared by `deprecated_aliases.go` (`type Old = New`, 7 + 9 `NewNil…` constructors, 7 + 0 constants; `gofmt -l` clean); `MailboxMessage` + `MessageID`/`InReplyTo`/`References`/`ReplyTo`; residual below | aliases keep every old name compiling; residual is the owner's call | `fix(go)` `99bf0b1` |
28+
| `sendmux/mailbox` (Packagist 2.1.0; 3.0 unreleased) | 4 / 0 · 4 / 4 | `getData()` ×3 and `getRcptTo()` typed by the canonical models; four `class_alias` shims (`E_USER_DEPRECATED`, until 4.0); four `MailboxMessage` getters added | additive; old names load | `fix(php-mailbox)` `8161af0` |
29+
| `sendmux/management` (Packagist 2.1.1) | 2 / 0 · 1 / 0 | `getCredential(): MailboxCredential`; two `class_alias` shims (until 3.0) | additive; old names load | `fix(php-management)` `09a6983` |
30+
| `sendmux-mailbox` (PyPI 2.0.1) | 4 / 0 · 4 / 4 | `data` ×3 / `rcpt_to` typed by the canonical models; four names resolve through `__getattr__` (one `DeprecationWarning`, `is` the replacement, absent from `__all__`); `var_from: Optional[MailboxAddress]`, `raw_body` unchanged | additive; old names import | `fix(python-mailbox)` `88c40fa` |
31+
| `sendmux-management` (PyPI 2.0.0) | 2 / 0 · 2 / 0 | `credential: MailboxCredential`; per-day union member `ProviderQuotaRange`; two alias names; `pending_request` unchanged | additive; old names import | `fix(python-management)` `d726a98` |
32+
| `sendmux-mcp` (PyPI 2.1.2) | 54 tools, 0 added/removed/renamed, 0 `input_schema` changes, 10 `output_schema` changes | snapshot `b901839…` → `b42ce870…`; unsatisfiable compositions 13 → 0, duplicate null branches 0; `mailbox_get_message` result.data 22 props / 21 required with the four fields, `from` `anyOf: [object, null]` | fix | `fix(python-mcp)` `2b9b2a5` |
33+
| `sendmux-mailbox` (RubyGems 2.0.1) | 4 / 0 · 4 / 4 | same shape under `Sendmux::Mailbox::Generated`; four `deprecate_constant` aliases; `from`, `send_scope` unchanged | additive; old constants resolve | `fix(ruby-mailbox)` `ea211b7` |
34+
| `sendmux-management` (RubyGems 2.0.0) | 2 / 0 · 1 / 0 | `credential` typed `MailboxCredential`; `ProviderCreateBodyQuotasPerDay` accepts `ProviderQuotaRange` or `Integer`; two aliases; `pending_request` unchanged | additive; old constants resolve | `fix(ruby-management)` `ab2af52` |
35+
| `sendmux` crate (crates.io 0.5.0) | — | provenance pins `rust/operation-decisions.json:3`, `rust/src/generated/mod.rs:26` → `b42ce870…`; no Rust type models a changed component | none | `chore(rust)` `fcb524a` |
36+
37+
`grep -rl OrNull` over every generated tree: none. Every field typed by a canonical component keeps its type in every language; the only retypes replace a synthesised artefact class with the canonical class the artefact name now aliases.
38+
39+
### Go residual (apidiff, `06-go-apidiff.log`, classified in `06b-go-apidiff-classified.txt`)
40+
41+
`apidiff -incompatible` (old = `go/v2.0.0` export data, new = this tree, go1.27.1): mailbox 44 lines, all on identifiers `deprecated_aliases.go` still declares (the type identity moved to the canonical type, so apidiff reports the old name "changed" and its methods "removed"; every reference still compiles, see the consumer program); management 59 lines, 11 of the same nominal kind plus 48 on the eight `Provider{Create,Update}BodyQuotasPer*` unions, whose object member is now `ProviderQuotaRange` instead of `NilProvider…1` (field, `Get…`/`Is…`/`Set…`, `NewNil…1Provider…` constructor and the `Nil…1Provider…` type constant removed — six facets × eight unions). Not listed by apidiff but real: five struct fields drop the `Nil` wrapper of a removed artefact type — `MailboxMessageContentResponse.Data`, `MailboxRawBodyResponse.Data`, `MailboxThreadContentResponse.Data`, `MailboxSubmissionEnvelope.RcptTo`, `MailboxAppPasswordResult.Credential` — so `resp.Data.Value.Body` becomes `resp.Data.Body`. `go/sending`: no change. This is exactly the residual L17c and #277 documented as the owner's call between `fix(go)` 2.0.1 and a `/v3` major; the brief's expected outcome is 2.0.1, so the commit is typed `fix(go)` and carries no `BREAKING CHANGE` footer. Rewording `99bf0b1` to `fix(go)!` (and adding the `sendmux.ai/go/v3` module path first) is the alternative.
42+
43+
## Compatibility proofs (`.claude/artifacts/l17f/`)
44+
45+
- Go: a generated program referencing every alias, constructor and constant declared in both `deprecated_aliases.go` files builds and vets (`08-go-consumer-build.log`: mailbox 25 aliases / 7 constructors / 7 constants, management 18 / 9 / 0, `missing: []`, `extra: []`, "consumer build ok").
46+
- Python: `test_mailbox_deprecated_aliases.py` + `test_management_deprecated_aliases.py` pass on the alias branch, mypy clean on both packages + tests; importing each of the six names yields exactly one `DeprecationWarning` naming the replacement and the removal release, `is` the replacement, and is absent from `__all__` (`09-python-alias-proof.log`).
47+
- Ruby: alias suites 5 runs / 28 assertions + 3 runs / 11 assertions, 0 failures (`10-ruby-alias-proof.log`).
48+
- PHP: `DeprecatedModelAliasesTest` OK (6 tests, 39 assertions) with the shims loaded through PSR-4; phpstan level max `[OK] No errors` (`11-php-alias-proof.log`).
49+
- `MailboxMessage` deserialisation, every language, from two fixtures (`fixtures/`): a message with `from`, `reply_to` and the reply headers populated, and one with `from: null` and the four arrays empty. Both validate against the live document's `MailboxMessage` schema and against the MCP `mailbox_get_message` result.data schema (Draft 2020-12, format checks; the negative controls — `message_id` removed, `from` a string — fail) (`12-fixture-validation.log`). Python `MailboxMessage.from_json` + `to_dict` round trip (`15-proof-python.log`, run with `-W error::DeprecationWarning`); Ruby `build_from_hash` + `valid?` + `to_hash` (`15-proof-ruby.log`); PHP `ObjectSerializer::deserialize` + `valid()` + JSON round trip (`15-proof-php.log`); Go `UnmarshalJSON`/`json.Unmarshal` + `MarshalJSON` round trip, plus alias-typed variables holding canonical values (`15-proof-go.log`); TypeScript: the fixture typed as `MailboxMessage` with the four fields and `from: MailboxAddress | null` compiles under `tsc --strict` against the built `dist/` types, three negative assignments fail (`TS2322` ×2, `TS2339`), and the file runs under node (`15-proof-ts.log`).
50+
- MCP from the built package: wheel built with `python -m build`, installed into a fresh venv, `tools/list` through `fastmcp.Client` on `create_server` per surface (`14-mcp-tools-list.log`, `14-mcp-tools-list-built.json`): 54 tools, 0 added/removed/renamed, 0 `input_schema` changes versus the published 2.1.2 contract, 10 `output_schema` changes, scan `nullAllOf 0 / closedAllOf 0 / dupNull 0`, `mailbox_get_message` result.data 22 props / 21 required with the four fields; the listing equals the committed `mcp-contract.json` tool for tool (54 compared, 0 differing). The wheel embeds the `b42ce870…` snapshot and the committed contract (`e7a0e915…`).
51+
52+
## Commits
53+
54+
One commit per package path, in #276's order; 1–9 with `--no-verify` because `.githooks/pre-commit` runs `pnpm drift:check`, which regenerates every package and can only be green on the complete tree; 10 and 11 ran with the hook and left a clean tree (`17-commits-10-11-hooked.log`; the generated files' mtimes show the regeneration during both commits, and `git status` stayed empty), which also shows the regeneration is deterministic. No `Claude-Session:` trailers.
55+
56+
- `3c7ca7e` fix(ts-mailbox): regenerate mailbox types from the deployed v1.8.250 schema
57+
- `2f4a156` fix(ts-management): regenerate management types from the deployed v1.8.250 schema
58+
- `99bf0b1` fix(go): regenerate mailbox and management types from the deployed v1.8.250 schema
59+
- `8161af0` fix(php-mailbox): regenerate mailbox models from the deployed v1.8.250 schema
60+
- `09a6983` fix(php-management): regenerate management models from the deployed v1.8.250 schema
61+
- `88c40fa` fix(python-mailbox): regenerate mailbox models from the deployed v1.8.250 schema
62+
- `d726a98` fix(python-management): regenerate management models from the deployed v1.8.250 schema
63+
- `2b9b2a5` fix(python-mcp): publish the deployed v1.8.250 app schema and tool result schemas
64+
- `ea211b7` fix(ruby-mailbox): regenerate mailbox models from the deployed v1.8.250 schema
65+
- `ab2af52` fix(ruby-management): regenerate management models from the deployed v1.8.250 schema
66+
- `fcb524a` chore(rust): sync OpenAPI provenance with the deployed v1.8.250 app schema
67+
68+
No hand-written code, test or README references a removed or narrowed symbol; the only non-generated change is the two Rust provenance digests.
69+
70+
## Checks on the head
71+
72+
- Rust floor (CI `rust-floor` + `rust-latest-package` fmt): `cargo +1.82.0 test --locked --all-targets --all-features` 9 + 3 + 12 + 5 passed, `--doc` 7 passed, `cargo +stable fmt --check` clean (`18-rust-floor.log`).
73+
- Publication guard `GITHUB_TOKEN=<gh auth token> node scripts/publication-guard.mjs candidate --sha fcb524a86c8bb97f5793c53bb4230c3bec8b6df4`: "Live OpenAPI specs match committed snapshots." with app `b42ce870…` and sending `c1f82f9…` (`21-publication-guard-fcb524a86c8bb97f5793c53bb4230c3bec8b6df4.log`).
74+
- CI verify replica on `fcb524a` (`20-ci-verify-replica-fcb524a86c8bb97f5793c53bb4230c3bec8b6df4.log`): `OPENAPI_INPUT_DIR=<docs> GOTOOLCHAIN=go1.27.1 pnpm build && pnpm test:publication-guard && pnpm test:runtime-contracts` exit 0 across 114 pnpm scripts (08:54–09:05) — `drift:check` "Generated SDK package directories have no uncommitted drift", `verify:layout`, `verify:ts-public-api`, `verify:tree-shaking`, `check:cli`, `check:surface-coverage` (104 operations), `check:live-e2e`; Go `go test ./...` ok for `core`, `livee2e`, `mailbox`, `sending` (`management` and `sdk` have no test files), `go vet` clean, `golangci-lint@v2.13.2` "0 issues"; PHP `generate-php.test` green, phpstan "[OK] No errors", PHPUnit "OK (76 tests, 367 assertions)", split dry-runs verified (the `legacy_range` composer failure printed as JSON comes from `scripts/generate-php.test.mjs:102`, which asserts that range is rejected; the test passed); Python mypy "no issues found" (57 + 1 + 4 source files), pytest 163 native + 10 langchain (173 shared, 0 skipped); MCP mypy clean (30 files), pytest 158 passed, `sendmux_mcp.contract --check` and `check-mcp-artifacts` green, conformance required scenarios passed (the 8 non-scored failures are the `tasks-*` extension scenarios); Ruby `check-ruby` 11 + 1 + 14 + 5 + 5 + 3 runs / 248 assertions, 0 failures, 10 gem builds; `node --test` totals across the build 226 tests / 226 pass / 0 fail / 0 skipped; `test:publication-guard` and `test:runtime-contracts` green. Afterwards the tree held only bundler's `Gemfile.lock` rewrite (below), reverted.
75+
- CI on PR #279: code head `fcb524a` run 35543876472 (queued when this file was written); the final state of the code head and of this evidence head is recorded on the PR and in the L17f report. #277's own CI showed the reference roster — 37 passing checks plus `verify` red for the docs-snapshot root cause this PR resolves; `ownership-diagnostic` is a Windows-only skipped job.
76+
- Review: independent reviewer over the 11 commit messages, the per-commit path scope, the `OrNull` grep, the Rust provenance edit and this draft — 0 defects, 1 observation (it had no shell, so it matched commit subjects, stat lines, alias counts, hashes and every table number against the captured logs but could not read commit bodies). Closed mechanically: every backticked identifier in each commit body was searched in that commit's diff — 203 found; the 10 not in any diff are the messages' "unchanged" claims (`getLastMessage()`, `getSendScope()`, `ProviderQuotas::getPer*()`, `getPendingRequest()`, `pending_request`, `ProviderQuotas#per_*`) and the alias file name, each verified present at HEAD and line-identical to `origin/main` (`19-commit-message-identifier-check.log`). CodeRabbit and the settle sweep: recorded on the PR and in the L17f report.
77+
- Root `Gemfile.lock`: bundler rewrote `sendmux-mailbox (2.0.0)` → `(2.0.1)` during the Ruby checks (the gem version on `main` is already 2.0.1) — a pre-existing lag, reverted locally and parked.
78+
79+
## Release consequences
80+
81+
- release-please after a merge-commit merge with an empty body (a PR title in the merge body is applied to every package path — #257's `628b161` did that): `ts-mailbox` 2.0.2, `ts-management` 2.0.2, then the `node-workspace` cascade `ts-sdk` 2.0.2, `ts-cli` 1.7.2, `ts-ai-sdk` 0.5.2; `python-mailbox` 2.0.2, `python-management` 2.0.1, `python-mcp` 2.1.3 (the hosted MCP image is rebuilt after that publish); `ruby-mailbox` 2.0.2, `ruby-management` 2.0.1; `go` 2.0.1 (see the Go residual); `rust` unchanged apart from the provenance sync riding in the pending 0.5.1; PHP by hand — `sendmux/management` 2.1.2, `sendmux/mailbox` already heads to 3.0.0 on `main`. Umbrella upper bounds (`packages/python/sdk`, `packages/python/langchain`, `packages/ruby/sdk`) stay valid because nothing reaches 3.0.0.
82+
- Closes #277 and #276 as superseded once merged.
83+
84+
## Artifacts
85+
86+
`.claude/artifacts/l17f/` in the main checkout (machine-local): `00-pnpm-install.log`, `01-drift-check-red.log`, `01-red-stale-list.txt`, `02-*.{log,txt}`, `03-{ts,go,php,python,ruby}-generated.diff`, `03-mcp-contract.diff`, `04-mcp-analysis-vs-2.1.2.log`, `05-surface-*.{json,txt}`, `06-go-apidiff.log`, `06b-go-apidiff-classified.txt`, `07-*-setup.log`, `08-go-consumer-build.log` + `go-consumer/`, `09`–`11` alias proofs, `12-fixture-validation.log` + `fixtures/`, `13-ts-workspace-build.log`, `14-mcp-tools-list*.{log,json}`, `15-proof-{python,ruby,php,go,ts}.log` + `proofs/`, `16-commits-1-9.log`, `17-commits-10-11-hooked.log`, `18-rust-floor.log`, `20-ci-verify-replica-*.log`, `21-publication-guard-*.log`, `pids`.

0 commit comments

Comments
 (0)