Skip to content

Commit f3b41e8

Browse files
claude[bot]claude
andauthored
fix(changeset): declare the installed-package response type break on plain-donkeys-repeat (#18126)
Fixes #18057 ## The defect `.changeset/plain-donkeys-repeat.md` describes a compile-affecting change to two **published** response types in prose, and declared it in no metadata. Measured on the file at the branch point `ca788604`: | predicate | this file | lit control, `.changeset/15110-retired-element-node-refusal.md` | |:--|:--|:--| | `BREAKING` | **0** | 1 | | `adr-0087` | **0** | 1 | The control runs the same predicate over the same directory, so the zero is a real negative rather than a dead grep. `@objectstack/spec` is at **17.4.0** and the changeset is still pending, so this corrects an **unconsumed** declaration rather than published history. ⛔ The runtime claim in that body is **not disputed** and is untouched: the change is genuinely additive at runtime. The defect is the declaration alone, and nothing outside `.changeset/` is edited. ## What this changes — one file, two additions, level untouched - a `**BREAKING**` banner naming the type-level break on the two published response types in **both** directions: reading a manifest field off `ListInstalledPackagesResponseSchema` / `GetInstalledPackageResponseSchema` can stop compiling, and assigning a malformed manifest to either can start compiling where the old annotation refused it. It also states that the compiler — not the ledger — is the channel that reaches every affected consumer; - the mandatory ADR-0087 disposition marker, in the `not-required (no-migration-prescription)` form. ``` .changeset/plain-donkeys-repeat.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) ``` ## The level is decided mechanically, not chosen — and it stays `minor` ⚠️ Dispatch flagged a live dispute here (minor vs major, issue #18064) and instructed that if the level I need is the one that card is deadlocked on, I must stop. **It is not deadlocked.** #18064 carries state `closed` (`completed`, 2026-09-14T00:36Z) with a director-seat ruling that supersedes one of the two rulings and names the other as the convention. Its authority is already on `main`, in `docs/adr/0087-metadata-protocol-upgrade-contract.md`, section *Ratified: the pre-launch launch-window exemption*, amended 2026-09-13: > - **Pre-GA, a metadata-facing retirement or break ships `minor`**, carrying the `**BREAKING**` banner and its ADR-0087 disposition entry. The level is *not* the carrier of breaking-ness during the window — the banner and the disposition are, and they are mandatory precisely because the level says nothing. > - **An npm `major` is a planned act** — taken when the window closes and the queued conversions activate together — ⛔ never a side effect of one retirement card, however breaking that card is. So the level was never the missing half; the two mandatory carriers were. `scripts/check-changeset-no-major.mjs` independently refuses a `major` outright, which settles the same question from the enforcement side. This PR therefore touches no side of any maintainer decision. ## Was an ADR-0087 registration owed? Measured, not assumed Adding the `BREAKING` token is exactly what arms `check-adr-0087-registration` — before this change the gate had nothing to judge. Armed, it does **not** demand a registration: ``` ✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. .changeset/plain-donkeys-repeat.md [BREAKING] not-required (no-migration-prescription) ``` ⇒ ⭐ **no semantic entry, and `packages/spec/src/migrations/registry.ts` is not touched.** That keeps the smaller diff, and it keeps that file free for the card serialised behind this one. ## Why this disposition and not the semantically closer one The category that *describes* this class is `type-surface-only` — the gate's own header was written for "a published type-surface narrowing … a consumer's code can stop compiling". It is mechanically unavailable to a declaration-only correction, and that was measured rather than assumed. Claiming it on a throwaway commit returns: ``` `type-surface-only packages/spec/src/api/package-api.zod.ts#ListInstalledPackagesResponseParsed` [predicate 4: narrowed-from-erased] is FALSE: at the merge base the exported `type ListInstalledPackagesResponseParsed` alias was already CONCRETE (a `z.infer` projection of `ListInstalledPackagesResponseSchema`), not `any` / `unknown` / unannotated. ``` The generic parameter in that last line is spelled out rather than quoted literally, because the angle-bracket form does not survive GitHub's body sanitizer; nothing else in the excerpt is altered. Predicate 4 reads the named symbol at the merge base and at HEAD. A PR that corrects a declaration moves no source, so the two reads are identical by construction and the predicate can never hold. `registered` is refused for a different and stronger reason, stated in the gate's own header: writing a ledger id for a type-only move would put a prescription in the ledger that `objectstack migrate meta`, `spec-changes.json` and the upgrade guide cannot project. `no-migration-prescription` is both the honest and the mechanically-verified answer: nothing is removed or renamed, `ManifestSchema` is unchanged, no authorable key and no stored row shape moves, so the ledger has nothing to carry. The gate re-verifies it against the body's own prose on every run, and the body carries no prescription. ## Gates Derived for the actual diff with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, run at the final head `25a539f5`, each exit code captured **before** any pipe. **19 commands, all exit 0** — the 18 derived families plus `check-changeset-fixed.mjs`, which the derivation flagged because its roster lives under `.changeset`, the directory this diff is in. Reconciled with `--ran`: `18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3)`. Named in dispatch, all green: `check-adr-0087-registration`, `check-changeset-no-major`, `check-empty-changeset`, each with its `--self-test` (370 / 265 / 118 assertions). Not owed, and measured rather than skipped: - **no build, no package tests.** The diff touches no workspace package, so there is no dependency closure to build and no affected package to test. - **`pnpm lint`.** eslint's configured surface in `eslint.config.mjs` contains no markdown block at all — every `files:` entry targets `{ts,tsx,mts,cts,js,jsx,mjs,cjs}`. `eslint --format json` over the one changed file returns `errorCount: 0` with `File ignored because no matching configuration was supplied`. Since no config block matches markdown and no config file moves, this diff cannot move the verdict on any untouched file. ## Acceptance notes Out of scope, noted and deliberately not filed: - The `Check Changeset` job counts changesets with `--diff-filter=A`, so a PR that **corrects** an existing changeset is credited with zero and reds without the `skip-changeset` label. That is the label's designed purpose rather than a defect, and this PR is the successor that carries it. - `type-surface-only` is structurally unreachable from a declaration-only correction PR, for the predicate-4 reason measured above. By design — the gate judges the claim the diff makes — and the catch-all accepted this case, so no consumer is harmed. Successor that would hit it: none identified beyond this PR. `Clause-②: no` — this corrects a declaration; it widens no accept set and no public surface. Nothing outside `.changeset/` moves, so the reading holds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_014DBGjJFyndTj766aReCL2g --- _Generated by [Claude Code](https://claude.ai/code/session_014DBGjJFyndTj766aReCL2g)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent cca6991 commit f3b41e8

1 file changed

Lines changed: 15 additions & 0 deletions

File tree

‎.changeset/plain-donkeys-repeat.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@
44

55
Declare the ASSEMBLED manifest stage on the installed-package read API.
66

7+
**BREAKING** — a TYPE-level break on two PUBLISHED response types. It ships
8+
`minor` under the pre-GA launch-window convention (ADR-0087, *Ratified: the
9+
pre-launch launch-window exemption*), where the npm level is deliberately not the
10+
carrier of breaking-ness; this banner and the ADR-0087 disposition at the bottom
11+
are. Runtime is untouched and stays additive — every payload that parsed before
12+
still parses — so the affected party is a TypeScript consumer and the channel is
13+
the compiler at their own call site. Reading a manifest field off
14+
`ListInstalledPackagesResponseSchema` or `GetInstalledPackageResponseSchema` can
15+
stop compiling, and assigning a malformed manifest to either can start compiling
16+
where the old annotation refused it. Both directions are measured against the
17+
built `.d.ts` under *The STATIC gain is one-sided* below, which is also where the
18+
point-of-use reading lives.
19+
720
`GET /api/v1/packages` and `GET /api/v1/packages/:packageId` serve whatever a
821
package was installed with, and two stages reach that table through declared
922
doors: `POST /api/v1/packages` installs an authoring manifest (`manifest.objects`
@@ -54,3 +67,5 @@ before `parse` — the unix-socket short-circuit and the refusal of the
5467
filesystem-reading `?sslcert=` / `?sslkey=` / `?sslrootcert=` query parameters
5568
are kept; the "is this a URL `pg` can open" arm answers "no findings". Datasource
5669
publish is a server-side act, so that arm never legitimately ran in a browser.
70+
71+
<!-- adr-0087: not-required (no-migration-prescription) nothing is removed or renamed: `ManifestSchema` is unchanged, no authorable key moves and no stored row shape moves, so `objectstack migrate meta` has nothing to rewrite and the ledger cannot carry this change. The break is confined to the declared TypeScript surface of two response schemas, where the compiler reaches every affected consumer. -->

0 commit comments

Comments
 (0)