SECS — Current Status
Last updated: 2026-07-07
v2 Architecture (AD-0006, accepted 2026-05-16)
The v2 rewrite is the current execution target. See
docs/adr/ad-0006-secs-v2-kernel-rewrite.md
for the full architecture and waves. The legacy v1 tree has been removed
after the Valenar v2 deletion gate was satisfied; this file now tracks the
active v2 surface only.
- Built v2 assemblies (all under
src/):SECS.Core,SECS.Native,SECS.Schema,SECS.Compiler.Contracts,SECS.Kernel,SECS.Runtime,SECS.Adapters.Standalone. All seven build clean underSECS.sln. - Save/load: implemented via
SecsSavePayload(kernel section + runtime section). Restore viaSecsSaveLoadCoordinator.Restore(bytes). Seedocs/runtime/rn-save-load-determinism.md. Policy-child linkage (PolicyActiveChildSaveRow) and per-actor mutable slot contents (PolicySlotSaveRow) are persisted; channel snapshot stores (prev-tick and current-tick) intentionally re-derive over the first post-restore tick. - Tests: v2 test projects live at top-level
tests/(SECS.Core.Tests,SECS.Kernel.Tests,SECS.Native.Tests,SECS.Runtime.Tests,SECS.Schema.Tests,SECS.Standalone.Tests,SECS.Compiler.Smoke,Valenar.V2.Server.Tests,Endgate.Server.Tests). The post-deletion validation matrix runs the six v2 engine test projects, the standalone adapter tests, and the Valenar v2 server tests from top-leveltests/.scripts/validate-production-core-spine.shis the focused production-core spine check: it runsSECS.Runtime.Tests,Valenar.V2.Server.Tests,Endgate.Server.Tests, then buildsSECS.sln. It supports split dotnet command selection throughDOTNET_TESTandDOTNET_BUILD. Two staged guard test files (tests/SECS.Kernel.Tests/Benchmarks/PrevTickSnapshotSmokeTests.csandtests/SECS.Standalone.Tests/Guards/V2GeneratedProvenanceGuardTests.cs) back the prev-tick snapshot and v2 Generated provenance contracts; the five guards undertests/Valenar.V2.Server.Tests/Guards/(AllCommandIdsRouteToStructuredEnvelope,AllQueryIdsRouteToStructuredEnvelope,NoFullParityClaimsInV2Readme,NoUnityTokensInV2ServerTree,NoUnityTokensInV2ClientTree) lock the v2 SignalR routing surface, the README honesty contract, and the no-Unity boundary. - Examples:
examples/valenar-v2/{Content,Generated,Host}is the current v2 host.secscemits the schema, catalog,Hashes, andSecsModuleforGenerated/fromContent/intoobj/**/SecsGenerated; the only retained checked-in.csunderGenerated/is the 21-lineValenarHostIds.cshost failure-reason surface.examples/minimal-v2/andexamples/character-trainer-v2/exercise the standalone adapter.examples/endgate/is a focused v2 prototype for authority, SignalR transport, browser-shell, and benchmark coverage. - Valenar v2 Server/Client (SignalR + reused v1 frontend):
examples/valenar-v2/Serveris an ASP.NET Core SignalR hub at/gamehubwrappingValenarV2GameHost, andexamples/valenar-v2/Clientis the reused v1 React/SignalR Valenar frontend pointed at that hub (Vitebase: '/valenar/'with proxy at/valenar/gamehub). Implemented domains: shell handshake, character sheet (vitals + stats), skills, inventory, activity catalog / runs, pending choices, tooltip (channel:/skill:/activity:families), time-bank state, game speed, activity availability, queue commands (enqueue / cancel / move), travel (location graph, queue.enqueue_travel, character.move, travel.plan, travel.cancel). Map domain ships geometry / biomes / hierarchy from an authored WorldData fixture; features deferred to a follow-up wave. Character sheet and inventory now ship v1-parity content (37 channels, 14 skills with full FeedsChannels propagation (all skills wired, not just Walking/Fieldcraft - the twelve other skills lift their SkillSheetProfile.FeedsChannels UI metadata into runtime modifier bindings; 34 propagation modifiers total), 14 starter items with v1-accurate per-item channel sources (Self-bound intrinsic modifiers carry the v1 +1/+2/+3/+10 delta, not the previous Static-stand-in +1.0 stub) and OnEquipped/OnUnequipped equip cycle including CrudeTorch's dual modifier (Perception + TrapDetection); all child entities seeded through MainCharacterHostLogic. SeedMainCharacterChildren via host-side SpawnSkillWithInitialData / SpawnItemWithInitialData helpers (no imperative seeding in Program.cs or ValenarV2GameHost.cs). Empty domains (matrix-tracked underexamples/valenar-v2/README.md, not faked): settlement, economy, forces, realm, journal, combat, camp. Production-core diagnostics now close fault visibility and Valenar persistence/rejection diagnostics: shared SECS runtime fault projection feeds host JSON reports, the Valenar hosted loop stops after a fatal tick fault, session persistence follows durable-state revisions rather than broadcast changes, paused UI broadcasts do not churn saves, and rejected saves are quarantined with.rejection.jsonreports. This does not close mod loading, save migration, activity-body lowering, spatial language, or full production readiness. - CI coverage:
.github/workflows/wave-t-validation.ymlruns the active v2 validation matrix on every push and pull request. The steps cover the six engine projects (SECS.Core.Tests,SECS.Native.Tests,SECS.Schema.Tests,SECS.Kernel.Tests,SECS.Runtime.Tests,SECS.Standalone.Tests) plus the v2 server (Valenar.V2.Server.Tests) and anpm run buildofexamples/valenar-v2/Clientagainst Node 20 so the reused v1 React/SignalR frontend stays type-clean.
What's Built
Engine
- Channel resolution — runtime implements the 6-phase pipeline (base → additive → multiplicative → HardOverride → clamp → return)
- Channel declarations — three kinds: Contributed, Base, Accumulative
- Channel value types — runtime supports
int,long,float,double, andboolchannel declarations, sources, formulas, and modifier effects - Dynamic formula channels — formula delegates evaluated at resolution time, with owner context
- Modifier system — bindings, triggers, duration, state machine (Active/Inactive/Removed)
- Modifier stacking policies — Single, Unique, Stackable with max_stacks and ReApplyMode (ignore, refresh, extend, replace)
- Dynamic modifier effects — formula-driven effect values that scale with game state
- Conditional modifiers —
when(guard-on-add) andwhile(continuous trigger) - Command processing — channel sources, modifier bindings, increment field
- Tick pipeline — ordered step execution with phase-based system sorting
- ITickSystem — systems with frequency gating and phase declarations
- Load distribution — per-entity tick spreading via AllByContractForTick (zero-alloc ref struct)
- Event system — pulse events, on_action hooks, player choices (PendingChoice)
- Scope walking — declared
walks_toedges lower toScopeWalkPlanRow; the kernel resolves walks on demand viaIKernelRelations.TryResolveScopeWalk(composing the Ownership / CollectionMembership owner graphs), and formulas reach the same single resolution path throughIFormulaReadContext.TryResolveScopeWalk - Instance store — entity tracking by contract, scope index for parent-child
- Template activation/deactivation — with command buffer pattern
- Cascading destruction — DestroyWithChildren for hierarchical entities
- Template upgrades — Upgrade method preserves entity handle
- Dirty tracking — the kernel current-tick channel store carries a per-tick dirty set; a host-field write through the cache-invalidating field bridge (
CacheInvalidatingFieldBridge) and a modifier-binding rebuild (ManagedModifierBindingTable.RebuildPreparedBuckets/DetachAllForTarget) mark the affected(entity, channel)pairs dirty, so the dirty-aware secondChannelResolveStepre-resolves only changed pairs andSyncDirtyStepwrites settled Contributed-with-field values back to host fields throughIHostFieldBridge - Identifier hashing — FNV-1a-64
ulongacross runtime registries, generatedHconstants, command payload ids, localization keys, host bridge signatures, Valenar, character-trainer, and benchmarks - Localization — YAML-based, ILocalizationProvider, FNV-1a-64 key hashing
- Rich text parsing — single-pass parser for scope references, colors, links, conditions
- Tooltip system — auto-generated from resolved channel breakdowns and ModifierDeclaration
- Typed identity record structs —
TemplateId,ModifierId,ChannelId,TagId,PhaseId,TickRateId,ScopeId,ContractId,SystemId,EventId,OnActionId,ActivityId,ActivityRunId,ActivityLaneId,PolicyId,DomainId,NeedId,SelectorId,RuleId,SlotKindId(allreadonly record struct (ulong Value)) - Scoped collections —
ScopeCollectionRowdeclarations (CollectionKind.List+CollectionKind.Dictionary) andScopeCollectionHookRowhook metadata onWorldSchema, generated lifecycle-hook dispatch throughCollectionLifecycleHookRegistry.Register, kernel relationsScanChildren/CountChildren(src/SECS.Kernel/Relations/Kinds/OwnershipStore.cs) andTryGetCollectionMemberByTemplate/ScanCollectionMembersByTemplate(IKernelRelations), generatedOnAdded/OnRemovedhooks (hook method-name collision detection: SECS0840). Spec:docs/design/08-collections-and-propagation.mdPart 1. - Virtual binding propagation —
ManagedModifierBindingTablematerializespropagates_to children [where ...]by scanning collection members (IKernelRelations.ScanCollectionMembers), attaches/detaches virtual modifier bindings on children when the source binding or collection membership changes, supports tag / template / contract filter predicates (PropagationFilterKind). Spec: 08 Part 2. - Structural tags —
TagIdtyped identity,template_tagsslot,has_tagpredicate evaluation in propagation filters and aggregatewhereclauses. Spec: 08 Part 3. - Prev-tick snapshots —
ChannelRow.TrackPrevopt-in (compiler-emit surfaceEmittedChannelDeclaration.TrackPrev/SecsChannelAttribute.TrackPrev), end-of-tick rollover viaSnapshotRolloverStep→IKernelSnapshots.RolloverTick, reads viaIKernelChannels.TryGetPrevValue/TryGetPrevSnapshot. Spec: 08 Part 4. - System execution model — Pre / Main / Post stages with intra-stage
Order, frequency gating viaTickRate, per-tickEveryTickand one-shotOncesentinels. Spec: 08 Part 5. - Activity/policy startup mod finalization —
ModRegistryplusSecsRegistry.RegisterActivityMod,RegisterPolicyMod, andFinalizeModRegistration()merge activity/policy mod records once at host bring-up, with load-order last-writer-wins slots, conflict diagnostics, and replace-only architectural-slot errors. - Runtime fault visibility — host wrappers throw the shared typed
SecsRuntimeTickFailedExceptionwhen a runtime tick records failed steps or error diagnostics, andSecsRuntimeFaultReportprojects the failure summary plus recent diagnostics into a host-persistable report shape. - SECS compiler structural/data/behavior/orchestration slice —
secs-roslyn/discovers.secsfiles, keeps them out of the normal C# parser, emits inspectable generated C#, injects generated syntax trees into compilation, and is pinned bytests/SECS.Compiler.Smoke. The backed source slice includes scopes, contracts, empty/scalar-field templates, serialized scalar template-field values, top-level field declarations, scalar scope fields,walks_to, typed scoped collections with generated lifecycle hooks, simple channels, static/dynamic channel modifiers, generated formula delegates, activation-bound no-arg contract methods, simple template method statements, simple systems, dynamic formula-backed channels, on_action rows, pulse/on_action events, event options, simple activities, backed FromActor policies, the six mod operations (inject,replace,try inject,try replace,inject_or_create,replace_or_create) routed through the source-set merge pass (SecsDeclarationMerger, pinned by thephase3a-*smoke fixtures), andGenerated.SecsModule.RegisterRuntime(SecsRuntimeBuilder). - SECS compiler Phase 1.5 tooling path —
SECS.Compiler.Toolingwrapssecscfor game/editor invocation, computes content hashes from package inputs, maintainsCache/SECS/<content-hash>/, reloads cached content assemblies, validatesGenerated.SecsModule.BuildSchema(), and supports the RID-scopedsecsctool-bundle contract without adding Phase 2 language syntax.
Valenar Example Game
Granular Valenar v2 status lives in examples/valenar-v2/README.md and
the project docs under docs-site/content/valenar/. High-level summary:
Valenar v2 surface (SignalR server + reused v1 frontend)
The SECS v2 runtime is green and the v2 compiler-emitted Generated/
output (emitted by secsc from Content/ into obj/**/SecsGenerated,
with only the 21-line ValenarHostIds.cs retained as a checked-in host
failure-reason surface) plus Host wiring under
examples/valenar-v2/{Content,Generated,Host}
exercise it. examples/valenar-v2/Server is an ASP.NET Core SignalR
hub at /gamehub wrapping ValenarV2GameHost, and the existing v1
React/SignalR Valenar frontend lives at examples/valenar-v2/Client
and connects to that hub through @microsoft/signalr + zustand (Wave
4 landing). The v2 server backs the wire surface with real runtime data
for shell, character sheet (vitals + stats), skills, inventory, activity
catalog / runs, pending choices, tooltip (channel: / skill: /
activity: families), time-bank state, game speed, activity availability,
queue commands (enqueue / cancel / move), travel (location graph,
queue.enqueue_travel, character.move, travel.plan, travel.cancel), and
map (geometry + biomes + hierarchy from an authored WorldData fixture
imported at boot through WorldDataImportSystem; the MapProjector
emits the v1 MapSnapshot wire shape, and features is honest-empty
until the feature-placement follow-up wave wires the
FeaturePlacementSelector port). Remaining gameplay domains are
matrix-tracked under examples/valenar-v2/README.md and stay
empty/unavailable rather than faked until per-domain content waves back
them.
Endgate Prototype
examples/endgate/ is a focused v2 prototype rather than a full game surface.
Content/,Generated/, andHost/form a runnable authority shell;secsccompilesContent/*.secsinto theGenerated/output at build time, with only explicit host/catalog helpers (ItemDefinitions.cs,WaveScripts.cs) checked in underGenerated/.Server/is an ASP.NET Core SignalR host on/gamehub(defaulthttp://localhost:5064), andClient/is a React/Vite browser shell that connects through@microsoft/signalr.- Fatal runtime faults mark the session faulted, write an Endgate JSON fault report using the shared
SecsRuntimeFaultReportprojection, and stop the hosted simulation loop. tests/Endgate.Server.Tests/covers the buildable shell, deterministic map generation, horde-lane detour metrics, defender assignment/tick outcomes, router/session live-map commands, and fatal runtime-fault reporting.tests/SECS.Standalone.Tests/Guards/EndgateGeneratedProvenanceGuardTests.cslocks the Generated provenance contract.examples/endgate/Benchmarks/EndgateAuthorityBenchmarks.cscovers live snapshot query, map snapshot query, defender assignment, steady-state tick, and map regeneration.
What's NOT Built
Engine
- SECS Compiler language features beyond the current backed slice —
secs-roslyn/now has the structural shell, packaging/cache/runtime invocation tooling, data declarations, scalar template-field values, simple channel modifiers, generated formulas, activation methods, simple systems, dynamic formula-backed channels, on_actions, events, event options, activities, the backed FromActor policy subset, and the six mod operations through the source-set merge pass.conflict-report.jsonemission, broad expression lowering,registry_only, structured type metadata, string field values, template-intrinsic channel sources, effective template-value modifiers, non-FromActor selectors, and unsupported policy decisions remain future compiler work. Phase plan inSECS-Compiler-Plan.md. - Contract-declaration mod merging — the compiler's source-set merger (
SecsDeclarationMergerinsecs-roslyn/, pinned by thephase3a-*fixtures intests/SECS.Compiler.Smoke) applies all six operations across template, scope, system, on_action, channel, modifier, event, activity, and policy declarations;contractdeclarations are not a merge target and a mod operation naming one is rejected. Design indocs/design/06-overrides-and-modding.md. - Collections — SECS source surface —
ScopedList<T>/ScopedDictionary<TKey, T>field declarations and@Settlement.Resources[Iron].add_modifier Xindexer access need the SECS compiler. The runtime API (ScanChildren/CountChildrenandTryGetCollectionMemberByTemplate/ScanCollectionMembersByTemplate) is wired today; the source-level lowering is not. - SECS0822 diagnostic —
ScopedList<T> does not support indexed access; use .Where() / .First() / iteration— diagnostic id reserved, requires compiler. (Renumbered from SECS0820 in Wave 8 to free that code for the slot-restore runtime band; seeSECS-Compiler-Plan.md § Diagnostic Code Catalog.) - Aggregate channels over collections — the Sum / Min / Max / Average / Count / CountWhere aggregate-channel surface specified in
docs/design/08-collections-and-propagation.mdPart 1 § Aggregate channels has no v2 runtime owner:ChannelRowcarries no aggregate source andIFormulaReadContextdefers aggregate reads to a later wave. The structural-predicate schema rows (PropagationFilterRow/PropagationFilterKind) are live for modifier propagation only. - Template modifiers — template-scoped modifier declarations (designed in
docs/design/03-channels-and-modifiers.md, not implemented) - Scope stack runtime —
@prev,@rootduring foreach iteration (designed indocs/design/05-expressions.md, not implemented) - Compile-time scope validation — walk reachability checking (designed, requires compiler)
- Save/load — slot codec scope — the unified engine save payload (
SecsSavePayload: kernel + runtime + host sections, restore viaSecsSaveLoadCoordinator.Restore) is implemented and persists modifier bindings, saved scopes, pending journal entries, pending choices, active activity runs, policy-child links, and per-actor slot contents (see What's Built). The remaining gap: slot kinds whose element type is outside the closed blittable codec (SlotElementTypeTag: UInt64 / Int64 / Int32) are out of save scope and throw at snapshot time (docs/design/FUTURE_WORK.md §1.3). - Channel resolution caching — demand-driven read-through caching exists:
ChannelResolverV2serves cache-eligible(entity, channel)pairs from the kernel current-tick channel store and recomputes on a dirty-or-miss, with impl-agnostic invalidation on host-field writes and modifier-binding rebuilds, a dirty-aware second resolve pass, andSyncDirtyStepwrite-back. Open: (1) formula-backed channels still BYPASS the cache and always recompute, because the runtime carries noreadsChannelsdependency graph (FormulaResolverRegistry.Registerhas no read-set parameter); a future wave that builds that graph upgrades them from bypass to invalidate-via-readset at the single eligibility switch point in the resolver; (2) aggregate-collection channel dependency invalidation; (3) the cache is intentionally transient and never persisted — there is no cache state to save/load. - Priority on modifier effects — field reserved, not implemented
Valenar
See examples/valenar-v2/README.md and docs-site/content/valenar/ for
the current implementation matrix and design backlog.
Open Decisions
- Namespaces — whether SECS definitions need namespacing for mod collision prevention. Currently global (Paradox-style). Decision deferred.
distribute false— opt-out syntax for load distribution on aggregation systems. Designed but not implemented.