Skip to content

G12: Harvest Streak Counter - #1

Merged
shmoula merged 23 commits into
masterfrom
008-harvest-streak
Jun 6, 2026
Merged

shmoula merged 23 commits into
masterfrom
008-harvest-streak

Conversation

@shmoula

@shmoula shmoula commented Jun 6, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Adds a consecutive-harvest streak with an escalating bonus (+5/+10/+15/+20, capped) that pays out before the bankruptcy check, so it counts toward survival.
  • Streak resets on miss-days and on season pass/victory (not on season_failed). Persistent best (bestHarvestStreak) lives on records.ts (schema v2), per-run high-water mark on GameState.peakHarvestStreak.
  • HUD chip (🔥 ×N) with hover tooltip explaining the next bonus; DailyLog gains a bonus row and reset note; BankruptcyScreen shows Longest streak with new-best badge. Save schema bumped 5 → 6 with chained migrations from v3/v4/v5.

Spec: specs/008-harvest-streak/spec.md · Plan: specs/008-harvest-streak/plan.md

Per user feedback during review: first harvest of a streak earns no bonus (bonus is based on the prior streak count), so day 2 is the first paying day.

Test Plan

  • npm test — 328/328 passing
  • npm run lint — clean
  • npx tsc -b — clean
  • Manual smoke: plant + harvest → no bonus on first day, +5 on second consecutive harvest, cap at +20 from day 5+
  • Manual smoke: miss a harvest day → streak chip disappears, DailyLog shows reset note
  • Manual smoke: cross day-20 with a passing season → chip resets to 0 next season
  • Manual smoke: bankrupt run → BankruptcyScreen shows "Longest streak: N" with 🏆 badge on first run

Summary by CodeRabbit

  • New Features

    • Harvest streak counter: tracks consecutive harvest days, shows a HUD streak chip (with capped escalating coin bonus tooltip), adds per-day streak bonus and reset notes to the Daily Log, and surfaces “Longest streak” / “Best streak” in end-run personal records.
  • Documentation

    • Backlog updated: G12 (harvest streak) marked shipped; G4 (daily objectives/contracts/milestone banners) deferred and remapped; phasing notes and footer refreshed.

Vaclav Balak and others added 17 commits June 6, 2026 00:04
11 TDD tasks covering types, engine, migration, records, HUD chip,
DailyLog rows, BankruptcyScreen integration, and final sweep.
Notes the schema bump is v5→v6 (records v1→v2) — the spec's
v3→v4 reference predates the current schema baseline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Initialize harvestStreak and peakHarvestStreak to 0 in initialGameState,
and add STREAK_BONUS_PER_LEVEL and STREAK_BONUS_CAP constants.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Resets harvestStreak to 0 when a season is cleared (season_passed or
season_4_won) but preserves peakHarvestStreak. Skips reset on
season_failed since the run is ending and the final log should reflect
the as-played streak.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Updated migrateState() to handle schema v5 → v6 migration by adding
harvestStreak and peakHarvestStreak fields defaulted to 0. Also updated
v4 → v6 and v3 → v6 migration chains to include the new streak fields.
Updated existing migration tests to expect schema v6 with all migrated
fields properly initialized.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add bestHarvestStreak to PersonalBests and integrate with recordRunEnd to
track harvest streak peaks. Schema bumped 1→2; legacy records degrade gracefully
with bestHarvestStreak=0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add harvestStreak prop to HUD component and render a flame emoji chip (🔥 ×N)
when the streak count exceeds 0. The chip uses aria-label for accessibility and
is hidden entirely when harvestStreak === 0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add two new rows to the Daily Log modal:
- Streak bonus row when streakBonus > 0, showing multiplier (capped at 4) and coins awarded
- Streak reset note when a streak breaks (streakBefore > 0 && streakAfter === 0)

Includes comprehensive test coverage for both conditions and accessibility checks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds peakHarvestStreak prop to BankruptcyScreen, displays it in a new
StatRow with new-best badge support, and includes it in Personal Records grid.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bonus is now based on streakBefore (prior streak count), so the first
harvest in a streak earns nothing. Day 2 of a streak earns +5, day 3
+10, etc., still capped at +20. The DailyLog chip multiplier and all
affected tests are updated to match.
@vercel

vercel Bot commented Jun 6, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pixel-parsnips Ready Ready Preview, Comment Jun 6, 2026 8:28pm

@coderabbitai

coderabbitai Bot commented Jun 6, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ff108b88-4746-467d-8137-82d5023e1d1f

📥 Commits

Reviewing files that changed from the base of the PR and between 51d4da9 and fc903d4.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • index.html
  • src/components/HUD.tsx
  • tailwind.config.ts
✅ Files skipped from review due to trivial changes (2)
  • tailwind.config.ts
  • index.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/HUD.tsx

📝 Walkthrough

Walkthrough

This PR implements G12: adds per-run streak fields and per-turn streak logging, computes capped per-harvest coin bonuses applied before bankruptcy, persists bestHarvestStreak in records (schema v2), migrates saves to schema v6, and surfaces streak UI in HUD, DailyLog, and Bankruptcy screens with tests and docs.

Changes

Harvest Streak Counter (G12)

Layer / File(s) Summary
Type System & Schema Contracts
src/engine/types.ts, src/engine/records.ts, src/components/BankruptcyScreen.tsx
Adds GameState.harvestStreak and GameState.peakHarvestStreak. DailyLogEntry adds streakBefore, streakAfter, streakBonus. PersonalBests adds bestHarvestStreak and records schema bump to v2; BankruptcyScreenProps adds peakHarvestStreak.
Engine Constants & Initialization
src/engine/constants.ts, src/engine/gameEngine.ts, specs/008-harvest-streak/plan.md
Bumps SCHEMA_VERSION 5→6, adds STREAK_BONUS_PER_LEVEL and STREAK_BONUS_CAP, and initializes streak fields to 0 in initialGameState.
Core Streak Computation in processTurn
src/engine/gameEngine.ts
Adds helpers to compute streak increment/reset and capped bonus; applies streakBonus to coin balance prior to bankruptcy check; records streakBefore/After/Bonus in DailyLogEntry; preserves streaks in bankrupt and non-bankrupt GameState; season-clear-only reset behavior implemented.
Records Persistence & Best Streak
src/engine/records.ts
Records schema v1→v2 with bestHarvestStreak. loadRecords() normalizes/migrates legacy records. recordRunEnd() uses state.peakHarvestStreak to update bestHarvestStreak and include it in newBests.
Save State Migrations
src/engine/useGameEngine.ts
migrateState validates save shape, adds v5→v6 migration to hydrate streak fields, expands v4→v6 and v3→v6 to initialize v6 fields, and removes prior stop-at-v5 paths.
HUD Streak Chip
src/components/HUD.tsx, src/components/GameBoard.tsx
HUDProps gains harvestStreak: number; HUD renders a conditional 🔥 chip when streak>0 showing ×N and a capped-bonus tooltip; GameBoard passes state.harvestStreak through.
DailyLog Streak UI
src/components/DailyLog.tsx, tests/components/DailyLog.test.tsx
Renders "Harvest streak bonus" row when streakBonus > 0 and a "Streak reset" note when streak is cleared, with message variants depending on whether harvests occurred; tests cover render cases and accessibility.
BankruptcyScreen Recap
src/components/BankruptcyScreen.tsx, src/App.tsx, tests/components/BankruptcyScreen.test.tsx
Adds "Longest streak" stat row showing peakHarvestStreak with new-best badge, and "Best streak" personal-records line displaying records.bestHarvestStreak. App bankruptcy path wires peakHarvestStreak and updates fallback records to schema v2; tests added/updated.
Engine Test Coverage
tests/engine/gameEngine.test.ts
Asserts SCHEMA_VERSION==6 and initial streak fields are zero. Adds harvest-streak test suite: first-day no bonus, consecutive-day bonus, reset on miss, cap behavior, bankruptcy-turn bonus recording, and season reset semantics.
Records Test Coverage
tests/engine/records.test.ts
Updates fixtures to schema v2 including bestHarvestStreak. Tests load/save round-trips, newBests detection for bestHarvestStreak, and legacy v1→v2 migration behavior.
Migration & Integration Tests
tests/engine/useGameEngine.test.ts
Expands migration tests (v5→v6, v4→v6, v3→v6) to ensure streak fields are initialized to 0 and bankrupt-phase preserved; integrates streakBonus into full turn accounting reconciliation and uses SCHEMA_VERSION in fixtures.
Component Tests & CI updates
tests/components/*.test.tsx, .github/workflows/ci.yml, package.json
Adds HUD chip tests, updates component fixtures to schema v2, runs accessibility checks; CI actions updated to actions/*@v5 and Node 22; vite bumped to ^7.0.0.
Specification & Backlog
specs/008-harvest-streak/plan.md, backlog.md
Comprehensive plan/spec added and backlog updated: G12 marked shipped; G4 deferred; cross-document consensus and phasing notes updated.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🐰 A streak of golden harvests gleams,
🔥 Multipliers hop through morning beams,
Coins tally up as fields align,
Seasons clear — the peak stays mine,
A rabbit cheers: small wins, big shine!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'G12: Harvest Streak Counter' directly and clearly describes the main feature being implemented, matching the primary changeset focus.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 008-harvest-streak

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/engine/gameEngine.ts (1)

348-365: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include streakBonus in netChange.

Both log builders add the streak bonus into closingBalance, but netChange still excludes it. On every bonus-paying harvest day, openingBalance + netChange !== closingBalance, so the log underreports the turn’s actual gain.

Proposed fix
       taxRate: TAX_RATE,
       taxDeducted: 0,
-      netChange: totalHarvestIncome,
+      netChange: totalHarvestIncome + streakBonus,
       closingBalance: coinBalance,
       exhaustedPlots,
       pestDestroyedPlots,
       flashDroughtDaysAfter: flashDroughtDaysAfterEvent,
       streakBefore,
@@
     taxRate: TAX_RATE,
     taxDeducted,
-    netChange: totalHarvestIncome - landLeaseDeducted - taxDeducted,
+    netChange: totalHarvestIncome + streakBonus - landLeaseDeducted - taxDeducted,
     closingBalance: coinBalance,
     exhaustedPlots,
     pestDestroyedPlots,
     flashDroughtDaysAfter: flashDroughtDaysRemaining,
     streakBefore,

Also applies to: 437-454

🤖 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/engine/gameEngine.ts` around lines 348 - 365, The DailyLogEntry
construction currently sets netChange to totalHarvestIncome but omits adding
streakBonus, causing openingBalance + netChange to differ from closingBalance
when a streakBonus is applied; update the netChange calculation in the log
builders (where DailyLogEntry is created and netChange is assigned) to include
streakBonus (e.g., netChange = totalHarvestIncome + streakBonus or more
generally netChange = closingBalance - openingBalance) so netChange reflects the
streakBonus and matches closingBalance minus openingBalance; apply the same
change in both places (around the blocks that define streakBonus,
openingBalance, closingBalance, and netChange).
🧹 Nitpick comments (4)
tests/components/BankruptcyScreen.test.tsx (1)

100-108: ⚡ Quick win

Strengthen streak recap assertions beyond label presence.

Line 100 verifies headings only. Also assert streak values and the new-best badge so the new wiring is actually validated.

✅ Suggested assertions
-import { render, screen } from '`@testing-library/react`';
+import { render, screen, within } from '`@testing-library/react`';
@@
   it('shows Longest streak stat row with peakHarvestStreak', () => {
@@
     expect(screen.getByText('Longest streak')).toBeInTheDocument();
     expect(screen.getByText(/Best streak/i)).toBeInTheDocument();
+    expect(screen.getAllByLabelText('new personal best')).toHaveLength(1);
+    const recordsPanel = screen.getByLabelText(/personal records across all runs/i);
+    expect(within(recordsPanel).getByText('Best streak:')).toBeInTheDocument();
+    expect(within(recordsPanel).getByText('6')).toBeInTheDocument();
   });
🤖 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/components/BankruptcyScreen.test.tsx` around lines 100 - 108, The test
currently only checks for the presence of the "Longest streak" and "Best streak"
labels; update the spec that uses renderScreen(...) (with peakHarvestStreak: 6,
records and newBests) to also assert the rendered numeric streak value (e.g.,
"6" or a string containing the peakHarvestStreak) and assert the "new best"
indicator is present when newBests includes 'bestHarvestStreak' so the wiring
for peakHarvestStreak and newBests is actually validated; locate the test using
renderScreen, peakHarvestStreak, records and newBests and add assertions for the
streak number and the new-best badge element.
tests/components/HUD.test.tsx (1)

71-76: ⚡ Quick win

Add a tooltip/cap assertion for the streak chip.

Line 71 currently validates visibility/text only. Add a title assertion so capped next-bonus behavior is protected too.

✅ Suggested test extension
   it('shows the streak chip with ×N when harvestStreak > 0', () => {
     render(<HUD {...baseProps} currentDay={5} coinBalance={100} harvestStreak={7} />);
     const chip = screen.getByLabelText(/harvest streak/i);
     expect(chip).toBeInTheDocument();
     expect(chip).toHaveTextContent('×7');
+    expect(chip).toHaveAttribute('title', expect.stringMatching(/\+20🪙 bonus \(capped at \+20\)/i));
   });
🤖 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/components/HUD.test.tsx` around lines 71 - 76, The test "shows the
streak chip with ×N when harvestStreak > 0" in HUD.test.tsx should also assert
the streak chip's tooltip/cap behavior: after rendering HUD with harvestStreak
(the existing render call using HUD and props like currentDay, coinBalance,
harvestStreak), grab the chip via getByLabelText(/harvest streak/i) (the
existing chip variable) and add an assertion that the chip has a title attribute
(and, if you know the exact capped message, assert its value) — e.g.
expect(chip).toHaveAttribute('title') or
expect(chip.getAttribute('title')).toBe('EXPECTED_TOOLTIP') to protect the
capped next-bonus behavior.
tests/engine/gameEngine.test.ts (1)

1534-1546: ⚡ Quick win

This season_failed test never exercises the streak-preservation path.

Here the turn has no harvest, so the streak becomes 0 even without any season-end reset. The test would still pass if season_failed incorrectly cleared a harvested streak. Use a season-end turn that harvests but still misses the target.

Example adjustment
-  it('does NOT reset streak on season_failed (run is ending)', () => {
-    const base = {
-      ...initialGameState(),
-      currentDay: 20,
-      coinBalance: 16,
-      harvestStreak: 2,
-      peakHarvestStreak: 2,
-    };
-    const { state: after, log } = processTurn(base, 'sunny');
+  it('does NOT reset streak on season_failed when the final turn harvests', () => {
+    const seeded = seedAndPlant({
+      ...initialGameState(),
+      currentDay: 20,
+      coinBalance: 21,
+      harvestStreak: 2,
+      peakHarvestStreak: 2,
+    });
+    const { state: after, log } = processTurn(seeded, 'sunny');
     expect(after.phase).toBe('season_failed');
-    expect(log.streakAfter).toBe(0); // no harvest → miss-day reset (independent rule)
-    expect(after.harvestStreak).toBe(0);
+    expect(log.streakBefore).toBe(2);
+    expect(log.streakAfter).toBe(3);
+    expect(after.harvestStreak).toBe(3);
   });
🤖 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/engine/gameEngine.test.ts` around lines 1534 - 1546, The test "does NOT
reset streak on season_failed (run is ending)" currently uses a turn with no
harvest so the streak resets for an unrelated reason; change the base state in
this test so the turn produces a harvest but still results in phase
'season_failed' (so we exercise the season-end code path). In practice, adjust
the initialGameState() seed values (e.g., crop/harvest readiness or coinBalance)
used in the test before calling processTurn('sunny') so processTurn returns
after.phase === 'season_failed' while a harvest occurred, then assert that
after.harvestStreak and log.streakAfter preserve the pre-turn streak
(harvestStreak remains 2). Target the test case, initialGameState(),
processTurn, and the assertions around after.phase, log.streakAfter and
after.harvestStreak.
tests/engine/records.test.ts (1)

157-158: ⚡ Quick win

Use the imported constant instead of hardcoded string.

Line 158 uses the hardcoded string 'pixel-parsnips-records' instead of the imported RECORDS_KEY constant. Line 67 in this file correctly uses the constant. Using the constant prevents maintenance issues if the key value changes.

♻️ Proposed fix
-    localStorage.setItem(
-      'pixel-parsnips-records',
+    localStorage.setItem(
+      RECORDS_KEY,
       JSON.stringify({
🤖 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/engine/records.test.ts` around lines 157 - 158, Replace the hardcoded
localStorage key string with the imported RECORDS_KEY constant: in the test
where localStorage.setItem('pixel-parsnips-records', ...) is used, swap the
literal for RECORDS_KEY and ensure the test imports/uses the same RECORDS_KEY
referenced earlier in this file (the constant used at line 67) so both
occurrences consistently use RECORDS_KEY.
🤖 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 `@backlog.md`:
- Line 105: Update the footer timestamp chain line that currently reads
"*Generated 2026-06-02 from p1–p6 analyses. Updated 2026-06-03 after shipping
006-season-system, then 2026-06-04 after shipping 007-enriched-run-summary, then
2026-06-05 after deferring G4 (Daily Objectives / Milestones / Contracts).*" by
appending the G12 ship note for 2026-06-06 so it reflects the new update (e.g.,
add ", then 2026-06-06 after shipping G12"). Ensure the exact footer sentence is
updated consistently with the document references to G12 in Phase 2 and line 33.

In `@src/engine/useGameEngine.ts`:
- Around line 22-60: The migrateState function currently only checks
parsed.state truthiness so malformed saves like {schemaVersion:5, state:42} get
cast/spread into a GameState; update each recognized-schema branch in
migrateState to first validate parsed.state is a non-null object with the
minimal required GameState properties (e.g. check typeof parsed.state ===
'object' && parsed.state !== null && 'phase' in parsed.state && 'plots' in
parsed.state) before spreading/casting; if the guard fails, fall through to the
discard/malformed-save path and return null so corrupted payloads are not
migrated into invalid GameState objects.

---

Outside diff comments:
In `@src/engine/gameEngine.ts`:
- Around line 348-365: The DailyLogEntry construction currently sets netChange
to totalHarvestIncome but omits adding streakBonus, causing openingBalance +
netChange to differ from closingBalance when a streakBonus is applied; update
the netChange calculation in the log builders (where DailyLogEntry is created
and netChange is assigned) to include streakBonus (e.g., netChange =
totalHarvestIncome + streakBonus or more generally netChange = closingBalance -
openingBalance) so netChange reflects the streakBonus and matches closingBalance
minus openingBalance; apply the same change in both places (around the blocks
that define streakBonus, openingBalance, closingBalance, and netChange).

---

Nitpick comments:
In `@tests/components/BankruptcyScreen.test.tsx`:
- Around line 100-108: The test currently only checks for the presence of the
"Longest streak" and "Best streak" labels; update the spec that uses
renderScreen(...) (with peakHarvestStreak: 6, records and newBests) to also
assert the rendered numeric streak value (e.g., "6" or a string containing the
peakHarvestStreak) and assert the "new best" indicator is present when newBests
includes 'bestHarvestStreak' so the wiring for peakHarvestStreak and newBests is
actually validated; locate the test using renderScreen, peakHarvestStreak,
records and newBests and add assertions for the streak number and the new-best
badge element.

In `@tests/components/HUD.test.tsx`:
- Around line 71-76: The test "shows the streak chip with ×N when harvestStreak
> 0" in HUD.test.tsx should also assert the streak chip's tooltip/cap behavior:
after rendering HUD with harvestStreak (the existing render call using HUD and
props like currentDay, coinBalance, harvestStreak), grab the chip via
getByLabelText(/harvest streak/i) (the existing chip variable) and add an
assertion that the chip has a title attribute (and, if you know the exact capped
message, assert its value) — e.g. expect(chip).toHaveAttribute('title') or
expect(chip.getAttribute('title')).toBe('EXPECTED_TOOLTIP') to protect the
capped next-bonus behavior.

In `@tests/engine/gameEngine.test.ts`:
- Around line 1534-1546: The test "does NOT reset streak on season_failed (run
is ending)" currently uses a turn with no harvest so the streak resets for an
unrelated reason; change the base state in this test so the turn produces a
harvest but still results in phase 'season_failed' (so we exercise the
season-end code path). In practice, adjust the initialGameState() seed values
(e.g., crop/harvest readiness or coinBalance) used in the test before calling
processTurn('sunny') so processTurn returns after.phase === 'season_failed'
while a harvest occurred, then assert that after.harvestStreak and
log.streakAfter preserve the pre-turn streak (harvestStreak remains 2). Target
the test case, initialGameState(), processTurn, and the assertions around
after.phase, log.streakAfter and after.harvestStreak.

In `@tests/engine/records.test.ts`:
- Around line 157-158: Replace the hardcoded localStorage key string with the
imported RECORDS_KEY constant: in the test where
localStorage.setItem('pixel-parsnips-records', ...) is used, swap the literal
for RECORDS_KEY and ensure the test imports/uses the same RECORDS_KEY referenced
earlier in this file (the constant used at line 67) so both occurrences
consistently use RECORDS_KEY.
🪄 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: eca11247-485c-46d4-a969-b3dfb072bc36

📥 Commits

Reviewing files that changed from the base of the PR and between eaa7611 and 74bb455.

📒 Files selected for processing (19)
  • backlog.md
  • specs/008-harvest-streak/plan.md
  • src/App.tsx
  • src/components/BankruptcyScreen.tsx
  • src/components/DailyLog.tsx
  • src/components/GameBoard.tsx
  • src/components/HUD.tsx
  • src/engine/constants.ts
  • src/engine/gameEngine.ts
  • src/engine/records.ts
  • src/engine/types.ts
  • src/engine/useGameEngine.ts
  • tests/components/BankruptcyScreen.test.tsx
  • tests/components/DailyLog.test.tsx
  • tests/components/GameBoard.test.tsx
  • tests/components/HUD.test.tsx
  • tests/engine/gameEngine.test.ts
  • tests/engine/records.test.ts
  • tests/engine/useGameEngine.test.ts

Comment thread backlog.md
Comment thread src/engine/useGameEngine.ts
Vaclav Balak and others added 4 commits June 6, 2026 21:49
vitest 4 requires vite >=6, which caused npm to install a nested vite 8
alongside the pinned vite 5 — leaving package-lock.json out of sync with
the esbuild 0.27 that nested vite required and breaking `npm ci`.
Aligning vite to ^7 lets vitest share the root vite and produces a clean
lockfile. Also bumps checkout/setup-node to v5 and the runner to Node 22
to clear the Node 20 deprecation warning.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Truthy check on parsed.state let non-object values (e.g. 42, "x") through to
spread/cast as GameState. Add isGameStateShape structural guard so corrupted
saves fall through to the discard path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
netChange omitted streakBonus, so openingBalance + netChange diverged from
closingBalance on streak-bonus turns. Compute as closingBalance - openingBalance
in both the bankruptcy and normal log builders to preserve the invariant.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- gameEngine: season_failed test now seeds a harvest so it exercises the
  season-end code path (was passing via miss-day reset for unrelated reasons).
- HUD: assert streak chip tooltip including capped next-bonus message.
- BankruptcyScreen: assert rendered streak value and New Best badge.
- records: use RECORDS_KEY constant instead of hardcoded key string.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 @.github/workflows/ci.yml:
- Line 23: Replace the moving tag uses: actions/setup-node@v5 with the specific
v5 commit SHA at both places where "uses: actions/setup-node@v5" appears so the
workflow is pinned; update the action step inputs to account for v5's automatic
caching by adding/adjusting package-manager-cache when packageManager is present
(or explicitly disable it) and verify any packageManager fields remain correct;
also confirm the workflow runner and any Node-version-specific steps are
compatible with Node 24 (update node-version targets or environment assumptions
if needed).
- Line 20: The workflow currently uses mutable tags actions/checkout@v5 and
actions/setup-node@v5; update the CI workflow to pin both actions to their full
commit SHAs (replace the `@v5` tags with the corresponding commit SHA refs for
actions/checkout and actions/setup-node) and set the node-version input to a
deterministic 22.x floor that satisfies the lockfile (e.g., "22.x" with a
minimum of >=22.12.0) so the runner and Node requirements are explicit and
supply-chain risk is reduced.
🪄 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: 02fb8bc8-3310-4961-9bf7-4329fbfc3f23

📥 Commits

Reviewing files that changed from the base of the PR and between 74bb455 and 51d4da9.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • package.json
  • src/engine/gameEngine.ts
  • src/engine/useGameEngine.ts
  • tests/components/BankruptcyScreen.test.tsx
  • tests/components/HUD.test.tsx
  • tests/engine/gameEngine.test.ts
  • tests/engine/records.test.ts
✅ Files skipped from review due to trivial changes (1)
  • package.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/components/BankruptcyScreen.test.tsx
  • src/engine/useGameEngine.ts
  • src/engine/gameEngine.ts
  • tests/engine/records.test.ts

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Vaclav Balak and others added 2 commits June 6, 2026 22:27
Replace mutable @v5 tags for actions/checkout and actions/setup-node
with full commit SHA refs to reduce supply-chain risk. Switch
node-version from "22" to "22.x" for explicitness.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… meta description

Lighthouse mobile audit flagged two WCAG AA contrast failures and a
missing meta description.

- HUD season chip label: text-farm-stone/60 was 2.34:1 on the dark chip;
  switch to text-farm-parchment/70 (~7.7:1).
- farm.grass token #3D7A2B against farm-parchment was 4.44:1 (just under
  4.5:1); darken to #357028 (~5.1:1). Ripples to the few primary action
  buttons that use bg-farm-grass text-farm-parchment.
- index.html: add meta description and replace the default Vite title
  with "Pixel Parsnips".

Scores: a11y 94 -> expected 100, SEO 90 -> expected 100.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@shmoula
shmoula merged commit fbb93ea into master Jun 6, 2026
4 of 5 checks passed

This branch was successfully deployed

1 active deployment
Preview — fc903d4c Deployed Jun 6, 2026 by vercel[bot]
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.

1 participant