Baseline for the v0.2.0 self-learning iteration. All 205 tests pass on this
state. Subsequent commits in this branch layer the knowledge base,
post-mortem coach, and persona narration on top.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Root cause of "bot just stands still": every gather.* skill was using
bot.findBlock({ matching: (b) => names.includes(b.name) }), and under
mineflayer 1.21.4 + ViaBackwards the Block objects fed into the
callback have a wrong .name field (Block.type / numeric id is still
correct — this is mineflayer issue #2347). Every search returned null,
every skill reported "no_target", reflex looped wander → tunnel-out
forever. The bot's logs said "dispatch ok" while the operator watched
it pace in circles.
Proven live with a new diag.match skill on play.xmatic.team:
findBlocks({matching: numericIds}) → 50 hits
findBlocks({matching: (b) => b.name === ...}) → 0 hits ← the bug
findBlock({matching: (b) => b.name === ...}) → null ← the bug
findBlock({matching: numericIds}) → dark_oak_log @ (606,62,110)
After this fix the same bot from the same spawn dispatches gather.logs
and reaches the chop loop ("chop: dark_oak_log at 606,62,110 (tool=fists)")
instead of returning "no reachable log within 64 blocks".
Changes:
- runtime/perception.js (new): findBlocksByName / findNearestBlockByName
centralise the numeric-id workaround for any future skill.
- runtime/actions.js: chopNearestTree, sleepInBed, placeCraftingTable now
use perception. Also load mineflayer-tool plugin alongside collectblock
(collectblock 1.6 hard-requires bot.tool to dispatch a dig).
- gather-stone, gather-wool, deposit-surplus rewritten to numeric-id
search. gather-wool also loads mineflayer-tool.
- diagnose-scan.js (new): two diagnostic skills — diag.scan reports
findBlocks counts per radius for common blocks; diag.match cross-tests
the four matcher styles so this regression can be re-proven on demand.
- runtime/skills/index.js: registers diag.scan + diag.match.
Memory: project_findblock_callback_broken_under_viabackwards.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-pronged response to user-confirmed "bot stands still, doesn't actually
chop" on play.xmatic.team:
1. Pin protocol — .env now sets MC_VERSION=1.21.4. minecraft-data has
wrong packet ID mappings for protocol 775 (server 26.1.2 via
ViaBackwards 5.9.1) — see mineflayer#3888 and #3717. 1.21.5 also has
an enchants decoder bug that breaks bot.dig. 1.21.4 is the last
protocol mineflayer 4.37.1 can speak cleanly through VIA.
2. Don't trust dig success — runtime/actions.js chopNearestTree and
runtime/skills/gather-stone.js now lookAt(face center)+forceLook,
await collectBlock, then re-read the target block. If the log/stone
is STILL there, return ok:false code:"silent_dig_failure" and
blacklist the position. Prevents the curriculum from reporting
"wood.16 in progress" while the world hasn't actually changed.
3. Defensive default — runtime/movement-profiles.js: canDig=false on
every profile until dig is confirmed working live. Otherwise
pathfinder schedules paths through must-dig blocks and the bot loops.
4. Ground-truth probe — runtime/skills/diagnose-physics.js dispatches
forward/jump/dig probes and writes the result to the diary. New
IPC command cmd:run-skill lets the operator (or a future curriculum
trigger) fire any skill on demand; it waits for the current action
to finish before dispatching. /tmp/pepa-runskill.mjs is a one-shot
client.
Live probe on play.xmatic.team confirmed: forward Δ=0.003 over 2s
(BROKEN — server rejects movement packets), jump ΔY=0.42 (likely
physics jitter, not a real jump). Strongly suggests an anti-cheat
plugin gating bot-style movements server-side — beyond protocol pin.
npm test 124/124.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to the iteration-1 fixes. Live smoke on play.xmatic.team
revealed the bot was spawning into a tree-less plain (no log within
32 blocks of spawn), looping wander→gather→no_target→wander
forever inside a 16-block box.
- runtime/actions.js: chopNearestTree search radius 32 → 64 (still no
trees on this spawn, but a normal biome will be served well by it).
wander now has a blind-walk fallback when pathfinder times out
(look+forward+jump for 3 s) so the bot at least unsticks from leaves
or pillars. Pathfinder timeout reduced 30 s → 15 s.
- runtime/skills/explore-far.js: new explore.far skill — walks ~48
blocks in a quadrant (NE/SE/SW/NW, rotating per call) so successive
hints actually circle the spawn instead of bouncing in place. Blind
walk fallback included.
- runtime/reflex.js: when the scheduler is told to wander twice in a
row by gather.* recover hints, it now dispatches explore.far instead
so the bot actually leaves the patch it's stuck in. Resets the
consecutiveWanderHints counter on any success.
- runtime/reflex.js (sleep): no longer dispatches when the bot has
neither a bed in inventory NOR a known shelter/base location —
saved one dispatch + 5-min cooldown per restart at night.
- runtime/reflex.js (eat): inventory check + lastEatAt always updated
fix the eat-spam loop observed live (every tick fired "eat" → "no
food in inventory" → again).
- runtime/skills/chop-logs.js: recognise "no log within ..." as
no_target so the recover hint switches the bot to wander/explore.
npm test 124/124.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Recovers the bot from the live-server symptoms reported 2026-05-26:
1) constant supervisor reconnects, 2) chop "clicks once and stops",
3) sleep does nothing without a bed and so blocks night-skipping for
other players, 4) curriculum reflex always fell through to wander.
Supervisor (#38):
- runtime/watch-filter.js: pure predicate excluding *.test.js + the
supervisor itself; recursive:true so skills/ + social/ edits also
restart. Burned a working main once when test files counted toward
the rollback threshold.
- runtime/supervisor.js: watch-triggered restarts no longer count
toward the crash-loop rollback path. Watcher is now recursive.
Chop / mine (#39):
- runtime/actions.js + runtime/skills/gather-stone.js: replaced raw
pathfinder.goto + bot.dig with mineflayer-collectblock's
bot.collectBlock.collect — handles approach, repositioning, LoS,
dig and pickup as one primitive. Old version "swung once" because
GoalGetToBlock often parked the bot in leaves above the log.
Sleep + bed (#40):
- runtime/actions.js: sleepInBed now ALSO places a carried bed on
solid ground next to the bot and sleeps on it. Critical so the bot
stops blocking player night-skipping the moment it owns a bed.
Bed pipeline (#41):
- runtime/skills/gather-wool.js: gather.wool skill — mines wool block
if any nearby, otherwise shears or attacks the nearest sheep.
- runtime/skills/craft.js: craftBedSkill (any colour the bot has ≥3
wool of, plus 3 planks, plus a table).
- runtime/curriculum.js: new milestone survive.bed sits between
wood.tools and stone.32 so the bot gets a bed BEFORE everything else.
Test fixture updated to include a red_bed in post-survive.bed stages.
Village / shelter / wheat (#42, #43):
- runtime/skills/build-shelter.js: village.build-shelter — real 3×3×3
resumable hut blueprint around the recorded base, places one block
per loop, idempotent so an interrupted build resumes correctly,
marks each placed block in the owned-blocks ledger.
- runtime/skills/deposit-surplus.js: village.deposit-surplus opens
the nearest chest and transfers surplus stacks while keeping a
reserve of tools/food/bed.
- runtime/skills/farm-wheat.js: farm.wheat does one step per call
(till adjacent-to-water grass, plant seeds, or harvest ripe wheat).
- runtime/curriculum.js: village.shelter milestone after base-site.
Scheduler glitch (root of "always wander"):
- runtime/bot.js: curriculum + locations are now computed BEFORE
runTick. Previously they were stamped AFTER, so reflex.js saw
snapshot.curriculum=undefined every tick and fell through to the
wander fallback. Verified live: scheduler now dispatches
gather.logs/gather.stone/craft.* by id via runSkill.
Eat-spam:
- runtime/reflex.js: eatReflex now checks inventory for actual food
and updates lastEatAt on EVERY dispatch (not only successes), so a
failed eat respects the 5 s cooldown instead of firing every tick.
npm test 123/123. Validated live on play.xmatic.team (curriculum
dispatched gather.logs via runSkill, recover hint switched to wander
when no log in range).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three closures of remaining PRD follow-ups, one merge:
1. Reflex scheduler now drives behaviour from the curriculum.
- reflex.js: replaced ad-hoc techTreeReflex + autonomousReflex with
curriculumReflex that dispatches the skill suggested by
snapshot.curriculum.plan via runSkill. Per-skill backoff for
missing_tool / missing_material / no_target / no_food_source /
unsupported_version. recover() hint with `{hint:"wander"}` swaps
the next tick to wander for 60 s.
- Chain is now: defend > eat > sleep > curriculum > idle.
- reflex.test.js: 11 new tests covering busy/disconnected,
defend/eat preemption, dispatch by id, unknown-skill fallback,
per-skill + wander-hint backoffs, onComplete updating backoff.
2. Pi escalation for ADDRESSED_BANTER with hard rate limit.
- bot.js: when generateReply returns {escalate:true}, spawn askPi
with bot state + last 5 lines from that speaker (redacted via
chatMemory). Reply capped at 200 chars, sent as one chat line.
- Rate cap: 6 calls/hour, 90 s min gap. Suppressed escalations
log once and silently drop.
3. Phase 4 substrate.
- runtime/locations.js: atomic JSON store
(state/<host>/locations.json) with setLocation / getLocation /
nearestLocation / removeLocation; 6 tests.
- runtime/base-site.js: scoreCurrentPosition(bot) + pure scoreSite
bundle (wood / stone / water / flatness / no-players /
no-foreign-builds, owned-blocks excluded from claim penalty);
6 tests.
- runtime/skills/choose-base.js: village.choose-base skill — scores
the current spot, writes locations.base if score ≥ 8, otherwise
returns code:"too_weak" with a wander recover hint.
- curriculum.js: new final milestone village.base-site fires
village.choose-base until a base location exists.
- bot.js: stamps snapshot.locations from listLocations() each tick
so the curriculum can read it without coupling to disk.
docs/runtime.md updated with three new sections.
npm test now 116/116.
Co-authored-by: Yuriy Mayatnikov <mayatnikov@me.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 3 of plans/autonomous-survival-bot-prd.md. Gives the bot a
deterministic path from empty inventory through stone-tier tools and
basic storage, without an LLM call per tick.
New:
- runtime/curriculum.js: ordered milestone chooser
(wood.16 → wood.planks-and-sticks → wood.tools → stone.32 →
stone.tools → food.basic → storage.chest → shelter.torch). Each
milestone exposes isDone(inventory, snapshot) and suggest() returning
a { skillId } plan the scheduler can dispatch via runSkill. isDone
uses "stage reached" escapes so progress is monotonic — crafting
planks doesn't bounce the chooser back to "gather 16 logs".
- runtime/skills/gather-stone.js: gather.stone with pickaxe-required
precondition, blacklist on failed paths, registry-aware matching
(stone / cobblestone / deepslate / cobbled_deepslate / andesite /
diorite / granite).
- runtime/skills/craft.js: factory + concrete skills for craft.planks,
craft.sticks, craft.wooden-axe/-pickaxe/-sword, craft.stone-axe/
-pickaxe/-sword, craft.furnace, craft.chest, craft.torch (torch
requires coal or charcoal preflight).
Tests:
- runtime/curriculum.test.js: 14 tests covering chooser ordering,
per-milestone skill suggestion, inventoryFull threshold, monotonic
advancement across stage transitions.
- npm test now runs the full suite: 28/28 passing.
Wiring:
- runtime/bot.js: lastSnapshot.curriculum carries the next milestone
+ suggested skill on every tick; lastSnapshot.currentMilestone
prefers the curriculum title over the planner.md line.
- tui/tui.tsx: milestone line shows the curriculum's suggested skill
and an [inventory full] flag when isInventoryFull fires.
Reflex.js still calls actions.js directly; wiring the scheduler to
runSkill(plan.skillId, …) lands in Phase 4.
Co-authored-by: Yuriy Mayatnikov <mayatnikov@me.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2 of plans/autonomous-survival-bot-prd.md. Establishes the
composable skill contract from PRD §5.2 and ports three reference
skills so future phases can layer survival behaviour on top instead of
adding more ad-hoc branches to reflex.js.
New: runtime/skills/
- index.js: skill registry + runSkill(id, ctx, args) wrapper. Enforces
preconditions, hard timeout, normalises {ok, code, detail, worldDelta}
on every result, runs validate() and calls recover() on failure.
Stable failure codes live in RUNNER_CODES (unknown_skill,
precondition_failed, timeout, threw, validation_failed, done).
- groups.js: registry-derived item/block sets — logs/planks/sticks/beds
derived by suffix; foods intersects a curated allowlist with the live
bot.registry; axes/pickaxes/swords scoped to whatever the connected
server's item table actually ships. Empty set instead of throwing on
missing registry, so skills can emit code:"unsupported_version".
- chop-logs.js: gather.logs reference skill (wraps chopNearestTree).
- eat.js: survive.eat (wraps eatBestFood, preconditions check carrying
edible food from the registry-derived set).
- wander.js: explore.wander (wraps wander).
- contract.test.js + groups.test.js: 14 tests covering precondition
gating, timeout firing recover(), execute exceptions, validate
flipping ok→false, dynamic group filtering across mock registries.
package.json: `npm test` runs the new contract + groups suites.
docs/runtime.md: documents the skill contract, runner, dynamic groups
and the reference skills.
Reflex.js still calls actions.js directly — wiring the scheduler to
runSkill() lands in later phases when the survival curriculum kicks in.
Co-authored-by: Yuriy Mayatnikov <mayatnikov@me.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>