Repository navigation
Commit d866fdd
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi961WGNsxKx9jXxC7GrhBueT)_
Fixes #31566
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
- 改了什么:启动时 `rehydrate` 如果遇到引擎拒绝注册的安装记录(旧版本写进去的),现在会把这次拒绝记下来;`GET
/api/v1/marketplace/install-local` 列表里这条记录带上 `notLoaded: { code, message
}`(引擎自己的错误码和原句),不再显示成“已安装”。
- 为什么改:以前这种记录每次启动都被拒、什么都没加载,只在日志里写一行 `rehydrate
failed`;列表却照样显示已安装,用户看不出这个应用其实没加载,也不知道该卸载还是修包。
- 风险与回滚:只改 `marketplace-install-local-plugin.ts` 一处 `register` 调用外的
try/catch 和标记类型;健康记录、协议版本不兼容记录的列表输出逐字节不变;回滚即撤销本 PR。
- 先看:`packages/cloud-connection/src/marketplace-install-local-plugin.ts`
中 rehydrate 里新增的 `register` catch(只记“声明 4xx 的拒绝”)和 `NotLoadedMarker`
联合类型;再看 `marketplace-install-local-register-refusal.test.ts` 末尾新增的
`#31566` 用例组。
- 两分钟验证:`pnpm --filter @objectstack/cloud-connection exec vitest run
src/marketplace-install-local-register-refusal.test.ts -t '#31566'`,预期 7
个用例全绿。
- 需要你做的:无,席位落地。
## What changes
- **Rehydrate (`kernel:ready`)**: the `manifest.register` call gets its
own `catch`. When the engine REFUSES the register (a throw declaring a
4xx, `isDeclaredRegisterRefusal`, the rule the install door already uses
since #31478), the refusal is recorded in `refusedAtRehydrate`, keyed by
manifest id and `installedAt` like #21822's protocol-range refusal. Then
the error is rethrown, so the existing `rehydrate failed` log is
unchanged. There is one record and one listing path (`notLoadedMarker`);
nothing new is read at list time.
- **`NotLoadedMarker`** becomes a closed union, each arm named member by
member:
- `{ code: 'OS_PROTOCOL_INCOMPATIBLE', requiredRange }`: unchanged
(#21822);
- `{ code, message }`: `code` is `resolveThrownHttpError(err).code`, so
it is the engine's own code when it is a registered ADR-0112 member (all
the registry's refusal classes are: `INVALID_METADATA`,
`DUPLICATE_ARTIFACT_OBJECT_NAME`, `OBJECT_OWNERSHIP_CONFLICT`,
`DEFAULT_RECORD_PAGE_CONFLICT`, `NAMESPACE_CONFLICT`, …). Otherwise it
is the code its declared status derives. No code is coined here.
`message` is the engine's sentence. The error is never spread into the
marker.
- **Only a refusal at register is marked** (seat ruling 3). Since #31614
a refused artifact registers none of its members, so nothing of the
entry is loaded. A failure after a successful register (datasource
connect, handler bind, side effects, heal) leaves a loaded package and
is not marked.
- **A register error with no declared 4xx** (seat ruling 4): the tree
decides. `isDeclaredRegisterRefusal` classes it as the runtime's own
fault, not a verdict on the package. The install door takes the lenient
path for it ("loads at the next restart") and the next boot can load it.
So it is not recorded and not marked, and no code is needed.
- Changeset
`.changeset/31566-install-local-register-refused-not-loaded.md`:
`@objectstack/cloud-connection` patch.
## Tests
New top-level `describe` in
`packages/cloud-connection/src/marketplace-install-local-register-refusal.test.ts`
(the shared harness file: a real `ObjectKernel` + `ObjectQLPlugin`
booted to `kernel:ready`, the plugin's own rehydrate after it).
`bootPlugin` gains the GET listing, DELETE, the logger, and an optional
after-register fault. Each legacy entry is written straight to the
ledger, as the earlier build's lenient cloud branch wrote it.
- a default page that is not a record page (a body refusal) and a
multi-package artifact whose second member is refused: the boot logs the
refusal, registers none of the packages and keeps the entry. The listing
serves the item with `notLoaded: { code, message }` in place of
`withSampleData`. `message` equals the logged error's message, and the
marker's keys are exactly `code` and `message`.
- CONTROL: a healthy entry beside a register-refused one and a
protocol-incompatible one is listed byte for byte as it is with no
refused entry. The protocol entry's marker is byte for byte `{ code:
'OS_PROTOCOL_INCOMPATIBLE', requiredRange }`.
- DELETE removes the refused entry, and the listing no longer serves it.
- A compatible version installed over the refused entry is loaded and
listed with no marker.
- CONTROL: a register fault declaring no status is not marked, and the
next boot loads the package.
- CONTROL: a failure after a successful register (the side-effects
replay throws) is not marked; the package is registered.
- No existing test was edited (no forced test edits). `bootPlugin`'s
`install` helper was folded into a shared `call` helper with the same
request shape.
## Acceptance notes
- **Console (no `ui/**` change)**: both readers of the marker
(`MarketplacePackagePage.tsx` `notLoadedReason`,
`MarketplaceRowActions.tsx` `MarketplaceNotLoaded`) branch on `code ===
'OS_PROTOCOL_INCOMPATIBLE'` and draw `marketplace.notLoaded.otherReason`
with the code for any other code. A register-refused entry shows the
"not loaded" badge with its code and offers no sample-data actions. The
console does not show the new `message` yet. Its `LocalInstallNotLoaded`
type still declares `requiredRange: string` as required, which this arm
does not carry. That is a type drift, and runtime is unaffected because
the field is read only under the protocol code. Both items are listed
for the seat.
- The served sentence is the one the install door's `422
PLUGIN_REGISTER_FAILED` already gives the installer. The listing serves
the marker to every authenticated caller, as #21822's marker is served.
The sentence names packages, objects and pages, but no data.
- Not measured: the two sample-data doors (reseed, purge) refuse only a
protocol-incompatible entry (`refuseProtocolIncompatibleEntry` judges
`checkProtocolCompat` on the entry, not `refusedAtRehydrate`). A
register-refused entry is not refused there. Listed for the seat.
## Verification
Head `6202c2e0`: the fix `dd867944` with origin/main merged as
`99364ea7`, which carries #31584 (same package, other files). The branch
was cut from `8784ccbc`, which carries #31614 / `bafbc681`.
<details><summary>Red first (the new pins on main's code)</summary>
`vitest run src/marketplace-install-local-register-refusal.test.ts` with
the test change only: `Tests 5 failed | 14 passed (19)`. The two refusal
cases, DELETE and the reinstall fail because the refused entry is served
`withSampleData: false` with no `notLoaded`. In the CONTROL, the healthy
and protocol-marker assertions held on main; only its last line (the
refused entry's marker) failed. The two fault CONTROLs pass on main.
</details>
<details><summary>Ablation (scripts/ablation-replace.mjs, anchor x1
each, restored blob == HEAD 377426e and git diff HEAD empty after each
leg)</summary>
The plugin is imported from `src` by a relative path (no dist leg).
- A1, the refusal not recorded (main's behaviour): `5 failed | 2
passed`, the five above.
- A2, every register throw recorded: `1 failed | 6 passed`, the
register-fault CONTROL.
- A3, every rehydrate failure recorded in the outer catch: `2 failed | 5
passed`, both fault CONTROLs.
- A4, the error spread into the marker: `2 failed | 5 passed`, the two
closed-marker cases.
</details>
<details><summary>Suites, typecheck, lint, gates</summary>
- On `6202c2e0`, after `pnpm install --frozen-lockfile` and `turbo run
build --filter='@objectstack/cloud-connection...'` (39/39 tasks): `pnpm
--filter @objectstack/cloud-connection test`: Test Files 80 passed (80),
Tests 1105 passed (1105); `pnpm --filter @objectstack/cloud-connection
typecheck` (both tsconfigs): exit 0. The lock's VERDICT was command-exit
0.
- On `dd867944` before the merge: Test Files 78 passed (78), Tests 1074
passed (1074); typecheck exit 0.
- Lint: `pnpm exec eslint --no-inline-config packages/cloud-connection`:
exit 0.
- Derived gates on `6202c2e0`: `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` listed 69 families, and
all 69 ran with exit 0. `--ran` reconciliation: "69 derived famil(ies)
accounted for — 69 run, 0 NOT-MEASURED (a DERIVED zero)". On `dd867944`
the same 69 also ran. Two of them first exited 3 on prerequisites
(`check-plugin-teardown-shape --self-test` needs fixture commit
`621a4876` fetched; `check:dual-build-cjs-loads` needs every package's
dist) and then ran green.
- Dogfood battery: n/a (widening, no `!`, no `(narrowing)` arm).
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmPANpbDYYSFnjd1B9Q)_
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 06060c6 commit d866fdd
3 files changed
Lines changed: 242 additions & 23 deletions
File tree
- .changeset
- packages/cloud-connection/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
394 | 394 | | |
395 | 395 | | |
396 | 396 | | |
397 | | - | |
| 397 | + | |
398 | 398 | | |
399 | 399 | | |
400 | 400 | | |
| |||
1209 | 1209 | | |
1210 | 1210 | | |
1211 | 1211 | | |
1212 | | - | |
1213 | | - | |
1214 | | - | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
1215 | 1215 | | |
1216 | | - | |
1217 | | - | |
1218 | | - | |
1219 | | - | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
1220 | 1230 | | |
1221 | | - | |
1222 | | - | |
1223 | | - | |
1224 | | - | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
| 1234 | + | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
1225 | 1240 | | |
1226 | 1241 | | |
1227 | 1242 | | |
| |||
1429 | 1444 | | |
1430 | 1445 | | |
1431 | 1446 | | |
1432 | | - | |
1433 | | - | |
1434 | | - | |
| 1447 | + | |
| 1448 | + | |
| 1449 | + | |
| 1450 | + | |
1435 | 1451 | | |
1436 | 1452 | | |
1437 | 1453 | | |
| |||
1613 | 1629 | | |
1614 | 1630 | | |
1615 | 1631 | | |
1616 | | - | |
| 1632 | + | |
| 1633 | + | |
| 1634 | + | |
| 1635 | + | |
| 1636 | + | |
| 1637 | + | |
| 1638 | + | |
| 1639 | + | |
| 1640 | + | |
| 1641 | + | |
| 1642 | + | |
| 1643 | + | |
| 1644 | + | |
| 1645 | + | |
| 1646 | + | |
| 1647 | + | |
| 1648 | + | |
| 1649 | + | |
| 1650 | + | |
| 1651 | + | |
| 1652 | + | |
| 1653 | + | |
1617 | 1654 | | |
1618 | 1655 | | |
1619 | 1656 | | |
| |||
3220 | 3257 | | |
3221 | 3258 | | |
3222 | 3259 | | |
| 3260 | + | |
| 3261 | + | |
| 3262 | + | |
| 3263 | + | |
| 3264 | + | |
| 3265 | + | |
3223 | 3266 | | |
3224 | 3267 | | |
3225 | 3268 | | |
| |||
0 commit comments