docs: hand-off refresh — container-tier work shipped, stale guidance corrected
TOOL-CALL-ARCHITECTURE.md said "switch claude_cli to stream-json" as the cheapest fix. That was wrong and is now marked so, with what actually happened: zero tool.call events with the parser working perfectly, because TurnEvent::ToolCall only fires for tools ZeroClaw itself executes. Hooks sidestep that entirely, and the doc now leads with the resolution rather than the theory. A fresh session is pointed at this file, so leaving the wrong recommendation on top would have sent it down the same path. NEXT-SESSION.md: state header, and the ordered list rewritten — items 1-3 are done or superseded. "Give the direct-session tier a tap" is dropped with its reason: that tier is dormant (CLAWMATES_MISSION_EXECUTOR unset), and checking before building saved the work. New top item is watching the first production mission, since the gate and tap are proven locally and unproven in prod. Added an operational section for the things that cost the most time: the 403 actions-log API, gw-04's legacy docker-compose, the socket proxy, disk contention between manual builds and CI, and Clerk-only prod auth. Also flagged that SKILL-USE-BASELINE.md's Trigger column is now stale in a good way — tool calls are observable on the container tier, so Trigger can be scored from behaviour instead of prose. That is the highest-value follow-up. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5a11fae0d6
commit
0b4d91889a
+66
-28
@@ -1,14 +1,20 @@
|
|||||||
# Where this left off — 2026-08-20
|
# Where this left off — 2026-08-21
|
||||||
|
|
||||||
Read `CAPABILITY-REVIEW.md` for the system picture,
|
Read `CAPABILITY-REVIEW.md` for the system picture,
|
||||||
`TOOL-CALL-ARCHITECTURE.md` for the current investigation, and
|
`TOOL-CALL-ARCHITECTURE.md` for how mission tools actually work (and the two
|
||||||
`SKILL-USE-BASELINE.md` for the measurement.
|
wrong theories that preceded it), and `SKILL-USE-BASELINE.md` for the
|
||||||
|
measurement — noting that its Trigger column is now out of date in a good way
|
||||||
|
(see "Next, in order" §2).
|
||||||
|
|
||||||
## State of the tree
|
## State of the tree
|
||||||
|
|
||||||
**Everything is pushed.** `main` is at `128b423`; the fork's
|
Everything is pushed. `main` is at `5a11fae`; the fork's
|
||||||
`merge/upstream-v0.8.4` is at `db1c50966`. The local suite is green — 106 test
|
`merge/upstream-v0.8.4` is at `be9c34b1c`. Local suite green: **107 test
|
||||||
binaries, zero build errors, frontend builds.
|
binaries, 412 lib tests**, frontend builds.
|
||||||
|
|
||||||
|
**CI is green and production is current.** Run 507 passed `test` and `build`,
|
||||||
|
and gw-04 rolled to it. Production also runs `clawmates-runtime:hooks` for
|
||||||
|
per-mission containers (see below).
|
||||||
|
|
||||||
## Container-tier tool gate + telemetry — SHIPPED 2026-08-21
|
## Container-tier tool gate + telemetry — SHIPPED 2026-08-21
|
||||||
|
|
||||||
@@ -74,32 +80,48 @@ services actually pushed — the failing runs had pushed `server`, aborted on
|
|||||||
|
|
||||||
**Operational note:** do not build images by hand on gw-04 while CI may run.
|
**Operational note:** do not build images by hand on gw-04 while CI may run.
|
||||||
|
|
||||||
## Then, in order
|
## Next, in order
|
||||||
|
|
||||||
1. **Prove the PreToolUse gate in a real VM.** `vm_tool_gate` is unit-tested and
|
1. **Watch the first production mission.** Nothing has run since the runtime
|
||||||
shell-tested on the host; it has never run in a guest. Four of its bugs were
|
flip, so the container-tier gate and tap are proven locally and unproven in
|
||||||
found only by executing the generated script, and two of them (syntax error,
|
prod. Prod auth is Clerk, so a mission cannot be launched from here.
|
||||||
`IFS`) produce a gate that looks installed while being fully closed or fully
|
```
|
||||||
open. Check `/root/toolgate/denied.jsonl` after a mission that tries
|
ssh gw-04 'docker logs clawmates_server_1 2>&1 | grep -E "per-mission runtime image|drained .* tool call"'
|
||||||
something denied.
|
ssh gw-04 'docker exec <cm-runtime-mission-…> cat /root/toolhooks/tap/tools.jsonl | head'
|
||||||
|
```
|
||||||
|
If tools appear in `mission_events`, the loop is closed. If not, check the
|
||||||
|
three failure shapes listed above — each looked correct from every other
|
||||||
|
angle.
|
||||||
|
|
||||||
2. **Give the direct-session tier a tap.** It is the one tier with no
|
2. **Now that tool calls are observable, redo the Skill-Use measurement.**
|
||||||
observability at all — `session_executor::run_session` is a single
|
`docs/SKILL-USE-BASELINE.md` reports Trigger as `not_observable` on the
|
||||||
`container_exec::exec` returning a string. Same settings document, written
|
container tier because there was no tool evidence. There is now.
|
||||||
into a container instead of a VM.
|
`mission_events` carries `tool.call` and `file.touch` per phase, so
|
||||||
|
Trigger can be scored from behaviour instead of prose — which is what the
|
||||||
|
paper actually measures. This is the single highest-value follow-up: it
|
||||||
|
turns the baseline from "the first honest number" into a real one.
|
||||||
|
|
||||||
3. **Deploy the door we already built** (`docs/TOOL-CALL-ARCHITECTURE.md` §3).
|
3. **Give the microVM tier the same treatment, or retire the difference.** It
|
||||||
Create `/zeroclaw-data/clawmates-mcp.json`, add a door-shaped provider alias,
|
has the gate and a tap already, but by a different route (`vm_tool_tap`
|
||||||
bind mission claws to it. This is config, not code — the provider feature is
|
installs into the guest, `microvm_executor` drains inside the turn). Two
|
||||||
ours and shipped. It would also make `clawmates_skills` genuinely reachable,
|
mechanisms for one job is how they drift. Worth folding onto
|
||||||
which is the precondition for moving skills from inlined bodies to
|
`container_tool_hooks` once the fleet is back — tank and morpheus have been
|
||||||
progressive disclosure and making Skill-Use **Trigger** measurable the way
|
offline for over a week, so the microVM tier cannot be tested at all today.
|
||||||
the paper defines it.
|
|
||||||
|
|
||||||
4. **Pull upstream's egress policy** — `0db7d999a feat(plugins): add shared
|
4. **Deploy the door** (`docs/TOOL-CALL-ARCHITECTURE.md` §3). Config, not code:
|
||||||
egress policy foundation (#9137)`. We are 218 commits behind; this is the one
|
`/zeroclaw-data/clawmates-mcp.json` plus a door-shaped provider alias. Now
|
||||||
item identified as worth taking, and it is defence for a problem we have not
|
less urgent than it looked — the gate no longer depends on it — but it is
|
||||||
solved.
|
still the precondition for `clawmates_skills` being reachable, and therefore
|
||||||
|
for skills moving from inlined bodies to progressive disclosure.
|
||||||
|
|
||||||
|
5. **Pull upstream's egress policy** — `0db7d999a feat(plugins): add shared
|
||||||
|
egress policy foundation (#9137)`. We are ~220 commits behind; this is the
|
||||||
|
one item identified as worth taking, and it is defence for a problem we have
|
||||||
|
not solved.
|
||||||
|
|
||||||
|
**Dropped from this list:** "give the direct-session tier a tap". That tier is
|
||||||
|
dormant — `CLAWMATES_MISSION_EXECUTOR` is unset in production, so it never
|
||||||
|
runs. Checking that before building for it saved the work.
|
||||||
|
|
||||||
## Open decisions that are yours
|
## Open decisions that are yours
|
||||||
|
|
||||||
@@ -126,6 +148,22 @@ services actually pushed — the failing runs had pushed `server`, aborted on
|
|||||||
- **Graph memory / `clawhdf5-agent`** — in the workspace manifest, used by no
|
- **Graph memory / `clawhdf5-agent`** — in the workspace manifest, used by no
|
||||||
crate. Measure against a baseline before migrating.
|
crate. Measure against a baseline before migrating.
|
||||||
|
|
||||||
|
## Operational facts that cost time to learn
|
||||||
|
|
||||||
|
- The Gitea **actions-log API returns 403** for the token in
|
||||||
|
`deploy/compose/.env`. Every CI failure this week was debugged blind because
|
||||||
|
of it. Steps now write to `/tmp/ci-logs` on the runner host as a workaround;
|
||||||
|
**a token with the `actions` scope** remains the highest-value thing to
|
||||||
|
obtain.
|
||||||
|
- **gw-04 uses legacy `docker-compose`**, not the v2 plugin. `docker compose`
|
||||||
|
fails there.
|
||||||
|
- **Do not build images by hand on gw-04 while CI may run** — same 150G volume,
|
||||||
|
and the frontend image build is what loses.
|
||||||
|
- The server reaches Docker through a **socket proxy** (`DOCKER_HOST`). Use
|
||||||
|
`container_exec::connect()`, never `connect_with_local_defaults()`.
|
||||||
|
- Prod auth is **Clerk**; the bootstrap password in `deploy/compose/.env` works
|
||||||
|
only against the local stack.
|
||||||
|
|
||||||
## Two corrections made this session, worth remembering
|
## Two corrections made this session, worth remembering
|
||||||
|
|
||||||
- **"Missions can't call tools at all" was wrong.** They call `Bash` and `Write`
|
- **"Missions can't call tools at all" was wrong.** They call `Bash` and `Write`
|
||||||
|
|||||||
@@ -118,9 +118,61 @@ We are **218 commits behind** `upstream/master`. Scanning for anything relevant:
|
|||||||
worth pulling: it is defence for the egress problem we have not solved, and
|
worth pulling: it is defence for the egress problem we have not solved, and
|
||||||
it is hardening we would otherwise write ourselves.
|
it is hardening we would otherwise write ourselves.
|
||||||
|
|
||||||
## What to do, cheapest first
|
## RESOLVED 2026-08-21 — hooks, not the transport
|
||||||
|
|
||||||
### 1. Observability — switch `claude_cli` to `stream-json` *(small, proven)*
|
Everything above the line is still an accurate account of how mission tools
|
||||||
|
work. The recommendation that followed it was wrong, and this is what actually
|
||||||
|
happened.
|
||||||
|
|
||||||
|
### `stream-json` was necessary-looking and useless
|
||||||
|
|
||||||
|
Shipped, deployed, and driven with a real mission: **zero `tool.call` events**,
|
||||||
|
with the parser working perfectly. `TurnEvent::ToolCall` — the thing the
|
||||||
|
gateway turns into a frame — is emitted from `tool_execution.rs`, i.e. only for
|
||||||
|
tools **ZeroClaw itself executes**. Claude Code runs its tools inside its own
|
||||||
|
subprocess, so the event never fires. A provider that *knows* about the calls
|
||||||
|
changes nothing.
|
||||||
|
|
||||||
|
Worse, v1 was harmful: it returned the observed calls as
|
||||||
|
`ChatResponse.tool_calls`, meaning "execute these", so the loop tried to run
|
||||||
|
`Bash`, got `Unknown tool: Bash`, and fed that back to the model — an extra LLM
|
||||||
|
round-trip per call, telling the agent its own work failed. The mission
|
||||||
|
completed correctly anyway, which is why it would have gone unnoticed. Fixed in
|
||||||
|
fork `295d6d3b8` (log them, never return them); the corrected image
|
||||||
|
`streamjson2` was built and **deliberately not deployed**, since it delivers
|
||||||
|
nothing on its own.
|
||||||
|
|
||||||
|
### What worked: hooks
|
||||||
|
|
||||||
|
`claude -p --settings <doc>` honours `PreToolUse` and `PostToolUse`, so the gate
|
||||||
|
blocks and the tap records **without ZeroClaw being involved at all**. That
|
||||||
|
sidesteps the entire `TurnEvent` problem instead of trying to solve it.
|
||||||
|
|
||||||
|
Shipped, and verified end to end on a real mission:
|
||||||
|
|
||||||
|
```
|
||||||
|
tool.call 10 Bash 6, Read 3, Write 1
|
||||||
|
file.touch 4 research/tapproof.md
|
||||||
|
```
|
||||||
|
|
||||||
|
- `--settings` on `claude_cli` — fork `be9c34b1c`
|
||||||
|
- `container_tool_hooks` writes both hook scripts and one settings document
|
||||||
|
into the mission container
|
||||||
|
- `set_claude_cli_settings` points the provider at it
|
||||||
|
- `phase_runner::drain_finished_container_phases` collects the tap into
|
||||||
|
`mission_events`, idempotent by truncation
|
||||||
|
|
||||||
|
### The lesson worth keeping
|
||||||
|
|
||||||
|
Two of the three ranked recommendations below were written from reading the
|
||||||
|
code, and the first one was confidently wrong in a way that took a deployment
|
||||||
|
and a live mission to discover. The gate (§2) was right. The transport fix (§1)
|
||||||
|
was not — the evidence for it was an absence, and an absence had another
|
||||||
|
explanation.
|
||||||
|
|
||||||
|
## The original recommendations, kept for the record
|
||||||
|
|
||||||
|
### 1. ~~Observability — switch `claude_cli` to `stream-json`~~ *(WRONG — see above)*
|
||||||
|
|
||||||
In our fork: `--output-format stream-json --verbose`, parse `tool_use` /
|
In our fork: `--output-format stream-json --verbose`, parse `tool_use` /
|
||||||
`tool_result` blocks into `ChatResponse.tool_calls` instead of `Vec::new()`.
|
`tool_result` blocks into `ChatResponse.tool_calls` instead of `Vec::new()`.
|
||||||
|
|||||||
Reference in New Issue
Block a user