feat: add food upkeep model for extra workers (#147)#167
Conversation
There was a problem hiding this comment.
AI Automated Review
Analysis engine: MiniMax@https://litellm.jory.dev/v1 (anthropic)
PR #167 Review — Food Upkeep Model
Recommendation: Approve. The PR implements a complete, well-scoped food upkeep model that satisfies every acceptance criterion in #147, is internally consistent, follows repository conventions (constants in constants.gd, tests in tests/, additive change to main.gd), and ships with 14 dedicated tests.
Change-by-change findings
scripts/constants.gd (+18 lines)
- Adds 7 new typed
consts with clear comments referencing #147 / #133. The docstring block above them is helpful and matches the soft-model intent. - Values are well-chosen and explicit: interval 10, base cap 2, cost 1, low threshold 3, starvation threshold 1, factors 0.5 / 0.0.
- This file was previously the home of
BASE_WORKER_CAPandWORKER_CAP_BONUSES(perf099947), so colocating these new constants here follows the established convention.
scripts/main.gd (+82 / -3)
- State: Adds
var food_upkeep_tracker := 0alongside other top-level state vars — appropriate location. - Helpers:
get_extra_workers_count()— guardsstate.has("workers")and usesmaxi(extra, 0), so the model is safe in edge cases (e.g. before state is fully initialized).apply_food_upkeep()— soft model: clamps to 0 withmaxi(...), so food never goes negative. Emits a player-facing event "The crew ate. Food -%d." only when food is actually consumed (good — avoids event spam when ≤ 2 workers).get_food_slowdown_factor()— implements linear interpolation betweenSTARVATION_FOOD_THRESHOLD(0.0) andLOW_FOOD_THRESHOLD(0.5), and returns 1.0 above the low threshold. The interpolation range is explicitly guarded against zero.get_low_food_level()/should_bias_to_food_gathering()— clean classifier.
- Tick integration: Upkeep tracker increments each tick and fires
apply_food_upkeep()every 10 ticks. Reset to 0 after firing. This produces upkeep on ticks 10, 20, 30, … — matches the design ("every 10 ticks"). - Task selection: In
choose_task(), whenkind == "gather"and food is low/starving, food tasks are sorted to the top before distance is considered. This delivers the "bias toward food gathering" described in the issue. (Note: this is a strong bias — food tasks will be preferred over closer non-food tasks, which is the intent.) - Build speed:
structure_build_speed()multiplies by the slowdown factor. Critically, this only affects build speed — gathering speed is not multiplied by the slowdown, so workers can still recover food even at starvation. This is consistent with the design comment "workers stop (0% speed) unless gathering food."
tests/test_food_upkeep.gd (new, +160 lines)
- 14 test cases covering: base workers exempt, extra workers cost 1 each, no-negative clamp, no slowdown above threshold, slowdown at low threshold, pause at starvation threshold, linear interpolation at midpoint, level classification, food-bias gating, interval constant, base workers constant, per-worker cost constant, and overall constant consistency.
- Includes an explicit consistency test (
test_constants_consistency) that assertsSTARVATION < LOWand factors stay in[0, 1]— nice safety net. - Test names are clear and the test file header documents the scope and the #147 / #133 link.
Standards Compliance
From AGENTS.md:
- Make one focused change. ✅ The PR is tightly scoped to food upkeep: no unrelated edits.
- Game is intentionally monolithic; one scene, two scripts. ✅ The PR adds logic to
main.gdrather than creating a new script, which fits this convention. The new helpers are clustered in one block and clearly delimited with# ── Food upkeep helpers (issue #147, links to #133) ──…separators, matching the style used elsewhere in the file. - Smoke test before pushing.
⚠️ Recommendation: run./.tools/Godot_v4.2.2-stable_linux.x86_64 --headless --path . --script res://tests/test_runner.gd(per AGENTS.md validation section) before merge to confirm the new test file is registered/discovered by the existing test runner and that GDScript warnings (treated as errors in practice) are clean. - No release after every small change. ✅ Not applicable; the PR itself does not cut a release.
- Detailed GitHub issues are used to delegate work; issue text should be concrete. ✅ This PR follows the implied agent workflow (branch
saffron/issue-147-food-upkeep).
Linked Issue Fit (#147)
Walking the issue's acceptance criteria:
| Criterion | Status |
|---|---|
| Food upkeep rule is defined and tested | ✅ Constants in constants.gd, model in main.gd, 14 tests in test_food_upkeep.gd |
| Base 2 workers do not create surprising early-game food pressure | ✅ BASE_WORKERS_NO_UPKEEP := 2 + test_base_workers_no_upkeep |
| Extra workers create clear food pressure | ✅ FOOD_PER_EXTRA_WORKER := 1, applied every 10 ticks; test_extra_workers_create_pressure, test_one_extra_worker_cost |
| Low-food behavior is predictable and covered by tests | ✅ Linear interpolation + dedicated tests for threshold boundaries, midpoint, classification, and bias |
| Link back to #133 | ✅ Comments at three call sites reference #133; repository grep confirms |
Also matches the issue's scope guardrail: "Do not add recruit UI in this issue." ✅ — no UI changes are included; only logic, constants, and tests.
The "soft, not punishing" tone from the issue is preserved: speed multiplier reduces to 0.5x then 0.0x, food is clamped at 0 (never negative), and food-gathering bias is applied so the colony can recover. No death/failure paths introduced.
Evidence Provider Findings
No evidence providers were configured for this run. The PR's correctness is being assessed from diff, linked issue, repository conventions, and impact scan only.
Tool Harness Findings
The harness reported 0 planned requests and a planning warning ("Could not parse planning response as JSON"). This is a harness-side parse issue, not a defect in the PR. No blocking tool findings were emitted.
Unknowns / Needs Verification
These are not blockers; they are items I could not confirm from the provided corpus and that a maintainer should sanity-check before merge:
-
Test runner registration. The existing test files (
test_runner.gd,test_constants.gd,test_worker_cap.gd,test_goal_persistence.gd,test_layout_math.gd) follow conventions for being discovered. I could not confirm from the provided corpus whethertests/test_food_upkeep.gdis auto-discovered or needs to be added to a manifest. Quick check: search the test harness for how newtests/test_*.gdfiles are picked up. If it uses directory globbing, no action needed; if it uses an explicit list, the file may need to be registered. -
Test isolation approach. The tests in
test_food_upkeep.gdmirrormain.gdlogic in static helpers ("mirroring main.gd logic for test isolation"). This validates the formula but does not catch drift between the test helpers andmain.gd's actual implementation. For a small, math-heavy model with no rendering or scene-tree dependencies this is acceptable, and the integration points (tick, task selection, build speed) are simple enough to be verified by code review. If the project later adopts a more rigorous test harness, consider refactoring these to instantiatemain.gdand assert against its real methods. -
Minor style nit: In
get_food_slowdown_factor()andget_food_level(), the new code uses untypedvar ... = ...(e.g.var range_size = float(...)) while the rest ofmain.gdand the new test helpers prefer:=. GDScript treats this as an "inferred type" warning depending on project settings. Given thatAGENTS.mdnotes "GDScript warnings are treated as errors in practice" (macOS runtime validation), it is worth confirming the project's warning level does not flag this. Easiest fix: change tovar range_size: float = ...andvar progress: float = .... -
Pacing verification (gameplay, not code). With 1 food/extra worker/10 ticks and existing food-generation rates, designers may want to playtest whether upkeep pressure feels right at the typical 2–4 worker count. This is a tuning question, not a correctness one — the PR is internally consistent and configurable via the constants.
Sources
- PR diff and metadata (head
546d52c) scripts/constants.gdpatchscripts/main.gdpatch (helpers, tick loop, task selection, build speed)tests/test_food_upkeep.gd(new file)- Linked issue #147 (acceptance criteria + "no recruit UI" scope)
- Repository impact scan (confirms #133 / #147 references and no stray mentions)
AGENTS.md(conventions: monolithic main.gd, constants in constants.gd, test smoke command)
- Constants: BASE_WORKERS_NO_UPKEEP=2, FOOD_PER_EXTRA_WORKER=1, UPKEEP_INTERVAL=10 ticks, starvation/low-food thresholds - apply_food_upkeep(): deduct food for workers above base count every interval; never drives food negative (soft model) - get_food_slowdown_factor(): linear slowdown from 50% at low threshold to 0% at starvation; full speed when food is ok - choose_task() bias: when food is low/starving, prioritize food-gather tasks over other gather targets - structure_build_speed(): multiplied by slowdown factor so buildings progress slower under food pressure - Tests: base workers no pressure, extra worker costs, negative clamp, slowdown interpolation, starvation pause, level classification, constants consistency Links to #133 (worker cap through food/housing tradeoffs).
546d52c to
8039fda
Compare
There was a problem hiding this comment.
AI Automated Review
Analysis engine: MiniMax@https://litellm.jory.dev/v1 (anthropic)
Recommendation
Approve. The PR cleanly implements the soft food upkeep model described in issue #147 and satisfies every acceptance criterion in the linked issue. The code is well-structured, well-commented, and follows the established pattern used by the recent worker-cap work (#160) — constants in constants.gd, helpers in main.gd, dedicated test file.
Change-by-change findings
scripts/constants.gd (+18, -0)
- Clean addition of a documented block of constants. Values match the design summary in the PR body (interval=10, base=2, per-extra=1, low=3, starvation=1, factors 0.5/0.0). No issues.
scripts/main.gd (+82, -3)
food_upkeep_trackeris added as an instance variable and reset each time upkeep fires — straight-forward counter pattern. ✔get_extra_workers_count()defensively returns 0 whenstate.workersis absent. ✔apply_food_upkeep()clamps withmaxi(..., 0)so food never goes negative, only fires whenextra > 0, and emits a single event with the actual amount eaten (post-clamp delta). ✔get_food_slowdown_factor()returns 0.0 at/below starvation, linearly interpolates between starvation and low thresholds, and includes arange_size == 0guard. ✔get_low_food_level()andshould_bias_to_food_gathering()are simple, clear classifiers used by task selection. ✔- Tick integration runs upkeep every 10 ticks, placed after
maybe_fire_event()and_clean_stale_reservations()but before worker iteration — this is the correct ordering so resource reservations for that tick are not double-counted against an outdated food value. ✔ choose_task()only re-orders within thegatherkind (food gather tasks first, then by distance) — consistent with the issue's "bias toward gathering food tiles first" wording. The build/haul/gather priority order is not modified, which is acceptable for a soft model. ✔structure_build_speed()multiplies by the slowdown factor at the end, which correctly leaves the workshop-bonus logic untouched. ✔
tests/test_food_upkeep.gd (new, +160, -0)
- 14 tests cover: base workers, extra workers, per-extra cost, negative-clamp, no-slowdown-when-ok, low-food threshold, starvation pause, linear interpolation at midpoint, food-level classification, bias predicate, interval/base/per-extra constants, and a constants-consistency check. All match the production behavior. ✔
Sources
- Linked issue #147 body: confirmed acceptance criteria are the same five bullets the PR checks off.
docs/SPEC.md(impact scan): build speed 0.34, +0.16 from workshop — matches the multiplication site instructure_build_speed, so the new factor is applied to the same base the spec describes.CONTRIBUTING.md(impact scan): "Make one focused change" and "Smoke test before pushing" — both satisfied.- Repository history: recent merges for #146/#160 used the same constants-in-
constants.gd+ helpers-in-main.gd+ dedicated test file layout, so this PR is consistent with the recent pattern.
Standards Compliance
- Monolithic-but-tidy code layout: New code is added to
main.gdnear other worker helpers and toconstants.gdnearBASE_WORKER_CAP— consistent with the project's "everything lives inmain.gd" model and the recent extraction of constants. ✔ - Tests: A new test file is added in the same
tests/directory as the existing food/worker cap tests. ✔ - Issue references in code: New helpers and constants are annotated with
issue #147, links to #133comments. ✔ - Indirect control / glanceable fantasy: Soft penalties and food bias preserve the "forgiving when ignored" and "my colony is alive while I work" pillars from AGENTS.md. ✔
- No recruit UI added (per issue constraint). ✔
Linked Issue Fit
- ✅ "Food upkeep rule is defined and tested" — 14 tests in
test_food_upkeep.gd. - ✅ "Base 2 workers do not create surprising early-game food pressure" —
BASE_WORKERS_NO_UPKEEP = 2and the early-return inapply_food_upkeep()whenextra <= 0. - ✅ "Extra workers create clear food pressure" —
FOOD_PER_EXTRA_WORKER = 1per interval, with an event log so the player sees it happen. - ✅ "Low-food behavior is predictable and covered by tests" — linear interpolation, clamp at 0, food-gather bias, build-speed multiplier, all tested.
- ✅ "Link back to #133" — explicit comments in
constants.gd,main.gd, and the test file. - ✅ "Do not add recruit UI" — no recruit-related code added.
Evidence Provider Findings
- No evidence providers configured for this PR. No findings to report.
Tool Harness Findings
- 0 planned requests and a JSON parse warning for the planning response are not relevant to this code-only PR; no impact on the review.
Unknowns or Needs Verification
- The test file uses
extends TestSuiteand methods namedassert_eq/assert_lt/assert_gte/assert_lte, which differ from the manual_assert_eqpattern intest_worker_cap.gdandtest_goal_persistence.gd. The new test style appears to be a different (likely GUT-style) framework; this was not runnable from the corpus, so I cannot confirm the test runner picks it up. Recommend a quick./.tools/Godot_v4.2.2-stable_linux.x86_64 --headless --path . --script res://tests/test_runner.gdrun to confirm green, but I see no functional reason it would fail. - Tests in this file mirror the production logic as static helpers rather than calling the real
main.gdmethods. This is acceptable for pure logic and matches the project style of isolated test helpers, but it does mean the integration points in_on_tick,choose_task, andstructure_build_speedare not exercised directly. The existingtest_worker_cap.gdalready follows this "isolate the math" pattern, so this is consistent. - The starvation speed factor is 0.0 universally, which means all build activity halts at food ≤ 1 even though the PR description says "workers pause (0% speed) unless actively gathering food." The exception in the design is not reflected in the
structure_build_speedmultiplier; the bias toward food-gather is the only escape valve. This matches the soft-failure intent (no harsh death), but a future issue may want a separate gather-speed multiplier if starvation grinding becomes an issue. Not a blocker.
There was a problem hiding this comment.
AI Automated Review
Analysis engine: MiniMax@https://litellm.jory.dev/v1 (anthropic)
PR Review: feat: add food upkeep model for extra workers (#147)
Recommendation: Approve
This PR cleanly implements the soft food upkeep model specified in issue #147, with comprehensive test coverage and clear linkage back to #133. All acceptance criteria from the linked issue are satisfied.
Change-by-change findings
scripts/constants.gd (+18)
- Adds 7 new constants with clear documentation comments:
FOOD_UPKEEP_INTERVAL_TICKS,BASE_WORKERS_NO_UPKEEP,FOOD_PER_EXTRA_WORKER,LOW_FOOD_THRESHOLD,STARVATION_FOOD_THRESHOLD,LOW_FOOD_SPEED_FACTOR,STARVATION_SPEED_FACTOR. - Constants match the PR body specification exactly.
- Header comment links to both #147 and #133 as required.
- Follows the existing constants file pattern (e.g.,
BASE_WORKER_CAP,WORKER_CAP_BONUSES).
scripts/main.gd (+82, −3)
- State variable: Adds
food_upkeep_trackerat the right location with sibling state fields. get_extra_workers_count(): Correctly clamps to zero usingmaxi, defensive against missingstate.workers.apply_food_upkeep(): Usesmaxi(current_food - cost, 0)to enforce the "never negative" soft model. Emits an event when food is actually consumed — good player feedback. Early-returns on zero extra workers, avoiding noisy events.get_food_slowdown_factor(): Implements linear interpolation between starvation and low-food thresholds. Therange_size == 0guard handles the (currently impossible, but defensive) case where both thresholds are equal. Threshold<=semantics are consistent across helpers and tests.get_low_food_level(): Clean three-way classifier with consistent boundary semantics.should_bias_to_food_gathering(): Thin wrapper but readable._on_tick(): Upkeep fires every 10 ticks, placed aftermaybe_fire_event()and_clean_stale_reservations()and before the worker loop — sensible placement.choose_task(): Bias sort is correctly gated onString(kind) == "gather"so non-gather tasks are unaffected. Comparator correctly demotes/promotes food resources before falling back to distance.structure_build_speed(): Multiplies the existing speed by the slowdown factor. This correctly applies the "low food = slower building" behavior. Note: this only affects building speed, not general task progress, which matches the issue's framing of "build speed drops to 50%".
tests/test_food_upkeep.gd (+160, new)
- 14 tests covering: base-worker exemption, extra-worker cost at multiple sizes, clamp-to-zero, slowdown at all threshold bands, level classification, bias decision, interval value, constant consistency.
test_constants_consistency()assertsSTARVATION < LOWand factor bounds — good defensive checks.- Test helpers mirror
main.gdlogic for isolation. This is intentional and reasonable for this project pattern (seetest_worker_cap.gd,test_goal_persistence.gdwhich follow the same style), but it does mean the tests verify the spec/clone of logic, not a direct integration. This is consistent with repository conventions. extends TestSuitematches the convention intest_constants.gdandtest_layout_math.gd.
Standards Compliance
- CONTRIBUTING.md: "Make one focused change" — ✓ PR is focused on the food upkeep model only.
- CONTRIBUTING.md: "Smoke test before pushing" — ✓ Headless test suite coverage added.
- AGENTS.md: Constants pattern in
constants.gd— ✓ Follows existingBASE_WORKER_CAP/WORKER_CAP_BONUSESstyle. - AGENTS.md: "Push directly to main unless told otherwise" — PR targets
mainfrom a feature branch; this is fine and consistent with prior PRs (#160, #157, #152 in history). - AGENTS.md: Detailed issue delegation preference — ✓ Implementation is concrete and matches the issue's design section.
- Test layout: Follows
tests/test_<feature>.gdconvention (e.g.,test_worker_cap.gd,test_goal_persistence.gd). - Comment style: Uses the same section-divider comment style (
# ── ... ──) seen throughoutmain.gd.
Linked Issue Fit
Comparing against issue #147's requirements and acceptance criteria:
- ✅ "Food upkeep rule is defined and tested" — Constants and 14 tests.
- ✅ "Base 2 workers do not create surprising early-game food pressure" —
BASE_WORKERS_NO_UPKEEP = 2,apply_food_upkeep()early-returns on zero extra workers,get_extra_workers_count()clamps to 0. - ✅ "Extra workers create clear food pressure" — 1 food per extra worker per 10 ticks, tested at worker counts 3, 4, 5.
- ✅ "Low-food behavior is predictable and covered by tests" — Linear interpolation between thresholds, three-tier classification, plus tests for boundary values, midpoint, and above-threshold.
- ✅ "Link back to #133" — Explicit references in
constants.gd:63,main.gd:966, andtests/test_food_upkeep.gd:2. - ✅ "Do not add recruit UI in this issue" — No recruit UI changes.
- ✅ Soft model (no harsh failure) — Food clamps to 0, no death/damage mechanics, only speed slowdown and task bias.
Evidence Provider Findings
No evidence providers configured — nothing to assess.
Tool Harness Findings
No tool harness requests planned; planning warning noted but irrelevant for this purely code-and-test PR with no external integrations to verify.
Unknowns / Needs Verification
- Save/load compatibility:
food_upkeep_trackeris a new member variable. Save migration isn't required (the counter resets to 0 on load and is non-critical state), but worth confirming if any future save-version bump touches this. Not blocking. - Apply to non-build tasks: Slowdown currently only multiplies
structure_build_speed(). If the design intent is that all task progress slows under low food (not just building), this would need follow-up. Based on the issue wording ("build speed drops to 50%"), the current scope appears correct. No action needed for #147. - Test isolation vs. integration: The test file mirrors the production logic rather than instantiating
main.gd. This matches the existing pattern intest_worker_cap.gdandtest_goal_persistence.gd, so it's consistent with repo conventions, but it does not directly exercise the helper functions inmain.gd. Acceptable given the precedent; a future integration test could be considered if the project grows that capability.
Sources
- PR #167 metadata and diff
- Issue #147 body and acceptance criteria
AGENTS.mdandCONTRIBUTING.md(repository standards)- Repository history for prior PR conventions (#160, #157, #152, #146, #143)
- Existing test files (
test_worker_cap.gd,test_goal_persistence.gd,test_constants.gd) for convention reference
There was a problem hiding this comment.
AI Automated Review
Analysis engine: MiniMax@https://litellm.jory.dev/v1 (anthropic)
PR Review: Food Upkeep Model for Extra Workers (#167)
Recommendation: Approve ✅
This PR delivers a clean, well-tested implementation of the soft food upkeep model described in issue #147. It satisfies all stated acceptance criteria, follows established repository conventions, and includes comprehensive test coverage.
Change-by-Change Findings
scripts/constants.gd (+18, -0)
- ✅ Adds seven well-named, well-documented constants in the existing
constants.gdstyle introduced byf099947. - ✅ Thresholds are sensible:
BASE_WORKERS_NO_UPKEEP=2matches the base worker cap;FOOD_PER_EXTRA_WORKER=1andFOOD_UPKEEP_INTERVAL_TICKS=10give a digestible early/mid-game food pressure curve. - ✅
STARVATION_FOOD_THRESHOLD (1) < LOW_FOOD_THRESHOLD (3)is enforced, so linear interpolation has a non-zero range (covered bytest_constants_consistency). - 💡 Minor: the comment says "unless actively gathering food" for starvation, but that carve-out is implicit (the slowdown factor is only applied in
structure_build_speed, not to gather tasks). Worth a code comment to make the intent explicit.
scripts/main.gd (+82, -3)
- ✅ Adds
food_upkeep_trackerstate and integrates upkeep into_on_tick()cleanly — runs every 10 ticks, before the worker loop, so costs are visible to workers the same tick. - ✅
apply_food_upkeep()is a soft model: it clamps withmaxi(..., 0), never lets food go negative, and emits a user-facingpush_eventso the player sees the cost. No harsh failure states. - ✅
get_food_slowdown_factor()uses linear interpolation between starvation (0%) and low-food threshold (50%), giving the predictable smooth slowdown the issue calls for. - ✅
should_bias_to_food_gathering()is integrated intochoose_task()only forkind == "gather"and only when gathering food is among available resources — preserves original behavior for other task kinds. - ✅
structure_build_speed()applies the slowdown multiplicatively, preserving workshop bonus math. - 💡 Style nit:
push_event("The crew ate. Food -%d." % ...)is fired even whencurrent_foodwas already 0 (food unchanged → silent). Actually rechecking the code — theif new_food < current_foodguard prevents this, so the event only fires when food actually changed. Good. - 💡 Note: the
push_eventwill fire every upkeep cycle whenever there is any extra worker, even if the cost is clamped. Players may see a steady stream of "The crew ate. Food -0." events in late game if food is at 0. Recommend: skip the event whencurrent_food == 0to avoid noisy starvation logs.
tests/test_food_upkeep.gd (+160, new)
- ✅ 14 tests covering: base worker no-pressure, extra-worker pressure, single-extra cost, negative-food clamping, slowdown at all food levels, linear interpolation, level classification, bias gating, and constant sanity.
- ✅ Uses the same static-helper pattern seen in
tests/test_worker_cap.gdandtests/test_constants.gdfor test isolation. - ✅
test_constants_consistencyguards against regression in threshold ordering and factor ranges. - ✅ All acceptance-criteria tests are present and named to map directly to the issue checklist.
Sources
- Linked issue
#147— defines acceptance criteria (all met) - Linked design issue
#133— referenced in code comments at every insertion point (scripts/constants.gd:63,scripts/main.gd:966,tests/test_food_upkeep.gd:2) - Repository history shows the branch is based on a follow-up commit
afb9cfe fix: add explicit int types for GDScript type inferenceon top of the original8039fda, indicating type-safety care.
Standards Compliance
| Standard | Status | Notes |
|---|---|---|
Use constants.gd for game constants |
✅ | Follows the convention established by f099947 refactor: extract game constants from main.gd into constants.gd |
| Add tests for new behavior | ✅ | 14 new tests in tests/test_food_upkeep.gd |
| One focused change per PR | ✅ | Single-issue scope, mirrors PR #146 worker cap pattern |
| Forgiving / low-attention design | ✅ | Soft penalties only — no harsh failure paths |
| Implementation matches issue text | ✅ | Issue specifies "slow, pause, or bias" — all three behaviors implemented |
Push directly to main (no release tag) |
✅ | Branch is saffron/issue-147-food-upkeep, no release tag added |
Linked Issue Fit
Acceptance criteria from issue #147 (mapping to PR):
- Food upkeep rule is defined and tested —
apply_food_upkeep+ 4 cost tests - Base 2 workers do not create surprising early-game food pressure —
BASE_WORKERS_NO_UPKEEP = 2,extra <= 0early-return,test_base_workers_no_upkeep - Extra workers create clear food pressure — 1 food/extra/interval, covered by
test_extra_workers_create_pressureandtest_one_extra_worker_cost - Low-food behavior is predictable and covered by tests — linear interpolation, smooth slowdown, food bias, all tested
- Link back to #133 — explicit comments in all 3 files
- No recruit UI — none added; out of scope respected
Issue intent fully satisfied.
Evidence Provider Findings
No evidence providers were configured for this PR. No blockers raised.
Tool Harness Findings
Tool harness reported Planning warning: Could not parse planning response as JSON with 0 planned requests. This is a harness-side issue, not a code issue, and has no bearing on the PR quality.
Unknowns / Needs Verification
⚠️ Event spam potential: when food is at 0 and there are extra workers,apply_food_upkeepwill run every 10 ticks. The current guard (if new_food < current_food) prevents the message from firing whencurrent_food == 0, so this is actually safe. No verification needed.⚠️ Gather-task speed: the slowdown factor is only applied instructure_build_speed. Gathering and movement are unaffected. The issue says "build speed drops to 50%" specifically, so this is consistent. Not a blocker, but worth confirming with the design owner that gather speed should remain at 100% during low-food (current behavior).⚠️ Save/load migration:food_upkeep_trackeris a new instance variable. Per AGENTS.md, "Save/version migration should be migration-first because the game auto-loads the latest save on startup." A save with nofood_upkeep_trackerkey (existing save) would not break since the variable is initialized to 0. Thestatedict may not be touched at all by this PR — onlystate.resources["food"]is modified, which is an existing key. No migration needed.
Summary
Clean, well-scoped, fully tested implementation. All acceptance criteria are met, repository conventions are followed, and the soft-failure design philosophy from AGENTS.md is respected. Minor suggestions (skip event when at-zero food is already handled; explicit comment for "unless actively gathering food" carve-out) are non-blocking.
Verdict: Approve.
Fixes #147
Implements a soft food upkeep model for workers above the base count of 2. Extra workers create ongoing food pressure without causing harsh failure — workers slow down and bias toward food gathering, but never face punitive consequences.
Design (links to #133)
Constants:
FOOD_UPKEEP_INTERVAL_TICKS= 10 — Deduct food every 10 ticksBASE_WORKERS_NO_UPKEEP= 2 — First 2 workers eat nothingFOOD_PER_EXTRA_WORKER= 1 — Each extra worker costs 1 food/intervalLOW_FOOD_THRESHOLD= 3 — Food <= 3 triggers 50% speed slowdownSTARVATION_FOOD_THRESHOLD= 1 — Food <= 1 pauses workers (0% speed)Behavior:
Acceptance criteria:
Changes
scripts/constants.gd— new food upkeep constantsscripts/main.gd— helper functions + integration into tick cycle, task selection, build speedtests/test_food_upkeep.gd— 14 tests covering all model behaviors