Skip to content

Bind published response contracts for the 17 unbound client-SDK methods; retire the false PackageRollbackResponseSchema (#12038, ruling 1C·2C·3A·4A·5A) - #12848

Merged
os-sales merged 13 commits into
mainfrom
claude/issue-12038-sdk-response-contracts
Aug 28, 2026
Merged

Bind published response contracts for the 17 unbound client-SDK methods; retire the false PackageRollbackResponseSchema (#12038, ruling 1C·2C·3A·4A·5A)#12848
os-sales merged 13 commits into
mainfrom
claude/issue-12038-sdk-response-contracts

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes #12038

Implements the recorded five-part maintainer ruling (2026-08-27, comment 5434804846: 1C · 2C · 3A · 4A · 5A, verbatim 「其他接受」) for the 17 client-SDK methods that had no published response contract, on the factual basis of the accepted survey (comment 5431841395, 12 sections).

What landed, in the ruling's order

  1. 3A retirement FIRST (own commit, before any transcription references the family): PackageRollbackResponseSchema + the PackageApiContracts.rollbackPackage entry that bound the wrong-operation (VERSION-rollback) schema to the live COMMIT-rollback path are retired through the ADR-0087 discipline — RETIRED_DEFS_BY_MAJOR[18] entry api/PackageRollbackResponse, D3 semantic entry package-rollback-response-retired, manifest key and authorable-surface baseline lines released with their registered proof (the json-schema.manifest.json 的「deliberate removal」删行仍是纪律而非门禁 —— #4650 的同类洞,上移一层(整 schema 级) #4725 / authorable-surface 的 tombstone 门禁可被手编基线绕过 —— 删掉基线行就删掉了证据(#4638 / #4643 已两次这样过绿) #4650 gates verified this on every build), runtime namespace-probe pins in package-api.test.ts. PackageRollbackRequestSchema stays published as ruled (it now binds to no route — recorded in the file comment).
  2. 12 describe-only transcriptions into @objectstack/spec/api, each from the return type its producer already declares inline — no wire byte changes anywhere (the only handler-owned shape, listCommits' { commits } wrapper, is declared as the handler's, not moved).
  3. 1C: GetPublishedMetaItemResponseSchema is deliberately opaque (z.unknown()) — no discriminated union over the type registry.
  4. 2C: meta.migrateStored stays UNBOUND, documented at both of its ledger rows and in the SDK annotation (plus a pinned toBeAny() so a future sweep cannot "fix" it against the ruling).
  5. 4A: PackageExportManifestSchema = four fixed keys (id, name, version, label?) + open catch-all; freezes nothing.
  6. 5A: PackagePublishResultSchema (from /system) and the ResolvedBook family (authored beside its interfaces in system/book.zod.ts, type-identity-pinned) are re-exported into the /api namespace the ledger resolver searches — never a second copy.
  7. Ledger rows in both ledgers, per-surface truth: every filled row states which layer's envelope it describes. SDK: 16 of 17 methods bound; the four invented client-test mocks replaced with producer-true shapes; the A fifth client-SDK erasure spelling no grep in #8140's census counted: 38 methods with NO return annotation, typed from unwrapResponse< …any… > #11925 rollback negative guard flipped to guard the new truth; the survey-§8.2 unwrapResponse mis-unwrap hazard pinned (no bound payload may declare boolean success beside data; the export manifest's open half is covered via the plural vocabulary).
  8. Conformance for every named schema (Response bodies are never checked against the schemas that declare them — staged plan, not a repo-wide sweep #3877's no-row-without-conformance rule, the house handwritten-capture pattern; the book-tree suite drives the real resolveBookTree() producer).

Per-method table

# client method schema named (in @objectstack/spec/api) ledger row(s) filled surface / envelope
1 meta.getPublished GetPublishedMetaItemResponseSchema (opaque, 1C) runtime + rest dual-mounted: payload is the data of the dispatcher envelope on runtime, the whole bare body on REST
2 meta.listDrafts ListDraftsResponseSchema runtime + rest dual-mounted, as above
3 meta.migrateStored — deliberately UNBOUND (2C) runtime + rest rows carry the documented-unbound note dual-mounted
4 meta.getDiagnostics GetMetaDiagnosticsResponseSchema rest REST-only, bare body
5 meta.getReferences FindReferencesToMetaResponseSchema rest REST-only, bare body
6 meta.getBookTree ResolvedBookSchema (declared in system/book.zod.ts, 5A re-export) rest REST-only, bare body
7 meta.getAudit AuditMetaItemResponseSchema (existing, #11678 — bucket c) rest REST-only, bare body
8 meta.rollbackItem RollbackMetaItemResponseSchema rest REST-only, bare body
9 meta.diffItem DiffMetaItemResponseSchema rest REST-only, bare body
10 packages.publish PackagePublishResultSchema (from /system, 5A re-export) runtime dispatcher-only, payload is the envelope's data
11 packages.discardDrafts DiscardPackageDraftsResponseSchema runtime dispatcher-only, enveloped
12 packages.listCommits ListPackageCommitsResponseSchema (wrapper declared as the handler's) runtime dispatcher-only, enveloped
13 packages.revertCommit RevertPackageCommitResponseSchema runtime dispatcher-only, enveloped
14 packages.rollback RollbackToPackageCommitResponseSchema (3A — after the retirement) runtime dispatcher-only, enveloped
15 packages.export PackageExportManifestSchema (4A) runtime dispatcher-only, enveloped
16 packages.adoptOrphans ReassignOrphanedMetadataResponseSchema runtime dispatcher-only, enveloped
17 packages.duplicate DuplicatePackageResponseSchema (payload success is the operation's verdict — the objectui#6593 confusion now has a declared answer) runtime dispatcher-only, enveloped

Row counts: 20 rows total for the 17 methods (three meta routes are dual-mounted); 18 rows name a schema, migrateStored's 2 rows document the ruled unbinding.

Verification record

All commands ran through scripts/pm/os-verify-lock.sh; verdicts quoted from each gate's own output. Final head at verification: a4c926c (post-merge of origin/main at f907fbe).

  • Full suites (tree at the pre-alias commit 2f1bf89; only type-alias/doc-count edits and regenerated artifacts followed): spec Test Files 436 passed + the initially-failing scripts/file-description.test.ts re-run green after the header fix (its two corpus pins were tripped by the new file's docblock shape, not by behaviour); runtime Test Files 197 passed (197); rest Test Files 153 passed (153); client Test Files 26 passed (26). Typechecks: spec / runtime / rest / client all green (check:test-typecheck: OK … 0 error(s) for client; spec's own test layer compiles with its pre-existing shrink-only debt ledger untouched).
  • Re-run on the final head a4c926c: check:generated✓ All 14 generated artifacts are up to date.; the five touched spec test files → Test Files 5 passed (5); client typecheck → OK; the resolver + unwrap pin suites → Test Files 2 passed (2).
  • Gate union from node scripts/pm/dispatch-gates.mjs (no paths passed — change set derived from the merge base, 36 paths): 28 families derived; all run green. Two reds surfaced and were fixed in-PR: check:quick-reference-counts (the API reference tree grew to 31 pages) and check:spec-parsed-alias (13 new types now carry their ADR-0122 Parsed aliases). check:type-check-debt ran with the whole packages/* closure built, as its remedy requires. check:nul-bytes run additionally (any-edit rule).
  • Changeset gates: check-adr-0087-registration1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … registered package-rollback-response-retired; check-changeset-no-major → no major introduced (launch-window convention: the breaking removal ships as minor with the FROM → TO mapping in the changeset body); check-empty-changeset → green.
  • Reverse verification (one transcription pin, mutation proven on disk, restore by blob equality): renamed the declared restoredFromVersion key of RollbackMetaItemResponseSchema in protocol.zod.ts (injection grep count 1, original line count 0), ran the schema's conformance block → Tests 2 failed | 147 skipped, exit 1 — the predicted red. Restore proven by git hash-object equality with the HEAD blob (6ed7351…) and an empty git diff HEAD. A first attempt at this leg was VOID and is declared as such: the restore trap fired at that shell's exit before the test ran, so its green measured an unmutated tree; the leg was redone in one shell.
  • Both ledgers' resolver guard (route-ledger-response-schema.test.ts) exercises every filled row against the live @objectstack/spec/api namespace — green on the final head.

Landing note (for the PM)

This PR is serialized LAST in the landing relay behind PR #12718 and PR #12737 — both touch packages/spec/src/migrations/registry.ts, which this PR's 3A retirement also regenerates. origin/main was merged before opening (f907fbe; neither sibling had landed). Expect a regen-merge sync (scripts/pm/os-regen-merge.sh) at enqueue time; not attempting ready/enqueue from this seat.

Out of scope, recorded: PackageRollbackRequestSchema (the version-rollback REQUEST half) stays published per the ruling's exact scope; it binds to no route since the contracts entry left. objectui#6593 remains open in objectui and is not addressed here.


Generated by Claude Code

claude added 7 commits August 28, 2026 01:55
…ntract binding (#12038 3A)

The schema declared a VERSION rollback ({ success, restoredVersion?,
message? }) while PackageApiContracts.rollbackPackage bound it to the live
POST /api/v1/packages/:packageId/rollback path, which the dispatcher serves
with rollbackToPackageCommit — the ADR-0067 COMMIT rollback, a different
operation with a different result. Retired through the ADR-0087 discipline:
RETIRED_DEFS_BY_MAJOR entry api/PackageRollbackResponse, D3 semantic entry
package-rollback-response-retired, manifest key and authorable-surface
baseline lines released with their registered proof (#4725 / #4650), runtime
namespace-probe pins in package-api.test.ts. PackageRollbackRequestSchema
stays published as ruled; the true commit-rollback contract follows in the
next commit, after this retirement per the ruling's sequencing.

Co-authored-by: Claude <noreply@anthropic.com>
…e contracts (#12038)

The twelve describe-only transcriptions from the recorded ruling
(1C/2C/3A/4A/5A), each transcribed from the return type its producer already
declares inline: six meta.* payloads in api/protocol.zod.ts (listDrafts,
getMetaDiagnostics, findReferencesToMeta, rollbackMetaItem, diffMetaItem,
plus the ruling-1C opaque GetPublishedMetaItemResponseSchema), the package
lifecycle family in the new api/package-lifecycle.zod.ts (discardDrafts,
listCommits with the handler-minted commits wrapper, revertCommit, the true
commit-rollback RollbackToPackageCommitResponseSchema, the ruling-4A
fixed-keys-plus-catchall PackageExportManifestSchema, adoptOrphans,
duplicate), and the resolved book tree as Zod beside its interfaces in
system/book.zod.ts. Ruling 5A re-exports (PackagePublishResultSchema,
ResolvedBookSchema family) land in the /api namespace the ledger resolver
searches — never a second copy. meta.migrateStored stays unbound, documented
(ruling 2C). Generated artifacts regenerated by the spec build.

Co-authored-by: Claude <noreply@anthropic.com>
…s and bind the SDK

Fill responseSchema on the 18 boundable ledger rows across both ledgers,
each row stating which surface's envelope it describes (the dispatcher wraps
{ success, data }; the REST server answers the payload bare — the three
dual-mounted meta routes carry one note per surface). meta.migrateStored's
two rows document the ruling-2C deliberate unbinding instead. Conformance
suites land with the rows (spec api/protocol.test.ts, the new
api/package-lifecycle.test.ts, system/book.test.ts — the #3877
no-row-without-conformance rule, house capture pattern).

The client SDK binds 16 of the 17 methods to the published payload types
(migrateStored stays any, documented per 2C; getPublished binds to unknown
per 1C), replaces the four invented test mocks (getDiagnostics, getBookTree,
rollbackItem, diffItem) with producer-true shapes, flips the #11925 rollback
negative guard to guard the new commit-rollback truth, adds the
returnTypePrecisionPins12038 type-level pins, and pins the unwrapResponse
mis-unwrap hazard (survey §8.2) so no bound payload can ever declare both a
boolean success and a data key.

Co-authored-by: Claude <noreply@anthropic.com>
…changeset

api-surface, export-origins, reference docs and the strictness ledger
regenerated by check:generated --fix (only the artifacts it proved stale);
the ResolvedBook type family re-exported on /api so the generated page's
import line resolves. The changeset carries the breaking FROM-to-TO mapping
and the ADR-0087 disposition (registered package-rollback-response-retired).

Co-authored-by: Claude <noreply@anthropic.com>
… pins

The file docblock opened at heading level 1 (the #12249 demotion pin counts
exactly the 38 files that predate it) and split a code span across lines,
leaving a same-directory source path as plain text for the #6484 rule. Open
at level 2 and keep the span on one line; reference docs regenerated.

Co-authored-by: Claude <noreply@anthropic.com>
…nce total for the new contracts

The 13 new response types gain their Parsed aliases (check:spec-parsed-alias;
all are isomorphic — no defaults or transforms — but the paired-alias route
keeps the family uniform with its package-api siblings), the API Protocol
quick-reference heading's M rises to the 31 pages the reference tree now
publishes, and the spec artifacts are regenerated for the new type exports.

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/client, @objectstack/rest, @objectstack/runtime, @objectstack/spec, touching 115 documentable anchor(s). ⚠️ 11 changed file(s) yielded no anchor (packages/client/exported-any-returns.json, packages/spec/api-surface/api.json, packages/spec/api-surface/system.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via diffItem (sdk), getAudit (sdk), getBookTree (sdk), getDiagnostics (sdk), getReferences (sdk), meta.diffItem (sdk), meta.getAudit (sdk), meta.getBookTree (sdk), meta.getDiagnostics (sdk), meta.getReferences (sdk), meta.rollbackItem (sdk), rollbackItem (sdk))
  • content/docs/kernel/contracts/metadata-service.mdx (via getPublished (sdk), /meta/:type/:name/rollback (route))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via /meta/book/:name/tree (route))
  • content/docs/releases/v17.mdx (via /api/v1/meta/:type/:name/diff (route), /meta/:type/:name/diff (route))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 11 changed file(s) yielded no anchor (packages/client/exported-any-returns.json, packages/spec/api-surface/api.json, packages/spec/api-surface/system.json, …) — pages documenting those are invisible to this run
  • 7 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 134 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 277948f7aea27a6f246bc73d95d8359086cca250packageMentionDocs.

Which tree this was computed on

This run read content/docs from 53f76d0eb3e7317a815aff29ba1798469e3d1ee1 — the merge of head e539b0848961c7ec8173201c45eec84d7af5f36c into base 277948f7aea27a6f246bc73d95d8359086cca250, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 53f76d0eb3e7317a815aff29ba1798469e3d1ee1 && git checkout 53f76d0eb3e7317a815aff29ba1798469e3d1ee1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 277948f7aea27a6f246bc73d95d8359086cca250 e539b0848961c7ec8173201c45eec84d7af5f36c && git checkout -B drift-repro 277948f7aea27a6f246bc73d95d8359086cca250 && git merge --no-ff e539b0848961c7ec8173201c45eec84d7af5f36c

node scripts/docs-audit/affected-docs.mjs --json 277948f7aea27a6f246bc73d95d8359086cca250

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 277948f7aea27a6f246bc73d95d8359086cca250 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:system tests tooling labels Aug 28, 2026
claude added 3 commits August 28, 2026 03:20
check:llms-txt (the last step of the source-gates job) went red on the new
api/package-lifecycle.zod.ts: the hand-kept inventory declared 207 schemas
total and 29 under api. Re-read per the gate's own rule — number AND prose:
208 / 30, with Package Lifecycle added to the api row's key-schema sampler.
Gate re-derived green locally: 97 claims, 14 domains, 208 schemas.

Co-authored-by: Claude <noreply@anthropic.com>
… closed

check:exported-any-returns (Type Check - consumer gates) is exact in both
directions: the #11925 entries for the newly bound meta.* and packages.*
methods no longer resolve to any, so their ledger rows must be deleted.
meta.migrateStored's entry stays - it remains any by the 2C ruling. Verified
against the rebuilt dist: 'no NEW exported callable resolves to any: 317
callables reached, 47 ledgered site(s) still open'.

Co-authored-by: Claude <noreply@anthropic.com>
…eclares it

check:error-code-casing (Lint & Repo Gates) flagged the handwritten
conformance capture's code: 'item_locked' — an invented lowercase spelling.
The producer propagates deleteMetaItem's refusal code verbatim, and the
protection path sets 'ITEM_LOCKED' (ledger-declared, error-code-ledger.zod.ts
ADR-0010 §3.3), so the capture now carries the code the route really answers.
Gate and the conformance suite re-run green.

Co-authored-by: Claude <noreply@anthropic.com>
claude added 3 commits August 28, 2026 04:58
…12849)

os-regen-merge.sh step 4: gen:migration-registry rebuilds registry.ts with
BOTH step-18 populations (#12849's ui-form-view-predicate-features-root-refused
semantic entry beside this PR's retirement + semantic entries), the retirement's
manifest and authorable-surface deletions are re-applied over main's side (the
merge takes main's copy of os-regen artifacts by design; the #4725/#4650 proofs
re-verify against the new base), and the full chain regenerates green:
check:generated 14/14, check:llms-txt 208 schemas, the five touched spec
suites, client typecheck and the resolver + unwrap pin suites all pass on the
merged tree.

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/xl tests tooling

Projects

None yet

2 participants