Skip to content

refactor: Clean Code refactoring — DRY entity hierarchy, decompose WorldScreen, remove duplication, DI audit#351

Merged
dmccoystephenson merged 7 commits into
mainfrom
copilot/refactor-clean-code-principles
Apr 20, 2026
Merged

refactor: Clean Code refactoring — DRY entity hierarchy, decompose WorldScreen, remove duplication, DI audit#351
dmccoystephenson merged 7 commits into
mainfrom
copilot/refactor-clean-code-principles

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 20, 2026

Systematic Clean Code refactoring addressing DRY violations, long methods, redundant comments, silent error handling, and DI container compliance across the codebase.

Entity hierarchy DRY

Moved isSolid() and solid into DrawableEntity base class with solid=False default parameter. Eliminated identical isSolid()/self.solid boilerplate from 20+ entity subclasses.

# Before: every entity repeated this
class Stone(DrawableEntity):
    def __init__(self):
        DrawableEntity.__init__(self, "Stone", "assets/images/stone.png")
        self.solid = True
    def isSolid(self):
        return self.solid

# After: solid passed to base, isSolid() inherited
class Stone(DrawableEntity):
    def __init__(self):
        DrawableEntity.__init__(self, "Stone", "assets/images/stone.png", True)

WorldScreen decomposition (2122 → 1871 lines)

  • Extracted WorldScreenPersistence — save/load delegation for player location, attributes, inventory, rooms
  • Extracted pickupableEntitiescanBePickedUp() and PICKUPABLE_TYPES tuple
  • Decomposed long methods into focused helpers:
    • draw() 218→52, run() 156→16, changeRooms() 104→30, handleKeyDownEvent() 73→30
    • New: _drawDayNightOverlay, _drawHotbar, _drawDebugInfo, _processEvents, _updateLivingEntities, _updateGameState, _loadOrGenerateRoom, _calculateTargetLocationForRoomTransition, _handleMovementKey, _handleHotbarKey, _handleUtilityKey, _handleHotbarClick, _handleWorldClick, _plantWheatSeed, _tryHarvestCrop, _drawHotbarSelectionIndicator
  • Consolidated 3 identical cooldown methods into _checkCooldown(tickToCheck, speed)

Screen class DRY

  • ConfigScreen: Replaced 9 identical toggle methods + 9 copy-pasted drawButton blocks with data-driven _toggleConfigAttribute(attributeName) and a toggleButtons list
  • OptionsScreen: Added _switchToScreen(screenType) base method, data-driven menu button rendering

Comments and error handling

  • Removed ~40 redundant comments that restated the code; preserved @author/@since and why comments
  • Added _logger.warning() to 2 silent except OSError: pass blocks in config/keyBindings persistence, including exception details and file path in structured log context
  • Renamed highestmtpshighestMeasuredTps

DI container audit

Audited the codebase for dependency injection violations and eliminated manual instantiation of DI-registered types:

  • WorldScreenPersistence: Registered with @component decorator and added type hints to all constructor parameters. WorldScreen now resolves it via container.resolve() instead of manual construction.
  • MapRoomFactory: Map.__init__() now accepts an optional roomFactory parameter, injected via bootstrap.py. Falls back to manual construction when not provided (backward compatibility for tests).
  • MapRoomJsonReaderWriter: Map.__init__() now accepts an optional roomJsonReaderWriterFactory callable, injected via bootstrap.py using the same factory-callback pattern as RoomPreloader. Eliminates duplicate RoomJsonReaderWriter instantiation in Map.getRoom().

Robustness fixes from review feedback

  • _loadOrGenerateRoom() delegates disk-load to Map.getRoom() instead of duplicating logic
  • initialize() handles None from loadPlayerLocationFromFile() with spawn fallback
  • loadPlayerLocationFromFile() guards against getRoom() returning -1 and uses try/except KeyError for Grid.getLocation() since it raises KeyError on missing IDs

All 409 tests pass. ./format.sh clean.

Copilot AI and others added 4 commits April 20, 2026 16:13
…oggles and screen switches

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/5290e10f-d18d-4083-8927-097575ce6e81

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
- Delegate save/load to WorldScreenPersistence helper class
- Delegate canBePickedUp to pickupableEntities module
- Extract methods: _drawDayNightOverlay, _drawHotbar, _drawDebugInfo from draw()
- Extract methods: _processEvents, _updateLivingEntities, _updateGameState from run()
- Extract methods: _loadOrGenerateRoom, _calculateTargetLocationForRoomTransition from changeRooms()
- Extract _plantWheatSeed from executePlaceAction()
- Extract _handleMovementKey, _handleHotbarKey from handleKeyDownEvent()
- Extract _handleHotbarClick, _handleWorldClick from handleMouseDownEvent()
- Consolidate cooldown methods into _checkCooldown(tickToCheck, speed)
- Simplify handleMouseWheelEvent with modulo arithmetic
- Remove 14 unused entity imports and jsonschema import
- File reduced from 2122 to 1963 lines (-159 lines)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
…ing to except blocks, update CHANGELOG

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/5290e10f-d18d-4083-8927-097575ce6e81

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
…handleUtilityKey to remove elif pass

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/5290e10f-d18d-4083-8927-097575ce6e81

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors core gameplay screens and entity classes to remove duplication (DRY), decompose oversized methods in WorldScreen, and centralize persistence/pickup logic while also improving logging around previously silent failures.

Changes:

  • Centralized solid/isSolid() in DrawableEntity and removed redundant implementations across many entity subclasses.
  • Decomposed WorldScreen into focused helpers and extracted WorldScreenPersistence + pickupableEntities.
  • Refactored ConfigScreen/OptionsScreen to be data-driven, and replaced silent OSError handling with warnings.

Reviewed changes

Copilot reviewed 39 out of 39 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/world/room.py Removes redundant comments while preserving behavior.
src/screen/worldScreenPersistence.py New helper class encapsulating WorldScreen save/load logic.
src/screen/worldScreen.py Decomposes large methods; delegates persistence/pickup logic; adds helpers for overlay/hotbar/debug.
src/screen/pickupableEntities.py Centralizes pickupable-type checks into a shared tuple + helper.
src/screen/optionsScreen.py DRYs screen switching and button rendering.
src/screen/inventoryScreen.py Removes redundant comments; no functional change intended.
src/screen/configScreen.py Replaces many toggles with attribute-driven toggle list.
src/screen/codexScreen.py Formatting-only change to structured error log.
src/gameLogging/logger.py Formatting-only changes to logger configuration/constants.
src/entity/drawableEntity.py Adds solid constructor arg + base isSolid() implementation.
src/entity/food.py Renames ctor parameter to imagePath (clarity) while preserving behavior.
src/entity/apple.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/banana.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/bearMeat.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/bed.py Passes solid=True to base instead of local boilerplate.
src/entity/campfire.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/chickenMeat.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/coalOre.py Passes solid=True to base instead of local boilerplate.
src/entity/excrement.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/fence.py Passes solid=True to base instead of local boilerplate.
src/entity/grass.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/ironOre.py Passes solid=True to base instead of local boilerplate.
src/entity/jungleWood.py Passes solid=True to base instead of local boilerplate.
src/entity/leaves.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/matureCrop.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/oakWood.py Passes solid=True to base instead of local boilerplate.
src/entity/stone.py Passes solid=True to base instead of local boilerplate.
src/entity/stoneBed.py Passes solid=True to base instead of local boilerplate.
src/entity/stoneFloor.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/torch.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/wheat.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/wheatSeed.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/woodFloor.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/youngCrop.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/living/bear.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/entity/living/chicken.py Removes redundant solid/isSolid boilerplate (inherits default non-solid).
src/config/keyBindings.py Adds warning log on save failure (replaces silent pass).
src/config/config.py Adds warning log on window size save failure (replaces silent pass).
CHANGELOG.md Documents the refactor and adds learning-log entries.

Comment thread src/screen/worldScreen.py
Comment on lines +328 to +340
def _loadOrGenerateRoom(self, x, y):
room = self.map.getRoom(x, y)
if room == -1:
# attempt to load room if file exists, otherwise generate new room
nextRoomPath = (
self.config.pathToSaveDirectory
+ "/rooms/room_"
+ str(x)
+ "_"
+ str(y)
+ ".json"
if room != -1:
return room
nextRoomPath = self.persistence.buildRoomPath(x, y)
if os.path.exists(nextRoomPath):
roomJsonReaderWriter = RoomJsonReaderWriter(
self.config.gridSize, self.graphik, self.tickCounter, self.config
)
if os.path.exists(nextRoomPath):
roomJsonReaderWriter = RoomJsonReaderWriter(
self.config.gridSize, self.graphik, self.tickCounter, self.config
)
room = roomJsonReaderWriter.loadRoom(nextRoomPath)
self.map.addRoom(room)
self.currentRoom = room
self.status.set("Area loaded")
else:
x, y = self.getCoordinatesForNewRoomBasedOnPlayerLocationAndDirection()
self.currentRoom = self.map.generateNewRoom(x, y)
self.status.set("New area discovered")
self.stats.incrementScore()
self.stats.incrementRoomsExplored()
else:
self.currentRoom = room
room = roomJsonReaderWriter.loadRoom(nextRoomPath)
self.map.addRoom(room)
self.status.set("Area loaded")
return room
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In _loadOrGenerateRoom, self.map.getRoom(x, y) already loads a room from disk when the room JSON exists (see Map.getRoom). Because of that, the subsequent os.path.exists(nextRoomPath) + manual RoomJsonReaderWriter.loadRoom() block is effectively unreachable, and the "Area loaded" status message will never be shown when loading a saved room. Consider restructuring this helper to either (a) use map.hasRoom() to detect cached rooms and perform the disk-load here (so you can set status), or (b) move the status side-effect into Map.getRoom and remove the redundant disk-load logic here.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +56
roomX = jsonPlayerLocation["roomX"]
roomY = jsonPlayerLocation["roomY"]
currentRoom = mapInstance.getRoom(roomX, roomY)

locationId = jsonPlayerLocation["locationId"]
location = currentRoom.getGrid().getLocation(locationId)
currentRoom.addEntityToLocation(self.player, location)
return currentRoom
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadPlayerLocationFromFile() assumes mapInstance.getRoom(roomX, roomY) always returns a valid Room. If the room file is missing/corrupt and getRoom() returns -1, the next line will raise (no getGrid()). It should guard against -1 (and potentially a missing locationId in the grid) and return None (optionally logging a warning) so the caller can fall back to generating the starting room.

Copilot uses AI. Check for mistakes.
Comment thread src/config/config.py Outdated
Comment on lines +278 to +281
try:
configFilePath.write_text("\n".join(newLines) + "\n", encoding="utf-8")
except OSError:
pass
_logger.warning("failed to save window size to config file")
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new warning on except OSError drops the underlying exception details and the target path, which makes diagnosing save failures hard. Capture the exception (except OSError as e) and include error=str(e) and the config file path in the warning context.

Copilot uses AI. Check for mistakes.
Comment thread src/config/keyBindings.py Outdated
Comment on lines +163 to +166
try:
configFilePath.write_text("\n".join(newLines) + "\n", encoding="utf-8")
except OSError:
pass
_logger.warning("failed to save key bindings to config file")
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new warning on except OSError omits the exception details and the config file path, which reduces debuggability of persistence failures. Capture the exception (except OSError as e) and log error=str(e) plus the target configFilePath in the warning context.

Copilot uses AI. Check for mistakes.
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes.


Instructions

1. Discover all comments

  • Read every review thread, inline comment, and general PR comment
  • Include comments from: Copilot, human reviewers, and any other bots
  • Do not skip comments marked as suggestions — apply them if they make sense

2. For each comment, do the following:

  • Understand what is being requested or flagged
  • Locate the relevant file(s) and line(s)
  • Apply the fix directly in code
  • If a Copilot suggestion diff is provided, apply it (or an equivalent fix)
  • If the comment is ambiguous, use your best judgment and reply in the PR thread or review comment explaining what you did; only add a code comment when explicitly requested or when future maintainers would benefit from it
  • After addressing a comment, reply to the thread summarising what you did and mark it as resolved if your environment supports it

3. Types of issues to handle:

  • Code quality / style issues (naming, formatting, readability)
  • Logic bugs or edge cases flagged by reviewers
  • Security concerns (e.g. input validation, secrets, injection risks)
  • Performance suggestions
  • Missing or incomplete error handling
  • Test coverage gaps
  • Documentation / comment improvements
  • Dependency or import issues

4. Do NOT:

  • Dismiss or ignore any comment without acting on it — note that "acting" can also mean replying to the thread with a rationale (e.g., won't fix, already addressed elsewhere) and leaving the code unchanged when appropriate
  • Leave TODO comments as a substitute for fixing the issue
  • Break existing tests while addressing comments
  • Change functionality beyond the scope of what the comment asks for

After making changes

  • Ensure all existing tests still pass
  • Add or update tests if a comment explicitly asks for them, or if a bug fix warrants it
  • Double-check that every open thread has been addressed, replied to, and resolved

Context

  • This PR may contain Copilot-generated review comments — treat these with the same priority as human comments
  • If two comments conflict with each other, prefer the human reviewer's intent and document the conflict and resolution in the PR conversation or commit message, not in a code comment
  • Commit changes in logical groups if there are many unrelated fixes

…Room, guard loadPlayerLocationFromFile against missing room/location, include exception details in OSError warnings

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/cd904318-5d61-4103-b72a-0c69fdb38a9c

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 20, 2026

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes....

All four review threads have been addressed in commit d03c1b7:

  1. _loadOrGenerateRoom unreachable disk-load — Replaced map.getRoom() (which itself loads from disk) with map.hasRoom() for the cache-only check, so the manual disk-load path is now reachable and the "Area loaded" status message will display correctly.

  2. loadPlayerLocationFromFile missing guard — Added checks for getRoom() returning -1 and getLocation() returning None, both returning None with a warning log so the caller falls back to generating the starting room.

  3. config.py OSError warning — Now captures the exception as e and includes error=str(e) and path=str(configFilePath) in the structured log context.

  4. keyBindings.py OSError warning — Same fix as above.

All 409 tests pass and ./format.sh is clean.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated 3 comments.

Comment thread src/screen/worldScreen.py
Comment on lines +1571 to +1573
result = self.persistence.loadPlayerLocationFromFile(self.map)
if result is not None:
self.currentRoom = result
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadPlayerLocationFromFile() now treats a failed load as result is None (e.g., missing room/location), but initialize() only falls back to spawn when the file is absent. If the file exists but loading fails, WorldScreen.currentRoom may remain unset and subsequent initialization will crash. Consider making this method ensure a valid room/player placement (e.g., spawn fallback) when persistence returns None, or have initialize() handle the None case explicitly.

Copilot uses AI. Check for mistakes.
Comment thread src/screen/worldScreen.py Outdated
Comment on lines +328 to +337
def _loadOrGenerateRoom(self, x, y):
if self.map.hasRoom(x, y):
return self.map.getRoom(x, y)
nextRoomPath = self.persistence.buildRoomPath(x, y)
if os.path.exists(nextRoomPath):
roomJsonReaderWriter = RoomJsonReaderWriter(
self.config.gridSize, self.graphik, self.tickCounter, self.config
)
if os.path.exists(nextRoomPath):
roomJsonReaderWriter = RoomJsonReaderWriter(
self.config.gridSize, self.graphik, self.tickCounter, self.config
)
room = roomJsonReaderWriter.loadRoom(nextRoomPath)
self.map.addRoom(room)
self.currentRoom = room
self.status.set("Area loaded")
else:
x, y = self.getCoordinatesForNewRoomBasedOnPlayerLocationAndDirection()
self.currentRoom = self.map.generateNewRoom(x, y)
self.status.set("New area discovered")
self.stats.incrementScore()
self.stats.incrementRoomsExplored()
else:
self.currentRoom = room
room = roomJsonReaderWriter.loadRoom(nextRoomPath)
self.map.addRoom(room)
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_loadOrGenerateRoom() manually constructs a new RoomJsonReaderWriter and reimplements Map.getRoom()'s load-from-disk behavior. This duplicates existing logic and bypasses the already-resolved self.roomJsonReaderWriter, increasing the chance of behavior drift (e.g., if RoomJsonReaderWriter gains state/config). Prefer delegating to Map.getRoom() (which already loads+adds under the Map lock) and/or using self.roomJsonReaderWriter.loadRoom() rather than instantiating a new reader/writer here.

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +66
locationId = jsonPlayerLocation["locationId"]
location = currentRoom.getGrid().getLocation(locationId)
if location is None:
_logger.warning(
"saved location not found in room, falling back to spawn",
locationId=locationId,
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WorldScreenPersistence.loadPlayerLocationFromFile() calls Grid.getLocation(locationId) and then checks if location is None, but Grid.getLocation() indexes the dict and raises KeyError when the ID is missing (it never returns None). This means corrupted/mismatched location IDs will currently crash before the warning/fallback path. Handle missing IDs via try/except KeyError (or use a safe accessor) and then return None so WorldScreen can fall back to spawn cleanly.

Copilot uses AI. Check for mistakes.
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes.


Instructions

1. Discover all comments

  • Read every review thread, inline comment, and general PR comment
  • Include comments from: Copilot, human reviewers, and any other bots
  • Do not skip comments marked as suggestions — apply them if they make sense

2. For each comment, do the following:

  • Understand what is being requested or flagged
  • Locate the relevant file(s) and line(s)
  • Apply the fix directly in code
  • If a Copilot suggestion diff is provided, apply it (or an equivalent fix)
  • If the comment is ambiguous, use your best judgment and reply in the PR thread or review comment explaining what you did; only add a code comment when explicitly requested or when future maintainers would benefit from it
  • After addressing a comment, reply to the thread summarising what you did and mark it as resolved if your environment supports it

3. Types of issues to handle:

  • Code quality / style issues (naming, formatting, readability)
  • Logic bugs or edge cases flagged by reviewers
  • Security concerns (e.g. input validation, secrets, injection risks)
  • Performance suggestions
  • Missing or incomplete error handling
  • Test coverage gaps
  • Documentation / comment improvements
  • Dependency or import issues

4. Do NOT:

  • Dismiss or ignore any comment without acting on it — note that "acting" can also mean replying to the thread with a rationale (e.g., won't fix, already addressed elsewhere) and leaving the code unchanged when appropriate
  • Leave TODO comments as a substitute for fixing the issue
  • Break existing tests while addressing comments
  • Change functionality beyond the scope of what the comment asks for

After making changes

  • Ensure all existing tests still pass
  • Add or update tests if a comment explicitly asks for them, or if a bug fix warrants it
  • Double-check that every open thread has been addressed, replied to, and resolved

Context

  • This PR may contain Copilot-generated review comments — treat these with the same priority as human comments
  • If two comments conflict with each other, prefer the human reviewer's intent and document the conflict and resolution in the PR conversation or commit message, not in a code comment
  • Commit changes in logical groups if there are many unrelated fixes

…-load to Map.getRoom(), use try/except KeyError for Grid.getLocation()

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/f372a9ab-2bfb-4fc0-bc55-ae19ea0affc2

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 20, 2026

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes....

All three review threads from round 2 addressed in commit c29ccdd:

  1. initialize() spawn fallbackself.currentRoom is now initialized to None and the spawn logic uses if self.currentRoom is None instead of else, so corrupt/failed loads properly fall back to spawning at (0, 0).

  2. _loadOrGenerateRoom() duplication — Removed the duplicate RoomJsonReaderWriter instantiation and delegated all disk-load logic to Map.getRoom() which handles loading under the lock. Uses hasRoom() pre-check only to detect cache vs disk-load for the status message.

  3. loadPlayerLocationFromFile() KeyError — Replaced if location is None with try/except KeyError since Grid.getLocation() raises KeyError on missing IDs, not returns None.

All 409 tests pass and ./format.sh is clean.

@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot Review code base and ensure that the dependency injection approach using the DI container is being followed when it makes sense and ensure that we are not manually instantiating objects when we should not be

…mFactory and RoomJsonReaderWriter factory into Map via bootstrap

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/ac4c7133-3d2d-4af8-abdb-bcb5bda08c38

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 20, 2026

@copilot Review code base and ensure that the dependency injection approach using the DI container is being followed when it makes sense and ensure that we are not manually instantiating objects when ...

Audited the codebase for DI violations and fixed 3 issues in commit 46c5ddf:

  1. WorldScreenPersistence (new class from this PR) — Added @component decorator and type hints to all constructor parameters. WorldScreen now resolves it via self.container.resolve(WorldScreenPersistence) instead of manual instantiation.

  2. Map.roomFactoryMap.__init__ now accepts an optional roomFactory: RoomFactory parameter, wired through bootstrap.py. Falls back to manual construction when not provided (backward compat for tests).

  3. Map.getRoom() RoomJsonReaderWriterMap.__init__ now accepts an optional roomJsonReaderWriterFactory callable, wired through bootstrap.py as lambda: container.resolve(RoomJsonReaderWriter). getRoom() uses the factory when available, falls back to manual construction otherwise.

No other DI violations found — all other container.resolve() usages and @component decorations are correct across the codebase.

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes....

All three review threads from round 2 were already addressed in commit c29ccdd. The subsequent DI audit (commit 46c5ddf) addressed the additional request to ensure proper DI container usage across the codebase.

Copilot AI changed the title refactor: Clean Code refactoring — DRY entity hierarchy, decompose WorldScreen, remove duplication refactor: Clean Code refactoring — DRY entity hierarchy, decompose WorldScreen, remove duplication, DI audit Apr 20, 2026
@dmccoystephenson dmccoystephenson merged commit 2dca41f into main Apr 20, 2026
1 check passed
@dmccoystephenson dmccoystephenson deleted the copilot/refactor-clean-code-principles branch April 20, 2026 18:41
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.

3 participants