MalloryEstate Briefing
M·041Awaiting review before plan
← All briefings

Q & Hermes

Hermes Phase 4 — Digest Sync View

Hermes' activity surfaces inside Mallory as a read-through feed — inbound-only, deduped, and voiced in Mallory's own tone.

Dated 2026-06-14Programme Q & Hermes

Situation

Phase 4 surfaces "what Hermes is doing" inside Mallory, without breaking the edge-local invariant: Hermes pushes an activity feed *inbound* to the edge Worker on a cron, the Worker stores and composes a Mallory-voiced one-liner per event, and the web app shows it as a read-through panel at ?view=hermes.

The design deliberately keeps this read-only and one-directional — sending *to* Hermes from the web, a router that delegates Worker requests to Hermes, and an Android view are all explicitly deferred to later work.

Key decisions

Accumulating feed, not a snapshot

Mirrors the existing /updates surface, with its own read/unread state.

Dedupe ledger lives on the Worker

Hermes re-emits its recent activity window every run; INSERT OR IGNORE on a stable dedupe_key is the single source of truth for "already seen" — Hermes carries no local dedupe state, the same philosophy as mention-nudges.

Voice on first insert only

composeTeaser (reused from src/lib/teaser.ts) runs once, when a row is newly inserted — no Haiku spend on re-emitted duplicates.

Read-through, read-only

HermesView.svelte mirrors UpdatesView and the ?view= deep-link pattern; no write path back to Hermes in this phase.

Architecture

Mirrors the /context//nudge inbound pattern end to end: a Tower cron job, a CN-gated Worker ingest, a D1 dedupe ledger, and a read-through Svelte panel.

migrations/013_hermes_events.sql
hermes_events table — dedupe_key PK, kind, summary, detail, url, voiced, event_ts, created_at, read_at.
src/lib/hermesDigest.ts
parseHermesDigestPayload — validates each event's kind/ts/summary/dedupe_key, mirroring parseNudgePayload.
src/routes/hermes.ts
handleHermesDigest (POST, CN-gated), handleHermesList (GET), handleHermesRead (POST) — insert-or-ignore, voice-on-first-insert, list/read.
tower/hermes-digest.sh (mallory-runtime)
Gathers recent Hermes git activity + vault notes into an events JSON and POSTs it via CF Access service token on a */30 cron.
web/src/lib/components/HermesView.svelte
Full-screen read-through panel: voiced one-liner + raw summary/detail + kind badge + timestamp, mark-all-read on load.

Risks & guardrails

Noisy feed

Every commit as an event would flood the view — scoped at v1 to dev (commits/PRs) + vault notes only.

Haiku cost

Voiced only on first insert; falls back to the raw summary if the voicing call is unavailable.

CN spoofing

The digest endpoint is gated to the Tower's service-token CN (HERMES_DIGEST_CN), mirroring /context.

Clock skew on event_ts

Both event_ts (Tower-supplied) and created_at (Worker receipt) are stored; display sorts by event_ts, dedupe stays keyed on dedupe_key.

Delivery

Worker (TDD)

Migration 013, payload parser, DB helpers, CN-gated route handlers, voice-on-first-insert — PR'd and merged first.

Web

HermesView.svelte + api client + ?view=hermes deep-link + menu entry.

Tower digest job

hermes-digest.sh + offline test, CF Access service-token creds, */30 cron.

Verify end-to-end

Cron POSTs → GET /hermes returns a deduped, voiced feed → re-running the cron produces no duplicate rows → a real Hermes commit appears within one cycle.

Out of scope

Sending to Hermes from the web (the full bidirectional gateway)Android view (web-first)A router that delegates Worker requests to HermesLocal model / cost-routing
spec · docs/superpowers/specs/2026-06-14-hermes-phase4-digest-sync-view-design.md
plan · docs/superpowers/plans/2026-06-14-hermes-phase4-digest-sync-view.md
Mallory · Estate briefing