Repository navigation
ArtifactPackageEntrySchema's body half cannot describe the payload the load path actually registers #14242
Description
Found while implementing #14162 (PR #14240). Filed rather than worked around there: widening a spec surface is not a loader's decision.
The fact
ArtifactPackageEntrySchema (packages/spec/src/stack.zod.ts, landed by #14161) wraps the package body as manifest: ManifestSchema. ManifestSchema.objects is z.array(z.string()) — glob patterns, the authoring-time shape.
What actually reaches the ADR-0130 load path is an assembled payload whose objects are object definitions: AppPlugin flattens an artifact into { ...bundle.manifest, ...bundle } before calling manifest.register(), and ObjectQL.registerApp iterates those bodies as definitions.
So a full parse of a real artifact entry refuses it. Measured on cfa99f3d against the landed schema:
ArtifactPackageEntrySchema.safeParse({ manifest: assembledPackageBody })
→ success: false
→ manifest.objects.0: Invalid input: expected string, received object
The entry schema therefore describes the authoring-time manifest, while the key it was added for (packages[] on ObjectStackDefinitionSchema) is read at load time, where the body has a different shape. One schema is being asked to describe two different lifecycle stages of the same noun.
Why it matters
Any consumer that reaches for the obvious ArtifactPackageEntrySchema.safeParse(entry) as its gate will refuse exactly the artifacts the path exists to register — and it will look correct while doing so, because the schema is the surface's one declaration and the refusal names a real key.
PR #14240 works within it deliberately and narrowly: it applies the schema as a gate on the wrapper only (issues at the entry root or on manifest itself), and hands registerApp the caller's original body rather than a parsed clone. That is written down in the module header with this measurement, so the next reader does not discover it again. But it is a local accommodation of a surface-level mismatch, and the accommodation is the kind of thing that quietly becomes convention if the surface is never revisited.
What the decision is
Not obvious, hence a card rather than a patch. Roughly:
- A. Leave the schema authoring-shaped and make "the wrapper is the only part this schema gates at load time" an explicit, documented property of the surface — the status quo PR ADR-0130 D5+D7: register an artifact's N packages in topological order, reusing resolvePluginOrder #14240 assumes, but stated on the schema instead of in one consumer.
- B. Give the assembled/registered form its own declaration, so both stages are describable and a load-time consumer has something correct to parse against.
- C. Widen
ManifestSchema.objectsto accept both spellings — cheapest to write, and the one that most deserves scrutiny: a union that accepts both stages makes neither stage checkable, which is the tolerance-at-the-consumer shape this repo generally refuses.
No packages[]-shaped artifact is produced anywhere yet, so nothing is broken in the field today and there is time to decide this properly rather than under a deadline.
Activity
hotlong commentedon Sep 2, 2026
决定:取 B —— 装配 / 注册态的包体有自己的声明
维护者 2026-09-02 在 PM 会话(
session_01UHvF5hyiZjnCyExFnfQB8m)对"立多包编译路径卡、顺带把 #14242 按 B 方向收口"答复「同意」。据此:packages[]引用它;装载门从 ADR-0130 D5+D7: register an artifact's N packages in topological order, reusing resolvePluginOrder #14240 的 wrapper-only 改为对装配态声明完整 parse。ManifestSchema.objects不加宽——接受两个阶段的 union 会让两个阶段都不可校验,正是本卡自己指出的形状。实施与关闭在 #14439(
Fixes #14242与该卡同一 PR)。摘除needs-user-decision,保留priority:p2/domain:spec。Generated by Claude Code
wip(spec): declare the assembled package body (ADR-0130 D4, #14242 B)
fix(runtime): mount the scoped /packages dispatcher door and reconcil…
feat(spec): declare the assembled manifest stage on the package read API
fix(runtime): mount the scoped /packages dispatcher door and reconcil…
fix(spec): declare the assembled manifest stage on the package read A…
fix(runtime): name the ADR-0025 reading of `manifest.permissions` whe…
fix(plugin-security, spec): name the ADR-0025 reading of `manifest.pe…
test(spec): the first root-level test file group's titles state each …