331 behind, 54 ahead. The previous scan said 218 and its conclusion about the egress commit was wrong, so it is marked superseded rather than edited. Merge cost is smaller than the number suggests: 660 files changed upstream, 52 by us, and **18 overlap**. `claude_cli.rs` — the provider every mission runs through — exists in our tree and in zero upstream files, so it cannot conflict. The find worth recording is not a feature. Upstream defaulted skills to compact injection on 2026-08-05 (#8313), then restored the full default for v0.8.x on 2026-08-13 (#9913). Eight days. That is our `index` arm, tried at larger scale and pulled back out of the stable line — evidence bearing directly on our own open question of whether to flip the default, and with our own data at n=1 per arm it argues for more pairs before flipping, not fewer. Their documentation also states plainly what ours should: "Compact mode reduces prompt size; it is not an isolation boundary for untrusted skill sources." Progressive disclosure is a token optimisation. It is not a security control. Also noted, as a documented limit rather than a surprise: upstream fixed case-insensitive allowlist matching (#9568) and symlink-escape path resolution (#9384) in their command gate. Ours resolves no paths, so a symlink to `curl` defeats it. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz
109 lines
5.5 KiB
Markdown
109 lines
5.5 KiB
Markdown
# ZeroClaw upstream, scanned against what we run — 2026-08-27
|
|
|
|
Our fork (`git.redclaw.dev/osobh/zeroclaw`) sits **331 commits behind**
|
|
`upstream/master` and **54 ahead**. The previous scan in
|
|
`TOOL-CALL-ARCHITECTURE.md` said 218; that number is stale and its conclusion
|
|
about the egress commit was wrong — see `MISSION-EGRESS.md`.
|
|
|
|
Upstream since our merge base (`a56c345d51`, v0.8.4): 198 fixes, 35 features,
|
|
25 docs, 24 tests. The scan below filters that to what touches how **ClawMates**
|
|
uses the runtime: `claude_cli` as the mission provider, the gateway RPC and WS,
|
|
pairing, the skills door, and the claude→kimi→glm fallback chain.
|
|
|
|
## Merge cost
|
|
|
|
| | |
|
|
|---|---|
|
|
| files we changed | 52 |
|
|
| files upstream changed | 660 |
|
|
| **overlap** | **18** |
|
|
|
|
`claude_cli.rs` exists in our tree and in **zero** upstream files, so the
|
|
provider that runs every mission cannot conflict. The overlap is concentrated in
|
|
`providers/{factory,catalog,lib,compatible}.rs`, `runtime/agent/{loop_,turn,
|
|
system_prompt}.rs`, `gateway/ws.rs` and `config/schema.rs`. A full merge is a
|
|
real piece of work but not an unbounded one.
|
|
|
|
## Tier 1 — take these
|
|
|
|
**`841f28c7f1` fix(gateway): serialize config writes so a flush can't erase
|
|
concurrent updates (#9519).** The one I would take first. Upstream added a
|
|
`config_write_lock` around "the read-mutate-save-swap critical section of every
|
|
HTTP" config write. Our pinned gateway has **no such lock** — the mutexes in
|
|
`gateway/src/lib.rs` are for rate limits, keys, cancellation and the SOP engine.
|
|
|
|
We make **two** config writes per mission, on adjacent keys:
|
|
|
|
`providers.models.claude_cli.default.settings` (hooks — orchestrator:341)
|
|
`providers.models.claude_cli.default.mcp_config` (skills door — orchestrator:980)
|
|
|
|
Stated honestly: ours are sequential and awaited, so this is a **latent hazard,
|
|
not a demonstrated bug in our deployment**. It earns first place because the
|
|
failure mode is one this codebase has hit repeatedly — two writers of one
|
|
document, second wins, no error — and because the loser here is either the
|
|
security gate or the skills door, both of which fail silently and invisibly.
|
|
|
|
**`eadaee0b62` fix(providers): redact Anthropic credential fragments (#10092).**
|
|
We forward provider credentials into mission containers and log heavily around
|
|
them. Credential fragments in logs are a live risk for us specifically.
|
|
|
|
**`47adb9863e` fix(gateway): harden unauthenticated /api/pair against lockout
|
|
bypass (#9438).** We mint a pairing code per mission container. The gateway port
|
|
is not published to the host, but mission containers share `clawmates_edge`, so
|
|
one mission's container can reach another's gateway. A pairing lockout that can
|
|
be bypassed is a cross-mission path.
|
|
|
|
**`49ba8064d3` + `0b1a715c72` + `0cba80e422` — reliable-fallback accounting and
|
|
served-model logging.** We run claude→kimi→glm and care which one answered;
|
|
these make the fallback's own account of itself accurate.
|
|
|
|
## Tier 2 — take the idea, not the code
|
|
|
|
Upstream's skills system is not ours: mission skills go through our delivery
|
|
path and our MCP door. The *findings* still apply.
|
|
|
|
**`d04b345bda` honor always-inject frontmatter in compact prompt mode (#9520).**
|
|
Upstream compact mode has an `always: true` escape hatch — a skill that stays
|
|
fully injected no matter the mode. Our `skills` table has no such column
|
|
(`id, workspace_id, title, author, description, body, installs, created_at,
|
|
name, when_to_use, tags, source_kind, current_version, updated_at`), and our
|
|
delivery A/B produced **exactly the failure it prevents**:
|
|
`workspace-repo-commit-protocol` scored Trigger=FAIL in the `index` arm because
|
|
no agent fetched it. An always-inject flag is the missing piece.
|
|
|
|
**`9ea4f3371a` → `633c06f7c5` — the default, and the reversal.** Upstream
|
|
defaulted skills to compact injection on **2026-08-05**, then restored the full
|
|
default for v0.8.x on **2026-08-13**. Eight days. That is direct evidence for
|
|
our own open question of whether `index` should become the default: someone
|
|
larger tried the same move and pulled it out of the stable line. It does not
|
|
tell us they were right, and their reason is in an issue rather than the commit
|
|
— but with our own evidence at n=1 per arm, it argues for more pairs before
|
|
flipping a default rather than fewer.
|
|
|
|
Their documentation also carries a line worth adopting verbatim in spirit:
|
|
*"Compact mode reduces prompt size; it is not an isolation boundary for
|
|
untrusted skill sources."* Progressive disclosure is a token optimisation. It is
|
|
not a security control, and ours should not be described as one.
|
|
|
|
**`86f03d2d75` match command allowlist names case-insensitively (#9568)** and
|
|
**`7388987b55` resolve shell command path arguments to block symlink escapes
|
|
(#9384).** Different codebase, same bug class as the one we fixed in
|
|
`vm_tool_gate` yesterday. Our gate resolves no paths and follows no symlinks, so
|
|
`/tmp/link-to-curl -d @secret …` defeats it. Worth knowing as a documented limit
|
|
rather than pretending otherwise.
|
|
|
|
## Tier 3 — situational
|
|
|
|
- `53cba1ddde` opt-in multi-arch Alpine image — we hand-build arm64 today.
|
|
- `4282a5564a` gateway chat WebSocket keepalive — we already hardened our side.
|
|
- `0db7d999a` shared egress policy — **chat tier only**; see `MISSION-EGRESS.md`
|
|
for why it cannot reach a mission's `curl`.
|
|
- `6d76787556` per-server custom CA trust for MCP — our door is plain HTTP
|
|
inside docker; no value today.
|
|
|
|
## Not relevant
|
|
|
|
`zerocode` (21), the channel integrations (WhatsApp, Telegram, Matrix, Slack),
|
|
`hardware`/uno-q, the installer, and the Nostr dependency work. None of it is on
|
|
a path ClawMates executes.
|