SECS Core Concepts
The engine is game-agnostic. It provides mechanisms; the game defines the data through .secs content, compiler-owned generated output, and explicit retained host-owned surfaces. These seven concepts together form the SECS programming model — read docs/design/00-overview.md as the current entry point, then follow the numbered design docs for the live contract.
The seven definitions
| Concept | What it is | Lifecycle |
|---|---|---|
| Template | Defines what entities ARE. Static. Registers own-root intrinsic channel sources, implements contract queries (CanBuild, CanEquip, etc.), and implements contract void methods (OnBuilt, OnEquipped, etc.). Contract lifecycle bindings choose which void methods the runtime calls automatically. | Registered at startup. Looked up by FNV-1a-64 hash of the template's canonical id string, which the compiler emits owner-less with the declared name reproduced verbatim: valenar:template/<Name> colon-slash (e.g. valenar:template/Farm; see docs/design/01-world-shape.md § "Canonical id string format" and docs/decisions/ADR-0003-canonical-id-grammar.md). The matching hash lives as H.Template<Name> in compiler-emitted Hashes.g.cs under the generated project obj/**/SecsGenerated output. When a name is dual-use (Wood, Stone, Gold are both Settlement scalar channels AND Resource templates), the compiler emits the distinct constants H.Channel<Name> (from valenar:channel/<Name>) and H.Template<Name> (from valenar:template/<Name>) so the channel and template hashes are distinct and the engine no longer needs to disambiguate by usage site. |
| System | Defines what HAPPENS each tick. Non-static (per-tick instance). Frequency-gated, phased. Iterates entities via AllByContractForTick (zero-alloc ref struct). | Runs in pipeline order during TickContext.Tick(). |
| Event | Defines what happens WHEN. Pulse-triggered, on_action-triggered, or player-choice-triggered. | EventDispatcher evaluates triggers per-tick; player choices park on PendingChoice. |
| Modifier | Reusable effect bundle. Bindings + triggers + duration + stacking policy + decay. Can be ReApplyMode.Refresh, Stack, or Reject. | Attached to entities by host or by OnAction. Decay handled by ModifierBindingStore. |
| Formula | Dynamic value used by an intrinsic channel source or modifier effect at resolution time with owner context. Has access to scope walks. | Evaluated lazily during channel resolution; a formula-backed channel BYPASSES the read-through cache and recomputes on every read against the owner's current state (the runtime has no readsChannels dependency graph yet, so caching on (entity, channel) alone could serve a stale value). Non-formula sibling reads inside the formula are still cache-served within the tick. |
| Scope | Hierarchy node. In v2, scope walks traverse the kernel relation table (ScopeWalk relation kind per docs/runtime/rn-kernel-contract.md); the host-facing surface is IHostFieldBridge / IHostEntityBridge. Host defines parent-child relationships (e.g., Empire → County → Province → Building). | Static hierarchy maintained by host; engine never mutates scope graph. |
| Mod operation / slot merge | Layered content change expressed with inject, replace, try inject, try replace, inject_or_create, or replace_or_create. Operations target compiler-owned semantic slots; later load-order writes win for the same slot. | Resolved by the compiler pre-binding merge pass, with activity/policy runtime finalization as the current executable subset. |
Entity & channel flow (the resolution pipeline)
- Registration. In v2, hosts construct a
WorldSchemafrom compiler-owned output such asGenerated.SecsModule.BuildSchema()and pass it toSecsRuntimeBuilder.Build(); templates, systems, modifiers, contracts, scopes,on_actions, activities, and policies are declared through the schema rather than registered imperatively. - Activation. Host calls
Activate(handle, contractId, templateId)for each entity. Engine looks up the template, attaches channel sources, then routes the contract's activation binding byContractMethodRow.ImplementationKind:Generateduses generated handlers,HostBridgeuses the host callable bridge, andContractMethodId.Noneis the only no-op. Method names such asOnBuiltare game vocabulary, not hard-coded engine hooks. - Resolution on demand. When the host or a system reads a channel,
ChannelResolutionruns the 6-phase pipeline documented indocs/design/03-channels-and-modifiers.md:- base — read host base for
Base/Accumulative, or start at zero and add own-root intrinsic channel sources forContributed - additive — sum additive contributions (
+5,-2) - multiplicative — apply multiplicative contributions (
*1.2,*0.85) - HardOverride — last-applied override replaces the post-multiply value
- clamp — apply min/max clamps from declarations
- return — final value; for a cache-eligible (non-formula) channel it is written into the kernel current-tick channel store, which doubles as the per-tick read-through cache. A later read of the same pair this tick is served from the cache unless the pair was marked dirty.
- base — read host base for
- Dynamic formulas re-evaluate every read using owner's current state (their other resolved channels, scope walks, etc.) — a formula-backed channel BYPASSES the cache entirely and recomputes on every read. This is the correctness-safe policy while the runtime has no
readsChannelsdependency graph; a non-formula sibling the formula reads is still cache-served within the tick. - Dirty tracking. When an input changes the dirty set records the affected
(entity, channel)pairs at the invalidation chokepoint — a host-field write through the cache-invalidating field bridge marks the field's dependent channels dirty, and a modifier-binding rebuild marks the target's modifier-affected channels dirty. The dirty-aware secondChannelResolveStepre-resolves only the dirty pairs; at end of tickSyncDirtywrites the settled value of each Contributed-with-field channel back to the host throughIHostFieldBridgein v2 (perdocs/runtime/rn-external-engine-adapters.md § The Seven Host Bridges). Base and Accumulative channels are NOT written back — their host field is the source of truth, not a cache. - Tick pipeline. Systems run in phase order (Pre → Main → Post). Each system iterates its contract's entities. Load distribution spreads expensive per-entity work across multiple ticks via
AllByContractForTick.
Critical pattern: Templates are data
Host data and engine channels are deliberately separate. Base and Accumulative channels read host-owned fields as their source of truth. Contributed channels start at zero; the host field, when present, is only a cache that SyncDirty writes after resolution.
Template-body channel declarations lower to compiler-emitted channel sources on the template activation root. They are not a generic anonymous contribution mechanism, and they never push into a different scope. Cross-scope influence is expressed through named modifiers so ownership, teardown, tooltips, and mod operations all have a stable target.
Where to look for examples
Terminology split: keep
statfor player-facing UI concepts (the Stats rail tab, stat tooltips, "No pinned stats" empty states, stat-category filters, file names likeStatsExpansion.tsx/statCategories.ts); usechannelfor engine/runtime plumbing AND the data layer (ChannelResolver,ChannelCache,ChannelSourceStore,RegisterChannelSource, thechannelkeyword in.secssource, DTOchannel: stringfields, internal tip-IDs likechannel:Food). Do not blindly global-rename — the prior global Stats→Channels sweep over UI labels was the wrong direction and had to be reverted; UI says "Stats", the data/engine layer says "channel".
src/SECS.Runtime/Channels/ChannelResolverV2.cs— v2 current six-phase channel pipelinesrc/SECS.Runtime/Tick/TickContextV2.cs— v2 system execution ordersrc/SECS.Runtime/Events/EventDispatcherV2.cs— v2 event evaluationsrc/SECS.Runtime/Boot/SecsRuntimeBuilder.cs— builds the runtime from aWorldSchemasrc/SECS.Runtime/Mods/ModRegistryV2.cs— v2 mod accumulatorexamples/valenar-v2/Generated/obj/**/SecsGenerated/*Templates.g.cs— compiler-emitted v2 concrete template-row patternsexamples/valenar-v2/Generated/obj/**/SecsGenerated/SecsModule.g.cs— compiler-emitted v2 schema entry point pattern