Repository navigation
Commit d0bb78e
fix(metadata): revertPackage finds a code-shipped package's members by the _packageId stamp (#22132)
Fixes #22113
Clause-②: no
## What changes
ADR-0070 D2 makes a code or installed package read-only. The two
package-wide doors now refuse one with the door's existing `422
WRITABLE_PACKAGE_REQUIRED`, through the same `requireWritablePackage`
predicate that `PATCH /packages/:id/disable` and `DELETE /packages/:id`
already ask. This follows triage's answer on the card: Q1 is A, Q2 is B.
- **`POST /api/v1/packages/:id/publish`** refuses a non-writable package
before `MetadataManager.publishPackage` runs.
- **`POST /api/v1/packages/:id/revert`** refuses a non-writable package
after the protocol's stored-row answer (`revertStoredPackage`). A stored
row bound to a code package, such as an organization overlay draft,
keeps the protocol's answer.
- **`MetadataManager.publishPackage` and `revertPackage`** find a
package's members through one private helper, `collectPackageMembers`.
It reads `packageId`, `package` and the private `_packageId` stamp.
- The stamp has two producers: the artifact loader writes it through
`applyProtection`, and the ObjectQL object bridge copies
`getAllObjects()`'s owner tag onto every object it registers.
- `publishPackage` takes the helper only because the publish door now
refuses a read-only package in front of it. `MetadataManager` has no
notion of package kind. Before the guard existed, the helper in
`publishPackage` was measured publishing a platform package's objects
(`0edb299`), which is why `cffca05` held it back. The only in-repo
caller of `publishPackage` is this door.
## Measured at the door
These readings come from an `objectstack dev --fresh` boot of
`examples/app-showcase`. Base is `51290bc`; head is `434d074`.
| door | base | head |
|---|---|---|
| revert, all 26 packages `GET /packages` lists (`com.objectstack.setup`
and 6 other registry-only packages, 18 platform packages that ship
objects, `com.example.showcase`) | 25 × 404 "No metadata items found";
the showcase 409 "has never been published" | 26 × 422
`WRITABLE_PACKAGE_REQUIRED` |
| publish `com.objectstack.setup`, `…platform-objects`, `…service.job` |
200, `success: false`, "No metadata items found" | 422
`WRITABLE_PACKAGE_REQUIRED` |
| publish `com.example.showcase` | 200, `success: true`,
`itemsPublished: 2` (writes onto two read-only capabilities) | 422
`WRITABLE_PACKAGE_REQUIRED` |
| unknown id `com.example.no_such_package` | revert 404; publish 200,
`success: false` | unchanged |
| writable package `com.example.repairs` (created through `POST
/packages`, one view draft-saved and published through `publish-drafts`)
| publish 200, `success: false`, "No metadata items found"; revert 200 |
unchanged |
**Overlay drafts (triage's premise for Q1), measured at both base and
head.** An organization overlay draft of `showcase_task.grid`, bound to
`com.example.showcase`, gave the same sequence on both builds except for
the first step:
1. `/publish` of the showcase.
- At base it answered 200 with `itemsPublished: 2`, and the overlay
draft was still pending afterwards. So this door never published overlay
drafts.
- At head it answers 422, and the draft is still pending.
2. `/revert` while the draft is pending: 409 `RESOURCE_CONFLICT`
"Package 'com.example.showcase' has never been published, so there is no
published version", the protocol's stored-row answer (#22090).
3. `/publish-drafts`: 200, `publishedCount: 1`. The draft is gone and
the overlay label serves.
4. `/revert` with the overlay published and no draft pending: 200.
Overlay drafts publish through `publish-drafts` and the per-item publish
door, and neither passes the guarded branch.
## Pins
-
`packages/runtime/src/package-revert-code-shipped-members.integration.test.ts`.
This uses a real ObjectQL over better-sqlite3, the real protocol, the
real `MetadataManager` and `HttpDispatcher`, and the real producers of
the stamp. Each refusal asserts `422` and `WRITABLE_PACKAGE_REQUIRED`
plus the sentence's head.
- A `scope: 'system'` package that ships objects: revert and publish
both answer 422 (flipped from the 409 this PR first pinned), and nothing
is snapshotted.
- A `scope: 'system'` package the metadata service never holds, the
setup shape: revert and publish both answer 422.
- A booted package, the showcase shape, including an
authored-`packageId` capability: publish and revert both answer 422, and
the capability is not snapshotted.
- Controls:
- an unknown id: revert 404, publish 200 with `success: false`;
- a writable package with authored `packageId` members: publish, edit
and revert answer 200, and the snapshot is restored;
- a writable base whose members carry only the stamp: revert 409 before
a publish, then publish 200 (`itemsPublished: 1`), then revert 200;
- a stored draft row bound to a booted package: revert keeps the
protocol's 409.
-
`packages/runtime/src/package-revert-stored-members.integration.test.ts`.
Its two showcase (d) cases are flipped. Each now boots the showcase
manifest and asserts 422 `WRITABLE_PACKAGE_REQUIRED`; the "published
then edited" case also asserts that nothing is restored.
- `packages/metadata/src/metadata-service.test.ts`.
- The revert pins hold: a stamped-only package answers 409 with the
exact sentence, and membership is any of the three keys.
- The publish pin is flipped: a stamped-only item is now a member and is
snapshotted.
## Verification (head `434d074`)
- `pnpm --filter @objectstack/metadata typecheck`: exit 0.
- `pnpm --filter @objectstack/metadata test`: 58 files, 870 tests
passed.
- `pnpm --filter @objectstack/runtime typecheck`, which includes
`check:test-typecheck`: exit 0, debt ledger held at 27 files / 190
errors / 68 signatures.
- `pnpm --filter @objectstack/runtime test` (the `local` project): 334
files, 4717 passed, 19 skipped.
- Reverse verification on the committed head, each leg through
`scripts/ablation-replace.mjs` with the restore proved by blob:
- (A1) Without the publish guard, 3 refusal pins went red ("expected 200
to be 422").
- (A2) Without the revert guard, 5 went red: 3 here and the 2 flipped
(d) pins ("expected 409 / 404 / 200 to be 422").
- (A3) With the revert guard moved before the protocol's stored-row
answer, the stored-row control went red ("expected 422 to be 409"). The
first A3 attempt was refused as a no-op, because its replacement still
contained the anchor; it was re-run with a new anchor.
- (A4) With `publishPackage` back on two keys (metadata rebuilt, dist
preflight hit in 4 files), the flipped unit pin and the stamped
writable-base door pin went red.
- Restore leg: both files matched their HEAD blobs, `git diff HEAD` was
empty and the tree was clean; after a rebuild the suites read 6/6 unit
and 16/16 door.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths) derived 64 families, and all 64
exited 0. `--ran` reconciliation: 64 derived, 64 run, 0 NOT-MEASURED (a
derived zero). The new family since the revert-only head is
`check:route-envelope`.
- Narrowed lint: `eslint --no-inline-config --format json` over the 5
touched `.ts` files found 5 files, 0 errors, 0 warnings.
`eslint.config.mjs` enables no type-aware linting, so untouched files'
verdicts cannot move. The full `pnpm lint` is CI's.
## Acceptance notes
- A revert of a flat (non-envelope) published item writes `metadata:
publishedDefinition`, a nested copy of the whole item, because publish
snapshots `data.metadata ?? data`. It is reachable only for a writable
package's flat items now. Carrier: the claimant; no card.
- `MetadataManager.unregisterPackage` and `query({ packageId })` still
match on the old keys. `unregisterPackage` has no production caller in
this repository. Carrier: the claimant; no card.
- objectui's `PackagesPage` calls both doors. On a code package it now
receives a 422 with a worded message instead of a 200 or a 404/409. The
response shapes are unchanged, so the Console pin is not affected.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01EUBvqtauTDmHi2ZgY759p2)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 7ef50a4 commit d0bb78e
6 files changed
Lines changed: 470 additions & 37 deletions
File tree
- .changeset
- packages
- metadata/src
- runtime/src
- domains
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1795 | 1795 | | |
1796 | 1796 | | |
1797 | 1797 | | |
| 1798 | + | |
| 1799 | + | |
| 1800 | + | |
| 1801 | + | |
| 1802 | + | |
| 1803 | + | |
| 1804 | + | |
| 1805 | + | |
| 1806 | + | |
| 1807 | + | |
| 1808 | + | |
| 1809 | + | |
| 1810 | + | |
| 1811 | + | |
| 1812 | + | |
| 1813 | + | |
| 1814 | + | |
| 1815 | + | |
| 1816 | + | |
| 1817 | + | |
| 1818 | + | |
| 1819 | + | |
| 1820 | + | |
| 1821 | + | |
| 1822 | + | |
| 1823 | + | |
| 1824 | + | |
| 1825 | + | |
| 1826 | + | |
| 1827 | + | |
| 1828 | + | |
| 1829 | + | |
| 1830 | + | |
| 1831 | + | |
| 1832 | + | |
| 1833 | + | |
| 1834 | + | |
| 1835 | + | |
| 1836 | + | |
| 1837 | + | |
| 1838 | + | |
1798 | 1839 | | |
1799 | 1840 | | |
1800 | 1841 | | |
| |||
1832 | 1873 | | |
1833 | 1874 | | |
1834 | 1875 | | |
1835 | | - | |
1836 | | - | |
1837 | | - | |
1838 | | - | |
1839 | | - | |
1840 | | - | |
1841 | | - | |
1842 | | - | |
1843 | | - | |
1844 | | - | |
| 1876 | + | |
| 1877 | + | |
| 1878 | + | |
| 1879 | + | |
1845 | 1880 | | |
1846 | 1881 | | |
1847 | 1882 | | |
| |||
2060 | 2095 | | |
2061 | 2096 | | |
2062 | 2097 | | |
2063 | | - | |
2064 | | - | |
2065 | | - | |
2066 | | - | |
2067 | | - | |
2068 | | - | |
2069 | | - | |
2070 | | - | |
2071 | | - | |
| 2098 | + | |
2072 | 2099 | | |
2073 | 2100 | | |
2074 | 2101 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
822 | 822 | | |
823 | 823 | | |
824 | 824 | | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
| 882 | + | |
| 883 | + | |
| 884 | + | |
| 885 | + | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
825 | 890 | | |
826 | 891 | | |
827 | 892 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
378 | 378 | | |
379 | 379 | | |
380 | 380 | | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
381 | 387 | | |
382 | 388 | | |
383 | 389 | | |
| |||
1459 | 1465 | | |
1460 | 1466 | | |
1461 | 1467 | | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
1462 | 1478 | | |
1463 | 1479 | | |
1464 | 1480 | | |
| |||
1917 | 1933 | | |
1918 | 1934 | | |
1919 | 1935 | | |
| 1936 | + | |
| 1937 | + | |
| 1938 | + | |
| 1939 | + | |
| 1940 | + | |
| 1941 | + | |
| 1942 | + | |
| 1943 | + | |
1920 | 1944 | | |
1921 | 1945 | | |
1922 | 1946 | | |
| |||
0 commit comments