Decision: remove rest from NEEDS_CATEGORIES so work fires before fallback

NEEDS_CATEGORIES used to be [&"rest", &"eat", &"sleep"]. Decision iterates
needs providers before eligible work providers and picks the first one
that returns a non-null job. EatProvider/SleepProvider correctly gate on
hunger/sleep thresholds (return null when not hungry/tired), so they
yield to work when no need is pressing. But RestProvider always returns
a job — by design, it's the catch-all fallback ("stand here"). With
"rest" in NEEDS_CATEGORIES the loop preempted on RestProvider every
tick: pawns assigned 'Rest at (50, 50)' and never reached the
chop/mine/construction providers despite valid designations.

Fix: remove &"rest" from NEEDS_CATEGORIES. RestProvider falls into the
eligible bucket; its provider.priority=0 sorts it last in the eligible
sort (chop=5, mine/construction=5, etc, win the priority tiebreaker).
work_priorities doesn't include a "rest" key so priorities.get falls
through to NORMAL=3, keeping rest eligible for everyone (the UI matrix
deliberately omits rest per Pawn.work_priorities comment).

Verified MCP runtime: fresh boot, all 3 pawns picked 'Build stone wall
at (44, 28)' instead of 'Rest at (50, 50)'; advanced to next wall site
within 5 seconds.

Bug surfaced by first real playtest with painted designations — the
in-context audit caught it within ~5 min of inspection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-05-12 13:37:21 +01:00
parent 708080a022
commit 07fb66608a

View file

@ -41,14 +41,21 @@ static func pick_next_job(pawn, work_providers: Array) -> Job:
# ── Layer 4: Work providers ──────────────────────────────────────────────
# Needs-driven categories are handled entirely by Layer-3 status interrupts
# and need-threshold providers (rest/eat/sleep/doctor fire when hunger/sleep
# and need-threshold providers (eat/sleep/doctor fire when hunger/sleep
# thresholds trigger, not via player priority). We skip the priority filter
# for them so a pawn can never accidentally starve because the player set
# &"eat" to OFF. The player-configurable list is the 7 work categories:
# construction / chop / plant / mine / crafting / haul / clean
# Doctor IS in the matrix (player can opt a pawn out of doctor duty) but
# the needs-driven "go heal yourself" path bypasses this filter at Layer 3.
const NEEDS_CATEGORIES: Array = [&"rest", &"eat", &"sleep"]
#
# Rest is NOT a need-gated category — RestProvider always returns a job (its
# job IS "stand here"), so if rest ran before elective work, pawns would
# never chop/mine/build. Rest is the eligible-bucket fallback: it sorts last
# because provider.priority=0, so any real work wins. Bug ref: 2026-05-12
# session — pawns idled with valid chop/mine/build designations because
# &"rest" was in NEEDS_CATEGORIES and RestProvider preempted everything.
const NEEDS_CATEGORIES: Array = [&"eat", &"sleep"]
# Pawn's work_priorities dict — empty dict if the field is absent (pre-Phase-17
# pawns loaded from old saves). Missing category key defaults to 3 (NORMAL) so