Repository navigation
Commit cdc1ae0
Fixes #19387
Clause-②: no
`package-registry` is on the spec's always-on slate, so `objectstack
serve` appends it to every app's `requires`. But
`Serve.CAPABILITY_PROVIDERS` did not key it, and the resolver's
no-provider branch says nothing for a token the app did not declare
itself. So a stock app got no `package` service, and a package created
through the API was lost on restart. This PR is the runtime half of
ruling A′ (decision batch #125 item 2): `protocol.installPackage` /
`updatePackage` now find the `package` service on a stock boot.
#17676 remains open. Its item 5 (three probes agreeing across a restart)
is its own acceptance and is not claimed here.
## What changes
- **`packages/cli/src/commands/serve.ts`, the mount table.**
`CAPABILITY_PROVIDERS['package-registry']` now points at
`@objectstack/service-package` / `PackageServicePlugin`. That is the
provider the spec's `PLATFORM_CAPABILITY_PROVIDERS['package-registry']`
row declares. The existing drift pin (`open-edition service tokens name
the SAME package as serve CAPABILITY_PROVIDERS`) now covers this row
too.
- **`serve.ts`, the capability resolver.** `hasPluginMatching` used to
read only the app's own `plugins[]`. Now it also reads
`resolverMounted`, the provider instances this loop has already mounted,
each added after its `kernel.use` succeeds. Without this, the table
entry alone gives an app that declares `marketplace` a second
PackageServicePlugin (measured, see below).
- **`packages/cli/test/serve-capability-vocabulary.test.ts`.** The prose
that deliberately declined to pin the absence is now the pin: every
`ALWAYS_ON_CAPABILITIES` token must key a `CAPABILITY_PROVIDERS` entry
or a `CAPABILITY_TO_TIER` tier. Those are the only two ways `serve`
mounts a token, and `CAPABILITY_TO_TIER` is the named list whose
docblock says why its tokens have no provider entry. A future slate
entry with no mount goes red on the PR that adds it.
- **`packages/cli/test/serve-capability-identity.test.ts`.** The new row
is added to `EXPECTED_PROVIDER_NAME`, whose coverage assertion requires
every table key.
- **`packages/cli/test/serve-package-registry-always-on.e2e.test.ts`**
(nightly tier by name, integration project by behaviour). It boots
`serve` twice on one SQLite file, installs a package through `POST
/api/v1/packages`, restarts, and reads it back. It also boots a
`marketplace` declarer and asserts that exactly one PackageServicePlugin
is mounted.
- **`packages/metadata-protocol/src/protocol.ts`** (patch round,
`b5b9b64c`, prose only). The `installPackage` docblock said the runtime
half of the split had not landed and that a stock boot still took the
in-memory-only branch. This diff makes that false, and the docblock
ships in `dist`. It now says that `serve` mounts `PackageServicePlugin`
for `package-registry`, and that the in-memory-only branch is for hosts
that mount no provider. One hunk, comment lines only.
- `.changeset/19387-package-registry-mount.md`: `@objectstack/cli` patch
and `@objectstack/metadata-protocol` patch.
## Measured, before and after
Fixture: an app that declares neither `marketplace` nor
`package-registry` (the `examples/app-crm` shape in the one way that
matters). Booted with `serve --dev` on a SQLite file.
| probe | `origin/main` 2c1011b | this branch |
|:--|:--|:--|
| `POST /api/v1/packages` | 201, plus `[protocol.installPackage] no
'package' service — 'com.example.survivor' registered in-memory only
(will not survive a restart)` | 201, no fallback line |
| restart on the same DB, then `GET
/api/v1/packages/com.example.survivor` | **404** | **200**, and the boot
logs the rehydration of `com.example.survivor` from `sys_packages` |
| banner plugin row | no PackageServicePlugin | PackageServicePlugin ×1
|
Declarer fixture (`requires: ['marketplace']`), with three builds of
`serve.ts`:
| build | `Plugin superseded: 'package-service'` | PackageServicePlugin
in the banner row |
|:--|:--|:--|
| `origin/main` | absent | ×1 |
| table entry only (no `resolverMounted`) | **present** (WARN) | **×2**
|
| this branch | absent | ×1 |
## Declarer impact of this diff (acceptance 3)
**Measured: unaffected.** An app that declares `marketplace` boots with
the same single PackageServicePlugin as before. No supersede warning, no
second instance, and `POST /api/v1/packages` persists as it did. The
only in-repo declarer is
`examples/app-showcase/objectstack.config.ts:107` (`git grep` over
`examples/`, `packages/`, `apps/`, `content/`, `skills/` for
`'marketplace'` in a `requires` array). This file is read-only for this
PR and does not change.
**The declarer migration moves with the repoint, not with this diff.**
Today the spec's `PLATFORM_CAPABILITY_PROVIDERS` maps **both** tokens to
`@objectstack/service-package`. The row's own comment says that sharing
is what the carve-out INHERITED, and that repointing `marketplace` at
the browse surface "moves the runtime's own resolver with it". This
table follows the spec map, and the existing drift pin enforces that. So
the repoint starts at that spec row (the spec seat's lane) and this
mount table follows it. It is reported to the seat as a `Seam:` finding
in the dispatch report, not done here.
**ADR-0087 disposition.** For this diff: not applicable. Nothing an
author writes changes shape, meaning or validity. `requires:
['marketplace']` and `requires: ['package-registry']` both stay valid
and still mount the persistence plugin. For the repoint, the
recommendation passed to the seat is: under 〈阶段姿态〉
(「速度优先于兼容。用的人少:退役立即生效,无过渡窗口、无别名双拼」) the meaning change of `marketplace`
takes effect at once, with no alias window. A `marketplace` declaration
made for the store becomes redundant, because the store is always on.
The case the carrier must spell out is an out-of-repo declarer whose
`requires: ['marketplace']` would start mounting the browse plugins.
That is a runtime-meaning change of a `requires` token, not a stored or
authored shape, so the recommendation is that no ADR-0087 conversion
entry is owed. The carrier decides.
## Why no runtime warning in the no-provider branch
That branch is reached only when a token has no `CAPABILITY_PROVIDERS`
entry. Once the pin above holds, an always-on token cannot reach it:
every slate token has a mount, and `@objectstack/spec` and
`@objectstack/cli` release in one `fixed` changeset group
(`.changeset/config.json`). A published CLI therefore never runs against
a slate its pin did not see. A warning there would be unreachable code.
A comment at the branch records this.
## Tests (at 6496714)
- `pnpm --filter @objectstack/cli exec vitest run --project unit
--maxWorkers=2`: 222 files. On first run, 220 passed and 2 were refused
with `packages/cli is not built` (`published-subpath-console.pin`,
`published-subpath-hook-body.pin`), a build prerequisite. After `pnpm
--filter @objectstack/cli build` those 2 files pass, 29 tests. Totals:
3116 passed, 29 skipped.
- `pnpm --filter @objectstack/cli run typecheck`: exit 0. The
`check:test-typecheck` debt holds at 3 files, 28 errors. `tsc -p
tsconfig.test.json --listFilesOnly` includes all 3 touched test files.
- The new e2e, `OS_TEST_TIERS=nightly … vitest run --project integration
test/serve-package-registry-always-on.e2e.test.ts`: 5/5 passed.
- Neighbouring nightly serve boots: every other `*.e2e.test.ts` in
`packages/cli/test` that boots `serve` (17 files) passed with 75/75
tests. A boot-composition change reaches them, and they run only
nightly.
**Reverse checks** (each mutation went through
`scripts/ablation-replace.mjs`; the tool confirmed the anchor moved from
1 to 0 and the blob changed, then restored the file and checked it
matched HEAD with `git diff HEAD` empty):
1. Delete the `'package-registry'` table entry, then run the vocabulary
and identity pins: **red**, as expected. The new pin reports `expected [
'package-registry' ] to deeply equal []`, and the identity table's
coverage assertion goes red too.
2. Same deletion, then run the e2e: **3 red** (banner row, in-memory
fallback line, post-restart `404`). The declarer block stays green.
3. Delete `resolverMounted.push(provider);`, then run the e2e: **1 red**
(`Plugin superseded: 'package-service'` on the declarer boot). The stock
block stays green.
No dist leg was needed. The unit pins import `../src/commands/serve.js`
directly, and the e2e spawns `bin/run-dev.js` through tsx, so both read
`src/`.
## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at 6496714 derived 64 commands. All were
run with the exit code captured before any pipe, and `--ran` reconciled
them: `64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN`. Three first
answered exit 3 (PREREQUISITE NOT MET) and passed once the prerequisite
was supplied:
- `check-plugin-teardown-shape --self-test` needed the pinned fixture
commit `621a4876`, which a shallow clone lacks. It passed after `git
fetch --depth=1 origin 621a487…`.
- `check:dual-build-cjs-loads` and `check:i18n-coverage` needed build
output. They passed after `turbo run build --filter=!@objectstack/docs`.
`pnpm lint` (the full `eslint . --no-inline-config` run): exit 0 at
6496714.
## Acceptance notes
- **The dispatch premise that `hasPluginMatching` stops a double mount
was false (measured).** That check reads only the app's own `plugins[]`,
never what the resolver has mounted. The `resolverMounted` list is the
in-scope repair.
- **Prose that goes stale with this diff, left for the files' next
editors** (those files are outside this PR's surface):
- `packages/spec/src/kernel/platform-capabilities.ts`: the SCOPE note on
the `package-registry` slate entry, and the comment on its provider row.
Both say the entry is inert under `serve`.
- ~~`packages/metadata-protocol/src/protocol.ts`: the `installPackage`
docblock~~: corrected in this PR (patch round, `b5b9b64c`), because it
ships in `dist`. The spec-side note above is carried on #17676 with the
`marketplace` repoint seam.
- **`os migrate plan`** reports, for information only, tables with a
platform prefix that no object declares. By reading
`packages/cli/src/utils/unmanaged-tables.ts`, `sys_packages` (raw DDL)
should now appear there for any stock database that `serve` has booted,
as it already would for `marketplace` declarers. NOT MEASURED.
- **On the in-memory driver (`memory://`)** a stock boot now logs four
more WARN lines (measured): three `Raw execution not supported in
InMemory driver` for the `sys_packages` DDL and query, and `Package
hydration from sys_packages SKIPPED`. `marketplace` declarers already
got these.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01TnPAC1UsTGfHPXVUCL6iLn)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent d42e06b commit cdc1ae0
6 files changed
Lines changed: 434 additions & 24 deletions
File tree
- .changeset
- packages
- cli
- src/commands
- test
- metadata-protocol/src
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1960 | 1960 | | |
1961 | 1961 | | |
1962 | 1962 | | |
| 1963 | + | |
| 1964 | + | |
| 1965 | + | |
| 1966 | + | |
| 1967 | + | |
| 1968 | + | |
| 1969 | + | |
| 1970 | + | |
| 1971 | + | |
| 1972 | + | |
| 1973 | + | |
| 1974 | + | |
| 1975 | + | |
| 1976 | + | |
| 1977 | + | |
1963 | 1978 | | |
1964 | 1979 | | |
1965 | 1980 | | |
| |||
4565 | 4580 | | |
4566 | 4581 | | |
4567 | 4582 | | |
| 4583 | + | |
| 4584 | + | |
| 4585 | + | |
| 4586 | + | |
| 4587 | + | |
| 4588 | + | |
| 4589 | + | |
| 4590 | + | |
| 4591 | + | |
| 4592 | + | |
| 4593 | + | |
4568 | 4594 | | |
4569 | 4595 | | |
4570 | 4596 | | |
4571 | 4597 | | |
4572 | | - | |
| 4598 | + | |
4573 | 4599 | | |
4574 | 4600 | | |
4575 | 4601 | | |
| |||
4582 | 4608 | | |
4583 | 4609 | | |
4584 | 4610 | | |
| 4611 | + | |
| 4612 | + | |
| 4613 | + | |
| 4614 | + | |
| 4615 | + | |
| 4616 | + | |
| 4617 | + | |
| 4618 | + | |
4585 | 4619 | | |
4586 | 4620 | | |
4587 | 4621 | | |
| |||
4650 | 4684 | | |
4651 | 4685 | | |
4652 | 4686 | | |
4653 | | - | |
| 4687 | + | |
| 4688 | + | |
| 4689 | + | |
4654 | 4690 | | |
4655 | 4691 | | |
4656 | 4692 | | |
| |||
4660 | 4696 | | |
4661 | 4697 | | |
4662 | 4698 | | |
4663 | | - | |
| 4699 | + | |
| 4700 | + | |
| 4701 | + | |
4664 | 4702 | | |
4665 | 4703 | | |
4666 | 4704 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
108 | 112 | | |
109 | 113 | | |
110 | 114 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
| 52 | + | |
| 53 | + | |
62 | 54 | | |
63 | 55 | | |
64 | 56 | | |
| |||
70 | 62 | | |
71 | 63 | | |
72 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
73 | 100 | | |
74 | 101 | | |
75 | 102 | | |
| |||
0 commit comments