Skip to content

Give the entity log cap a single source of truth - #44

Merged
dmccoystephenson merged 2 commits into
masterfrom
feature/single-source-entity-log-cap
Aug 7, 2026
Merged

Give the entity log cap a single source of truth#44
dmccoystephenson merged 2 commits into
masterfrom
feature/single-source-entity-log-cap

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

  • The per-entity log cap was declared in two places: as a default parameter on LivingEntity.addLogEntry (maxLogSize=50) and as Config.entityLogMaxSize. Of every call site, only Kreatures.createChildEntity passed the configured value, so the setting was effectively inert and the two values would have diverged the moment either was changed.
  • The cap is now owned by the entity and fixed at construction: LivingEntity(name, maxLogSize=DEFAULT_LOG_MAX_SIZE) builds self.log as an already-bounded deque, and addLogEntry(message) is a bare O(1) append with no cap argument. The isinstance/maxlen check that would have rebuilt the deque on nearly every call has been dropped, since divergence is no longer possible.
  • DEFAULT_LOG_MAX_SIZE is defined once, in src/entity/livingEntity.py, and Config.entityLogMaxSize is initialised from it. This adds one import from config to entity; it is what makes "one source of truth" literally true rather than two literals that happen to agree.
  • The configured cap is now propagated to every entity the game creates — the world's ten starter creatures (World.__init__ takes maxLogSize), the player creature, creatures spawned by createEntity, and children from createChildEntity. Config is constructed first in Kreatures.__init__ so the world can be given the value.
  • The stray src/stats/__init__ copy.py has been deleted. It was byte-identical to src/stats/__init__.py, is not importable (the space makes the name invalid as a module identifier), and is referenced by nothing.

Test plan

  • python3 -m pytest --verbose -vv --cov=src --cov-report=term-missing — 66 passed (65 before; one test added).
  • python3 -m compileall -q src — clean.
  • tests/test_lag_prevention.py::TestPerformanceOptimizations::test_config_log_cap_is_the_only_source_of_truth was added as the regression guard: a Config with entityLogMaxSize = 7 is injected into Kreatures, and every entity — player, starters, spawned, child — is asserted to carry log.maxlen == 7, with enforcement checked by overflowing the log. This test fails against the previous code, where entities built without the config value kept the stale default of 50.
  • The two existing tests that passed a per-call maxLogSize were updated to set the cap at construction instead; their assertions were kept intact rather than weakened.
  • A 60-tick end-to-end play-through of src/kreatures.py was run locally with mocked input; the simulation completed and printed its summary, confirming that log being a deque from construction is compatible with the log[0] / del log[0] handling in Kreatures.run.

Deferred this cycle

The remaining open issues were not picked up, for these reasons:

Notes

Pre-existing black drift in the touched files (single-quoted strings, trailing whitespace on blank lines) was deliberately left alone so that this diff stays scoped to the two issues; it belongs in its own formatting-only sweep.

Closes #40
Closes #43

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

dmccoystephenson and others added 2 commits August 7, 2026 02:00
The per-entity log cap was declared twice: as a default parameter on
LivingEntity.addLogEntry and as Config.entityLogMaxSize. Only one call
site passed the configured value, so changing the setting from 50 would
have made the two disagree and rebuilt the bounded deque on nearly every
append -- an O(n) copy on the hot path the bounded log exists to keep
O(1).

The cap now belongs to the entity and is fixed at construction, and the
config default reads the entity module's constant. Kreatures passes the
configured cap to the world, the player creature, spawned creatures and
children, so the setting reaches every entity in the game.

Also removes src/stats/__init__ copy.py, a byte-identical editor artifact
that nothing imports.

Closes #40
Closes #43

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The deque's maxlen already carries the cap; a parallel attribute on
LivingEntity and World was a second place for the same value to live,
which is the duplication this change set exists to remove. Neither field
was read anywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review rubric

Scored against the local pytest run (66 passed) and the diff, adversarially — assume FAIL until evidenced.

  • Scope: PASS — six files are touched; five carry Config.entityLogMaxSize is honored at only one call site #40 (config.py, livingEntity.py, world.py, kreatures.py, test_lag_prevention.py) and one deletion carries Remove the stray 'src/stats/__init__ copy.py' duplicate #43. black --check still reports the same pre-existing drift in these files as it does on master, confirming no incidental reformatting was folded in.
  • Tests-new: PASS — addLogEntry lost a parameter rather than gaining a public method, and the changed LivingEntity.__init__ signature is exercised by test_entity_log_size_management (explicit cap of 10 at construction) and by the new test_config_log_cap_is_the_only_source_of_truth (cap of 7 threaded through the whole game).
  • Tests-fix: PASS — confirmed empirically, not by reasoning. With git checkout origin/master -- src/ applied, pytest tests/test_lag_prevention.py reports 3 failed / 10 passed, the new test failing on assert 50 == 7; with the branch's src/ restored, all 66 pass. The sentinel cap of 7 is what makes the failure observable — 50 would have been indistinguishable from the default.
  • Sibling structure: PASS — no new files are added.
  • Sibling renames: PASS — grep -rn "addLogEntry\|LivingEntity(" was used to confirm that every call site and every construction site was updated in the same commit; no caller is left passing the removed parameter.
  • Docs: PASS — the documentation sources of truth were checked against this diff. README.md makes no claim this change affects; the architecture and file-structure bullets in .github/copilot-instructions.md remain accurate (src/stats/ still holds __init__.py and stats.py); COPYRIGHT.md and src/config/names.json are untouched.
  • Issue resolution: PASS — Config.entityLogMaxSize is honored at only one call site #40's named surface is gone (addLogEntry no longer takes a cap, and Config.entityLogMaxSize is no longer a second declaration of 50); Remove the stray 'src/stats/__init__ copy.py' duplicate #43's file no longer exists.
  • Local test suite: PASS — python3 -m pytest --verbose -vv --cov=src --cov-report=term-missing reports 66 passed on the PR head. python3 -m compileall -q src is clean.
  • Copyright header: PASS — every touched .py file retains its # Copyright (c) 2022 Daniel McCoy Stephenson / # Apache License 2.0 lines.
  • Indentation: PASS — all added lines use 4 spaces, matching the actual source (the tab claim in .github/copilot-instructions.md is the known drift tracked as copilot-instructions.md documents tab indentation, but the codebase uses 4 spaces #42).
  • Names-config sync: not applicable — src/config/names.json was not modified.

Findings

  • src/config/config.py:3 — the new from entity.livingEntity import DEFAULT_LOG_MAX_SIZE points the config package at the entity package, which is the reverse of the direction dependencies normally run here. It is a deliberate trade: without it the cap would still be written as the literal 50 in two files, and "one source of truth" would hold only by coincidence. Should the coupling be judged worse than the duplication, the alternative is to drop the import and let Config.entityLogMaxSize carry the literal, since the per-call divergence — the actual defect in Config.entityLogMaxSize is honored at only one call site #40 — is fixed either way by the constructor change alone.
  • src/entity/livingEntity.py:23, src/world/world.py:12 — caught during this review and fixed in b746762: a self.maxLogSize field was being stored on both classes and read by nothing. The deque's maxlen already carries the cap, so the field was a second home for the same value inside a change whose whole purpose is to remove one.
  • tests/test_lag_prevention.py:184 — the retained assertion is <= where == now holds exactly. It was left alone rather than tightened, since strengthening an assertion unrelated to either issue would widen this diff; it is noted here rather than changed.
  • Behavioural note, src/entity/livingEntity.py:24entity.log is now a deque from construction, where previously it was a list that was converted on the first addLogEntry call. Kreatures.run both indexes (log[0]) and deletes (del log[0]) from it; both are supported by deque, and this was checked beyond the unit tests by running a 60-tick play-through of src/kreatures.py locally with mocked input, which completed and printed its summary.

This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

@dmccoystephenson
dmccoystephenson merged commit e15972a into master Aug 7, 2026
1 check passed
@dmccoystephenson
dmccoystephenson deleted the feature/single-source-entity-log-cap branch August 7, 2026 08:04
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.

Remove the stray 'src/stats/__init__ copy.py' duplicate Config.entityLogMaxSize is honored at only one call site

1 participant