Repository navigation
Conversation
Both are Turtle additions that the world export already carries as map 189 spawn rows, but neither was in a plan's boss list: the Scarlet Monastery drafts only pulled them as members of a pack, so a bot run neither names them as a boss nor keeps its step count in line with the wing it clears. scarlet_library: bosses 3974, 61983, 6487. Brother Wystan (EventAI, spell list 180317 with Fireball 8402, movement_type 2) stands on the way to Arcanist Doan, so the new leg sits between Loksey's and Doan's. scarlet_graveyard: bosses 3983, 61972, 14693, 4543. Duke Dreadmoore lies in the crypt level (z -3.7, script npc_duke_dreadmoore sets him to 20 % health on reset and yells on aggro and death); his crypt pack stays needs_walk until that part is mapped. Generated with tools/dungeongen from the spec (no hand edits to the drafts). plan-check at origin/dev e50dc28: scarlet_library 88 points, 2 warnings, exit 0 -> 89 points, 1 warning (a detour on leg brother_wystan), exit 0 scarlet_graveyard 1 of 141 points fail, exit 11 -> 1 of 143, exit 11 (the same needs_walk crypt group, now leg duke_dreadmoore); the plan stays on planCheckKnownFailing Legs: library 18/24/2, graveyard 14/6/22/11.
…ery plan attribute the rows cannot carry An exact via (the launch point of the direct step after it, Wailing Caverns' Serpentis foot) becomes a move row with flags 64, TortoiseBots' new exact waypoint: its run leader counts it done only once the lead bot stands within 1.5 yd of it, still for 2 s, and only then sends the move_direct. Validate refuses the flag on any other kind and an exact move that is not followed by its move_direct (nothing inserted between). Every plan attribute the rows cannot carry is now a warning, one per attribute, naming where it stands and what the run leader does instead (skip, leftover_entries, leg rest, needs_walk, patrol, boss tactics and companions_loot, a dropped companions_loot false, fleers, no_cc, summoned_kill_first, hire_composition, level_band, raid, watchdog, the entrance and teleports, avoid without a navmesh, a loot or ritual stand, an object's spawn or range, a loot batch, an area trigger's expect check). check_skip (plan check only), equiv and notes are not warned.
… a rolled-back module fails closed The direct step right after an exact via is written as move_direct_exact. A TortoiseBots with exact waypoints reads it as a move_direct launched from the exact move before it; an older one does not know the kind and its loader skips the row (UnknownKind), so a binary rollback that walks the foot as a plain move never sends the hop over the hole from short of it. Validate pairs them both ways (flag 64 move right before a move_direct_exact, a move_direct_exact right after a flag 64 move), the avoid-box router and the long-hop cutter treat it as a direct step.
…har(16)) and refuse longer kinds move_direct_exact is 17 characters; ai_dungeon_step.kind is a varchar(16), so the INSERT would fail in strict mode or cut the kind. The kind is now move_hop_exact (14). Validate refuses any kind longer than the column (MaxKind), and a test holds every kind the exporter writes to it.
…ts exact move The cutter measured a move_hop_exact like any move and, for a hop longer than MoveGapWarnYd, put its navmesh points between the exact move and the hop, which Validate then refused. The hop is now indivisible there (no Cut call, nothing inserted before it; it stays the next hop's anchor), as the avoid-box router already had it. dungeon.Parse holds a direct via to 25 yd, so the test sets a 65 yd hop on the parsed plan.
SM drafts: Brother Wystan and Duke Dreadmoore get boss steps
…ak has them /api/minimap/<id> now answers World map (and each world-map floor), Minimap and Navmesh in that order, every slot with kind, floor, label and available, and a one-line reason instead of picture fields when the folder has none. A map the folder has nothing for answers 200 with three missing slots; only an unset --minimap-dir answers 404. A slot's reason comes from an optional <id>.status.json (64 KiB, 160 characters a reason), with a built-in line per kind otherwise. The main <id>.json stands in only for a source without a file of its own, so a picture is never listed twice. The page's picker shows all three slots, disables a missing one as "<label> (none)" with its reason, and falls back to the first available picture when a saved choice went missing; the map picture layer is greyed with the minimap's reason when nothing is available.
plan-export: exact waypoints (move flag 64 + move_hop_exact) and warnings for every attribute the rows cannot carry
Review round 1 of the three-slot change: - /minimap/ serves only images again: the allow-list used by serveMinimapImage loses status, while the folder reader keeps it, so <id>.status.png can never be handed out as an image. - an answer with no available picture at all (an empty folder, or one just re-extracted) is kept for its reasons but asked again after 30 s, as before, via the pure PictureSlots.keep/hasPicture helpers. - the greyed picture layer's reason now names every missing slot instead of only the minimap's when nothing is available; with no reasons at all it stays empty so the page's own text stands. - a status reason's control characters (line break, tab) become spaces. - the floor test uses 2f, 10f, 3f so a text sort can no longer pass it. - README: missing slots carry name and source too, and name is the label.
run_meta.server_trace sends .bot trace start <run_id> driver=... and .bot trace stop (and trace status, which the stop's retries read), but CheckCommand rejected the whole trace group: the start never went out and the run reported the server trace state unknown. Allow exactly trace start|stop|status, with trace start's driver= argument and its grammar; every other non-commander .bot line stays refused.
The preflight's .bot maintenance walks every hire to a vendor right after its autogear: the party scatters over maps 0/1 (one to map 43) and the run leader pauses with "the lead bot is not on the plan's map". In observe mode maintenance is now off unless observe.maintenance is true; the inventory and talent reads still run. A driven (commander) run is unchanged.
Dashboard: always list the three map pictures (World map, Minimap, Navmesh) with a reason when missing
…#248) checkObserve refused from_leg / from_pull_group, so an observed run always started at step 1; the 2026-10-20 worker had to start the server run by hand (.bot run start wailing_caverns from 207) and adopt it. from_leg maps to the leg's number in the exported rows (an optional leg takes none) and goes out as '.bot run start <plan> from <n>'; from_step (observe only) passes the run leader's own step number. from_pull_group needs that step number: the plan's rows are rebuilt with the exporter's option sets (plain, cut hops, approach paths, avoid-box routes) and the set whose step count matches the one the server lists in '.bot run list' is used; when none matches, the probe refuses before a bot is hired and names from_step. The start words go out in startRunLeader's line, resolved once at the probe; invalid combinations stay refused.
A dungeon the client draws from WMO art gets its real minimap: the terrain tiles, the WMO groups placed by the WDT/ADT MODF and MOGI with the settled transform (2 px/yd, 128-yd tile step, bottom-left anchored tiles, yaw plus the 180-degree term, rising PZ+z0), or the WMO tiles over the terrain. The JSON gains from=terrain|wmo| terrain+wmo, --list shows the makeup, a skipped tile or group is logged, and a source with no picture writes its reason into <id>.status.json.
Review round 1 on #248 (A, D, E, G): - A busy run of the same plan is adopted only when its status is at the requested step (or in the requested leg, when the start was sent by name); another step, another leg, or a leg sent as a number refuses with a note instead of watching a run from the wrong place. - A leg whose name the command grammar takes and is unique in the plan is sent as 'from leg <name>'; else its row number. The run leader's start line ('The run starts at step X of Y (leg NAME)') is compared with the plan file: a drifted deployment stops the run and refuses the watch. A start the module moved back into a scripted event is noted. - Before 'run start' the lead bot must stand within 80 yd of the start step's point (the mapped step's coordinates, or the leg's first point); observe.allow_far_start turns the refusal into a note. PartialStart carries the point and the resolved step from the resolver.
Review round 1 (B): the hire flow's top-up of a reused hire or one short of drinks or ammo called .bot maintenance in observe mode too, right after autogear, and scattered the party like the preflight did. prepEnv carries the same observe.maintenance decision (preflightMaintains), the hire check warns and reports the supplies as they are when it is off, and a driven run is unchanged.
Review round 1 (C): GroupStep for from_pull_group took the first row whose comment names the group, skipping the 'approach path' / 'path' / 'path (keeps out of an avoid box)' rows cutLongHops and routeAroundAvoid put before it: the run leader would start at the pull row and walk in from its own reach instead of the exported approach (live Wailing Caverns leg 9 group 4: step 202, not 199). It now walks back over those rows to the first row after the previous anchor. StepPoint exposes a step's coordinates for the observer's place check.
Review round 1 (F): the trace argument regexp allowed 64 characters after
driver=, while RunMeta.Check allows the 96 the server stores, and it did
not anchor the word to the driver= prefix. A separate anchored
^driver=[A-Za-z0-9_.+\-:]{1,96}$ is required on the fourth word only;
trace start's run id keeps the commander's argument grammar, so a
driver= word cannot stand in its slot.
Round 3 (Codex): both safety gates could fail open. - verifyStartReply accepted a start whose message was absent (ACK only, changed text, a dropped line) with just a note. Every non-empty partial start must now be proven: the parsed start line first, else `run status` right after the start (the step, and the leg, must be the requested ones); if neither proves it, the newly started run is stopped and the watch refused (no polls, normal unbind). - checkStartPlace skipped the party-position check when the start step's point or the lead's position was not known. Both now refuse the start with a clear message unless observe.allow_far_start is true. Tests: ACK-only at another step stops and rejects; ACK-only with a status proof is accepted; missing point refused; allow_far_start accepted; in range accepted; out of range refused. README and LIVE-CHECKS state both gates.
Round 4 (Codex): a parsed start reply was accepted without comparing its step, and from_step (Leg -1) skipped the leg check too: a request for step 3 accepted "The run starts at step 1 ..." and watched it. The reply no longer overwrites the requested step. For a step start the reported step must equal the requested one, and the leg must equal the expected one (the requested leg, or the requested step's leg resolved from the plan rows: PartialStart.ExpectLeg). The module's documented "moved back to the step that starts the event" is the only accepted step difference and is noted. A mismatch stops the run, returns an error without polling or watching. planexport.PlanLeg maps a row leg to the plan leg for ExpectLeg; observeStepInfo resolves it with the step's point. Tests: a wrong step for from_step and for from_pull_group stops exactly once with no status polls; a wrong leg with the right step stops; the requested step and the documented move-back are accepted (the latter noted). README and LIVE-CHECKS say the step and leg are compared.
Keep every terrain minimap except the five Q-e maps byte for byte as the old extractor wrote it; draw WMO art over the terrain only on 33, 36, 289, 329 and 800. Fail the all-dungeon alignment test on any measured failure except the known client-data exceptions (189, 819, 822, 533), log the raw shift, drop placements outside the navmesh box and the two Frostmane mini-hives, cap drawables and sampled pixels with a status reason, draw per tile rectangle, and write the known limits as the JSON note. Fixture tests pin draw order, alpha 128, uid dedup, terrain underlay, empty groups and both caps, each mutation-proved.
…tions A rejected MOGI entry used to compact the group slice, so a tile's original group id could land on another group's box. Keep one slot per MOGI entry (nil when rejected), drop tiles whose group was rejected, and test an invalid group 0 before a tiled group 1. The all-dungeon test now records the expected artOnNav, plan points and shift of the four exception maps and fails on any value outside the band (an improvement included); the client-gated skips print NOT RUN.
minimap-extract: real indoor minimaps for the 21 dungeons that had none (WMO tiles), indoor art over the terrain minimap on 5 maps
The audit's Stage 1 needs a fixture per dungeon plan that says how far the plan's stored route and what the server's navmesh plan are apart, leg by leg. The command builds the objective chain (entrance, then per leg its boss, else its to/last step point) and the stored polyline (the plan's own points in the order planexport writes them), plans every leg twice (plain, and on a scratch planner with SetAvoid of the plan's avoid boxes, whose path the metrics use), and reports status, end gap, live and stored length, ratio, stored points' distance to the live path, Z jumps over 8 yd, the breaks touched (exact/direct/gm_cross/check_skip vias within 6 yd of the path, or within 10 yd of a partial path's end), pull groups' corridor membership (radius + 18 yd, dz 8) and redundant approach/via points (within 5 yd), with the SHA-256 of every mmap file used. internal/routeoracle holds the report; its tests plan over a synthetic navfixture mesh, so no real mmaps are needed.
The zf-full-19z16 run stopped at the four-mob pack on the west road: the lead stood at 1629 1099, the foot of the pool stairs, which is a navmesh island, and three followers plus the healer went up to z 31-46.8 and took six minutes to gather back. Give the last two packs approach points on the lower floor west of the stair mouth so the run leader's party stays there. Pin the real Gong of Zul'Farrak (141832) in a test, not the Turtle decoration copy 20901. Record where the Mallet comes from: item 9240 is made by using 9241, and 9241 drops only from Qiaga the Keeper (7996) on map 0, so no bot run can acquire it and the leg stays optional. Note the missing pyramid phase gate (instance data 1, value 7): the plan format has no instance-data wait, so the stairs leg keeps its Nekrum-seen proxy.
The review's three highs: - The live route and the stored route were two different walks: boss legs planned previous objective to boss position directly, while the stored length counted the via points, the to, every pull group's approach points and centre, and the boss approaches. The oracle now plans the driver's own ordered sequence (via, to, pull approaches and pull centres or holds, step stands, boss approaches, boss, after-boss stands), hop by hop, and scores each stored hop against its own navmesh hop: per-segment rows in the JSON, per-leg sums in the table. - A partial path poisoned the numbers behind it: a hop that does not path complete now fails its leg; the leg's remaining hops are planned on from the point the failed path reached and marked non_comparable, the legs after a failed leg are indeterminate, and every aggregate (ratio, packs, breaks, redundancy) is over the complete legs only. - The report is written under the plan's name, which the command read from the plan JSON: a name like ../escape would write outside --out. The plan's own name is validated with the same strict basename regex as --plan, it must equal the requested plan name, and the resolved destination must stay under --out. Tests: a boss leg with to and pull groups (the walk and its ratio), a partial leg followed by another leg (indeterminate), a traversal name in the plan JSON (refused, nothing written outside --out), a name mismatch, and the end-to-end fixture run; each fails if its check is removed.
… points, no break hops The walk mode (still the default) proves the stored hops are walkable; it cannot say whether a route could be planned from a boss list. The bosses mode plans each leg once, avoid-aware, from the previous objective to the leg's boss (else its to or last point), skipping nothing. - A leg the mesh does not reach is FAILED, adds nothing to any aggregate, and names the stored break it hit: a break hop whose foot the mesh reaches from the start and whose landing reaches the objective, or one within 15 yd of the live end or of the line to the objective. - The leg after a failed one is planned from the last point reached and is labelled non-comparable and excluded; it is also planned from the previous objective (bridged), reported beside the primary numbers, never in them. - Complete legs report packs whose centre the path passes, hand points within 10 yd of it, floor jumps and live length. No stored/live ratio. - Files are <plan>-bosses.txt and .json; an unknown --mode is a usage error. Tests: a fixture mesh with a gap fails the leg and names the break; a failed leg and the leg after it stay out of the totals; a break far from the end is named because it bridges; the mode defaults to walk.
…mutations, vet, full test run and docs still to do
…out another unit (#292)
An observed run's report read 0 leader deaths and resurrections: only the group-mode driver counted them. The observe watch now follows the party's deaths in the feed, counts the lead bot's deaths and revives (report, legs, leader_death trace events, seconds dead), and counts a wipe when every member died within 90 s even though the lead bot revived in between, each death newer than the last counted wipe (the RFC run: all five dead within 63 s, never in one frame).
Driven-run start: check the commander's z against .gps FloorZ (#292)
…indow Observe: count the lead bot's deaths and revives, and a wipe spread over 90 s (#264)
A paused TBM:RUN line still carries the phase it paused in, so a run paused in the rest phase kept its rest open and the whole pause (PausedWait, 10 min) went into rest_seconds and rest_share. The rest is now open only while the run is running in phase rest; any other state closes it, and a resume in the same phase opens a new one. The new test fails at 290 s rest without the state check and passes at 10 s with it.
cc_hold joins only long crowd control (the short stuns end unflagged and read as broken holds) and a mob killed while flagged is not broken. A taunt with a target answers only that mob's episode.
Observe: count the run leader's rest phases and expose the instance id (#271 A+B)
trace-score: taunt latency, long-CC hold and pet threat in the spells check (#289 T-c)
A paused status whose note says the module paused for a player order (the observer's own cmd summon, a move) is now in report.remote.observe.player_orders with the step and the order; controls also records the player order and the resume after it. The module's own note addenda count once.
…rom failing walk, flight and replay tests (#296) The WSL2 host steps its wall clock back about 2 s every 30 s. The session slews its movement time towards the wall clock, so a step during a walk or flight slows the client time for seconds (walk tests timed out at 2 s, FeatherFallMidFlight saw a step 0.03 yd over its bound), and the capture stamps records with the wall clock, so a step put records before earlier ones and the replay missed the knockback landing (17 decoded, 18 live). - harness: sessions run on a steady clock (monotonic-driven clockFn); packet, ready and condition waits are 15 s instead of 2 s (they end on arrival) - replay tests: the live scenario runs again when the wall clock stepped during it - no product code changed
…#296) selectConfirmed accepted any echo of the player's target field that showed the unit, so an echo from an earlier select of the same unit, with another unit's select still pending behind it, confirmed the new select; the GM line then ran on that other unit. The session now keeps a log of the selections it sent and moves a lower bound on the server's progress with each echo (to the oldest send the echo can answer). A selection is confirmed only when every send from there on is that unit. Reselecting the unit already confirmed needs no echo (the core sends none for an unchanged field). When a new echo could not be told from an old one, a neutral selection (none, else the agent itself) is made and confirmed first. Respawn's wait for a cleared selection uses the same rule.
Preflight: tank HP floor and other-faction warning (#266 first slice)
Observe: record player-order pauses and their resume (#270 agent side)
Tests: make the session tests independent of WSL wall-clock steps (#296)
…on (#296 review) UNIT_FIELD_TARGET is also written by Unit::Attack, Unit::AttackStop and a stun, without touching the selection GM commands read. An echo of a value the session never sent now leaves the selection in force unknown, and a repeated select is confirmed with no echo only when the base came from an echo of the session's own send; otherwise a neutral selection is made first (one the log, the dropped sends and the field do not hold). The log is capped at 32 sends; an echo of a dropped unit confirms nothing. The confirmed select lives in the session (SelectConfirmed); GMOn (.aura world buffs) goes through it and GMOnConfirmed, and .respawn is sent with GMOnConfirmed on a confirmed clear.
… stunned (#296 review 2) The core rewrites the player's target field only through Attack, AttackStop and stun apply/end; a rewrite to exactly the unit of a pending select read as its echo. While the player is hot (its own SMSG_ATTACKSTART until the ATTACKSTOP, UNIT_FLAG_IN_COMBAT or UNIT_FLAG_STUNNED) and for SelectionCombatSettle after, no echo confirms and no GM line on a selection goes out. Cooling down fences off every send so far; SelectConfirmed waits inside its timer, then selects again (through a neutral selection when the unit was fenced off) and needs the echo of that new send. A neutral selection no longer counts the unknown base's placeholder as a unit.
When GPS started before the session had read the create block, the target field was unknown and SelectConfirmed made a neutral selection of nothing first, which the test refused (CI run 39915580906). The test now echoes every selection, allows only one neutral selection of nothing before the commander, and still checks that .gps runs with the commander selected.
GM lines run only on a confirmed selection (#296)
* Parse run leader left-packs field; a run with packs left is no clear * Reset packs-left on every run_leader event * Prefer clean clear in ghost tiebreak * Ghost tie-break needs a confirmed clean clear, not just Left 0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings main up to dev: PRs #287-#302 (Uldaman pull order and CC, trace spell checks, start z check via .gps, observe leader deaths/rest/instance id, trace-score spell checks, preflight tank and faction warnings, player-order pauses, flaky session tests, GM selection echo, packs-left status on the dashboard). Every change merged to dev through a reviewed PR with green CI; main has nothing dev lacks.