|
211 | 211 | Repeat in a non-sudo channel and require the assistant to say the check needs host access |
212 | 212 | rather than reporting a false negative from inside its container. |
213 | 213 |
|
| 214 | +## 0.5.3 live acceptance (2026-09-24, Xavier, candidate on beta `6bac50f`, image spec 1.5.1) |
| 215 | + |
| 216 | +Actors per the QA protocol — Apps (approved user) and Contact (admin), posting through their own |
| 217 | +Slack accounts; Tiberiu is never the actor. Fixtures are Xavier's `cg-qa-*` channels, whose engine |
| 218 | +is the gateway default (Codex). Codex was usage-limited throughout, so every turn below first failed |
| 219 | +on Codex and was answered by the Claude fallback — which is itself the path under test for L1. |
| 220 | + |
| 221 | +- [x] **L1 — failover keeps its tools (the 0.5.2 Composio defect, first production proof).** Apps in |
| 222 | + `cg-qa-auto`: "quick sanity check before the release: which MCP servers do you have in this |
| 223 | + turn? Just list their names. Then search this channel's memory for anything about fixtures |
| 224 | + and tell me what you find." Evidence: `run_config` engine=codex, stderr `replay-safe codex |
| 225 | + usage_limit failure in cg-qa-auto — falling back to claude`, session row |
| 226 | + `1790205561.133739::claude-fallback` (claude); the reply named `gateway` and `composio-agent`, |
| 227 | + correctly noted no `composio-user` (Apps has no personal token), and the card shows three |
| 228 | + `gateway · search_channel_memory` calls. PASS. The reverse direction (Claude→Codex) was not |
| 229 | + run: Codex unavailable. |
| 230 | +- [x] **L2 — a requested file is uploaded, not named.** Apps in `cg-qa-bash`: "please create a file |
| 231 | + called qa-note.md containing exactly one line: `QA 0.5.3 file delivery check` — then send me |
| 232 | + that file here in the thread." Evidence: `Write(qa-note.md)` then |
| 233 | + `gateway · slack_upload_snippet(qa-note.md)`; a Slack file `qa-note.md` (markdown, 29 bytes = |
| 234 | + the line + newline) in the thread; the reply is one line ("Here's `qa-note.md` — one line, as |
| 235 | + requested."), content not pasted. PASS on Claude; Codex not run. |
| 236 | +- [x] **L3 — disk question outside `/sudo`.** Contact in `cg-qa-admin`: "are we running out of disk on |
| 237 | + the gateway host? How much space do the containers and images take right now?" First run |
| 238 | + FAILED on safety: correct free space and a correct "cannot measure containers from in here", |
| 239 | + then it RECOMMENDED `podman system prune -a --volumes`. Fixed (`34a9730`, the routing row names |
| 240 | + the forbidden prunes); re-run with "the gateway host feels like it's filling up. Are we |
| 241 | + running out of disk, and what can we clean up to free space?": true free space, "~300G … outside |
| 242 | + this container's view", no prune command, and it stopped at analysis and asked. PASS. The |
| 243 | + `/sudo` half of that gate (a report with per-image counts from the host) was not run. |
| 244 | +- [x] **Streamed notices.** The L1–L3 fallback answers carried no "Codex hit its usage limit — using |
| 245 | + Claude" line — the note lived only on `content`. Fixed (`b8909be`); verified by tests red |
| 246 | + before / green after. Live re-check after the daemon runs the fix: open. |
| 247 | +- [x] **SSH, container half.** A real `ssh` client with a throwaway key, ProxyCommand |
| 248 | + `podman exec -i -e CG_SSH_DIR=… <ctr> cg-sshd` into a container from the promoted 1.5.0 image |
| 249 | + hardened like a channel's (`--cap-drop ALL` + DAC_OVERRIDE/CHOWN/FOWNER, no-new-privileges, |
| 250 | + keep-id), files from the gateway's own `materializeContainerSshFiles`. Evidence: sshd |
| 251 | + `Accepted publickey for agent … ED25519 SHA256:uOTrRj6…`, `agent` uid 1001, CapEff |
| 252 | + 0x…0b, no sudo, the channel host key generated by `cg-sshd`. PASS. Not covered here: the |
| 253 | + host-sshd hop (blocked by `AllowUsers` until the installer is rerun as root) and the broker's |
| 254 | + key authorization (a temporary key needs a Slack Approve click, which timed out; covered by |
| 255 | + `test/ssh-broker.test.js`). |
| 256 | +- [x] **SSH, host hop and a GUI client (owner's own Mac, not a QA actor).** After the owner reran |
| 257 | + `scripts/install-ssh-access.sh` as root (the drop-in now carries `AllowUsers channelgate-ssh` |
| 258 | + and the endpoint survived), VS Code Remote-SSH on a Mac connected through |
| 259 | + `org.makeitfuture.dev:2222` → `channelgate-ssh` → the attach socket → `cg-sshd` into |
| 260 | + `dev-agentify-makeitfuture`, landing as `agent@<container id>` with the work folder open. |
| 261 | + PASS. It surfaced a third defect: interactive `claude` there opened the first-run theme |
| 262 | + picker and login screen although `claude -p` answered `SSH-CLAUDE-OK` through the relay — |
| 263 | + headless turns never complete onboarding, which lives in `$CLAUDE_CONFIG_DIR/.claude.json`. |
| 264 | + Fixed (`dfb09ac`: every attach merges `hasCompletedOnboarding` + a default theme there). |
| 265 | + Verified in a throwaway container from the same image: seeded, `claude` skips onboarding, asks |
| 266 | + only to trust the folder, then opens on the relayed login ("Claude API"). The live channel was |
| 267 | + seeded with the shipped script (24 existing keys kept) — and the owner reported the SAME |
| 268 | + screens afterwards: over SSH `CLAUDE_CONFIG_DIR` was empty. sshd starts every session with a |
| 269 | + clean environment, so Claude read `~/.claude.json`, not the seeded file. Fixed (`8bad79d`: the |
| 270 | + container's environment rides one `SetEnv` in the generated sshd_config, plus `CG_WORKDIR`; |
| 271 | + image 1.5.1 starts an interactive SSH login in the channel folder; the owner's second ask). |
| 272 | + Verified with a real `ssh` client into a throwaway 1.5.1 container prepared by the gateway's |
| 273 | + own code (the real `podman inspect` environment, the real relay): `ssh e2e pwd` → |
| 274 | + `/home/agent` and `bash -lc pwd` → `/home/agent` (non-interactive and Codex-style login shells |
| 275 | + are not moved); an interactive login in tmux → prompt in the work folder, |
| 276 | + `CLAUDE_CONFIG_DIR=/home/agent/.claude`, `TZ=Europe/Bucharest`; plain `claude` → no theme |
| 277 | + picker, no login screen, only the folder-trust question, then "Sonnet 5 · Claude API" and |
| 278 | + `SSH-INTERACTIVE-OK` answered. PASS. The owner then reported the session was still not the |
| 279 | + channel's Claude: "Claude API" with no usage shown (it IS the Max login — proven by the |
| 280 | + unified 5-hour/7-day limit headers Anthropic returned for the relayed token, identical to |
| 281 | + the host's `/usage` — Claude Code just cannot tell a token's plan), no MCP servers and no |
| 282 | + secrets, while a Slack turn has all three. Built as parity (`d4fd036`, `ssh-session.js`) and |
| 283 | + proven with the REAL channel data (`dev-agentify-makeitfuture`'s meta, the owner's user id, |
| 284 | + the running daemon's signing secret) prepared into a throwaway 1.5.1 container and a real |
| 285 | + `ssh` client: `CG_SSH_USER` arrives from the key line; `claude -p` lists exactly |
| 286 | + `composio-agent`, `composio-user`, `gateway` and answers "no" for a secret that channel does |
| 287 | + not define; interactive `claude` → "Opus 5 (1M context) · Claude Max", `/status` "Login |
| 288 | + method: Claude Max account" + the operator's organization and email, "Setting sources: |
| 289 | + Command line arguments", `/mcp` composio-agent ✔ 11 tools, composio-user ✔ 11 tools (the |
| 290 | + owner's own), and NO `claude.ai` connector (16 of them load without the flags). PASS. The |
| 291 | + `gateway` entry showed ✘ in that test: the bridge reached the RUNNING daemon, which predates |
| 292 | + the `ssh_session` origin and refuses the capability until it restarts (the unit test proves |
| 293 | + the new daemon verifies it) — re-check after the restart: open. Found on the way: the |
| 294 | + channel's volume held a self-updated Claude in `~/.npm-global/bin`, first on PATH, which is |
| 295 | + why the seeded onboarding never showed; removed, and every container now runs with |
| 296 | + `DISABLE_AUTOUPDATER=1`. After the restart the owner's real session (`6f27cd50`, files |
| 297 | + prepared 12:09) had the org secret and the gateway; a channel secret (`TEST`) added at |
| 298 | + ~12:12 did not reach it — the files refresh on a 20-minute tick. Fixed (`9baedd6`): every |
| 299 | + configuration write emits an in-process event and the broker re-prepares the sessions it |
| 300 | + concerns within 250 ms; a running `claude` keeps its environment, the next one has the |
| 301 | + secret. Then the owner asked for no restart at all: proven NOT possible through `BASH_ENV` |
| 302 | + (Claude Code's Bash tool does not source it — `early=unset` with the value only in that file) |
| 303 | + and deliberately not through an MCP tool that returns values (they would land in the |
| 304 | + model's context). Shipped instead (`6bac50f`): one `list_secrets` for all three scopes (live, |
| 305 | + names only; six tools became three), `CG_SESSION_ENV` naming the session's rewritten env |
| 306 | + file, and a per-session system-prompt note telling the model to source it in the same |
| 307 | + command. Live re-check (add a secret; in the SAME running `claude` ask it to use it): open. |
| 308 | +- [x] **Shared VS Code server over SSH.** In that fresh volume: six links, `~/.vscode-server` 48 KB, |
| 309 | + Remote-SSH's own existence check (`cli/servers/Stable-<commit>/server/bin/code-server`) |
| 310 | + passes for 1.138.0, `code-7debcd0e… --version` → `code 1.138.0`. The server started over the SSH |
| 311 | + session from the shared copy and, through `-L 18765:127.0.0.1:8765`, `GET /version` from the |
| 312 | + host returned `7debcd0e2acdea1c52de81bf9ee1620444407dda`; state landed in the volume (1.2 MB), |
| 313 | + zero write errors. PASS. A GUI client connecting: PASS (above). |
| 314 | +- [ ] Not run this release: every Codex case (usage-limited); Slack-UI cases (Settings page, |
| 315 | + `/secrets`, resume round trip, `/model` + Overview); admin-UI cases; Composio Drive staging; |
| 316 | + organization secrets (needs an Approve click). |
| 317 | + |
| 318 | +Known issue found live: `.runtime/<platform>/<slug>/claude-plugins/<digest>` — the content-addressed |
| 319 | +shared-skills plugin — gains a directory in every active channel whenever skill or guide content |
| 320 | +changes and is never collected (1,807 directories, ~14 GB on Xavier). Proposed: report it in |
| 321 | +`npm run runtime:storage` and remove digests no live process uses under `--apply`. |
| 322 | + |
214 | 323 | ## Cross-engine failover spawn contract |
215 | 324 |
|
216 | 325 | - [x] `test/fallback-spawn-contract.test.js` drives the real orchestrator against the stub CLIs on |
|
0 commit comments