Skip to content

REQ-CONFIG-004: population genesis reconciliation drops typed region identity #452

Description

@drevendev

Goal

Restore location-sensitive population opening-stock reconciliation without changing any population quantity, demographic mechanic, Clan relationship, or phase behavior.

Evidence

Canonical Handoff/03 defines CohortSeed.regionKey and records POPULATION_ENDOWMENT with a typed regionId; the world-generation contract also states that every cohort belongs to one Region and one Clan. REQ-CONFIG-004 requires opening-stock reconciliation to prove the actual constructed stocks, not only aggregate totals.

On current master (25065757889f2db3513aa472c67d8e982761a6ae), src/domain/genesisLedger.ts preserves POPULATION_ENDOWMENT.regionId, but src/simulation/genesisReconciliation.ts ignores it. Expected population is keyed only by sourceSeedKey:

const granularity = `POP:${record.sourceSeedKey}`;

Actual population is reconstructed from the cohort seed key:

const granularity = `POP:${cohort.seed.key}.population`;

Therefore a POPULATION_ENDOWMENT with the wrong typed regionId can still reconcile as long as sourceSeedKey and amount are unchanged. This is a distinct typed-location false positive from #448's cohort-owner defect: #448 repairs who owns the population stock; this finding repairs the Region identity carried by that stock record.

Scope

Make the smallest CONFIG-004 repair:

Non-goals

  • No change to population amounts, births/deaths, migration, social mobility, Clan derivation, or phase order.
  • No duplicate Clan population stock.
  • No generic WorldState population container.
  • No specification-mirror edit; Drive remains specification authority.

Acceptance criteria

  1. POPULATION_ENDOWMENT reconciliation verifies canonical cohort owner and typed Region identity, not only sourceSeedKey.
  2. A wrong-region negative control fails while source key, owner, amount, and aggregate population remain unchanged.
  3. Correct baseline genesis continues to pass within canonical population/reconciliation tolerance.
  4. REQ-CONFIG-004 / REQ-MARKET-005: cohort opening stocks are mis-owned by Clan in genesis ledger #448's cohort-owner repair and this Region-identity repair share one canonical population-stock key/model.
  5. docs/spec/implementation_status.csv does not claim CONFIG-004 is fully proved until both owner and Region identity gaps are repaired and evidenced.
  6. Required TypeScript and retained .NET gates remain green.

Verification

Run the focused genesis-reconciliation tests, including the wrong-region population negative control, then the repository-required TypeScript typecheck/test/build and retained .NET gates.

Activity

  1. added
    priority:highImportant and time-sensitive; schedule ahead of normal work
    type:bugVerified behavior differs from the intended contract
    status:readySpecified and unblocked; safe for an agent to claim
    on Sep 13, 2026
  2. added a commit that references this issue on Sep 14, 2026
  3. zendev-author commented on Sep 14, 2026

    @zendev-author
    Contributor

    AUTHOR claim

    Role: AUTHOR

    Selection path (AUTHOR_RUNBOOK.md §2):

    Intended scope: key cohort POPULATION_ENDOWMENT genesis reconciliation by canonical cohort owner plus typed Region identity in src/simulation/genesisReconciliation.ts, deriving actual Region identity from canonical cohort state/seed rather than sourceSeedKey text, keeping sourceSeedKey as provenance only. Add a focused negative control proving that changing only POPULATION_ENDOWMENT.regionId fails reconciliation while owner, source key and aggregate population are unchanged. Update the REQ-CONFIG-004 ledger row truthfully in the same pull request.

    Dependency check: acceptance criterion 4 requires sharing one canonical population-stock key with #448's cohort-owner repair. #448 landed as PR #459 (11bd421); genesisReconciliation.ts already keys population as POP:${serializeOwner(...)}, so this repair extends that same key rather than introducing a parallel convention.

    Branch: claude/issue-452-population-region-identity

    Known blockers: none identified.

  4. 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 14, 2026
  5. zendev-author commented on Sep 14, 2026

    @zendev-author
    Contributor

    AUTHOR handoff

    Branch: claude/issue-452-population-region-identity
    Pull request: #471
    Tested revision: a8349336310508cbb6896d082893cb89f08923b4 (branch head; all checks re-run at this revision after the ledger commit)

    What changed: src/simulation/genesisReconciliation.ts now keys cohort population by POP:<cohort owner>:<region> on both sides instead of by owner alone. The expected side reads the record's own typed regionId; the actual side resolves the cohort's Region through the region registry from CohortSeed.regionKey — the same canonical relation and the same lookup the goods projection already uses — so sourceSeedKey stays provenance only. This extends the single population key PR #459 introduced rather than adding a parallel convention (criterion 4).

    Negative control (criterion 2): genesisReconciliation.test.ts → "fails when a cohort's population record names another region while owner, source key and total are unchanged". It asserts the unmodified genesis reconciles, asserts the relabelled record's owner/sourceSeedKey/amount are identical and that total recorded population is unchanged, then asserts failure with category === "POPULATION". Proved sensitive by stashing genesisReconciliation.ts back to its master content with the test in place: exactly that test fails (1 failed | 29 passed); restoring the repair returns 30/30.

    Checks: npm ci passed; npm run typecheck passed; npm test passed (590/590, 40 files, 1 new); npm run build passed; dotnet restore passed; dotnet build --configuration Release --no-restore passed; dotnet test --configuration Release --no-build passed (45/45, REQ-MIGRATION-003 maintained); python scripts/implementation_status.py --check passed; python scripts/status_lint.py --self 471 --base master passed; python scripts/policy_guard.py --base master passed. The forge-run required checks had not reported when this was written — those outcomes are local runs at the tested revision, not a claim about CI.

    Ledger: the REQ-CONFIG-004 row is updated in place (ISSUE 452, PR 471, MERGE_COMMIT left empty for backfill_merge_commits.py, EVIDENCE extended). STATUS deliberately stays PARTIAL per criterion 5: sibling Issue #455 (ProductionUnit GOOD_ENDOWMENT INPUT/OUTPUT/INVESTMENT bucket identity, spec changelog HANDOFF-REPAIR-017) is still open, so the requirement is not yet provable at full typed-identity granularity.

    Decision recorded: Handoff/03 section 20 spells the owner+region reconciliation rule out explicitly for ProductionUnit-owned GOOD_ENDOWMENT but not separately for POPULATION_ENDOWMENT. I read the typed-identity principle ("sourceSeedKey is provenance only and must never substitute for typed stock identity") as general, consistent with how PRs #467 and #469 treated goods and resources, and with this Issue's own Evidence. If the researcher intends population to be deliberately Region-blind, the remedy is a spec question rather than a revert; no mirror file was read beyond registry, changelog, EXECUTION_ORDER and the single named domain document, and none was edited.

    No out-of-scope work was smuggled in: the diff is two source files plus the two ledger files. No new Issue was needed — no out-of-scope defect was discovered.

    Remaining gate: none for this Issue. Setting status:needs-review. This run does not approve and does not merge.

  6. zendev-acceptor commented on Sep 14, 2026

    @zendev-acceptor
    Contributor

    ACCEPTOR: accepted and merged

    PR #471 was reviewed at head a8349336310508cbb6896d082893cb89f08923b4 and merged as squash commit d3c354171db642ce28218cb3b108e9eeccd82f29 (2026-09-14T21:24:36Z). Pull request state confirmed MERGED before this note was written; this Issue closed automatically as COMPLETED via Closes #452.

    Verdict: #471 (comment)

    Evidence observed at the accepted revision

    Required checks, read from the checks tab at a834933: mergeability success (measured after master's then-head c2f1fd8; branch 0 commits behind, no conflict), typescript success, build-and-test success, policy-guard success. The one superseded policy-guard failure earlier at the same head was a scope_guard refusal of a placeholder body, cured by writing the handoff and re-running with no new commit — verified from run timestamps, not taken on the body's word.

    Independent re-execution of both runtimes at a834933: npm ci passed, npm run typecheck passed, npm test passed (590/590 across 40 files), npm run build passed, dotnet build --configuration Release passed (0 warnings, 0 errors), dotnet test --configuration Release passed (45/45, REQ-MIGRATION-003 maintained).

    All six acceptance criteria met. The key change: both sides of population reconciliation now key on POP:${serializeOwner(...)}:${serializeRegion(...)}, with the actual side resolving the cohort's Region through regionIdByRegionKey from CohortSeed.regionKey rather than from seed-key text, so sourceSeedKey stays provenance only per Handoff/03 §20.

    The negative control's sensitivity was verified independently and more tightly than the pull request claimed: neutralizing only the region segment on both sides, changing nothing else, fails exactly one test — "fails when a cohort's population record names another region while owner, source key and total are unchanged" (1 failed | 29 passed in that file). The control is sensitive to the region segment in isolation, closing the residual risk the AUTHOR had honestly flagged as unproven.

    Status of REQ-CONFIG-004

    Deliberately still PARTIAL in docs/spec/implementation_status.csv, and correctly so. This Issue's typed-Region gap for POPULATION_ENDOWMENT is closed, but sibling defect #455 (ProductionUnit GOOD_ENDOWMENT INPUT/OUTPUT/INVESTMENT bucket identity, spec changelog entry HANDOFF-REPAIR-017) remains open and is out of this Issue's scope by its own Non-goals. The requirement is not yet provable at full typed-identity granularity, and the ledger says so rather than overclaiming.

    The row's MERGE_COMMIT was left empty by the authoring run because it is unknowable before merge; scripts/backfill_merge_commits.py records d3c3541 for it, as it did for the preceding rows.

    Removing status:needs-review: a closed Issue carries no active status:* label — the forge's closed state and its COMPLETED reason are the durable resolution.

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:configpriority:highImportant and time-sensitive; schedule ahead of normal worktype:bugVerified behavior differs from the intended contract

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions