Skip to content

Commit 43460b9

Browse files
feat(spec)!: remove the three unmounted PackageApiContracts entries (upgrade / resolve-dependencies / upload) (#19937)
Fixes #19116 Clause-②: no Executes ruling 5793374037 on #19116 (batch #217 item 4, letter A, maintainer 「217 同意」): the three `PackageApiContracts` entries that named paths nothing mounts leave the contract map. ## What changed - **`packages/spec/src/api/package-api.zod.ts`** - `PackageApiContracts` loses `upgradePackage` (`POST /api/v1/packages/upgrade`), `resolveDependencies` (`POST /api/v1/packages/resolve-dependencies`) and `uploadArtifact` (`POST /api/v1/packages/upload`). A comment at the removal site records why, and carries ruling item 3: a package upgrade / dependency-resolution / upload entry is declared only in the same change that mounts its route. - The file-header `Endpoints` list loses the same three lines (this is the text the generated reference page prints). - Docblock-only corrections under the dispatch's Zone 2 exception, no shape change, no `.describe()` touched: 1. section header `// 5. Upgrade Package (POST /api/v1/packages/upgrade)` now reads `(request/response shapes — bound to no route, see §11)`; 2. `PackageUpgradeRequestSchema` JSDoc: the `@example POST /api/v1/packages/upgrade` request line is dropped (the example body stays) and one sentence says no route accepts it; 3. section header 6 (`resolve-dependencies`), same edit as 1; 4. `ResolveDependenciesRequestSchema` JSDoc, same edit as 2; 5. section header 7 (`upload`), same edit as 1; 6. `UploadArtifactRequestSchema` JSDoc, same edit as 2, and the `Content-Type: multipart/form-data` line goes with the request line, because it described the HTTP request of the unmounted route. - **`packages/spec/src/api/package-api.test.ts`**: the three `toBeDefined` / method / path pins on the removed keys are flipped into a new block that asserts the new fact: none of the three keys is present; no entry under any key is bound to any of the three paths (the whole map is swept, so a phantom cannot return under another key); the map holds exactly the four serving entries; and the six per-route schemas still resolve from the `api` namespace (scope of the ruling, see Acceptance notes). - **ADR-0087**: D3 semantic entry `package-api-contracts-unmounted-entries-retired` (`packages/spec/src/migrations/entries/semantic/18.package-api-contracts-unmounted-entries-retired.ts`), concatenated into `registry.ts` by `gen:migration-registry`. A contract-map entry is not metadata, so there is no source a D2 conversion could rewrite. - **Generated**: `content/docs/references/api/package-api.mdx` regenerated by `check:generated --fix` (only `check:docs` was proven stale): the three lines are gone. ⛔ Not hand-edited. `spec-changes.json` / `docs/protocol-upgrade-guide.md` were NOT stale (step-18 semantic entries are not projected yet), measured by `check:spec-changes` / `check:upgrade-guide` green. - **Changeset** `.changeset/19116-package-api-unmounted-entries-retired.md`: `@objectstack/spec` **minor** with a **BREAKING** banner, a FROM → TO table and the one-line fix, and the ADR-0087 disposition `registered package-api-contracts-unmounted-entries-retired`. Level chosen by the launch-window convention: `scripts/check-changeset-no-major.mjs` refuses `major` outside pre-mode (`.changeset/pre.json` absent), and ruling 5770445652 on #19611 (batch #210 item 3) ratified `minor` + BREAKING banner + ADR-0087 marker as the carrier for a breaking spec retirement. Rule text read: AGENTS.md Post-Task Checklist step 3 (breaking changesets carry FROM → TO + one-line fix; exactly one ADR-0087 marker; the changeset carries the PR's `Clause-②` line). Runtime behaviour is unchanged: nothing mounted the three paths or built a route from the entries. ## Premise re-taken at `fdeeea0cc9` (this branch's base) - The three path strings occur in exactly 3 files (`package-api.zod.ts`, `package-api.test.ts`, `content/docs/references/api/package-api.mdx`), `.map` and `CHANGELOG.md` excluded; control `/api/v1/packages/publish`: 27 files. - `upgradePackage` occurs only in the two spec files. `resolveDependencies` / `uploadArtifact` also occur in `packages/core` kernels and `packages/spec/src/contracts/package-service.ts`: those are the kernel's plugin sort and the `IPackageService` methods, a different surface, not readers of the map. - No reader of `PackageApiContracts` outside `package-api.test.ts` (non-generated, non-changeset); no route table, client method, SDK/codegen, MCP tool or doc generator iterates it. `handlePackagesRequest` (`packages/runtime/src/domains/packages.ts`) has no single-segment `POST` branch, so the three paths fall through (static read; the dynamic `handled=false` reading is the one recorded on #18604). - objectui at the pin `62597c58` (the sibling checkout's HEAD equals `.objectui-sha`): 0 files for `PackageApiContracts`, each of the three keys, each of the three paths; controls `GetMetaItemLayeredResponseSchema` 14 files, `/api/v1/packages` 33 files. ## Verification (all at head `3c4da3412`) - `pnpm --filter @objectstack/spec build` (under the verify lock) → exit 0. - `pnpm --filter @objectstack/spec test` → 527 files, 15507 passed / 1 todo, exit 0. `test:repo` → 35 files, 602 passed, exit 0. `typecheck` (tsc + scripts + test-typecheck) → exit 0. - Cross-package suites whose turbo inputs cover the diff: `@objectstack/core` / `types` / `runtime` / `objectql` / `rest` `test:repo` → 3/1/2/1/1 files, all passed (runtime and objectql after building their dependency closure; the first runtime attempt refused on unbuilt dependencies and is not counted). - `@objectstack/downstream-contract` `test` → 2 files passed, 1 file NOT MEASURED: `consumer-specifier-ledger.test.ts` refuses because `@objectstack/cli` is not built; it measures published export specifiers, which this diff does not move. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` → 111 commands, each run with its exit code recorded; `--ran` reconciliation: **111 derived, 109 run, 2 NOT MEASURED, 0 UNRUN**. - NOT MEASURED, reason PREREQUISITE NOT MET (exit 3, a whole-tree build): `pnpm check:dual-build-cjs-loads`, `pnpm check:type-check-debt`. Both are in CI's required jobs. - Three families first exited 3 (`@objectstack/lint` `check:doc-formula-expressions` / `check:doc-security-posture`, `@objectstack/spec` `check:skill-examples`); after building their named prerequisites (`turbo run build` for formula, lint, client-react, 34 tasks) all three exited 0. - `check:adr-0087-registration`: `1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … registered package-api-contracts-unmounted-entries-retired (new here …)`. - The 11 declared wide-population families (CI-owned) were also run, because they walk `packages/**/*.ts` and this diff adds prose to `registry.ts`: all exit 0. - Lint, narrowed and proven: `eslint --no-inline-config --format json` over the 4 changed `.ts` files → 4 files, 0 errors, 0 warnings. Population read from eslint's own config: `--print-config` resolves for all four; the changeset `.md` and the `.mdx` are outside it (`File ignored because no matching configuration was supplied`). Invariance: `eslint.config.mjs` enables no type-aware linting (the resolved `parserOptions` carry only `ecmaVersion` / `sourceType`, no `project` / `projectService`), so this diff cannot move a verdict on an untouched file. - Reverse verification against the rebuilt `dist/api/index.d.mts`: a probe reading `PackageApiContracts.upgradePackage` / `.resolveDependencies` / `.uploadArtifact` → `TS2339` on each (tsc exit 2); the control leg reading `.installPackage.path` alone → exit 0. Direction observed: red, as expected. - Ablation of the new pins (`scripts/ablation-replace.mjs`, wrap mode, under the verify lock): re-planting an `upgradePackage` entry (anchor 1 → 0, blob `d2f65040` → `9173ef90`) turned 3 of the 4 new tests red (`expected true to be false`, `expected [ 'upgradePackage' ] to deeply equal []`, `expected [ Array(5) ] to deeply equal [ Array(4) ]`); the schemas-still-published test stayed green, as it should. Restore proven: blob back to HEAD `d2f65040`, `git diff HEAD` empty. The restored leg: `package-api.test.ts` 78/78 passed. ⚠️ The first ablation attempt was a no-op (its replacement contained its anchor, so the tool refused before running anything and restored); it is not counted. ## Acceptance notes - **Per-route schemas (sections 5–7) are NOT deleted** — the ruling names the map entries only. Consumers measured at `fdeeea0cc9`, tests counted separately: | export | non-test consumers (this repo) | tests | objectui pin | | --- | --- | --- | --- | | `PackageUpgradeRequestSchema` / `PackageUpgradeResponseSchema` | 0 | 1 (`package-api.test.ts`) each | 0 | | `ResolveDependenciesRequestSchema` / `ResolveDependenciesResponseSchema` | 0 | 1 each | 0 | | `UploadArtifactRequestSchema` / `UploadArtifactResponseSchema` | 0 | 1 each | 0 | | the 12 matching `…Request` / `…Response` / `…Parsed` types | 0 | 0 | 0 | "Non-test consumers" excludes the generated artefacts that merely record the exports (`api-surface/`, `export-origins/`, `declaration-map/`, `authorable-surface/`, `json-schema.manifest/`, `dropped-refinements.baseline.json`, `content/docs/references/**`) and comment mentions in two retired-`mergeStrategy` migration entries. Imports that only these sections use in this file: `UpgradePlanSchema` (other consumers: its own `kernel/package-upgrade.zod.ts` and test) and `PackageArtifactSchema` (also used by `system/environment-artifact.zod.ts`). A follow-up can decide their fate on these numbers. - The `PackageApiContracts` docblock still says the map is "Used for generating SDKs, documentation, and route registration"; nothing in this repo registers routes or generates an SDK from it (0 readers outside its own tests). Noted, not filed; carrier: whoever takes the sections 5–7 decision. - `PackageApiErrorCode` still lists `upgrade_failed` and `upload_failed`; no emitter outside `package-api.zod.ts` and its test. Noted, not filed; same carrier. - The weight-carrying pin is package-local (the map's keys and paths). The page's absence of the three routes is held by `check:docs`, because the page is a generated projection of the file-header docblock this PR edits. A tree-scoped absence pin would need `scripts/cross-package-test-inputs.mjs` and `turbo.json` edits outside the claimed file surface, so it was not added. - No labels were written: the dispatch's write budget names none, and `skip-changeset` does not apply (a changeset is present). ## Files `.changeset/19116-package-api-unmounted-entries-retired.md` · `content/docs/references/api/package-api.mdx` · `packages/spec/src/api/package-api.test.ts` · `packages/spec/src/api/package-api.zod.ts` · `packages/spec/src/migrations/entries/semantic/18.package-api-contracts-unmounted-entries-retired.ts` · `packages/spec/src/migrations/registry.ts` — +208 / −43. Dispatched by PM seat `domain:spec#4` (session `session_019c3Hi6ZMU1p6m6aA6Bz45d`), claim comment 5804927566. --- _Generated by [Claude Code](https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 24d1a2d commit 43460b9

6 files changed

Lines changed: 208 additions & 43 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
**BREAKING** — `PackageApiContracts` loses its three entries that named routes nothing serves: `upgradePackage`, `resolveDependencies` and `uploadArtifact` (#19116).
6+
7+
A `major`-class change, recorded as `minor` under the launch-window convention. Maintainer ruling 2026-09-23, director seat decision batch #217 item 4, letter A, 「217 同意」; ADR-0049 enforce-or-remove.
8+
9+
**Why.** Each entry bound a path the composed runtime mounts nowhere — `POST /api/v1/packages/upgrade`, `POST /api/v1/packages/resolve-dependencies` and `POST /api/v1/packages/upload`. The package dispatcher has no route for any of them and `@objectstack/rest` mounts only `/packages/publish` under `/packages`, so a request to any of the three was never answered, while the generated API reference printed all three as live endpoints. Unlike `installPackage`, which was rebound onto the serving `POST /api/v1/packages`, there was no serving door to rebind these onto, and mounting three new capabilities nobody has asked for was ruled out.
10+
11+
### FROM → TO
12+
13+
| removed | what to write instead |
14+
| --- | --- |
15+
| `PackageApiContracts.upgradePackage` (`POST /api/v1/packages/upgrade`) | nothing — delete the read and any URL built from it. No route serves a package upgrade. |
16+
| `PackageApiContracts.resolveDependencies` (`POST /api/v1/packages/resolve-dependencies`) | nothing — delete the read and any URL built from it. No route serves dependency resolution. |
17+
| `PackageApiContracts.uploadArtifact` (`POST /api/v1/packages/upload`) | nothing — delete the read and any URL built from it. No route serves an artifact upload. |
18+
19+
**The one-line fix: delete every read of the three keys, and every request to the three paths.** The compiler finds the reads (`TS2339: Property 'upgradePackage' does not exist`); a hard-coded path has to be searched for. No behaviour is lost — none of those requests was ever answered.
20+
21+
**What stays.** The four entries whose doors serve — `listPackages`, `getPackage`, `installPackage`, `uninstallPackage` — are unchanged. The per-route request/response schemas (`PackageUpgradeRequestSchema`, `PackageUpgradeResponseSchema`, `ResolveDependenciesRequestSchema`, `ResolveDependenciesResponseSchema`, `UploadArtifactRequestSchema`, `UploadArtifactResponseSchema`, with their types) stay published, now bound to no route; their docblocks no longer name a route. If the platform later serves a package upgrade, dependency-resolution or upload route, its contract entry is declared in the same change that mounts it.
22+
23+
⚠️ Runtime behaviour is deliberately **unchanged**: nothing ever mounted the three paths or built a route, client or SDK method from the entries, so every request answers exactly as before. The removal retracts a false claim, not a capability. **No deprecation window** (maintainer 2026-08-27: 「项目在创业阶段,用户也很少,短期不考虑渐进」).
24+
25+
⚠️ **The out-of-repo consumer population is NOT MEASURED.** Inside this repository the three paths occurred only in the declaring file, its unit test and the generated reference page, and the pinned objectui checkout names none of the keys, none of the paths and not `PackageApiContracts`; `@objectstack/spec` is published, so readers elsewhere were not measured.
26+
27+
The ADR-0087 D3 semantic entry `package-api-contracts-unmounted-entries-retired` carries the judgement: a contract-map entry is not metadata, so there is no source for a D2 conversion to rewrite.
28+
29+
Clause-②: no
30+
31+
<!-- adr-0087: registered package-api-contracts-unmounted-entries-retired -->

‎content/docs/references/api/package-api.mdx‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ Base path: /api/v1/packages
1414
**Endpoints**
1515
```
1616
POST /api/v1/packages — Install a package
17-
POST /api/v1/packages/upgrade — Upgrade a package
18-
POST /api/v1/packages/resolve-dependencies — Resolve dependencies
19-
POST /api/v1/packages/upload — Upload an artifact
2017
GET /api/v1/packages — List installed packages
2118
GET /api/v1/packages/:packageId — Get package details
2219
POST /api/v1/packages/:packageId/rollback — Rollback a package

‎packages/spec/src/api/package-api.test.ts‎

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -505,19 +505,13 @@ describe('PackageApiContracts', () => {
505505
expect(PackageApiContracts.listPackages).toBeDefined();
506506
expect(PackageApiContracts.getPackage).toBeDefined();
507507
expect(PackageApiContracts.installPackage).toBeDefined();
508-
expect(PackageApiContracts.upgradePackage).toBeDefined();
509-
expect(PackageApiContracts.resolveDependencies).toBeDefined();
510-
expect(PackageApiContracts.uploadArtifact).toBeDefined();
511508
expect(PackageApiContracts.uninstallPackage).toBeDefined();
512509
});
513510

514511
it('should have correct HTTP methods', () => {
515512
expect(PackageApiContracts.listPackages.method).toBe('GET');
516513
expect(PackageApiContracts.getPackage.method).toBe('GET');
517514
expect(PackageApiContracts.installPackage.method).toBe('POST');
518-
expect(PackageApiContracts.upgradePackage.method).toBe('POST');
519-
expect(PackageApiContracts.resolveDependencies.method).toBe('POST');
520-
expect(PackageApiContracts.uploadArtifact.method).toBe('POST');
521515
expect(PackageApiContracts.uninstallPackage.method).toBe('DELETE');
522516
});
523517

@@ -530,9 +524,6 @@ describe('PackageApiContracts', () => {
530524
// distinguished from `listPackages` by method, not by path.
531525
expect(PackageApiContracts.installPackage.path).toBe('/api/v1/packages');
532526
expect(PackageApiContracts.installPackage.path).not.toContain('install');
533-
expect(PackageApiContracts.upgradePackage.path).toBe('/api/v1/packages/upgrade');
534-
expect(PackageApiContracts.resolveDependencies.path).toBe('/api/v1/packages/resolve-dependencies');
535-
expect(PackageApiContracts.uploadArtifact.path).toBe('/api/v1/packages/upload');
536527
expect(PackageApiContracts.uninstallPackage.path).toBe('/api/v1/packages/:packageId');
537528
});
538529

@@ -546,6 +537,61 @@ describe('PackageApiContracts', () => {
546537
});
547538
});
548539

540+
// ==========================================
541+
// Unmounted contract-map entries removed (#19116)
542+
// ==========================================
543+
544+
describe('the three unmounted `PackageApiContracts` entries are removed (#19116)', () => {
545+
// The paths the removed entries bound. Nothing in the composed runtime
546+
// mounts any of them, and no serving door existed to rebind them onto.
547+
const UNMOUNTED_PATHS = [
548+
'/api/v1/packages/upgrade',
549+
'/api/v1/packages/resolve-dependencies',
550+
'/api/v1/packages/upload',
551+
];
552+
553+
it('no longer carries the `upgradePackage`, `resolveDependencies` or `uploadArtifact` keys', () => {
554+
for (const key of ['upgradePackage', 'resolveDependencies', 'uploadArtifact']) {
555+
expect(Object.prototype.hasOwnProperty.call(PackageApiContracts, key)).toBe(false);
556+
}
557+
});
558+
559+
it('binds no entry, under any key, to one of the three unmounted paths', () => {
560+
// The whole map, so a phantom cannot come back under a different key.
561+
const claimants = Object.entries(PackageApiContracts)
562+
.filter(([, c]) => UNMOUNTED_PATHS.includes(c.path))
563+
.map(([key]) => key);
564+
expect(claimants).toEqual([]);
565+
});
566+
567+
it('keeps exactly the four entries whose doors serve', () => {
568+
// Anti-vacuity for the two absence pins above, and the declare-with-mount
569+
// rule made visible: a new entry lands here only with the mount it names.
570+
expect(Object.keys(PackageApiContracts).sort()).toEqual([
571+
'getPackage',
572+
'installPackage',
573+
'listPackages',
574+
'uninstallPackage',
575+
]);
576+
});
577+
578+
it('leaves the request/response schemas published, bound to no route', async () => {
579+
// The ruling removed the map entries only; the per-route schemas are a
580+
// separate question and still resolve from the namespace.
581+
const ns = (await import('./index')) as unknown as Record<string, unknown>;
582+
for (const name of [
583+
'PackageUpgradeRequestSchema',
584+
'PackageUpgradeResponseSchema',
585+
'ResolveDependenciesRequestSchema',
586+
'ResolveDependenciesResponseSchema',
587+
'UploadArtifactRequestSchema',
588+
'UploadArtifactResponseSchema',
589+
]) {
590+
expect(Object.prototype.hasOwnProperty.call(ns, name)).toBe(true);
591+
}
592+
});
593+
});
594+
549595
// ==========================================
550596
// package-rollback-response retirement (#12038 3A)
551597
// ==========================================

‎packages/spec/src/api/package-api.zod.ts‎

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ import { RecordStagePackageBodySchema } from '../stack.zod';
2121
* @example Endpoints
2222
* ```
2323
* POST /api/v1/packages — Install a package
24-
* POST /api/v1/packages/upgrade — Upgrade a package
25-
* POST /api/v1/packages/resolve-dependencies — Resolve dependencies
26-
* POST /api/v1/packages/upload — Upload an artifact
2724
* GET /api/v1/packages — List installed packages
2825
* GET /api/v1/packages/:packageId — Get package details
2926
* POST /api/v1/packages/:packageId/rollback — Rollback a package
@@ -563,13 +560,14 @@ export type PackageInstallResponse = z.input<typeof PackageInstallResponseSchema
563560
export type PackageInstallResponseParsed = z.infer<typeof PackageInstallResponseSchema>;
564561

565562
// ==========================================
566-
// 5. Upgrade Package (POST /api/v1/packages/upgrade)
563+
// 5. Upgrade Package (request/response shapes — bound to no route, see §11)
567564
// ==========================================
568565

569566
/**
570-
* Request body for upgrading a package.
567+
* Request body for upgrading a package. No route accepts it — see the note in
568+
* `PackageApiContracts`.
571569
*
572-
* @example POST /api/v1/packages/upgrade
570+
* @example
573571
* { packageId: 'com.acme.crm', targetVersion: '2.0.0', createSnapshot: true }
574572
*/
575573
export const PackageUpgradeRequestSchema = lazySchema(() => z.object({
@@ -629,13 +627,14 @@ export type PackageUpgradeResponse = z.input<typeof PackageUpgradeResponseSchema
629627
export type PackageUpgradeResponseParsed = z.infer<typeof PackageUpgradeResponseSchema>;
630628

631629
// ==========================================
632-
// 6. Resolve Dependencies (POST /api/v1/packages/resolve-dependencies)
630+
// 6. Resolve Dependencies (request/response shapes — bound to no route, see §11)
633631
// ==========================================
634632

635633
/**
636-
* Request body for resolving package dependencies.
634+
* Request body for resolving package dependencies. No route accepts it — see the
635+
* note in `PackageApiContracts`.
637636
*
638-
* @example POST /api/v1/packages/resolve-dependencies
637+
* @example
639638
* { manifest: {...}, platformVersion: '3.2.0' }
640639
*/
641640
export const ResolveDependenciesRequestSchema = lazySchema(() => z.object({
@@ -661,14 +660,14 @@ export type ResolveDependenciesResponse = z.input<typeof ResolveDependenciesResp
661660
export type ResolveDependenciesResponseParsed = z.infer<typeof ResolveDependenciesResponseSchema>;
662661

663662
// ==========================================
664-
// 7. Upload Artifact (POST /api/v1/packages/upload)
663+
// 7. Upload Artifact (request/response shapes — bound to no route, see §11)
665664
// ==========================================
666665

667666
/**
668-
* Request body for uploading a package artifact.
667+
* Request body for uploading a package artifact. No route accepts it — see the
668+
* note in `PackageApiContracts`.
669669
*
670-
* @example POST /api/v1/packages/upload
671-
* Content-Type: multipart/form-data
670+
* @example
672671
* { artifact: <metadata>, file: <binary> }
673672
*/
674673
export const UploadArtifactRequestSchema = lazySchema(() => z.object({
@@ -851,24 +850,16 @@ export const PackageApiContracts = {
851850
input: PackageInstallBodySchema,
852851
output: PackageInstallResponseSchema,
853852
},
854-
upgradePackage: {
855-
method: 'POST' as const,
856-
path: '/api/v1/packages/upgrade',
857-
input: PackageUpgradeRequestSchema,
858-
output: PackageUpgradeResponseSchema,
859-
},
860-
resolveDependencies: {
861-
method: 'POST' as const,
862-
path: '/api/v1/packages/resolve-dependencies',
863-
input: ResolveDependenciesRequestSchema,
864-
output: ResolveDependenciesResponseSchema,
865-
},
866-
uploadArtifact: {
867-
method: 'POST' as const,
868-
path: '/api/v1/packages/upload',
869-
input: UploadArtifactRequestSchema,
870-
output: UploadArtifactResponseSchema,
871-
},
853+
// `upgradePackage`, `resolveDependencies` and `uploadArtifact` REMOVED
854+
// (#19116, ADR-0087 semantic entry
855+
// `package-api-contracts-unmounted-entries-retired`) — they bound
856+
// `POST /api/v1/packages/upgrade`, `/resolve-dependencies` and `/upload`,
857+
// three paths the composed runtime mounts nowhere (the dispatcher answers
858+
// `handled=false`; `packages/rest` mounts only `/packages/publish`), and no
859+
// serving door existed to rebind them onto as `installPackage` was. Their
860+
// request/response schemas (sections 5–7) stay published, bound to no route.
861+
// ⛔ A package upgrade / dependency-resolution / upload route is declared
862+
// here only in the same change that MOUNTS it — never ahead of its door.
872863
// `rollbackPackage` RETIRED (#12038 3A) — it bound the version-rollback
873864
// schemas to the live `/api/v1/packages/:packageId/rollback` path, which
874865
// actually serves the ADR-0067 COMMIT rollback (`rollbackToPackageCommit`).
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import type { SemanticMigration } from '../../types.js';
4+
5+
export const entry: SemanticMigration = {
6+
id: 'package-api-contracts-unmounted-entries-retired',
7+
// No backticks in `surface` — build-upgrade-guide.ts renders it inside a
8+
// code span.
9+
surface:
10+
'api.PackageApiContracts.upgradePackage / api.PackageApiContracts.resolveDependencies / '
11+
+ 'api.PackageApiContracts.uploadArtifact — the three contract-map entries that bound '
12+
+ 'POST /api/v1/packages/upgrade, POST /api/v1/packages/resolve-dependencies and '
13+
+ 'POST /api/v1/packages/upload',
14+
replacement:
15+
'nothing — no route serves any of the three paths, so there is no entry to read instead. '
16+
+ 'Delete every read of `PackageApiContracts.upgradePackage`, '
17+
+ '`PackageApiContracts.resolveDependencies` and `PackageApiContracts.uploadArtifact`, and every '
18+
+ 'URL built from them or from the three hard-coded paths: a request to any of them was never '
19+
+ 'answered. The per-route request/response schemas (`PackageUpgradeRequestSchema`, '
20+
+ '`PackageUpgradeResponseSchema`, `ResolveDependenciesRequestSchema`, '
21+
+ '`ResolveDependenciesResponseSchema`, `UploadArtifactRequestSchema`, '
22+
+ '`UploadArtifactResponseSchema`) stay published, bound to no route. The four surviving entries '
23+
+ '(`listPackages`, `getPackage`, `installPackage`, `uninstallPackage`) are unchanged. If the '
24+
+ 'platform later serves a package upgrade, dependency-resolution or upload route, its entry '
25+
+ 'arrives in the same change that mounts it.',
26+
reason:
27+
'Maintainer ruling 2026-09-23 on #19116 (director seat, decision batch #217 item 4, letter A, '
28+
+ '「217 同意」). The contract map is the declaration SDKs, codegen and AI clients are entitled to '
29+
+ 'trust, and three of its seven entries named paths the composed runtime mounts nowhere: the '
30+
+ 'package dispatcher has no branch for a single-segment POST under /packages and '
31+
+ '`@objectstack/rest` mounts only /packages/publish there, so all three answered handled=false '
32+
+ 'while the four surviving entries answer 200/201 (measured on one HttpDispatcher over a real '
33+
+ 'SchemaRegistry, #18604) — and the generated reference page printed '
34+
+ 'all three as live endpoints. Unlike `installPackage` (#18058, rebound onto the serving '
35+
+ 'POST /api/v1/packages), no serving door existed to rebind them onto, and mounting three '
36+
+ 'capabilities with zero measured pull was ruled out (ADR-0049 enforce-or-remove). Zero '
37+
+ 'consumers measured at the retiring PR\'s base: across this repository the three paths occur '
38+
+ 'only in the declaring file, its unit test and the generated page, and the pinned objectui '
39+
+ 'checkout names none of the three keys, none of the paths and not `PackageApiContracts` '
40+
+ 'itself. A contract-map entry is not metadata — nothing authors, stores or parses it — so '
41+
+ 'there is no source a D2 conversion could rewrite, and the removal is recorded here.',
42+
acceptanceCriteria:
43+
'No code reads `PackageApiContracts.upgradePackage`, `.resolveDependencies` or '
44+
+ '`.uploadArtifact` from `@objectstack/spec` or `@objectstack/spec/api` — each is a TS2339 '
45+
+ 'property error after upgrade, and at runtime the key is absent (pinned in '
46+
+ 'api/package-api.test.ts together with the rule that no surviving entry is bound to any of the '
47+
+ 'three paths). No client, route table or generated artefact of yours still names '
48+
+ 'POST /api/v1/packages/upgrade, /resolve-dependencies or /upload. No metadata document needs '
49+
+ 'editing. ⚠️ Runtime behaviour is deliberately UNCHANGED: nothing ever mounted the three paths '
50+
+ 'or built a route from the entries, so every request answers exactly as before — the removal '
51+
+ 'retracts a false claim, not a capability.',
52+
};

0 commit comments

Comments
 (0)