KEY FIX: Changed candidate filter to allow dorfs with Idle tasks:
- Before: Only dorfs with empty queues were candidates
- After: Dorfs with empty queue OR Idle task are candidates
- This fixes deadlock where all dorfs had Idle tasks and were excluded
Debug logging added:
- job_pathfinding.rs: Log job processing, candidate counts, path results
- queue_debug.rs: Show job state (Unclaimed/Calculating/Claimed/Suspended), retry count, and dorf idle status
The bug was that job_pathfinding_system filtered out ALL dorfs because:
1. job_assignment_system gave Idle tasks to dorfs with empty queues
2. task_executor_system promoted them to Active
3. job_pathfinding_system then excluded Active dorfs AND non-empty queues
- Add calculate_traversal_distance() - full A* that returns actual distance
- Add batch_calculate_traversals() for efficient batch processing
- Add Calculating state to JobState enum for dorf locking
- Add approach_target and retry_count to job Entry
- Add helper methods: is_dorf_locked, get_locked_dorfs, get_scope_for_job
- Add job_assignment config section with max_dorfs_per_job
- Create job_pathfinding.rs with main pathfinding system:
- Throttled to MAX_JOBS_PER_TICK (5) per frame
- Expanding scope: 5 -> 20 -> 200 dorfs based on retry count
- Uses full pathfinding, not provisional
- Pre-computes approach_target for assigned dorfs
- Simplify job_assignment.rs to only handle idle fallback
- Update has_fell_tree to check state (Unclaimed, Calculating, Claimed)
- Add JobState enum (Unclaimed, Claimed, Complete) replacing boolean claimed flag
- Increase provisional node limit from 256 to 4096
- Add iter_unclaimed() and claim_job_at() methods to JobQueue
- Update job_assignment to filter idle dorfs, match by distance
- Add unclaim_jobs_for_entity() for handling dorf death/failure
- Add JobId collision detection in debug builds
- Fix demo system to check Pending state for chopping dorfs
- Throttle queue_debug to every 120 ticks
- Add surface position deduplication for terrain chunks
- Add JobId (rolling u16 counter) to track jobs through their lifecycle
- Add job_id field to all Task variants (Idle, GoTo, ChopTree, HaulCargo, DropHauled)
- Add unclaim_job() method to requeue failed jobs for reassignment
- Change task failure paths to use unclaim_job() instead of complete_job_by_id()
- This allows failed jobs to be picked up by another dorf instead of being stuck
- Add to_job_kind() and get_job_id() helper methods to Task
- Fix pathfinding is_standable_tile debug logging spam
- Fix chunk bounds check in ChunkData::is_standable (was checking wrong z range)
Switch from add-then-XOR pattern to proper sequential multiply-add
avalanche. Each coordinate feeds into the next multiply, preventing
correlation between adjacent tiles when rare drops are introduced.