Skip to main content

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 under SECS.sln.
  • Save/load: implemented via SecsSavePayload (kernel section + runtime section). Restore via SecsSaveLoadCoordinator.Restore(bytes). See docs/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-level tests/. scripts/validate-production-core-spine.sh is the focused production-core spine check: it runs SECS.Runtime.Tests, Valenar.V2.Server.Tests, Endgate.Server.Tests, then builds SECS.sln. It supports split dotnet command selection through DOTNET_TEST and DOTNET_BUILD. Two staged guard test files (tests/SECS.Kernel.Tests/Benchmarks/PrevTickSnapshotSmokeTests.cs and tests/SECS.Standalone.Tests/Guards/V2GeneratedProvenanceGuardTests.cs) back the prev-tick snapshot and v2 Generated provenance contracts; the five guards under tests/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. secsc emits the schema, catalog, Hashes, and SecsModule for Generated/ from Content/ into obj/**/SecsGenerated; the only retained checked-in .cs under Generated/ is the 21-line ValenarHostIds.cs host failure-reason surface. examples/minimal-v2/ and examples/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/Server is an ASP.NET Core SignalR hub at /gamehub wrapping ValenarV2GameHost, and examples/valenar-v2/Client is the reused v1 React/SignalR Valenar frontend pointed at that hub (Vite base: '/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 under examples/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.json reports. This does not close mod loading, save migration, activity-body lowering, spatial language, or full production readiness.
  • CI coverage: .github/workflows/wave-t-validation.yml runs 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 a npm run build of examples/valenar-v2/Client against 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, and bool channel 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) and while (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_to edges lower to ScopeWalkPlanRow; the kernel resolves walks on demand via IKernelRelations.TryResolveScopeWalk (composing the Ownership / CollectionMembership owner graphs), and formulas reach the same single resolution path through IFormulaReadContext.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 second ChannelResolveStep re-resolves only changed pairs and SyncDirtyStep writes settled Contributed-with-field values back to host fields through IHostFieldBridge
  • Identifier hashing — FNV-1a-64 ulong across runtime registries, generated H constants, 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 (all readonly record struct (ulong Value))
  • Scoped collections — ScopeCollectionRow declarations (CollectionKind.List + CollectionKind.Dictionary) and ScopeCollectionHookRow hook metadata on WorldSchema, generated lifecycle-hook dispatch through CollectionLifecycleHookRegistry.Register, kernel relations ScanChildren / CountChildren (src/SECS.Kernel/Relations/Kinds/OwnershipStore.cs) and TryGetCollectionMemberByTemplate / ScanCollectionMembersByTemplate (IKernelRelations), generated OnAdded / OnRemoved hooks (hook method-name collision detection: SECS0840). Spec: docs/design/08-collections-and-propagation.md Part 1.
  • Virtual binding propagation — ManagedModifierBindingTable materializes propagates_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 — TagId typed identity, template_tags slot, has_tag predicate evaluation in propagation filters and aggregate where clauses. Spec: 08 Part 3.
  • Prev-tick snapshots — ChannelRow.TrackPrev opt-in (compiler-emit surface EmittedChannelDeclaration.TrackPrev / SecsChannelAttribute.TrackPrev), end-of-tick rollover via SnapshotRolloverStepIKernelSnapshots.RolloverTick, reads via IKernelChannels.TryGetPrevValue / TryGetPrevSnapshot. Spec: 08 Part 4.
  • System execution model — Pre / Main / Post stages with intra-stage Order, frequency gating via TickRate, per-tick EveryTick and one-shot Once sentinels. Spec: 08 Part 5.
  • Activity/policy startup mod finalization — ModRegistry plus SecsRegistry.RegisterActivityMod, RegisterPolicyMod, and FinalizeModRegistration() 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 SecsRuntimeTickFailedException when a runtime tick records failed steps or error diagnostics, and SecsRuntimeFaultReport projects the failure summary plus recent diagnostics into a host-persistable report shape.
  • SECS compiler structural/data/behavior/orchestration slice — secs-roslyn/ discovers .secs files, keeps them out of the normal C# parser, emits inspectable generated C#, injects generated syntax trees into compilation, and is pinned by tests/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 the phase3a-* smoke fixtures), and Generated.SecsModule.RegisterRuntime(SecsRuntimeBuilder).
  • SECS compiler Phase 1.5 tooling path — SECS.Compiler.Tooling wraps secsc for game/editor invocation, computes content hashes from package inputs, maintains Cache/SECS/<content-hash>/, reloads cached content assemblies, validates Generated.SecsModule.BuildSchema(), and supports the RID-scoped secsc tool-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/, and Host/ form a runnable authority shell; secsc compiles Content/*.secs into the Generated/ output at build time, with only explicit host/catalog helpers (ItemDefinitions.cs, WaveScripts.cs) checked in under Generated/.
  • Server/ is an ASP.NET Core SignalR host on /gamehub (default http://localhost:5064), and Client/ 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 SecsRuntimeFaultReport projection, 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.cs locks the Generated provenance contract.
  • examples/endgate/Benchmarks/EndgateAuthorityBenchmarks.cs covers 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 slicesecs-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.json emission, 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 in SECS-Compiler-Plan.md.
  • Contract-declaration mod merging — the compiler's source-set merger (SecsDeclarationMerger in secs-roslyn/, pinned by the phase3a-* fixtures in tests/SECS.Compiler.Smoke) applies all six operations across template, scope, system, on_action, channel, modifier, event, activity, and policy declarations; contract declarations are not a merge target and a mod operation naming one is rejected. Design in docs/design/06-overrides-and-modding.md.
  • Collections — SECS source surfaceScopedList<T> / ScopedDictionary<TKey, T> field declarations and @Settlement.Resources[Iron].add_modifier X indexer access need the SECS compiler. The runtime API (ScanChildren / CountChildren and TryGetCollectionMemberByTemplate / ScanCollectionMembersByTemplate) is wired today; the source-level lowering is not.
  • SECS0822 diagnosticScopedList<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; see SECS-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.md Part 1 § Aggregate channels has no v2 runtime owner: ChannelRow carries no aggregate source and IFormulaReadContext defers 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, @root during foreach iteration (designed in docs/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 via SecsSaveLoadCoordinator.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: ChannelResolverV2 serves 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, and SyncDirtyStep write-back. Open: (1) formula-backed channels still BYPASS the cache and always recompute, because the runtime carries no readsChannels dependency graph (FormulaResolverRegistry.Register has 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.