Multi-Stack Output
An estate past one stack splits into side-by-side projects — each stack its own project, its own flat src/, its own build. This page covers what that means at the serialization layer. For the project-level view, see Multi-Stack Projects.
What chant build partitions on
Section titled “What chant build partitions on”One invocation, one output path. Within that invocation, chant partitions entities by lexicon — a project declaring aws and k8s yields one serialized document per lexicon, since each lexicon serializes to its own deployable artifact (a CloudFormation template, a Kubernetes manifest, a CI config).
Directory structure is not a partitioning input. chant build src on a project whose src/ holds subdirectories produces one build of everything under src/, not one output per subdirectory.
So a side-by-side project builds exactly the way a single-stack project does, because that is what it is:
chant build src -o dist/template.jsonThere is no multi-stack build mode. Several stacks means several projects, each running that.
Cross-stack references
Section titled “Cross-stack references”References cross by name, declared on both sides.
The producer marks a value for export. When the serializer encounters a StackOutput entity it emits it into the template’s outputs section:
export const clusterArn = output(shared.cluster.Arn, "ClusterArn");The consumer declares a parameter that the value is fed into at deploy time — --parameter-overrides for CloudFormation, each lexicon’s own mechanism elsewhere:
export const clusterArn = new Parameter("String", { description: "ECS Cluster ARN" });Nothing is resolved at build time and nothing is shared between the two builds. Each template is complete and independently valid; the handle names are the contract.
What the IR carries
Section titled “What the IR carries”Both halves are surfaced so a consumer can reconstruct the link without parsing templates:
exports— the outputname(the handle another stack imports by), the node producing it, and the producer-side attribute it reads.imports— the parameternameand its node.
Match an import’s name against another stack’s export name to draw the cross-stack edge. In-stack, the parameter’s own consumers are ordinary $ref edges.
Composition across projects belongs to the viewer, not to chant: chant graphs one project and emits the handles, and a tool pointed at several joins them.
Stack grouping in the IR
Section titled “Stack grouping in the IR”groups.byStack maps stack name to node ids — what a renderer reads to draw boundary boxes.
For a project that declares stacks in chant.config.ts, chant graph builds each stack’s source in isolation, qualifies node ids <stack>::<id> to match how observation keys live nodes, and groups by the declared names.
For a project built from one source tree — including every side-by-side project — groups.byStack reports the lexicon partition, which is the unit that tree deploys as.
Child projects (lexicon-specific nested stacks)
Section titled “Child projects (lexicon-specific nested stacks)”Some lexicons support child projects — subdirectories that build to separate output files, referenced from a parent that deploys them as a unit. Unlike side-by-side projects, these are declared in code.
chant’s core provides two primitives:
stackOutput(ref)— marks a value for cross-stack export. When the serializer encounters aStackOutputentity, it emits it into the template’sOutputssection.ChildProjectInstance— aDeclarablerepresenting a reference to a child project directory. The build pipeline detects these, recursively builds the child, and attaches the child’sBuildResultfor the serializer.
Discovery stops at child project boundaries — findInfraFiles() does not recurse into child project subdirectories. Each child project is a separate project scope, only built when referenced by a ChildProjectInstance.
When a serializer produces multiple files, it returns a SerializerResult with a primary template plus additional files:
interface SerializerResult { /** Primary template content */ primary: string; /** Additional files keyed by filename */ files?: Record<string, string>;}The build pipeline writes additional files alongside the primary output. For example, the AWS lexicon’s nestedStack() produces child CloudFormation templates with explicit cross-stack reference wiring via stackOutput(). See the AWS Nested Stacks guide.
For lexicon authors implementing child project support, see Child Projects.