Skip to content

fix(sim): gcEvents drops events still referenced by modifiers/factions/subjects - #40

Merged
csheldrick merged 1 commit into
mainfrom
claude/galimulator-ng-improvement-g759h0
Jul 15, 2026
Merged

csheldrick merged 1 commit into
mainfrom
claude/galimulator-ng-improvement-g759h0

Conversation

@csheldrick

Copy link
Copy Markdown
Owner

Summary

gcEvents() in src/sim/Events.ts swept state.events using a "referenced" set built only from eventLog, per-empire history, per-system recent events, and dynasty chronicle. Three other live pointers into state.events were never counted:

  • RelationModifier.sourceEventId
  • Faction.historicalEventIds
  • SubjectRelation.historicalEventIds

The GC sweep runs every 128 events created, but a grievance modifier stays active for up to 3600 ticks — far longer than 128 events typically takes to elapse in a running galaxy. So the modifier's source event was almost always purged long before the modifier itself expired, silently corrupting:

  • summarizeGrievances()'s oldestAgeTicks (a balance-tuning read-out surfaced in the headless report and in-app grievance summary), and
  • the "from [tick] Title" annotation on grievances in the exported report (buildReport() in App.tsx), which just silently disappears once the source event ages out.

Confirmed empirically: at the same seed/tick count, oldestAgeTicks at tick 3000 went from 304 (before fix) to 637 (after fix) — matching the predicted corruption.

Changes

  • Extend gcEvents()'s referenced-set collection to include RelationModifier.sourceEventId (via each empire's relationship modifiers), Faction.historicalEventIds, and SubjectRelation.historicalEventIds, before sweeping unreferenced events.
  • Add src/sim/Events.test.ts covering all three previously-missed cases, plus a baseline test that true orphans are still collected.
  • Record a Noet constraint capturing the durable rule this bug uncovered: any new Id reference into state.events must be added to gcEvents()'s referenced set.

Test plan

  • npm run typecheck / npm run build
  • npm run lint
  • npm run test:unit (9/9 passing, including 4 new tests in Events.test.ts)
  • Verified all 3 targeted regression tests fail against the pre-fix code and pass against the fix
  • npm run report (headless health check, 1000/3000-tick milestones) — passes, and oldestAgeTicks now reflects the fix
  • Determinism replay check passes (npm run report --milestones 500, byte-identical replay)

Generated by Claude Code

…factions/subjects

gcEvents() only tracked references from eventLog, empire history, system
history, and dynasty chronicle before sweeping state.events. It missed three
other live pointers into state.events: RelationModifier.sourceEventId,
Faction.historicalEventIds, and SubjectRelation.historicalEventIds. Since
grievance modifiers can stay active for 3600 ticks — far longer than the
128-event GC interval — their source event was almost always purged well
before the modifier expired, silently corrupting summarizeGrievances()'s
oldestAgeTicks (a balance-tuning signal) and dropping the "from [tick] Title"
annotation in the exported report.

Extends gcEvents' referenced-set collection to include those three pointers
before sweeping. Adds Events.test.ts covering all three previously-missed
cases plus the baseline "still collects true orphans" behavior.
@csheldrick
csheldrick merged commit 0f26109 into main Jul 15, 2026
2 checks passed
@csheldrick
csheldrick deleted the claude/galimulator-ng-improvement-g759h0 branch July 15, 2026 15:26
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.

2 participants