Skip to content

Commit e167d43

Browse files
committed
fix(spec): keep the assembled-body aliases key-precise but element-loose — the per-key mapped alias emitted the collections shape a second time and OOM'd the type-check-debt re-measure (#14439)
`AssembledPackageBody` / `AssembledPackageBodyParsed` referenced `(typeof STACK_DEFINITION_COLLECTIONS_SHAPE)[K]` per collection key. Because those aliases are exported, the declaration emit wrote the shape const into `stack.zod-*.d.ts` a second time (21,443 lines beside the 42,449 the stack schema already inlines), and every consumer program re-inferred all ~35 collection input/output types once more. `Type Check · debt ledger` on the PR went red: the `qa/http-conformance` TEST_DEBT re-measure exceeded the 4096 MB ceiling the gate pins as CI's, while the merge-queue run on the base passed the same step. Reproduced locally (exit 3) on b4b9732. The aliases now keep the DERIVED key set (`AssembledPackageBodyKey`, still read off `COMPOSE_KEY_DISPOSITIONS`) and type each collection as `unknown`. Nothing exported references the shape const any more, so the second copy is not emitted and the per-key inference does not run. `assembledPackageBodyShape()` keeps its `Pick<typeof STACK_DEFINITION_COLLECTIONS_SHAPE, K>` return type (internal, not emitted), so a disposition key missing from the collections shape is still a compile error, and the RUNTIME schema still carries every collection's full declaration — refusals are unchanged. One spec pin narrows its element access at the point of use, as every reader of an assembled body already does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m
1 parent b4b9732 commit e167d43

2 files changed

Lines changed: 35 additions & 10 deletions

File tree

‎packages/spec/src/assembled-package-body.test.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,13 @@ describe("ADR-0130 D4 — `manifest: 'preserve'` assembles each input stack", ()
240240
'com.example.multi.orders',
241241
'com.example.multi.core',
242242
]);
243-
expect(parsed[1].manifest.objects?.map((o) => o.name)).toEqual(['crm_account']);
244-
expect(parsed[0].manifest.objects?.map((o) => o.name)).toEqual(['crm_order']);
243+
// Collections inside an assembled body are `unknown` at the TYPE level (see
244+
// the note above `AssembledPackageBodySchema` — the element-precise alias
245+
// OOM'd consumer type-checks); narrow at the point of use, as every reader
246+
// of an assembled body does.
247+
const objectNames = (objects: unknown): string[] | undefined =>
248+
(objects as Array<{ name: string }> | undefined)?.map((o) => o.name);
249+
expect(objectNames(parsed[1].manifest.objects)).toEqual(['crm_account']);
250+
expect(objectNames(parsed[0].manifest.objects)).toEqual(['crm_order']);
245251
});
246252
});

‎packages/spec/src/stack.zod.ts‎

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,26 +1028,45 @@ function assembledPackageBodyShape(): Pick<typeof STACK_DEFINITION_COLLECTIONS_S
10281028
*/
10291029
export type AssembledPackageBody =
10301030
Omit<z.input<typeof ManifestSchema>, AssembledPackageBodyKey>
1031-
& Partial<{ [K in AssembledPackageBodyKey]: z.input<(typeof STACK_DEFINITION_COLLECTIONS_SHAPE)[K]> }>;
1031+
& Partial<Record<AssembledPackageBodyKey, unknown>>;
10321032

10331033
/** Post-parse shape of {@link AssembledPackageBody} — defaults applied, transforms run (ADR-0122). */
10341034
export type AssembledPackageBodyParsed =
10351035
Omit<z.infer<typeof ManifestSchema>, AssembledPackageBodyKey>
1036-
& Partial<{ [K in AssembledPackageBodyKey]: z.infer<(typeof STACK_DEFINITION_COLLECTIONS_SHAPE)[K]> }>;
1036+
& Partial<Record<AssembledPackageBodyKey, unknown>>;
10371037

10381038
/*
1039-
* ANNOTATED, not inferred, and the annotation is derived rather than
1040-
* transcribed (the two aliases above are mapped over the same key set the value
1041-
* is built from). Inferring it emits the manifest and all ~35 collection
1039+
* ANNOTATED, not inferred, and the annotation is KEY-precise but
1040+
* ELEMENT-loose — both halves measured, neither a preference.
1041+
*
1042+
* Not inferred: inferring it emits the manifest and all ~35 collection
10421043
* declarations a SECOND time inside `ObjectStackDefinitionSchema`'s own
10431044
* inferred type, which `tsc` refuses to serialize at all:
10441045
*
10451046
* TS7056: The inferred type of this node exceeds the maximum length the
10461047
* compiler will serialize. An explicit type annotation is needed.
10471048
*
1048-
* Measured on this change: the stack schema's `.d.ts` node was already near
1049-
* that ceiling, and adding `packages`' expanded element type pushed it over.
1050-
* The annotation keeps the declaration one named reference wide.
1049+
* Element-loose: the two aliases above name every collection KEY (the set is
1050+
* still derived from `COMPOSE_KEY_DISPOSITIONS`, see `AssembledPackageBodyKey`)
1051+
* but type each collection as `unknown` rather than mapping
1052+
* `z.input<(typeof STACK_DEFINITION_COLLECTIONS_SHAPE)[K]>` per key. The mapped
1053+
* form was tried first and measured on #14513: referencing the shape const from
1054+
* an EXPORTED type makes the declaration emit write `STACK_DEFINITION_COLLECTIONS_SHAPE`
1055+
* into `stack.zod-*.d.ts` a second time (21,443 lines beside the 42,449 the
1056+
* stack schema already occupies), and every consumer program then re-infers all
1057+
* ~35 collection input/output types once more — the `qa/http-conformance`
1058+
* type-check-debt re-measure went from green to out-of-memory at the 4096 MB
1059+
* ceiling `scripts/check-type-check-coverage.mjs` pins as CI's. With the keys
1060+
* held and the elements `unknown`, nothing here references the shape const, the
1061+
* second copy is not emitted, and the per-key inference does not run.
1062+
*
1063+
* What a consumer loses is only the STATIC element type of a collection inside
1064+
* an assembled body (`body.objects` is `unknown`, not `ObjectDefinition[]`); the
1065+
* RUNTIME schema below still carries every collection's full declaration, so a
1066+
* wrong-shaped body is refused exactly as before. Readers of assembled bodies
1067+
* (`compile.ts`, `artifact-packages.ts`) already treat them as records and
1068+
* narrow at the point of use; that is the honest shape for a payload whose
1069+
* collections are attributed per package only at composition time.
10511070
*/
10521071
export const AssembledPackageBodySchema: z.ZodType<AssembledPackageBodyParsed, AssembledPackageBody> =
10531072
lazySchema(() =>

0 commit comments

Comments
 (0)