Skip to main content
Relates to 0090 (the discovery that feeds these files), 0158 (the committed artefacts, which this ADR does not change), 0233 (the # alias mechanism), 0234 (the assets output), 0249 and 0250 (the capabilities output) and 0263 (the wiring output).
Amended by 0265: Gen also writes .stackra/gen/hooks.ts, which lists every hook point with its implementers in order and their file paths (decision 1 lists the outputs, and hooks.ts is one of them).

Context

stackra gen builds one discovery IR of every module, route, setting, permission and client in the graph. The IR must reach the code that uses it. Virtual modules (files that exist only inside a bundler) work in Vite, but Metro, Node, the test runner and agents cannot read them, so each reader would need its own shim. Real files work in every reader. The owner decided on 2026-10-21 that gen writes real files and the bundler only serves them.

Decision

  1. One IR, real files. stackra gen writes typed TypeScript files to .stackra/gen/: routes, navigation, i18n, resources, assets, clients, services, permissions, settings, capabilities and wiring.
  2. One alias. Code imports them only as #stackra/<name>. The member declares the alias in the imports map of its package.json (0233), for example "#stackra/*": "./.stackra/gen/*.ts".
  3. Every reader uses the real files. Metro, Node, tests and agents read .stackra/gen/ directly.
  4. Vite serves the same modules. In a Vite app, @stackra/vite-plugin (developer category, 0127) serves the same modules as virtual modules with hot module replacement. The plugin adds no export of its own.
  5. Fresh files. pnpm gen --watch regenerates the files as sources change. The files are gitignored under .stackra/*, as .stackra/encore is (0091). The committed artefacts of 0158 are unchanged.
  6. Generated, never edited. Each file opens with a contract block (0007) saying it is generated. A hand edit is overwritten on the next run.

Consequences

  • A developer and an agent read the same file that the code runs against, so a finding about a route or a permission can be checked by reading one file.
  • The Vite plugin is a thin view. A divergence between the plugin and the file is a bug in the plugin.
  • Each consumer must run stackra gen before it builds or tests. The Turborepo task graph carries that dependency.

Enforcement

planned — stackra gen --check fails when .stackra/gen/ is stale. Planned: pnpm validate fails a #stackra/* import that names a file gen does not write. Planned: the conformance test of 0144 checks that the Vite virtual module exports the same names as the real file.

Supersedes and sources

Supersedes: none. Relates to the ADRs listed above. Sources: owner decision in chat, 2026-10-21 (generated modules), and DX10 of the 2026-10-20 DX round (@stackra/vite-plugin).