Skip to main content
Amends 0098 (decision 2: @AsListener is removed), 0144 (decision 5: @Setting stays a field decorator), 0126 (decision 3: new kind folders), 0130 (decision 1: new kind folders), 0131 (decision 1: the key is one segment; decision 3: name is gone), 0133 (decision 3: model keys are snake_case), 0145 (decision 2: the define family), 0231 (module keys and model keys in the naming table) and 0234 (decision 2: the namespace is the module key) and 0247 (decision 2: the family).
Forward note (2026-10-21, STA-219): decision 7’s kind folders are part of the one kind-folder list in 0130 (amendment of 2026-10-21, decisions 1 and 2). That list is the only list; this decision’s folders are in it, and the banned folders are listed there too.

Context

Keys appear in permissions, events, action keys, settings and i18n namespaces. Two key forms were in use: a module key and a separate name, and dotted module keys that make <module>.<resource>.<action> and <module>.<aggregate>.<verb>.v<n> ambiguous. The define family was described in three places and had no closed list. Kind folders were listed without hooks, widgets, pages, links, drivers and ai. The owner settled these on 2026-10-21.

Decision

  1. One key grammar. A module key is one segment, snake_case for a multi-word key (orders, giftcards, loyalty_kit), and globally unique. The key is also the namespace for permissions, events, action keys, settings, i18n, assets and templates. The @Module({ name }) field is removed, so name and key are never both used.
  2. Model keys follow it. A model key is snake_case under its module, inventory.stock_level, not inventory.stockLevel.
  3. Dotted module keys are refused. The grammars of 0132 and 0141 parse only when the module segment has no dot.
  4. The define family is closed. The family has eight entries: defineToken, defineConfig, definePermissions, defineEvents, defineHooks, defineComponents, defineMeters with defineEntitlements, and defineSkill with definePrompts. The last two pairs count as one entry each. defineHooks and defineComponents are new. A new member of the family takes an ADR.
  5. Settings stay a decorated class. A @Settings class holds @Setting fields (0144). Code reads them with SettingsReader.get(OrderSettings), which returns the typed object for the current scope.
  6. The authoring rule. Decorators annotate what the framework calls. define* creates values that other code references (0247). A string literal where a reference exists is refused, in a permission, an event, refreshOn, invalidateOn, an @OnEvent argument or a hook point. The exceptions are a remote component key and the wire names in generated files.
  7. Kind folders. hooks/, widgets/, pages/, links/, drivers/ and ai/ join the kind folders of 0130. Each appears with its first file.
  8. One listener form. @AsListener is removed. A reaction is @OnEvent(Events.x, options) on a method of a class in listeners/. A listener that must run a whole action calls it through the action bus.
  9. Jobs are actions. “Job” is not a class kind. It is the @AsJob adapter on an @Action, and @Cron only dispatches it.
  10. Two senses of “service” stay. A deployable is services/<name>/. A module service is an @Injectable in services/ inside a module. Agents tell them apart by context.

Consequences

  • A permission, an event, an action key and an i18n key share one prefix, so a reviewer reads the module from any of them.
  • A typo in a key is a type error at the use site, because the key is a value from a define* call.
  • Renaming a module key is a breaking change under 0131. A key is chosen once.
  • The family is closed, so a new kind of value needs an ADR. That is intended.

Enforcement

planned — gen fails on a module key collision, on a key with uppercase letters or dots, on a define* outside the family, on a name field in a @Module, and on a string where a reference is required (the lint of 0247). Nothing checks these yet.

Supersedes and sources

Supersedes: none. Amends: 0098, 0144, 0126, 0130, 0131, 0133, 0145, 0231, 0234, 0247. Sources: owner settlement in chat, 2026-10-21 (programming model questions Q1, Q2 and Q7).