Skip to main content

Resources, Items, and Crafting

This page owns the distinction between settlement-side resources, actor-held items, and the crafting layer that transforms one into the other.

Resource / Item / Crafting Distinctions

  • Resources are settlement-facing or world-facing quantities such as grain, timber, stone, ore, fuel, or warding materials.
  • Items are held objects such as tools, weapons, armor, supplies, relics, or crafted goods.
  • Crafting is the transformation layer that consumes available inputs and produces useful outputs.
  • Labels such as Gold, Food, Wood, Metal, and Stone are resource-stockpile or UI labels. They are not the primary settlement channel canon.

Core Grounds Ore and Material Carve-Out

  • The Core Grounds may support small-batch ore processing, smelting, or material refinement at the repair forge or workshop scale.
  • This serves the MC's personal repair, craft, and expedition needs — it is not the main ore extraction economy.
  • Settlement-scale ore extraction, smelting capacity, and industrial material throughput belong to the Settlement layer.

Design Rules

  • Keep the resource layer readable at strategic scale.
  • Keep item identity meaningful for the MC and other named characters.
  • Let crafting bridge the two layers instead of collapsing them into one generic inventory bucket.
  • Tools, facilities, sites, and skills should influence what can be crafted and how efficiently it can be done.

Current Direction

  • Early acts care about fuel, food, shelter inputs, basic tools, and survival gear.
  • Settlement acts introduce processing chains, workshops, storage, and larger throughput questions.
  • Later acts expand into rarer materials, military supply, ritual support, and realm-scale logistics.

Recipe Contract

Recipes are registry-only metadata. A Recipe describes the inputs, outputs, and lifecycle of a crafting transformation; it is never instantiated as an entity in the game world. The committed shape is authored at legacy/v1/examples/valenar/Content/economy/recipes/contracts.secs lines 1-8:

contract Recipe
{
root_scope Recipe;
registry_only;
method void OnCreated();
}

registry_only is the qualifier that pins this contract as data-only. Recipe templates register against the contract for type-system participation and for designer authoring, but no RecipeEntity is ever materialized — the crafting machine, workshop, or tool reads the recipe template by id and consumes inputs / produces outputs against its own entity slot.

The method void OnCreated() declaration is a designer-facing lifecycle hook reserved for recipe-registration-time bookkeeping (e.g., binding the recipe into a workshop's available-recipe catalog). Because the contract is registry_only, OnCreated never runs against a Recipe instance — it runs at template registration time, not at activation.

Registry-Only Enforcement

registry_only is enforced through three runtime properties:

  • Activation rejection (implemented). Any attempt to activate a template against a registry_only contract is rejected with the SECS0803 diagnostic in legacy/v1/src/SECS.Engine/TemplateActivator.cs lines 103-107. The ContractDeclaration.IsRegistryOnly flag at legacy/v1/src/SECS.Abstractions/Contracts/ContractDeclaration.cs lines 13-16 is the canonical schema-side check.
  • create_entity rejection (deferred). The host's create_entity helper must reject creation calls against registry_only contracts with the same diagnostic. This path is tracked in docs/design/FUTURE_WORK.md line 50 as a remaining gap.
  • Startup validation (deferred). At engine startup the registry should run a registry.Validate() boot-time check confirming that no TemplateEntry is bound against a registry_only contract in a way that would permit activation by side-channel. This validation is also tracked in docs/design/FUTURE_WORK.md line 50.

The three enforcement points together form the registry-only contract: no entity may materialize, no creation call may bypass the check, and no registry state may exist in which the first two checks could be silently sidestepped.

Current Runtime Status

  • Activation rejection: shipped (TemplateActivator.cs lines 103-107 throw InvalidOperationException with SECS0803).
  • create_entity rejection: not shipped (host helper does not yet enforce the check; tracked in FUTURE_WORK.md line 50).
  • Startup validation: not shipped (registry.Validate() does not yet enforce the registry-only invariant; tracked in FUTURE_WORK.md line 50).

Live Recipe templates today rely on the activation rejection alone. The two deferred checks are required for full registry-only contract coverage; until they land, a host author who bypasses the activator (direct entity construction, bypassed registry helpers) could theoretically materialize a Recipe instance. The activator rejection is the canonical enforcement and is sufficient for the current Generated/ stand-in surface, but the contract is not fully closed until all three checks ship.

Runtime Backing Status

Status at this doc's acceptance time: partial.

  • Host/runtime owner: legacy/v1/src/SECS.Engine/TemplateActivator.cs (activation rejection); legacy/v1/src/SECS.Engine/SecsRegistry.cs (registry- side validation hook); a future host-helper boundary owns create_entity rejection.
  • Generated/.secs owner: legacy/v1/examples/valenar/Content/economy/recipes/contracts.secs (live source); legacy/v1/examples/valenar/Generated/Declarations.cs carries the matching ContractDeclaration rows with IsRegistryOnly = true.
  • Read-model/UI owner: not applicable; recipes are registry metadata and have no per-instance UI surface beyond the workshop/tool selection screens that read recipe templates by id.
  • Tests: activator rejection is covered by existing engine tests for SECS0803; create_entity rejection and startup-validation tests are deferred to the future engine-validation wave.
  • Known gaps: create_entity rejection, startup validation. Both are tracked in docs/design/FUTURE_WORK.md line 50.
  • Illegal fallback behavior: silent activation of a registry_only template is forbidden. A host that bypasses TemplateActivator to materialize a Recipe entity is a tenet failure, NOT a workaround. The contract is enforce-at-three-points or it is broken; partial enforcement does not authorize silent activation through the unenforced points.
  • Next closure wave: a future engine-validation wave authors the create_entity rejection path and the registry.Validate() boot-time check.

AAA Precedent

The registry-only recipe pattern draws on three AAA references:

  • Factorio recipes (primary) — Factorio's data.raw.recipe is the canonical modern reference. A recipe row is registry-only metadata consumed by crafting machines; recipes are never instantiated as entities in the game world. The crafting machine looks up the recipe by id, consumes inputs from its own inventory slot, and produces outputs into its output slot. The recipe itself is data, not an entity. The contract Recipe { registry_only; } shape is structurally identical to Factorio's data-stage recipe definition.
  • Dwarf Fortress reactions (secondary) — Dwarf Fortress' reaction_standard.txt and per-mod reaction RAWs are registry- only metadata consumed by workshops. A reaction is never an entity; it is a row that workshops query at production time.
  • Minecraft datapacks (tertiary) — Minecraft's per-pack recipes/ JSON files are registry-only data loaded at world-load time; recipes are never instantiated entities. Minecraft's datapack-driven recipe registry is the modern AAA realization of "recipe is data, not an entity" outside the procedural-generation space.

The pattern is universal across shipped AAA: recipes are data, never entities. The registry_only qualifier on contract Recipe is the SECS expression of that pattern.