From 82821561e65df12aef85b3c6e673aed1da430b38 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Sat, 23 May 2026 14:42:43 -0700 Subject: [PATCH] =?UTF-8?q?snapshot:=20persistent-memory=20=E2=80=94=20Hei?= =?UTF-8?q?mdall=20scope-model=20foot-gun=20note=20(post-v0.1.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Captures the lesson from today's lofn-scope chase: "per-Tier-1-agent scope add" is a phantom ask. The `agent.call:*` (singular) baseline rule in config/policies.yaml covers ALL Tier 1 foundational agents (mimir, lofn, all Asgardians) for every authenticated tier; there is no per-agent grant in this path. The plural `agents.call::` namespace is Tier 3 only (consumer-defined agents via POST /agents/define). Worldtree-dev shipped a corresponding doc fix (dd6e091) — new "Authorization model — agent invocation" section at docs/conversation-api-spec.md lines 58-114 + a heimdall.contract.md fix removing a misleading agent.call:mimir example. Don't ping infra-ops for "per-Tier-1-agent scope adds" again. Real future infra-ops asks remain: admin-tier key for the AdminEvents pane (admin.events.read scope, different tier) and Tier 3 custom-agent registration (POST /agents/define flow, different from scope-add). --- persistent-memory.md | 1 + 1 file changed, 1 insertion(+) diff --git a/persistent-memory.md b/persistent-memory.md index 8c98886..48b8a86 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -134,3 +134,4 @@ defense against re-attempting the same cul-de-sac. - `[2026-05-21]` **TUI session-identity rendering via `self.sub_title` + `self.hint` plain attributes.** Stored state but never rendered to a visible widget. The contract's "session-identity-always-visible" invariant was satisfied at the state-attribute level but not the user-visible-widget level. Tests asserted the attributes (which passed); Volva code-review flagged the gap. Fix: dedicated `Static(id="identity")` + `Static(id="hint")` widgets in compose; `_set_hint()` helper mirrors state → widget. Calibration evidence for the "TDD catches state, code-review catches whether the user can see it" pattern. - `[2026-05-23]` **Using the cross-model review agent's name directly in composed prose.** The peer review agent's name (the `althing` handle starting with "V-o-l-v-a") is one letter from a body-part term. Anthropic's content classifier does fuzzy matching and intermittently blocks responses mid-stream when the name appears in composed prose sentences (especially in meta-commentary about the agent's work). Direct-quoted tool output (e.g., the `althing-cli thread` body) passes through fine. Mitigation: use role descriptions ("the cross-model reviewer," "the paraphrase peer") in prose rather than the name; quote content via tool output. Confirmed by switching to Sonnet 4.6 for a test read — same raw content read cleanly when fetched via Bash rather than composed into an LLM response. This is a persistent environmental constraint, not a one-off. - `[2026-05-22]` **`json.loads(sse.data)` unguarded against empty data.** `_iter_events` unconditionally called `json.loads` on every dispatched `ServerSentEvent`. When `httpx_sse` surfaced a frame with `id:` present but `data:` empty (a known library-vs-spec divergence — RFC says don't dispatch; httpx_sse is permissive), `json.loads('')` raised `JSONDecodeError` → propagated through Textual's worker → app crash. Crashed mimir conversation at turn 93/seq 1078 after 1077 successful events. Fix: `if sse.data == '': continue` BEFORE `_parse_sse_id` (empty-data event with a malformed id is still a keepalive — don't reorder). Non-empty malformed data raises new `MalformedSseData(raw[:200])`. Don't reintroduce unconditional `json.loads(sse.data)`; always pre-check for the empty case. +- `[2026-05-23]` **Phantom "per-Tier-1-agent scope add" pattern.** Issue #5's lofn 422 was initially diagnosed (with worldtree-dev's first reply) as needing `agents.call:lofn` added to ratatoskr's existing key. Routed through infra-ops via althing per the credential-brokerage rule; infra-ops discovered no public scope-mutation endpoint on personal Worldtree, brokered to worldtree-dev for the actual mechanism. Worldtree-dev came back with a correction: their first answer conflated two distinct Heimdall scope namespaces. **Tier 1 foundational agents** (mimir, lofn, soong, all Asgardians) are covered by a blanket `agent.call:*` (singular) baseline rule in `config/policies.yaml > tiers..scopes` for ALL authenticated tiers including `user`. There is no per-agent grant for Tier 1 — the baseline rule covers it. **Tier 3 consumer-defined agents** (IDs containing `:`, like `vh:custom-bot`) use the plural `agents.call::` shape granted implicitly via owning a `consumer_agents` DB row, registered through `POST /agents/define`. The two notations differ by one letter and that was the source of the confusion. **The actual lofn fix was issue #5's `--end-user-id` flag — it was always a request-body validation, not an auth-scope gate.** Don't ping infra-ops for "per-Tier-1-agent scope adds" again; the pattern is a phantom ask. Real future infra-ops asks: admin-tier key for the AdminEvents pane (`admin.events.read` scope, different tier), and Tier 3 custom-agent registration (different flow entirely, requires `POST /agents/define`).