v0.3.0-rc.3: event-driven awareness + skill pre-emption
Adds a reactive layer on top of the polling reflex. The bot now
notices environmental shocks (forced moves, HP plunges, hostile
spawns) within ~100ms instead of waiting for the next DISPATCH tick,
and the in-flight skill is preempted so the next reflex cycle can
re-plan against the current world state.
This is the rc that wires the "rc.1 plumbing + rc.2 manifesto" into
a feedback loop:
- awareness fires preempt → dispatch aborts
- reflex tick re-evaluates → manifesto walks the ladder
- new dispatch picks the right skill for the new world state
Pieces:
- runtime/awareness/events.js (new) — bot.on listeners:
- move: single-tick Δposition ≥ 5 blocks → forced_move flag + preempt
- health: HP drop ≥ 2 → health_plunge flag + preempt
- entitySpawn: hostile mob within 12 blocks → hostile_added + preempt
- blockUpdate: nearby block change → env_changed flag (no preempt,
throttled 800ms; otherwise gather skills would self-preempt
every dig)
- runtime/skills/index.js — RUNNER_CODES.PREEMPTED + raceWithAbort()
wraps every execute() against ctx.abortSignal. Existing skills get
preemption for free; they don't have to check the signal manually.
- runtime/bot.js:
- dispatchAction creates a fresh AbortController per dispatch and
stores it on reflexCtx.currentAbort
- attachAwareness fires controller.abort() when something disrupts
the active skill; runSkill returns code: "preempted" and the
reflex moves on
- reflexCtx.lastPreempt records the most recent shock
Tests: 332 green (was 315 on rc.2, +17 new):
- runtime/awareness/events.test.js — 12 tests (each event type +
thresholds + throttling + passive-mob filter)
- runtime/skills/contract.test.js — 3 abortSignal tests
(mid-flight, pre-armed, clean signal)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+45
-1
@@ -108,7 +108,51 @@ Tests: 315 green (was 279 on rc.1, +36 new):
|
||||
- `runtime/reflex.test.js` — 2 new integration tests (manifesto-on
|
||||
overrides curriculum; well-fed bot pursues tools_stone)
|
||||
|
||||
### rc.3 — (pending) Event-driven awareness + skill pre-emption
|
||||
### rc.3 — Event-driven awareness + skill pre-emption
|
||||
**Root problem solved**: in v0.2.x the reflex was purely polling. The
|
||||
loop took a snapshot every DISPATCH_INTERVAL_MS (~2s) and decided what
|
||||
to do, but anything that happened **between** ticks was invisible.
|
||||
Concretely: when the operator dug a path that let the bot fall to a
|
||||
new area, the bot continued executing its prior `explore.far` against
|
||||
stale assumptions until the next tick. By then it had wandered further
|
||||
off course, and the cycle never broke. Same problem for hostile spawns
|
||||
and HP plunges — the reflex saw them only after the current skill ran
|
||||
its 30-90s timeout.
|
||||
|
||||
This rc gives the reflex an event-driven layer that **preempts** the
|
||||
in-flight skill within ~100ms of an environmental shock.
|
||||
|
||||
- [`runtime/awareness/events.js`](../../runtime/awareness/events.js) —
|
||||
wires direct `bot.on(...)` listeners and surfaces them as flags + an
|
||||
optional preempt callback:
|
||||
- `bot.on("move")` — single-tick position jump ≥ 5 blocks (teleport,
|
||||
fall, pathfinder snap, operator pushed us) → `forced_move`
|
||||
- `bot.on("health")` — HP drop ≥ 2 in one tick → `health_plunge`
|
||||
- `bot.on("entitySpawn")` — hostile mob spawns within 12 blocks →
|
||||
`hostile_added`
|
||||
- `bot.on("blockUpdate")` — block change within manhattan 4 →
|
||||
`env_changed` (informational only, NOT preempting; throttled 800ms)
|
||||
- [`runtime/skills/index.js`](../../runtime/skills/index.js):
|
||||
- `RUNNER_CODES.PREEMPTED` — new stable failure code
|
||||
- `runSkill()` now races `execute()` with `ctx.abortSignal`. If the
|
||||
signal fires mid-await, the skill returns `{ ok: false, code:
|
||||
"preempted" }` within one microtask — no skill code change needed.
|
||||
Long-running skills (`gather.logs`, `explore.far`,
|
||||
`recovery.tunnel-out`, `survive.pillar-up`) get this for free.
|
||||
- [`runtime/bot.js`](../../runtime/bot.js):
|
||||
- `dispatchAction` creates a fresh `AbortController` per dispatch
|
||||
and stores it on `reflexCtx.currentAbort` + `reflexCtx.abortSignal`
|
||||
- `bot.once("spawn")` calls `attachAwareness(bot, {onPreempt})`
|
||||
where `onPreempt` aborts the current dispatch
|
||||
- `reflexCtx.lastPreempt` records the most recent shock for
|
||||
snapshot/telemetry consumers
|
||||
|
||||
Tests: 332 green (was 315 on rc.2, +17 new):
|
||||
- `runtime/awareness/events.test.js` — 12 tests (each event type,
|
||||
thresholds, throttling, hostile filter)
|
||||
- `runtime/skills/contract.test.js` — 3 new preempt tests (mid-flight
|
||||
abort, pre-armed signal, clean signal doesn't interfere)
|
||||
- 2 extra contract sanity checks shaken out by signal plumbing
|
||||
|
||||
## Next session quick start
|
||||
|
||||
|
||||
Reference in New Issue
Block a user