Skip to content

Fewer stopped runs: navmesh plan check, refused-step fallback, one-call wipe recovery, party-short grace, pre-run check, simpler resume #187

Closed

Description

@pjerra

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, every via waypoint and every direct hop 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_run runs the check first and refuses a plan that fails, unless force is given.
  • Copy into dungeons/zulfarrak.json the fixes the workers made in their private copies, after the check confirms each one:
    • leg antusul_a2: the 1289.9 816.1 9.4 via point (it sits on the z20 layer), and the direct flag on 1297.1 827.9 9.3 (Haiku s4);
    • leg 4 (antusul): the direct step to 1535 834 9.0, which a lead on the ledge cannot see or reach (Haiku s2 and s5);
    • leg 8 (velratha): the walk to 1579.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).
  • 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):

  1. take a navmesh path to the same point;
  2. if that fails, go on to the next point of the leg;
  3. 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:

  1. releases the dead bots to the entrance;
  2. revives them, using the commander's own GM rights when it has them (the same rule as now);
  3. gathers the party at the last cleared safe spot, waits for full health and mana, and rebuffs;
  4. 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_status shows them ("pull group 5 of 5"). Today from_pull_group is 0-based.
  • Allow from_pull_group together with start_here, and accept resume with from_pull_group (it is ignored). Both models had refused calls on these.

Done when

  • plan check zulfarrak passes 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_recover call and the run finishes.
  • make test covers each change.

Related: Sagiroth/TortoiseBots#986 (dungeon report), Sagiroth/TortoiseBots#984 (dungeon events).

Activity

  1. pjerra commented on Oct 15, 2026

    @pjerra
    OwnerAuthor

    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.

  2. pjerra commented on Oct 15, 2026

    @pjerra
    OwnerAuthor

    Correction after checking the points on the real map-209 navmesh (plan-check work, 2026-10-15):

    • Velratha pull group 9: confirmed. The path ends 11 yd short of the pack and the look point sits 21 yd back on it (1605.475 1104.167, 31 yd away), so the run stops on "no view". Fix: the driver clamps the look point to within the see radius.
    • antusul_a2 via 1289.9 816.1 / direct 1297.1 827.9, leg 4 direct 1535 834, leg 8 walk to 1579.4 1041.4: the points themselves are fine on the mesh. The refusals came from where the lead bot stood at the time (stuck on a z18 ledge, dragged by a fight, inside a small mesh pocket). These are run-time positions and belong to part 2 (fallback), not the plan check. The plan drops the needless direct flags on these hops, since the navmesh already has a straight path.
    • Velratha pull group 12: passes the check; the live refusal is server-side (tank cannot reach a firing position). Removed from the plan anyway: both runs that removed it finished, and it lies beyond the boss.
    • A ray check on pack look points gives about 25 false positives on ZF (the ray is a walkability stand-in and buildings and ledges block it), so packs are checked only for reachability and distance.
  3. pjerra commented on Oct 15, 2026

    @pjerra
    OwnerAuthor

    Related ideas from jrad7/mod-dungeon-clear (AGPL-3.0-or-later):

    • Corpse handling: PreventBotRelease keeps dead bots in the instance as corpses. PostCombatRez holds 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.
    • Cleared detection out of sight (for the run leader): MapPersistentState::GetCreatureRespawnTime(dbGuid) (core Maps/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.
  4. pjerra commented on Oct 15, 2026

    @pjerra
    OwnerAuthor

    Pull-test findings (zf-g9-6cd0b13, 2026-10-15). A pulltest on Velratha pull group 9 in a fresh instance never reached its pull:

    • The start gathers at the group's gather spot (1586.6 1060.4), which the run itself flags as 21 yd from a live pack (group 7 of 11), while a safe spot needs 30 yd. In a fresh instance the earlier groups are alive, so the party aggroes them at the gather spot. Both attempts died there.
    • Attempt 1 also failed with "the lead bot does not hold still after .bot summon (it follows the commander): after cmd stay the lead bot was 5 yd from where it landed".
    • A pull test skips the plan check (by design), so this was not caught earlier.
      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 force is given. Also look at the lead bot drifting after the summon.
  5. pjerra commented on Oct 15, 2026

    @pjerra
    OwnerAuthor

    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?).

  6. pjerra commented on Oct 15, 2026

    @pjerra
    OwnerAuthor

    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).

  7. pjerra commented on Oct 16, 2026

    @pjerra
    OwnerAuthor

    Status 2026-10-16:

    • Part 2 (refused step): partly done by Walk steps are sent on the navmesh floor; an unreachable step is resent snapped #201 (merged). An unreachable walk step is now sent on the navmesh floor, resent snapped (or raised 3 yd), and only then halved. Still open: the fallback for no-los and too-far on direct steps and cmd move, skipping to the next leg point when the navmesh path also fails, and logging every fallback in events.json.
    • Parts 3, 4 and 6: unchanged (one-call dungeon_recover, party-short grace, resume numbering).
    • Part 5 (pre-run check): the in-instance talent answers still don't arrive (see the comment above).
  8. self-assigned this
    on Oct 25, 2026
  9. pjerra commented on Oct 26, 2026

    @pjerra
    OwnerAuthor

    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).

  10. added a commit that references this issue on Oct 27, 2026
  11. pjerra commented on Oct 27, 2026

    @pjerra
    OwnerAuthor

    Fixed by #306 (merged into dev, 80470da): observe auto-resume (off by default), hire-time talents in the preflight, both pull-group numbers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions