Repository navigation
Fewer stopped runs: navmesh plan check, refused-step fallback, one-call wipe recovery, party-short grace, pre-run check, simpler resume #187
Description
Why
In the Zul'Farrak model comparison (2026-10-15: 4 runs on Sonnet · medium, 4 on Haiku · max, server 19z4, binary 9adde948), every run stopped and needed a resume, on both models. Most stops were not the model's mistakes. The same few things stopped the run, and then the model spent minutes and tokens restarting it. The clear times show the cost: 66 min for a run with 0 stops, 116 min for one with many. Report: https://claude.ai/artifact/WtPcFDJCQizHqTZhAMaQLW (private).
The fixes below are in order of effect. Each one can be its own PR.
1. Check the plan against the navmesh before any run
- A
plan check <dungeon>(CLI and RPC) walks every leg, everyviawaypoint and everydirecthop on the dungeon's navmesh. It fails on a point that cannot be reached, or on a direct hop without line of sight, naming the leg, the point and the reason. dungeon_runruns the check first and refuses a plan that fails, unlessforceis given.- Copy into
dungeons/zulfarrak.jsonthe fixes the workers made in their private copies, after the check confirms each one:- leg
antusul_a2: the1289.9 816.1 9.4via point (it sits on the z20 layer), and the direct flag on1297.1 827.9 9.3(Haiku s4); - leg 4 (
antusul): the direct step to1535 834 9.0, which a lead on the ledge cannot see or reach (Haiku s2 and s5); - leg 8 (
velratha): the walk to1579.4 1041.4, refused as unreachable 8× (Sonnet s2); - Velratha pull group 12 (the lone 5649 at
1714.4 1218.6), which the tank cannot reach (Sonnet s1).
- leg
- A test runs the check on every shipped plan. This covers the case Walks start from the floor under the bot, not a ledge over its head (Keystone look walk no longer refused) #160 tried to cover, but against the plan as it ships.
Stops it removes: 5 of the 8 runs.
2. Let the driver handle a refused step itself
When a direct step or a cmd move is refused (no-los, unreachable, too-far):
- take a navmesh path to the same point;
- if that fails, go on to the next point of the leg;
- stop the run only if both fail. Log every fallback in events.json with the reason.
Today one refused step ends the run (state=failed), and the model has to work out a resume.
3. One dungeon_recover call after a wipe
One RPC call that:
- releases the dead bots to the entrance;
- revives them, using the commander's own GM rights when it has them (the same rule as now);
- gathers the party at the last cleared safe spot, waits for full health and mana, and rebuffs;
- resumes from the pull group that wiped, with the same binding and instance.
Today the model types this by hand: 16 to 38 GM commands per run (revive, summon, appear, release, tele) and 10 to 35 minutes. In Sonnet s1 the model once revived bots in place next to a live pack, and all four died again. The call has to release first.
4. A grace period before "party short"
- Before failing with "party short", wait up to
party_short_grace_s(default 120) for dead members to be back up, for example by a rez or a soulstone. Fail only if they are still dead. - In Sonnet s4 the run failed at leg 4 while the tank and warlock were back up 2 minutes later on their own.
5. A pre-run check
Before enter, check and fail fast with a clear message if any of these is false:
- the commander is at the dungeon portal on the right map. In Sonnet s1 it was parked in Uldaman; in s2 it was on map 0.
- the commander's level is at least the hire level, since hires are capped to it. Batch 1 had commanders at 44, 45 and 46.
- all hires are alive, with gear and talents.
6. Simpler resume options
- Number pull groups the same way
dungeon_statusshows them ("pull group 5 of 5"). Todayfrom_pull_groupis 0-based. - Allow
from_pull_grouptogether withstart_here, and acceptresumewithfrom_pull_group(it is ignored). Both models had refused calls on these.
Done when
plan check zulfarrakpasses on the shipped plan and fails on the old copy (sha c86c1e62ee5a) with the points above.- In a live ZF run, a refused step falls back without stopping the run; it shows in events.json.
- A forced wipe in a live run is recovered with one
dungeon_recovercall and the run finishes. make testcovers each change.
Related: Sagiroth/TortoiseBots#986 (dungeon report), Sagiroth/TortoiseBots#984 (dungeon events).
Activity
pjerra commentedon Oct 15, 2026
Added from Haiku batch 2 (zf-ab-2-s2): at leg 8 pull group 9 of 12 (pack at 1636 1105 9) the driver's look move ends 25–31 yd short (move target 1605.47 1104.17, the nearest walkable point), and the run stops on "could not get a view of the pack". Lowering look_standoff from 21 to 15 did not help. Part 1 (plan check) should also check that every pull group's look point is reachable and has line of sight to the pack; part 2 (fallback) should try another look point around the pack before stopping.
pjerra commentedon Oct 15, 2026
Correction after checking the points on the real map-209 navmesh (plan-check work, 2026-10-15):
pjerra commentedon Oct 15, 2026
Related ideas from jrad7/mod-dungeon-clear (AGPL-3.0-or-later):
PreventBotReleasekeeps dead bots in the instance as corpses.PostCombatRezholds the run while a living rezzer raises them, with a 90 s out-of-combat budget. Useful for part 3 (dungeon_recover) before any release.MapPersistentState::GetCreatureRespawnTime(dbGuid)(coreMaps/MapPersistentStateMgr.h:88) says whether a database-spawned creature is dead even when its grid is unloaded. The plan would carry each pack's spawn guids.pjerra commentedon Oct 15, 2026
Pull-test findings (zf-g9-6cd0b13, 2026-10-15). A
pullteston Velratha pull group 9 in a fresh instance never reached its pull:Suggested for part 5 (pre-run check): refuse a pulltest or a from_pull_group start whose gather spot is under the safe distance from a pack that is still alive, unless the earlier groups are cleared or
forceis given. Also look at the lead bot drifting after the summon.Plan check: walk every plan point on the navmesh before a run (part 1…
pjerra commentedon Oct 15, 2026
Pre-run check (part 5): with #191 the talent check asks all bots at once and waits one 5 s window, but in zf-proof-191 again 5 of 5 bots gave no answer. The hire-time asking is answered in about 40 ms as chat from the bot, but the asking after entering the instance never gets a reply packet. Find out what the bot does with the request at that point (is it handled at all once in the instance? is the reply sent to another channel?).
pjerra commentedon Oct 15, 2026
Status: part 1 (plan check) merged as #188; the pre-run talent check is faster in #191 (5 s, but the answers still don't arrive in the instance). Recovery of a party dead or outside the dungeon, and re-entry fixes, are in #192. Still open: the refused-step fallback (part 2), a one-call dungeon_recover (part 3, partly via recover_wipe), the party-short grace (part 4), and resume numbering (part 6).
pjerra commentedon Oct 16, 2026
Status 2026-10-16:
unreachablewalk step is now sent on the navmesh floor, resent snapped (or raised 3 yd), and only then halved. Still open: the fallback forno-losandtoo-farondirectsteps andcmd move, skipping to the next leg point when the navmesh path also fails, and logging every fallback in events.json.pjerra commentedon Oct 26, 2026
Part 2a done: TortoiseBots fix/ta187-legs (36dd2b9a) raises the legged-walk cap from 6 to 16 and counts a leg that ends back at any earlier leg's end as a repeat (the Windhorn step 1179 too-far pause). In integ/20d, reviewed, deployed as all-fixes-20d2. Still open here: parts 3b (observe.auto_resume), 5a (preflight reuses hire-time talents), 6a (both pull-group numbers in messages).
Merge pull request #306 from pjerra/fix/ta187-agent
pjerra commentedon Oct 27, 2026
Fixed by #306 (merged into dev, 80470da): observe auto-resume (off by default), hire-time talents in the preflight, both pull-group numbers.