Repository navigation
Restore statistics tracking with verified Qt lifecycle - #44
ViciousSquid merged 13 commits into
Conversation
📝 WalkthroughWalkthroughStatistics tracking is centralized in ChangesCanonical statistics migration
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Gameplay
participant TamagotchiLogic
participant SquidStatistics
participant StatisticsTab
Gameplay->>TamagotchiLogic: report gameplay event or movement
TamagotchiLogic->>SquidStatistics: update canonical statistics
TamagotchiLogic->>StatisticsTab: refresh synchronized display
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (8)
src/squid_statistics.py (2)
123-134: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider having
update_distancedelegate here.
add_distancehandles multi-rollover correctly viadivmod, while the olderupdate_distanceonly subtracts one limit. Routingupdate_distancethroughadd_distance(math.sqrt(dx*dx + dy*dy))removes the duplicate rollover logic and the behavioral divergence between the two entry points.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/squid_statistics.py` around lines 123 - 134, Update update_distance to calculate the Euclidean distance and delegate to add_distance, removing its duplicate rollover handling. Preserve the existing input behavior while ensuring multi-rollover accumulation uses add_distance’s divmod logic.
17-64: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAnnotate the lookup tables as
ClassVarto silence RUF012.These are immutable-intent lookup tables;
ClassVardocuments that and clears the three Ruff warnings.♻️ Proposed annotation
+from typing import ClassVar, Dict + class SquidStatistics: @@ - _PERSISTENCE_KEYS = { + _PERSISTENCE_KEYS: ClassVar[Dict[str, str]] = { @@ - _EVENT_KEYS = { + _EVENT_KEYS: ClassVar[Dict[str, str]] = { @@ - _NEURON_BIRTH_KEYS = { + _NEURON_BIRTH_KEYS: ClassVar[Dict[str, str]] = {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/squid_statistics.py` around lines 17 - 64, Annotate the class-level lookup tables _PERSISTENCE_KEYS, _EVENT_KEYS, and _NEURON_BIRTH_KEYS as ClassVar with their existing dictionary value types. Add the required ClassVar import if needed, while preserving all table contents and behavior.Source: Linters/SAST tools
tests/test_squid_statistics.py (1)
81-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for
add_distancerollover andincrementlegacy-name mapping.Both are new/reworked public APIs in this layer with branch logic (multi-rollover via
divmod, unknown-nameFalsereturn) and no test here.load_statistics/to_dictround-tripping a partial payload is also worth a case unless it is fully covered by the persistence tests in the later layer.💚 Suggested additional cases
def test_add_distance_rolls_over_multiple_times(self): from src.squid_statistics import DISTANCE_ROLLOVER_LIMIT self.statistics.add_distance(DISTANCE_ROLLOVER_LIMIT * 2 + 5) self.assertEqual(self.statistics.distance_swam, 5) self.assertEqual(self.statistics.distance_swam_multiplier, 3) def test_add_distance_rejects_invalid_values(self): with self.assertRaises(ValueError): self.statistics.add_distance(-1) with self.assertRaises(ValueError): self.statistics.add_distance(float("inf")) def test_increment_maps_legacy_names_and_reports_unknown(self): self.assertTrue(self.statistics.increment("cheese_eaten")) self.assertEqual(self.statistics.cheese_consumed, 1) self.assertFalse(self.statistics.increment("not_a_real_event"))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_squid_statistics.py` around lines 81 - 89, Add tests covering the public APIs in the statistics test suite: verify add_distance handles multiple DISTANCE_ROLLOVER_LIMIT rollovers, rejects negative and non-finite values, and verify increment maps the legacy cheese_eaten name while returning False for unknown event names. Add a load_statistics/to_dict partial-payload round-trip case only if persistence tests do not already cover it.tests/test_statistics_tab_lifecycle.py (2)
74-81: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
FakeSquidomits the core stat attributes.It only defines
anxiety,happiness,satisfaction,is_sleeping,is_sick. That is enough for the statistics path, but any future assertion that reachesSquidBrainWindow.sync_state_from_squid(which readshunger,cleanliness,sleepiness,curiosity,squid_x/y) will fail withAttributeErrorrather than a meaningful assertion. Worth adding the missing defaults now.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_statistics_tab_lifecycle.py` around lines 74 - 81, Update the FakeSquid fixture to define default values for hunger, cleanliness, sleepiness, curiosity, squid_x, and squid_y alongside its existing attributes. Ensure these defaults satisfy SquidBrainWindow.sync_state_from_squid so future assertions fail on behavior rather than missing attributes.
89-298: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffOptional: split this into three tests (initial bind / live event refresh / unbind+rebind).
At ~210 lines it exercises four distinct phases behind one name, so a failure in the rebind assertions reports as a failure of "late logic wiring". A shared helper that builds the patched window plus one
logicfactory would keep the setup cost identical.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_statistics_tab_lifecycle.py` around lines 89 - 298, Split test_late_logic_wiring_projects_model_statistics_into_real_tab into focused tests for initial binding, live event refresh, and unbind/rebind behavior. Extract the repeated patched-window setup and TamagotchiLogic construction into shared helpers, preserving the existing assertions and setup behavior while giving each phase its own descriptive test name.src/brain_statistics_tab.py (2)
267-270: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueNit: "Max Neurons" is the only unlocalised row in the export.
Every other line uses
loc.get(...). Consider adding a key (e.g.stat_max_neurons) and reusing it for the label at Line 153 too.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/brain_statistics_tab.py` around lines 267 - 270, Localize the “Max Neurons” export label in the statistics export code by adding and using a localization key such as stat_max_neurons through loc.get(...). Reuse the same key for the corresponding label near the existing line-153 display so both occurrences remain consistent with the other localized rows.
187-194: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueOptional: gate the 1 Hz refresh on
is_visible.
is_visibleis now tracked inshowEvent/hideEventbut never read, so the timer keeps re-syncing and repainting 17 labels while the tab is hidden. Cheap, but the flag suggests the intent was to skip hidden updates.♻️ Proposed change
def update_statistics(self): """Refresh the UI from the canonical squid statistics model.""" if not self.tamagotchi_logic or not self.tamagotchi_logic.squid: return self._sync_from_squid_statistics() self.statistics['last_update_time'] = time.time() - self.update_display() + if self.is_visible: + self.update_display()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/brain_statistics_tab.py` around lines 187 - 194, Optionally update update_statistics to return early when is_visible is false, while preserving the existing tamagotchi_logic and squid guard. This should prevent _sync_from_squid_statistics, timestamp updates, and update_display from running while the tab is hidden; continue the current refresh flow when the tab is visible.tests/test_statistics_persistence.py (1)
15-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueGood schema-lock test. Mirroring the mapping as a literal and asserting equality means any accidental rename or dropped persisted field fails loudly. Note it does couple the suite to the private
_PERSISTENCE_KEYS; if the model ever exposes a public accessor, prefer that here and at Line 400.Also applies to: 91-107
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_statistics_persistence.py` around lines 15 - 44, The schema-lock test currently depends directly on the private _PERSISTENCE_KEYS mapping. If a public accessor for the persistence schema exists or is introduced, update EXPECTED_PERSISTENCE_KEYS and the corresponding assertions around the additional referenced range to use that public accessor instead, while preserving the exact mapping equality check.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/squid_statistics.py`:
- Around line 209-216: Update the invalid-event branch in the increment method
to emit a debug-level log identifying the unknown event_name before returning
False. Reuse the class’s existing logger or logging path, and leave valid event
increments and return values unchanged.
- Around line 285-300: The neurogenesis peak metrics are never updated before
persistence. Add observation logic for peak_novelty, peak_stress, and
peak_reward at the neuron creation/output point, updating each persisted peak
with the corresponding neuron value while preserving existing initialization and
persistence behavior.
- Around line 171-188: Update load_statistics’s _PERSISTENCE_KEYS assignment to
defensively coerce each loaded persistence value to the appropriate numeric
type, falling back to its existing model default when the value is None or
non-numeric. Preserve the current_neurons and max_neurons_reached handling while
ensuring all canonical counters are numeric before setattr persists them.
---
Nitpick comments:
In `@src/brain_statistics_tab.py`:
- Around line 267-270: Localize the “Max Neurons” export label in the statistics
export code by adding and using a localization key such as stat_max_neurons
through loc.get(...). Reuse the same key for the corresponding label near the
existing line-153 display so both occurrences remain consistent with the other
localized rows.
- Around line 187-194: Optionally update update_statistics to return early when
is_visible is false, while preserving the existing tamagotchi_logic and squid
guard. This should prevent _sync_from_squid_statistics, timestamp updates, and
update_display from running while the tab is hidden; continue the current
refresh flow when the tab is visible.
In `@src/squid_statistics.py`:
- Around line 123-134: Update update_distance to calculate the Euclidean
distance and delegate to add_distance, removing its duplicate rollover handling.
Preserve the existing input behavior while ensuring multi-rollover accumulation
uses add_distance’s divmod logic.
- Around line 17-64: Annotate the class-level lookup tables _PERSISTENCE_KEYS,
_EVENT_KEYS, and _NEURON_BIRTH_KEYS as ClassVar with their existing dictionary
value types. Add the required ClassVar import if needed, while preserving all
table contents and behavior.
In `@tests/test_squid_statistics.py`:
- Around line 81-89: Add tests covering the public APIs in the statistics test
suite: verify add_distance handles multiple DISTANCE_ROLLOVER_LIMIT rollovers,
rejects negative and non-finite values, and verify increment maps the legacy
cheese_eaten name while returning False for unknown event names. Add a
load_statistics/to_dict partial-payload round-trip case only if persistence
tests do not already cover it.
In `@tests/test_statistics_persistence.py`:
- Around line 15-44: The schema-lock test currently depends directly on the
private _PERSISTENCE_KEYS mapping. If a public accessor for the persistence
schema exists or is introduced, update EXPECTED_PERSISTENCE_KEYS and the
corresponding assertions around the additional referenced range to use that
public accessor instead, while preserving the exact mapping equality check.
In `@tests/test_statistics_tab_lifecycle.py`:
- Around line 74-81: Update the FakeSquid fixture to define default values for
hunger, cleanliness, sleepiness, curiosity, squid_x, and squid_y alongside its
existing attributes. Ensure these defaults satisfy
SquidBrainWindow.sync_state_from_squid so future assertions fail on behavior
rather than missing attributes.
- Around line 89-298: Split
test_late_logic_wiring_projects_model_statistics_into_real_tab into focused
tests for initial binding, live event refresh, and unbind/rebind behavior.
Extract the repeated patched-window setup and TamagotchiLogic construction into
shared helpers, preserving the existing assertions and setup behavior while
giving each phase its own descriptive test name.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 77eb7817-8ba8-46a1-9f1f-92887e720c08
📒 Files selected for processing (14)
src/brain_about_tab.pysrc/brain_statistics_tab.pysrc/brain_tool.pysrc/brain_widget.pysrc/interactions.pysrc/interactions2.pysrc/neurogenesis.pysrc/squid.pysrc/squid_statistics.pysrc/tamagotchi_logic.pytests/test_squid_statistics.pytests/test_statistics_event_wiring.pytests/test_statistics_persistence.pytests/test_statistics_tab_lifecycle.py
| def update(self, elapsed_seconds): | ||
| """Advance continuous statistics by an explicit elapsed duration.""" | ||
| elapsed_seconds = float(elapsed_seconds) | ||
| if elapsed_seconds < 0 or not math.isfinite(elapsed_seconds): | ||
| raise ValueError("elapsed_seconds must be a finite non-negative number") | ||
|
|
||
| def update(self): | ||
| # Update peak mental states | ||
| if self.squid.anxiety > self.highest_anxiety: | ||
| self.highest_anxiety = self.squid.anxiety | ||
| if self.squid.happiness < self.lowest_happiness: | ||
| self.lowest_happiness = self.squid.happiness | ||
| if self.squid.satisfaction > self.highest_satisfaction: | ||
| self.highest_satisfaction = self.squid.satisfaction | ||
|
|
||
| if self.squid.is_sleeping: | ||
| self.time_spent_asleep += 1 # update is called every second | ||
|
|
||
| # --- Check and update peak neurogenesis values --- | ||
| if self.squid.tamagotchi_logic and self.squid.tamagotchi_logic.brain_window: | ||
| brain_widget = self.squid.tamagotchi_logic.brain_window.brain_widget | ||
| if brain_widget: | ||
| # 1. Update Neurogenesis Stats | ||
| if hasattr(brain_widget, 'neurogenesis_data'): | ||
| neuro_data = brain_widget.neurogenesis_data | ||
| current_novelty = neuro_data.get('novelty_counter', 0) | ||
| current_stress = neuro_data.get('stress_counter', 0) | ||
| current_reward = neuro_data.get('reward_counter', 0) | ||
|
|
||
| if current_novelty > self.peak_novelty: | ||
| self.peak_novelty = current_novelty | ||
| if current_stress > self.peak_stress: | ||
| self.peak_stress = current_stress | ||
| if current_reward > self.peak_reward: | ||
| self.peak_reward = current_reward | ||
|
|
||
| # 2. Update Max Neurons Reached (FIX) | ||
| if hasattr(brain_widget, 'neuron_positions'): | ||
| actual_count = len(brain_widget.neuron_positions) | ||
| if actual_count > self.max_neurons_reached: | ||
| self.max_neurons_reached = actual_count | ||
| self.current_neurons = actual_count | ||
| self.time_spent_asleep += elapsed_seconds |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find any writer for the peak neurogenesis stats outside the model.
rg -nP --type=py -C3 '\b(peak_novelty|peak_stress|peak_reward)\b' src testsRepository: ViciousSquid/Dosidicus
Length of output: 2207
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== squid_statistics outline =="
ast-grep outline src/squid_statistics.py || true
echo "== squid_statistics persistence/stat fields =="
sed -n '1,130p' src/squid_statistics.py
echo "== squid_statistics update =="
sed -n '240,330p' src/squid_statistics.py
echo "== references to statistics object / peak fields outside exact previous matches =="
rg -n --type=py -C2 '\b(SquidStatistics|statistics|stats)\b|peak_novelty|peak_stress|peak_reward' src tests || true
echo "== recent commit diff stat/name-only =="
git diff --stat HEAD~1..HEAD 2>/dev/null || true
git diff --name-only HEAD~1..HEAD 2>/dev/null || trueRepository: ViciousSquid/Dosidicus
Length of output: 50378
Restore the neurogenesis peak observers.
peak_novelty, peak_stress, and peak_reward have no writer besides initialization outside tests, yet they persist via _PERSISTENCE_KEYS, so persisted saves will always read back at 0. Add observation at neuron creation/output time for these three neurons.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/squid_statistics.py` around lines 285 - 300, The neurogenesis peak
metrics are never updated before persistence. Add observation logic for
peak_novelty, peak_stress, and peak_reward at the neuron creation/output point,
updating each persisted peak with the corresponding neuron value while
preserving existing initialization and persistence behavior.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/tamagotchi_logic.py (1)
3349-3383: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick winFix the crash after
current_poop_targetis cleared.The new line sets
self.squid.current_poop_target = Noneafter a successful throw. Line 3354 guardscheck_poop_interaction()withif not hasattr(self.squid, 'current_poop_target'): return.hasattr()returnsTrueeven when the attribute value isNone, since the attribute still exists on the object.On the very next simulation tick,
check_poop_interaction()runs again. The guard passes (the attribute exists), so execution reachespoop = self.squid.current_poop_target(nowNone) and thenpoop_rect = poop.sceneBoundingRect()unconditionally, before thestatusbranch check. This raisesAttributeError: 'NoneType' object has no attribute 'sceneBoundingRect'.
update_rock_interaction()guards the equivalent rock state correctly at Line 3261:if not hasattr(self.squid, 'current_rock_target') or not self.squid.current_rock_target: return. It checks both presence and truthiness. The poop guard at Line 3354 only checks presence.
update_simulation()callsself.check_poop_interaction()every tick (Line 1431) with no surroundingtry/except. Per PyQt5's default behavior (5.5+), an unhandled exception raised inside a timer-driven slot aborts the whole process, not just the tick.Fix the guard at Line 3354 to match the rock guard's pattern:
🐛 Proposed fix for the crash on the next tick after a poop throw
- if not hasattr(self.squid, 'current_poop_target'): + if not getattr(self.squid, 'current_poop_target', None): returnNo test in
tests/test_statistics_event_wiring.pycallscheck_poop_interaction()a second time after a successful throw, so this regression is not covered. Consider adding a regression test that callscheck_poop_interaction()again afterthrow_poop()succeeds. Let me know if you want me to draft that test.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/tamagotchi_logic.py` around lines 3349 - 3383, Update the guard at the start of check_poop_interaction to return when current_poop_target is absent or falsy, matching the existing current_rock_target guard pattern. Preserve the existing interaction logic for valid poop targets.
🧹 Nitpick comments (5)
src/squid_statistics.py (3)
123-134: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider routing
update_distancethroughadd_distance.Rollover logic now exists in two places.
update_distanceat lines 107-121 keeps the older single-subtraction form, which under-counts if one step crosses more than one rollover limit.add_distancehandles that case withdivmod. Delegation removes the duplication and makes both paths behave the same.♻️ Proposed delegation
def update_distance(self, dx, dy): '''Track distance traveled by squid with rollover protection''' distance = math.sqrt(dx*dx + dy*dy) - self.distance_swam += distance - - # Check for rollover and reset with multiplier - if self.distance_swam >= DISTANCE_ROLLOVER_LIMIT: - self.distance_swam = self.distance_swam - DISTANCE_ROLLOVER_LIMIT - self.distance_swam_multiplier += 1 - + previous_multiplier = self.distance_swam_multiplier + self.add_distance(distance) + if self.distance_swam_multiplier != previous_multiplier: # Log the rollover event if hasattr(self.squid, 'tamagotchi_logic'): self.squid.tamagotchi_logic.show_message( f"🌊 Distance counter rolled over! Now at {self.distance_swam_multiplier}x" )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/squid_statistics.py` around lines 123 - 134, Update update_distance to delegate its distance accumulation and rollover handling to add_distance, removing the duplicated single-subtraction rollover logic while preserving any update_distance-specific state or behavior that is still required.
192-197: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract counter initialization instead of calling
__init__again.
load_statisticsandresetboth callself.__init__(self.squid)to clear counters. That call also resetsself.squidandself.start_time, and it will silently clear any non-counter attribute added to__init__later. Move the counter assignments into a private method and call it from__init__,load_statistics, andreset. The reset semantics stay the same, and the intent becomes explicit.♻️ Proposed extraction
def __init__(self, squid): self.squid = squid self.start_time = time.time() + self._reset_counters() + self._last_sickness_state = bool(getattr(squid, 'is_sick', False)) + + def _reset_counters(self): + """Return every tracked counter to its initial value.""" self.total_age_seconds = 0 self.sushi_consumed = 0 # ... remaining counter assignments ... self.max_neurons_reached = DEFAULT_NEURON_COUNT self.current_neurons = DEFAULT_NEURON_COUNT - self._last_sickness_state = bool(getattr(squid, 'is_sick', False))def load_statistics(self, data): """Load canonical statistics from a desktop ``statistics.json`` dict.""" # Loading replaces the current model. Without this reset, fields absent # from an older or partial save leak in from the live session and create # a hybrid of two different states. - self.__init__(self.squid) + self._reset_counters() + self.start_time = time.time()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/squid_statistics.py` around lines 192 - 197, Extract the counter-field initialization from __init__ into a private helper, then invoke that helper from __init__, load_statistics, and reset. Replace the self.__init__(self.squid) calls in load_statistics and reset so they clear only counters while preserving self.squid, self.start_time, and other non-counter state.
291-300: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueThe
max_poops_cleanedname does not match whatobserve_poop_countrecords.The method records the largest number of poop items present at one time. The attribute name states cleaned poops. Because
max_poops_cleanedis a persisted key and a statistics tab label, renaming it needs a save-schema and UI change. If you keep the name, add a short comment that states the value is a simultaneous-count maximum.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/squid_statistics.py` around lines 291 - 300, Keep the persisted max_poops_cleaned name unchanged for compatibility, and add a concise comment near its update in observe_poop_count clarifying that it stores the maximum simultaneous poop-item count, not the number cleaned.tests/test_statistics_persistence.py (2)
243-244: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAssert against
DEFAULT_NEURON_COUNTinstead of the literal8.The expected fallback is the model default.
src/squid_statistics.pyexportsDEFAULT_NEURON_COUNT, andtests/test_statistics_tab_lifecycle.pyalready imports it. If the default changes, these assertions and the ones at lines 163-164 and 180-181 fail for a reason unrelated to the behavior under test.♻️ Proposed change
- self.assertEqual(statistics.current_neurons, 8) - self.assertEqual(statistics.max_neurons_reached, 8) + self.assertEqual( + statistics.current_neurons, + DEFAULT_NEURON_COUNT, + ) + self.assertEqual( + statistics.max_neurons_reached, + DEFAULT_NEURON_COUNT, + )Add the import near the existing
SquidStatisticsimport:from src.squid_statistics import DEFAULT_NEURON_COUNT, SquidStatistics🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_statistics_persistence.py` around lines 243 - 244, Replace the hard-coded expected value 8 in the statistics persistence assertions with DEFAULT_NEURON_COUNT, importing it alongside SquidStatistics from src.squid_statistics. Apply the same update to the corresponding assertions at lines 163-164 and 180-181.
246-317: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSplit the two loader scenarios into separate tests.
This method verifies
_apply_save_dataandload_gamein sequence. If the first block of assertions fails at line 280, the ZIP loader path never runs, so one defect hides the other. Two test methods that share themalformed_statisticsfixture, or asubTestper loader, report both paths independently.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_statistics_persistence.py` around lines 246 - 317, Split test_both_public_loaders_accept_malformed_numeric_statistics into separate tests covering _apply_save_data and load_game independently. Share the malformed_statistics fixture/setup without duplicating its definition, and preserve all existing assertions so each loader reports failures even when the other path is broken.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/tamagotchi_logic.py`:
- Around line 3349-3383: Update the guard at the start of check_poop_interaction
to return when current_poop_target is absent or falsy, matching the existing
current_rock_target guard pattern. Preserve the existing interaction logic for
valid poop targets.
---
Nitpick comments:
In `@src/squid_statistics.py`:
- Around line 123-134: Update update_distance to delegate its distance
accumulation and rollover handling to add_distance, removing the duplicated
single-subtraction rollover logic while preserving any update_distance-specific
state or behavior that is still required.
- Around line 192-197: Extract the counter-field initialization from __init__
into a private helper, then invoke that helper from __init__, load_statistics,
and reset. Replace the self.__init__(self.squid) calls in load_statistics and
reset so they clear only counters while preserving self.squid, self.start_time,
and other non-counter state.
- Around line 291-300: Keep the persisted max_poops_cleaned name unchanged for
compatibility, and add a concise comment near its update in observe_poop_count
clarifying that it stores the maximum simultaneous poop-item count, not the
number cleaned.
In `@tests/test_statistics_persistence.py`:
- Around line 243-244: Replace the hard-coded expected value 8 in the statistics
persistence assertions with DEFAULT_NEURON_COUNT, importing it alongside
SquidStatistics from src.squid_statistics. Apply the same update to the
corresponding assertions at lines 163-164 and 180-181.
- Around line 246-317: Split
test_both_public_loaders_accept_malformed_numeric_statistics into separate tests
covering _apply_save_data and load_game independently. Share the
malformed_statistics fixture/setup without duplicating its definition, and
preserve all existing assertions so each loader reports failures even when the
other path is broken.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6fab7e2d-151a-4779-817a-1e41f65a1ac7
📒 Files selected for processing (7)
src/squid.pysrc/squid_statistics.pysrc/tamagotchi_logic.pytests/test_squid_statistics.pytests/test_statistics_event_wiring.pytests/test_statistics_persistence.pytests/test_statistics_tab_lifecycle.py
🚧 Files skipped from review as they are similar to previous changes (3)
- tests/test_squid_statistics.py
- src/squid.py
- tests/test_statistics_tab_lifecycle.py
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/test_statistics_event_wiring.py (1)
562-568: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winDo not mask StatisticsTab mutations in the rock test.
statistics_tab.increment_statcurrently delegates tosquid.statistics.increment. IfRockInteractionManager.throw_rock()regresses to the obsolete statistics-tab mutation path, this test still records one event and passes. Replace the lambda with aMockand assert that it is not called. Keep the model counter assertion.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_statistics_event_wiring.py` around lines 562 - 568, Update the statistics_tab fixture in the rock interaction test so increment_stat is a Mock rather than delegating to squid.statistics.increment, then assert that this mock is not called after RockInteractionManager.throw_rock(). Preserve the existing model counter assertion.tests/test_squid_statistics.py (1)
122-128: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAssert that no event counter changes for an unknown event.
The test checks only
cheese_consumed. A regression that changessushi_consumed,poops_created, or another mapped counter can still pass. Snapshot all event-backed counters before and after the call.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_squid_statistics.py` around lines 122 - 128, Update test_unknown_event_is_logged_without_mutating_counters to snapshot all event-backed counters before calling increment("misspelled_event"), then assert the complete counter state is unchanged afterward instead of checking only cheese_consumed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/test_squid_statistics.py`:
- Around line 122-128: Update
test_unknown_event_is_logged_without_mutating_counters to snapshot all
event-backed counters before calling increment("misspelled_event"), then assert
the complete counter state is unchanged afterward instead of checking only
cheese_consumed.
In `@tests/test_statistics_event_wiring.py`:
- Around line 562-568: Update the statistics_tab fixture in the rock interaction
test so increment_stat is a Mock rather than delegating to
squid.statistics.increment, then assert that this mock is not called after
RockInteractionManager.throw_rock(). Preserve the existing model counter
assertion.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 110a6aff-0a2f-4ec8-ad64-15e716c844a4
📒 Files selected for processing (5)
src/squid_statistics.pysrc/tamagotchi_logic.pytests/test_squid_statistics.pytests/test_statistics_event_wiring.pytests/test_statistics_persistence.py
🚧 Files skipped from review as they are similar to previous changes (3)
- tests/test_statistics_persistence.py
- src/tamagotchi_logic.py
- src/squid_statistics.py
|
All changes tested and verified. Thank you very much! I am really grateful for your contributions to my personal project x x |
e573317
into
ViciousSquid:2.6.5.0_LatestVersion
I'm sincerely sorry for #41. It made the Statistics tab worse and cost you time to merge, test, and revert it. You were right to revert it. I should have tested the real desktop Qt startup path before asking you to merge the change, and I did not.
Root cause
Both desktop entrypoints create
SquidBrainWindowbeforeTamagotchiLogic, then attach the completed logic later throughSquidBrainWindow.set_tamagotchi_logic().When I changed
StatisticsTabinto a read-only view of the canonicalSquidStatisticsmodel, I failed to update that late-injection path. Its propagation loop omittedstatistics_tabandpersonality_taband referencedlearning_tabinstead of the realnn_viz_tab. The Statistics tab never received the model it had been changed to display, so its refresh methods returned early and its labels remained at their initial placeholder values.The tests submitted with #41 covered model, event-delivery, persistence, and reset behavior with fakes, but never constructed the real seven-tab startup sequence. I mistook unit coverage for GUI integration coverage. That validation failure was mine.
Repair
This restores the complete reverted statistics change rather than applying a lifecycle-only patch: the issue requires the statistics to be recorded and persisted correctly, not merely for the tab to display a model.
StatisticsTabafter bindingFinal R4 review fixes
CodeRabbit's completed review of the submitted R3 head found two additional real defects, and both are fixed in this update:
peak_novelty,peak_stress, andpeak_rewardfrom the current production neurogenesis triggers immediately after they update, using the same stress scale as the neurogenesis brain state and preserving lifetime maxima while live values decayNone, booleans, negatives, NaN/Infinity, malformed strings, containers, and fractional neuron counts cannot poison or crash the model through either desktop loaderThe update also preserves the former zero-argument
SquidStatistics.update()and one-argumenttrack_neuron_creation()APIs, derives the standard neuron count from the real eight-positionBrainWidget, and drives the real synchronous neurogenesis method instead of stubbing the behavior under test.Final takeover review fixes
A fresh final-head audit found one more production writer outside
track_neurogenesis_triggers(): eating could raise the live reward trigger from 4 to its cap of 5, then the same simulation callback could decay it to 4.8 before the lifetime peak was observed. The food-consumption boundary now records the canonical live maximum immediately after writing it. A regression drives the realSquid.eat()andTamagotchiLogic.track_neurogenesis_triggers()methods through that exact sequence and proves the live value may decay to 4.8 whilepeak_rewardremains 5.A subsequent exact-head audit found that the direct ZIP loader's generic squid-state loop could replace the canonical
SquidStatisticsinstance with a nested legacy statistics dictionary, then fail when it called the model loader. The loader now preserves the model owner, prefers the current top-levelstatistics.json, and falls back to the supported nested legacy payload when that file is absent. A regression drives the real ZIP loader and proves loading succeeds, the model identity is preserved, and the nested value is restored.Final R7 review fixes
CodeRabbit's review of the exact R6 head identified an unsafe legacy poop-target
guard. If
current_poop_targetexists but isNone, the simulation callbacktested only whether the attribute existed and then called
sceneBoundingRect()onNone. The repository currently has no productionassignment of a poop item to this legacy target attribute, so the review's
proposed successful-throw sequence is not reachable as described. The guard is
still hardened for a missing, cleared, or externally supplied target, and a
regression invokes the real interaction method with both absent and
Nonestates.
The same review found that the legacy
update_distance(dx, dy)entrypointduplicated older single-rollover logic. A sufficiently large step could cross
multiple rollover boundaries but increment the multiplier only once. It now
delegates to the validated
add_distance()implementation and preserves itsexisting rollover notification. A regression crosses three boundaries in one
step and verifies the exact remainder, multiplier, and message.
Unsupported event keys still return
Falsewithout changing state. They nowalso emit a debug log so a future typo or incomplete event mapping is visible
during development.
The review's remaining test-only findings were handled without changing
production semantics: default-neuron assertions use
DEFAULT_NEURON_COUNT, and the two public malformed-statistics loaderscenarios now report independently. The proposed
__init__reset refactor wasnot applied because no defect was present and preserving the old
start_timeacross a load would risk counting pre-load session time again.
Regression evidence
A focused reproduction invokes the production late-injection method against a real
StatisticsTab. It fails on the exact merged #41 tree and on the audited upstream base becauseStatisticsTab.tamagotchi_logicremainsNone; it passes with this repair.A separate offscreen Qt regression constructs the real
SquidBrainWindow,BrainWidget,QTabWidget, all seven real tabs, and theirQLabelvalues. It exerciseslogic1 -> None -> logic2, verifies every real tab reference at each stage, proves the window has no ghostlearning_tab, derives the eight-neuron baseline from the real widget, and asserts all 17 displayed statistics with distinct values on both initial binding and replacement rebinding.Additional integration tests exercise both real save-loading entrypoints with malformed, partial, missing, and nested legacy statistics payloads. Movement tests cover horizontal, vertical, boundary-clipped, sleeping, direct, and diagonal Euclidean displacement. Event tests cover accepted and rejected poop creation, repeated click wake, signal cleanup, real synchronous neurogenesis creation, the food-writer reward peak, and compatibility call forms.
Fifteen earlier targeted mutation checks cover the R6 repair's model, load,
peak, lifecycle, and exactly-once invariants. Three additional R7 mutation
checks confirm that the new regression tests fail if the cleared-target guard,
multi-rollover delegation, or unknown-event debug logging is removed.
Testing
python -m pytest tests -q(57 passed, 87 subtests passed)python -m pytest android/tests -q(7 passed)python -m compileall -q src tests androidTwo fresh packet-only reviews found the food-boundary peak loss in R4 and the legacy-loader model replacement in R5. Both findings were independently reproduced and fixed in transparent successor commits. The exact R7 packet is reviewed separately before submission; reviewer execution limitations and the verdict are preserved in the submission evidence rather than represented as local test execution here.
The GUI lifecycle regression is offscreen and stubs background workers, bridge I/O, and task-manager UI. It uses the real window, widget, tabs, labels, injection order, model, and relevant production methods. No interactive visual smoke was performed for this model-boundary R7 update.
Closes #26
Summary by CodeRabbit
New Features
Bug Fixes