Skip to content

Rest a co-op fetch goal that cannot be routed to - #251

Merged
saworbit merged 1 commit into
mainfrom
coop-fetch-routefail-rest
Sep 4, 2026
Merged

Rest a co-op fetch goal that cannot be routed to#251
saworbit merged 1 commit into
mainfrom
coop-fetch-routefail-rest

Conversation

@saworbit

@saworbit saworbit commented Sep 4, 2026

Copy link
Copy Markdown
Owner

A co-op companion on e1m6 logged 400 routefails in 175 s against four goal pushes. The cause is two halves that only loop together.

Argus_AI clears ar_goal on every routefail. The GOAL_FETCH branch of the co-op dispatch re-adopts its subject the moment ar_goal is not already it, and only pops when the item stops existing. So the pair retries at think rate for the whole 45 s life of the goal, and the co-op layer then re-pushes it.

Why the obvious guard does not work

The item shelf cannot cover this. ar_failtime is only stamped when the failure comes from a different area, because a global shelf stamped by a trapped bot starves every other bot through PickGoal's menu. A bot hammering one objective mills inside a single patch, so it takes the failstreak branch every time and the shelf is never set.

That is also why an earlier attempt failed. Putting the shelf check on Argus_CoopCommitKey changed nothing, because the retries come from the goal already on the stack rather than from re-commitment. It also cost e1m2 routefails 0 to 77 and stalls 40 to 73, and was reverted.

The fix

A routefail whose goal is the current fetch subject rests that bot for 20 s, the same constant the item shelf uses.

Two deliberate choices:

  • Per-bot, not the global item shelf. This is a verdict about one bot's ability to route there, and shelving a shared co-op objective is exactly how one stuck bot would starve a whole team.
  • ar_goal left clear rather than the goal popped. The caller runs Argus_PickGoal whenever the goal is world, so the bot shops normally for those 20 s instead of standing still, and the objective is retried afterwards rather than abandoned. The goal stays on the stack throughout.

e1m6, two tapes each side

routefails cells busiest cell stalls freezes
before 400, 377 6, 9 57%, 57% 6, 2 0
after 8, 9 23, 22 10%, 8% 7, 3 0

The bot stops being parked in one cell and works the map while its objective rests. e1m6 still cannot be finished, because the gold key sits behind a func_door that rises on a trigger_once chain nothing fires in a botmatch. This makes the failure cheap instead of constant, which matters because an_busy serialises the router and one bot failing twice a second starves the others.

dm4, 180 s

Routefails 10 against 9, stalls 4 against 18, cells 74 against 75, goals 157 against 126, no freezes.

e1m2 is untouched by construction

ar_routefail is set in exactly one place, immediately before the routefail event print, so events map one to one with the flag. Both e1m2 tapes log zero, so the guard never executes there.

Worth recording rather than hiding: the coverage on those tapes reads 31 and 50 cells against 52, 55 and 55 earlier, on code that provably cannot behave differently. That is e1m2 co-op variance, and a caution against judging co-op on a single tape. The 6 to 8 second freezes on e1m2 are the pre-existing give-up-capped class, and the shipped build's own tape carries one.

Refs #249

🤖 Generated with Claude Code

A co-op companion on e1m6 logged 400 routefails in 175 s against four
goal pushes. The cause is two halves that only loop together.

Argus_AI clears ar_goal on every routefail. The GOAL_FETCH branch of
the co-op dispatch re-adopts its subject the moment ar_goal is not
already it, and only pops when the item stops existing. So the pair
retries at think rate for the whole 45 s life of the goal, and the
co-op layer then re-pushes it.

The item shelf cannot cover this. ar_failtime is only stamped when
the failure comes from a DIFFERENT area, because a global shelf
stamped by a trapped bot starves every other bot through PickGoal's
menu. A bot hammering one objective mills inside a single patch, so
it takes the failstreak branch every time and the shelf is never set.
That is also why the obvious guard fails: an earlier attempt put the
shelf check on Argus_CoopCommitKey and changed nothing, because the
retries come from the goal already on the stack, not from
re-commitment. It cost e1m2 routefails 0 to 77 and stalls 40 to 73
and was reverted.

A routefail whose goal is the current fetch subject now rests that
bot for 20 s, the same constant the item shelf uses. Per-bot and not
the global item shelf, because this is a verdict about one bot's
ability to route there, and shelving a shared co-op objective is
exactly how one stuck bot would starve a whole team.

ar_goal is left clear rather than the goal popped. The caller runs
Argus_PickGoal whenever the goal is world, so the bot shops normally
for those 20 s instead of standing still, and the objective is
retried afterwards rather than abandoned. The goal stays on the stack
throughout.

e1m6, two tapes each side:

  routefails  400, 377  ->    8, 9
  cells         6, 9    ->   23, 22
  busiest cell 57%, 57% ->  10%, 8%
  stalls        6, 2    ->    7, 3
  freezes       0       ->    0

The bot stops being parked in one cell and works the map while its
objective rests. e1m6 still cannot be finished, because the gold key
sits behind a func_door that rises on a trigger_once chain nothing
fires in a botmatch. This makes the failure cheap instead of
constant, which matters because an_busy serialises the router and one
bot failing twice a second starves the others.

dm4, 180 s: routefails 10 against 9, stalls 4 against 18, cells 74
against 75, goals 157 against 126, no freezes.

e1m2 is untouched by construction. ar_routefail is set in exactly one
place, immediately before the routefail event print, so events map
one to one with the flag, and both e1m2 tapes log zero. The guard
never executes there. The coverage spread on those tapes, 31 and 50
cells against 52, 55 and 55 earlier, is e1m2 co-op variance on
functionally identical code, which is worth remembering the next time
a single co-op tape is used to judge anything. The 6 to 8 second
freezes on e1m2 are the pre-existing give-up-capped class; the
shipped build's own tape carries one.

Refs #249

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@saworbit
saworbit merged commit 289eefe into main Sep 4, 2026
3 checks passed
@saworbit
saworbit deleted the coop-fetch-routefail-rest branch September 4, 2026 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant