Workflows
Tier-Gated Implementation Selection
How .env.Land propagates through build tools into Mountain, Cocoon, Wind, and Sky, selecting which implementation tier each capability runs on.
Tier-Gated Implementation Selection 🎚️
Goal: Every capability that has more than one viable implementation (“file-system access via gRPC vs native tokio”, “glob compilation in JavaScript vs globset in Rust”, “extension-host IPC routed to Mountain Rust vs Cocoon Node.js”) lives in the codebase simultaneously. The active path is selected through a single configuration file - .env.Land - so we never duplicate call sites and never regress the default tier. This workflow documents how that file propagates through the build tools into Mountain, Cocoon, Wind, and Sky, and how each Element reads the resolved tier at run time.
The system is the foundation we use to test upgrades, keep fallback paths warm, and bisect regressions to a specific implementation tier.
sequenceDiagram
participant DevFile as .env.Land<br/>(or .env.Land.Sample)
participant Flavor as .env.Land.<Flavor><br/>(RustOnly / NodeFirst / Full)
participant BuildSh as Maintain/Debug/Build.sh
participant TierSh as Maintain/Script/TierEnvironment.sh
participant Cargo as Cargo / Mountain/build.rs
participant ESBuild as Cocoon ESBuild<br/>(CocoonEsbuildDefine)
participant Vite as Sky Vite<br/>(astro.config.ts)
participant Mountain as Mountain<br/>(Rust binary)
participant Cocoon as Cocoon<br/>(Node sidecar)
participant Wind as Wind / Sky<br/>(webview)
DevFile->>BuildSh: Base tier set
Flavor->>BuildSh: Optional overlay via --flavor <name>
BuildSh->>TierSh: Source helpers
TierSh->>TierSh: set -a; . .env.Land; set +a<br/>(sweeps every Tier* / Product* / Network*)
TierSh->>Cargo: Tier* env exported
TierSh->>ESBuild: CocoonEsbuildDefine = JSON of every Tier* in env
TierSh->>Vite: Tier* env exported
TierSh->>BuildSh: CargoFeatures = comma-joined feature list
Cargo->>Cargo: build.rs::PropagateTierGating()<br/>emit cargo:rustc-env + cargo:rustc-cfg
ESBuild->>ESBuild: Inject __LandTier_<Name>__ defines
Vite->>Vite: Substitute import.meta.env.Tier*
Note over Mountain,Wind: Build complete - every Element<br/>has the resolved tier burned in.
Mountain->>Mountain: LandFixTier::LogResolvedTiers()<br/>prints two-line runtime banner
Cocoon->>Cocoon: Utility/Tier.ts default export<br/>prints runtime banner
Wind->>Wind: Utility/Tier.ts default export<br/>prints runtime banner
Note over Mountain,Wind: All three banners must agree -<br/>mismatch indicates config drift.Phase 1: Authoring the Tier Set (.env.Land)
Canonical source of truth (
Land/.env.Land.Sample)Every capability participating in tier gating has exactly one row in
.env.Land.Samplein the formTier<Capability>=<Value>.The sample file is committed and represents the compiled-in defaults. A per-machine
.env.Land(gitignored on shipping builds, committed in this repo for the team baseline) may override any line.The current capability set spans two layers:
Infrastructure tiers (Cargo-feature-gated, compile-time selection):
Domain Variable Default Other values Transport TierRemoteProcedureCallgRPCSharedMemoryTransport TierHTTPProxyHandRolledHyperTransport TierLoggerStandardRingFile system TierFileSystemLayer2Layer3,Layer4File system TierFindFilesLayer3Layer4File system TierGlobJavaScriptNativeFile system TierFileWatcherLayer4StubAssets TierSchemeAssetsFileSystemEmbedded,HybridVS Code API TierConfigurationCacheEagerVS Code API TierDiagnosticsFullDeltaVS Code API TierClipboardLayer3Layer4,Layer5VS Code API TierOpenExternalLayer3Layer4VS Code API TierDocumentMirrorFullLazyLifecycle TierExtensionActivationParallel8Sequential,Parallel4,Parallel16Lifecycle TierExtensionScanSequentialParallelLifecycle TierModuleCacheSimpleOff,SharedTelemetry TierTelemetrySynchronousBatched,OffPer-subsystem routing tiers (runtime-readable, baked at compile time via
cargo:rustc-envfor every binary so dispatch arms canenv!()them without astd::env::varcall hot-path):Variable Default Routes TierIPCMountainMountain/NodeDeferred/NodeTierTerminalMountainMountain/NodeTierSCMMountainMountain/NodeTierDebugMountainMountain/NodeTierLanguageFeaturesMountainMountain/NodeTierSearchMountainMountain/NodeTierOutputChannelMountainMountain/NodeTierNativeHostMountainMountain/NodeTierTreeViewMountainMountain/NodeTierStorageMountainMountain/NodeTierModelMountainMountain/NodeTierTasksNodeMountain/NodeTierAuthNodeMountain/NodeTierEncryptionMountainMountain/NodeTierExtensionHostProcessProcess/WebWorker/DisabledTierWebSocketDisabledDisabled/Mountain/MistNotable entries:
TierIPCis the global IPC routing switch.Wind’sTauriMainProcessService.tsreads this variable at module initialisation time (before any IPC call is made) and selects one of three dispatch paths:Mountain(default) - all calls go directly to Mountain via@tauri-apps/apiinvoke.NodeDeferred- Mountain is tried first; on a miss orundefinedresult the call falls through to Cocoon via thecocoon:requestbridge.Node- all calls go directly to Cocoon; Mountain is used only for window management and PTY.
TierTasksdefaults toNodebecause task execution relies on Cocoon’sExtHostTaskServicefor provider enumeration and execution.TierAuthdefaults toNodebecause authentication flows go through Cocoon’sExtHostAuthenticationshim.
Naming convention
- Variables use
PascalCasewith theTierprefix. - Infrastructure values use
Layer2..Layer5where L2 is a round-trip RPC, L3 a native in-language fallback, L4 a pure-Rust implementation, and L5 an OS-level integration. For non-layered infrastructure they use descriptive labels (gRPC/SharedMemory/HandRolled/Hyper). - Per-subsystem routing values are always one of
Mountain,Node,Process,WebWorker,Disabled, orMist. See the table above for which set applies to each variable.
- Variables use
Flavor overlays
Three pre-built tier sets ship at the repo root so you can flip every subsystem at once:
.env.Land.RustOnly- every subsystem routes to Mountain Rust where a native implementation exists. Cocoon is still spawned to run extension code that needs Node.js APIs..env.Land.NodeFirst- every API surface routes to Cocoon Node.js. Mountain becomes a thin IPC pass-through. Useful for upstream-VS-Code behaviour validation..env.Land.Full- native Rust where it exists, Mist WebSocket on for high-frequency calls. The “ship it” flavor.
Source one with
sh Maintain/Debug/Build.sh --flavor RustOnly(or--flavor NodeFirst/Full). The flag sources the overlay BEFORETierEnvironment.shruns, so every downstream tool sees the overlay values as if they came from.env.Landdirectly.ProductFlavor encoding: Each flavor overlay sets
ProductFlavorto a 10-character code that encodes routing decisions in 5 two-char groups:Group Encodes Values G1 IPC mode + WebSocket IW=IPC Mtn+WS Dis,IF=IPC NodeDef+WS Mist,IN=IPC Node+WS DisG2 Tasks + Auth + Encryption TN=Tasks Node+Auth Node+Enc Mtn,TA=all MountainG3 Terminal + SCM + Debug MM=all Mountain,NN=all NodeG4 Search + Language + Output + TreeView + NativeHost MM=all MountainG5 Storage + Model + FileSystem MM=all MountainFlavor reference:
Flavor Code Identity Default (full) IWTNMMMMMMfiddee-IWTNMMMMMMRustOnly IWTAMMMMMMfiddee-IWTAMMMMMMNode INTNNNNNNNfiddee-INTNNNNNNNFull + Mist WS IFTNMMMMMMfiddee-IFTNMMMMMMSet
ProductFlavorLong=trueto use short human-readable names (fiddee-F8,fiddee-FR,fiddee-FN,fiddee-FC) instead of the 10-char encoding. The flavor code is appended to every filesystem identity (data folder, URL protocol, binary identifier) so each flavor runs in complete isolation - no shared state, no collisions.
Phase 2: Build-Time Propagation (Maintain/Debug/Build.sh → TierEnvironment.sh)
Env file discovery and overlay (
Land/Maintain/Debug/Build.sh,Land/Maintain/Script/TierEnvironment.sh)- The build script handles
--flavor <name>first, sourcing.env.Land.<name>into the current shell (set -a; . FILE; set +a). - Then it sources
TierEnvironment.sh, which resolves the base.env.Land(or falls through to.env.Land.Samplewhen the override file is absent) and re-applies any shell-exportedTier*so a one-offexport TierStorage=Node ./Maintain/Debug/Build.sh ...always wins. - The resolved tier set is echoed before the build starts so CI logs show the exact configuration for each run.
- The build script handles
Cargo feature derivation
- Non-default infrastructure values activate Cargo features named
Tier<Capability><Value>(e.g.TierFileSystem=Layer4→--features TierFileSystemLayer4). - Default infrastructure values do NOT activate a feature - the default is the always-compiled baseline; only upgrades add to the build.
- Per-subsystem routing values do NOT map to Cargo features. They are compile-time string constants consumed at runtime by
mod.rs:: mountain_ipc_invokeviaenv!()plus astd::env::varfallback for shell-time overrides.
- Non-default infrastructure values activate Cargo features named
Cocoon ESBuild define blob
- Every
Tier*env var in the process at build time becomes a__LandTier_<Capability>__replacement token serialised into$CocoonEsbuildDefine(a JSON string Node serialises before export). - The sweep is a wildcard over
process.envkeys starting withTier, so new tiers added to.env.Landare picked up automatically with zero source changes.
- Every
Vite / Astro define
- Sky’s
astro.config.tsforwards everyTier*env var to the Vitedefinemap so the webview bundle also carries the baked-in values asimport.meta.env.Tier*substitutions. Same wildcard pattern as the Cocoon sweep; new tiers pass through without code changes.
- Sky’s
Phase 3: Mountain - Rust Compile-Time + Runtime Propagation
build.rstier propagation (Land/Element/Mountain/build.rs)- Mountain’s build script calls
PropagateTierGating()once per compile. EmitTierDefaults()emitscargo:rustc-env=Tier<Capability>=<Default>for every infrastructure AND per-subsystem tier, soenv!()always resolves at compile time even when.env.Landis absent.- The override pass reads the resolved
.env.Landand re-emits values so file entries take precedence over the defaults. - For infrastructure tiers it emits
cargo:rustc-cfg=feature="Tier<...>"for every non-default pair that matches a declared feature. - It emits
cargo:rerun-if-changed=<envfile>so touching.env.Landinvalidates the compile. TierExtensionHost=WebWorkeractivatescargo:rustc-cfg=no_node_hostwhich gatesCocoonManagement.rsout of the binary entirely.
- Mountain’s build script calls
Feature whitelist
IsDeclaredTierFeaturemirrors Mountain’sCargo.toml[features]block.IsDefaultTierValuelists every valid(Key, Value)pair (defaults AND non-default routing values). Any pair absent from both tables produces acargo:warning, so typos in.env.Landfail loud atcargo buildrather than silently no-op at runtime.
Runtime dispatch in
mod.rs(Land/Element/Mountain/Source/IPC/WindServiceHandlers/mod.rs)mountain_ipc_invokedeclaresconst TIER_<Name>: &str = env!("Tier<Name>", "<default>");at the top so each per-subsystem dispatch arm reads its tier from a compile-time string constant.tier_routes_to_node(BakedConst, EnvKey)returns whether the resolved value (env override first, baked const second) is"Node". Dispatch arms gate on this:"git:exec" | "git:clone" /* … */ if tier_routes_to_node(TIER_SCM, "TierSCM") => { forward_to_cocoon!("scm", command, Arguments) }, "git:exec" => { Git::HandleExec::Fn(Arguments).await },- The same pattern guards
storage:*,debug:*, every subsystem with a native Mountain handler.
Boot banner (
Land/Element/Mountain/Source/LandFixTier.rs)LogResolvedTiers()is called fromBinary::Main::Entry::Fnbefore the Tokio runtime spins up.- Two lines are emitted: the first lists every compile-baked infrastructure tier (using
env!()literals - zero runtime cost), the second lists every runtime-readable per-subsystem tier (usingstd::env::varwith the bakedenv!()fallback - exactly whatmod.rsreads).
Phase 4: Cocoon - TypeScript Runtime Dispatch
CocoonMain.tsprelude (Land/Element/Cocoon/Source/Bootstrap/Implementation/Cocoon/Main.ts)- The very first statements of the bundled Cocoon main file populate
globalThis.__LandTiersfrom the esbuild-substituted__LandTier_<Capability>__identifiers, falling through toprocess.env.Tier<Capability>and finally the hard-coded defaults. - One
declare const __LandTier_<Name>__: stringper tier ensures TypeScript checks pass while esbuild’sdefinesubstitutes the literal at bundle time. - The prelude runs before the
Tierutility is first imported.
- The very first statements of the bundled Cocoon main file populate
Utility/Tier.tsdefault export (Land/Element/Cocoon/Source/Utility/Tier.ts)- The module exposes a single
const Tier = { … } as constobject whose keys match the capability names. Reads are synchronous and side-effect-free. - Every per-subsystem tier has a
TierXValueunion type. ThePick<…>(…)calls resolve from__LandTiersfirst, thenprocess.env.TierX, then the baked default. - On first import it emits the Cocoon runtime banner via
LandFixLog.Infoso the boot log documents exactly which tier set this process is using.
- The module exposes a single
Dispatch patterns
- Static dispatch (preferred for infrastructure tiers):
export default Tier.Glob === "Native" ? CompileNative : CompileJavaScript;- esbuild’s
definesubstitutions dead-code-eliminate the inactive arm in production bundles.
- esbuild’s
- Runtime branch (per-subsystem tiers):
if (Tier.Storage === "Node") { … } else { … }- used when two arms must coexist in the same bundle (every subsystem tier is in this category since Mountain ↔ Cocoon routing flips at runtime).
- Static dispatch (preferred for infrastructure tiers):
Phase 5: Wind / Sky - Webview Dispatch
Wind/Source/Utility/Tier.ts(Land/Element/Wind/Source/Utility/Tier.ts)- Mirrors Cocoon’s module in shape but reads from
import.meta.env.Tier<Capability>(Vite-substituted at build time), falling through toglobalThis.__LandTiersand finally hard-coded defaults. - Identical type-export set and identical
Pick<…>(…)table - the only differences are the env source (import.meta.envvs.process.env) and the banner (console.infovs.LandFixLog).
- Mirrors Cocoon’s module in shape but reads from
Cross-Element agreement
- All three banners - Mountain, Cocoon, Wind - must report identical values for each capability. A mismatch means one build tool read a different env file or a polyfill failed to populate
globalThis.__LandTiers; both paths are grep-able from the boot log.
- All three banners - Mountain, Cocoon, Wind - must report identical values for each capability. A mismatch means one build tool read a different env file or a polyfill failed to populate
Phase 6: Adding a New Tier
Declare the capability
- Add a row to
.env.Land.SampleAND.env.Landwith the default value.
- Add a row to
Bake the default in Mountain
- Add
(name, default)toEmitTierDefaults()inMountain/build.rs. - Add every legal
(Key, Value)pair toIsDefaultTierValue(). - If the new tier should gate a Cargo feature, add the feature name to
IsDeclaredTierFeature()AND the matching entry toMountain/Cargo.toml [features].
- Add
Add to
turbo.json- Add the variable name to
globalEnvso turbo treats it as a cache-invalidating input.
- Add the variable name to
Extend the TypeScript type declarations
- Add a
Tier<Capability>Valueunion type and aPick<…>(…)call inElement/Cocoon/Source/Utility/Tier.ts. - Mirror the change in
Element/Wind/Source/Utility/Tier.ts. - Add the
declare const __LandTier_<Name>__: string;line + matching__LandTiersentry inElement/Cocoon/Source/Bootstrap/Implementation/Cocoon/Main.ts.
- Add a
Wire the dispatch arm
- In
Mountain/Source/IPC/WindServiceHandlers/mod.rs, addconst TIER_<NAME>: &str = env!("Tier<Name>", "<default>");near the other tier consts, then wrap the relevant command group withif tier_routes_to_node(TIER_<NAME>, "Tier<Name>") => { forward_to_cocoon!(…) }.
- In
Update flavor overlays
- Add the tier (with the correct flavor-specific value) to all three overlay files:
.env.Land.RustOnly,.env.Land.NodeFirst,.env.Land.Full.
- Add the tier (with the correct flavor-specific value) to all three overlay files:
Extend the runtime banner
- Add the variable to
LogResolvedTiers()inMountain/Source/LandFixTier.rsso the new capability appears in the boot log.
- Add the variable to
Failure Modes
.env.Landmissing:Build.shfalls back to.env.Land.Sample;build.rsfalls back to theEmitTierDefaultstable;Utility/Tier.tsfalls back to the same defaults. All four paths converge on the same baseline.- Typo in
.env.Land: Rust build surfacescargo:warning=Tier<…>=<…> declared in <file> but has no matching Cargo feature. TypeScript accepts the value as an opaque string (union literals at the type level only); the banner shows the typo verbatim so it is visible at boot. - Banner disagreement: Compare the three
[LandFix:Tier] …lines in the boot log. If they disagree on a capability, one build tool did not see the override - usually because the shell that launched the build did not source.env.Land. Re-run under./Maintain/Debug/Build.sh, which is the sole supported entry point. - Flavor override surprises: A
--flavorarg overrides everything in.env.Land. Confirm the flavor line inBuild.sh’s startup banner matches what you expect; the resolved tier table is echoed shortly after. TierWebSocket=MistrequiresMist::WebSocketinfrastructure: Setting this without a Mist build will boot the listener but no handlers are registered. Check the Mountain boot log for[Lifecycle] [Setup] TierWebSocket=Mist - starting WebSocket transport.
Related Source Files
| Element | Path | Role |
|---|---|---|
| Repo root | Land/.env.Land | Active tier set (overrides Sample) |
| Repo root | Land/.env.Land.Sample | Canonical default tier set |
| Repo root | Land/.env.Land.RustOnly | Flavor overlay: native Rust everywhere |
| Repo root | Land/.env.Land.NodeFirst | Flavor overlay: Cocoon Node.js everywhere |
| Repo root | Land/.env.Land.Full | Flavor overlay: native + Mist WebSocket |
| Repo root | Land/turbo.json | Tier names registered in globalEnv |
| Maintain | Land/Maintain/Debug/Build.sh | --flavor arg + env fan-out |
| Maintain | Land/Maintain/Release/Build.sh | Same flavor support for release builds |
| Maintain | Land/Maintain/Script/TierEnvironment.sh | Cargo feature derivation + esbuild define |
| Mountain | Land/Element/Mountain/build.rs | Cargo feature + rustc-env propagation |
| Mountain | Land/Element/Mountain/Source/LandFixTier.rs | Runtime banner (both layers) |
| Mountain | Land/Element/Mountain/Source/IPC/WindServiceHandlers/mod.rs | Per-subsystem tier_routes_to_node dispatch |
| Cocoon | Land/Element/Cocoon/Source/Utility/Tier.ts | Node-side dispatcher |
| Cocoon | Land/Element/Cocoon/Source/Bootstrap/Implementation/Cocoon/Main.ts | globalThis.__LandTiers prelude |
| Wind | Land/Element/Wind/Source/Utility/Tier.ts | Webview-side dispatcher |
| Sky | Land/Element/Sky/astro.config.ts | Vite define forwarding (wildcard sweep) |