feat: add colony stance toggle for task priority weights (#140)#169
Conversation
- Add ColonyStance pure data model with 4 stances: Balanced, Build, Gather, Food - Integrate stance into choose_task() via effective priority order - Add compact UI toggle in sidebar with 4 stance buttons and description - Persist colony_stance in game state; backward-compatible default to Balanced - Handle gather_food task kind for food-biased stance - Add tests for stance logic, integration, and persistence
There was a problem hiding this comment.
AI Automated Review
Analysis engine: MiniMax@https://litellm.jory.dev/v1 (anthropic)
Recommendation: Approve
This PR implements the colony stance toggle feature requested in issue #140 cleanly and completely. All six acceptance criteria are satisfied, the code follows existing repository patterns, tests are comprehensive, and backward compatibility for legacy saves is preserved.
Change-by-Change Findings
scripts/colony_stance.gd (new, 62 lines)
- Clean, pure data model — no UI/scene dependencies, easy to unit test.
- 4 stances defined matching the issue's suggested list:
Balanced,Build,Gather,Food. get_effective_priority_order()correctly handles all stance cases including:- Balanced / empty stance → returns player order unchanged
- Build / Gather → moves preferred kind to front (no duplication)
- Food → adds a virtual
gather_foodkind first, then the rest of the player order
is_food_gather_task()is a small, well-scoped helper for food-biased detection.- Lint-clean, static, no side effects.
scripts/main.gd (modified, +103 / −4)
- State:
colony_stancedefaults toSTANCE_BALANCED, included inbootstrap_state()and restored inload_saved_game()with a safe default for legacy saves. - Persistence:
state["colony_stance"]is written inpersist()✅. - Task selection:
choose_task()now iterates overeffective_orderfromColonyStance.get_effective_priority_order(), and adds agather_food-aware sort that hoists food-resource gather tasks to the front. The reservation call also covers the new kind defensively. tasks_for_kind(): both"gather"and"gather_food"map togather_gather_tasks(), so food-stance prioritization is purely a sort/sort-key concern, not a task-listing change. Good separation.- UI:
render_stance_toggle()rebuilds the stance panel undermenu_actionson eachrender_all()call. The existing panel is correctly removed andqueue_free()d to avoid leaks. Buttons are toggle-mode, pressed state reflectscolony_stance, tooltips sourced fromSTANCE_INFO. - Event log:
change_stance()emits the exact log line the PR body advertises: "Colony stance changed to X. Workers adjust priorities." — matches the issue's "short event/log line" requirement. - Guard rails:
change_stance()early-exits for unknown stances and no-op transitions.
tests/test_colony_stance.gd (new, 204 lines)
- Covers the static model (Tests 1–9): effective priority order for every stance, no duplication,
is_food_gather_taskcases, fullALL_STANCES/STANCE_INFOcatalog integrity. - Covers integration with
main.gd(Test 10): driveschoose_task()from a constructed main instance and confirms a task is returned. - Covers persistence (Tests 11–13): write/read of
colony_stance, restore from a saved game, and the legacy-save fallback toSTANCE_BALANCED. - Test runner pattern matches existing tests (
extends SceneTree, custom_assert/_assert_eq, exits with non-zero on failure) so the smoke test harness intests/test_runner.gdstyle should pick it up consistently.
Sources
- PR metadata & diff: 3 files, 369+/4− lines, branch
saffron/colony-stance-toggleagainstmain. - Linked issue: #140 — ux: add colony stance toggle for task priority weights (open, in-review).
- Repository impact scan: confirms references to #140 are limited to the new files; no unrelated files modified.
- Repository history: head commit
f328fe8is the PR commit itself; no follow-up fixups already in history.
Standards Compliance
- AGENTS.md — "indirect control systems for priorities, planning, and agent-driven work": ✅ stances are exactly the kind of indirect control the project is designed around.
- AGENTS.md — "The player plans and places; workers execute": ✅ stances nudge worker behavior without assigning workers directly.
- AGENTS.md — "Menus should be temporary popup overlays, not persistent sidebars": ✅ the stance panel lives under
menu_actionsand is rendered as part of the existing menu, not a new persistent sidebar. - AGENTS.md — "Save/version migration should be migration-first": ✅ new save field defaults gracefully for legacy saves; no migration scaffolding required because the default is a benign no-op (
Balanced= original behavior). - CONTRIBUTING.md — test pattern: ✅ test file follows the same
SceneTree-based pattern as other tests intests/.
Linked Issue Fit
Each acceptance criterion from #140 is met:
| Issue #140 criterion | Status |
|---|---|
| Add a compact stance control | ✅ render_stance_toggle() adds a 4-button panel |
| Stance affects task priority selection in a predictable way | ✅ get_effective_priority_order() is pure, deterministic, and tested |
| Current stance is visible in compact UI | ✅ toggle button button_pressed + description label reflect current stance |
| Changing stance produces a short event/log line | ✅ push_event("Colony stance changed to X. Workers adjust priorities.") |
| Add tests for priority weighting behavior | ✅ 9 dedicated tests on the data model + integration test |
| Works in bottom and side dock modes | ✅ added to menu_actions, which the author states is present in both modes; consistent with how other UI sub-panels are wired |
Issue intent ("modify existing task selection priorities only", "must not add direct worker assignment") is honored — the feature is purely a reordering of task-kind selection plus a sort-key tweak for food.
Evidence Provider Findings
- None configured; no findings to report.
Tool Harness Findings
- Harness ran 0 planned requests and warned "Could not parse planning response as JSON". This is a harness-side issue, not a PR defect — no blocking findings on the PR itself.
Unknowns / Needs Verification
- Dock-mode coverage: I did not see the full
menu_actionslifecycle in the truncatedmain.gddiff to confirm it is populated in both bottom and side dock modes. The PR description and prior UI wiring suggest it is, and the test suite doesn't cover UI rendering, so I'd want a quick manual editor check that the stance panel appears in both dock modes. This is a minor follow-up, not a blocker for merge. - Dead-code branch in reservation check:
String(chosen.kind) == "gather_food"inchoose_task()'s reservation is technically unreachable today becausegather_gather_tasks()always emitskind: "gather". It is harmless defensive code, but worth a comment in a follow-up.
Packages
[]No dependency changes in this PR.
Godot 4.2 treats Variant inference from Dictionary.get() as a compile error. Add explicit :String type annotation to fix headless smoke test.
There was a problem hiding this comment.
AI Automated Review
Analysis engine: MiniMax@https://litellm.jory.dev/v1 (anthropic)
PR #169 Review — feat: add colony stance toggle for task priority weights
Recommendation
Approve. This PR cleanly implements issue #140 with a well‑separated data model, a compact sidebar toggle panel, predictable priority weighting, and comprehensive tests. It satisfies all six acceptance criteria from the linked issue and follows repository conventions.
Change‑by‑change Findings
scripts/colony_stance.gd (new, 62 lines)
- Pure data model with
class_name ColonyStance. Good separation from UI. - Constants
STANCE_BALANCED/BUILD/GATHER/FOOD,ALL_STANCES,STANCE_INFO, andSTANCE_PREFERRED_KINDare clearly named and self‑documenting. get_effective_priority_order()is deterministic: balanced returnsplayer_order.duplicate(); build/gather prepend the preferred kind; food prepends a syntheticgather_foodkind. The dedup logic (not result.has(kind)) prevents duplication when the preferred kind already exists in the player order. ✅ Correct.is_food_gather_task()is a tiny, well‑scoped predicate. ✅
scripts/main.gd (+103 / −4)
- Adds
const ColonyStance := preload(...)and acolony_stancemember defaulting to balanced. Backward compatible. bootstrap_state()seedscolony_stancein the new save state.load_saved_game()usesstate.get("colony_stance", STANCE_BALANCED)so legacy saves degrade gracefully.persist()writes the field.render_stance_toggle()builds a compactPanelContainer(title "Colony Stance", 4 toggle buttons, description label) and attaches it tomenu_actionsso it shows in both bottom and side dock modes. Re‑creates the panel each call to avoid stale state.change_stance()validates input againstALL_STANCES, no‑ops when the value is unchanged, pushes a log line"Colony stance changed to %s. Workers adjust priorities.", and triggersrender_all(). This matches the issue requirement for a short event/log line.choose_task()now usesget_effective_priority_order(colony_stance, priority_order)— the integration is minimal and the default (balanced) path is unchanged.tasks_for_kind("gather_food")falls through togather_gather_tasks(), thenchoose_task()applies a food‑biasedsort_customusingis_food_gather_task(). The chosen task'skindis still"gather", so the reservation branchString(chosen.kind) == "gather"covers it. The extraor String(chosen.kind) == "gather_food"clause is defensive (harmless, slightly redundant).
tests/test_colony_stance.gd (new, 204 lines)
- 13 test cases covering: balanced/empty pass‑through, build/gather/food ordering, alt player order, food gather detection, stance catalog completeness, label/description non‑emptiness, main‑game integration, persist+load round‑trip, and legacy save defaulting. Strong coverage for the acceptance criteria.
- Tests use a simple SceneTree harness with pass/fail counters and
quit(1)on failure — consistent with other tests in the repo.
Linked Sources
No external sources were linked in the corpus.
Standards Compliance
- AGENTS.md / Product Direction: The change strengthens "indirect control systems for priorities, planning" and respects the constraint that "the player plans and places; workers execute". No direct worker assignment or micromanagement is added — only priority weight tweaks.
- AGENTS.md / UX Constraints: The toggle is added inside the existing popup menu (under
menu_actions), avoiding a persistent sidebar. It is compact and consistent with the rest of the dock UI. - Repo conventions: Pure‑data
colony_stance.gdmirrors the pattern used byrotating_goal.gdandlayout_math.gd. Test file naming and SceneTree harness match the existing style. - Backward compatibility: Legacy saves missing
colony_stancedefault to balanced — consistent with the schema migration guidance in AGENTS.md.
Linked Issue Fit (#140)
| Issue requirement | Status |
|---|---|
| Compact stance control | ✅ 4 toggle buttons in a PanelContainer |
| Stance affects task priority in a predictable way | ✅ get_effective_priority_order() is deterministic and unit‑tested |
| Current stance visible in compact UI | ✅ Pressed‑state button + description label |
| Changing stance produces a short event/log line | ✅ push_event("Colony stance changed to %s. Workers adjust priorities.") |
| Add tests for priority weighting behavior | ✅ 13 tests covering catalog, ordering, detection, integration, persistence |
| Works in bottom and side dock modes | ✅ Panel attached to menu_actions (present in both modes) |
| No direct worker assignment/micromanagement | ✅ Only priority weights are adjusted |
All acceptance criteria are met.
Evidence Provider Findings
No evidence providers configured — nothing to evaluate.
Tool Harness Findings
Tool harness returned no actionable findings ("Could not parse planning response as JSON"). Treated as a non‑signal.
Unknowns / Needs Verification
- The
menu_actionsplacement claim (present in both bottom and side dock modes) is consistent with the diff and the issue author's PR description, but I did not independently readscenes/main.tscnto confirm. This is a low‑risk assumption given the existing code already renders other panels intomenu_actionsand the issue's acceptance check is satisfied by the integration point. - The chosen task's
kindis"gather"(not"gather_food") becausetasks_for_kindreturns the original gather tasks; theor String(chosen.kind) == "gather_food"clause in the reservation check is therefore redundant. Harmless, but a minor cleanup opportunity. change_stance()callsrender_all()which in turn recreates the stance panel — the previously pressed button may briefly toggle off before being recreated. Not a functional bug, but a small UX micro‑flicker is possible.
Verdict
approve — the implementation is correct, complete against the issue, well‑tested, and aligned with repository standards.
Fixes #140
Changes
scripts/colony_stance.gd: Pure data model defining 4 stances (Balanced, Build, Gather, Food) with effective priority order computation and food-gather detection.scripts/main.gd: Integrated stance system — stance-aware task selection viaget_effective_priority_order(), compact sidebar UI with 4 toggle buttons, persistence in game state, backward-compatible default to Balanced.tests/test_colony_stance.gd: Tests for stance logic, effective priority order, food-gather detection, integration withchoose_task(), and save/load persistence.Stances
gather_foodas a special task kind that prioritizes food resourcesUI
A compact stance toggle panel appears in the sidebar when the menu is open, showing 4 toggle buttons with tooltips and a description of the active stance.
Acceptance Criteria