> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackra.figentra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 0091 — The Encore app is generated into .stackra/encore; services hold no Encore files

> Accepted, amended by 0158 · 2026-10-19

> [!NOTE]
>
> Amended by [0158](/adr/0158-gen-artefacts-openapi-manifest-and-client): openapi.json and
> app-manifest.json are committed; the client is published in CI.

## Context

[0040](/adr/0040-ddd-feature-modules-in-services) put Encore primitives at each service's root, and
[0041](/adr/0041-decorated-controllers-and-build-time-generator) had `stackra 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

1. **Services are plain TypeScript with no `encore.*` files** and no `__generated__/` folder.
2. **`stackra gen` emits a complete Encore application into a gitignored `.stackra/encore/`**: `encore.app`, one
   `encore.service.ts` per service, and generated `api()`, `Topic`, `Subscription` and `CronJob` adapters. The adapters call the
   built service packages through the container.
3. **Only `app-manifest.json` is committed.** It is the reviewable record of what gen produced: endpoints, topics,
   subscriptions and crons.
4. **`stackra gen --check` runs in CI** and fails when `app-manifest.json` is stale.
5. **`stackra dev` is `gen --watch` plus `encore run`. `stackra build` is `gen` plus `encore build docker`.** CI deploys from
   the generated application.
6. **Encore is a swappable target.** Nothing outside `.stackra/` and the generator's Encore emitter knows about Encore.
7. **Authoring is actions** ([0098](/adr/0098-actions-are-the-unit)): `@Action` and `@Query` exposed with `@AsEndpoint` and
   the other adapters, plus `@OnEvent` and `@Cron`, discovered per [0090](/adr/0090-codegen-discovery-by-root-module).
   `@Controller` with `@Get`, `@Post` and the rest remains only for raw endpoints. HTTP conventions are unchanged, generated
   files are never edited by hand, and `@Crud` is [0105](/adr/0105-crud-generates-service-methods).
8. **The dev loop imports service source (mode B).** `stackra gen --watch --src` makes `.stackra/encore/` import each
   service's source through TypeScript `paths`, and the generated `tsconfig.json` sets `experimentalDecorators` and
   `useDefineForClassFields: false`. Production and CI keep importing built `dist`. 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.ts` hash 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.
9. **The spike passed** all six cases: `encore run` with 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 docker` on arm64 and amd64; `encore gen client`. The constraints it found are listed under Consequences.

## Consequences

* Reviewers see `app-manifest.json` changes 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](/adr/0042-di-first-with-one-container-per-service) decision 5.
* Constraints the spike found, which the generator and authors must respect:
  * Topics are referenced directly: the generator emits one `switch` branch 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 `APIError` is required; a plain `Error` surfaces as a 500.
  * The pipeline order is `pnpm build`, then `stackra gen`, then `pnpm install`, then `encore`.
  * Each environment needs an Encore infra config once Pub/Sub exists.
  * Importing built `dist`, the generated app does not need `experimentalDecorators`; importing source (decision 8) it does.
  * `encore build docker` hangs inside git worktrees (the CLI loops on the `.git` file), so Docker builds run in CI or in a normal clone.
  * `encore run` does not run crons locally.
  * Encore watches only its app root, so source edits reach it only through the stamp file of decision 8.

## 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](/adr/0041-decorated-controllers-and-build-time-generator) (decisions 1, 3 to 7 restated; output moves from
`services/<svc>/__generated__/` to `.stackra/encore/`). Amends [0040](/adr/0040-ddd-feature-modules-in-services) decisions 3
and 4, and [0042](/adr/0042-di-first-with-one-container-per-service) decision 5. Sources: owner decision in chat, 2026-10-19
(Round 9 G3). Amended by [0098](/adr/0098-actions-are-the-unit) (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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.