Repository navigation
fix(service-automation,objectql,cloud-connection)!: an installed package's connector entry never reads the host's files - #28587
Conversation
…ve connector ref An entry whose declaring package was installed (registry source 'marketplace') is handed refusing loadPackageFile / resolvePackagePath members instead of ones anchored at the host's root. Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U Co-authored-by: Claude <noreply@anthropic.com>
…al; docs and changeset Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U Co-authored-by: Claude <noreply@anthropic.com>
…stalled-package-file-ref
…stalled-package-file-ref
📓 Docs Drift CheckThis PR changes 3 package(s): 40 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 29 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6833018f5db92e0b528e9dfd0a41995297a7984c && git checkout 6833018f5db92e0b528e9dfd0a41995297a7984c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0e78922ee07bda70879a49793159804fa6b41b77 ffbdd0fbe1dcea2d6b0aacf5e72b0c2a589c87ff && git checkout -B drift-repro 0e78922ee07bda70879a49793159804fa6b41b77 && git merge --no-ff ffbdd0fbe1dcea2d6b0aacf5e72b0c2a589c87ff
node scripts/docs-audit/affected-docs.mjs --json 0e78922ee07bda70879a49793159804fa6b41b77
|
|
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U Contract reviewServed-tier: Read at 2026-11-01T07:18Z, read-only: the diff ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe change holds against the scenario it was accepted on (an honest installed manifest naming a host file or location) and against the contract's words. It does not hold against the adversary the card names ("an installed package must not be able to bind another package's files by guessing a path"), because the classification trusts the package's own
Implemented-by: VERDICT: FAIL |
…age classifier trusts only what a registering door wrote registerApp and the residual door refuse a collection item whose body _packageId differs from the package registering it; install-local registers every packages[] member as 'marketplace'; the manifest service records each residual owner's door; a stamp no registration answers for is refused its package-relative refs. Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U Co-authored-by: Claude <noreply@anthropic.com>
… member source, and both bypasses over the real door; docs and changesets Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U Co-authored-by: Claude <noreply@anthropic.com>
…is not refused as the body's registerItem and stampPackage stamp the caller's object in place, so a module constant registered by one package and handed to another arrived wearing the first registration's _packageId. Those stamps are recorded where written and exempt; a stamp an object arrives with is still refused. Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U Co-authored-by: Claude <noreply@anthropic.com>
|
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U Contract reviewServed-tier: Read at 2026-11-01T08:59Z, read-only: the delta ① Derived judgments
② Semver levelThe declaration is wrong after M1, and this is the must-fix.
③ Boundary flags
④ Design judgmentThe change now holds against the adversary the card names: an installed package can no longer opt out of its classification by stamping
Implemented-by: VERDICT: FAIL |
…ctql and cloud-connection at minor Clause-2 no (narrowing) and Metadata-protocol narrowed, one adr-0087 disposition each. Pins the viewItems channel stamping its parse output and words the docs row as 'a _packageId other than its own package's'. Claude-Session: https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U Co-authored-by: Claude <noreply@anthropic.com>
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract-review-claim: Taken under Generated by Claude Code |
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract reviewServed-tier: Read at 2026-11-01T10:40Z, read-only: the net diff Check-runs on ① Derived judgmentsEach accept-set or public-surface change the diff implies, named right or wrong.
Must-fixes of the two earlier records on this head: M1 (self-stamped ② Semver levelThree changesets, read on the tree: The kind is right. D1's FROM / TO half. The earlier record asked for a FROM / TO prescription beside the One level nit, should-fix S9: the service-automation changeset grades ③ Boundary flags
④ Design judgmentThe narrowing holds against the published spec on Per file on the narrowing scope:
No must-fix. Should-fixes, one line each:
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U 席位核对ACCEPT at head
Should-fixes S9 to S12 of the PASS record, the seat's call:
Landing: not governed. The seat lands it now ( |
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U
Fixes #28534
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — no
skills/**file teaches_packageIdor an install door'ssource; the refusal concerns a key only the registration writes维护者速读
_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。What changes
packages/services/service-automation/src/plugin.ts— at the one call site that builds each entry'sConnectorProviderContext,nonHostDeclaringPackageOf(entry, registry)decides whose entry it is. It answers host only for: no registry; no stamp; a registry row whosesourceis not'marketplace'; or a residual owner that a'code'door recorded. Every other stamp is treated as installed or unrecorded, and that entry getscreateInstalledPackageRefRefusalfor bothloadPackageFileandresolvePackagePath. 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) — theregisterAppcollection loop, nested plugins, seed datasets,viewItemsand the manifest service's residual door all refuse an item whose body arrives with a_packageIdother 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/residualOwnerSourcerecord a residual owner with its door's source.packages/cloud-connection/src/marketplace-install-local-plugin.ts(must-fix M2) —asMarketplaceRegistrationregisters everypackages[]member as'marketplace', at install and at rehydrate. The boot artifact's entry-over-door precedence is unchanged.content/docs/automation/connectors.mdx. Changesets:@objectstack/objectqlminor,@objectstack/cloud-connectionminor,@objectstack/service-automationpatch. Each carriesClause-②: no (narrowing),Metadata-protocol: narrowed, a BREAKING banner andadr-0087: not-required (no-migration-prescription), in the shape of.changeset/26711-view-write-door-unknown-key-refused.md.Tests
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.collection-item-owner-stamp.test.ts(12);marketplace-install-local-member-source.test.ts(4);#28534block inconnector-materialization.test.ts(8);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 claimssource: 'code'with its file ref still refused.f8fa5255(4 unit and 1 dogfood red, and the bypass reachedready); M2 at7ea3d092(3 unit and 1 dogfood red; the file is byte-identical since).(narrowing)arm) atffbdd0fb,pnpm --filter @objectstack/dogfood test, whole package: 365 files passed, 3 skipped; 2940 tests passed, 28 skipped; no failure.Acceptance notes
2be3fd45(6554878186, M1 and M2). The delta record FAILed atf8fa5255on the declaration only (6555831347, D1). This head re-declares, and the record for this head is pending.mcpentries are refused, because the factory always asksresolvePackagePath('.'); an installed package reaches MCP over http. The reload path keeps the existing ADR-0097 policy: a configuration fault is logged aterrorand the entry is skipped, so the reason is in the reconcile log, not onGET /automation/connectors.authoredprovider-bound connector row resolves its package-relativeproviderConfigrefs against the HOST's root (the #28534 class, through the metadata door) #28594; connectors missing after a restart, install-local: after a restart an installed package's connector never materializes — thekernel:readyrehydrate runs after automation's boot reconcile and announces no reload #28591; the build-door judge for_packageId, spec: judge a collection item's_packageIdagainst its owning body atdefineStack/os validate/os build, so the stamp the boot now refuses (#28534) is refused where it is written #28627.not-required (no-migration-prescription)with a prose remedy, as spec: the view save door silently strips unknown top-level keys from the view envelope (200, not stored) while object, page and dashboard bodies refuse them 422 unrecognized_keys #26711 did. The gate accepts a FROM / TO table only beside a registered ADR-0087 ledger entry, which would be a newpackages/spec/src/migrations/entries/semantic/file. That is left to this head's contract review.Verification
Base measurement at a52191f (service-automation built from BASE's plugin.ts, same dogfood file)
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 == HEAD6aa82bd6,git diff HEADempty, rebuilt, preflight: marker present in 2 built files, working tree clean.Suites: service-automation whole unit suite, typecheck, dogfood typecheck
After the fix: dogfood file and its error records (4 passed at 2047273, 71597f1 and 2be3fd4)
Ablation of the new check (once)
ablation-replace.mjsanchorreturn registry?.getPackage?.(packageId)?.source === 'marketplace' ? packageId : undefined;replaced byreturn void registry, undefined; /* ABLATED_28534 */: anchor 1 -> 0, blob6aa82bd6->e71f702b. The unit file imports./plugin.jsfrom 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 == HEAD6aa82bd6,git diff HEADempty.Local gates at 2be3fd4 (the derived set; CI runs the full farm)
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 toplugin.ts, the package'sindex.tsunchanged).Generated by Claude Code