Repository navigation
feat(spec): the package export manifest declares withheldMaps, classified door for the unmanaged import - #29521
Conversation
…fied door for the unmanaged import
PackageExportManifestSchema gains the fixed key withheldMaps: each map the
export withheld whole ({ type: 'connector', name, path }, path one of the
connector redactor's four map positions). UNMANAGED_INSTALL_KEY_DISPOSITIONS
classifies it door, and the export's label (declared by the export contract,
never classified) door too, so an unmanaged import accepts an export as
written. The table's totality test now reads the export's fixed keys as the
third schema a package body is read with.
Claude-Session: https://claude.ai/code/session_01ADHkmPi3pqv7Vkx4kymD87
Co-authored-by: Claude <noreply@anthropic.com>
…ness counts for withheldMaps; add the changeset Claude-Session: https://claude.ai/code/session_01ADHkmPi3pqv7Vkx4kymD87 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 168 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 36d64618552ec31dbf211f89ebcec16532ae0932 && git checkout 36d64618552ec31dbf211f89ebcec16532ae0932
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0bfb225e4d6b359d8481ec3f5351221c634ed35c d558859f50d9e6f3bc0f904bb639334a3572402e && git checkout -B drift-repro 0bfb225e4d6b359d8481ec3f5351221c634ed35c && git merge --no-ff d558859f50d9e6f3bc0f904bb639334a3572402e
node scripts/docs-audit/affected-docs.mjs --json 0bfb225e4d6b359d8481ec3f5351221c634ed35c
|
…ack/spec minor, as the level gate reads the moved authorable-surface shard Claude-Session: https://claude.ai/code/session_01ADHkmPi3pqv7Vkx4kymD87 Co-authored-by: Claude <noreply@anthropic.com>
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Head-sha: 维护者速读
席位意见:可落地,等契约复审。 席位核对 — ACCEPT ·
|
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Contract-review-claim: session_01ADHkmPi25XXKx92Xmg4viS · head 27e2a7a · 2026-11-02T17:38Z |
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Contract reviewServed-tier: Inputs read: card #29373 body and every comment (triage 6580300184, claim 6582245473, dev report 6583817463, claim amendment 6583855373); #28640 director record 6566912252 (ruling ③A); #29342 dev report 6578135265 and seat ruling 6578196369 (option A); PR #29521 body, 8 files, net diff ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: FAIL |
…ame the exception in the module header No export writes withheldMaps yet and no install door reads it, so its describe now opens [PLANNED — not yet served] (ADR-0049), says what absence means from an exporter that predates the key, and a test pins the marker. The module header names the two members declared ahead of their producer. Claude-Session: https://claude.ai/code/session_01ADHkmPi3pqv7Vkx4kymD87 Co-authored-by: Claude <noreply@anthropic.com>
…ed withheldMaps describe Claude-Session: https://claude.ai/code/session_01ADHkmPi3pqv7Vkx4kymD87 Co-authored-by: Claude <noreply@anthropic.com>
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Contract reviewServed-tier: Delta review on the FAIL record for head ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Hold re-enqueueing until the maintainer approves the new key name. ⛔ Not a claim.
Generated by Claude Code |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 41503306729 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
…eep both sides' tests in package-lifecycle.test.ts Claude-Session: https://claude.ai/code/session_01ADHkmPi3pqv7Vkx4kymD87 Co-authored-by: Claude <noreply@anthropic.com>
…ness counts on the merged tree Claude-Session: https://claude.ai/code/session_01ADHkmPi3pqv7Vkx4kymD87 Co-authored-by: Claude <noreply@anthropic.com>
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Merge-only head The merge queue removed the PR 2026-11-02T19:07Z. In the merge group, the strictness
There is no semantic change, so the seat carries the CONTRACT_REVIEW_TIER delta PASS on |
Requested by Steve · project thread
Fixes #29373
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: none — an export response key the runtime writes; no metadata author writes it, so no skill or authoring doc teaches it
维护者速读
GET /packages/:id/export的契约)新增可选键withheldMaps,逐条列出导出时被整张扣下的连接器请求头 / 环境变量表的位置(只列位置,不列任何头名或变量名);非托管导入的键分类表把它记为door。导出一直声明的label在该表里也从未分类,同 PR 一并记为door。label或withheldMaps的导出被非托管导入整包拒收;改动后两者都被接受、新基座清单不带它们。回滚即 revert 本 PR。packages/spec/src/api/package-lifecycle.zod.ts(新键与 describe)、packages/spec/src/kernel/unmanaged-install.ts(两行door及注释)、packages/spec/src/kernel/unmanaged-install.test.ts(全量性测试改为同时读导出清单的固定键)。pnpm --filter @objectstack/spec exec vitest run src/api/package-lifecycle.test.ts src/kernel/unmanaged-install.test.ts,预期 2 个文件全部通过,含「refuses a list of the withheld header names」与「classifies every fixed key of the package export」两条新用例。New names (proposed; the seat's call)
PackageExportManifestSchema.withheldMaps: a new fixed key on a published contract.type(the literalconnector),name,path(closed set:providerConfig.headers,providerConfig.defaultHeaders,providerConfig.transport.headers,providerConfig.transport.env).api-surfacedoes not move.withheld(shorter, but reads as a boolean),incompleteMaps(names the import's reading rather than the export's act),redactedMaps(collides with the redactor's internalredactedKeys, which is deliberately never put on the wire).What changes
packages/spec/src/api/package-lifecycle.zod.ts:PackageExportManifestSchemagains the optional fixed keywithheldMaps, an array of strict{ type: 'connector', name, path }entries. The docblock now says five fixed keys and explains the key ([Decision] connector credential follow-ups after #28596: header-value advisory, legacy auth keys at rest, whole-map withholding #28640 ruling ③A).PackageExportManifestSchemastays the same exported constant, so the bundle schema in draft feat(spec): declare the whole-deployment export, every installed package as installable packages in one read (PackageExportBundleSchema) #29100 composes it by identity as before.packages/spec/src/kernel/unmanaged-install.ts:UNMANAGED_INSTALL_KEY_DISPOSITIONSgainswithheldMaps: 'door'andlabel: 'door'. The module note and the table's docblock now name the export's fixed keys as the third schema a package body is read with; thedoordefinition says the door may set a key aside (the export'slabel).content/docs/references/api/package-lifecycle.mdx(the key and its nested shape),packages/spec/authorable-surface/api.json(one row:api/PackageExportManifest:withheldMaps),docs/audits/2026-07-unknown-key-strictness-ledger.counts/api.md(api/sites 487 to 488, the new.strict()entry)..changeset/29373-export-withheld-maps.md:@objectstack/specminor, with the two declaration lines copied from the claim as the seat amended it (Metadata-protocol: added).The path vocabulary, measured
The card spells three of the four maps without their root (
defaultHeaders,transport.headers,transport.env). On main the connector redactor'sCONNECTOR_CREDENTIAL_MAP_PATHS(packages/spec/src/integration/connector-credential-redaction.ts) holds all four underproviderConfig, andredactConnectorItemreports them inredactedKeysasproviderConfig.headers,providerConfig.defaultHeaders,providerConfig.transport.headers,providerConfig.transport.env. The closed enum uses that spelling, so the export can copy the redactor's own report. A closed set of four is right for this card: it is exactly the set the redactor withholds whole as maps. The redactor's other positions (undeclaredauthkeys,authenticationkeys, credential URL parts) are single values, not maps, and webhookheadersis out of scope by the seat ruling. A test holds the enum equal to the redactor's list and to itsredactedKeyson a real redaction, so the two cannot drift.label, measuredThe card noted (not measured) that the export's top-level
labelis not classified. Measured on this branch's base (04e0c2c0):PackageExportManifestSchemadeclareslabel;UNMANAGED_INSTALL_KEY_DISPOSITIONShas no row for it; the import planner's default arm refuses an unclassified key. A probe callingplanUnmanagedImportwith{ id, name, version, label: 'Example CRM' }, run against a spec build with the two rows removed, answeredUNMANAGED_INSTALL_UNCOPYABLE"label— not a key a package body declares, so the import cannot place it" (and the same forwithheldMaps); against this PR's build both are accepted and the base manifest is{ name, version, id, namespace }. So it is a real gap, classified here and declared as a deviation. It isdoor, notidentity: the base is installed through the closedPackageInstallManifestSchema, which has nolabel, so carrying it would refuse the base. Reach: the export writeslabelonly frompkg.manifest.label ?? pkg.label, and no schema in this tree declares either (ManifestSchemaandPackageInstallManifestSchemaare closed withoutlabel), so no declared producer here emits it today; the gap is between two spec contracts.Tests
New:
package-lifecycle.test.ts: an export carryingwithheldMapsparses and is preserved; the key is optional; the path enum equalsCONNECTOR_CREDENTIAL_MAP_PATHSand the redactor'sredactedKeys; five refusals (a position outside the four, a position without itsproviderConfigroot, a non-connector type, an entry carrying a list of header names, an entry with no name).unmanaged-install.test.ts: "classifies every fixed key of the package export", and a pin thatwithheldMapsandlabelaredoor.Forced edits (named):
unmanaged-install.test.ts"classifies nothing neither schema declares": renamed "classifies nothing no schema declares", and its declared set now includes the export's fixed keys. Without this, the newwithheldMapsrow would red it as a stale row.unmanaged-install.test.tsandpackage-lifecycle.test.tsdescribe titles: "both schemas" to "the three schemas"; "four fixed keys" to "five fixed keys"; "requires exactly the four fixed keys" to "requires only the three identity keys" (the old title was already loose:labelwas optional).Reverse check (each table row deleted with
ablation-replace.mjs, thenunmanaged-install.test.ts): deletingwithheldMaps: 'door'reds "classifies every fixed key of the package export" and the door pin; deletinglabel: 'door'reds the same two. Tree restored to HEAD each time (git diff HEADempty).Acceptance notes
Check Changesetlevel axis: the claim first readMetadata-protocol: none; the gate refuses it because the regeneratedpackages/spec/authorable-surface/api.jsonshard moved, and refusesaddedwith apatchchangeset. The seat amended the claim toadded, matching main's precedent for API response keys (.changeset/28737-record-page-timeline-count.md,28722-customizations-contributions.md), and the changeset is now@objectstack/spec: minor(commit27e2a7a5). Measured offline on head27e2a7a5with this body:check-changeset-no-major.mjs --base origin/main --event FILEexits 0 (LEVEL AXIS passes).04e0c2c0, not merged with main: main is 20 commits ahead, none touching these 8 files or theapishards they regenerate (git diff HEAD...origin/mainon spec touches ui/contracts/conversions/migrations only), andgit merge-treeis clean. Whichever of this PR and feat(spec): declare the whole-deployment export, every installed package as installable packages in one read (PackageExportBundleSchema) #29100 lands second merges main.headers(the same whole-map case) stays out of scope by the seat ruling on packages: a package export marks each withheld connector header/env map, so an import knows it is incomplete (#28640 ruling 3A) #29342; wideningtypeto a union later is a widening.Verification
Readings on head
bb1718cc(base04e0c2c0), all underos-verify-lock.sh(VERDICT command-exit 0 each). Head27e2a7a5adds one commit that edits only the changeset's level and declaration line (no package source, no generated artifact), so these readings carry to it; the changeset gate was re-run on27e2a7a5.Spec and consumer suites
turbo run build --filter='./packages/**': 78/78 tasks, 0 cached.pnpm --filter @objectstack/spec test: Test Files 792 passed (792), Tests 23529 passed, 1 todo.pnpm --filter @objectstack/spec typecheck: exit 0.pnpm --filter @objectstack/spec check:generated: 2 of 14 stale before--fix(check:docs,check:strictness-ledger), both regenerated and committed;api-surfacenot stale (no new export).@objectstack/metadata-protocolpackage-unmanaged-install.test.ts+protocol.duplicate-package-install-refusal.test.ts: 2 files, 68 tests passed.pnpm --filter @objectstack/client test: 54 files, 693 tests passed;typecheckexit 0 (PackageExportManifestis the client'spackages.exportreturn type).@objectstack/runtimepackages-export-override-declaration,packages-export-per-package-layer,packages-seed-apply-read-decorations,packages-vetted-org-source: 4 files, 114 tests passed.@objectstack/cloud-connectionmarketplace-install-local-install-modes.test.ts: 23 tests passed.--absentpassed)labelandwithheldMapseach refusedUNMANAGED_INSTALL_UNCOPYABLE; after, both accepted.Derived gates (dispatch-gates --commands)
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 103 families atbb1718cc; each ran with stdin closed, all 103 exit 0. Reconciliation: "103 derived, 103 run, 0 NOT-MEASURED, 0 UNRUN" (a DERIVED zero). Changed lines 161 (+144 / -17) against the 3000 threshold.