Repository navigation
Commit d2c4874
fix(service-automation,objectql,cloud-connection)!: an installed package's connector entry never reads the host's files (#28587)
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U
Fixes #28534
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — no `skills/**` file teaches `_packageId` or an
install door's `source`; the refusal concerns a key only the
registration writes
## 维护者速读
-
**改了什么**:从市场安装(install-local)的包,其连接器条目不再能按宿主的根目录读文件或启动进程:相对路径的文件引用一律被拒,报错里写明条目、包、引用和改法。另外,防止被安装的包冒充宿主:(1)
objectql 拒绝自带 `_packageId`、且与注册它的包不一致的条目;(2) install-local 把每个子包都登记成
`marketplace`,首次安装和重启后重新加载都一样;(3) 查不到记录的包 id 一律按"已安装"处理并拒绝,不再默认算作宿主的。
- **为什么改**:实测在 `a52191f6` 上,装进 showcase 的包能读到宿主自己的 OpenAPI 文档,也能在宿主目录下运行
MCP 脚本。第一版 PR 只看注册表里的来源记录,评审 6554878186 指出包能自己写 `_packageId` 或 `source:
'code'` 绕过,所以补了 (1) (2) (3)。
- **风险与回滚**:这是一次收窄(`Metadata-protocol: narrowed`),objectql 和
cloud-connection 升 minor。受影响的只有两类:手写了别的包 `_packageId`
的条目(仓库里没有任何正常产出会这样写),和已安装包里的 stdio MCP 条目(以后要改用 http)。回滚就是 revert 这一个
PR。
- **先看**:`packages/objectql/src/engine.ts` 里的
`collectionItemOwnerStampRefusal`
和登记表,`packages/cloud-connection/src/marketplace-install-local-plugin.ts`
里的
`asMarketplaceRegistration`,`packages/services/service-automation/src/plugin.ts`
里的 `nonHostDeclaringPackageOf`。
- **两分钟验证**:`pnpm --filter @objectstack/dogfood exec vitest run
--maxWorkers=2
test/install-local-connector-package-ref.dogfood.test.ts`,预期 6 passed。
- **需要你做的**:不受管面,契约评审 PASS 后由席位落地,无需你操作。
## What changes
- **`packages/services/service-automation/src/plugin.ts`** — at the one
call site that builds each entry's `ConnectorProviderContext`,
`nonHostDeclaringPackageOf(entry, registry)` decides whose entry it is.
It answers host only for: no registry; no stamp; a registry row whose
`source` is not `'marketplace'`; or a residual owner that a `'code'`
door recorded. Every other stamp is treated as installed or unrecorded,
and that entry gets `createInstalledPackageRefRefusal` for both
`loadPackageFile` and `resolvePackagePath`. The refusal names the entry,
the package, the ref and the remedy, and never a host path. The
classifier's docblock states what it trusts (the row the registering
door wrote) and what it does not (the item's own stamp, the entry's
stated source).
- **`packages/objectql/src/engine.ts` / `plugin.ts`** (review must-fix
M1) — the `registerApp` collection loop, nested plugins, seed datasets,
`viewItems` and the manifest service's residual door all refuse an item
whose body arrives with a `_packageId` other than the registering
package's: `VALIDATION_ERROR`/400 at `_packageId`, the same envelope as
the permission-set refusal. A stamp the registration itself wrote onto a
caller's object is recorded and exempt. An install door always parses a
fresh body, so an installed item can never use that exemption.
`ObjectQL.recordResidualOwner` / `residualOwnerSource` record a residual
owner with its door's source.
-
**`packages/cloud-connection/src/marketplace-install-local-plugin.ts`**
(must-fix M2) — `asMarketplaceRegistration` registers every `packages[]`
member as `'marketplace'`, at install and at rehydrate. The boot
artifact's entry-over-door precedence is unchanged.
- Docs: one row in the failure-modes table of
`content/docs/automation/connectors.mdx`. Changesets:
`@objectstack/objectql` minor, `@objectstack/cloud-connection` minor,
`@objectstack/service-automation` patch. Each carries `Clause-②: no
(narrowing)`, `Metadata-protocol: narrowed`, a BREAKING banner and
`adr-0087: not-required (no-migration-prescription)`, in the shape of
`.changeset/26711-view-write-door-unknown-key-refused.md`.
## Tests
- Whole suites at `ffbdd0fb`: objectql 512 files / 9895 tests,
cloud-connection 64 / 776, service-automation 250 / 3177, all passed.
Typecheck exit 0 for objectql, cloud-connection, service-automation and
dogfood.
- New pins:
- `collection-item-owner-stamp.test.ts` (12);
- `marketplace-install-local-member-source.test.ts` (4);
- the `#28534` block in `connector-materialization.test.ts` (8);
- the dogfood file `install-local-connector-package-ref.dogfood.test.ts`
(6), which covers a foreign self-stamp refused with 422 through the real
door, and a two-member bundle whose member claims `source: 'code'` with
its file ref still refused.
- Ablations: M1 at `f8fa5255` (4 unit and 1 dogfood red, and the bypass
reached `ready`); M2 at `7ea3d092` (3 unit and 1 dogfood red; the file
is byte-identical since).
- Dogfood battery (the `(narrowing)` arm) at `ffbdd0fb`, `pnpm --filter
@objectstack/dogfood test`, whole package: 365 files passed, 3 skipped;
2940 tests passed, 28 skipped; no failure.
## Acceptance notes
- Reviews: the boundary record FAILed at `2be3fd45` (6554878186, M1 and
M2). The delta record FAILed at `f8fa5255` on the declaration only
(6555831347, D1). This head re-declares, and the record for this head is
pending.
- Kept: an installed package's stdio `mcp` entries are refused, because
the factory always asks `resolvePackagePath('.')`; an installed package
reaches MCP over http. The reload path keeps the existing ADR-0097
policy: a configuration fault is logged at `error` and the entry is
skipped, so the reason is in the reconcile log, not on `GET
/automation/connectors`.
- Carried elsewhere: authored and stored connector rows, #28594;
connectors missing after a restart, #28591; the build-door judge for
`_packageId`, #28627.
- Open (D1 shape): the changesets use `not-required
(no-migration-prescription)` with a prose remedy, as #26711 did. The
gate accepts a FROM / TO table only beside a registered ADR-0087 ledger
entry, which would be a new
`packages/spec/src/migrations/entries/semantic/` file. That is left to
this head's contract review.
## Verification
<details><summary>Base measurement at a52191f (service-automation built
from BASE's plugin.ts, same dogfood file)</summary>
```text
FAIL the installed entry naming a host FILE does not materialize ...
AssertionError: {"name":"pkgref_host_file","state":"ready","description":"Minimal OpenAPI document for the showcase's own health probe. ...","actions":["getHealth"]}: expected { name: 'pkgref_host_file', ...(7) } to be undefined
FAIL the installed entry asking for a host LOCATION does not materialize ...
AssertionError: {"name":"pkgref_host_cwd","state":"ready","description":"MCP connector exposing 1 tool(s) from a Model Context Protocol server.","actions":["echo_upper"]}: expected { name: 'pkgref_host_cwd', ...(7) } to be undefined
Test Files 1 failed (1)
Tests 2 failed | 2 passed (4)
```
Leg proof: tree blob of plugin.ts == BASE blob `a9bd5ae0`;
`ablation-dist-preflight @objectstack/service-automation
createInstalledPackageRefRefusal --absent`: marker absent from all 6
built files; restore: blob == HEAD `6aa82bd6`, `git diff HEAD` empty,
rebuilt, preflight: marker present in 2 built files, working tree clean.
</details>
<details><summary>Suites: service-automation whole unit suite,
typecheck, dogfood typecheck</summary>
```text
71597f1 pnpm --filter @objectstack/service-automation test Test Files 250 passed (250) · Tests 3175 passed (3175)
(the merge of bca27ad that followed touched no service-automation file)
2be3fd4 pnpm --filter @objectstack/service-automation typecheck exit 0 (tsc + check:test-typecheck: 0 files / 0 errors in the debt ledger)
2be3fd4 pnpm --filter @objectstack/dogfood typecheck exit 0 (both also exit 0 at 2047273)
2047273 tsc --listFiles: dogfood program holds the new dogfood file (1), service-automation tsconfig.test.json holds connector-materialization.test.ts (1)
```
</details>
<details><summary>After the fix: dogfood file and its error records (4
passed at 2047273, 71597f1 and 2be3fd4)</summary>
```text
Test Files 1 passed (1)
Tests 4 passed (4)
ERROR [Automation] failed to materialize connector instance 'pkgref_host_file' via provider 'openapi' (ADR-0097). {"error":"connector-openapi provider: connector 'pkgref_host_file' failed to read providerConfig.spec './src/system/connectors/status-openapi.json': connector instance 'pkgref_host_file' is declared by package 'com.dogfood.pkgref', which was installed into this runtime and has no package root on its disk, so its package file ref './src/system/connectors/status-openapi.json' is refused: ... Rebuild the package with a current `os build`, ..."}
ERROR [Automation] failed to materialize connector instance 'pkgref_host_cwd' via provider 'mcp' (ADR-0097). {"error":"connector instance 'pkgref_host_cwd' is declared by package 'com.dogfood.pkgref', ... so its package path ref '.' is refused: ..."}
```
</details>
<details><summary>Ablation of the new check (once)</summary>
`ablation-replace.mjs` anchor `return
registry?.getPackage?.(packageId)?.source === 'marketplace' ? packageId
: undefined;` replaced by `return void registry, undefined; /*
ABLATED_28534 */`: anchor 1 -> 0, blob `6aa82bd6` -> `e71f702b`. The
unit file imports `./plugin.js` from source, so no rebuild is in the
path. `-t 28534`: 4 failed (every case that expects an installed entry
refused) | 2 passed (the inline and host-entry controls). Restore: blob
== HEAD `6aa82bd6`, `git diff HEAD` empty.
</details>
<details><summary>Local gates at 2be3fd4 (the derived set; CI runs the
full farm)</summary>
```text
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack
(no paths; 5-path change set vs merge base bca27ad) -> 90 commands, each exit captured before any pipe
first pass: 88 exit 0; 2 exit 3 PREREQUISITE NOT MET (dists of seven packages outside this diff):
pnpm --filter @objectstack/spec run check:skill-examples -> built via turbo (44/44 cached) -> re-run exit 0
(279 prose examples type-check across 3 surfaces)
pnpm check:dual-build-cjs-loads -> re-run exit 0
(115 require entry points across 70 packages load)
dispatch-gates --ran (record with exit codes): 90 derived, 90 run, 0 NOT-MEASURED (a derived zero), 0 UNRUN
pnpm --filter @objectstack/spec check:generated at 71597f1: all 14 generated artifacts up to date
(the merge of bca27ad that followed touched no packages/spec file)
```
Not run locally, declared: `test:repo` (no repo-project test touched;
CI's Test Core runs it); the full dogfood battery (no `!` in the title,
`Clause-②: no`); `check-platform-checklist` (no export renamed or
deleted; two exports added to `plugin.ts`, the package's `index.ts`
unchanged).
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d78a7e4 commit d2c4874
12 files changed
Lines changed: 1343 additions & 9 deletions
File tree
- .changeset
- content/docs/automation
- packages
- cloud-connection/src
- objectql/src
- qa/dogfood/test
- services/service-automation/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 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 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 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 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 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
865 | 865 | | |
866 | 866 | | |
867 | 867 | | |
| 868 | + | |
868 | 869 | | |
869 | 870 | | |
870 | 871 | | |
| |||
0 commit comments