Skip to content

docs(SOUL): §6 Failure Recovery — pattern-match details + decide by player intent - #20

Open
Pablomonte wants to merge 1 commit into
nicoechaniz:feat/canonical-loopfrom
Pablomonte:docs/soul-failure-recovery-pattern-match
Open

docs(SOUL): §6 Failure Recovery — pattern-match details + decide by player intent#20
Pablomonte wants to merge 1 commit into
nicoechaniz:feat/canonical-loopfrom
Pablomonte:docs/soul-failure-recovery-pattern-match

Conversation

@Pablomonte

Copy link
Copy Markdown
Contributor

Summary

Rewrites §6 of `agents/embodied-service/profile-templates/daemoncraft-base.SOUL.md` so the captain LLM's recovery contract matches runtime reality and stops silently relocating placements that carried player intent.

What was wrong

The previous §6 promised three canonical error_types — `target_occupied`, `no_solid_neighbor`, `bot_in_target` — that the runner's Tier 2a auto-retry block (`agents/local_agent/embodied.py:SPATIAL_ERRORS`) is keyed on. But the embodied service dispatcher (`foldBotResponse` in `lib/dispatcher.js`) collapsed every bot error to a generic `bot_action_failed`. The canonical labels were never emitted, so the recovery contract the SOUL described did not match what the captain actually saw at runtime.

Field test exposed a second issue with the first iteration of this rewrite: the catalog listed "place at adjacent cell" as the default for `target_occupied`. The original coordinate often carried player intent (sealing a hole, hitting a named cell, completing a structure). Silently placing one cell over is a worse failure than refusing — it leaves the player with an incorrect world AND the false belief the task succeeded.

What changes

  1. Pattern-match `details` rather than `error_type`. The captain is taught to read the bot's free-text diagnostic string against a 9-row catalog of real bot phrases. Each pattern was verified against the actual error throw site in `agents/bot/server.js`.

  2. Spatial recovery: pause and decide. Added a "think first" checklist before any spatial recovery:

    • Was the cell load-bearing? → don't relocate; break-and-replace or ask.
    • Was the location approximate? → adjacent cell OK; report what was done.
    • Is it a body-position issue? → move the bot, keep the original target.
    • Default tie-breaker: ASK the player.
  3. §1 worked example rewritten to show "think aloud" before relocating, including an example where the right answer is a chat message back to the player rather than a placement workaround.

Pattern table (each row anchored in a real throw site):

pattern in details source
target space is occupied server.js:2363 (place)
inside my own body / footprint server.js:2354 (place — PR #17)
no solid adjacent block / against server.js:2407 + 2358
did not materialize server.js:2343 (place)
No {item} in inventory server.js:2227 + 2317 (equip/place)
crafting_table nearby server.js:2163, 2169 (craft_item)
Mined K/N dispatcher.js:detectSoftFailure
Can't ... / Failed to ... bot soft-failure prefix
timeout generic

Companion PR

DC #19 — dispatcher classifier that promotes the bot's diagnostic to canonical `error_type` upstream. With that merged, the captain has BOTH a canonical error_type (for runner auto-retry) AND the SOUL pattern catalog (for cases the classifier doesn't cover). This SOUL PR works on its own and degrades gracefully — but the two together close the loop end-to-end.

Test plan

  • SOUL is plain markdown; no code/tests affected.
  • Diff reviewed: 47+ / 19- on the canonical template, structure preserved.
  • Field test (already executed in dev): chat sequence `@bot poné un crafting_table → @bot poné un cobblestone en la misma posición`. Verify the captain emits a "break + place" intent OR a chat clarification message, NOT a silent relocation to an adjacent cell.

🤖 Generated with Claude Code

… player intent

The previous §6 promised three canonical error_types to the captain LLM:
"target_occupied", "no_solid_neighbor", "bot_in_target". These are
referenced in agents/local_agent/embodied.py (SPATIAL_ERRORS set) and
used to gate Tier 2a auto-retry. But the embodied service dispatcher
(foldBotResponse in lib/dispatcher.js) collapsed ALL bot errors to a
generic `error_type: "bot_action_failed"`. The canonical labels were
never emitted, so the captain's recovery path keyed on them was dead.
The bug is preexisting upstream — neither the dispatcher nor the bot
re-classified placement failure strings into canonical error_types, so
the recovery contract the SOUL described did not match runtime reality.

This commit closes the gap at the SOUL layer (lowest blast radius —
zero code change, deploys via profile sync). Two things change:

1. **Pattern-match `details` rather than `error_type`.** The captain is
   taught to read the bot's free-text diagnostic string against a
   9-row catalog of real bot phrases. Each pattern was verified against
   the actual error throw site in agents/bot/server.js.

2. **Spatial recovery: think before relocating.** The first iteration
   of this SOUL listed "place at adjacent cell" as the default response
   to `target_occupied`. Field test exposed the flaw: the original
   coordinate often carried player intent (sealing a hole, hitting a
   named cell, completing a structure). Silently placing one cell over
   is a worse failure than refusing — it leaves the player with an
   incorrect world AND the false belief the task succeeded.

   New shape: §6 starts with a "pause and decide" checklist asking
   whether the cell was load-bearing, approximate, or just blocked by
   the body. The table lists OPTIONS, not prescriptions. Default
   tie-breaker is "ask the player" via a short chat message.

Pattern table (each row anchored in a real bot string):

  | pattern in details                | source                               |
  |-----------------------------------|--------------------------------------|
  | target space is occupied          | server.js:2363 (place)               |
  | inside my own body / footprint    | server.js:2354 (place — PR nicoechaniz#17)      |
  | no solid adjacent block / against | server.js:2407 + 2358 (place)        |
  | did not materialize               | server.js:2343 (place)               |
  | No {item} in inventory            | server.js:2227 + 2317 (equip/place)  |
  | crafting_table nearby             | server.js:2163, 2169 (craft_item)    |
  | Mined K/N                         | dispatcher.js:detectSoftFailure      |
  | Can't ... / Failed to ...         | bot soft-failure prefix              |
  | timeout                           | generic                              |

§1 worked example also rewritten: instead of silently relocating to an
adjacent cell, the captain now thinks aloud about whether relocation
serves the player's intent — and asks if it doesn't.

Companion PR with the dispatcher-side fix that ALSO emits canonical
error_types (so SOUL pattern matching + canonical error_types both
work, defense-in-depth): nicoechaniz#19

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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