Home / Engine / Agents and Council Architecture

Hermes reading loop - bug report (blocks all `/api/hermes` readings)

Updated Jun 20, 2026 · Affirmology_HermesLoop_Bug_v1.md

Summary. Jeff ran the two-person wealth-codes question live. After a hard refresh, Hermes returned a FULL, rich, mechanism-level reading for both Jeff and Sol: pulled both charts, ran Lakshmi (and others) deep, cited 8th-house leverage, the Gene Keys Pearls (45.5 Syner

Hermes reading loop - bug report (blocks all /api/hermes readings)

Found 2026-06-20 from Cowork, testing the wealth-reading rerun

RESOLVED LIVE 2026-06-20 - T1 PASSED on studio.affirmology.ai (cloud)

Jeff ran the two-person wealth-codes question live. After a hard refresh, Hermes returned a FULL, rich, mechanism-level reading for both Jeff and Sol: pulled both charts, ran Lakshmi (and others) deep, cited 8th-house leverage, the Gene Keys Pearls (45.5 Synergy / 49.5 Revolution), the channels, Black Moon Lilith / Chiron blocks, and current transits (Pluto square MC, Uranus trine Sun, Saturn conj ASC). Not a stub. The agentic loop fix is confirmed working on the deployed cloud.

BOTH ROUGH EDGES FIXED + DEPLOYED 2026-06-20 (commits ca3a055 + eb6c075, live on studio.affirmology.ai)

Two remaining ROUGH EDGES (non-blocking, hardening only):

  1. FRONT-END "could not reach my model" FLASH. The chat's blocking POST to /api/hermes gives up before the slow agentic read finishes, so hermesSend's catch shows "I could not reach my model just now." But the server keeps working and PERSISTS the reply - a refresh surfaces it. FIX (per CLAUDE.md's own guidance): when the POST is slow or errors, POLL GET /api/hermes/history?as_person=<id> for the new turn instead of showing the error. UX-only, no engine change.
  2. RESIDUAL MULTI-ORACLE BadRequestError (latent, see section below). Did NOT trigger on this two-person read (loop finished in budget), but a heavier request can still hit it. FIX still pending: in the forced-synthesis call, keep tools=HERMES_TOOLS OR strip tool_use/tool_result blocks before the tool-free call.

FIXED 2026-06-20 (Cowork) - UNCOMMITTED, needs a local server restart + a cloud push

Applied all of fixes 1, 2, 3 in affirmology-studio/api/main.py (the hermes() loop) + a defensive corpus-path fix in hermes_tools.py: - PROMPT (fix 1): _hermes_system now forbids narrating intent ("NEVER ANNOUNCE WHAT YOU ARE ABOUT TO DO... for a reading you MUST actually call consult_oracle before you answer"). - LOOP NUDGE (fix 2): a turn that ends with a narration-only preamble (detected by _is_narration) no longer counts as the answer; the loop appends "act, do not announce" and continues. Round budget raised 6 -> 8, max_tokens 1600 -> 2000. - FORCED FINAL SYNTHESIS (fix 3): after the loop, a tool-free call always composes the complete reading (handles both the cap-hit and the stub cases, with correct message-role alternation). No more stubs. - CORPUS (secondary): search_corpus now defaults to the engine's known corpus path if the env is unset, so it queries the same DB the health endpoint reports. TO ACTIVATE: restart start_public.sh for the local/funnel test, and commit+push api/main.py + api/hermes_tools.py for the cloud. Then re-run the single-person wealth prompt to confirm the oracles fire, then the two-person comparison.

RESIDUAL BUG after the fix (found 2026-06-20, post-restart)

Single-oracle requests now WORK (Sol + Lakshmi returned a real, craft-quality reading using the new points incl. Black Moon Lilith and the Progressed Moon). But MULTI-ORACLE requests (e.g., Jeff asking for 6 oracles) return ok:False "Hermes hit a snag reaching its model (BadRequestError)". Cause: the heavy request exhausts the round budget still in tool_use, so the FORCED FINAL SYNTHESIS call runs, and it omits tools= while the accumulated messages still contain tool_use/tool_result blocks. Anthropic 400s when messages contain tool_use blocks but the request defines no tools. FIX: in the forced-synthesis call, EITHER keep tools=HERMES_TOOLS in the request, OR strip all tool_use/tool_result blocks (and their partner assistant turns) from messages before the tool-free call. Workaround until fixed: keep each /api/hermes reading to 1-2 oracles so the loop finishes naturally.

Symptom

POST /api/hermes returns a STUB instead of a reading. Three live tests against the running server (funnel up, corpus present, Anthropic key set): 1. Two-person deep wealth request -> reply: "Corpus is quiet today. Moving straight to the oracles with full chart data." (tools_used: none persisted) 2. Jeff-only wealth request -> reply: "Good. Now running Lakshmi and Agastya simultaneously with the full chart data." (tools_used: ['get_person_chart']) 3. Jeff-only with an explicit "do not narrate, actually call consult_oracle for lakshmi/agastya/athena/prometheus/pythagoras and write the full reading" -> reply: "I did not catch that, can you say it another way?" (tools_used: ['get_person_chart'])

In every case Hermes pulls the chart once, then NEVER calls consult_oracle. No oracle ever runs. The user gets a one-line non-answer.

Root cause (in affirmology-studio/api/main.py, the hermes() agentic loop)

The loop breaks the moment a model turn comes back with stop_reason != "tool_use":

for _round in range(6):
    resp = client.messages.create(... tools=HERMES_TOOLS, messages=messages)
    if resp.stop_reason != "tool_use":
        break          # <-- exits here on a narration-only turn
    ... run tools, append results ...
parts = [b.text for b in resp.content if b.type == "text"]
reply = "\n".join(parts).strip() or "I did not catch that, can you say it another way?"

Hermes is ending a turn with NARRATION TEXT and stop_reason = "end_turn" (it says "now I'll run the oracles" instead of emitting tool_use blocks). The loop treats that as "done" and returns the narration as the final answer. When the narration turn has no text blocks at all, it returns the "did not catch that" fallback. So the orchestration never reaches the consult-the-oracles-then-synthesize stage.

Fixes (any one helps; do 1+3 for robustness)

  1. PROMPT: in _hermes_system, forbid narrating intent. Require that each turn either (a) emits tool calls, or (b) writes the final answer. Explicit line: "Never say what you are about to do. Either call the tools now, or give the finished reading. Do not announce steps."
  2. LOOP: do not treat a narration-only end_turn as the final answer when no real reading has been produced. If stop_reason == "end_turn" but no oracle was consulted yet and the text looks like a preamble, append a user nudge ("Now actually call the oracles and write the full reading") and continue the loop instead of breaking.
  3. FORCED FINAL SYNTHESIS: after the loop (or when breaking), make ONE last messages.create with tools omitted (or tool_choice disabled) and a system nudge "write the complete reading now from everything gathered." This guarantees a composed answer instead of a stub.
  4. Consider tool_choice={"type":"any"} on the early rounds to force tool use until enough oracles have run, then a final free-form synthesis round.

Secondary issue: corpus search returns "quiet"

Run 1's reply implies the corpus search tool came back empty, yet /api/corpus-stats shows 25,552 records at /Users/jeffreyparker/CLAUDE/AFFIRMOLOGY/corpus/corpus.db across all six traditions. The search_corpus tool is likely not matching (query/embedding or SQL/FTS mismatch, or a path/handle opened before the env was set). Verify the tool actually queries the same DB the health endpoint reports, and that it returns hits for a known term (e.g., "gate 45" or "8th house").

Net for the wealth-reading comparison Jeff asked for

The upgrade added the right ARCHITECTURE (12 oracles incl. Lakshmi for wealth and Agastya for Vedic, corpus grounding, the craft standard in the prompts). But end to end it does not yet produce a reading, because of the loop bug above. Until that is fixed, the agents cannot be compared on quality, because they return stubs. Priority: fix the loop (1+3), re-test with the single-person wealth prompt, then re-run the two-person comparison.

THIRD ISSUE: readings TRUNCATE at ~2000 tokens (found 2026-06-20)

Every long reading (the wealth syntheses, the numerology teaching, the Concordia joint) ends MID-SENTENCE around ~8000 chars. Cause: max_tokens is 2000 in the hermes loop / forced-synthesis call. Deep readings need more. FIX: raise max_tokens for the final synthesis call to ~4096-8192 (or stream and stop on natural end). Jeff noticed the v2 joint section and the Pinnacle teaching both cut off. This is the most visible quality bug right now.