Repository navigation
Commit e756782
feat(runtime): a package export names each connector map it withheld; install-local names each as incomplete (#29746)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8CBufL2gFph9TKgWijoL1TU)_
Fixes #29342
Clause-②: yes (widening)
Metadata-protocol: none
One narrowing arm, named here in prose as the seat directed (amendment
6592752231): `POST /marketplace/install-local` now refuses a
`withheldMaps` value that the export's own declaration refuses,
answering `PLUGIN_MANIFEST_INVALID` before any write in either mode.
Before this change the door ignored the key whatever its value. No
producer wrote the key before this PR, so only a hand-written list can
be refused.
## 维护者速读
- 改了什么:包导出(`GET /packages/:id/export`)现在写出 `withheldMaps`(整库导出 `GET
/packages/export` 的每个条目同样带上,因其逐包复用同一导出),逐条列出被整张扣下的连接器 header/env
表;install-local 的托管与非托管两种安装都在应答里逐条点名这些不完整的表,并打一条 warn。
- 为什么改:导出读的是脱敏后的条目,这四张表被整张拿掉后,导入方分不清"被扣下"和"本来就没有"(#28640 裁决 ③A,席位裁决 A
6578196369)。
- 风险与回滚:导出对每个连接器多一次按名执行读;拿不到执行读就失败而不是省略键。安装门对格式错误的 `withheldMaps` 新增 400
拒绝。回滚即还原本 PR。
- 先看:`packages/runtime/src/domains/packages.ts` 的
`withheldConnectorMaps`;`packages/cloud-connection/src/marketplace-install-local-plugin.ts`
的 `judgeWithheldMaps`
及两处应答;`packages/spec/src/api/package-lifecycle.zod.ts` 的描述改动。
- 两分钟验证:`pnpm --filter @objectstack/runtime exec vitest run
src/domains/packages-export-withheld-maps.test.ts`,预期 6 条全绿,第一条列出四个位置。
- 需要你做的:无。契约复核 PASS(6594653702),两轮增量复核 PASS(6595333171、6596808111),席位落地。
## What changes
- **The export writes `withheldMaps`.** `assemblePackageManifest`
(runtime `packages.ts`) now lists `{ type: 'connector', name, path }`
for every connector map the served read withheld whole. These are the
four positions `PackageExportManifestSchema.withheldMaps` declares:
`providerConfig.headers`, `providerConfig.defaultHeaders`,
`providerConfig.transport.headers` and `providerConfig.transport.env`.
The new hunk sits at the end of the function, after the `label`,
`namespace` and `dependencies` lines (the last two from #29617), apart
from the per-package `declaresOverrideOf` filter that #29687 (card
#29649) edits.
- **How the verdict is read.** The export writes SERVED items, where a
withheld map is simply absent. So each exported connector's stored body
is read on the protocol's by-name execution face,
`getMetaItemForExecution({ type: 'connector', name, packageId })`, and
handed to the same `connector` redactor the served read ran
(`getMetadataTypeRedactor('connector')`). Every position its
`redactedKeys` names that the declaration lists is written. That face's
docblock allows a door to read a decision off it, as long as no byte of
the body reaches the response. Only the position leaves: no header or
variable name, no value.
- **Why the by-name read is the item the per-package list served.** The
two reads differ only in the effective layer's declared-override step
(ADR-0146 D8). `connector` is not an overridable type: it has no row in
`OVERRIDE_TYPE_CLASSIFICATION`, and the write gate refuses an override
declaration on a type outside it. So under the package's own id, the
by-name read answers the package's own connector, the one the
per-package list served.
- **No guessed verdict.** If the protocol has no execution face, or the
face answers no body for an exported connector, the export fails (500)
instead of omitting the key. The declaration says that from an exporter
that writes the key, an absent key means nothing was withheld.
- **Both install modes read it.** `POST /marketplace/install-local`
(cloud-connection, the door the Studio import button posts to) parses
the body's `withheldMaps` through
`PackageExportManifestSchema.shape.withheldMaps` before any write. The
managed and unmanaged answers each gain `withheldMaps`, the list as the
export wrote it, and one `warn` line names every map. The key is left
out when the body carries no list, so that answer's key set is
unchanged. The package is accepted and never refused because a map is
incomplete (ruling A).
- **Spec.** The `[PLANNED — not yet served]` marker is dropped from
`withheldMaps`' describe, and its pin in `package-lifecycle.test.ts`
flips to assert that the marker is absent. The module and schema
docblocks now say the key is served. The comment on the `door` row in
`kernel/unmanaged-install.ts` says the same (comment only, approved in
amendment 6592752231).
`content/docs/references/api/package-lifecycle.mdx` is regenerated by
`check:generated --fix`. After merging #29742 (nav served) and #29755
(#28914, the whole-deployment bundle served), no member of
`package-lifecycle.zod.ts` is declared ahead any more, so the header's
declared-ahead paragraph is removed (85d56fc) and the mdx regenerated.
- **Changeset:** `29342-export-withheld-connector-maps.md`, patch for
runtime, cloud-connection and spec, carrying both declaration lines and
the narrowing arm in prose.
## Tests
- `packages/runtime/src/domains/packages-export-withheld-maps.test.ts`
(6 tests). It uses the real dispatcher, with a protocol double whose
served list runs the real `connector` redactor. It checks: all four
positions in order; the exported items as served, with no header name or
value in the manifest; a connector with no map, or with a cleared map,
is not listed; a package with no connectors asks the face nothing; a
protocol without the face, and a missing body, each fail the export.
-
`packages/cloud-connection/src/marketplace-install-local-withheld-maps.test.ts`
(7 tests), at the plugin's real route. Managed and unmanaged answers
each name the list, with one warn line. A body without the list gets no
key and no warn. Three malformed lists are refused
`PLUGIN_MANIFEST_INVALID`, 400, in both modes, with nothing registered,
ledgered or imported.
-
`packages/qa/dogfood/test/export-withheld-connector-maps.dogfood.test.ts`
(4 tests) runs on the real showcase composition. It installs a package
with three connectors, two carrying header maps, then exports it. The
export lists the three maps and carries no header name or value. A
managed re-install of the export names them. On a second boot that never
held the package, an unmanaged install of the export also names them.
<details><summary>Red first, by reverse verification on the base source
(744211f)</summary>
`git restore --source=744211fc` of the two runtime and cloud-connection
source files, with on-disk marker counts `0 0` and runtime and
cloud-connection rebuilt. Then `ablation-dist-preflight` confirmed that
`withheldConnectorMaps` is absent from `packages/runtime/dist`.
- runtime unit: 3 failed, 3 passed. The "lists every withheld position"
test and both no-guess tests were red.
- cloud-connection unit: 5 failed, 2 passed. Both naming tests and the 3
refusals were red.
- dogfood: 3 failed, 1 passed. The export listing, the managed answer
and the unmanaged answer were red.
Restored with `git restore --source=HEAD --staged --worktree`, `git
status --porcelain` empty, rebuilt, preflight found the marker present
again, and the dogfood ran 4/4 green.
</details>
<details><summary>Ablation of the narrowing arm (seat ask)</summary>
`node scripts/ablation-replace.mjs` turned `if (!declared.success)` into
`if (!declared.success && false)` in `judgeWithheldMaps`: anchor 1 → 0,
blob f2cf413 → 84f0fc0a. Result: 3 failed | 4 passed, and the failures
were exactly the three `PLUGIN_MANIFEST_INVALID` cases. Restored: the
blob equals HEAD, and `git diff HEAD` is empty.
</details>
## Acceptance notes
- **The export carries no `type` or `installModes`, so it does not
import unmanaged as written.** Measured on the showcase:
`importUnmanagedPackage` refused the bare export with `manifest.type:
Invalid option`. The per-package export writes only `id`, `name`,
`version`, `label`, `namespace`, `dependencies` and its collections.
This belongs to the export's manifest-key family; carrier: #28914. The
dogfood adds the two keys an operator adds before importing an export as
a copy, and its header says so.
- **The unmanaged install answers "the reason is in the server log" for
a 4xx that it never logs.** Measured in the same run: a 400
`VALIDATION_ERROR` answered `The unmanaged install of
'com.dogfood.withheldmaps' failed; the reason is in the server log.`,
and the server log had no line with the reason. `installUnmanaged` logs
only when `resolved.status >= 500`. Reported to the seat for filing
(class a). Not fixed here. Filed by the seat as #29822.
- **Webhook `headers`** (`credential: 'map'`) are dropped in the same
silent way. Ruling A keeps them out of scope.
- **Serial note:** #29687 (card #29649) also edits
`assemblePackageManifest`. It landed at 33ee9aa and is merged into this
branch (009c0b7). After the merge, this PR's hunk is still separate
from its `declaresOverrideOf` filter. #29742 (seat 3) and #29755
(#28914, seat 4) landed after it and are merged in (c5f0877 at
b3d1415). `assemblePackageExportBundle` composes the per-package
export, so each bundle entry carries `withheldMaps` too;
`PackageExportBundleSchema.packages` is `PackageExportManifestSchema[]`,
which already declares the key.
## Verification
Unit suites, typecheck, gates and lint below were measured at
`583fce44`, before the merge, and re-run at head `c5a34147`, after
merging `origin/main` (33ee9aa). Post-merge figures are given where
they differ.
- `pnpm --filter @objectstack/runtime test`: 530 files, 8518 passed, 19
skipped; after the merge, 532 files, 8529 passed, 19 skipped. `pnpm
--filter @objectstack/cloud-connection test`: 68 files, 820 passed, the
same after the merge. Spec `package-lifecycle.test.ts` and
`unmanaged-install.test.ts`: 63 passed; after the merge, 67 passed.
Dogfood file: 4/4 after the merge. `pnpm --filter @objectstack/runtime
--filter @objectstack/cloud-connection typecheck`: exit 0.
- `pnpm --filter @objectstack/spec check:generated`: `check:docs` was
stale and was fixed with `--fix` (one artifact). The other 13 were up to
date.
- `node scripts/pm/dispatch-gates.mjs --commands`: 105 families. All 105
exit 0, three of them after their prerequisites were met: the clone was
unshallowed for `check-plugin-teardown-shape --self-test`, and the
client and plugin packages were built for `check:skill-examples` and
`check:dual-build-cjs-loads`. `--ran` reconciliation: 105 derived, 105
run, 0 NOT-MEASURED, 0 UNRUN.
- `pnpm lint`: exit 0.
- Full dogfood battery: not run. The title has no `!` and Clause-② has
no `(narrowing)` arm.
Re-run at head `85d56fc4` (after merging `origin/main` b3d1415):
typecheck for spec, runtime and cloud-connection exit 0; spec
package-lifecycle and unmanaged-install 67 passed; runtime 535 files,
8560 passed, 19 skipped; cloud-connection 68 files, 820 passed; dogfood
file 4/4; `dispatch-gates --commands` 105 derived, 105 exit 0, `--ran`
105/105, 0 NOT-MEASURED.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01JQwxbYXRMR5S5B7JSmKkV7
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbYXRMR5S5B7JSmKkV7)_
<!-- signed: domain:server#5 · os-warren ·
session_01JQwxbZUR3xvHyDPCjNL5P4 -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 7a25b39 commit e756782
10 files changed
Lines changed: 691 additions & 41 deletions
File tree
- .changeset
- content/docs/references/api
- packages
- cloud-connection/src
- qa/dogfood/test
- runtime/src/domains
- spec/src
- api
- kernel
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | 46 | | |
54 | 47 | | |
55 | 48 | | |
| |||
335 | 328 | | |
336 | 329 | | |
337 | 330 | | |
338 | | - | |
| 331 | + | |
339 | 332 | | |
340 | 333 | | |
341 | 334 | | |
| |||
352 | 345 | | |
353 | 346 | | |
354 | 347 | | |
355 | | - | |
| 348 | + | |
356 | 349 | | |
357 | 350 | | |
358 | 351 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
78 | 84 | | |
79 | 85 | | |
80 | 86 | | |
| |||
187 | 193 | | |
188 | 194 | | |
189 | 195 | | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
190 | 199 | | |
191 | 200 | | |
192 | 201 | | |
| |||
602 | 611 | | |
603 | 612 | | |
604 | 613 | | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
605 | 665 | | |
606 | 666 | | |
607 | 667 | | |
| |||
1925 | 1985 | | |
1926 | 1986 | | |
1927 | 1987 | | |
| 1988 | + | |
| 1989 | + | |
| 1990 | + | |
| 1991 | + | |
| 1992 | + | |
| 1993 | + | |
| 1994 | + | |
| 1995 | + | |
| 1996 | + | |
1928 | 1997 | | |
1929 | 1998 | | |
1930 | 1999 | | |
| |||
1940 | 2009 | | |
1941 | 2010 | | |
1942 | 2011 | | |
| 2012 | + | |
1943 | 2013 | | |
1944 | 2014 | | |
1945 | 2015 | | |
| |||
2308 | 2378 | | |
2309 | 2379 | | |
2310 | 2380 | | |
| 2381 | + | |
2311 | 2382 | | |
2312 | 2383 | | |
2313 | 2384 | | |
| |||
2333 | 2404 | | |
2334 | 2405 | | |
2335 | 2406 | | |
| 2407 | + | |
| 2408 | + | |
| 2409 | + | |
| 2410 | + | |
2336 | 2411 | | |
2337 | 2412 | | |
2338 | 2413 | | |
| |||
3538 | 3613 | | |
3539 | 3614 | | |
3540 | 3615 | | |
3541 | | - | |
| 3616 | + | |
| 3617 | + | |
| 3618 | + | |
| 3619 | + | |
3542 | 3620 | | |
3543 | 3621 | | |
3544 | 3622 | | |
3545 | 3623 | | |
3546 | | - | |
| 3624 | + | |
| 3625 | + | |
| 3626 | + | |
| 3627 | + | |
| 3628 | + | |
3547 | 3629 | | |
3548 | | - | |
| 3630 | + | |
3549 | 3631 | | |
3550 | 3632 | | |
3551 | 3633 | | |
| |||
3598 | 3680 | | |
3599 | 3681 | | |
3600 | 3682 | | |
| 3683 | + | |
3601 | 3684 | | |
3602 | 3685 | | |
3603 | 3686 | | |
| |||
3613 | 3696 | | |
3614 | 3697 | | |
3615 | 3698 | | |
| 3699 | + | |
| 3700 | + | |
| 3701 | + | |
| 3702 | + | |
3616 | 3703 | | |
3617 | 3704 | | |
3618 | 3705 | | |
| |||
0 commit comments