Amended by 0158: openapi.json and
app-manifest.json are committed; the client is published in CI.
Context
0040 put Encore primitives at each service’s root, and 0041 hadstackra gen write static Encore declarations into
services/<svc>/__generated__/. That ties every service’s source tree to Encore and puts generated code in review diffs. The
owner proposed the reverse: services are plain TypeScript, and the whole Encore application is a build output. This makes
Encore one target among possible others.
Decision
- Services are plain TypeScript with no
encore.*files and no__generated__/folder. stackra genemits a complete Encore application into a gitignored.stackra/encore/:encore.app, oneencore.service.tsper service, and generatedapi(),Topic,SubscriptionandCronJobadapters. The adapters call the built service packages through the container.- Only
app-manifest.jsonis committed. It is the reviewable record of what gen produced: endpoints, topics, subscriptions and crons. stackra gen --checkruns in CI and fails whenapp-manifest.jsonis stale.stackra devisgen --watchplusencore run.stackra buildisgenplusencore build docker. CI deploys from the generated application.- Encore is a swappable target. Nothing outside
.stackra/and the generator’s Encore emitter knows about Encore. - Authoring is actions (0098):
@Actionand@Queryexposed with@AsEndpointand the other adapters, plus@OnEventand@Cron, discovered per 0090.@Controllerwith@Get,@Postand the rest remains only for raw endpoints. HTTP conventions are unchanged, generated files are never edited by hand, and@Crudis 0105. - The dev loop imports service source (mode B).
stackra gen --watch --srcmakes.stackra/encore/import each service’s source through TypeScriptpaths, and the generatedtsconfig.jsonsetsexperimentalDecoratorsanduseDefineForClassFields: false. Production and CI keep importing builtdist. gen bundles service source with esbuild into a cache folder to read metadata, writes a file only when its content changes, and writes a<svc>/dist-stamp.tshash file inside the app root on every rebuild, because Encore ignores changes outside its app root. An edit reaches a response in about 0.6 s. - The spike passed all six cases:
encore runwith typed, raw, cookie and bearer endpoints; Pub/Sub and cron;encore test; static analysis of types in.stackra/with handlers in external packages;encore build dockeron arm64 and amd64;encore gen client. The constraints it found are listed under Consequences.
Consequences
- Reviewers see
app-manifest.jsonchanges instead of generated code. - Encore errors point into generated code under
.stackra/, so the generator must emit source comments that point back to the authored class. - The DDD module layout of 0040 stands, minus its decisions 3 and 4, which this ADR replaces. Encore primitives reach modules as container tokens provided by the generated app, which amends 0042 decision 5.
- Constraints the spike found, which the generator and authors must respect:
- Topics are referenced directly: the generator emits one
switchbranch per topic. - Classes are matched by export identity, not
class.name, so every provider with handlers is exported from its package index. - Handlers carry explicit parameter and return type annotations.
- A typed mapping from domain errors to
APIErroris required; a plainErrorsurfaces as a 500. - The pipeline order is
pnpm build, thenstackra gen, thenpnpm install, thenencore. - Each environment needs an Encore infra config once Pub/Sub exists.
- Importing built
dist, the generated app does not needexperimentalDecorators; importing source (decision 8) it does. encore build dockerhangs inside git worktrees (the CLI loops on the.gitfile), so Docker builds run in CI or in a normal clone.encore rundoes not run crons locally.- Encore watches only its app root, so source edits reach it only through the stamp file of decision 8.
- Topics are referenced directly: the generator emits one
Enforcement
Planned:stackra gen --check in CI and in pnpm verify. A validator check that no encore.* file or __generated__/
folder exists under services/, and that .stackra/ is gitignored. Nothing checks this today.
Supersedes and sources
Supersedes: 0041 (decisions 1, 3 to 7 restated; output moves fromservices/<svc>/__generated__/ to .stackra/encore/). Amends 0040 decisions 3
and 4, and 0042 decision 5. Sources: owner decision in chat, 2026-10-19
(Round 9 G3). Amended by 0098 (decision 7). Evidence: spike stackra-spike-gen/FINDINGS.md (local
throwaway repository, Encore CLI 1.58.6), all six cases passed; hot-reload spike in the same repository (tools/dev.mjs), about
0.6 s from edit to response.