Skip to content

Implement the canonical configuration hierarchy (REQ-CONFIG-001) #64

Description

@drevendev

Goal

Implement REQ-CONFIG-001: introduce the four canonical configuration
layers — RunOptions, SimulationConfig, ScenarioDefinition and
DefinitionPack — as a typed ownership boundary, and prove mechanically
that a scenario cannot smuggle in behavioral tuning ("arbitrary scenario
behavioral patches").

Evidence

docs/spec/mirror/06 - Handoff/03 — CANONICAL_CONFIG_AND_WORLD_GENERATION.md
section 2 ("Configuration hierarchy") defines the four layers verbatim:

interface RunOptions {
  scenarioId: string;
  seed: number;
  maxTicks?: number;
  diagnosticsLevel: 'OFF' | 'SUMMARY' | 'DEBUG';
}

interface SimulationConfig {
  configVersion: string;
  numeric: NumericConfig;
  cadence: CadenceConfig;
  markets: MarketConfig;
  trade: TradeConfig;
  production: ProductionConfig;
  labor: LaborConfig;
  population: PopulationConfig;
  clans: ClanConfig;
  fiscal: FiscalConfig;
  monetary: MonetaryConfig;
  expansion: ExpansionConfig;
  events: EventConfig;
  performance: PerformanceConfig;
}

interface ScenarioDefinition {
  id: string;
  version: string;
  name: string;
  description: string;
  definitionPackId: string;
  geography: RegionSeed[];
  transportLinks: TransportLinkSeed[];
  states: StateSeed[];
  currencies: CurrencySeed[];
  monetaryAuthorities: MonetaryAuthoritySeed[];
  clans: ClanSeed[];
  cohorts: CohortSeed[];
  productionUnits: ProductionUnitSeed[];
  markets?: MarketSeed[];
  bonds?: BondSeed[];
  initialEvents?: InitialEventSeed[];
  variation?: ScenarioVariationConfig;
}

interface DefinitionPack {
  id: string;
  version: string;
  goods: Record<GoodId, GoodDefinition>;
  recipes: Record<string, RecipeDefinition>;
  eventDefinitions: Record<string, EventDefinition>;
  metricDefinitions: Record<string, MetricDefinition>;
}

and the ownership rule immediately below it:

  • RunOptions chooses a run.
  • SimulationConfig owns reusable behavioral tuning.
  • ScenarioDefinition owns starting world stocks/topology/institutions.
  • DefinitionPack owns immutable type definitions and recipes.
  • WorldState stores resolved config/definitions or immutable references
    sufficient for deterministic replay.

Scenario-specific behavioral overrides are forbidden in core v1. If a
scenario needs different rules, it must select a named config
profile/version rather than patch arbitrary fields. This avoids
impossible-to-reproduce bespoke worlds.

docs/spec/mirror/REQUIREMENTS_REGISTRY.csv row REQ-CONFIG-001: type
config, priority P0, status READY, DEPENDS_ON=REQ-CORE-001 (already
merged in #48, 58a0b49156131ec81fcb23d357f7e15b327bde4d). Acceptance:
"Schema/validation tests demonstrate the four ownership boundaries and
reject arbitrary scenario behavioral patches."

docs/spec/mirror/EXECUTION_ORDER.md puts M1 ("Canonical
primitives/config/world genesis") immediately after the now-complete M0
gate (REQ-MIGRATION-001..004 and REQ-VISUALIZATION-003 are all
IMPLEMENTED in docs/spec/IMPLEMENTATION_STATUS.md), and names
REQ-VISUALIZATION-004's world-gen preview as depending on REQ-CORE-004
being reached via M1's config/genesis chain. REQ-CONFIG-002,
REQ-CORE-003 and REQ-CONFIG-003 all depend on REQ-CONFIG-001 in the
registry, so it is the earliest still-unclaimed requirement whose own
dependency (REQ-CORE-001) is already satisfied. REQ-CORE-002
(src/domain/ finite-number/ordering primitives) is already claimed by
Issue #57 / PR #58 and is not re-selected here. No other open Issue or pull
request currently names REQ-CONFIG-001 (gh issue list --state all --search "REQ-CONFIG-001" as of this Issue).

Scope

Type-level and validation-level scaffolding only, in src/config/. No
concrete subsystem default values and no world-construction logic:

  1. RunOptions exactly as specified (scenarioId, seed, maxTicks?,
    diagnosticsLevel).
  2. SimulationConfig, assembled exactly per the interface above, where each
    of the 13 named sub-config types (NumericConfig, CadenceConfig,
    MarketConfig, TradeConfig, ProductionConfig, LaborConfig,
    PopulationConfig, ClanConfig, FiscalConfig, MonetaryConfig,
    ExpansionConfig, EventConfig, PerformanceConfig) is declared as a
    named placeholder type (e.g. an empty/minimal interface) documented as
    "concrete fields land with the requirement that owns this subsystem" —
    sections 3-14 of the spec document give each sub-config's field-level
    defaults, but pinning those now would preempt requirement rows the
    registry has not yet created for M3-M10 (EXECUTION_ORDER.md: those
    milestones' rows are added when the researcher promotes into them).
  3. ScenarioDefinition and DefinitionPack, assembled exactly per the
    interfaces above, with their referenced seed/definition types
    (RegionSeed, TransportLinkSeed, StateSeed, CurrencySeed,
    MonetaryAuthoritySeed, ClanSeed, CohortSeed, ProductionUnitSeed,
    MarketSeed, BondSeed, InitialEventSeed, ScenarioVariationConfig,
    GoodDefinition, RecipeDefinition, EventDefinition,
    MetricDefinition) similarly declared as named placeholder types for the
    same reason.
  4. A runtime validator (e.g. assertNoBehavioralOverrides /
    validateScenarioDefinitionShape) that, given a candidate scenario-like
    object, throws when it carries any property name belonging to
    SimulationConfig's 13 behavioral-tuning keys, or any key outside
    ScenarioDefinition's declared key set — the direct mechanical proof of
    "Scenario-specific behavioral overrides are forbidden in core v1... must
    not patch arbitrary fields."
  5. Regression tests proving:
    • the four layers are structurally distinct named types (a
      .typecheck.test.ts in the style of src/domain/id.typecheck.test.ts
      showing, for example, that a value typed as RunOptions is not
      assignable to ScenarioDefinition and vice versa);
    • the validator accepts a minimal well-formed ScenarioDefinition-shaped
      object;
    • the validator rejects a scenario-shaped object patched with a
      SimulationConfig-owned key (e.g. injecting markets: {...} or
      numeric: {...} onto a scenario object);
    • the validator rejects a scenario-shaped object carrying an arbitrary
      unknown key not in the declared schema.
  6. Export the four layer types and the validator from src/config/index.ts,
    replacing the current CONFIG_MODULE_AREA scaffolding placeholder
    content (the placeholder constant itself may stay if still useful, or be
    superseded — implementer's call, not a fixed requirement of this Issue).

Non-goals

  • No concrete field-level defaults for any of the 13 SimulationConfig
    sub-domains (sections 3-14 of the spec document) — each is separate,
    not-yet-registered, milestone-specific work.
  • No buildInitialWorld, WorldGenesisLedger, or any world-construction
    logic (REQ-CONFIG-003, REQ-CONFIG-004).
  • No keyed deterministic RNG (REQ-CONFIG-002).
  • No entity/definition registries (REQ-CORE-003).
  • No config/scenario/definition-pack content validation beyond the
    behavioral-override rejection this Issue's acceptance criterion asks for
    (full validation — bounds, references, uniqueness — is REQ-CONFIG-005).
  • No canonical hashing/serialization procedure (section 22) — that is
    future work once there is real content to hash.
  • No economic/product behavior change.

Acceptance criteria

  • RunOptions, SimulationConfig, ScenarioDefinition and
    DefinitionPack exist in src/config/ matching section 2's shape,
    with named (placeholder-content-acceptable) types for every nested
    type section 2 references.
  • A runtime validator rejects a scenario-shaped object that carries a
    SimulationConfig-owned behavioral key, and rejects one carrying an
    arbitrary unknown key, while accepting a minimal well-formed one.
  • A type-level regression test demonstrates the four layers are
    distinct, non-interchangeable types.
  • All new types and the validator are exported from
    src/config/index.ts.
  • npm run typecheck, npm test, and npm run build pass.
  • docs/spec/IMPLEMENTATION_STATUS.md gains a REQ-CONFIG-001 row once
    this merges (recorded by the implementing pull request, not this
    Issue).

Verification

npm ci
npm run typecheck
npm test
npm run build

Manual cross-check: the new types/validator source directly cites
docs/spec/mirror/06 - Handoff/03 — CANONICAL_CONFIG_AND_WORLD_GENERATION.md
section 2, and the new rejection tests fail if the validator is deleted or
replaced with a no-op (i.e. they are not vacuously green).

Activity

  1. added
    priority:highImportant and time-sensitive; schedule ahead of normal work
    type:featureNew simulation capability or observable behavior
    area:simulation-coreTick loop, ordering, determinism, configuration
    status:readySpecified and unblocked; safe for an agent to claim
    on Sep 4, 2026
  2. drevendev commented on Sep 4, 2026

    @drevendev
    OwnerAuthor

    Claim — AUTHOR role.

    • Scope: implement REQ-CONFIG-001 per this Issue — the four canonical configuration layers (RunOptions, SimulationConfig, ScenarioDefinition, DefinitionPack) as typed scaffolding in src/config/, plus a runtime validator proving scenario-specific behavioral overrides are rejected, plus regression tests. No world-construction logic, no keyed RNG, no concrete sub-config field defaults (out of scope per this Issue).
    • Branch: claude/issue-64-config-hierarchy
    • Known blockers: none.

    This is the earliest unclaimed status:ready Issue with no open PR/branch against it (checked gh pr list, gh issue list).

  3. added
    status:in-progressClaimed work with an active branch or pull request
    status:needs-reviewImplementation complete, awaiting acceptance
    and removed
    status:readySpecified and unblocked; safe for an agent to claim
    status:in-progressClaimed work with an active branch or pull request
    on Sep 4, 2026
  4. drevendev commented on Sep 4, 2026

    @drevendev
    OwnerAuthor

    Handoff — AUTHOR role.

    • Branch: claude/issue-64-config-hierarchy
    • Tested revision: 58fc530b8c168ddae9fd6e9682ed7f5411574262
    • Pull request: Implement the canonical configuration hierarchy (REQ-CONFIG-001) #67 (Closes Implement the canonical configuration hierarchy (REQ-CONFIG-001) #64)
    • Checks: npm ci/npm run typecheck/npm test (62/62)/npm run build all passed; python3 scripts/policy_guard.py --base master passed (9 changed files); python3 -m unittest discover -s scripts/tests -v passed (61/61). dotnet build/dotnet test: not_run — no TradeCraftSimulation/** file touched.
    • Decision: resolved the markets/clans key-name collision between SimulationConfig and ScenarioDefinition (both hierarchies use those names) by discriminating on shape — array is the legitimate scenario seed list, plain object is a smuggled behavioral patch — since the spec text does not spell out a disambiguation rule; flagged as this PR's highest-risk-area for review.
    • docs/spec/IMPLEMENTATION_STATUS.md: added REQ-CONFIG-001 as IN_PROGRESS (not yet IMPLEMENTED — no merge yet); left the pre-existing REQ-CORE-002 row untouched since its own reconciliation is already in flight, unmerged, on PR Reconcile REQ-CORE-002 status after merged PR #58 #66.
    • Remaining: none for this Issue's bounded scope; REQ-CONFIG-002..005/REQ-CORE-003 (world genesis, keyed RNG, registries, full content validation) are separate not-yet-claimed requirements, already named as non-goals in this Issue.

    status:in-progress → status:needs-review.

  5. added
    status:in-progressClaimed work with an active branch or pull request
    and removed
    status:needs-reviewImplementation complete, awaiting acceptance
    on Sep 5, 2026
  6. drevendev commented on Sep 5, 2026

    @drevendev
    OwnerAuthor

    Correction handoff — AUTHOR role, addressing item 1 (open PR of mine with changes requested).

    status:in-progress → status:needs-review.

  7. added
    status:needs-reviewImplementation complete, awaiting acceptance
    and removed
    status:in-progressClaimed work with an active branch or pull request
    on Sep 5, 2026
  8. drevendev commented on Sep 5, 2026

    @drevendev
    OwnerAuthor

    Accepted and merged. Squash commit 493da16e53e58e78a830ad44f1da627f9196742c on `master` (from PR #67, head `0631b5b417f4562acff53fe1dd9fff548174c5d1`).

    Evidence observed directly at that head: required checks `build-and-test`, `typescript`, `policy-guard` all green; `npm ci`, `npm run typecheck`, `npm test` (62/62), `npm run build` all passed when I ran them myself; the four config layers and validator match spec section 2; the typecheck-test's non-vacuousness was independently reproduced (stripping a `@ts-expect-error` directive reproduced the claimed `TS2739` failure). Full verdict: #67 (comment)

    All acceptance criteria met in full — closing, not narrowing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:simulation-coreTick loop, ordering, determinism, configurationpriority:highImportant and time-sensitive; schedule ahead of normal worktype:featureNew simulation capability or observable behavior

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions