# 2026-07-09 — Session execution, UX, and learning improvements

**Status:** Planned

## Goal

Fix the hard blocker and UX issues found in the latest production Nomos session
(`b9c5de7c-e0b5-424c-9149-9fd45b2e7011` — "deploy TypeType as an LXC on strong").
The user asked Nomos to deploy an LXC; Nomos gathered data, proposed a plan, but
at the final step ("run all yourself") said it *couldn't* — `request_execution`
has no `pct_create` action. The user's objective was not achieved.

## Session analysis

10 messages (5 user / 5 assistant), 150 tool calls, zero LXC created.

| Msg | Role | Tool calls | Top tools | Summary |
|-----|------|-----------|-----------|---------|
| 0 | user | 0 | — | "deploy TypeType on strong as LXC" |
| 1 | assistant | 40 | get_entity(20), search_knowledge(12) | Fleet scan, entity detail per host/LXC |
| 2 | user | 0 | — | "it's github.com/Priveetee/TypeType, use tube.hubris.network" |
| 3 | assistant | 2 | search_knowledge(2) | MCP has no web fetch tool → couldn't read GitHub |
| 4 | user | 0 | — | "I think you can figure out those" |
| 5 | assistant | 60 | search_knowledge(32), request_execution(14) | Built the plan, tried provisioning, failed silently |
| 6 | user | 0 | — | "connect to strong media disk, no youtube login, proceed" |
| 7 | assistant | 44 | search_knowledge(10), get_entity(10), request_execution(8) | Full deployment plan laid out |
| 8 | user | 0 | — | "run all yourself" |
| 9 | assistant | 4 | request_execution(4) | **"I can't run pct create — only pct_exec, systemctl, restart, apt_upgrade"** — failure |

## Findings

### 1. HARD BLOCKER: `pct_create` missing from `request_execution`

`request_execution` (`internal/mcp/server.go:264-376`) supports `restart`,
`systemctl`, `pct_exec`, `apt_upgrade`. The actuator has `ProvisionLXC()`
(`internal/actuator/actuator.go:209`) that calls `pct create` via SSH, but it
is only wired into the auto-act signal pipeline — Nomos has no way to invoke
it through MCP.

Nomos's final response laid out the exact `pct create` command the operator
needs to run manually on the Proxmox host. That's a dead end for the user, who
expected the agent to execute from chat.

**Fix:**
- Add `pct_create` action to `request_execution` handler.
- Wire it to the existing `ProvisionLXC()` function.
- Classification: `config_mutation` — requires operator approval. Once approved
  (from the Ops page or Matrix), the actuator picks it up and provisions the
  LXC with the step callback reporting progress.
- Alternatively (for the "run from chat" expectation): add a chat-level approval
  flow — when Nomos proposes `request_execution` with `pct_create`, the frontend
  renders an inline "Approve" button in the chat bubble. Operator clicks → it runs.
  This is the UX the user described: "allow the agent to run things directly from
  the chat I'm in."

### 2. UI: ToolCallGroup expanded by default during streaming — no status animation

`ToolCallGroup.svelte` uses `<details open>` when `active=true`. During a
streaming turn with 40+ tool calls, the collapsed group fills the viewport
with raw JSON. The summary header shows only a static icon and "N tools" text.

**What happens now:**
- Streaming starts → group opens and stays open → all tool results visible as raw JSON.
- When streaming ends → auto-collapses. No animation.
- Header shows `WrenchIcon` pulsing OR `CheckIcon` OR `XIcon` — but no live
  running count, no per-tool status in the collapsed summary bar.

**What should happen:**
- Group starts **collapsed** by default. The summary header shows a live animated
  status: "⠋ Running get_lxc_state (caddy)… [2/40 done]" with the active tool
  name + a progress fraction, updating in real time.
- When a tool completes, the header briefly reflects it ("✓ get_lxc_state (caddy)")
  before moving to the next.
- Clicking the summary expands the group with a smooth animated open/close
  (replacing native `<details>` with bits-ui `Collapsible` + CSS transition).
- On load from history (not streaming), always starts collapsed.

**Fix:**
- Replace `<details open>` with bits-ui `Collapsible` component (already in
  `web/src/lib/components/ui/collapsible/`).
- Add `animate-pulse` to the chevron icon during streaming (user sees motion).
- Add a `statusText` derived that shows the in-progress tool name + count.
- CSS animation: `Collapsible.Content` supports `forceMount` with transitions.

### 3. No web-fetch tool → Nomos can't read GitHub READMEs

Msg 3: Nomos needed to inspect `github.com/Priveetee/TypeType` to understand the
stack. It used `search_knowledge` (DB FTS), which returned nothing because the
repo isn't in the DB. The agent had no way to fetch external URLs.

The MCP has 27 tools (21 listed in AGENTS.md + 6 more added since), but none
for HTTP/web fetching. Nomos can only query the DB or execute SSH commands on
existing hosts.

This forced the human to provide context that should have been machine-read.

**Fix options (non-blocking):**
- Add an `http_get` MCP tool that returns sanitized body text (strip scripts,
  truncate to 8KB). Rate-limited per-turn.
- Or: add `web_fetch` as a first-class action in the MCP gateway itself, since
  the gateway container already makes outbound HTTP calls to OpenRouter.

### 4. Task: Bulk-tool awareness already in SOUL.md but not enough

The SOUL.md already says "prefer `list_lxcs` over `get_lxc_state` for fleet-wide"
(line 27-28). But msg 1 still made 40 calls. The issue:

- `get_entity` was called 20 times (one per entity found by `list_entities`).
  `list_entities` already returns all entities; the agent wanted per-entity
  detail, which is redundant since `explain` or `get_state_snapshot` gives the
  same info in one call.

**Fix (already planned in `2026-07-09-chat-sessions-improvements.md` finding 3):**
- Enrich `list_lxcs` with CPU/memory utilization so the model doesn't feel it
  needs `get_lxc_state` per container.
- Add `get_tools_summary` to SOUL.md preamble that lists each tool's intended
  use and warns about N+1 call patterns.

### 5. Skill gaps

| Missing | Why | Where to add |
|---------|-----|-------------|
| `http_get` / `web_fetch` | Agent can't read external URLs | MCP tool in `internal/mcp/server.go` |
| `session-review` skill | No way to learn from failed sessions | `.agents/skills/session-review/SKILL.md` |
| `pct_create` action | Can't provision new LXCs from chat | `internal/mcp/server.go` + `internal/actuator/` |
| `request_execution` approval from chat | Operator must switch to Ops page | Inline chat approval component |

## Implementation order

1. **Add `pct_create` to `request_execution`** (`internal/mcp/server.go`) —
   hard blocker, needed for the session's objective.
2. **ToolCallGroup: collapse by default + animated status header**
   (`web/src/lib/components/ToolCallGroup.svelte`) — immediate UX win, the user
   explicitly asked for this.
3. **Inline chat approval for `request_execution`** — renders an "Approve"/"Deny"
   button inside the chat when an execution is queued for approval. Lets the
   operator approve from the same chat.
4. **`session-review` local skill** — lives in `.agents/skills/`, loads when
   examining chat sessions for failure patterns.
5. **`http_get` MCP tool** — non-blocking but addresses a real gap seen in this
   session.

## Verification

- Re-run the TypeType deploy prompt against the patched agent. Confirm:
  - Agent proposes plan as before (keep plan-proposal behavior).
  - When user says "run all", agent calls `request_execution(target=lxc:typetype,
    action=pct_create, params=<json>)`.
  - Approval fires → operator sees inline approval in chat → approves → LXC created.
- ToolCallGroup: start a session that triggers 5+ tool calls. Confirm:
  - Group starts collapsed.
  - Summary header shows animated status: tool name + count updating in real time.
  - Clicking expands smoothly.
  - On session reload (history), stays collapsed.
- Load `session-review` skill and ask it to analyze the TypeType failure session;
  confirm it identifies the missing action as the root cause.
