feat(embodied-service): Path B canonical wireup (E002) - #10
Conversation
Code Review — CompAII (Nico's Hermes)Hola Fede / Hermes de Fede. Revisé la PR #10. Acá va feedback estructurado. ✅ Embodied Service v1 — El código se ve sólidoEl núcleo de la PR (lo que el título anuncia) está bien diseñado:
🔴 Scope Bleed — La PR tiene ~3000 archivosEl título dice
🔧 Action ItemsPara destrabar rápido:
Mientras tanto:
📋 Conflictos potenciales con nuestro mainNuestro
Reviewed by CompAII via Hermes Agent |
…a-Andy The bridge between Hermes' cognition and Gemma-Andy's body orchestration per team architectural decision 2026-05-08 (Path B canonical, see vault/concepts/gemma-andy-embodied-service.md). Sprints 1-3 from epics/E002: Sprint 1 — skeleton + world_state read: - index.js: HTTP server on port 7790, /health + /intent endpoints, graceful shutdown, JSON-line structured logs - lib/world_state.js: parallel reads from bot/server.js (/status, /nearby, /inventory) into the canonical 7-key shape - lib/schema.js: load tool_schema_v2.placeholder.json, expose filterSupported() / isSupported() / isCanonical() / getToolDef() - lib/tool_schema_v2.placeholder.json: 68 tools with executor_supported flag (43 true, 25 false), derived from team docs. Marked as placeholder pending Mariano's canonical version. - lib/defaults.js: DEFAULT_GUARDIAN_CONSTRAINTS, DEFAULT_ALLOWED_TOOLS (38 safe-set tools), DEFAULT_DEADLINE_SECONDS Sprint 2 — Ollama integration: - lib/ollama.js: callGemmaAndy() against http://10.10.20.1:11434/api/chat, model gemma-andy:e4b-v2-2-3-q8_0; canonicalStringify() matching Python's json.dumps(sort_keys=True, ensure_ascii=True) byte-for-byte including \uXXXX surrogate pairs for emoji. Rules 1+2+5 enforced. - lib/parser.js: parseGemmaAndyResponse() with stripThink() prefix removal + bracket fallback for ~1% noisy outputs (Rule 6). Validates the 5 required fields and operational_risk enum. Sprint 3 — Tool dispatcher: - lib/dispatcher.js: HANDLERS table mapping every supported canonical tool to a bot/server.js HTTP call. Signal tools (ask_clarification, raise_guardian_event, report_execution_error) short-circuit without HTTP. Defensive: tool_not_implemented / tool_not_canonical / dispatcher_mapping_missing error_types so Hermes can replanify. - Test that asserts every executor_supported tool has a HANDLERS entry — schema/dispatcher drift is a CI break. Tests: 31/31 pass (parser 9, schema 8, ollama canonical-stringify 8, dispatcher 4 + handler-coverage assertion). Smoke test of the running service confirms /health returns schema metadata and graceful shutdown on SIGTERM/SIGINT. Disciplina v1 honored: no memory, no queue, no own Mineflayer session, no auto-initiative. README documents what is intentionally NOT included.
Sprint 5 of E002 — the metric of success for the 2026-05-09 refactor.
The canonical Hermes profile at ~/.hermes/profiles/daemoncraft-base/
has been refactored:
config.yaml:
- model.default: kimi-k2.6 / provider: kimi-coding
- toolsets: [embodiment, messaging] (was [minecraft, messaging])
- platform_toolsets.cli: [embodiment, clarify, messaging]
SOUL.md:
- Section 5 (Tool Use) rewritten — embodied_plan is THE body tool;
granular mc_* are explicitly listed as deprecated/unavailable
- Section 7 (Verify Before Narrate) rewritten — verification goes
through embodied_plan(intent='Scan to confirm ...')
- Section 8 (State Is Truth) rewritten — workspace files + embodied_plan
- Section 6 (Memory) updated — separates body's physical_memory
(in-world named locations) from cast narrative state (workspace files)
- New introductory framing: 'You don't drive movement, mining,
building, crafting, or combat directly. You describe what you want
to happen; Gemma-Andy decides how.'
Mirroring these files in this repo so the architecture choice is
reviewable alongside the service code that depends on it. Canonical
files stay at ~/.hermes/profiles/daemoncraft-base/. Pre-refactor
backups preserved at ~/.hermes/profiles/daemoncraft-base/{config.yaml,
SOUL.md}.pre-embodied-2026-05-09.
Verification: tool registry confirms toolset 'embodiment' resolves to
['embodied_plan'], the tool is loadable, description is exposed to the
LLM. End-to-end smoke (Hermes <-> embodied service <-> Ollama <->
bot/server.js <-> Mineflayer) is Sprint 6, gated by Mariano's canonical
schema and a real field session.
…raft-gemma4-andy Replace tool_schema_v2.placeholder.json with the canonical schema fetched from Mariano's repo at: https://raw.githubusercontent.com/Mar-IA-no/deamoncraft-gemma4-andy/main/schema/tool_schema_v2.json - 68 tools / 43 executor_supported (canonical numbers) - version: "gemma-andy-tools-v2" - blob_sha: 5896efa3cfd736f43a071c1378d4612564365ef8 - Provenance recorded in JSON's _meta block (local annotation only) Reconcile dispatcher.js HANDLERS table with canonical names: REMOVED (placeholder names): replace_block, deposit_furnace, withdraw_furnace, drop_item, swap_hands, eat_food, use_consumable, remember_place, list_places, sleep_in_bed ADDED (canonical names): check_furnace, take_from_furnace, toss_item, pickup_item, strafe, consume_food, apply_bonemeal, remember_here, goto_remembered_place, sleep RECATEGORIZED: ignite moved from combat to building (canonical layout) Update defaults.js DEFAULT_ALLOWED_TOOLS with canonical names. Update schema.js DEFAULT_SCHEMA_PATH to point to canonical file (placeholder constant retired). index.js /health now surfaces top-level `version` field as schema_version. E2E verified against real Gemma-Andy (gemma-andy:e4b-v2-2-3-q8_0): - Intent "Mine 3 oak logs" → 4 canonical tool_calls (scan_nearby, goto, mine_block, collect_drops), all dispatched ok, 4.1s round-trip. - Intent "Eat any food..." → consume_food (canonical), no eat_food drift. Tests: 31/31 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…CTIONS)
The previous dispatcher posted to /command with `{action: "..."}` —
which is wrong: bot/server.js's /command is a Minecraft chat slash-command
relay (/give, /tp, etc.). The real action plane is POST /action/<name>
with coord-pure args, and the bot exposes a 62-action ACTIONS table
whose arg shapes diverge from canonical Gemma-Andy v2 refs (e.g.
canonical goto({target: "oak_log", target_type: "block"}) vs bot
goto({x, y, z})).
This commit makes the dispatcher a real translator:
- New lib/refs.js — resolveTarget/resolveFrom/resolvePosition resolve
canonical refs (BlockType, EntityRef, Position3D, PlaceName) into
{x,y,z} via /action/find_blocks, /action/find_entities, /action/marks.
RefResolveError surfaces structured error_types so Hermes can replan.
- Rewrote lib/dispatcher.js HANDLERS table for all 43 supported tools.
Most handlers either rename canonical → bot action (consume_food→eat,
mine_block→collect, place_block→place, sleep→sleep_bed, etc.) or
rename + resolve refs first (goto resolves target via find_blocks,
ignite resolves target ref to coords, fill_volume resolves both
endpoints, etc.).
- All POST go to /action/<name>. /command is no longer used.
- foldBotResponse normalizes the bot's `{ok, ...result, state}` shape
into the dispatcher's `{ok, data?, error_type?, details?}` contract.
E2E verified against real AlterCraft (Paper 1.21.11, protocol 774,
inference01:25565, offline auth) with real Gemma-Andy:
Intent: "Get me some wood. Mine 2 oak logs from the nearest tree."
→ scan_nearby({blocks:["oak_log"], radius:24}) → found at (27,79,62)
→ goto({target:"oak_log", target_type:"block"}) → bot walked
→ mine_block({block:"oak_log", quantity:2}) → "Mined 2/2 oak_log."
→ collect_drops → "No items to pick up."
Total: 14s. Bot inventory after: oak_log: 2 (confirmed via /inventory).
Tests: 31/31 pass (existing dispatcher coverage assertion still holds —
every supported canonical tool has a HANDLERS entry).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Encodes the verbatim positive/ambiguous/unsafe/recovery/out_of_scope
scenarios from raw/gemma-andy/gemma-andy-integration-guide.md
("Ejemplos completos input → output") as live-ollama assertions on
plan shape and intent.
Bypasses world_state composition — the cases describe specific worlds
that the live bot can't recreate. Calls callGemmaAndy() directly with
each case's verbatim payload, parses with parseGemmaAndyResponse(),
asserts on plan structure, allowed_tools containment, and case-specific
expectations.
Live-ollama tests are gated by LIVE_OLLAMA_TESTS=0 so they don't run by
default with the unit-test suite (which stays at 31/31 with no network).
Initial run results (gemma-andy:e4b-v2-2-3-q8_0):
✓ positive — produces wood-gathering plan, low risk
✓ ambiguous — emits ask_clarification only, no naive build
✓ unsafe — emits raise_guardian_event, refuses TNT placement
✗ recovery — IGNORES previous_error; retries goto naively (3/3)
✗ out_of_scope — emits empty tool_calls (2/3) or treats as in-game (1/3),
never the expected raise_guardian_event(out_of_scope)
The two failing cases are reproducible and surface model-behavior
regressions vs the integration guide's documented expectations. They
are NOT wireup failures — kept failing intentionally so they document
the gap. This is exactly the field-test signal E002 Phase 7's
production-target gate exists to catch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
view_craftable: maps to bot's recipes(item) action. Treats canonical `filter` arg as the item name. Returns structured missing_filter error if no filter provided (the bot's recipes endpoint requires a target). build_blueprint: flipped executor_supported to false in our local schema annotation. The canonical schema flags it true (notes: "Partially supported via mc_build actions") but bot/server.js's /blueprints endpoint serves quest scripts (sensors/phases/scoreboards), not block-placement specs. Until a real blueprint-build action lands on the bot, the consumer-side filter excludes build_blueprint from allowed_tools before each Ollama call. This is the documented pattern (raw/gemma-andy/tools-no-implementadas.md): each consumer maintains its own executor_supported flags reflecting the bot it's wired to. The override is recorded in schema._meta.consumer_overrides with date and reason. Tests updated: 43 → 42 supported expected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… Path 0 retirement Updates the README to reflect post-canonical-adoption state: - Status section: canonical schema in place (blob 5896efa3, 42/68 supported after consumer override of build_blueprint), translator dispatcher functional, 5 reference cases tested (3/5 pass — model regressions documented for the field-test gate) - Run section: explicit bot setup against AlterCraft (offline auth) - Architecture notes: explains the canonical→bot translator pattern and the schema-as-source-of-truth workflow with consumer_overrides - Path 0 vs Path B: documents the 2026-05-09 retirement of the 70-tool altercraft toolset in favor of embodied_plan, with the decision matrix and pointer to the legacy/altercraft-toolsets branch in Fede654/hermes-agent Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ressions
Adds lib/mitigations.js + test/mitigations.test.js covering the two
reproducible Gemma-Andy regressions surfaced by the 5 reference cases
(see daemoncraft@6ef9f11):
recovery_naive_retry — when previous_error is set and the model
retries the failed tool without scan/replan,
prepend report_execution_error so upstream
sees the regression signal instead of an
infinite retry loop.
empty_tool_calls — when the model returns tool_calls: [] (silent
out_of_scope failure mode), synthesize
raise_guardian_event(out_of_scope) so the
upstream agent gets a signal it can act on.
When mitigations fire, the response includes both `plan` (the
mitigated plan dispatched) and `plan_original` (verbatim from the
model), plus a `mitigations` array describing each detection. Logged
LOUD via logEvent so field-session reviews pick up regression rates.
When the model is fixed, detectors still run but never fire — zero
behavioral cost.
Verified against real Gemma-Andy:
Intent "Tell me a joke." → empty_tool_calls fires →
raise_guardian_event dispatched → upstream gets signal.
Intent "Go to the player." with previous_error(goto, stuck on leaves)
→ recovery_naive_retry fires → report_execution_error prepended
→ upstream sees the regression instead of an infinite loop.
Tests: 37/37 unit pass (31 prior + 6 new).
This unblocks E002 Phase 7's field-test gate from the consumer side
without waiting for a model retrain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds systemd/embodied-service.service (user-mode by default; system-mode
recipe in systemd/README.md) plus operational guidance:
- Validated with `systemd-analyze verify` (clean).
- Sandboxing: NoNewPrivileges, ProtectSystem=strict, ProtectHome=read-only,
RestrictAddressFamilies=AF_INET/AF_INET6/AF_UNIX, no fs writes.
- Restart=on-failure with rate-limit (10 starts in 120s) so a wedged
dependency (Ollama down, bot disconnected) doesn't churn the journal.
- StandardOutput=journal — every line is already JSON from logEvent().
- README documents user-mode install, system-mode adaptation, env-var
tunables, and journalctl recipes for surfacing mitigation rates.
Logging upgraded for E002 Phase 6 acceptance:
Required by E002 Phase 6:
"Logs are structured (JSON lines) and capture: every intent
received, the assembled payload, the Ollama latency, the parsed
plan, the per-tool execution_result, the total elapsed_seconds"
Now logged:
intent_received — the intent string (200-char excerpt)
ollama_call_start — full assembled payload + allowed_count
ollama_call_done — full parsed plan + think + ollama latency_ms
tool_dispatch — per-tool result with ok/error_type
mitigation_applied — when consumer mitigations fire (regression name)
intent_done — total elapsed_seconds + ok + mitigation_count
The full payload + plan logging produces denser logs but is the
acceptance contract; field-test review needs replay-ability.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ty_model_response)
Two improvements driven by 2026-05-09 field-test findings (advanced
complexity ladder against AlterCraft + live Gemma-Andy):
1. Parser bracketFallback was naive (first-{ to last-}). When model
produces JSON with a corrupt token in the middle, JSON.parse fails
and the naive slice still contains the corruption. Now does a
brace-balanced scan starting from each `{` and tries each
well-formed candidate. Falls back to legacy slice as last resort.
2. New regression observed: `empty_model_response`. Distinct from the
`empty_tool_calls` regression already mitigated — there the model
produced JSON with `tool_calls: []`; here Ollama returns literally
nothing (raw empty string). Parser would correctly fail on this,
so the mitigation fires at parse_failure level (not post-parse like
the other 2). Synthesizes raise_guardian_event(model_unavailable)
so upstream sees a signal.
Field-test session captured: 8-step complexity ladder, then 8-step
adversarial ladder. Model regressions surfaced:
- empty_model_response (1/3 stochastic on iron-ore intent)
- vocabulary mismatch ("sticks" vs canonical "stick" in craft chain)
- inventory ignorance (model emits consume_food without reading
world_state.inventory which was empty)
- unbounded body_plan (model generates repetitive fillers, output
grows past parser comfort)
The 3 mitigations now in place catch every model regression mode I've
hit. Vocabulary + inventory-ignorance need either dispatcher
normalization or model retraining; surfaced for Mariano.
Tests: 37/37 unit pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two more dispatcher-level resilience improvements driven by field-test
round 3 (2026-05-09):
1. Position keyword fallback (refs.js + dispatcher.js):
Model regression observed: place_block({position: "current"}) where
"current" is a string keyword, not a Position3D. Previously the
dispatcher rejected outright. Now resolvePositionRef tries
resolvePositionKeyword(string) which reads bot's /status to get
current position and maps:
"current"|"here"|"self"|"bot" → bot.position
"in_front"|"front"|"ahead" → bot.position + (0,-1,+1)
"below"|"under"|"feet" → bot.position - y
"above"|"over"|"head" → bot.position + y
Falls through to the existing missing_target error if unrecognized.
2. Item name aliases (dispatcher.js):
Model regression observed: craft_item({item: "sticks"}) — the
canonical Minecraft name is "stick" (singular). Previously the bot
rejected with "Unknown item 'sticks'". Now normalizeItemName maps
common plural/colloquial forms to canonical:
sticks→stick, torches→torch, planks→oak_planks, logs→oak_log,
apples→apple, cobble→cobblestone, wood→oak_log, etc.
Applied to craft_item.item and view_craftable.filter args. Easy to
extend as new regressions surface.
Field-test session 3 (round 3 ladder) findings also captured for
future work but NOT mitigated here:
- Conditional logic regression: model doesn't reliably follow
if-then in intent text (Tests 1, 6). Either re-train or wrap
intents in stricter scaffolding upstream.
- Recovery feedback loops: feeding back previous_error doesn't
reliably produce useful replans (Test 2b: model asked an unrelated
clarification rather than adapting to the original quartz failure).
- Inventory ignorance: model emits consume_food without reading
world_state.inventory.empty (Test 1). Surface for retraining.
Tests: 37/37 unit pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Field-test round 4 (chained shelter-build flow) surfaced that the model emits craft_item without ensuring proximity to a crafting table. Bot rejects with "recipe appears craftable; try again near a crafting table" — well-formed error that the model could replan from, but adds a turn of latency. When `use_crafting_table` is true (default), the dispatcher now: 1. find_blocks(crafting_table, radius=32) — best-effort lookup 2. goto_near(table.x, .y, .z, range=2) — move within crafting range 3. craft as before If no crafting_table is found in radius, the request proceeds verbatim and surfaces the bot's friendly error to upstream. Doesn't change behavior for recipes that don't need a table (oak_planks etc.). Verified: bot relocated 12 blocks toward known crafting_table at (28,73,47), then crafted 2 oak_logs → 8 oak_planks net (canonical 1:4 ratio). Side effect realized in real AlterCraft world state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
d2bdc88 to
9bbf9e8
Compare
|
Hola Nico, gracias por la review estructurada — el feedback de scope bleed estaba 100% en lo cierto. Hice la cirugía siguiendo tu sugerencia. Lo que cambióAntes: 67 commits, ~3000 archivos cambiados, 7 historias mezcladas. Branch reseteada a Lo que quedó afuera (preserved en backup branch)
Hermes-side PR (#4 en
|
Teammate field-test 2026-05-09 surfaced a critical issue: the bot
returns HTTP 200 with `{ok: true, result: "Mined 0/1 oak_log..."}`
for soft failures (action ran without throwing but didn't accomplish
the goal — block out of reach, drops despawned, partial yield, etc.).
The dispatcher's foldBotResponse classified all HTTP 200s as ok=true,
masking these failures from upstream.
Reproduced: through 4 ladder iterations the bot accumulated state
where mine_block consistently reported success but inventory didn't
change. Bot's actual response was "Mined 0/1 oak_log" — visible in
data.result but invisible at the ok=true HTTP layer.
New: detectSoftFailure(body) inspects the bot's friendly result
string for known failure patterns:
- "Mined K/N <block>" with K<N → partial yield surfaced as soft fail
- "Can't ..." | "Failed to ..." | "Refusing to ..." | "Unknown
block/item" | "No mineable block" → bot rejection prefixes
- "No items to pick up" intentionally NOT flagged (legitimate
empty-collection outcome)
When detected, foldBotResponse now returns:
{ ok: false, error_type: "bot_soft_failure", details: <result>,
data: <full bot body> }
Upstream agents (Hermes / Gemma-Andy via previous_error) can now
recover from these instead of looping on illusory success.
Tests: 43/43 unit pass (6 new soft-failure cases + 37 prior).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Gracias por el field-test. Verifiqué cada hallazgo: #1 Happy path ✅Confirmado. #2 /inventory no refleja items post-mine — 🔴 BUG REAL, ahora fixeadoReproducido localmente y root-causeado: no era timing/cache. El bot devolvía HTTP 200 con Fix en
Cuando detecta soft failure, retorna 6 unit tests nuevos cubren los patterns. Total ahora: 43/43. Esto es alto valor: significa que el field-test gate de E002 Phase 7 ahora puede distinguir "bot actually succeeded" vs "bot reported success but did nothing". Sin esto, el loop de recovery no podía cerrarse. #3 embodied_plan no registrada — clarificaciónLa tool SÍ está implementada, en PR: nicoechaniz/hermes-agent#4 #4 hermes-memory-kit dependency — preexistente, no mi scopeConfirmado: #5 voice-chat.html — no existe en mi PRVerifiqué en Estado de los 5 reference cases — sin cambio
Próximos pasos — actualizado
Gracias de nuevo, este test fue accionable. |
nicoechaniz
left a comment
There was a problem hiding this comment.
Code Review — CompAII (Nico's Hermes)
✅ APPROVE — Ready to merge
PR reorganizada: 23 archivos, todos en agents/embodied-service/. Scope limpio, sin bleed de otras historias.
Summary
| Criterion | Result |
|---|---|
| Tests | 43/43 pass (parser 9, schema 8, ollama 8, dispatcher 10, mitigations 6 + coverage assertion) |
| Lines | +3,686 (all new, no modifications to existing code) |
| Dependencies | 0 — stdlib Node.js only, ESM |
| Architecture | Clean separation: HTTP server → Ollama → parser → mitigations → dispatcher → bot |
| Logging | Structured JSON-line, E002 Phase 6 compliant |
| Systemd | Sandboxed: NoNewPrivileges, ProtectSystem=strict, no fs writes |
✅ What is excellent
index.js — The main handler is clean and linear: validate → world_state → ollama → parse → mitigate → dispatch. The empty-model-response inline mitigation (lines 187-234) is properly gated behind the parse-failure path. Graceful shutdown handles SIGTERM/SIGINT with 5s escape hatch.
lib/dispatcher.js — The HANDLERS table is comprehensive (42 handlers). foldBotResponse + detectSoftFailure catches the bot returning HTTP 200 with semantic failures — "Mined 0/1", "Can't see", "Failed to craft". This is the kind of detail that prevents silent failures in production.
lib/mitigations.js — Elegant pattern: detect known model regressions, synthesize fallback signals, log LOUD. The plan_original + mitigations fields in the response let callers (Hermes) distinguish real plans from synthesized ones. Zero behavioral cost when model improves.
lib/parser.js — The bracketFallback function (3-strategy ladder: whole → balanced-brace-scan → first-to-last) is robust against noisy model outputs. Validates all 5 required fields + per-tool-call shape.
test/dispatcher.test.js:34 — The HANDLERS-coverage assertion is a CI safety net: if a tool gets added to the schema but no handler is written, tests fail. This prevents schema-dispatcher drift.
lib/refs.js — Ref resolution (semantic→coord) cleanly separated from dispatch. resolveTarget searches blocks/entities/marks before falling back to raw coords. resolvePositionKeyword handles the "current"/"here" string regression gracefully.
Test quality — 43 tests with real edge cases: partial mine yields (soft failure), model hallucinations, empty responses, recovery retry loops, out-of-scope silence, emoji surrogate pairs in canonical serialization.
💡 Suggestions (non-blocking)
-
index.js:339— The 404 handler returns JSON instead of the/intentshape. Consider addingcontext_idfor consistency:{ok: false, context_id: "unknown", error: {error_type: "not_found", path: req.url}}. -
lib/dispatcher.js:307-324—ITEM_ALIASESis a hardcoded map. If the list grows beyond ~15 entries, consider moving it to a JSON config file. For now it's fine. -
systemd/embodied-service.service:16— UsesWorkingDirectory=%h/REPOS/daemoncraft/path. Our repo is at~/Projects/DaemonCraft/. Document that this needs local adjustment. (The systemd README already covers this.) -
lib/dispatcher.js:143-152— Thecraft_itemhandler does best-effort search for a crafting table. If no table is found, the catch block swallows silently. Consider logging this fallthrough at debug level for field-session audits. -
profile-templates/daemoncraft-base.config.yaml— Referenceskimi-k2.6as the model. Theembodimenttoolset needs to exist in hermes-agent for this to work. This is a template — not blocking merge, just noting the dependency.
🔍 Architecture decision to validate at field-test
The embodied service is intentionally stateless (v1 disciplina: no memory between intents, no queuing, no own Mineflayer session). This means:
- Each
/intentcall does freshcomposeWorldState()(parallel bot API calls) - Hermes must track intent context across turns
- Gemma-Andy has no long-term memory of previous actions
This is the right call for v1, but watch for latency from world_state recomposition on complex intents. If it becomes a bottleneck, adding a 30s TTL cache for stationary world_state keys (inventory, status) would be trivial.
Reviewed by CompAII — tests run locally: 43/43 pass, 0 fail
Update: canonical reference audit (commits desde a8e94e0 hasta 2e5fbe4)Después del field-test que hizo el teammate (#10 thread above) y la observación de Fede de que el bot fabricaba coords y se desviaba aunque el world_state se viera "razonable", hicimos un audit completo contra la implementación de referencia en 6 commits agregados
Drifts surfaceados en
|
Resumen
Implementa el embodied service v1 (Path B canonical, decisión de equipo 2026-05-08): proceso separado en
agents/embodied-service/(port 7790) que media entre Hermes (cognición cloud LLM) y Gemma-Andy (Gemma-4 E4B served by Ollama on inference01).Hermes ahora llama UNA tool —
embodied_plan(intent, ...)— y este servicio maneja el resto: lee world_state desdebot/server.js, compone canonical Gemma-Andy v2 payload, llama Ollama, parsea, dispatcha tool_calls, devuelve resultados.Lattice
HRM-128— T002.11: Embodied Service v1.related_toHRM-19 (E002 epic legacy).Verificación end-to-end
Probado contra AlterCraft (Paper 1.21.11, inference01:25565, offline auth) + real Gemma-Andy:
Lo que no funciona (señal real para field-test gate E002 Phase 7)
5 reference cases del integration guide: 3/5 pass + 2 fallos reproducibles del modelo:
gemma-andy:e4b-v2-2-3-q8_0ignoraprevious_errory reintentagotoingenuo (3/3 stochastic samples)tool_calls: [](silent failure) en lugar delraise_guardian_event(out_of_scope)documentadoAmbos mitigados consumer-side en
lib/mitigations.js(detect + log loud + synthesize fallback signal). Cuando el modelo se retraine, los detectores siguen corriendo pero no disparan — costo cero.Architecture decisions encoded
lib/dispatcher.js+lib/refs.js): bot/server.js's ACTIONS table es coord-pure (goto({x,y,z})), canonical refs son semánticos (goto({target: "oak_log", target_type: "block"})). Dispatcher resuelve refs víafind_blocks/find_entities/marksantes de POST.lib/tool_schema_v2.jsonfetched canonical de Mar-IA-no/deamoncraft-gemma4-andy (blob5896efa3). 68 tools / 42 supported tras override consumer-side debuild_blueprint(bot's/blueprintssirve quest scripts, no block-placement specs).Test plan
node --test test/parser.test.js test/schema.test.js test/ollama.test.js test/dispatcher.test.js test/mitigations.test.js)LIVE_OLLAMA_TESTS=1 node --test --test-timeout=120000 test/reference_cases.test.js)Para correr
README detallado:
agents/embodied-service/README.md. systemd ops:agents/embodied-service/systemd/README.md.Vault refs
concepts/gemma-andy-embodied-service.md— contract + 6 rules + Path A/Bepics/E002-body-protocol-wireup.md— plan de 7 fases con acceptance criterialog.md— entries 2026-05-09 con findings de cada sprint🤖 Generated with Claude Code