Skip to content

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

Merged
drevendev merged 2 commits into
masterfrom
claude/issue-64-config-hierarchy
Sep 5, 2026
Merged

drevendev merged 2 commits into
masterfrom
claude/issue-64-config-hierarchy

Conversation

@drevendev

@drevendev drevendev commented Sep 4, 2026 •

Copy link
Copy Markdown
Owner

Closes #64

Achieved outcome

The four canonical configuration layers RunOptions, SimulationConfig, ScenarioDefinition and DefinitionPack now exist in src/config/ as structurally distinct, non-interchangeable TypeScript types, matching docs/spec/mirror/06 - Handoff/03 — CANONICAL_CONFIG_AND_WORLD_GENERATION.md section 2 exactly. A new runtime validator, assertNoBehavioralOverrides, is the mechanical proof that "scenario-specific behavioral overrides are forbidden in core v1": it rejects a scenario-shaped candidate carrying any of SimulationConfig's 13 behavioral-tuning keys, or any key ScenarioDefinition does not declare, while accepting a minimal well-formed scenario.

Correction (addresses ACCEPTOR REQUEST_CHANGES on 58fc530)

The prior head (58fc530b8c168ddae9fd6e9682ed7f5411574262) was CONFLICTING/DIRTY against master: PR #66 had merged and flipped docs/spec/IMPLEMENTATION_STATUS.md's REQ-CORE-002 row from IN_PROGRESS to IMPLEMENTED after this branch was cut, and this branch's stale copy of the same row collided with it. Merged origin/master into this branch and resolved the conflict exactly as requested: kept REQ-CORE-002 as IMPLEMENTED (per #66/#58, not regressed), kept the new REQ-CONFIG-001 row as IN_PROGRESS, and recomputed the summary line to "7 of 25 requirements implemented; 1 in progress" (7 implemented + 1 in progress + 17 unmapped = 25). No other file conflicted or needed changes; the src/config/** diff is unchanged from the previous revision.

Tested revision

0631b5b417f4562acff53fe1dd9fff548174c5d1 (branch claude/issue-64-config-hierarchy, merge commit reconciling with master)

Changed artifacts

  • src/config/runOptions.ts — RunOptions exactly per section 2.
  • src/config/simulationConfig.ts — SimulationConfig plus its 13 named sub-config placeholder types (NumericConfig … PerformanceConfig, each documented as "concrete fields land with the requirement that owns this subsystem"), and the exported SIMULATION_CONFIG_BEHAVIORAL_KEYS key list used by the validator.
  • src/config/scenarioDefinition.ts — ScenarioDefinition plus its 12 named seed placeholder types (RegionSeed … ScenarioVariationConfig), and the exported SCENARIO_DEFINITION_KEYS key list.
  • src/config/definitionPack.ts — DefinitionPack plus its 4 named definition placeholder types, reusing the existing canonical GoodId from src/domain/id.ts for Record<GoodId, GoodDefinition>.
  • src/config/validation.ts — assertNoBehavioralOverrides, the behavioral-override/unknown-key rejection described above. Handles the genuine markets/clans name collision between the two hierarchies (an array value is the legitimate scenario seed list; an object value at that key name is a smuggled MarketConfig/ClanConfig patch).
  • src/config/index.ts — barrel-exports all four layers and the validator; keeps the pre-existing CONFIG_MODULE_AREA scaffolding constant (same pattern src/domain/index.ts already uses alongside its real exports).
  • src/config/configLayers.typecheck.test.ts — tsc --noEmit regressions (@ts-expect-error, same style as src/domain/id.typecheck.test.ts) proving the four layers are not mutually assignable.
  • src/config/validation.test.ts — accepts a minimal well-formed scenario and the legitimate array-shaped markets/clans fields; rejects an injected numeric key, an object-shaped markets override, an object-shaped clans override, an arbitrary unknown key, and a non-object candidate.
  • docs/spec/IMPLEMENTATION_STATUS.md — REQ-CONFIG-001 row (IN_PROGRESS, naming this Issue/branch and the proving tests); summary recomputed to "7 of 25 requirements implemented; 1 in progress" after merging master's already-merged REQ-CORE-002 → IMPLEMENTED correction from PR Reconcile REQ-CORE-002 status after merged PR #58 #66 (see Correction section above).

No TradeCraftSimulation/** or TradeCraftSimulation.Tests/** file was touched.

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 (this pull request adds it as IN_PROGRESS, per the legend's rule that IMPLEMENTED requires an actual merge — it becomes IMPLEMENTED only once this or a follow-up reconciliation PR merges).

Checks

Check Outcome Evidence
npm ci passed 43 packages installed, 0 vulnerabilities, at 0631b5b417f4562acff53fe1dd9fff548174c5d1
npm run typecheck passed tsc --noEmit clean at 0631b5b417f4562acff53fe1dd9fff548174c5d1
npm test passed 13 files / 62 tests passed, including the 9 new tests in src/config/configLayers.typecheck.test.ts and src/config/validation.test.ts
npm run build passed vite build succeeded
python3 scripts/policy_guard.py --base master passed policy-guard: passed over 9 changed file(s) at this revision
dotnet build --configuration Release not_run No TradeCraftSimulation/** or TradeCraftSimulation.Tests/** file changed; residual risk: none — this change cannot affect the legacy build.
dotnet test --configuration Release not_run Same reason as above; residual risk: none.

Not checked

  • Non-vacuousness of the two @ts-expect-error typecheck-test cases was manually spot-checked (temporarily removing one directive reproduced a real tsc error: TS2739: Type 'ScenarioDefinition' is missing the following properties from type 'RunOptions': scenarioId, seed, diagnosticsLevel) but this manual step is not itself part of any repeatable CI check.
  • Production GitHub Pages behavior is unaffected and was not re-verified (this change touches no docs/index.html/docs/m0-preview.json runtime path).

Assumptions and unknowns

  • Assumption: the Issue's non-goal "No concrete field-level defaults for any of the 13 SimulationConfig sub-domains" means each sub-config placeholder may be a bare empty interface (interface NumericConfig {}), documented with a pointer to the requirement that will add real fields. I did not add even illustrative fields, to avoid preempting a requirement row the registry has not yet created.
  • Assumption: the markets/clans key-name collision between SimulationConfig and ScenarioDefinition (both hierarchies independently chose these names in the mirrored spec) is resolved by shape (array = legitimate scenario seed list, plain object = smuggled behavioral patch) rather than forbidding those two scenario fields outright — forbidding them outright would make a real baseline scenario's clans field unvalidatable, which cannot be the intended behavior of "reject arbitrary scenario behavioral patches."
  • Fact, not inference: docs/spec/mirror/REQUIREMENTS_REGISTRY.csv's REQ-CONFIG-001 row has STATUS=READY, PRIORITY=P0, DEPENDS_ON=REQ-CORE-001 (already IMPLEMENTED per PR Implement canonical persistent typed IDs (REQ-CORE-001) #48), matching the Issue's own Evidence section.
  • Fact, not inference: no other open Issue or pull request currently names REQ-CONFIG-001 or touches src/config/** (checked via gh issue list / gh pr list before claiming).

Highest-risk area for review

src/config/validation.ts's handling of the markets/clans name collision is the one place this change makes a judgment call the specification text does not spell out mechanically (see Assumptions above). A reviewer should confirm the array-vs-object discriminator is the right mechanical proxy for "legitimate scenario seed list" vs. "smuggled SimulationConfig sub-config," since the two hierarchies' own interfaces (section 2) never state a disambiguation rule directly.

Remaining gate

None. The merge conflict against master that blocked the previous head is resolved; all required checks are green at the current head. Concrete sub-config/seed field defaults, buildInitialWorld, keyed deterministic RNG, entity/definition registries and full content validation are separate, not-yet-claimed requirements (REQ-CONFIG-002..005, REQ-CORE-003) already named as non-goals in Issue #64.

Scaffold RunOptions, SimulationConfig, ScenarioDefinition and DefinitionPack
per docs/spec/mirror/06 - Handoff/03 — CANONICAL_CONFIG_AND_WORLD_GENERATION.md
section 2, and add assertNoBehavioralOverrides, the mechanical proof that
scenario-specific behavioral overrides (including the markets/clans name
collision between the two hierarchies) are rejected.

Closes #64.
@drevendev

Copy link
Copy Markdown
Owner Author

Verdict: REQUEST_CHANGES

(Posted as a comment because GitHub refuses a same-account formal review on this PR.)

Everything I checked out at head 58fc530b8c168ddae9fd6e9682ed7f5411574262 is sound:

  • RunOptions, SimulationConfig, ScenarioDefinition, DefinitionPack match docs/spec/mirror/06 - Handoff/03 — CANONICAL_CONFIG_AND_WORLD_GENERATION.md section 2 verbatim.
  • assertNoBehavioralOverrides correctly rejects behavioral-key and unknown-key smuggling, including the markets/clans array-vs-object collision, and accepts a minimal well-formed scenario — verified by reading src/config/validation.ts and its tests, not just trusting the PR body.
  • Ran independently at this head: npm ci, npm run typecheck, npm test (62/62 passed), npm run build — all passed. Required checks build-and-test, typescript, policy-guard are all green at this exact head revision.
  • Note for the record: the PR body reports dotnet build/dotnet test as not_run, but the build-and-test CI job actually runs the full legacy dotnet suite unconditionally on every PR (.github/workflows/ci.yml), and it passed at this head (job 101207095747). Not a defect — the legacy build is in fact green — but the handoff record understates what actually ran.
  • Diff is confined to src/config/** and docs/spec/IMPLEMENTATION_STATUS.md, matching Issue Implement the canonical configuration hierarchy (REQ-CONFIG-001) #64's declared scope. No secrets/credentials/machine paths in the diff. No test was deleted, disabled, or weakened.

The blocking defect

File: docs/spec/IMPLEMENTATION_STATUS.md. This pull request is CONFLICTING / mergeStateStatus: DIRTY against current master. gh pr merge 67 --squash --delete-branch fails with "the merge commit cannot be cleanly created."

Root cause: PR #66 merged after this branch was cut and flipped the REQ-CORE-002 row from IN_PROGRESS to IMPLEMENTED (evidence: PR #58, commit 002343805d14a4d3b43faf089aef9f7d52da9d4a), and recomputed the summary line to "7 of 25 requirements implemented; 0 in progress." This branch's copy of the file still has REQ-CORE-002 as IN_PROGRESS/pending, so the two edits to the same table collide.

What would satisfy it: merge or rebase onto current master, keep REQ-CORE-002 as IMPLEMENTED (per #66/#58, do not regress it back to IN_PROGRESS), keep the new REQ-CONFIG-001 row from this PR as IN_PROGRESS, and recompute the summary line to reflect both: 7 implemented, 1 in progress, 17 unmapped (7+1+17=25). Then push the update to this same branch and re-request review — per AGENTS.md, this run does not implement or push to the PR branch itself.

Set the linked Issue (#64) back to status:in-progress.

…fig-hierarchy

# Conflicts:
#	docs/spec/IMPLEMENTATION_STATUS.md
@drevendev

Copy link
Copy Markdown
Owner Author

Verdict: ACCEPT

(Posted as a comment because GitHub refuses a same-account formal review on this PR.)

Reviewed head 0631b5b417f4562acff53fe1dd9fff548174c5d1 independently (checked out locally, not trusting the PR body).

  1. Every required check is measured green at the head revision. build-and-test, typescript, policy-guard all pass at 0631b5b417f4562acff53fe1dd9fff548174c5d1 (gh pr checks 67). mergeStateStatus: CLEAN, mergeable: MERGEABLE — the merge conflict that blocked the prior head (58fc530b) is resolved; REQ-CORE-002 stays IMPLEMENTED per Reconcile REQ-CORE-002 status after merged PR #58 #66/Implement finite-number and deterministic-ordering primitives (REQ-CORE-002) #58, and the summary line correctly reads "7 of 25 implemented; 1 in progress" (7+1+17=25).
  2. Every acceptance criterion is met, with evidence I observed:
    • RunOptions, SimulationConfig, ScenarioDefinition, DefinitionPack in src/config/ match section 2 of docs/spec/mirror/06 - Handoff/03 — CANONICAL_CONFIG_AND_WORLD_GENERATION.md verbatim, including all 13 SimulationConfig sub-configs and all 12 ScenarioDefinition seed types as named placeholders.
    • assertNoBehavioralOverrides (src/config/validation.ts) correctly rejects behavioral-key smuggling (including the markets/clans array-vs-object collision) and unknown keys, while accepting a minimal well-formed scenario — read the implementation, not just the tests.
    • src/config/configLayers.typecheck.test.ts proves the four layers are non-interchangeable. I independently verified non-vacuousness myself: temporarily stripped one @ts-expect-error directive and reproduced the exact TS2739 failure the PR claims, then restored the file and re-confirmed a clean typecheck.
    • All four layers and the validator are exported from src/config/index.ts.
    • Ran independently at this head: npm ci, npm run typecheck (clean), npm test (13 files / 62 tests passed), npm run build (succeeded).
    • docs/spec/IMPLEMENTATION_STATUS.md gained the REQ-CONFIG-001 row as IN_PROGRESS, consistent with the legend (promotes to IMPLEMENTED only once merged).
  3. Diff is confined to declared scope: src/config/** (new) plus docs/spec/IMPLEMENTATION_STATUS.md, matching Issue Implement the canonical configuration hierarchy (REQ-CONFIG-001) #64. No .github/workflows/**, AGENTS.md, or docs/zendev/** touched. No TradeCraftSimulation/** file touched, so dotnet build/dotnet test are correctly not_run (this change cannot affect the legacy build).
  4. No invariant or test was weakened. All tests in this diff are new additions; nothing pre-existing was deleted or loosened.
  5. No secret, credential, or personal data anywhere in the diff (read the full diff).
  6. Handoff record is complete: outcome, Issue/revision, changed artifacts, acceptance criteria with evidence, check outcomes (including honest not_run for the legacy suite with a stated reason), assumptions/unknowns, highest-risk area, remaining gate (none).

Merging via the protected path.

@drevendev
drevendev merged commit 493da16 into master Sep 5, 2026
3 checks passed
@drevendev
drevendev deleted the claude/issue-64-config-hierarchy branch September 5, 2026 00:42
drevendev added a commit that referenced this pull request Sep 5, 2026
Adds deriveKeyedRandom, a pure function of (seed, tick, phase, key) with
no shared mutable state, so iteration order over a set of keys cannot
change any individual key's derived value. Also reconciles the
REQ-CONFIG-001 IMPLEMENTATION_STATUS row to IMPLEMENTED now that PR #67
merged as 493da16.

Closes #69

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant