Skip to main content

SECS Future Work — Backlog Index

A durable punch-list aggregating every deferred item, open engine task, stand-in bug, and unresolved design topic across the design docs. Each item cites the doc section or audit finding that generated it. Items are not closed by editing the source; they move to the ## Completed section at the bottom when addressed.

Last regenerated: 2026-05-13.

How this doc is maintained

  • When a new design commit creates engine tasks, compiler diagnostics, deferred features, or stand-in bugs, add rows here at the same time the docs are updated.
  • When an item lands (compiler implements a diagnostic, engine catches up with spec, stand-in gets refactored), move the row to ## Completed with a short "landed " note. Do NOT delete — the history matters.
  • Re-sweep this doc once per quarter (or after any major design pass) against the canonical docs to catch drift.
  • Entries cite committed docs (docs/design/*.md) first, then audit-findings.md and mod-coverage-audit.md by finding number. If a backlog item survives only from deleted legacy docs, restate the needed semantics here in concise live wording instead of citing a removed source.
  • Mod-coverage findings (from mod-coverage-audit.md or future audits of the same class) land in section § 7 below. Each finding admits via the same row schema used elsewhere — citation, blocks, notes — plus a "mod scenario" summary line so the reader can trace the originating concern without re-reading the audit.

1. Engine implementation tasks

Runtime catch-up work driven by committed spec. Each row is work the engine must absorb before the compiler can emit the committed shape without the engine silently dropping or mis-handling the output.

TaskSource doc §Engine file:lineBlocksNotes
Implement scope-walk declared-edge validation at registry-init time — reject WalkScope(from, to) when to is not in from's walks_to list (server-side safety net for SECS0111)01-world-shape.md § "Scope-walk correctness" (L222-291)src/SECS.Engine/SecsRegistry.cs (new method) + walk dispatcherCompiler SECS0111 emits at build time; engine catches mod-injected walks
Implement runtime formula read-set validation — debug-build check on ChannelResolver that checks every resolve(ChannelId) against GetFormulaDependencies(formulaId); throw SecsFormulaReadSetViolation on mismatch03-channels-and-modifiers.md § "Runtime read-set validation" (L947-959)src/SECS.Engine/Resolution/ChannelResolution.cs; new exception type under src/SECS.Abstractions/Exceptions/D-Hybrid rule; debug-build cycle-miss catchCompiled-out under #if DEBUG or equivalent
Auto-extract formula readsChannels from the expression tree (compiler-side task; engine provides the registration surface)03-channels-and-modifiers.md § "reads { } — D-Hybrid rule" (L873-943)Compiler AST walker feeds SecsRegistry.RegisterFormula (src/SECS.Engine/SecsRegistry.cs:110-115)D-Hybrid case 1 (static literal) and case 3 (mixed)Engine side is already built; compiler pass is the missing piece
Populate captured-entity slots (captured0, captured1) at add_modifier call sites when the trigger body references root or prev03-channels-and-modifiers.md § "Trigger lowering" (L814-871), 05-expressions.md § "Built-in sigils" / "add_modifier" (L66-67, L355-356)src/SECS.Abstractions/Commands/CommandBufferExtensions.cs:57-75 (API exists); compiler call-site emission is the gapFull root/prev semantics in triggersAll Generated/ sites currently pass EntityHandle.Null
Implement scope-stack capture in ChannelResolver for root / prev in dynamic-effect re-evaluation (captured1 slot on Formula delegates)03-channels-and-modifiers.md § "Formula lowering" (L802-812)src/SECS.Engine/Resolution/ChannelResolution.csDynamic modifier effects that read cross-scope through root/prevSlot is reserved; both captured args currently pass EntityHandle.Null
Implement DestroyEntityWithChildren command in CommandBuffer / CommandProcessor04-behavior.md § "activity Name — targeted form" (L1106-1108)src/SECS.Abstractions/Commands/CommandBuffer.cs, CommandProcessor dispatch branchscope.destroy_with_children() in activity effect bodiesUsed by raze_location activity
Implement a SECS log command if script-authored logging returns to current fixtures04-behavior.md § "End-to-end: PlagueSpreadsEvent"src/SECS.Engine/Pipeline/TickContext.cs or a host-observer bridgeOptional source-authored gameplay/UI loggingCurrent .secs fixtures avoid @log(...); earlier stand-ins dropped it because no command surface existed
Implement arbitrary TickRate system cadence support beyond the named Daily/Weekly/Monthly/Yearly/Once helpers04-behavior.md § "Frequency gating" (L180), superseded source shape in 08-collections-and-propagation.md § Part 5src/SECS.Engine/Pipeline/ITickSystem.cs already accepts int; compiler binding/emission for frequency = <TickRate expression>; is the gapNon-standard cadencesEngine accepts any positive int today
Implement distribute false (opt-out of per-entity load distribution) on Monthly/Weekly systems04-behavior.md § "Frequency gating" (L178)src/SECS.Engine/Pipeline/ITickSystem.cs (possibly new Distributed prop)Monthly global recalculation systemsCurrent live behavior docs commit automatic distribution for these cadences; this row tracks the explicit opt-out surface if we add it.
Register EventSource discriminant on EventEntry (analog of SystemSource for mod-operation provenance/dispatch)04-behavior.md § "Event registration" (L580-581)src/SECS.Engine/Events/EventEntry.csEvent provenance symmetry with systemsNot yet a field; open question whether needed
Implement ResourceProductionSystem frequency (currently missing; runs every tick at O(settlements × locations))audit-findings.md § 8 / Systems (L291-292); 05-expressions.md per-entity flush idiom (L617)examples/valenar/Generated/Systems/Economy/ResourceProductionSystem.cs:34-42Load distribution at map-generation scaleNo Frequency property declared — uses interface default of 1
Canonicalise TemplateEntry.Methods dictionary-init syntax — Buildings use brace-brace ({ { H.X, X } }), Features use indexer ([H.X] = X). Compiler must pick one.audit-findings.md § 8 / Templates/Buildings (L257), 02-templates.md § "Lifecycle methods" (L493-494)Generated/ output uniformity (compiler emission rule)Diff cleanliness post-compiler bring-upBoth forms compile today
Emit generated C# value types and SecsTypeDeclaration[] from the future compiler for structs, records, enums, arrays, and chosen C# collection-shaped values07-structured-template-data-and-callables.md § "C# Source Types"Compiler structural prepass + Generated/ stand-insPhase 1 structural prepass; typed fields/callablesRuntime SecsTypeRef/type registry support landed 2026-05-02; compiler emission is still pending
Implement structured template-field storage/accessors — scalar fast accessors exist; structured immutable accessors and exact-type value validation still need generated/runtime support07-structured-template-data-and-callables.md § "Structured template fields"; 01-world-shape.md § "field declarations"src/SECS.Engine/Instances/TemplateEntry.cs, generated template classes, TemplateValueResolverFeaturePlacementProfile and other structured designer dataTemplateFieldDeclaration.ValueType = SecsTypeRef landed 2026-05-02; additive/multiplicative runtime effects remain numeric-scalar only
Implement registry_only end-to-end instead of documenting it as shipped00-overview.md § registry_only, 08-collections-and-propagation.md § Summary of new primitivessrc/SECS.Abstractions/Contracts/ContractDeclaration.cs, src/SECS.Engine/SecsRegistry.cs, TemplateActivator, entity creation helpers, generated declarations/testsRecipe/metadata-only template contractsRequired shape: ContractDeclaration.IsRegistryOnly, registry validation, activation skip, create_entity rejection, generated output, diagnostics/tests, and docs. Until all land, registry_only remains specified/future/current gap.
Add/confirm a typed TickContext.PrevTick(...) helper or document the existing bridge read as the sole committed API08-collections-and-propagation.md § Part 4, SECS-Compiler-Plan.md § Phase 4src/SECS.Engine/Pipeline/TickContext.cs, prev-tick snapshot storeAnalyzer target for SECS0830; source examples using bridge/runtime call patternDo not introduce standalone prev_tick(...) source syntax.

1.1 Behavior layer — deferred policy infrastructure

Wave 3 of the behavior refactor shipped PolicyDispatcher plus the no-fallback slot model. The remaining policy plumbing is split across the next behavior waves and tracked here so it does not get lost.

TaskSource doc §Engine file:lineBlocksNotes
Per-actor active policy stack (Wave 6) — currently the host invokes PolicyDispatcher.Tick(policy, actor, tick) directly with hardcoded policy ids; Wave 6 introduces the attach_policy event hook and the per-actor active-policy list described in 09-ai-policies-and-activities.md § Attaching a policy to an actor.04-behavior.md § Open questions / Policy attachment, 09-ai-policies-and-activities.md § Attaching a policy to an actorsrc/SECS.Engine/Policies/PolicyDispatcher.cs, host bootstrap that currently calls Tick directlyattach_policy(...) runtime semantics; player-edit panels that need to know which policies are activeToday the dispatcher tracks at most one child run per (actor, policyId) pair; the Wave 6 stack will replace the hardcoded host invocation with an event-driven attach/detach surface.
Multi-domain arbitration with priority rules — when multiple policies in different domains both want to dispatch call_best, the host orchestrates today. A future wave introduces a per-actor priority stack so the engine arbitrates deterministically.04-behavior.md § Open questions / Concurrency across domainsNew per-actor policy-priority structure (TBD)Multi-policy decision determinism across domainsListed as an open question on doc 04 prior to this wave; promoted here to a tracked row.
Activity catalog snapshot wrapper type — Wave 8 deferred a typed ActivityCatalogSnapshot that mirrors AllActivityDeclarations() with player-facing display fields baked in. Hosts can build the catalog from AllActivityDeclarations() today (the projection is straightforward); a typed wrapper would let SecsRegistry cache the projection across mod-finalize boundaries.04-behavior.md § Activity request and candidatesrc/SECS.Engine/SecsRegistry.cs, src/SECS.Engine/Activities/Catalog projection caching across save/load and re-finalizeRaw AllActivityDeclarations() is sufficient for Wave 8; deferred until catalog allocation pressure justifies the abstraction.
ActivityRunStore.GetByLane(actor, lane) filter — currently hosts iterate RunStore.ActiveRuns and filter manually when they need lane-specific lookups beyond TryGetActive. A typed GetByLane would short-circuit the iteration.04-behavior.md § Save shapesrc/SECS.Engine/Activities/ActivityRunStore.csLane-scoped lookups on actors with many concurrent runsFiltering via ActiveRuns is sufficient for Wave 8; TryGetActive covers the single-active-per-lane case.

1.2 Tag fixture backlog

The tag contract is committed and runtime-supported. Current committed Valenar executable tag provenance covers template tags only; this backlog does not claim current Valenar source/generated fixtures for activity tags, CharacterSurvival.FoodSelector, or policy from tags selectors. Those fixtures belong to a scoped tag-fixture wave that lands source, Generated stand-ins, and tests together. The rows below are deferred fixture gaps only; do not invent Valenar gameplay to close them.

TaskSource doc §Engine file:lineBlocksNotes
Add a live Valenar activity tags = ... source/generated/test fixture only when a real activity family needs a tag shared by selector or UI behavior04-behavior.md § Selectors and SelectorSource, SECS-Compiler-Plan.md § Phase 2 tag bindingRuntime support exists in src/SECS.Abstractions/Activities/SecsActivity.cs and src/SECS.Engine/SecsRegistry.csCompiler-readiness provenance for activity tag loweringMust be owned by the tag-fixture wave; Wave D mod-finalization docs must not rely on staged or unmerged Valenar fixture edits
Add a live Valenar policy consider activities from tags = ... source/generated/test fixture only when it selects over real tagged activities04-behavior.md § Selectors and SelectorSource, 09-ai-policies-and-activities.md § Utility AI selectorRuntime support exists in src/SECS.Engine/Policies/PolicyExecutor.cs via SelectorSource.FromTagsCompiler-readiness provenance for policy selector tag loweringMust land with or after the activity-tag fixture; do not invent a selector or tag solely to satisfy provenance
Add a live Valenar propagates_to children where has_tag ... source/generated fixture only when a real modifier needs tag-filtered child propagation08-collections-and-propagation.md § Part 2, SECS-Compiler-Plan.md § Phase 2 tag bindingRuntime support exists in src/SECS.Abstractions/Modifiers/PropagationFilter.cs, src/SECS.Engine/StructuralPredicate.cs, and src/SECS.Engine/PropagationDispatcher.csCompiler-readiness provenance for modifier propagation has_tagGeneric engine tests already cover PropagationFilter.ForTag; no current Valenar mechanic needs this source shape
Add a live Valenar Children.Where(has_tag ...).Sum(...) aggregate source/generated fixture only when a real aggregate channel needs tag-filtered children08-collections-and-propagation.md § Part 1, SECS-Compiler-Plan.md § Phase 4 aggregate channelsRuntime support exists in src/SECS.Abstractions/Channels/AggregateChannelSource.cs and src/SECS.Engine/Resolution/ChannelResolution.csCompiler-readiness provenance for aggregate .Where(has_tag ...)Generic engine tests already cover aggregate tag predicates; no current Valenar aggregate can be changed without inventing behavior

1.3 Save/load durability backlog

The runtime currently has partial store-level primitives only: ScopeSlotStore.Snapshot/Restore, ActivityRun.ToState, and ActivityRunStore.Restore. This subsection tracks the future unified payload and the gaps that must close before SECS can claim a complete durable save/load contract.

TaskSource doc §Engine file:lineBlocksNotes
Design and implement a future unified SecsSavePayload / SaveSnapshot / RestoreSnapshot boundary without taking over the host's byte format or world container.10-host-secs-execution-boundary.md § 20.2Future save/load surface under src/SECS.Engine/SaveLoad/One coherent SECS runtime payload for activity runs, slots, modifier bindings, pending choices, and policy childrenCurrent committed APIs remain the partial store-level primitives; do not treat the future payload record as shipped.
Add ModifierBindingStore persistence for real source bindings, including stable creation-order / source-binding metadata so virtual bindings can be reconstructed as derived runtime state.03-channels-and-modifiers.md § Modifier propagation, 08-collections-and-propagation.md § Save/load: virtual bindings are derived state, 10-host-secs-execution-boundary.md § 20.1src/SECS.Engine/ModifierBindingStore.cs, src/SECS.Engine/PropagationDispatcher.csDurable modifier effects, propagation reload, HardOverride order determinismVirtual bindings themselves stay out of the payload. Restore must rebuild them from restored real bindings and current children.
Define the pending-choice key protocol before persisting player-choice events.04-behavior.md § Player-choice events, 10-host-secs-execution-boundary.md § 19.5 and § 20.3src/SECS.Engine/Events/EventOption.cs, src/SECS.Engine/Events/EventDispatcher.cs future changesSafe save/load across event-option reorder/add/remove patchesCurrent pending choices are UI-projected only and resolved by array index; GameSnapshotBuilder DTOs are not durable deserialization.
Persist PolicyDispatcher.activeChildren after activity runs restore.09-ai-policies-and-activities.md § Save / load and migration, 10-host-secs-execution-boundary.md § 20.1src/SECS.Engine/Policies/PolicyDispatcher.cs future snapshot/restore surfacecancel_child, wait/resume, and parent-policy bookkeeping across loadCurrent dictionary is in-memory only; future records must validate referenced ActivityRunIds instead of fabricating missing runs.
Design a no-lifecycle InstanceStore rehydrate path.10-host-secs-execution-boundary.md § 11.1 A8, § 20.1, § 20.2src/SECS.Engine/Instances/InstanceStore.cs, src/SECS.Engine/TemplateActivator.csDurable entity restore without replaying activation/lifecycle side effectsCurrent TemplateActivator.Create / Activate fires activation behavior and is not a valid restore primitive for already-created entities.
Build Valenar durable world save/load beyond GameTimeBank.10-host-secs-execution-boundary.md § 3.1 Host-owned, examples/valenar/Server/Services/GameTimeBank.csexamples/valenar/Host/GameWorld.cs, examples/valenar/Server/Services/GameSessionStore.csEnd-to-end playable saves that combine host world state with SECS runtime recordsGameSnapshotBuilder and read-model DTOs are UI projections, not durable save deserialization. GameTimeBank persists elapsed/offline time only.

2. Compiler diagnostics to implement

Diagnostics specified in the design docs but not yet wired into the (future) SECS compiler. Codes follow the SECS<doc><code> convention — first two digits = home doc number.

CodeWhat it enforcesDoc sourceBlocks
SECS0101kind = clause missing on top-level channel declaration01-world-shape.md § "ChannelKind decision table" L696Phase 1 — every channel must state its kind explicitly
SECS0104Unknown channel identifier in resolve(X) or template-body channel X usage (channel not declared in top-level Channels[])01-world-shape.md § "Channel with no source"; 02-templates.md § "Historical stand-in bugs under the current channel model"Catches future undeclared-channel drift; Valenar feature channels are now declared in locations/features/channels.secs
SECS0105kind = Base without source = clause01-world-shape.md § "ChannelKind" L697Use kind = Contributed for channels with no host field
SECS0106kind = Accumulative without source = clause01-world-shape.md § "ChannelKind" L698Accumulative requires host-owned storage field
SECS0107Two identifiers collide under case-insensitive FNV-1a-64 lowering01-world-shape.md § "Case-collision rule" L699, L765-770Rename one of the colliding identifiers
SECS0108 (warning)Cross-assembly redeclaration of an identifier without an inject / replace / try / or_create mod operation01-world-shape.md § "Cross-assembly same-name rule" L700, L772-774; current operation model in 06-overrides-and-modding.md § 3Steer mod authors toward an explicit mod operation when intentional
SECS0110Clamp literal type does not match channel type (or bool channel has min/max)01-world-shape.md § "Clamp literal types" L701, L620-672Surfaces declaration-type drift
SECS0111WalkScope(from, to) / to.X targets a scope not declared in from's walks_to list01-world-shape.md § "Scope-walk correctness" L702, L222-291Catches undeclared host-walk bug class
SECS0201Template-body channel declaration on a scope that differs from the template's root_scope02-templates.md § "Per-instance flat channels" / "Per-instance derived channels" L167, L220Enforces the own-root intrinsic channel-source contract
SECS0301Modifier effect on bool channel uses += or *= instead of = (HardOverride)03-channels-and-modifiers.md § "bool-channel effect-mode rule" L240-268, L1243Hard compile error for bool-channel modifiers
SECS0302Dynamic resolve(<expr>) used without an explicit reads { } block, or reads { } missing a channel the body reads03-channels-and-modifiers.md § "reads { } — D-Hybrid rule" L878D-Hybrid case 2 (dynamic)
SECS0303 (warning)Explicit reads { } declares channels not auto-extracted (over-approximation warning)03-channels-and-modifiers.md § "reads { } — D-Hybrid rule" L877Keeps author intent and auto-extraction in sync
SECS0601Hash collision during mod-operation merge — two distinct identifier strings hashing to the same FNV-1a value06-overrides-and-modding.md § "Identity scheme" L149, § "The merge-pass pipeline" L591Phase 3 merger must abort, not warn
SECS0602Duplicate declaration in mod without an inject / replace / try / or_create operation (collides with existing registry entry)06-overrides-and-modding.md § "The merge-pass pipeline" L593Phase 3 merger aborts on ambiguous redeclaration
SECS0701Unknown type name in a field, callable signature, struct/record field, array element, collection generic argument, or template/scope reference07-structured-template-data-and-callables.md § "Diagnostics"C# source type binding
SECS0702Value initializer does not match the declared SecsTypeRef07-structured-template-data-and-callables.md § "Diagnostics"Structured template field validation
SECS0703method declaration has a non-void return type; use query for read-only typed returns07-structured-template-data-and-callables.md § "Diagnostics"Preserves query/method read/write split
SECS0704Query body performs a command-producing operation07-structured-template-data-and-callables.md § "Diagnostics"Keeps typed query returns read-only
SECS0705Callable body replacement changes parameter or return SecsTypeRef07-structured-template-data-and-callables.md § "Diagnostics"; 06-overrides-and-modding.md § "What's NOT overridable"Signature-stable body replacement
SECS0706Runtime template-field modifier attempts additive/multiplicative effect on a non-numeric structured field07-structured-template-data-and-callables.md § "Diagnostics"Structured field effect discipline
SECS0032Source keyword whitelist/analyzer guard rejects uncommitted SECS keywords (formula top-level declarations, old override, top-level tag, top-level phase/tick_rate, arbitrary direct fire EventName) while permitting ordinary C# collected for metadataSECS-Compiler-Plan.md § Source keyword whitelist and grammar status, docs/design/README.mdPrevents stale docs/fixtures from silently expanding source syntax
SECS0803Command-producing call appears in a read-only body such as event Condition, activity IsVisible, or policy rule decision evaluation10-host-secs-execution-boundary.md § 13.3Analyzer/binder target for the host execution-boundary taxonomy
SECS0804Generated code reaches ISecsHostCommands without a command-producing context10-host-secs-execution-boundary.md § 13.3Analyzer/binder target for command-surface misuse outside command-producing bodies
SECS0830Prev-tick bridge/API read such as ctx.PrevTick(S) targets channel S without track_prev = true08-collections-and-propagation.md § Part 4, SECS-Compiler-Plan.md § Diagnostic Code CatalogAnalyzer/binder target once helper/API shape is finalized

Note on code convention (resolved 2026-04-24): Earlier drafts of doc 06 reused SECS0301 / SECS0302, colliding with doc 03's HardOverride and D-Hybrid codes. Per the XX = doc number convention in 00-overview.md § "Diagnostic code convention", doc 06's merger codes are now SECS0601 / SECS0602. The FUTURE_WORK sweep caught the drift; fix landed the same day.

3. Valenar stand-in bugs

Backlog only — these files must not be edited until the compiler is ready to emit correct output. Each entry cites file:line, what's wrong, the fix shape, and whether anything blocks remediation beyond compiler bring-up.

3.1 Cross-scope template-body channel bugs — resolved 2026-04-26

The own-root channel-source violations where location-rooted Building templates directly declared settlement channels have been rewritten in both .secs source and hand-written Generated/ output. Buildings now attach always-on modifiers to settlement in OnBuilt:

  • House, Great Hall, Temple, Training Grounds, Walls, Watchtower, Forge, Barracks, Housing, and Tavern now use named modifier declarations such as HouseMoralePresence, WatchtowerDefense, and BarracksGarrison.
  • Dynamic settlement effects (HousePopulationCapStack, WatchtowerDefense) are modifier-effect formulas, so their formula output side is discovered from ModifierDeclaration.Effects instead of RegisterFormulaContribution(...).
  • Farm/IrrigatedFarm/TerracedFarm remain template dynamic channels because FoodOutput is a location channel and their template root_scope is Location.

Storehouse's old channel int Food = 100/250/500 rows were removed instead of being converted to modifiers. Food is Accumulative, and modifiers intentionally do not apply to accumulative stockpile channels. The generic resource model is now documented in 01-world-shape.md § "Resource amount, capacity, and flow" and 03-channels-and-modifiers.md § "The 6-phase resolution pipeline": Storehouse-style storage targets a separate capacity channel (FoodCapacity, GoldCapacity, etc.), while systems clamp amount writes against resolved capacity.

Valenar status: Implemented for food storage on 2026-04-26. Valenar now has settlement.FoodCapacity, channel int FoodCapacity { kind = Contributed; ... }, Storehouse capacity modifiers, and a production-system clamp against resolved food capacity. Additional resources can follow the same amount/capacity pair only when that resource actually needs storage gameplay.

3.2 Modifier lifecycle leaks — resolved 2026-04-26

Source: audit-findings.md § 2 Lifecycle pairing L84-124. The original audit correctly spotted that permanent modifier bindings needed a lifecycle story. The committed design is owner/target cleanup, not required source-level attach/detach pairs:

  • owner is the lifetime source.
  • target is the effect anchor.
  • TemplateActivator.Destroy runs the contract's deactivation lifecycle binding, then queues RemoveAllChannelSources(owner), RemoveAllBindings(owner), and RemoveAllBindingsOnTarget(owner).

The 12-template "missing OnDestroyed" table is now historical. A template that attaches settlement.add_modifier HouseMoralePresence in OnBuilt does not leak because the building instance owns the binding. remove_modifier is reserved for explicit early/custom removal, but it no longer blocks ordinary template lifetime cleanup.

3.3 Undeclared scope walk — resolved 2026-04-26

location -> settlement is now a declared Valenar edge (walks_to Settlement; on scope Location) and generated scope declarations use ParentScopeIds. Farm.CanBuild now returns false for an unowned location instead of using a permissive fallback.

3.4 Missing frequency in ResourceProductionSystem

Source: audit-findings.md § 8 Systems L291-292. Runs every tick at O(settlements × locations); no Frequency declared, uses AllByContract (no distribution). File: examples/valenar/Generated/Systems/Economy/ResourceProductionSystem.cs:34-42.

Also the corpus's only ctx.Host.ReadInt call from a system (:55). Decide whether direct host reads are idiomatic for ownership-by-field scopes, or whether this should be factored through host.WalkScope.

3.5 Lower-severity audit findings (6 active items)

Sourced from audit-findings.md. Each is cosmetic or latent; none blocks compiler bring-up.

  • GreatHall.cs:30GetField => 0; switch-free implementation; every other GetField is a switch expression (audit § 8 L255)
  • 12 Tier-variants never declare a CanBuild query — intent is "tier transition bypasses the BuildingCount query" but undocumented (audit § 8 L256)
  • Dictionary-init syntax inconsistent — Buildings use brace-brace, Features use indexer (audit § 8 L257) — also tracked in Engine Task 19
  • CelebrationBonusEvent.cs — omits Condition override (relies on SecsEvent base default true); every other Valenar event with a real gate declares it (audit § 8 L302)
  • Trigger_MoraleGte60.cs:10-11 — only trigger that branches on captured0.IsNull; silent mode switch (audit § 8 L328)
  • FamineStartsEvent.cs:29 — only event that explicitly sets Priority = 0 (audit § 8 L303)

3.6 Sync-drift between .secs sources and Generated/ output

  • Hashes.cs hand-picked sentinels — resolved 2026-04-25. The generated stand-ins now use FNV-1a-64 ulong constants instead of hand-picked 32-bit sentinels.
  • MainCharacter.cs:15 carries Name = "Hero" — violates the narrative rule that MC must not be called "Hero" (audit § 8 Characters, L282; user memory feedback_valenar_genre). Content bug, not engine bug

3.7 Deferred spell-authoring seam

Source: examples/valenar/docs/implementation/pr-docs-codegen-rules.md § Live Behavior Vocabulary.

SpellRoot remains an unresolved Valenar/example design choice, and KnownSpellsCandidateBuilder remains an internal runtime/lowering bridge. Revisit both only when a committed future spell-authoring model exists. Until then, neither should be generalized into live .secs syntax or treated as the settled long-term shape.

4. Deferred language features

Features explicitly marked not-in-Phase-1 across the design docs. Each entry lists the feature, source, rationale for deferral, and criteria for revisiting.

  • range = 0..100 clamp sugar01-world-shape.md § "range = sugar status" L674-679. Sugar over min / max; core syntax is committed complete form. Deferred indefinitely; no keyword reserved. Reconsider if author surveys show the two-line form is consistently clunky. Desugars at parse time — purely additive.

  • <= N clamp operator in modifier effects03-channels-and-modifiers.md § "Effect modes" L172. Compiler rejects. Use channel-declaration min / max instead. Reconsider when a use case demands a modifier-scoped upper cap distinct from the channel-global cap.

  • priority N on modifier declarations03-channels-and-modifiers.md § "Effect modes" L173. Historical proposal retained as a backlog label; the field is reserved but not implemented. Reconsider when HardOverride ordering or stacking-tie-breaker determinism surfaces as a real author need. SECS03XX code to be assigned when it lands.

  • decimal channel type01-world-shape.md § "Numeric type set" L393. Niche (accounting-grade 28-digit fixed-point). Add as a sixth SecsScalarType entry when a real use case emerges. Single-enum-entry addition; no restructuring.

  • BigInt arbitrary-precision channel type01-world-shape.md L394. Out of scope for Phase 1 and Phase 2 — breaks the O(1) fast-path model and the value-union discriminant. Reconsider only if a shipped game needs it; multi- month project.

  • uint / ulong / byte / short channel types01-world-shape.md § "Explicitly rejected" L391-392. Rejected, not deferred. uint/ulong overlap with clamped signed types (channel int X { min = 0; }). byte/short are premature optimisation. Listed here only to record the rejection.

  • upgrade block in templates02-templates.md § "upgrade block (NOT YET IMPLEMENTED)" L694-727. Engine already supports TemplateActivator.Upgrade as C# API; .secs surface syntax and compiler lowering are missing. Reconsider after Farm / IrrigatedFarm / TerracedFarm tier content is settled. Open shape questions: cost-per-resource vs cost-expression, condition expression vs precondition block, TemplateEntry field vs standalone registration.

  • save_temporary_scope_as (statement-scoped vs chain-scoped)05-expressions.md § "save_scope_as" L505-506. No runtime plumbing yet; the current runtime only supports chain-scoped saved scopes. Reconsider when nested on-action chains need per-statement scope cleanup.

  • every_in_list / random_in_list query keywords — historical saved-scope-list query proposals. Scope-list storage would need a separate Dictionary<ulong, List<EntityHandle>> alongside the flat scopes dict. Reconsider when a content pattern demands lists of saved scopes.

  • Interprocedural analysis for formula helpers03-channels-and-modifiers.md § "reads { } — D-Hybrid rule" open question L962. A formula that calls a helper method currently treats the helper's resolve() calls as dynamic (requiring explicit reads { }). Future compiler pass could inline helper bodies or build per-helper read-set summaries. D-Hybrid's "dynamic → explicit" branch covers the interim. Upgrade-compatible — extras in reads { } stay legal once helpers are analysed.

  • Singleton constraint on templates — historical proposal retained as a backlog label. Not supported in Phase 1. Reconsider if content needs a compile-time single- instance guarantee.

  • Inline modifier declaration in method bodies — historical proposal retained as a backlog label. Not supported. Use template-level modifier members.

  • expects channel on contracts — historical proposal retained as a backlog label. Contract-level channel-presence declarations. Not supported.

  • Aggregate channel declarations (scope = aggregate) — historical proposal retained as a backlog label. Not supported; content uses dynamic methods with foreach.

  • resolve() call-site restrictions — historical proposal retained as a backlog label. Not enforced; revisit when dependency issues arise.

  • Compile-time channel-cycle detection (strong) — historical proposal retained as a backlog label. Runtime re-entry check is mandatory; compile-time is advisory-warning-only. D-Hybrid readsChannels metadata is the input a stronger compile-time check could use.

  • Dependency-aware channel caching — historical proposal retained as a backlog label. Dynamic sources re-evaluate eagerly; no caching. Host may add dirty tracking on scope-field mutations as an optimisation.

  • distribute false opt-out — see Engine Task 16 above. .secs syntax and runtime handling both missing. Current live docs do not commit an opt-out surface yet.

  • Contract inheritance / mixins (extends)01-world-shape.md § "contract declarations" L355. Not present in .secs or Generated/. If introduced must flatten at lowering time.

  • Event option description localization04-behavior.md § "Player-choice events" L543. Strings hand-written in Generated/; the live docs still need a committed localization contract for option descriptions. Option display names are now explicit source strings passed to options.Add("Name", Handler); descriptions still need either derived localization keys or a method-based metadata surface.

  • Event option timeout / auto-pick04-behavior.md § "Player-choice events" L544. Not specified.

  • Chained-on-action source syntax04-behavior.md § "End-to-end: OnActionDeclarations.cs". Runtime fields exist, but no committed .secs source syntax exists. The current runtime stores these fields as inert metadata only; dispatcher execution and registry link validation remain disabled until the syntax and semantics are deliberately recommitted. If this lands, it must preserve metadata-only on-actions and avoid reintroducing on-action effect bodies.

  • Fallback on-action source syntax04-behavior.md. Runtime field exists, but no committed .secs source syntax exists. The current runtime stores it as inert metadata only; dispatcher execution and registry link validation remain disabled until the syntax and semantics are deliberately recommitted. Same metadata-only constraint as chained on-actions.

  • Old on_action effect body migration — resolved for source syntax and generated stand-ins by moving guaranteed behavior into normal subscriber events with early priority (for example BuildingCompleteMorale). Runtime OnActionDeclaration no longer has an Effect delegate; fallback/chaining fields remain reserved inert metadata with no committed source syntax.

  • .secs cost X N; authoring syntax — the runtime activity_costs slot is committed and live (SecsActivity.Costs, ActivityExecutor.CanPayCosts/DeductCosts, ActivityModPatch.Costs). The .secs source syntax for declaring costs in an activity block (e.g. cost stamina 1;) is reserved for a future content-source wave; today, costs are authored by setting Costs on the generated activity class directly. See 04-behavior.md § "activity Name". The runtime side already supports full-list replacement at registration; only the source-syntax parser and lowering remain to be built. Phase 3 mod parsing must accept the full-list-replacement merge rule for the activity_costs slot when the source syntax lands.

  • Generic non-activity/policy declaration replacement in Phase 306-overrides-and-modding.md § 8 and docs/adr/ad-0001-runtime-mod-finalization-boundary.md. The shipped runtime finalizer handles activity and policy mods only. The remaining source-level merger work is compiler-owned: parse source sets, extract semantic slots for templates/modifiers/systems/events/on-actions and other non-activity/policy declarations, apply load-order writes, and lower the resulting merged declaration set. Do not implement this as a generic SecsRegistry / ModRegistry runtime merger.

  • Compiler lowering for scope methods01-world-shape.md § "Methods declared on scopes", 04-behavior.md § "Targeted actions", and 05-expressions.md § "Host-exposed scope methods". Runtime/generated C# now has the ISecsHostCommands bridge plus generated typed scope command and query facades. Compiler work still needs parsing, declaration emission, type-checking, and lowering for read-only and command-producing scope method calls; known generated call sites should use concrete C# source types, with SecsValue reserved for command payload storage or explicitly erased dynamic/tooling adapters.

  • Raw C# helper replacement surface declared inside .secs06-overrides-and-modding.md § 10 leaves namespace-scoped raw C# helper replacement unsettled. Phase 3 commits only to SECS mod operations (inject, replace, try inject, try replace, inject_or_create, replace_or_create) and namespace isolation for ordinary helper C#.

  • Static → dynamic channel conversions through mod operations06-overrides-and-modding.md § "Overriding dynamic channels" L287. Base has channel int X = 5; a later inject / replace body has channel int X { return ...; }. Requires synthesizing / deleting Formula_*.cs. Marked "Phase 3 stretch".

  • --strict-conflicts CLI flag06-overrides-and-modding.md § "Conflict detection" L560. CI-only; fail build on mod-operation slot conflict. Not Phase 3.

  • scope:X compile-time validation against on-action provides04- behavior.md § "provides scope:X" L765-766. Compiler should check every subscriber event's saved-scope reads match the on-action's provides list. Unchecked today.

  • fire on_action call-site provides-coverage check04-behavior.md L766. Every firing site must save the scopes declared by the on-action's provides list. Runtime dispatch now throws when a declared provided-scope key is absent, but the compiler diagnostic is still required.

4a. Deferred doc hygiene

Cosmetic / labeling cleanup that does not block any commit, scheduled for future doc-hygiene waves. Each row cites the originating wave and lists the docs requiring follow-up.

TaskAffected docsNotes
Per-doc Valenar-illustrative labeling cleanupdocs/design/01-world-shape.md, 04-behavior.md, 05-expressions.md, 08-collections-and-propagation.md, 09-ai-policies-and-activities.mdPer § 7 of docs/design/10-host-secs-execution-boundary.md (Wave B1 added the convention; one caveat added to 01-world-shape.md:5). Full retrofit of per-section "(Valenar example)" caveats is non-blocking and cosmetic; deferred. (OQ-6 routing — see docs/design/10-host-secs-execution-boundary.md § 22.5.)

5. Design topics still to discuss

Open design questions that are not yet committed. Cluster E and post-cluster topics, system and contract shape decisions, and compiler-internal concerns.

5.1 Modifier lifecycle pattern — resolved owner cleanup

Status: Resolved 2026-04-26; retained here until the next backlog re-index.

Trigger: 12-template leak audit finding (§ 3.2). The finding forced a proper lifecycle decision for permanent modifier bindings created from template lifecycle methods.

Commitment: Owner/target meaning is fixed in 05-expressions.md § "add_modifier": owner is the lifetime source and target is the effect anchor. Every persistent binding has a non-null owner; global effects use a real World / Realm / law entity rather than a magic null owner. Template destruction performs owner/target cleanup in the engine: RemoveAllChannelSources(owner), RemoveAllBindings(owner), and RemoveAllBindingsOnTarget(owner).

Result: Source authors attach permanent template-owned modifiers once, usually in OnBuilt / OnSpawned / OnDiscovered. They do not write paired OnDestroyed removal just to prevent leaks. remove_modifier remains useful for early/custom removal, but not for ordinary template lifetime cleanup.

Mod impact: This is the most mod-friendly option from the old ranking: modifying the attach site cannot accidentally omit the hidden cleanup path because cleanup is keyed by binding owner, not by a second source method the mod author must remember to update.

5.2 ChannelKind.Internal — fourth ChannelKind for compiler-internal channels

Trigger: 01-world-shape.md § "Channel with no source" L618 — "Internal ChannelKind for purely compiler-internal channels remains a potential future addition (deferred — no current use case in Valenar)."

Question: Should the compiler be able to declare channels that are not author- facing (intermediate calculations, profiler/debug values, engine-internal caches)?

Referenced: 01-world-shape.md § "Lowering summary" L859 — "Internal flag is not usable from .secs yet."

Scope: Small. Add the enum entry, specify that authors cannot use it directly, specify the host / compiler-side surface that does.

5.3 Channel-array ordering in Declarations.Channels

Trigger: Cluster E (carried forward). Not explicitly cited in the committed design docs but flagged for decision in the task prompt.

Question: Should ChannelDeclaration[] Channels be ordered alphabetically, in declaration order (.secs source order), or grouped by ChannelKind?

Scope: Affects diff cleanliness and cache locality at registration time. Pick one at compiler bring-up.

5.4 System declaration topic — frequency required / AllByContractForTick default

Trigger: audit-findings.md § 3 summary finding L396 — ResourceProductionSystem.cs ships without a Frequency declaration, runs O(settlements × locations) every tick. Every other system uses AllByContractForTick.

Question:

  1. Should frequency be required on every system { ... } declaration?
  2. Should foreach <entity> in <Contract> in a system body auto-distribute by default (use AllByContractForTick)?

Referenced: 04-behavior.md § "Frequency gating" L180 open question on arbitrary cadence values, with current source shape frequency = <TickRate expression>; per 08-collections-and-propagation.md § Part 5.

Scope: Language-level default. Affects every system in Valenar. Commitment.

Mod-friendliness weighting: Mods load untrusted system code. Defaults that fail-safe under mod use (required frequency, auto-distribute on) should weight higher than defaults that fail-noisy only for base. A careless mod-added system without frequency could flood the tick loop; a mod-added foreach without distribution could spike per-tick cost to O(entities). The commitment should treat untrusted-mod safety as a first-class dimension alongside base-author ergonomics. Cross-link mod-coverage Finding 64.

5.5 Contract-declared methods with zero implementers

Trigger: audit-findings.md § 2 Lifecycle pairing L112-124. Eight methods are declared on contracts but never implemented by any template: OnDestroyed, OnDayTick, OnSeasonTick, OnCreated, OnLoaded, OnClaimed, OnDesignated, OnInteract.

Question: Either the host never invokes these methods (in which case the contract declarations are lies), or it does and every target silently no-ops (in which case the compiler should warn on zero-implementer contract methods).

Scope: Likely a SECS02XX warning diagnostic plus a sweep of the Valenar contracts to prune unused method names.

2026-04-29 design refinement: Contract method names are game/developer vocabulary. Lifecycle slot ids are engine vocabulary. activation OnBuilt; means "invoke the method bound to ContractLifecycleIds.Activation"; it does not make OnBuilt a reserved engine hook. The same contract mechanism should support activation OnEquipped;, activation OnCrafted;, or any other declared void method in another game.

2026-04-29 implementation: Activation and deactivation now both follow this binding shape. TemplateEntry.Deactivate was removed; destruction resolves ContractLifecycleIds.Deactivation -> <method id> and invokes the method through TemplateEntry.Methods, so every lifecycle slot uses one generic method-dispatch path.

2026-05-01 implementation: Contract callable signature metadata now exists as ContractMethodDeclaration[]. Registration validates that query rows are listed in ContractDeclaration.QueryMethodIds and command rows are listed in ContractDeclaration.MethodIds; this resolves compiler-facing signature drift. The zero-implementer warning remains open.

5.6 CommandBufferExtensions follow-up surface

Trigger: audit-findings.md § 9 Unused engine APIs L332-342. Seven CommandBufferExtensions methods had zero call sites in Generated/ at the time of the audit. The scalar channel-source helper family is now runtime-complete (RegisterChannelSourceFloat, RegisterChannelSourceLong, RegisterChannelSourceDouble, RegisterChannelSourceBool) even though current Valenar content only exercises int sources. Removal helpers remain compiler targets for source keywords and runtime lifecycle plumbing.

Question: Are these compiler-phase-later targets (required once the remove_modifiers_by_tag keywords land) or dead engine surface to cull?

Scope: Audit each. RemoveAllBindings is now used by template destruction; RemoveModifier is compiler-required for remove_modifier and explicit early/custom removal. RemoveModifiersByTag* tie to a deferred batch-removal source surface. The typed RegisterChannelSource* helpers are part of the 5-type scalar surface and are kept even without content-side consumers. The others need a decision.

5.7 root / prev scope-stack capture semantics

Trigger: 05-expressions.md § "Built-in sigils" L66-67, 03-channels-and- modifiers.md § "Trigger lowering" L871, plus the still-open runtime gap for captured outer-scope entities "[NOT YET IMPLEMENTED]". All current Trigger_*.Evaluate signatures accept captured0 / captured1, but every add_modifier call in Generated/ passes EntityHandle.Null.

Question: What's the canonical shape for populating the captured slots? Compiler detects root / prev references in the trigger body and emits the corresponding captured0: / captured1: arguments at the add_modifier call site. The concrete rule for which enclosing foreach iteration variables land in which slot needs spec-level commitment.

Referenced: Trigger_MoraleGte60.cs:9-11 uses the captured0.IsNull ? target : captured0 fallback — silent mode switch that audit § 8 L328 flags as a bug class.

Scope: Depth-aware rule. At depth 1 prev == root; deeper nesting needs clearer semantics. Archive docs describe the goal; compiler and engine both need the rule spelled out before implementation.

5.8 Multi-scope capture for deeper scope expressions

Trigger: 03-channels-and-modifiers.md § "Trigger lowering" open question L871. The two captured slots cover root + prev at depth 1. Expressions like root.owner.HasLaw("foo") need a third or fourth entity reference.

Question: Fixed-size captured slots (expand to Captured0/1/2/3) or variable-length array? The first option preserves the struct-based binding layout; the second complicates ModifierBinding but scales further.

Scope: Engine-side struct layout change. Defer until a concrete .secs body exercises the need.

5.9 provides scope:X type discipline

Trigger: 04-behavior.md § "provides scope:X" L765-766. ProvidedScopes today is Dictionary<ulong, ulong> mapping scope-name-hash → scope-type-hash, but the runtime does not interpret the type for non-handle payloads (e.g., scope:WasVictory uses .Value == 1 encoding).

Question: Does the language want a first-class bool/int-valued saved scope, or should EntityHandle.Value continue doubling as a flag carrier?

Scope: Type system decision. Affects SaveScope / GetSavedScope API shape and every scope:X == literal condition lowering.

5.10 Formula keyword/source form clarification

Trigger: None explicit, but 03-channels-and-modifiers.md § "Formula lowering" L756 shows formulas come from dynamic template channels or dynamic modifier effects. Worth confirming.

Question: Can a .secs file declare a top-level formula independently of any template or modifier (e.g., a shared calculation referenced from two places)? Current Wave L contract says no: formula is not a committed top-level source keyword, and every formula delegate is lifted from a specific dynamic template channel or dynamic modifier effect body.

Scope: Likely no — the Formula_{TemplateName}{ChannelName} naming convention in 02-templates.md L292 treats formulas as derivative. But worth deciding explicitly.

5.11 Template Name-vs-identifier rule for identity-only templates

Trigger: 02-templates.md § "Identity-only templates" L793 — can name accept an expression? Currently rejected. What about other metadata fields (icon, description, category)?

Question: Fixed set of metadata init fields on TemplateEntry, or a string/string dictionary for forward compatibility?

Scope: Small. Each new metadata field adds one init field.

5.12 Collection type inference (of <ScopeType>)

Trigger: 01-world-shape.md § "collection" L151-152. .secs sources elide of <ScopeType>; compiler could require it (typed CollectionDeclaration row) or continue to infer. Bidirectional consistency pass between Settlement.collection Provinces and province.walks_to Area is unchecked.

Scope: Parse-time decision. Affects Declarations.cs — whether CollectionDeclaration[] is a new top-level array or elided into Hashes.cs alone.

5.13 walks_to storage — resolved 2026-04-26

ScopeDeclaration now stores ParentScopeIds : ulong[], and Valenar generated declarations emit every declared walks_to edge including self-walks and the game-specific location -> settlement ownership edge.

5.14 Scope-level method lowering

Trigger: 01-world-shape.md § "Methods declared on scopes" — scope method signatures now include typed read-only methods and typed void command methods. Open compiler catch-up: emit the method declaration table and type-check call sites against { ScopeId, MethodId, ReturnType: SecsTypeRef, ParameterTypes: SecsTypeRef[] }.

Scope: Compiler parsing/declaration emission, one compiler type-check pass, and lowering. Existing corpus read sites are the CallIntMethod(…, H.Scope_Location_BuildingCount_TemplateId_Int, …) calls; Valenar activity source adds DrainStamina, GainXp, AdvanceLead, and Reveal as command-producing method calls.

5.15 The root_scope = name vs root_scope name grammar — resolved 2026-04-29

The Valenar source now uses the canonical root_scope Character; spelling. The compiler should reject the equals-sign variant unless a future migration requires accepting legacy content.

5.16 Raw C# helper replacement attributes

Trigger: Earlier drafts asked whether override accepted trailing attributes ([Deprecated]). The current design has no single override operation for SECS constructs; raw C# helper replacement remains unsettled.

Scope: Out of scope for Phase 3 unless 06-overrides-and-modding.md commits a raw C# helper replacement surface.

5.17 Compiler command flush cadence — resolved 2026-05-01

The compiler-emitted rule is now specified in 05-expressions.md § "Generated command flush contract": generated system, event, activity, and template contract method bodies flush with FlushCommands() after every command-producing source statement. Runtime dispatchers flush on normal body return and discard still-pending commands on exceptions so command buffers do not leak across system/event/activity/template method boundaries. Template activation remains activation-only intrinsic channel-source registration and is flushed at the runtime boundary.

5.18 Event source discriminant

Trigger: 04-behavior.md § "Event registration" L580-581 — "There is no SystemSource-analog EventSource field on EventEntry. Host-registered events are not distinguished from SECS-generated ones. Whether that matters for mod-operation dispatch/provenance is unresolved."

Scope: One new struct field plus the corresponding event dispatch / provenance branch.

5.19 The hot-path 5-minute cache issue doesn't apply here (removed)

(Sanity filter — the prompt's guidance about cache windows in loop tools is not a design topic.)

5.20 Identity-only Name handling for BareX templates

Trigger: 02-templates.md § "Bare templates" L570-571 — BareLocation registers intrinsic Slots = 4 but the .secs has no declaration for it. "Hand- written-only artifact — the compiler needs the source of that default, likely as a scope-level declaration in 01-world-shape."

Scope: Decide whether scope-field defaults live in scope declarations or in Bare template bodies. Small.

5.21 CanBuild failure-mode convention — resolved 2026-04-26

Undeclared scope walks are SECS0111; generated CanBuild queries do not silently fall back to true. A declared walk may still return null for this entity's runtime state, and the query handles that explicitly.

5.22 [CompilerGenerated] attributes on emitted files

Trigger: 02-templates.md § "template<Contract> Name" L85 — "Should the compiler emit [CompilerGenerated] attributes? Not currently done."

Scope: Adds one attribute to every emitted class. Noise vs signal in diffs / analyzers.

5.23 Source-range metadata (.map sidecar files)

Trigger: 02-templates.md § "Source-comment and docstring conventions" L640 — "Should the compiler embed source-range metadata (line/col) in a sidecar .map file? Likely yes; not yet decided. The Roslyn fork already has infrastructure for pdb-style source maps."

Scope: Debug-trace quality. Roslyn-fork feature; may land naturally as a byproduct of the fork's PDB story.

5.24 Active-time duration variant

Trigger: 03-channels-and-modifiers.md § "Lifetime clauses: owned_by, duration, when, while" commits that duration + while counts total elapsed binding lifetime even while inactive.

Question: Should SECS eventually support an explicit "active time only" variant, e.g. duration_active 30 or duration 30 active_only, where the timer pauses while a while trigger is false?

Scope: Deferred until a concrete content pattern needs it. Do not overload plain duration; its current meaning is total elapsed binding lifetime.

5.25 Event option source syntax

Trigger: 06-overrides-and-modding.md § 8.4 needs slot-level event option merge semantics, while doc 04's live lowering shape is BuildOptions(EventOptions options, EventContext context) plus explicit handler methods.

Question: Commit a first-class option { ... } source syntax, or keep BuildOptions/handler methods as the source model and define option slots by analyzing options.Add("Label", Handler) under strict rules?

Scope: Compiler parser/binder/lowering, event-option mod slots, generated provenance, and tests. Until resolved, option {} examples are proposed/future, not current .secs vocabulary.

6. Audit-findings follow-ups

Cross-reference of every item in docs/design/audit-findings.md against the categories above. Items already folded into sections § 1–5 are noted; only lower-severity residuals appear here.

  • § 1 API call frequency — informational table, no action items
  • § 2 Lifecycle pairing — twelve templates folded into § 3.2; contract methods without implementers folded into § 5.5
  • § 3 Scope-walk correctnessFarm.cs:32 folded into § 3.3
  • § 4 Command-flush convention — minor inconsistency folded into § 5.17
  • § 5 Reflection / dynamic dispatch — zero hits (clean)
  • § 6 Non-determinism — zero hits (clean); note on EventDispatcher.random field is engine-side, not Generated-side; out of audit scope
  • § 7 Host-side leaks — zero hits (clean)
  • § 8 Pattern divergence — cosmetic divergences folded into § 3.5 and § 5.6; specific call-outs:
    • House.cs:15 explicit-null — resolved by the 2026-04-26 building channel sweep
    • Housing.cs no GetFieldInt — resolved by the 2026-04-26 cost-field sweep; Workshop/AdvancedWorkshop intentionally have no build-cost fields yet
    • GreatHall.cs:30 switch-free — § 3.5
    • 12 tier-variants no CanBuild query — § 3.5
    • Dictionary-init syntax — § 1 / § 3.5
    • StorehouseTier2/3 cross-scope H.Food registration — resolved by § 3.1 and the 2026-04-26 FoodCapacity Valenar implementation
    • Granary.cs uniqueness — § 3.1 (noted as exemplar)
    • MainCharacter.cs:15 Name = "Hero" — § 3.6
    • ResourceProductionSystem no frequency + direct host read — § 3.4 / § 1
    • CelebrationBonusEvent.cs no Condition override — § 3.5
    • Trigger_MoraleGte60.cs captured0 branch — § 3.5 / § 5.7
    • FamineStartsEvent.cs:29 Priority = 0 — § 3.5
  • § 9 Unused engine APIs — folded into § 5.6 (CommandBufferExtensions completeness). Candidates for deprecation (RandomFloat, ReadFloat / WriteFloat / ReadBool / WriteBool, ReadEntity / GetChildren) are also tied to the 5-type SecsScalarType expansion in § 1. The type set is now expanded; the remaining question is which typed readers earn content-side consumers.

No audit finding stands alone. Every row in audit-findings.md is either already folded into sections § 1–5, or called out above as a lower-severity residual.

7. Mod-coverage follow-ups

Findings from docs/design/mod-coverage-audit.md (and any future audits of the same class — re-qualifying base-scoped rules under the base ∪ mods union). Each row cites the audit finding number, the affected doc(s), and the fix status. Findings absorbed inline into the source docs in the 2026-04-24 fix pass appear with landed 2026-04-24; remaining findings list the doc(s) they target and the residual decision they require.

FindingSeverityDoc(s)SubjectStatus / Fix
1DRIFT00"Override" glossary entry was the only mod conceptlanded 2026-04-24 — added "Mod extension" sibling glossary entry
2DRIFT00Active-diagnostics list was silent on cross-source-set firinglanded 2026-04-24 — table re-qualified per code
3POLISH00Status flags didn't separate lowering-contract state from mod lifecyclelanded 2026-04-24 — added clarifying paragraph
4BLOCKING01, 06walks_to storage design unresolved under modslanded 2026-04-24 — committed multi-edge design, mod scope note added
5BLOCKING01, 06Scope-extension semantics unspecifiedlanded 2026-04-24 — added "Mod extension of scopes" section
6DRIFT01single-parent scope storage not mod-composablelanded 2026-04-24 — resolved by Finding 4 multi-edge commit
7BLOCKING01, 06Scope methods had no mod extensibility storylanded 2026-04-24 — covered in Finding 5 "Method additions" subsection; host-bridge replacement/extensibility left as open question
8DRIFT01, 06Contract extensibility unspecifiedlanded 2026-04-24 — added "contracts are not extensible by mods" mod scope note
9BLOCKING01, 06SecsScalarType enum closure unspecifiedlanded 2026-04-24 — added closed-surface mod scope note; reserves SECS0102 for the diagnostic
10BLOCKING01, 06kind = requirement / mod channel operationslanded 2026-04-24 — added cross-source-set qualifier; clarified channel metadata is not replaceable by mods
11DRIFT01Internal = false on user channels — base-onlylanded 2026-04-24 — added mod scope note
12BLOCKING01, 06, 00FNV-1a-32 → FNV-1a-64 commitdesign landed 2026-04-24; runtime/generated code migrated 2026-04-25
13DRIFT01SECS0107 cross-mod scopelanded 2026-04-24 — added mod scope note
14DRIFT01, 06SECS0108 cross-assembly three-case splitlanded 2026-04-24 — added three-case mod scope note
15BLOCKING01identifier width for mod-added entity idsdesign landed 2026-04-24; runtime/generated code migrated 2026-04-25
16DRIFT01Hand-picked hash sentinels block mod detectionlanded 2026-04-24 (prior pass)
17BLOCKING01, 06Registry collision policy underspecifiedlanded 2026-04-24 — committed compile-time-merger-only policy; runtime DLL sideload out of scope
18DRIFT01Invariant list base-onlylanded 2026-04-24 — added per-invariant mod scope notes
19BLOCKING02, 06Modifier auto-detach under mod operationsresolved 2026-04-26 — owner/target cleanup is the committed lifecycle contract
20DRIFT02, 06Formula hash naming under mod-added templatesopen — tied to own-root channel-source enforcement
21BLOCKING02Template-method dictionary-init canonicalisationopen — already tracked in § 1 row 19 / § 3.5
22DRIFT02, 06CanBuild permissive fallback under mod walksresolved 2026-04-26 — undeclared walks are SECS0111; null declared walks are explicit query logic
23BLOCKING02, 06Bare templates under modslanded 2026-04-30 — doc 02 specifies synthesized Bare templates; doc 06 exposes bare_template_defaults slots
24DRIFT02, 06name = "X" template metadata under mod operationslanded 2026-04-30 — template_name_metadata is a per-field replacement slot in doc 06
25DRIFT02, 06reads { } auto-extraction under mod operationsopen — merger must rewrite RegisterFormula readsChannels:
26BLOCKING02, 06upgrade block mod-operation granularityopen — pending upgrade keyword landing
27DRIFT02, 06TemplateEntry.Methods mod-added contract methodsresolved 2026-05-01 — contracts are not extensible, and ContractMethodDeclaration[] must match QueryMethodIds/MethodIds
28BLOCKING03, 06Modifier declarations duplicate across modsopen — needs SECS0602 enumeration in doc 06
29DRIFT03, 06EffectMode enum closure unspecifiedlanded 2026-04-24 — doc 03 now defines EffectMode as a closed enum and doc 06 lists it as non-overridable
30BLOCKING03, 06Stacking policy under mod-added attachers / operation changesopen — max_stacks change rule unspecified
31DRIFT03Plain C# TagId constants and short-name resolution under modsopen — exported SDK / alias-resolution rule unspecified
32BLOCKING03, 06Template-scoped modifier mod-operation identityopen — needs cross-mod walk-through
33BLOCKING03, 06reads { } D-Hybrid under mod formulasopen — ValidateDependencies cross-mod re-run
34DRIFT03SecsFormulaReadSetViolation cross-modopen — --strict-readsets flag
35BLOCKING036-phase HardOverride ordering under modslanded 2026-04-24 — last-applied wins by modifier-binding-registration order; explicit priority remains deferred
36DRIFT03translation = localization under modsopen — mod-localization YAML directory convention
37BLOCKING04, 06Phase-extension story under modsopen — reject or spec mod.json phase_order
38DRIFT04SystemSource.Host mod dispatchopen — mod scope note needed
39BLOCKING04Event trigger on mod on-actions / load orderopen — mod.json load_after interaction
40DRIFT04, 06Event condition under mod operation changeslanded 2026-04-30 — condition is now a query slot: query bool Condition() { ... }
41BLOCKING04, 06Player-choice event option replacementlanded 2026-04-30 — options are explicit-name registrations in BuildOptions; handler methods are per-option slots
42DRIFT04Option descriptions localization under modsopen — tied to Finding 36
43BLOCKING04EventEntry array index identityopen — sort-by-EventId rule
44DRIFT04No EventSource discriminantopen — already in § 1 row 22
45BLOCKING04, 06On-action mode priority under mod eventslanded 2026-04-30 — lower priority values run earlier; 0-99 ordinary content, negative reserved for early platform/compatibility subscribers
46BLOCKING04, 06provides scope:X mod extensibilityopen — tie to fire-site provides-coverage check
47DRIFT04Chained / fallback on-actions under modsopen — pending syntax commit
48DRIFT04, 06Activity mod-operation per-slot granularitylanded 2026-04-30 — class-based activity slots are enumerated in doc 06 (IsVisible, IsTargetValid, CanStart, lifecycle methods, cooldown/AI fields)
49BLOCKING04, 06Targeted-activity candidate validation replacementsuperseded 2026-04-30 — old candidate-filter block wording is replaced by the IsTargetValid activity slot
50DRIFT05<scope> sigil from mod-declared scopeopen — SECS0501 proposed
51DRIFT05add_modifier cross-mod identityopen — error-message provenance
52BLOCKING05, 06foreach entity in Contract under mod templateslanded 2026-04-30 — doc 05 states contract iteration includes mod-authored templates implementing the contract
53DRIFT05Query keywords under mod scope-graph extensionsopen — tied to Finding 5
54BLOCKING06"What's NOT overridable" enumeration incompleteopen — extension to doc 06 list
55BLOCKING06FNV-1a-32 vs 64 mod-scale (Doc 06 side)design landed 2026-04-24; runtime/generated code migrated 2026-04-25
56BLOCKING06Conflict detection slot enumerationopen — half-day extension to doc 06's table
57BLOCKING06Merge-pass pipeline missing add-extension caseopen — extend step 3 of pipeline
58BLOCKING06Hash collision case table cross-mod additionsopen — explicit four-case table
59DRIFT06Load-order resolution cycle policyopen — launcher manifest validation
60DRIFT06Base as load order 0 semanticsopen — explicit reservation rule
61DRIFTFUTURE_WORK"Not a plan" metadata + mod-item admissionlanded 2026-04-24 — added § 7 (this section) and updated maintenance preamble
62BLOCKINGFUTURE_WORK3.2 Modifier lifecycle leaks — mod inheritancesuperseded 2026-04-26 — owner/target cleanup makes paired source teardown unnecessary
63BLOCKINGFUTURE_WORK5.1 Modifier lifecycle pattern — mod-friendliness rankingresolved 2026-04-26 — chosen pattern is engine owner/target cleanup
64DRIFTFUTURE_WORK5.4 System declaration defaults — mod audit weightinglanded 2026-04-24 — added mod-friendliness weighting to § 5.4

The 2026-04-24 fix pass landed 22 findings inline (1, 2, 3 in doc 00; 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 17, 18 in doc 01; 61, 62, 63, 64 in this file; plus 12, 15, 16, 55 from the prior cascade pass). Later passes also closed 19, 23, 24, 40, 41, 45, 48, 49, and 52. Rows still marked open remain to be absorbed into docs 02-06 by separate fix passes.

Completed

TaskLandedNotes
Expanded SecsScalarType from 3 entries (Int, Float, Bool) to 5 entries (Int, Long, Float, Double, Bool)2026-04-25Shared scalar enum now covers channels, scope fields, template fields, commands, formulas, and modifier effects.
Added ResolveLongFast / ResolveDoubleFast and matching slow result paths to ChannelResolver2026-04-25Numeric resolver paths use target-typed additive/override slots and DoubleValue multiplicative factors.
Added typed ReadLong / WriteLong / ReadDouble / WriteDouble and channel re-entry methods to host interfaces2026-04-25Valenar, character-trainer, benchmarks, and test hosts implement the expanded interface.
Expanded Command and ModifierEffect payload slots to IntValue, LongValue, FloatValue, DoubleValue, BoolValue2026-04-25Multiplicative modifier effects always use DoubleValue; FloatValue is only an actual float value.
Expanded channel/template-field clamp slots to cover int, long, float, and double2026-04-25Bool remains unclamped by design.
Added EffectMode.HardOverride and inserted the HardOverride phase into channel and template-field resolution2026-04-25Accumulative channels skip additive, multiplicative, and HardOverride phases.
Added typed scope-field increment helpers for long, float, and double2026-04-25increment is now helper-backed for all numeric scope-field scalar types; bool remains invalid.
Committed modifier owner/target lifecycle cleanup2026-04-26owner is lifetime source, target is effect anchor; template destruction removes owned bindings and bindings targeted at the destroyed entity.
Removed special template deactivation delegate path2026-04-29Deactivation now uses ContractLifecycleIds.Deactivation -> MethodId through TemplateEntry.Methods, matching activation and preserving developer-owned method names.
Replaced flat saved scopes with dispatch frames2026-05-01FireOnAction pushes/pops frames, nested dispatches inherit snapshots without leaking writes, deferred choices execute inside their captured snapshot, and runtime validates declared provides keys before subscriber dispatch.
Specified generated command flush contract2026-05-01Systems, events, activities, and template contract methods flush after every command-producing source statement via FlushCommands(); runtime boundaries prevent pending command leakage. Template activation remains activation-only intrinsic channel-source registration.
Added SecsTypeRef, SecsTypeDeclaration, type-reference validation, and SecsTypeRef callable metadata2026-05-02Scope methods, contract methods, template fields, and typed query registration now describe first-class SECS types instead of scalar-only SecsValueKind metadata.
Added typed template-query registration and dispatch2026-05-02Templates register concrete TemplateQuery<TArgs,TResult> delegates; query returns no longer use SecsValue.
Refactored Valenar content/host feature generation around structured placement profiles and placement results2026-05-02Feature templates author FeaturePlacementProfile, expose EvaluatePlacement(Location candidate, FeaturePlacementInput input), and host placement applies global budgets, spacing, and conflicts after local scoring.
Completed Valenar Generated feature-placement query/profile lowering stand-ins2026-05-02Generated feature templates expose placement profiles and register Feature.EvaluatePlacement(Location, FeaturePlacementInput):FeaturePlacementResult through typed query adapters; the host consumes the generated evaluator and no longer carries duplicate placement model definitions.
Activity/policy startup mod finalization runtime2026-05-12 doc sweepShipped before this sweep: ModRegistry plus SecsRegistry.RegisterActivityMod, RegisterPolicyMod, and FinalizeModRegistration() merge activity/policy mods once at startup. The remaining generic source-set merger is compiler-owned Phase 3 work.
Closed Wave 8 — save/load durability for ScopeSlotStore and ActivityRunStore, migration discipline, and tooling hardening2026-05-08Shipped APIs: ScopeSlotStore.Snapshot() returns IReadOnlyList<SlotSnapshot>; ScopeSlotStore.Restore(snapshots) rehydrates a fresh store with hard validation (SECS0820 unknown kind / non-empty store, SECS0821 element type mismatch). ScopeSlotStore.EnumerateSlotKeys() lists all (scope, kind) pairs without materialising payloads. ActivityRunStore.Restore now validates schema id (SECS0811) and lane id (SECS0812) in addition to activity id (SECS0810). PolicyDispatcher adds opt-in EnableTrace / LastTrace / PolicyRuleTrace / PolicyRuleTraceEntry / PolicyDispatchAction for debug tooling. Diagnostic codes SECS0810-0812 and SECS0820-0821 reserved on RegistryDiagnosticCode; the legacy SECS0820 (Phase 2 binder, ScopedList<T> indexed access) renumbered to SECS0822. Docs 04, 06 § 8.10, 09 updated; SECS-Compiler-Plan diagnostic catalog updated.
Closed Wave D/C — runtime activity provenance and save/load durability2026-05-13ActivityRun and ActivityRunState now carry ActivityRequestOrigin; ToState() / Restore(...) round-trip it. Player queue starts build player-origin requests while preserving explicit activity-clock ticks. PolicyDispatcher stamps direct calls as Policy and normalizes CallBest candidates to Policy at the dispatch boundary. PolicyExecutor static selector producers emit policy-origin requests. Origin remains runtime/save provenance, not a required UI projection.