Against a self-hosted Honcho 3.0.11 every read path came back empty while
the server was healthy and full of derived facts: the plugin parsed a
`conclusions`/`summary` shape the API no longer emits.
- honcho-client: typed models for the real schema — `PeerContext`
(`representation` markdown + `peer_card`), `SessionContext` (`summary` as
an object, `peer_representation`), wrapped `PeerCard` (a bare array on PUT
is a 422, which also broke `honcho_profile` writes).
- plugin: the turn-time injection and `honcho_context` read the
representation; `honcho_search` and the page's /search now use
`conclusions/query` (observer/observed scoping inside `filters`) — a real
ranked semantic search with fact ids, which `peer_context?search_query`
never provided; /overview returns card + representation + conclusions.
- compose: pin the Honcho image by digest (3.0.11) — ghcr publishes no v3
semver tags, and an untracked `:latest` pull is what drifted the schema.
- tests: fixture tests from payloads captured on the live server, plus an
env-gated live smoke test (HONCHO_E2E_URL/_WS/_PEER, `cargo test
-p honcho-client -- --ignored`) — run it before any future Honcho bump.
The single-user notifications.md mechanism (a file in data/) died in the
multi-user move: assistant's prompt still pointed at it, but nothing read
it. Replace it with a memory note:
- event-triage injects user-memory/notifications.md verbatim on every
pass and treats it as authoritative over its default heuristics
- a shared common/notifications.md fragment, included by assistant, kid
and project-coordinator, tells the chat agents to record preference
requests there: one dated rule per bullet under a source heading or
General, asking for the source when ambiguous
- docs/system-agents.md explains the steering to users
Event triage is the one system agent whose right cadence depends on who it
runs for: it fires on inbound events, so someone on a dozen mailing lists
has something waiting on nearly every tick while a quiet account has
something waiting almost never. A single instance-wide interval serves one
of them badly, and the observed failure is the first: the agent starts on
practically every pass.
An admin can now set a per-person interval on that user's page (Users ->
the person -> Event triage). Empty means "follow the instance setting",
which stays the state nobody has a row for.
- New registry table `system_agent_user_settings(agent_id, user_id,
interval_secs)`. A row is an override and its absence is inheritance --
no sentinel value, no row seeded at user creation, clearing the field
deletes the row. Registry rather than the user's own file because the
writer is the admin and a member's database is unreadable unless they
happen to be logged in; a setting that could only be changed during its
subject's session would not be a setting. Keyed by agent_id though only
one agent uses it, so a future agent's schedule is not a schema change.
- `SystemAgent` gains `interval_secs_for(user_id)`, which `is_due` now
measures against, and `shortest_interval_secs()`. Both default to the
existing `interval_secs`, so every other agent implements nothing. The
second is the non-obvious half: `base_tick` sleeps for the shortest
interval any enabled agent asks for, so without it an override below the
instance value would be rounded up to it -- an override that works when
it lengthens and silently does nothing when it shortens.
- `GET/PUT /api/users/{id}/event-triage`, admin-gated, minutes on the
wire, null to clear. Nothing rides the bus: the scheduler re-reads the
interval every tick and due-ness is counted from the user's own last
attempt, so a change lands on the next wake-up with no push.
Both helpers fail open onto the instance value -- an unreadable registry
must not turn into an agent that stops running for someone.
Docs: docs/system-agents.md gains the per-person section and no longer
reads as if the interval were one number for everybody.