Context
The owner’s final structure review of 2026-10-24 produced a long list of decisions. Most of them are recorded in the ADRs of that wave, 0314 to 0316 and 0321 to 0333. A sweep of every decided item against the ADR index then found the rules below, each decided and each recorded nowhere. They are small and they span many pages, so one round-up records them, and a note on each touched page points here. Two items are process decisions rather than code rules; they are kept here so that the review has one complete record. The review also asked whether the framework covers the enterprise capabilities of Magento 2. The sweep confirmed an anchor for each framework-side capability, and this page records the map so that the next reader need not repeat it:Decision
Data layer
-
Every column carries a comment and an explicit nullability. Gen writes a Postgres
COMMENTon every column from the docblock of its model field, and every column says whether it is nullable. Gen fails on a field with no docblock and on a column whose nullability is not stated. Column groups are the existing traits and macros; there is no separate “column set” kind. -
Migration order is checked against foreign keys. Order is the number within a module and
after: [...]across members, as 0159 says. Gen fails when a migration adds a foreign key to a table that no earlier migration, in order, has created. -
An extension is a migration step. A migration enables a Postgres extension through the built-in
schema.createExtension(name)step, and only for the approved extensions of 0160 as amended by 0314. Gen refuses any other name. A rawschema.sqlis not the way to enable one. - There are no patch kinds. A schema change is a migration. A data change is a seeder or a backfill job. DML in a migration stays banned (0159).
-
Validation messages come from lang keys; computed values are getters or generated columns. Field validation
reuses the field types and the constraint decorators of 0095. A message is
the lang key
<module>::validation.<field>.<rule>, never a string in the decorator. A value computed and not stored is a getter on the model. A value computed and stored is a Postgres generated column, declared in the migration (0014 decision 6). There is no@Computed(0133). -
The module is the finest database split unit. Each module’s tables live in the module’s own Postgres schema
(0294), and no query joins across modules
(relations between modules are
Link<T>). Runway topology may therefore move one module to its own logical database with no code change, and MikroORM then holds one connection per module. No single table is ever split out on its own. One logical database per service (0238) stays the default.
Runtime and gateway
-
The API host answers JSON only. The root of the API host is a small index document. An unknown route returns the
error envelope of 0140 with
not_found, never HTML. A web host renders a React 404 route, and the static host keeps its branded 404 page (0284). -
Local Encore tools reach agents through
stackra mcp.stackra mcpproxies the local Encore MCP tools, with the app id taken from runway, so a project’s.mcp.yamlhas no separateencoreentry. Theencore-cloudserver stays a separate entry, because it has its own OAuth flow.
Panel
-
Every resource View page has “View as JSON”. The action shows the record as the wire shape of its
getquery’s output, with classified fields redacted by the caller’s permission, a copy button and the record’s public id. It is free, and no resource writes code for it. - Every resource has a clone action and an activity tab. The clone (duplicate) action is built in. The activity tab puts the record’s audit entries, comments and domain events on one timeline. Both belong to the resource surface of 0307, and 0321 noted them without scheduling them.
-
Import maps columns through a port. The import pipeline has a mapping step behind an
ImportMapperport. The default driver matches headers to fields. An AI mapper driver is deferred; its revival trigger is the first customer onboarding with spreadsheets or documents that header matching cannot map. The port exists so that the driver plugs in without a change to any import resource.
Web apps
-
Security headers and analytics defaults are generated. Every web-served app runs behind Cloudflare
(0297). Gen writes a CSP with
nonces (
@stackra/csp), HSTS,frame-ancestors,Referrer-Policy,Permissions-Policy, and COOP and COEP where they are safe, fromstackra.config. WAF, bot management and Turnstile on public forms sit at the edge (0237 layer 1). Cloudflare Web Analytics, which sets no cookie, is on by default. Product analytics runs through the consent-gated driver (0190). No Google Analytics is added by default. Google Search Console verification is a setting of 0301. -
One web manifest per locale. Gen writes one manifest for each configured locale, taking
name,short_name,description,dir,langandshortcutsfrom the lang keys of the identity section of 0326. Each locale’s HTML links its own manifest. -
A declared mobile member adds the web-to-app links. When
stackra.configdeclares a mobile member, gen addsrelated_applicationsfor the stores, the Apple smart app banner meta, the association files and an install or download banner component. Nothing is hand-written. -
The capability list is fixed and every capability degrades. The ports of
0250 cover haptics, the install prompt, share,
clipboard, camera, notifications, badging and file handling. Each is feature-detected and degrades: haptics run on
expo-hapticsnatively, onnavigator.vibrateon Android web and do nothing on iOS web; the install prompt usesbeforeinstallprompton Chromium and an instructions sheet on iOS Safari.
Packages and source
-
A package’s contributions are discovered and listed. Settings, config, widgets, routes, panels, menus,
shortcuts, resources, documents, lang files and assets are discovered by their group folders and listed under
contributionsin the package’sstackra.manifest.json. An app that imports the package module gets all of them. Nothing is registered by hand (0111). -
A docblock has one form per size. A docblock with one sentence and no tags is one line,
/** … */. A docblock with a tag or more than one sentence is multi-line. The header and TSDoc rules of 0007 are unchanged. -
No
ai/folder for machine readability. A package is read by agents through itsAGENTS.md, its docs pages and the skills gen writes. Theai/group stays the home of the AI kinds only (0245, 0294). -
TanStack AI is a spike, not a switch. 0155 stands:
@stackra/aibuilds on the AI SDK core and the MCP SDK. A half-day spike compares TanStack AI as a model adapter driver behind the port of@stackra/ai, and its typeddecide()may be copied as a helper either way. A switch needs an ADR that amends 0155.
Process
-
Docs follow the framework build. One docs workflow runs after the framework packages land: the root README is
rewritten to the final structure; each package gets a docs section (overview, API, examples, do and don’t,
configuration); guides cover an app, a service, a package, a Worker and a CLI command; images are optimised SVG or
WebP in
apps/docs/src/assets; videos are hosted outside and embedded; gen keeps the API reference current. - The owner reviews the CLI inventory before the CLI is built. The full command list (create and scaffold, gen, db, cache, tenant, queue and jobs, auth, settings, flags, i18n, deploy and runway, doctor) is written first, with each command’s arguments, its interactive prompts (search, select, confirm) and its non-interactive flags for CI, all on the CLI package’s one prompt and component API.
Scope
- The commerce features belong to Figentra Commerce, a product above the OS. Customer segments as a storefront feature, related-product rules, reward points, gift cards, store credit, returns and fulfilment, the B2B set (company accounts, shared catalogues, quotes, requisition lists, purchase approvals), visual merchandising, private sales, the order, inventory and point-of-sale engines and generic manufacturing are features of Figentra Commerce (working name), a first-party product that sits between Figentra OS and its editions, of which Muslin is the fashion edition. The placement rule is: a capability that only sellers need is Commerce; one that another product or Figentra’s own billing needs too is OS. So pricing and Markets, the payments ledger (with gift-card and store-credit balances), invoicing and the workforce app stay in the OS. None of these is a Stackra package, and this repository writes no ADR for them; 0201 carries the matching note on its layer-3 list, and the line that 0227 draws between the framework and its products stands. The edition concept itself is a framework matter and gets its own ADR.
Consequences
- A migration is self-describing: every column says what it holds and whether it may be null, and the order of migrations can be trusted across members.
- A tenant never sees HTML from the API host, and a client can parse every answer it gets.
- Every resource gets “View as JSON”, clone and an activity tab without code, so every product page has the same floor.
- Every web app ships the same security headers and the same analytics defaults, and a product that wants Google Analytics turns it on in code, where the choice is reviewed.
- Two process rules bind the framework build: the docs workflow and the CLI review happen, in that order, before the CLI and the docs are called done.
- The commerce backlog has one home, and a reader who looks for it in the ADR index finds this page and the pointer.
Enforcement
- planned — gen checks (STA-340): a field without a docblock, a column without nullability, a foreign key out of
order, an extension outside the approved set, a
.mcp.yamlwith anencoreentry, a web member without its per-locale manifests or its security headers, and a package whose group folders do not match its manifestcontributions. - planned — an oxlint house rule, under 0129: the docblock form of rule 17, and a validation message given as a string literal (rule 5).
- planned — a gateway test: an unknown route on the API host answers the JSON envelope, and no API route answers HTML.
- planned — a panel test: every resource View page renders “View as JSON” and the activity tab, and every resource has the clone action.
- Rules 20 to 22 are process and scope decisions. Review checks them; nothing mechanical does.