> ## 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.

# 0090 — Codegen discovers decorators through each member's root module

> Accepted, amended by 0144, 0268, 0294 · 2026-10-19

> [!NOTE]
>
> Amended by [0294](/adr/0294-programming-model-v3-grammar-folders-identities-and-naming.md): gen finds kinds by reading their base classes (for example `extends Action({ key })`), so it no longer imports the root module to discover them, and decision 2 is replaced. A root module is found at its path, `src/<key>.module.ts` (or `src/node/<key>.module.ts` in a package), so the `stackra.module` field of decision 1 is not needed to find it. A handler's input and output are the types of its `handle` signature, so decision 3 reads those signatures. Decision 4 stands.

> [!NOTE]
>
> Amended by [0144](/adr/0144-metadata-registry-and-explorers.md): every decorator package ships an
> Explorer, and the metadata registry lives in @stackra/container.

> [!NOTE]
>
> Amended by [0268](/adr/0268-register-answers-thin-items-r1-to-r18.md): R8: a documentation app
> imports only the `./contracts` entries and the generated clients of the members it documents. It
> never imports module source.

## Context

`stackra gen` ([0041](/adr/0041-decorated-controllers-and-build-time-generator.md)) must find every decorated class (controllers,
handlers, crons, models, resources), including those that come from packages. Scanning every TypeScript file with a parser is
slow and misses runtime registration. Running the app to find them would start databases and side effects. The container
already knows every provider through its modules ([0042](/adr/0042-di-first-with-one-container-per-service.md)), and its decorator
metadata lives in its own `WeakMap` store ([0005](/adr/0005-di-container-and-legacy-decorators.md)).

## Decision

1. **Each member declares its root module in `package.json`**: `"stackra": { "module": "<path>" }`.
2. **`stackra gen` imports that module in Node and runs the container's scan and plan steps only.** It never instantiates a
   provider. Decorator metadata comes from the container's store.
3. **ts-morph reads only the request and response types** of the handlers that discovery found, and emits wire interfaces
   from them. No other file is parsed.
4. **A validation pass fails the build** on duplicate paths, a destructive action without a permission, an unversioned
   integration event ([0078](/adr/0078-messaging-layer-eight-packages.md)), or exposed PII.

## Consequences

* A package's module is discovered the same way as a service's. No file globbing and no naming convention is needed.
* Module code must be safe to import without starting anything. Side effects belong in providers, which are never instantiated during gen.
* Discovery is only as complete as the module graph: a class not reachable from the root module does not exist for gen.

## Enforcement

The validation pass of decision 4 is itself the check, once built. Planned: `pnpm validate` fails a `tier:service` or
`tier:app` member without a `stackra.module` field. Nothing checks this today.

## Supersedes and sources

Supersedes: none. Specifies the discovery mechanism of [0041](/adr/0041-decorated-controllers-and-build-time-generator.md)
decision 2. Sources: owner decision in chat, 2026-10-19 (Round 9 G2).


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