Commit fade3da
Part of #17676 — ruling A′ items 2 and 3.
⛔ Deliberately **not** a closing reference: ruling item 5's acceptance
(three probes agreeing across a restart) is **not met** by this PR, so
the card must stay open. What is left, and why it is not in this diff,
is stated under "The fork" below.
Clause-②: no
## What the ruling asked for
Decision batch #125 item 2, letter A′ (maintainer 「同意」 2026-09-13), five
items across three lanes. Item 1 was the `packages/spec` carve-out and
landed via carrier #18053. Item 4 is the spec seat's ADR read. This PR
is the engine lane's half:
> **2.** `protocol.installPackage` and `protocol.updatePackage`
therefore always find the `package` service on a stock boot; their
in-memory-only branches stay as the documented degraded path for reduced
hosts.
> **3.** `durable-package.test.ts`'s `makeImpl()` gains the
service-ABSENT composition so the pin can express the bug this card
found.
> **5.** The three probes (Studio writable list, `GET
/api/v1/data/NS_OBJ`, `GET /api/v1/meta/object/NS_OBJ/published`) agree
after a restart — that is the acceptance.
## Item 3 — the service-ABSENT composition
`makeImpl()` hard-wired `services = new Map([['package', …]])`, so it
could build only the service-PRESENT host and **no pin in this file
could fail the way the card failed**. It now composes either host:
- `packageService: false` builds a services registry with no `package`
entry at all — what a host that mounts no `PackageServicePlugin` hands
the protocol.
- `hydrate: [...]` replays durable manifests through the registry's own
`installPackage` verb, which is literally what
`PackageServicePlugin.start()` does when it reconciles `sys_packages`
back into the registry. A host composed with the rows a previous host
persisted **is** that process after a restart.
- The fake `registry` now mirrors the real `SchemaRegistry` package API
**by name** (`installPackage` / `getPackage` / `getAllPackages` /
`updatePackageManifest`), so a pin written against it is a pin about the
primitive's real collaborator rather than an invented one.
Four new pins on top of it: the service-absent `installPackage` and
`updatePackage` degraded paths (registry write lands, nothing durable,
the degradation is loud), the three-way split after a restart, and a
**service-present control** where the same sequence agrees instead — the
control is what keeps the split pin from being vacuously green.
## Item 2 — the branches stay; the note describing them did not survive
item 1
No behaviour change: the in-memory-only branches are exactly as they
were. ⭐ Item 2 does **not** say to delete them, and a change that
removed them would have over-reached.
What did change is documentation that item 1 made false.
`installPackage`'s docblock read "when the `package` service is absent
(e.g. the `marketplace` capability is off)". After the carve-out that
names the wrong capability: persistence is `package-registry`, an
always-on core token, and `marketplace` now names only the optional
catalogue half. The note now says so, says why the branch is kept, and
records the measured state of the split. `updatePackage` points at the
same note, because the ruling names both primitives.
## ⚠️ The fork — item 2's factual half is FALSE on today's tree
Ruling item 2 says these primitives "therefore always find the `package`
service on a stock boot". **Measured on `origin/main` at `c334ba0f3a`,
they do not**, and item 1 landing did not change that:
- `PLATFORM_ALWAYS_ON_CAPABILITIES` contains `package-registry`, and
`serve` force-appends every slate token to each app's `requires`
(`serve.ts`, the `ALWAYS_CAPS` loop).
- The capability resolver then mounts a token only when
`Serve.CAPABILITY_PROVIDERS` keys it. That registry keys
**`marketplace`** and does **not** key `package-registry`.
- A token with no provider entry hits `if (!spec) … continue`, and
because `package-registry` **is** in `PLATFORM_CAPABILITY_TOKENS` the
resolver does not even warn. So the always-on entry is silently inert.
- ⇒ on a stock `objectstack dev` boot of an app that does not itself
declare `requires: ['marketplace']` (`examples/app-crm` declares `['ui',
'automation']`), `POST /api/v1/packages` still reaches
`protocol.installPackage` with no `package` service and still registers
the package in memory only. **The defect this card reports is live.**
This is not a new reading. Both spec-lane artifacts say the same thing
in their own words and both name the engine lane as the owner:
> ⚠️ SCOPE, measured on `serve`'s capability resolver at c17ff70: a
slate entry is force-appended to every app's `requires`, and the CLI
then mounts it only if `Serve.CAPABILITY_PROVIDERS` keys the token. That
registry keys `marketplace`, not this token, so under `objectstack
serve` this entry is inert until the runtime half of the same ruling
lands (#17676 items 2/3/5, the engine lane).
— `packages/spec/src/kernel/platform-capabilities.ts`, and the same note
in `packages/cli/test/serve-capability-vocabulary.test.ts`, which
deliberately declines to pin the absence so the engine lane's own change
is not turned red for doing the ruled thing.
**That remaining half is not in this diff, by scope and by shape.** The
dispatch fences this card at `packages/metadata-protocol/src/`; the
resolver lives in `packages/cli/src/commands/serve.ts`. And it is not a
one-line map entry: the spec's own provider row records that
`marketplace` and `package-registry` point at the **same package
today**, that this is what the carve-out *inherited* rather than
decided, and that "repointing `marketplace` at the browse surface moves
the runtime's own resolver with it". Which of those two shapes to take
is a decision, not a mechanical edit, and it changes the boot
composition of every app — a verification surface well outside this one.
## What is left — ⛔ item 5 is NOT met
Item 5's acceptance is three HTTP probes agreeing across a restart of a
booted composition. This is a unit tier over the protocol primitive, and
a unit tier cannot restart a server. What the new pins do instead is pin
the one fact the three probes disagree about — whether the package
crosses the boundary at all — at the seam that decides it, with the
registry reads standing for the two registry-backed probes and the
metadata read for the third. The test file says this in a scope banner
so a green run here is not over-read. ⛔ Item 5 stays open, and it cannot
be met at all until the resolver half above lands.
## Verification
Run under `scripts/pm/os-verify-lock.sh`; exit codes captured before any
pipe.
| what | command | result |
|---|---|---|
| package suite | `pnpm --filter @objectstack/metadata-protocol test` |
exit 0 — 182 files passed / 3 skipped, **2600 tests passed**, 19 skipped
(at `adcd4a161`) |
| package typecheck | `pnpm --filter @objectstack/metadata-protocol
typecheck` | exit 0 (`tsc --noEmit`; it **does** compile this test file
— an earlier run of it caught a TS2353 in the new fixture type) |
| dependency closure | `pnpm --filter
'@objectstack/metadata-protocol^...' build` | exit 0 |
| derived gate families | `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` | 53 derived, 53 run, **50 exit
0** |
| gate reconciliation | same tool with `--ran` (each line `command ::
exit N`) | exit 0 — 53 accounted, 0 UNRUN |
| repo-wide lint | ESLint API over `.` with `allowInlineConfig: false` |
exit 0 — **6934 files, 0 errors, 0 warnings** (the union, not a narrowed
subset, at `adcd4a161`) |
Three gates first exited **3 — PREREQUISITE NOT MET**
(`check:dual-build-cjs-loads`, `check:lean-entry-closure`,
`check:type-check-debt`): each reads built output and the worktree had
none. The prerequisite was cleared with `pnpm exec turbo run build
--filter='./packages/*' --filter='./packages/*/*'` (72/72 tasks, exit 0)
and all three then exited **0**. ⛔ An exit 3 is recorded as NOT
MEASURED, never as a pass.
### Ablation — the new pin can fail
Driven through `scripts/ablation-replace.mjs`, so the mutation is proved
on disk rather than assumed from an exit code. The test imports
`./index.js` — a same-package relative specifier that vitest resolves to
`src/`, not `dist/` (an earlier failing run named `src/protocol.ts:6:1`
in its stack), so no rebuild sits between the mutation and the run.
- **mutation**: the `console.warn` in `installPackage`'s no-service
branch replaced with a no-op — anchor `x1 → x0`, blob `653d2155fb74 →
7072a8718166`, both verified by re-reading the file.
- **result**: `1 failed | 10 passed (11)` — the service-absent pin went
red on the product change, so it reads the primitive and not the fake.
- **restore**: blob back to `653d2155fb74`, identical to `HEAD`, and
`git diff HEAD` empty.
No ablation artifact remains in the tree.
## Changeset
`Clause-②: no`, and a **`patch`** changeset for
`@objectstack/metadata-protocol` —
`.changeset/17676-package-registry-docblock-capability-name.md`.
⚠️ This PR first proposed `skip-changeset`; the seat **overruled** it,
on the measurement this branch itself took. That measurement is recorded
here because it is what decides the question rather than a preference.
AGENTS.md Post-Task Checklist §3 scopes the label to "a diff that
publishes nothing from any released package". Measured:
`@objectstack/metadata-protocol` is not private, its `files[]` ships
`dist`, and the corrected docblock reaches `dist/index.js`,
`dist/index.cjs`, `dist/index.d.ts` and `dist/index.d.cts` — subject
count 1 each, with a positive control present in the two JS outputs —
because tsup keeps JSDoc. ⇒ the label's precondition is **false by
measurement**.
And it is a **fix** rather than a cosmetic. The old sentence told a
consumer reading `dist/index.d.ts` in their editor that the
absent-service branch is what you get "e.g. the `marketplace` capability
is off", which stopped being true the moment ruling A′ item 1 landed. A
false TSDoc in shipped content is a defect on its own terms — and under
`skip-changeset` this correction would sit on `main` **unshipped**, the
false sentence still going out, until some unrelated change happened to
bump the package.
⛔ The changeset claims no behaviour change and says so in its own body:
the in-memory branches are byte-identical, and this card's defect is not
repaired here.
⛔ No label write was made in either round. The seat holds the labels
endpoint, and with a changeset present there is no longer a label to
apply.
## Acceptance notes
Noted in passing, ⛔ not filed and ⛔ not addressed here:
- **The three degradation sites in these two primitives log at `warn`.**
AGENTS.md's degradation-log-level rule grades a durability degradation
at `error` ("a write that claims to persist does not… nothing looks
broken"), which is exactly this seam's shape — the door answered 201.
The same section also says an `if (!service)` composition branch "is
usually functional and belongs at `warn`", so the `else` arm is
genuinely arguable; the `catch` arm around the `publish` call is the one
the rule says it bites. `pnpm check:durability-log-level` does not see
either, because `pkgSvc.publish` is not in
`DURABILITY_CRITICAL_CALLEES`. Left alone: changing a log level is not
in this ruling, and the ruling's own remedy is to stop hosts taking the
branch at all.
- **`dispatch-gates.mjs` reported STALE TREE** on both derivations (two
of its own input scripts moved on `origin/main` while this branch
worked). Discharged empirically rather than by merging: the command list
was derived twice, before and after a fetch, and the two are
byte-identical.
- **Comment `5651690676` — the "may item 3 be delivered independently of
item 1" question — returns 404** and was destroyed with the suspended
`os-musk` identity. It could not be read, so it is recorded NOT
MEASURED. This PR proceeds on the PM's reading that item 1 having landed
resolves it by circumstance.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE)_
---
_Generated by [Claude Code](https://claude.ai/code)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent c5d3d1d commit fade3da
3 files changed
Lines changed: 256 additions & 11 deletions
File tree
- .changeset
- packages/metadata-protocol/src
Lines changed: 15 additions & 0 deletions
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
10 | 25 | | |
11 | 26 | | |
12 | 27 | | |
13 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
14 | 37 | | |
15 | 38 | | |
16 | 39 | | |
17 | 40 | | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
18 | 60 | | |
19 | 61 | | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
26 | 77 | | |
27 | | - | |
28 | 78 | | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
29 | 84 | | |
30 | 85 | | |
31 | | - | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
32 | 90 | | |
33 | | - | |
| 91 | + | |
34 | 92 | | |
35 | 93 | | |
36 | 94 | | |
| |||
130 | 188 | | |
131 | 189 | | |
132 | 190 | | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22325 | 22325 | | |
22326 | 22326 | | |
22327 | 22327 | | |
22328 | | - | |
22329 | | - | |
| 22328 | + | |
| 22329 | + | |
| 22330 | + | |
| 22331 | + | |
| 22332 | + | |
| 22333 | + | |
| 22334 | + | |
| 22335 | + | |
| 22336 | + | |
| 22337 | + | |
| 22338 | + | |
| 22339 | + | |
| 22340 | + | |
| 22341 | + | |
| 22342 | + | |
| 22343 | + | |
| 22344 | + | |
| 22345 | + | |
| 22346 | + | |
| 22347 | + | |
| 22348 | + | |
| 22349 | + | |
| 22350 | + | |
| 22351 | + | |
| 22352 | + | |
22330 | 22353 | | |
22331 | 22354 | | |
22332 | 22355 | | |
| |||
22469 | 22492 | | |
22470 | 22493 | | |
22471 | 22494 | | |
| 22495 | + | |
| 22496 | + | |
| 22497 | + | |
| 22498 | + | |
22472 | 22499 | | |
22473 | 22500 | | |
22474 | 22501 | | |
| |||
0 commit comments