class_name Decision ## Static utility — picks the next Job for a pawn via the 5-layer pipeline. ## ## Layer order (top wins): ## 1. Incapacitation — has_method probe; implementation lands Phase 9. ## 2. Forced job — pawn.forced_job; cleared (consumed) on pick. ## 3. Status interrupt — stub; implementation lands Phase 9. ## 4. Work providers — iterated highest priority first; first non-null Job wins. ## 5. Idle — returns null (caller interprets as "stand still"). ## ## Callers pass the world-scoped provider list so Decision is fully stateless. ## This makes it safe to call from any pawn tick without shared mutable state. ## Returns the best Job for `pawn`, or null if the pawn should idle. ## ## `work_providers` is the current world-scoped list of WorkProvider nodes ## (e.g. [RestProvider]). Order does not matter — the method sorts by priority. ## `pawn` is duck-typed: must expose .pawn_name, .forced_job, and ## has_method("is_incapacitated"). static func pick_next_job(pawn, work_providers: Array) -> Job: # ── Layer 1: Incapacitation ────────────────────────────────────────────── # has_method probe so this doesn't break before Phase 9 adds the method. if pawn.has_method("is_incapacitated") and pawn.is_incapacitated(): return null # ── Layer 2: Forced job ────────────────────────────────────────────────── if pawn.forced_job != null: var fj: Job = pawn.forced_job pawn.forced_job = null Audit.log("decision", "%s: forced '%s'" % [pawn.pawn_name, fj.label]) return fj # ── Layer 3: Status interrupt ───────────────────────────────────────────── # Phase 9: status interrupt (Bleeding → seek bed/doctor) lands here. # ── Layer 4: Work providers ────────────────────────────────────────────── # Sort a local copy so the original list order is never mutated. var sorted: Array = work_providers.duplicate() sorted.sort_custom(func(a, b): return a.priority > b.priority) for wp in sorted: var j: Job = wp.find_best_for(pawn) if j != null: Audit.log("decision", "%s: %s → '%s'" % [pawn.pawn_name, String(wp.category), j.label]) return j # ── Layer 5: Idle ──────────────────────────────────────────────────────── # No log — would fire every tick for every idle pawn (too chatty). return null