Context
A product repository has noapps/ folder (0441 decision 2). Its root runway names shell: os/console@3,
shell: os/landing@1 and template: os/mobile@3, and the OS repository owns those shells and the mobile template
(0436 decision 1). Runway’s mobile job wrote the Expo config into an already checked-out templateDir, and nothing
fetched a line. Two release mechanisms were weighed:
- A git tag per line (
mobile@3.2.0in figentra/os). Product CI would need read access to the private OS repository, and a member there names its versions ascatalog:andworkspace:references that resolve only inside that workspace, so a tagged tree is not buildable on its own. “The highest 3.x” would be a tag search. - A package per line on the Stackra registry (
npm.stackra.figentra.com, already used for every@stackra/*package and published to over OIDC).pnpm publishreplacescatalog:andworkspace:with exact versions, so the tarball is a standalone app; the registry answers “the highest 3.x” as a semver range; each tarball has an integrity hash; and access is the same read token product CI already has for@stackra/*.
Decision
- A line is a package; its major is the line. The line
<owner>/<name>@<major>is the package<scope>/<owner>-<name>at versions<major>.x.yon the Stackra registry:os/mobile@3is@figentra/os-mobile@3.x.y,os/console@3is@figentra/os-console@3.x.y,os/landing@1is@figentra/os-landing@1.x.y, and a Commerce template such ascommerce/till@1is@figentra/commerce-till@1.x.y. A breaking change to a shell’s import map or a new native line is a new major, and so a new line. Pre-releases are never a line’s release. - What a release holds. A mobile template release is the Expo project:
app.json(a baseexpoblock without product identity),capabilities.json(the line’s flags, in Runway’sMobileCapabilityshape),package.json,eas.json, the sources and the template’s default assets. A web shell release holds its builtdist/withdist/shell.json: the surface, the major, the version, the entry, the stylesheets and the import map of the shared dependencies, each relative todist/. - Runway checks a line out.
checkoutLinereads the highest released version of the major (npm view), packs it (npm pack) and unpacks it into the target folder, optionally as a git checkout with one commit, because EAS builds from a commit. The mobile job checks the app’s line out into itstemplateDirand commits the config it writes there before it builds; the OS deploy and product CI read a shell’s release withshellReleaseOf, which answers theshell:<surface>:<major>row’s{ version, entry, importMap }with URLs under<origin>/shells/<surface>/<major>/<version>/. - Runway names no owner’s scope. The scope is an input of the step (
LineSource.scope), passed by the deploy workflow; the registry is the product checkout’s.npmrcmapping for it. Runway depends on no Figentra code (0441 decision 5). - A local checkout for development and tests.
LineSource.localpoints at a folder that is the line’s own package at the line’s major (a local OS checkout, as 0436 decision 6 allowsstackra dev --surface); it is copied without installs, caches and generated native projects. - Dry runs.
dryRunCommandrecords every command and answers stand-ins, andDryRunEdgeKvDriverkeeps the KV rows in memory. Passed to the line checkout, the mobile job, the compatibility gate and the publish step, they leave every local output written (the Expo config, the autolinking excludes) and every external effect (npm, tar, git, EAS, wrangler, KV) recorded, which is how the OS line test set runs in CI. A dry run of a registry checkout still reads the version, and plans the fetch.
Consequences
- The OS repository publishes
apps/mobile,apps/consoleandapps/landingto the registry from CI on release, with the registry’s@figentrascope rows; the console and landing tarballs carry their builtdist/. - Products pin nothing: a new 3.x release reaches every product on its next build, and a product that needs time stays on its build of the old release only until its next merge. The contract test of 0436 decision 5 stays the guard.
- A product’s CI needs the registry read token it already has; it never needs access to figentra/os.
- The mobile job’s
shais optional when it checks the line out itself: the build commit is the template checkout’s, and the result reports the line, package and version built.
Enforcement
- active —
checkoutLinerefuses a malformed line or scope, a non-empty target, a local checkout of another package or major, and a line whose major has no release. - active —
shellReleaseOfrefuses ashell.jsonwhose surface, major or version is not the checked-out line’s, or whose paths climb out ofdist/. - planned — the OS release workflow publishes each line and fails when a package’s major differs from the line its README names.
- planned — the validator refuses a
@figentra/*dependency in@stackra/runway(0441).
Supersedes and sources
Supersedes: none. Sources: the owner decision of 2026-10-29 (no apps/ folder; lines from core’s apps), STA-1601
to STA-1604, and packages/developer/runway (runMobileJob, publishShell, checkShellCompatibility).