fix(bots): ask whether the spot is still there before believing the timer #241

Merged
sickday merged 1 commit from fix/bank-stands-and-site-pairing into main 2026-08-19 01:59:08 +00:00
Owner

Watched five fishers for twenty minutes once the bank leg worked. They banked
345 fish between them, and two of them also stood still for eight minutes
mid-pack: phase working, fails=0, nothing animating, skilling clock pinned
at zero. The instrumented sitting names it -- target={17914, 1} against a
TickSnapshot.npc/1 answering nil. The spot had retired under them, which is
what a relocating spot does: the replacement stands at another tile under a
new index.

working asked skilling? before present?/1. skilling? is a
World.Content.Fish timer on the body and the timer was still registered --
which is the whole of a timer is not the interaction that armed it -- so it
answered "yes, working" about a spot that was not there, and the branch that
would have noticed sat underneath it and was never reached. The goal's own
moduledoc says relocation costs it nothing because presence is re-asked. It
could not be, and never had been.

So presence goes first, here and in GatherAndBank's alive?/1 mirror, which
has the same two lines in the same order and had not been seen doing it.
Re-watched: the retire now lasts one poll -- index 17905 gone, 18026 acquired,
fishing again.

Both unit tests for the hold were passing against the bug. Bot.FishTest held
against index 50 and Bot.GatherAndBankTest against loc 999_999, and
neither is placed anywhere, so in both files the hold was only ever reachable
while the stale-target branch came second. They now hold against a spot and a
tree that are really there -- one seeded into the snapshot tables, one drawn
from the derived table -- because a test that asserts a branch is also an
assertion about every branch above it.

What is left is engine rather than bot: the orphaned timer itself. pass/3
halts on not standing? and halt/1 clears the timer, so a loop that fired
would have cleaned up within a tick. It did not fire -- clock at zero,
permanently due, nothing animating -- which points at the timer being driven
from an interaction the engine dropped when the NPC was retired. No goal
depends on the answer now; anything else reading skilling? still does.

Watched five fishers for twenty minutes once the bank leg worked. They banked 345 fish between them, and two of them also stood still for **eight minutes** mid-pack: phase `working`, `fails=0`, nothing animating, skilling clock pinned at zero. The instrumented sitting names it -- `target={17914, 1}` against a `TickSnapshot.npc/1` answering nil. The spot had retired under them, which is what a relocating spot does: the replacement stands at another tile under a new index. `working` asked `skilling?` before `present?/1`. `skilling?` is a `World.Content.Fish` timer on the body and the timer was still registered -- which is the whole of *a timer is not the interaction that armed it* -- so it answered "yes, working" about a spot that was not there, and the branch that would have noticed sat underneath it and was never reached. The goal's own moduledoc says relocation costs it nothing *because* presence is re-asked. It could not be, and never had been. So presence goes first, here and in `GatherAndBank`'s `alive?/1` mirror, which has the same two lines in the same order and had not been seen doing it. Re-watched: the retire now lasts one poll -- index 17905 gone, 18026 acquired, fishing again. Both unit tests for the hold were passing against the bug. `Bot.FishTest` held against index `50` and `Bot.GatherAndBankTest` against loc `999_999`, and neither is placed anywhere, so in both files the hold was only ever reachable while the stale-target branch came second. They now hold against a spot and a tree that are really there -- one seeded into the snapshot tables, one drawn from the derived table -- because a test that asserts a branch is also an assertion about every branch above it. What is left is engine rather than bot: the orphaned timer itself. `pass/3` halts on `not standing?` and `halt/1` clears the timer, so a loop that fired would have cleaned up within a tick. It did not fire -- clock at zero, permanently due, nothing animating -- which points at the timer being driven from an interaction the engine dropped when the NPC was retired. No goal depends on the answer now; anything else reading `skilling?` still does.
fix(bots): ask whether the spot is still there before believing the timer
All checks were successful
ci / gates (pull_request) Successful in 3m45s
build / image (push) Successful in 38s
ci / gates (push) Successful in 3m30s
e9b974755f
Watched five fishers for twenty minutes once the bank leg worked. They banked
345 fish between them, and two of them also stood still for **eight minutes**
mid-pack: phase `working`, `fails=0`, nothing animating, skilling clock pinned
at zero. The instrumented sitting names it -- `target={17914, 1}` against a
`TickSnapshot.npc/1` answering nil. The spot had retired under them, which is
what a relocating spot does: the replacement stands at another tile under a
new index.

`working` asked `skilling?` before `present?/1`. `skilling?` is a
`World.Content.Fish` timer on the body and the timer was still registered --
which is the whole of *a timer is not the interaction that armed it* -- so it
answered "yes, working" about a spot that was not there, and the branch that
would have noticed sat underneath it and was never reached. The goal's own
moduledoc says relocation costs it nothing *because* presence is re-asked. It
could not be, and never had been.

So presence goes first, here and in `GatherAndBank`'s `alive?/1` mirror, which
has the same two lines in the same order and had not been seen doing it.
Re-watched: the retire now lasts one poll -- index 17905 gone, 18026 acquired,
fishing again.

Both unit tests for the hold were passing against the bug. `Bot.FishTest` held
against index `50` and `Bot.GatherAndBankTest` against loc `999_999`, and
neither is placed anywhere, so in both files the hold was only ever reachable
while the stale-target branch came second. They now hold against a spot and a
tree that are really there -- one seeded into the snapshot tables, one drawn
from the derived table -- because a test that asserts a branch is also an
assertion about every branch above it.

What is left is engine rather than bot: the orphaned timer itself. `pass/3`
halts on `not standing?` and `halt/1` clears the timer, so a loop that fired
would have cleaned up within a tick. It did not fire -- clock at zero,
permanently due, nothing animating -- which points at the timer being driven
from an interaction the engine dropped when the NPC was retired. No goal
depends on the answer now; anything else reading `skilling?` still does.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Revenant/Server!241
No description provided.