MalloryEstate Briefing
M·046Approved — ready for plan
← All briefings

Q & Hermes

Q Memory Context

Giving conversational Q actual memory — per-turn assembled context for the general thread, a persistent session for task threads.

Dated 2026-06-22Programme Q & Hermes

Situation

Root-caused the same day Q joined the home feed (Slice 1): the chat frame is only {taskId, text}, relayed verbatim; the Tower's one-shot hermes -z call forgets every prior turn immediately; and Q's own q_messages thread never sees the Mallory/Moneypenny turns that live in messages. Handing off to Q from the feed gave it zero context — pre-existing, not a Slice-1 regression, but newly glaring now that Q is a feed persona.

The fix is a hybrid split by thread type: the general home-feed thread stays stateless on the Tower but gets a per-turn context block assembled at the edge (mirroring exactly how Mallory/Moneypenny get context today); the task-scoped thread gets a real persistent Tower hermes session, since Q's working state across a build should actually persist. Spans two repos — mallory (edge) and mallory-runtime (Tower q-gateway) — as two independent PRs.

Key decisions

Hybrid by thread type

General thread (taskId IS NULL): edge-assembled per-turn context, Tower stays stateless. Task thread (taskId set): a persistent Tower hermes session keyed to the task, since its working state should persist across a build.

One mechanism solves memory and cross-persona context

The edge-assembled transcript already contains Q's own prior replies (memory) plus the Mallory/Moneypenny turns (cross-persona context) — no session lifecycle needed for the general thread.

Bounded context

A pure renderQContext caps the transcript to the last ~16 turns and the pinned About-Alex block to ~10 facts — the single place these caps live.

Optional protocol field, not a breaking change

The client→edge frame is unchanged; the edge→Tower relayed frame gains an optional context? string (general thread only).

Never break a Q turn

If context assembly throws (a D1 error), the DO relays the bare frame; if the Tower session call errors, the gateway falls back to one-shot -z. Both degrade gracefully rather than hanging.

Deploy order not load-bearing

An old Tower ignoring the new context field still works one-shot; an edge that hasn't sent context yet leaves the Tower on its current behaviour — the edge and Tower PRs can ship in either order.

Architecture

Two independent halves: the edge assembles and attaches context on general-thread frames; the Tower prepends that context (or runs a named session for task threads) when calling the hermes CLI.

src/lib/qcontext.ts (edge)
Pure renderQContext(items, about, modeLine) — builds a persona-labelled transcript (You:/Mallory:/Moneypenny:/Q:) plus mode + About-Alex grounding, capped and clipped.
src/qhub.ts (edge, QHub Durable Object)
On a {t:'chat', taskId:null} frame, calls getHomeFeedPage + getPinnedFacts + resolveCurrentMode, attaches context to the relayed frame before forwarding to the Tower.
src/lib/qproto.ts (edge)
Adds the optional context?: string field to the chat envelope.
tower/q-gateway/q-gateway.mjs (Tower)
A pure buildChatArgs({taskId, text, context}) helper decides the hermes CLI invocation: prepend context for general-thread one-shot calls, or use --continue q-task-<taskId> for a persistent task session.
Tower session lifecycle
A named session is created lazily on first task chat, persists for the task's life, and only resets on an explicit reopen (new name or reset transition).

Risks & guardrails

Exact session flag unconfirmed

The plan assumes hermes --continue <name> -z <text>, but the implementer must confirm the real flag (--continue vs. --resume/--pass-session-id) against live hermes --help on the Tower before wiring it in.

Edge D1 reads must never block a Q turn

A failure in getHomeFeedPage/getPinnedFacts/resolveCurrentMode has to degrade to the bare frame, not throw and stall the relay.

Task session reopen handling

Restarting or reopening a cancelled/done task needs a correctly fresh session name/reset, or Q's task memory could bleed across unrelated runs of the same task id.

Delivery

Edge PR (mallory)

renderQContext pure helper + tests, context? added to the chat envelope, QHub attaches context on general-thread frames, verification — deploys from the main checkout independently of the Tower.

Tower PR (mallory-runtime)

buildChatArgs pure helper + tests, wired into the chat handler with a one-shot fallback, verification — deploy is pull + restart the q-gateway systemd unit.

Testing

Edge: transcript capping/clipping, context attached only on taskId:null, D1-error degrades to bare frame. Tower: context prepended on the general path, --continue invoked on the task path, error falls back to one-shot.

Out of scope

UI coherence — killing the stray addressee selector and unifying the sit-rep Q chat onto the home feed — handed to Hermes as its own follow-up taskQ-in-feed Slice 2 (inline task-action cards + the live-reference card primitive)Mallory-authored handoff summaries richer than the raw transcript
spec · docs/superpowers/specs/2026-06-22-q-memory-context-design.md
plan · docs/superpowers/plans/2026-06-22-q-memory-context-edge.md
plan · docs/superpowers/plans/2026-06-22-q-memory-context-tower.md
Mallory · Estate briefing