[{"id":"10458366186","type":"PushEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"repository_id":1148615527,"push_id":32750643492,"ref":"refs/heads/fix/engine-v2-security-hardening","head":"c50d70af726106dbccf60404d05c5415647e3391","before":"2bdd5c058bc6ad5fe9d2e16fb86c6d772b2ef50e"},"public":true,"created_at":"2026-04-11T13:21:52Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310876379","type":"IssueCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","issue":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2211","repository_url":"https://api.github.com/repos/nearai/ironclaw","labels_url":"https://api.github.com/repos/nearai/ironclaw/issues/2211/labels{/name}","comments_url":"https://api.github.com/repos/nearai/ironclaw/issues/2211/comments","events_url":"https://api.github.com/repos/nearai/ironclaw/issues/2211/events","html_url":"https://github.com/nearai/ironclaw/pull/2211","id":4234063877,"node_id":"PR_kwDORHZ7Z87RN08P","number":2211,"title":"feat(engine-v2): per-project sandbox (Phases 1–7)","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":10248775942,"node_id":"LA_kwDORHZ7Z88AAAACYt_pBg","url":"https://api.github.com/repos/nearai/ironclaw/labels/size:%20XL","name":"size: XL","color":"B71C1C","default":false,"description":"500+ changed lines"},{"id":10248776001,"node_id":"LA_kwDORHZ7Z88AAAACYt_pQQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/risk:%20low","name":"risk: low","color":"4CAF50","default":false,"description":"Changes to docs, tests, or low-risk modules"},{"id":10248777318,"node_id":"LA_kwDORHZ7Z88AAAACYt_uZg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20docs","name":"scope: docs","color":"78909C","default":false,"description":"Documentation"},{"id":10248777350,"node_id":"LA_kwDORHZ7Z88AAAACYt_uhg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20dependencies","name":"scope: dependencies","color":"90A4AE","default":false,"description":"Dependency updates"},{"id":10248777536,"node_id":"LA_kwDORHZ7Z88AAAACYt_vQA","url":"https://api.github.com/repos/nearai/ironclaw/labels/contributor:%20core","name":"contributor: core","color":"FF8A65","default":false,"description":"20+ merged PRs"}],"state":"open","locked":false,"assignees":[],"milestone":null,"comments":4,"created_at":"2026-04-09T18:03:52Z","updated_at":"2026-04-11T08:47:29Z","closed_at":null,"assignee":null,"type":null,"active_lock_reason":null,"draft":false,"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","html_url":"https://github.com/nearai/ironclaw/pull/2211","diff_url":"https://github.com/nearai/ironclaw/pull/2211.diff","patch_url":"https://github.com/nearai/ironclaw/pull/2211.patch","merged_at":null},"body":"## Summary\n\nImplements the complete per-project sandbox for engine v2 — from the mount-backend abstraction through to a live Docker e2e test that proves the full stack works.\n\nWhen `ENGINE_V2_SANDBOX=true`, the five filesystem/shell tools (`file_read`/`read_file`, `file_write`/`write_file`, `list_dir`, `apply_patch`, `shell`) route through a per-project Docker container instead of the host filesystem. The host directory at `~/.ironclaw/projects/<project_id>/` is bind-mounted at `/project/` inside the container. A `sandbox_daemon` binary speaks NDJSON over `docker exec -i`. When unset, the same code path uses a host-filesystem `MountBackend` — behavior is unchanged.\n\n### What's in each phase\n\n| Phase | What |\n|---|---|\n| **1** (prior commit) | `MountBackend` trait, `FilesystemBackend`, `WorkspaceMounts` registry, bridge interceptor |\n| **2** Project workspace folder | `Project.workspace_path` field, `ensure_project_workspace_dir()` (0700), `FilesystemMountFactory` |\n| **3** Daemon binary | `src/bin/sandbox_daemon.rs` — NDJSON loop, 5 tools with `base_dir=/project` |\n| **4** Dockerfile | Multi-stage: rust builder + debian-slim runtime with tini PID 1 |\n| **5** Container backend + manager | `ContainerizedFilesystemBackend`, `DockerTransport` (bollard exec), `ProjectSandboxManager`, lifecycle |\n| **6** Router gating | `ENGINE_V2_SANDBOX` env var → `ContainerizedMountFactory` when Docker reachable; filesystem fallback otherwise |\n| **7** Polish | Docs, regression tests for 3 bugs caught by live e2e, fmt/clippy clean |\n\n### Bugs caught by the live e2e test\n\n| Bug | Fix |\n|---|---|\n| Shell without explicit `workdir` escaped the sandbox (fell through to host) | Default to `/project/` in `extract_path_param` for shell |\n| `ContainerizedFilesystemBackend::shell` returned empty stdout (parsed `stdout` key but host ShellTool returns merged `output`) | Fall back to `output` key when `stdout` absent |\n| Interceptor only matched v2 names (`file_read`/`file_write`) but host registry uses v1 names (`read_file`/`write_file`) | Accept both alias sets in `SANDBOX_TOOL_NAMES` and dispatch match |\n\n### Test coverage (62 sandbox tests)\n\n- 27 bridge sandbox unit tests\n- 7 containerized-backend tests (inc. 2 regression tests for shell bugs)\n- 5 engine v2 sandbox integration tests (EffectBridgeAdapter e2e)\n- 5 daemon binary smoke tests (real subprocess + NDJSON I/O)\n- 17 engine workspace unit tests\n- 1 live Docker e2e: agent clones `nearai/ironclaw` into sandbox, renames to `megaclaw`, verifies with grep (70s, $0.09, trace committed)\n\n### How to test\n\n```bash\n# Unit + integration (no Docker needed)\ncargo test --lib bridge::sandbox\ncargo test --test sandbox_daemon_smoke\ncargo test --test engine_v2_sandbox_integration\ncargo test -p ironclaw_engine workspace::\n\n# Live Docker e2e (needs Docker + image + LLM keys)\ndocker build -f crates/Dockerfile.sandbox -t ironclaw/sandbox:dev .\nENGINE_V2_SANDBOX=true IRONCLAW_LIVE_TEST=1 \\\n  cargo test --features libsql --test sandbox_live_e2e -- --ignored --nocapture\n\n# Replay mode (needs Docker + image, no LLM keys)\nENGINE_V2_SANDBOX=true \\\n  cargo test --features libsql --test sandbox_live_e2e -- --ignored --nocapture\n```\n\n### Not in this PR (by design)\n\n- Idle-timeout reaper on `ProjectSandboxManager`\n- `ironclaw doctor` cleanup of stale containers\n- Per-project resource limit configuration UI\n- Crate extraction (`ironclaw_workspace`) — deferred to follow-up\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2211/reactions","total_count":1,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":1},"timeline_url":"https://api.github.com/repos/nearai/ironclaw/issues/2211/timeline","performed_via_github_app":null,"state_reason":null},"comment":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229133410","html_url":"https://github.com/nearai/ironclaw/pull/2211#issuecomment-4229133410","issue_url":"https://api.github.com/repos/nearai/ironclaw/issues/2211","id":4229133410,"node_id":"IC_kwDORHZ7Z878E3Ri","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2026-04-11T08:47:29Z","updated_at":"2026-04-11T08:47:29Z","body":"Addressing @standardtoaster's review points:\n\n**CodeAct environment confusion** — Valid concern. Monty (Python) runs on the host, shell runs in the container. We haven't seen the model get confused in testing yet, and per your follow-up advice, skipping the preamble fence for now. Will track if we see the behavior and add disambiguation instructions then.\n\n**Dynamic tool provisioning via MCP** — Interesting alternative architecture. Agree it would eliminate the NDJSON protocol duplication and the interceptor layer. The trade-off is startup latency (container must be running before tools appear) and the loss of `FilesystemBackend` fallback when sandbox is off. Worth exploring as a v2 of this — tracking as a follow-up discussion on #1894.\n\n**Silent stderr** — Fixed in 1c447b0d. `StreamReader::poll_read` now logs `StdErr` frames at `debug!` level instead of silently discarding them.\n\n**Intermediate directory permissions** — Fixed in 1c447b0d. `ensure_dir` now tracks which directories were newly created and tightens all of them to 0o700, not just the leaf. Added a test (`ensure_tightens_intermediate_dirs`) that verifies multi-level creation.\n\n**Stale module doc** — Fixed in 1c447b0d. Updated `sandbox/mod.rs` to reflect the current state (all phases shipped).\n\n**`apply_patch` path routing** — Fixed in 1c447b0d as part of the broader apply_patch schema fix. The `MountBackend::patch` signature now takes `(rel_path, old_string, new_string, replace_all)` matching `ApplyPatchTool`'s search/replace contract. The `path` param routes to the correct backend, and `old_string`/`new_string` are passed through — each patch call targets a single file, which matches how the agent actually uses the tool.","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229133410/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}},"public":true,"created_at":"2026-04-11T08:47:29Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310870376","type":"PullRequestReviewCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807067","pull_request_review_id":4093580122,"id":3067807067,"node_id":"PRRC_kwDORHZ7Z8622wVb","diff_hunk":"@@ -0,0 +1,353 @@\n+# Engine v2 Per-Project Sandbox\n+\n+## Context\n+\n+IronClaw engine v2 (Phase 8 of `docs/plans/2026-03-20-engine-v2-architecture.md`) needs sandboxed execution for tools that touch the filesystem or run shell commands. Today the engine bridge (`src/bridge/effect_adapter.rs:1007`) executes every tool directly on the host with no isolation, even though the underlying engine has a `CapabilityLease` + `EffectType` model that was designed to enable this.\n+\n+The dominant risk is **accidental filesystem damage from LLM mistakes** — `rm -rf`, overwriting files, traversing into `~/.ssh` or `.env`, runaway shell processes. The engine's existing `base_dir` path validation in `src/tools/builtin/path_utils.rs:78` is the only line of defense and it's optional.\n+\n+The intended outcome is a **persistent per-project workspace container** that:\n+- Bind-mounts the project's user-facing files at `/project/` so they remain on the host (visible, backupable)\n+- Owns its own writable filesystem layer where `cargo`, `pip`, `npm`, `apt`, downloaded artifacts, build caches and toolchains accumulate over time — turning the container into the project's \"workspace computer\"\n+- Hosts a thin tool-execution daemon that runs `file_*`, `shell`, `list_dir`, `apply_patch` against the mounted project, while everything else (memory, network, LLM, secrets, orchestrator, Monty) stays on the host\n+- Survives across IronClaw restarts (started/stopped, never `--rm` except on explicit user action) so the accumulated environment persists\n+\n+## Cross-reference: Issue nearai/ironclaw#1894 (Unified Workspace VFS)\n+\n+Issue #1894 proposes a unified mount-table abstraction where the agent sees one filesystem and paths route through `MountBackend` variants (`Filesystem`, `Database`, future `S3`, `GitRemote`, etc.). The sandbox in this plan is **the storage backend for the `/project/` mount when isolation is enabled**, not a separate feature toggle. Concretely:\n+\n+- `/project/` (the agent-facing path prefix) is owned by a `MountBackend`\n+- When sandbox is off: `MountBackend::Filesystem { root: ~/.ironclaw/projects/<id>/ }` — passthrough to host fs\n+- When sandbox is on: `MountBackend::ContainerizedFilesystem { project_id }` — JSON-RPC to per-project daemon\n+- The agent calls `file_read(\"/project/foo.txt\")` either way — the backend swap is invisible to the agent and to the orchestrator\n+\n+This plan introduces a **minimal subset of #1894's Phase 1** — just enough mount-backend machinery to make the sandbox a backend rather than a special case. The full mount table, `/memory/` routing, search unification, and tool renames from #1894 are independent and can land separately. Both efforts converge cleanly because the sandbox backend is designed to drop into #1894's full mount table without changes.\n+\n+What this plan does NOT do that #1894 does:\n+- Does NOT rename `file_read`/`file_write`/`list_dir`/`apply_patch` to `read_file`/`write_file`/`list` (#1894 Phase 3)\n+- Does NOT route `memory_*` through the workspace mount table (#1894 Phase 2)\n+- Does NOT add `search`, `expand_mount`, or dynamic mount expansion (#1894 Phase 5)\n+- Does NOT introduce `materialize()` / `sync_back()` for db-backed `/project/` mounts — the sandbox uses a real filesystem (bind mount) so no copy in/out\n+- Does NOT remove the orchestrator/container tool registration split — that disappears naturally because the daemon registers the same `Tool` trait impls as the host, not a parallel \"container-only\" tool surface\n+\n+What this plan DOES that #1894 will reuse:\n+- `MountBackend` trait + minimal `Workspace::resolve_mount(path) -> &dyn MountBackend` API\n+- `FilesystemBackend` (passthrough) and `ContainerizedFilesystemBackend` (JSON-RPC)\n+- `/project/` as the canonical path prefix for the user's project files\n+- The principle that backends swap transparently under unchanged tool surfaces\n+\n+## Locked design decisions\n+\n+1. **Granularity**: One container per `Project` (`crates/ironclaw_engine/src/types/project.rs`). All threads in the same project — including sub-threads spawned by `rlm_query()` which inherit `project_id` at `crates/ironclaw_engine/src/runtime/manager.rs:505` — share the container.\n+2. **Persistence**: Named container. `docker create` once, `docker start`/`stop` lifecycle, removed only on project deletion or explicit user reset. Container's own writable layer holds installed dependencies and accumulates over time.\n+3. **Mount**: `~/.ironclaw/projects/<project_id>/` (host) → `/project/` (container) bind mount, read-write. Project's user files live here.\n+4. **Daemon model**: Container PID 1 is a minimal init (`tini`). Tool-execution daemon is spawned via `docker exec -i sandbox_daemon` per IronClaw session. Daemon talks JSON-RPC over stdin/stdout to the host.\n+5. **What's inside**: Only `file_read`, `file_write`, `list_dir`, `apply_patch`, `shell`. Each instantiated with `base_dir=/project/` so existing path validation enforces sandboxing. cwd defaults to `/project/`.","path":"docs/plans/2026-04-10-engine-v2-sandbox.md","commit_id":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","original_commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Fixed in 1c447b0d — all occurrences in the plan doc now say `<user_id>/<project_id>/`.","created_at":"2026-04-11T08:47:01Z","updated_at":"2026-04-11T08:47:01Z","html_url":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067807067","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"self":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807067"},"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067807067"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807067/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"in_reply_to_id":3067678034,"original_position":45,"position":45,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}}},"public":true,"created_at":"2026-04-11T08:47:01Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310870375","type":"PullRequestReviewEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"review":{"id":4093580122,"node_id":"PRR_kwDORHZ7Z87z_xNa","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":null,"commit_id":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","state":"commented","html_url":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580122","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580122"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"submitted_at":"2026-04-11T08:47:01Z","updated_at":"2026-04-11T08:47:01Z"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}},"action":"created"},"public":true,"created_at":"2026-04-11T08:47:02Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310870208","type":"PullRequestReviewCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807046","pull_request_review_id":4093580107,"id":3067807046,"node_id":"PRRC_kwDORHZ7Z8622wVG","diff_hunk":"@@ -255,6 +255,20 @@ See `.claude/rules/tools.md` for the full pattern, allowed exemptions,\n and migration status. The dispatcher itself lives in\n `src/tools/dispatch.rs`.\n \n+## Engine v2 Per-Project Sandbox\n+\n+When `SANDBOX_ENABLED=true` (or `ENGINE_V2_SANDBOX=true`), engine v2 routes the five filesystem/shell tools\n+(`file_read`, `file_write`, `list_dir`, `apply_patch`, `shell`) for `/project/`\n+paths through a per-project Docker container instead of the host filesystem.\n+The host's directory at `~/.ironclaw/projects/<project_id>/` is bind-mounted at","path":"CLAUDE.md","commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","original_commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Fixed in 1c447b0d — updated to `~/.ironclaw/projects/<user_id>/<project_id>/`.","created_at":"2026-04-11T08:47:00Z","updated_at":"2026-04-11T08:47:00Z","html_url":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067807046","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"self":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807046"},"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067807046"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807046/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"in_reply_to_id":3067678031,"original_position":9,"position":1,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}}},"public":true,"created_at":"2026-04-11T08:47:00Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310870198","type":"PullRequestReviewEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"review":{"id":4093580107,"node_id":"PRR_kwDORHZ7Z87z_xNL","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":null,"commit_id":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","state":"commented","html_url":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580107","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580107"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"submitted_at":"2026-04-11T08:47:00Z","updated_at":"2026-04-11T08:47:00Z"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}},"action":"created"},"public":true,"created_at":"2026-04-11T08:47:00Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310869969","type":"PullRequestReviewCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807025","pull_request_review_id":4093580096,"id":3067807025,"node_id":"PRRC_kwDORHZ7Z8622wUx","diff_hunk":"@@ -0,0 +1,128 @@\n+//! Per-project host workspace directory resolution.\n+//!\n+//! Each engine v2 project gets a real directory on the host filesystem at\n+//! `~/.ironclaw/projects/<project_id>/`. That's the directory the user can\n+//! see, edit, and back up. It's also the bind-mount source for the","path":"src/bridge/sandbox/workspace_path.rs","commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","original_commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Fixed in 1c447b0d — updated the module doc to say `~/.ironclaw/projects/<user_id>/<project_id>/`, matching the actual implementation.","created_at":"2026-04-11T08:46:59Z","updated_at":"2026-04-11T08:46:59Z","html_url":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067807025","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"self":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807025"},"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067807025"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807025/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"in_reply_to_id":3067678027,"original_position":5,"position":1,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}}},"public":true,"created_at":"2026-04-11T08:46:59Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310869967","type":"PullRequestReviewEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"review":{"id":4093580096,"node_id":"PRR_kwDORHZ7Z87z_xNA","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":null,"commit_id":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","state":"commented","html_url":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580096","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580096"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"submitted_at":"2026-04-11T08:46:59Z","updated_at":"2026-04-11T08:46:59Z"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}},"action":"created"},"public":true,"created_at":"2026-04-11T08:46:59Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310869663","type":"PullRequestReviewCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807007","pull_request_review_id":4093580077,"id":3067807007,"node_id":"PRRC_kwDORHZ7Z8622wUf","diff_hunk":"@@ -0,0 +1,458 @@\n+//! [`ContainerizedFilesystemBackend`] — [`MountBackend`] for the per-project\n+//! sandbox container.\n+//!\n+//! Each backend instance owns an [`Arc<dyn SandboxTransport>`] (typically\n+//! [`super::docker_transport::DockerTransport`]) and serializes\n+//! filesystem/shell calls into JSON-RPC requests for the daemon running\n+//! inside the project's container.\n+//!\n+//! Path semantics are identical to [`ironclaw_engine::FilesystemBackend`]:\n+//! the backend receives **relative** paths (the bridge interceptor strips\n+//! the `/project/` prefix before calling). The daemon's tools are configured\n+//! with `base_dir = /project/`, so the relative path is re-anchored at\n+//! `/project/<rel>` inside the container.\n+\n+use std::collections::HashMap;\n+use std::path::{Path, PathBuf};\n+use std::sync::Arc;\n+\n+use async_trait::async_trait;\n+use ironclaw_engine::{DirEntry, EntryKind, MountBackend, MountError, ShellOutput};\n+use serde_json::Value;\n+use uuid::Uuid;\n+\n+use super::protocol::{Request, Response, RpcError};\n+use super::transport::SandboxTransport;\n+\n+/// All daemon paths live under `/project/` inside the container. The host\n+/// translates relative paths into absolute container paths here so the\n+/// daemon's `base_dir` validation accepts them.\n+const CONTAINER_PROJECT_ROOT: &str = \"/project\";\n+\n+/// [`MountBackend`] backed by a per-project sandbox container.\n+#[derive(Debug, Clone)]\n+pub struct ContainerizedFilesystemBackend {\n+    transport: Arc<dyn SandboxTransport>,\n+}\n+\n+impl ContainerizedFilesystemBackend {\n+    pub fn new(transport: Arc<dyn SandboxTransport>) -> Self {\n+        Self { transport }\n+    }\n+\n+    /// Build the absolute container path for a relative mount path.\n+    fn container_path(rel: &Path) -> String {\n+        let rel_str = rel.to_string_lossy();\n+        if rel_str.is_empty() {\n+            CONTAINER_PROJECT_ROOT.to_string()\n+        } else {\n+            format!(\n+                \"{CONTAINER_PROJECT_ROOT}/{}\",\n+                rel_str.trim_start_matches('/')\n+            )\n+        }\n+    }\n+\n+    /// Run an `execute_tool` JSON-RPC call and unwrap the standard\n+    /// `result.output` envelope, mapping daemon-side `error` payloads into\n+    /// the appropriate [`MountError`] variants.\n+    async fn run_tool(&self, tool: &str, input: Value) -> Result<Value, MountError> {\n+        let request = Request::execute_tool(Uuid::new_v4().to_string(), tool, input);\n+        let response = self.transport.dispatch(request).await?;\n+        unwrap_tool_response(tool, response)\n+    }\n+}\n+\n+fn unwrap_tool_response(tool: &str, response: Response) -> Result<Value, MountError> {\n+    if let Some(err) = response.error {\n+        return Err(map_rpc_error(tool, err));\n+    }\n+    let result = response.result.ok_or_else(|| MountError::Backend {\n+        reason: format!(\"daemon returned neither result nor error for {tool}\"),\n+    })?;\n+    let output = result\n+        .get(\"output\")\n+        .cloned()\n+        .unwrap_or_else(|| Value::Object(Default::default()));\n+    Ok(output)\n+}\n+\n+/// Map daemon RPC errors to [`MountError`] so the bridge surfaces them\n+/// consistently with the [`ironclaw_engine::FilesystemBackend`] equivalents.\n+fn map_rpc_error(tool: &str, err: RpcError) -> MountError {\n+    match err.code.as_str() {\n+        \"tool_error\" if err.message.contains(\"not found\") || err.message.contains(\"No such\") => {\n+            MountError::NotFound {\n+                path: err.message.clone(),\n+            }\n+        }\n+        \"tool_error\" if err.message.contains(\"permission denied\") => MountError::PermissionDenied {\n+            path: err.message.clone(),\n+        },\n+        \"tool_error\" => MountError::Tool {\n+            reason: format!(\"{tool}: {}\", err.message),\n+        },\n+        \"invalid_params\" => MountError::InvalidPath {\n+            path: String::new(),\n+            reason: err.message,\n+        },\n+        \"rate_limited\" => MountError::Tool {\n+            reason: format!(\"rate limited: {}\", err.message),\n+        },\n+        \"sandbox_error\" | \"backend\" => MountError::Backend {\n+            reason: format!(\"{tool}: {}\", err.message),\n+        },\n+        \"parse_error\" | \"unknown_method\" | \"unknown_tool\" => MountError::Backend {\n+            reason: format!(\"{tool}: protocol error: {}\", err.message),\n+        },\n+        _ => MountError::Backend {\n+            reason: format!(\"{tool}: {} ({})\", err.message, err.code),\n+        },\n+    }\n+}\n+\n+#[async_trait]\n+impl MountBackend for ContainerizedFilesystemBackend {\n+    async fn read(&self, rel_path: &Path) -> Result<Vec<u8>, MountError> {\n+        let path = Self::container_path(rel_path);\n+        let output = self\n+            .run_tool(\"file_read\", serde_json::json!({\"path\": path}))\n+            .await?;\n+        let content = output\n+            .get(\"content\")\n+            .and_then(|v| v.as_str())\n+            .ok_or_else(|| MountError::Backend {\n+                reason: \"file_read response missing content\".into(),\n+            })?;\n+        Ok(content.as_bytes().to_vec())\n+    }\n+\n+    async fn write(&self, rel_path: &Path, content: &[u8]) -> Result<(), MountError> {\n+        let path = Self::container_path(rel_path);\n+        let body = String::from_utf8_lossy(content).into_owned();\n+        self.run_tool(\n+            \"file_write\",\n+            serde_json::json!({\"path\": path, \"content\": body}),\n+        )\n+        .await?;\n+        Ok(())\n+    }\n+\n+    async fn list(&self, rel_path: &Path, depth: usize) -> Result<Vec<DirEntry>, MountError> {\n+        let path = Self::container_path(rel_path);\n+        let recursive = depth > 0;\n+        let output = self\n+            .run_tool(\n+                \"list_dir\",\n+                serde_json::json!({\n+                    \"path\": path,\n+                    \"recursive\": recursive,\n+                    \"max_depth\": depth.max(1),\n+                }),\n+            )\n+            .await?;\n+        let entries = output\n+            .get(\"entries\")\n+            .and_then(|v| v.as_array())\n+            .ok_or_else(|| MountError::Backend {\n+                reason: \"list_dir response missing entries\".into(),\n+            })?;\n+        // Daemon returns formatted strings like \"foo.txt (1.2K)\" or \"sub/\".\n+        // We parse them back into DirEntry minimally — the bridge interceptor\n+        // re-formats this on the way to the LLM, so we just need correct\n+        // path + kind. Sizes round-trip best-effort.\n+        let mut out = Vec::with_capacity(entries.len());\n+        for entry in entries {\n+            let raw = match entry.as_str() {\n+                Some(s) => s,\n+                None => continue,\n+            };\n+            let (path_part, kind) = if let Some(prefix) = raw.strip_suffix('/') {\n+                (prefix.to_string(), EntryKind::Directory)\n+            } else if let Some((name, _)) = raw.rsplit_once(\" (\") {\n+                (name.to_string(), EntryKind::File)\n+            } else {\n+                (raw.to_string(), EntryKind::File)\n+            };\n+            out.push(DirEntry {\n+                path: PathBuf::from(path_part),\n+                kind,\n+                size: None,\n+            });\n+        }\n+        Ok(out)\n+    }\n+\n+    async fn patch(&self, rel_path: &Path, diff: &str) -> Result<(), MountError> {\n+        let path = Self::container_path(rel_path);\n+        self.run_tool(\n+            \"apply_patch\",\n+            serde_json::json!({\"path\": path, \"patch\": diff}),\n+        )\n+        .await?;","path":"src/bridge/sandbox/containerized_backend.rs","commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","original_commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Fixed in 1c447b0d — same fix as the intercept side. `ContainerizedFilesystemBackend::patch` now sends `{path, old_string, new_string, replace_all}` matching the daemon's `ApplyPatchTool` schema.","created_at":"2026-04-11T08:46:57Z","updated_at":"2026-04-11T08:46:57Z","html_url":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067807007","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"self":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807007"},"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067807007"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067807007/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"in_reply_to_id":3067678019,"original_position":192,"position":1,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}}},"public":true,"created_at":"2026-04-11T08:46:57Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310869655","type":"PullRequestReviewEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"review":{"id":4093580077,"node_id":"PRR_kwDORHZ7Z87z_xMt","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":null,"commit_id":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","state":"commented","html_url":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580077","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580077"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"submitted_at":"2026-04-11T08:46:57Z","updated_at":"2026-04-11T08:46:57Z"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}},"action":"created"},"public":true,"created_at":"2026-04-11T08:46:58Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310869062","type":"PullRequestReviewCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067806974","pull_request_review_id":4093580049,"id":3067806974,"node_id":"PRRC_kwDORHZ7Z8622wT-","diff_hunk":"@@ -0,0 +1,561 @@\n+//! Tool dispatch interception for the per-project sandbox.\n+//!\n+//! See [`maybe_intercept`].\n+\n+use std::collections::HashMap;\n+use std::path::Path;\n+\n+use ironclaw_engine::{MountError, ProjectId, WorkspaceMounts};\n+use serde_json::Value;\n+\n+/// Tool names that the sandbox **may** handle when their path argument\n+/// resolves into a workspace mount.\n+///\n+/// Used by [`maybe_intercept`] and by `EffectBridgeAdapter` to advertise\n+/// the sandbox-eligible tool surface. Keep in sync with the daemon's\n+/// registered tool list (see `src/bin/sandbox_daemon.rs`).\n+/// Includes both engine-v2 names (`file_read`/`file_write`) and the host's\n+/// actual v1 tool registry names (`read_file`/`write_file`) so the\n+/// interceptor catches calls regardless of which alias the agent uses. The\n+/// daemon also accepts both on the container side, keeping the pair fully\n+/// symmetric.\n+pub const SANDBOX_TOOL_NAMES: &[&str] = &[\n+    \"file_read\",\n+    \"file_write\",\n+    \"read_file\",\n+    \"write_file\",\n+    \"list_dir\",\n+    \"apply_patch\",\n+    \"shell\",\n+];\n+\n+/// Outcome of a sandbox interception attempt.\n+///\n+/// `Handled` means the call was dispatched through a mount backend and the\n+/// included `String` is the JSON-pretty-serialized result, ready to slot\n+/// into the existing post-`execute_tool_with_safety` pipeline (sanitization,\n+/// `wrap_for_llm`, `ActionResult` construction).\n+///\n+/// `FellThrough` means the call was not eligible for sandbox dispatch and\n+/// the caller should run normal host-side tool execution. Reasons include:\n+/// no mount table configured, action name outside the sandbox set, no\n+/// recognizable path in params, path doesn't resolve to any mount, or the\n+/// matched backend returned [`MountError::Unsupported`].\n+#[derive(Debug)]\n+pub enum InterceptOutcome {\n+    Handled(String),\n+    FellThrough,\n+}\n+\n+/// Try to handle a tool call via the per-project sandbox mount table.\n+///\n+/// Returns:\n+/// - `Ok(Handled(json))` — sandbox handled the call; `json` is the\n+///   pretty-serialized JSON tool output, matching what\n+///   `execute_tool_with_safety` would have returned.\n+/// - `Ok(FellThrough)` — sandbox declined; caller should run host execution.\n+/// - `Err(MountError)` — backend reported a real failure (NotFound,\n+///   InvalidPath, PermissionDenied, Tool, Backend). The caller converts\n+///   this into the appropriate engine error.\n+///\n+/// `Unsupported` errors from the backend are converted to `FellThrough` so\n+/// the bridge falls back to host execution gracefully — that's how the\n+/// `FilesystemBackend` Phase 1 stubs for `apply_patch` and `shell` keep\n+/// working without breaking the agent.\n+pub async fn maybe_intercept(\n+    action_name: &str,\n+    parameters: &Value,\n+    project_id: ProjectId,\n+    mounts: &WorkspaceMounts,\n+) -> Result<InterceptOutcome, MountError> {\n+    if !SANDBOX_TOOL_NAMES.contains(&action_name) {\n+        return Ok(InterceptOutcome::FellThrough);\n+    }\n+\n+    let Some(path_str) = extract_path_param(action_name, parameters) else {\n+        return Ok(InterceptOutcome::FellThrough);\n+    };\n+    if !is_mountable_path(&path_str) {\n+        return Ok(InterceptOutcome::FellThrough);\n+    }\n+\n+    let Some((backend, rel_path)) = mounts.resolve(project_id, &path_str).await? else {\n+        return Ok(InterceptOutcome::FellThrough);\n+    };\n+\n+    let result = match action_name {\n+        \"file_read\" | \"read_file\" => match backend.read(&rel_path).await {\n+            Ok(bytes) => {\n+                let content = String::from_utf8_lossy(&bytes).into_owned();\n+                serde_json::json!({\n+                    \"path\": path_str,\n+                    \"content\": content,\n+                    \"size\": bytes.len(),\n+                })\n+            }\n+            Err(MountError::Unsupported { .. }) => return Ok(InterceptOutcome::FellThrough),\n+            Err(e) => return Err(e),\n+        },\n+        \"file_write\" | \"write_file\" => {\n+            let content = parameters\n+                .get(\"content\")\n+                .and_then(|v| v.as_str())\n+                .unwrap_or(\"\");\n+            match backend.write(&rel_path, content.as_bytes()).await {\n+                Ok(()) => serde_json::json!({\n+                    \"path\": path_str,\n+                    \"bytes_written\": content.len(),\n+                    \"success\": true,\n+                }),\n+                Err(MountError::Unsupported { .. }) => return Ok(InterceptOutcome::FellThrough),\n+                Err(e) => return Err(e),\n+            }\n+        }\n+        \"list_dir\" => {\n+            let depth = parameters\n+                .get(\"depth\")\n+                .and_then(|v| v.as_u64())\n+                .unwrap_or(0) as usize;\n+            match backend.list(&rel_path, depth).await {\n+                Ok(entries) => {\n+                    let entry_strings: Vec<String> = entries\n+                        .iter()\n+                        .map(|e| {\n+                            let suffix = match e.kind {\n+                                ironclaw_engine::EntryKind::Directory => \"/\",\n+                                _ => \"\",\n+                            };\n+                            format!(\"{}{}\", e.path.display(), suffix)\n+                        })\n+                        .collect();\n+                    serde_json::json!({\n+                        \"path\": path_str,\n+                        \"entries\": entry_strings,\n+                        \"count\": entries.len(),\n+                        \"truncated\": false,\n+                    })\n+                }\n+                Err(MountError::Unsupported { .. }) => return Ok(InterceptOutcome::FellThrough),\n+                Err(e) => return Err(e),\n+            }\n+        }\n+        \"apply_patch\" => {\n+            let diff = parameters\n+                .get(\"diff\")\n+                .or_else(|| parameters.get(\"patch\"))\n+                .and_then(|v| v.as_str())\n+                .unwrap_or(\"\");\n+            match backend.patch(&rel_path, diff).await {\n+                Ok(()) => serde_json::json!({\n+                    \"path\": path_str,\n+                    \"success\": true,\n+                }),\n+                Err(MountError::Unsupported { .. }) => return Ok(InterceptOutcome::FellThrough),\n+                Err(e) => return Err(e),","path":"src/bridge/sandbox/intercept.rs","commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","original_commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Fixed in 1c447b0d — changed `MountBackend::patch` signature from `(rel_path, diff)` to `(rel_path, old_string, new_string, replace_all)`, matching `ApplyPatchTool`'s actual schema. The interceptor now extracts `old_string`/`new_string`/`replace_all` and the containerized backend sends the correct payload.","created_at":"2026-04-11T08:46:55Z","updated_at":"2026-04-11T08:46:55Z","html_url":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067806974","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"self":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067806974"},"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067806974"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067806974/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"in_reply_to_id":3067678014,"original_position":154,"position":1,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}}},"public":true,"created_at":"2026-04-11T08:46:55Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310869059","type":"PullRequestReviewEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"review":{"id":4093580049,"node_id":"PRR_kwDORHZ7Z87z_xMR","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":null,"commit_id":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","state":"commented","html_url":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580049","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580049"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"submitted_at":"2026-04-11T08:46:55Z","updated_at":"2026-04-11T08:46:55Z"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}},"action":"created"},"public":true,"created_at":"2026-04-11T08:46:55Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310868640","type":"PullRequestReviewCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067806889","pull_request_review_id":4093580018,"id":3067806889,"node_id":"PRRC_kwDORHZ7Z8622wSp","diff_hunk":"@@ -0,0 +1,561 @@\n+//! Tool dispatch interception for the per-project sandbox.\n+//!\n+//! See [`maybe_intercept`].\n+\n+use std::collections::HashMap;\n+use std::path::Path;\n+\n+use ironclaw_engine::{MountError, ProjectId, WorkspaceMounts};\n+use serde_json::Value;\n+\n+/// Tool names that the sandbox **may** handle when their path argument\n+/// resolves into a workspace mount.\n+///\n+/// Used by [`maybe_intercept`] and by `EffectBridgeAdapter` to advertise\n+/// the sandbox-eligible tool surface. Keep in sync with the daemon's\n+/// registered tool list (see `src/bin/sandbox_daemon.rs`).\n+/// Includes both engine-v2 names (`file_read`/`file_write`) and the host's\n+/// actual v1 tool registry names (`read_file`/`write_file`) so the\n+/// interceptor catches calls regardless of which alias the agent uses. The\n+/// daemon also accepts both on the container side, keeping the pair fully\n+/// symmetric.\n+pub const SANDBOX_TOOL_NAMES: &[&str] = &[\n+    \"file_read\",\n+    \"file_write\",\n+    \"read_file\",\n+    \"write_file\",\n+    \"list_dir\",\n+    \"apply_patch\",\n+    \"shell\",\n+];\n+\n+/// Outcome of a sandbox interception attempt.\n+///\n+/// `Handled` means the call was dispatched through a mount backend and the\n+/// included `String` is the JSON-pretty-serialized result, ready to slot\n+/// into the existing post-`execute_tool_with_safety` pipeline (sanitization,\n+/// `wrap_for_llm`, `ActionResult` construction).\n+///\n+/// `FellThrough` means the call was not eligible for sandbox dispatch and\n+/// the caller should run normal host-side tool execution. Reasons include:\n+/// no mount table configured, action name outside the sandbox set, no\n+/// recognizable path in params, path doesn't resolve to any mount, or the\n+/// matched backend returned [`MountError::Unsupported`].\n+#[derive(Debug)]\n+pub enum InterceptOutcome {\n+    Handled(String),\n+    FellThrough,\n+}\n+\n+/// Try to handle a tool call via the per-project sandbox mount table.\n+///\n+/// Returns:\n+/// - `Ok(Handled(json))` — sandbox handled the call; `json` is the\n+///   pretty-serialized JSON tool output, matching what\n+///   `execute_tool_with_safety` would have returned.\n+/// - `Ok(FellThrough)` — sandbox declined; caller should run host execution.\n+/// - `Err(MountError)` — backend reported a real failure (NotFound,\n+///   InvalidPath, PermissionDenied, Tool, Backend). The caller converts\n+///   this into the appropriate engine error.\n+///\n+/// `Unsupported` errors from the backend are converted to `FellThrough` so\n+/// the bridge falls back to host execution gracefully — that's how the\n+/// `FilesystemBackend` Phase 1 stubs for `apply_patch` and `shell` keep\n+/// working without breaking the agent.\n+pub async fn maybe_intercept(\n+    action_name: &str,\n+    parameters: &Value,\n+    project_id: ProjectId,\n+    mounts: &WorkspaceMounts,\n+) -> Result<InterceptOutcome, MountError> {\n+    if !SANDBOX_TOOL_NAMES.contains(&action_name) {\n+        return Ok(InterceptOutcome::FellThrough);\n+    }\n+\n+    let Some(path_str) = extract_path_param(action_name, parameters) else {\n+        return Ok(InterceptOutcome::FellThrough);\n+    };\n+    if !is_mountable_path(&path_str) {\n+        return Ok(InterceptOutcome::FellThrough);\n+    }\n+\n+    let Some((backend, rel_path)) = mounts.resolve(project_id, &path_str).await? else {\n+        return Ok(InterceptOutcome::FellThrough);\n+    };\n+\n+    let result = match action_name {\n+        \"file_read\" | \"read_file\" => match backend.read(&rel_path).await {\n+            Ok(bytes) => {\n+                let content = String::from_utf8_lossy(&bytes).into_owned();\n+                serde_json::json!({\n+                    \"path\": path_str,\n+                    \"content\": content,\n+                    \"size\": bytes.len(),\n+                })\n+            }\n+            Err(MountError::Unsupported { .. }) => return Ok(InterceptOutcome::FellThrough),\n+            Err(e) => return Err(e),\n+        },\n+        \"file_write\" | \"write_file\" => {\n+            let content = parameters\n+                .get(\"content\")\n+                .and_then(|v| v.as_str())\n+                .unwrap_or(\"\");\n+            match backend.write(&rel_path, content.as_bytes()).await {\n+                Ok(()) => serde_json::json!({\n+                    \"path\": path_str,\n+                    \"bytes_written\": content.len(),","path":"src/bridge/sandbox/intercept.rs","commit_id":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","original_commit_id":"980058e3beda33c450b9f1f3b3023fcc1b38d012","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Fixed in 1c447b0d — `file_write` now returns `MountError::InvalidPath` when `content` is missing instead of silently writing an empty string.","created_at":"2026-04-11T08:46:52Z","updated_at":"2026-04-11T08:46:52Z","html_url":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067806889","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"self":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067806889"},"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#discussion_r3067806889"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/comments/3067806889/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"in_reply_to_id":3067678007,"original_position":107,"position":112,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}}},"public":true,"created_at":"2026-04-11T08:46:52Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310868635","type":"PullRequestReviewEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"review":{"id":4093580018,"node_id":"PRR_kwDORHZ7Z87z_xLy","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"body":null,"commit_id":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","state":"commented","html_url":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580018","pull_request_url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","_links":{"html":{"href":"https://github.com/nearai/ironclaw/pull/2211#pullrequestreview-4093580018"},"pull_request":{"href":"https://api.github.com/repos/nearai/ironclaw/pulls/2211"}},"submitted_at":"2026-04-11T08:46:52Z","updated_at":"2026-04-11T08:46:52Z"},"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2211","id":3510062863,"number":2211,"head":{"ref":"feat/engine-v2-sandbox-mount-backend","sha":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"9399fcccc36eb0ce0cbbcd3a6a5ce94628b1f0df","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}},"action":"created"},"public":true,"created_at":"2026-04-11T08:46:53Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453518942","type":"PushEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"repository_id":1148615527,"push_id":32745791093,"ref":"refs/heads/feat/engine-v2-sandbox-mount-backend","head":"1c447b0d103dc6fc3641e7d62a40ff31d0933ae8","before":"980058e3beda33c450b9f1f3b3023fcc1b38d012"},"public":true,"created_at":"2026-04-11T08:46:40Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453352228","type":"DeleteEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"ref":"feat/user-facing-temperature","ref_type":"branch","full_ref":"refs/heads/feat/user-facing-temperature","pusher_type":"user"},"public":true,"created_at":"2026-04-11T08:36:00Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453351426","type":"PushEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"repository_id":1148615527,"push_id":32745623312,"ref":"refs/heads/staging","head":"88b87c0ae1144ddf739d725b268b725b81f17258","before":"cd9b60c64b860f65dc834529d08a8104842c15da"},"public":true,"created_at":"2026-04-11T08:35:58Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310751077","type":"PullRequestEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"merged","number":2275,"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2275","id":3515073451,"number":2275,"head":{"ref":"feat/user-facing-temperature","sha":"857d3b2694a36bc937590cd21bf07d6bd44331b7","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}},"base":{"ref":"staging","sha":"a53eac5c2dec6b6cd5c08189086093fde64aa9cb","repo":{"id":1148615527,"url":"https://api.github.com/repos/nearai/ironclaw","name":"ironclaw"}}}},"public":true,"created_at":"2026-04-11T08:35:56Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310633153","type":"IssueCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","issue":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2318","repository_url":"https://api.github.com/repos/nearai/ironclaw","labels_url":"https://api.github.com/repos/nearai/ironclaw/issues/2318/labels{/name}","comments_url":"https://api.github.com/repos/nearai/ironclaw/issues/2318/comments","events_url":"https://api.github.com/repos/nearai/ironclaw/issues/2318/events","html_url":"https://github.com/nearai/ironclaw/pull/2318","id":4242674798,"node_id":"PR_kwDORHZ7Z87RnNor","number":2318,"title":"feat(build): wasm-sandbox feature flag for armv7/no-wasmtime builds (#1339)","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":10248775942,"node_id":"LA_kwDORHZ7Z88AAAACYt_pBg","url":"https://api.github.com/repos/nearai/ironclaw/labels/size:%20XL","name":"size: XL","color":"B71C1C","default":false,"description":"500+ changed lines"},{"id":10248776107,"node_id":"LA_kwDORHZ7Z88AAAACYt_pqw","url":"https://api.github.com/repos/nearai/ironclaw/labels/risk:%20high","name":"risk: high","color":"F44336","default":false,"description":"Safety, secrets, auth, or critical infrastructure"},{"id":10248776259,"node_id":"LA_kwDORHZ7Z88AAAACYt_qQw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20agent","name":"scope: agent","color":"006B75","default":false,"description":"Agent core (agent loop, router, scheduler)"},{"id":10248776283,"node_id":"LA_kwDORHZ7Z88AAAACYt_qWw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel","name":"scope: channel","color":"00838F","default":false,"description":"Channel infrastructure"},{"id":10248776296,"node_id":"LA_kwDORHZ7Z88AAAACYt_qaA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/cli","name":"scope: channel/cli","color":"00897B","default":false,"description":"TUI / CLI channel"},{"id":10248776330,"node_id":"LA_kwDORHZ7Z88AAAACYt_qig","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/web","name":"scope: channel/web","color":"00796B","default":false,"description":"Web gateway channel"},{"id":10248776362,"node_id":"LA_kwDORHZ7Z88AAAACYt_qqg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/wasm","name":"scope: channel/wasm","color":"00695C","default":false,"description":"WASM channel runtime"},{"id":10248776391,"node_id":"LA_kwDORHZ7Z88AAAACYt_qxw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool","name":"scope: tool","color":"1565C0","default":false,"description":"Tool infrastructure"},{"id":10248776444,"node_id":"LA_kwDORHZ7Z88AAAACYt_q_A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builtin","name":"scope: tool/builtin","color":"1976D2","default":false,"description":"Built-in tools"},{"id":10248776498,"node_id":"LA_kwDORHZ7Z88AAAACYt_rMg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/wasm","name":"scope: tool/wasm","color":"1E88E5","default":false,"description":"WASM tool sandbox"},{"id":10248776569,"node_id":"LA_kwDORHZ7Z88AAAACYt_reQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builder","name":"scope: tool/builder","color":"42A5F5","default":false,"description":"Dynamic tool builder"},{"id":10248776608,"node_id":"LA_kwDORHZ7Z88AAAACYt_roA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db","name":"scope: db","color":"4A148C","default":false,"description":"Database trait / abstraction"},{"id":10248776633,"node_id":"LA_kwDORHZ7Z88AAAACYt_ruQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/postgres","name":"scope: db/postgres","color":"6A1B9A","default":false,"description":"PostgreSQL backend"},{"id":10248776653,"node_id":"LA_kwDORHZ7Z88AAAACYt_rzQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/libsql","name":"scope: db/libsql","color":"7B1FA2","default":false,"description":"libSQL / Turso backend"},{"id":10248776672,"node_id":"LA_kwDORHZ7Z88AAAACYt_r4A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20llm","name":"scope: llm","color":"4527A0","default":false,"description":"LLM integration"},{"id":10248776791,"node_id":"LA_kwDORHZ7Z88AAAACYt_sVw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20orchestrator","name":"scope: orchestrator","color":"0D47A1","default":false,"description":"Container orchestrator"},{"id":10248776821,"node_id":"LA_kwDORHZ7Z88AAAACYt_sdQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20worker","name":"scope: worker","color":"01579B","default":false,"description":"Container worker"},{"id":10248776865,"node_id":"LA_kwDORHZ7Z88AAAACYt_soQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20config","name":"scope: config","color":"E65100","default":false,"description":"Configuration"},{"id":10248776942,"node_id":"LA_kwDORHZ7Z88AAAACYt_s7g","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20setup","name":"scope: setup","color":"827717","default":false,"description":"Onboarding / setup"},{"id":10248777012,"node_id":"LA_kwDORHZ7Z88AAAACYt_tNA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20sandbox","name":"scope: sandbox","color":"00BFA5","default":false,"description":"Docker sandbox"},{"id":10248777215,"node_id":"LA_kwDORHZ7Z88AAAACYt_t_w","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20ci","name":"scope: ci","color":"546E7A","default":false,"description":"CI/CD workflows"},{"id":10248777318,"node_id":"LA_kwDORHZ7Z88AAAACYt_uZg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20docs","name":"scope: docs","color":"78909C","default":false,"description":"Documentation"},{"id":10248777350,"node_id":"LA_kwDORHZ7Z88AAAACYt_uhg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20dependencies","name":"scope: dependencies","color":"90A4AE","default":false,"description":"Dependency updates"},{"id":10248777536,"node_id":"LA_kwDORHZ7Z88AAAACYt_vQA","url":"https://api.github.com/repos/nearai/ironclaw/labels/contributor:%20core","name":"contributor: core","color":"FF8A65","default":false,"description":"20+ merged PRs"},{"id":10665130192,"node_id":"LA_kwDORHZ7Z88AAAACe7D40A","url":"https://api.github.com/repos/nearai/ironclaw/labels/DB%20MIGRATION","name":"DB MIGRATION","color":"C62828","default":false,"description":"PR adds or modifies PostgreSQL or libSQL migration definitions"}],"state":"open","locked":false,"assignees":[],"milestone":null,"comments":2,"created_at":"2026-04-11T02:34:54Z","updated_at":"2026-04-11T08:24:21Z","closed_at":null,"assignee":null,"type":null,"active_lock_reason":null,"draft":true,"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2318","html_url":"https://github.com/nearai/ironclaw/pull/2318","diff_url":"https://github.com/nearai/ironclaw/pull/2318.diff","patch_url":"https://github.com/nearai/ironclaw/pull/2318.patch","merged_at":null},"body":"## Summary\nAddresses #1339. Compiling on armv7 (Raspberry Pi) fails because cranelift-codegen doesn't support the architecture. This PR adds a `wasm-sandbox` Cargo feature (default-on) gating all wasmtime/cranelift dependencies.\n\nChanges across 9 files:\n- `Cargo.toml`: `wasmtime`, `wasmtime-wasi`, `wasmparser` made optional; new `wasm-sandbox` default feature\n- `src/tools/wasm/mod.rs`: Gated `host`/`loader`/`runtime`/`wrapper` submodules; added stub types (`WasmToolRuntime`, `WasmRuntimeConfig`, `PreparedModule`, `WasmToolLoader`, etc.)\n- `src/channels/wasm/mod.rs`: Same pattern; added stub types (`WasmChannelRuntime`, `WasmChannel`, `WasmChannelRouter`, etc.)\n- `src/tools/wasm/limits.rs`, `src/config/wasm.rs`, `src/tools/builder/mod.rs`, `src/tools/registry.rs`, `src/app.rs`\n\n## Remaining work\n`extensions/manager.rs` (~200 lines of WASM channel activation) and `src/main.rs` WASM channel setup need additional gating for full `--no-default-features` compilation. Documented in commit.\n\n## Test plan\n- [x] `cargo fmt`, `cargo clippy --all-features` zero warnings\n- [x] All 4033 unit tests pass with default features\n- [ ] Manual: compile with `--no-default-features` on armv7\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2318/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/nearai/ironclaw/issues/2318/timeline","performed_via_github_app":null,"state_reason":null},"comment":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229100984","html_url":"https://github.com/nearai/ironclaw/pull/2318#issuecomment-4229100984","issue_url":"https://api.github.com/repos/nearai/ironclaw/issues/2318","id":4229100984,"node_id":"IC_kwDORHZ7Z878EvW4","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2026-04-11T08:24:21Z","updated_at":"2026-04-11T08:24:21Z","body":"Addressed in a580c093: `cargo check --no-default-features` now compiles cleanly.\n\n**Approach:** stub-impl alignment (no new cfg gates in call sites). The PR already placed stub types in `src/channels/wasm/mod.rs` behind `#[cfg(not(feature = \"wasm-sandbox\"))]`, but the stub signatures had drifted from the real API that `src/extensions/manager.rs` calls. I updated the stubs to mirror the real signatures exactly, so the extension manager and main.rs compile unchanged in both modes:\n\n- `WasmChannelLoader::new` → 4 args + `with_secrets_store()` builder\n- `LoadedChannel { channel: WasmChannel }` (owned, not Arc); webhook/signature/hmac secret-name return types match real impl\n- `WasmChannel`: `with_owner_actor_id`, `channel_name`, correct `set_credential`/`update_config`/`call_on_start` signatures (returning `Result<ChannelConfig, WasmChannelError>`)\n- `WasmChannelRouter::register(channel, endpoints, secret, header)`; `register_signature_key → Result<(), String>`; `update_secret(String)`\n- `setup_wasm_channels` stub matches real 5-arg signature, returns `Option<WasmChannelSetup>` (warn-and-skip no-op)\n\nPer CLAUDE.md \"Module-owned initialization\", all feature-flag branching stays confined to `src/channels/wasm/mod.rs` and `src/tools/wasm/mod.rs`. Also cleaned up two derivable `Default` impls flagged by clippy.\n\nVerified:\n- `cargo check --no-default-features` — clean\n- `cargo check` (default) — clean\n- `cargo clippy --all --benches --tests --examples --all-features` — zero warnings\n- `cargo test --lib` — 4033 passed\n\nThe wasm-sandbox-enabled path is untouched.","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229100984/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}},"public":true,"created_at":"2026-04-11T08:24:21Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453155381","type":"PushEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"repository_id":1148615527,"push_id":32745426941,"ref":"refs/heads/fix/1339-armv7-wasm-feature-flag","head":"a580c0939f5fd2c1f596df749fc135406e742f53","before":"1f278e0ffd19bb00de8586f527eb2075baea8071"},"public":true,"created_at":"2026-04-11T08:24:07Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310630160","type":"IssueCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","issue":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2314","repository_url":"https://api.github.com/repos/nearai/ironclaw","labels_url":"https://api.github.com/repos/nearai/ironclaw/issues/2314/labels{/name}","comments_url":"https://api.github.com/repos/nearai/ironclaw/issues/2314/comments","events_url":"https://api.github.com/repos/nearai/ironclaw/issues/2314/events","html_url":"https://github.com/nearai/ironclaw/pull/2314","id":4242672765,"node_id":"PR_kwDORHZ7Z87RnNZP","number":2314,"title":"feat(version): track running version, detect downgrades, warn on restart (#1846)","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":10248775942,"node_id":"LA_kwDORHZ7Z88AAAACYt_pBg","url":"https://api.github.com/repos/nearai/ironclaw/labels/size:%20XL","name":"size: XL","color":"B71C1C","default":false,"description":"500+ changed lines"},{"id":10248776107,"node_id":"LA_kwDORHZ7Z88AAAACYt_pqw","url":"https://api.github.com/repos/nearai/ironclaw/labels/risk:%20high","name":"risk: high","color":"F44336","default":false,"description":"Safety, secrets, auth, or critical infrastructure"},{"id":10248776259,"node_id":"LA_kwDORHZ7Z88AAAACYt_qQw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20agent","name":"scope: agent","color":"006B75","default":false,"description":"Agent core (agent loop, router, scheduler)"},{"id":10248776283,"node_id":"LA_kwDORHZ7Z88AAAACYt_qWw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel","name":"scope: channel","color":"00838F","default":false,"description":"Channel infrastructure"},{"id":10248776296,"node_id":"LA_kwDORHZ7Z88AAAACYt_qaA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/cli","name":"scope: channel/cli","color":"00897B","default":false,"description":"TUI / CLI channel"},{"id":10248776330,"node_id":"LA_kwDORHZ7Z88AAAACYt_qig","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/web","name":"scope: channel/web","color":"00796B","default":false,"description":"Web gateway channel"},{"id":10248776362,"node_id":"LA_kwDORHZ7Z88AAAACYt_qqg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/wasm","name":"scope: channel/wasm","color":"00695C","default":false,"description":"WASM channel runtime"},{"id":10248776391,"node_id":"LA_kwDORHZ7Z88AAAACYt_qxw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool","name":"scope: tool","color":"1565C0","default":false,"description":"Tool infrastructure"},{"id":10248776444,"node_id":"LA_kwDORHZ7Z88AAAACYt_q_A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builtin","name":"scope: tool/builtin","color":"1976D2","default":false,"description":"Built-in tools"},{"id":10248776498,"node_id":"LA_kwDORHZ7Z88AAAACYt_rMg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/wasm","name":"scope: tool/wasm","color":"1E88E5","default":false,"description":"WASM tool sandbox"},{"id":10248776569,"node_id":"LA_kwDORHZ7Z88AAAACYt_reQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builder","name":"scope: tool/builder","color":"42A5F5","default":false,"description":"Dynamic tool builder"},{"id":10248776608,"node_id":"LA_kwDORHZ7Z88AAAACYt_roA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db","name":"scope: db","color":"4A148C","default":false,"description":"Database trait / abstraction"},{"id":10248776633,"node_id":"LA_kwDORHZ7Z88AAAACYt_ruQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/postgres","name":"scope: db/postgres","color":"6A1B9A","default":false,"description":"PostgreSQL backend"},{"id":10248776653,"node_id":"LA_kwDORHZ7Z88AAAACYt_rzQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/libsql","name":"scope: db/libsql","color":"7B1FA2","default":false,"description":"libSQL / Turso backend"},{"id":10248776672,"node_id":"LA_kwDORHZ7Z88AAAACYt_r4A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20llm","name":"scope: llm","color":"4527A0","default":false,"description":"LLM integration"},{"id":10248776791,"node_id":"LA_kwDORHZ7Z88AAAACYt_sVw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20orchestrator","name":"scope: orchestrator","color":"0D47A1","default":false,"description":"Container orchestrator"},{"id":10248776821,"node_id":"LA_kwDORHZ7Z88AAAACYt_sdQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20worker","name":"scope: worker","color":"01579B","default":false,"description":"Container worker"},{"id":10248776865,"node_id":"LA_kwDORHZ7Z88AAAACYt_soQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20config","name":"scope: config","color":"E65100","default":false,"description":"Configuration"},{"id":10248776942,"node_id":"LA_kwDORHZ7Z88AAAACYt_s7g","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20setup","name":"scope: setup","color":"827717","default":false,"description":"Onboarding / setup"},{"id":10248777012,"node_id":"LA_kwDORHZ7Z88AAAACYt_tNA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20sandbox","name":"scope: sandbox","color":"00BFA5","default":false,"description":"Docker sandbox"},{"id":10248777215,"node_id":"LA_kwDORHZ7Z88AAAACYt_t_w","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20ci","name":"scope: ci","color":"546E7A","default":false,"description":"CI/CD workflows"},{"id":10248777318,"node_id":"LA_kwDORHZ7Z88AAAACYt_uZg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20docs","name":"scope: docs","color":"78909C","default":false,"description":"Documentation"},{"id":10248777536,"node_id":"LA_kwDORHZ7Z88AAAACYt_vQA","url":"https://api.github.com/repos/nearai/ironclaw/labels/contributor:%20core","name":"contributor: core","color":"FF8A65","default":false,"description":"20+ merged PRs"},{"id":10665130192,"node_id":"LA_kwDORHZ7Z88AAAACe7D40A","url":"https://api.github.com/repos/nearai/ironclaw/labels/DB%20MIGRATION","name":"DB MIGRATION","color":"C62828","default":false,"description":"PR adds or modifies PostgreSQL or libSQL migration definitions"}],"state":"open","locked":false,"assignees":[],"milestone":null,"comments":2,"created_at":"2026-04-11T02:34:24Z","updated_at":"2026-04-11T08:24:18Z","closed_at":null,"assignee":null,"type":null,"active_lock_reason":null,"draft":true,"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2314","html_url":"https://github.com/nearai/ironclaw/pull/2314","diff_url":"https://github.com/nearai/ironclaw/pull/2314.diff","patch_url":"https://github.com/nearai/ironclaw/pull/2314.patch","merged_at":null},"body":"## Summary\nAddresses #1846. The web UI restart button calls `process::exit(0)`; if the Docker tag is mutable (e.g. `latest`), the container may come back on an older image, causing schema mismatch and data loss.\n\n- **New `src/version.rs`**: Persists running version in DB settings under `system/ironclaw.version`. Compares via semver to detect `Fresh`/`Unchanged`/`Upgraded`/`Downgraded` transitions (5 unit tests)\n- **Startup downgrade detection** in `src/app.rs`: Logs `ERROR` on downgrade with actionable message\n- **Enhanced restart dialog**: Shows current version, warns about mutable image tags\n- **Post-restart SSE handler**: Compares pre/post versions, alerts user via system message if changed\n- i18n strings added for English and Chinese\n\n## Test plan\n- [x] `cargo fmt`, clippy, all 4038 unit tests pass\n- [ ] Manual: simulate downgrade, verify ERROR log on startup\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2314/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/nearai/ironclaw/issues/2314/timeline","performed_via_github_app":null,"state_reason":null},"comment":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229100576","html_url":"https://github.com/nearai/ironclaw/pull/2314#issuecomment-4229100576","issue_url":"https://api.github.com/repos/nearai/ironclaw/issues/2314","id":4229100576,"node_id":"IC_kwDORHZ7Z878EvQg","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2026-04-11T08:24:03Z","updated_at":"2026-04-11T08:24:03Z","body":"Addressed in 433ff3d5: added caller-level startup test covering the full version-transition matrix (first-boot/same/upgrade/downgrade) through persisted state, plus a gateway-level test asserting the reconnect warning fires when persisted version changes across boots.","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229100576/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}},"public":true,"created_at":"2026-04-11T08:24:03Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453153551","type":"PushEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"repository_id":1148615527,"push_id":32745425035,"ref":"refs/heads/fix/1846-upgrade-data-loss","head":"433ff3d5713b72c3b81431296ce63bcec989682c","before":"e26a90f63c2043f8e9d83944dc7234252f254b97"},"public":true,"created_at":"2026-04-11T08:24:00Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453114854","type":"DeleteEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"ref":"fix/reapply-telegram-utf16-and-db-migration-label","ref_type":"branch","full_ref":"refs/heads/fix/reapply-telegram-utf16-and-db-migration-label","pusher_type":"user"},"public":true,"created_at":"2026-04-11T08:21:28Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453113310","type":"PushEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"repository_id":1148615527,"push_id":32745384220,"ref":"refs/heads/staging","head":"cd9b60c64b860f65dc834529d08a8104842c15da","before":"207c4d4694123a1319a30724115de58537045ab0"},"public":true,"created_at":"2026-04-11T08:21:27Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310590589","type":"IssueCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","issue":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2312","repository_url":"https://api.github.com/repos/nearai/ironclaw","labels_url":"https://api.github.com/repos/nearai/ironclaw/issues/2312/labels{/name}","comments_url":"https://api.github.com/repos/nearai/ironclaw/issues/2312/comments","events_url":"https://api.github.com/repos/nearai/ironclaw/issues/2312/events","html_url":"https://github.com/nearai/ironclaw/pull/2312","id":4242671862,"node_id":"PR_kwDORHZ7Z87RnNR0","number":2312,"title":"fix(secrets): auto-generate master key with file fallback when keychain unavailable (#1820)","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":10248775942,"node_id":"LA_kwDORHZ7Z88AAAACYt_pBg","url":"https://api.github.com/repos/nearai/ironclaw/labels/size:%20XL","name":"size: XL","color":"B71C1C","default":false,"description":"500+ changed lines"},{"id":10248776107,"node_id":"LA_kwDORHZ7Z88AAAACYt_pqw","url":"https://api.github.com/repos/nearai/ironclaw/labels/risk:%20high","name":"risk: high","color":"F44336","default":false,"description":"Safety, secrets, auth, or critical infrastructure"},{"id":10248776259,"node_id":"LA_kwDORHZ7Z88AAAACYt_qQw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20agent","name":"scope: agent","color":"006B75","default":false,"description":"Agent core (agent loop, router, scheduler)"},{"id":10248776283,"node_id":"LA_kwDORHZ7Z88AAAACYt_qWw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel","name":"scope: channel","color":"00838F","default":false,"description":"Channel infrastructure"},{"id":10248776296,"node_id":"LA_kwDORHZ7Z88AAAACYt_qaA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/cli","name":"scope: channel/cli","color":"00897B","default":false,"description":"TUI / CLI channel"},{"id":10248776330,"node_id":"LA_kwDORHZ7Z88AAAACYt_qig","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/web","name":"scope: channel/web","color":"00796B","default":false,"description":"Web gateway channel"},{"id":10248776362,"node_id":"LA_kwDORHZ7Z88AAAACYt_qqg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/wasm","name":"scope: channel/wasm","color":"00695C","default":false,"description":"WASM channel runtime"},{"id":10248776391,"node_id":"LA_kwDORHZ7Z88AAAACYt_qxw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool","name":"scope: tool","color":"1565C0","default":false,"description":"Tool infrastructure"},{"id":10248776444,"node_id":"LA_kwDORHZ7Z88AAAACYt_q_A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builtin","name":"scope: tool/builtin","color":"1976D2","default":false,"description":"Built-in tools"},{"id":10248776498,"node_id":"LA_kwDORHZ7Z88AAAACYt_rMg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/wasm","name":"scope: tool/wasm","color":"1E88E5","default":false,"description":"WASM tool sandbox"},{"id":10248776569,"node_id":"LA_kwDORHZ7Z88AAAACYt_reQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builder","name":"scope: tool/builder","color":"42A5F5","default":false,"description":"Dynamic tool builder"},{"id":10248776608,"node_id":"LA_kwDORHZ7Z88AAAACYt_roA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db","name":"scope: db","color":"4A148C","default":false,"description":"Database trait / abstraction"},{"id":10248776633,"node_id":"LA_kwDORHZ7Z88AAAACYt_ruQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/postgres","name":"scope: db/postgres","color":"6A1B9A","default":false,"description":"PostgreSQL backend"},{"id":10248776653,"node_id":"LA_kwDORHZ7Z88AAAACYt_rzQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/libsql","name":"scope: db/libsql","color":"7B1FA2","default":false,"description":"libSQL / Turso backend"},{"id":10248776672,"node_id":"LA_kwDORHZ7Z88AAAACYt_r4A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20llm","name":"scope: llm","color":"4527A0","default":false,"description":"LLM integration"},{"id":10248776791,"node_id":"LA_kwDORHZ7Z88AAAACYt_sVw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20orchestrator","name":"scope: orchestrator","color":"0D47A1","default":false,"description":"Container orchestrator"},{"id":10248776821,"node_id":"LA_kwDORHZ7Z88AAAACYt_sdQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20worker","name":"scope: worker","color":"01579B","default":false,"description":"Container worker"},{"id":10248776848,"node_id":"LA_kwDORHZ7Z88AAAACYt_skA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20secrets","name":"scope: secrets","color":"BF360C","default":false,"description":"Secrets management"},{"id":10248776865,"node_id":"LA_kwDORHZ7Z88AAAACYt_soQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20config","name":"scope: config","color":"E65100","default":false,"description":"Configuration"},{"id":10248776942,"node_id":"LA_kwDORHZ7Z88AAAACYt_s7g","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20setup","name":"scope: setup","color":"827717","default":false,"description":"Onboarding / setup"},{"id":10248777012,"node_id":"LA_kwDORHZ7Z88AAAACYt_tNA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20sandbox","name":"scope: sandbox","color":"00BFA5","default":false,"description":"Docker sandbox"},{"id":10248777215,"node_id":"LA_kwDORHZ7Z88AAAACYt_t_w","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20ci","name":"scope: ci","color":"546E7A","default":false,"description":"CI/CD workflows"},{"id":10248777318,"node_id":"LA_kwDORHZ7Z88AAAACYt_uZg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20docs","name":"scope: docs","color":"78909C","default":false,"description":"Documentation"},{"id":10248777536,"node_id":"LA_kwDORHZ7Z88AAAACYt_vQA","url":"https://api.github.com/repos/nearai/ironclaw/labels/contributor:%20core","name":"contributor: core","color":"FF8A65","default":false,"description":"20+ merged PRs"}],"state":"open","locked":false,"assignees":[],"milestone":null,"comments":2,"created_at":"2026-04-11T02:34:08Z","updated_at":"2026-04-11T08:20:41Z","closed_at":null,"assignee":null,"type":null,"active_lock_reason":null,"draft":true,"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2312","html_url":"https://github.com/nearai/ironclaw/pull/2312","diff_url":"https://github.com/nearai/ironclaw/pull/2312.diff","patch_url":"https://github.com/nearai/ironclaw/pull/2312.patch","merged_at":null},"body":"## Summary\nFixes #1820. When neither `SECRETS_MASTER_KEY` env var nor an OS keychain entry existed (headless Linux, skipped onboarding), `SecretsConfig::resolve()` returned `master_key: None`, the secrets store was never created, and configuring API keys failed with \"secrets store is not available\".\n\n- `SecretsConfig::resolve()` now auto-generates a 32-byte master key when none is found\n- Tries OS keychain first; falls back to `~/.ironclaw/master.key` (0o600)\n- New `KeySource::File` variant handled in doctor/setup/settings handlers\n- File-based fallback validates 64+ hex chars, trims whitespace\n- 7 regression tests (round-trip, invalid data, permissions, missing files)\n\n## Test plan\n- [x] `cargo fmt`, `cargo clippy --all-features` zero warnings\n- [x] Unit tests pass\n- [ ] Manual: on a system without keychain, verify API keys can be configured\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2312/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/nearai/ironclaw/issues/2312/timeline","performed_via_github_app":null,"state_reason":null},"comment":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229094050","html_url":"https://github.com/nearai/ironclaw/pull/2312#issuecomment-4229094050","issue_url":"https://api.github.com/repos/nearai/ironclaw/issues/2312","id":4229094050,"node_id":"IC_kwDORHZ7Z878Etqi","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2026-04-11T08:20:31Z","updated_at":"2026-04-11T08:20:31Z","body":"Addressed in f917be30: the keystore abstraction now distinguishes `NotFound` from transient failure kinds. `SecretsConfig::resolve()` only falls back to file/generate on `NotFound`; transient errors propagate so we never silently rotate the master key. Added caller-level tests covering first-boot, reuse, NotFound-with-file-fallback, transient-with-existing-file, and transient-without-any-existing-key paths using a test keystore stub.","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229094050/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}},"public":true,"created_at":"2026-04-11T08:20:31Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453096889","type":"PushEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"repository_id":1148615527,"push_id":32745367755,"ref":"refs/heads/fix/1820-secrets-store-availability","head":"f917be300ea8e5dccd2adcd14e1a3877e388c769","before":"b1113a54aa2ad161f1453d0ffac8dff9e517f0c4"},"public":true,"created_at":"2026-04-11T08:20:26Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310564243","type":"IssueCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","issue":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2309","repository_url":"https://api.github.com/repos/nearai/ironclaw","labels_url":"https://api.github.com/repos/nearai/ironclaw/issues/2309/labels{/name}","comments_url":"https://api.github.com/repos/nearai/ironclaw/issues/2309/comments","events_url":"https://api.github.com/repos/nearai/ironclaw/issues/2309/events","html_url":"https://github.com/nearai/ironclaw/pull/2309","id":4242670830,"node_id":"PR_kwDORHZ7Z87RnNJw","number":2309,"title":"fix(setup): run migrations during onboard when DATABASE_URL preset (#846)","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":10248775942,"node_id":"LA_kwDORHZ7Z88AAAACYt_pBg","url":"https://api.github.com/repos/nearai/ironclaw/labels/size:%20XL","name":"size: XL","color":"B71C1C","default":false,"description":"500+ changed lines"},{"id":10248776107,"node_id":"LA_kwDORHZ7Z88AAAACYt_pqw","url":"https://api.github.com/repos/nearai/ironclaw/labels/risk:%20high","name":"risk: high","color":"F44336","default":false,"description":"Safety, secrets, auth, or critical infrastructure"},{"id":10248776259,"node_id":"LA_kwDORHZ7Z88AAAACYt_qQw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20agent","name":"scope: agent","color":"006B75","default":false,"description":"Agent core (agent loop, router, scheduler)"},{"id":10248776283,"node_id":"LA_kwDORHZ7Z88AAAACYt_qWw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel","name":"scope: channel","color":"00838F","default":false,"description":"Channel infrastructure"},{"id":10248776296,"node_id":"LA_kwDORHZ7Z88AAAACYt_qaA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/cli","name":"scope: channel/cli","color":"00897B","default":false,"description":"TUI / CLI channel"},{"id":10248776330,"node_id":"LA_kwDORHZ7Z88AAAACYt_qig","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/web","name":"scope: channel/web","color":"00796B","default":false,"description":"Web gateway channel"},{"id":10248776362,"node_id":"LA_kwDORHZ7Z88AAAACYt_qqg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/wasm","name":"scope: channel/wasm","color":"00695C","default":false,"description":"WASM channel runtime"},{"id":10248776391,"node_id":"LA_kwDORHZ7Z88AAAACYt_qxw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool","name":"scope: tool","color":"1565C0","default":false,"description":"Tool infrastructure"},{"id":10248776444,"node_id":"LA_kwDORHZ7Z88AAAACYt_q_A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builtin","name":"scope: tool/builtin","color":"1976D2","default":false,"description":"Built-in tools"},{"id":10248776498,"node_id":"LA_kwDORHZ7Z88AAAACYt_rMg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/wasm","name":"scope: tool/wasm","color":"1E88E5","default":false,"description":"WASM tool sandbox"},{"id":10248776569,"node_id":"LA_kwDORHZ7Z88AAAACYt_reQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builder","name":"scope: tool/builder","color":"42A5F5","default":false,"description":"Dynamic tool builder"},{"id":10248776608,"node_id":"LA_kwDORHZ7Z88AAAACYt_roA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db","name":"scope: db","color":"4A148C","default":false,"description":"Database trait / abstraction"},{"id":10248776633,"node_id":"LA_kwDORHZ7Z88AAAACYt_ruQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/postgres","name":"scope: db/postgres","color":"6A1B9A","default":false,"description":"PostgreSQL backend"},{"id":10248776653,"node_id":"LA_kwDORHZ7Z88AAAACYt_rzQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/libsql","name":"scope: db/libsql","color":"7B1FA2","default":false,"description":"libSQL / Turso backend"},{"id":10248776672,"node_id":"LA_kwDORHZ7Z88AAAACYt_r4A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20llm","name":"scope: llm","color":"4527A0","default":false,"description":"LLM integration"},{"id":10248776791,"node_id":"LA_kwDORHZ7Z88AAAACYt_sVw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20orchestrator","name":"scope: orchestrator","color":"0D47A1","default":false,"description":"Container orchestrator"},{"id":10248776821,"node_id":"LA_kwDORHZ7Z88AAAACYt_sdQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20worker","name":"scope: worker","color":"01579B","default":false,"description":"Container worker"},{"id":10248776865,"node_id":"LA_kwDORHZ7Z88AAAACYt_soQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20config","name":"scope: config","color":"E65100","default":false,"description":"Configuration"},{"id":10248776942,"node_id":"LA_kwDORHZ7Z88AAAACYt_s7g","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20setup","name":"scope: setup","color":"827717","default":false,"description":"Onboarding / setup"},{"id":10248777012,"node_id":"LA_kwDORHZ7Z88AAAACYt_tNA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20sandbox","name":"scope: sandbox","color":"00BFA5","default":false,"description":"Docker sandbox"},{"id":10248777215,"node_id":"LA_kwDORHZ7Z88AAAACYt_t_w","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20ci","name":"scope: ci","color":"546E7A","default":false,"description":"CI/CD workflows"},{"id":10248777318,"node_id":"LA_kwDORHZ7Z88AAAACYt_uZg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20docs","name":"scope: docs","color":"78909C","default":false,"description":"Documentation"},{"id":10248777536,"node_id":"LA_kwDORHZ7Z88AAAACYt_vQA","url":"https://api.github.com/repos/nearai/ironclaw/labels/contributor:%20core","name":"contributor: core","color":"FF8A65","default":false,"description":"20+ merged PRs"}],"state":"open","locked":false,"assignees":[],"milestone":null,"comments":2,"created_at":"2026-04-11T02:33:49Z","updated_at":"2026-04-11T08:19:13Z","closed_at":null,"assignee":null,"type":null,"active_lock_reason":null,"draft":true,"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2309","html_url":"https://github.com/nearai/ironclaw/pull/2309","diff_url":"https://github.com/nearai/ironclaw/pull/2309.diff","patch_url":"https://github.com/nearai/ironclaw/pull/2309.patch","merged_at":null},"body":"## Summary\nFixes #846. The `auto_setup_database()` PostgreSQL early-return paths skipped `test_database_connection_postgres()` and `run_migrations_postgres()` when `DATABASE_URL` was already set, leaving `self.db_pool = None` and causing \"Failed to save settings to database\" at the final onboard step.\n\n- Both early-return PostgreSQL paths now establish the connection and run migrations\n- Added `test_auto_setup_database_runs_migrations_with_existing_env` regression test (libsql variant)\n\n## Test plan\n- [x] `cargo fmt`, `cargo clippy --all-features` zero warnings\n- [x] `cargo test --lib -- setup bootstrap` (125 tests pass)\n- [ ] Manual: set DATABASE_URL, run `ironclaw onboard`, verify it completes\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2309/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/nearai/ironclaw/issues/2309/timeline","performed_via_github_app":null,"state_reason":null},"comment":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229083928","html_url":"https://github.com/nearai/ironclaw/pull/2309#issuecomment-4229083928","issue_url":"https://api.github.com/repos/nearai/ironclaw/issues/2309","id":4229083928,"node_id":"IC_kwDORHZ7Z878ErMY","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2026-04-11T08:19:04Z","updated_at":"2026-04-11T08:19:04Z","body":"Addressed in ebe8a807: added caller-level integration test driving `auto_setup_database()` with a preset Postgres `DATABASE_URL`, exercising the early-return branch and asserting the wizard ends with a live DB handle + successful persistence (including a round-trip read-back via `Store::get_setting`). Uses `testcontainers-modules` with the `pgvector/pgvector:pg16` image so the migrated schema (which requires pgvector) can actually be applied. Gated behind the `integration` feature; skips gracefully when Docker is unavailable.","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229083928/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}},"public":true,"created_at":"2026-04-11T08:19:04Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"10453072789","type":"PushEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"repository_id":1148615527,"push_id":32745342921,"ref":"refs/heads/fix/846-onboard-migration-order","head":"ebe8a8072eb53e51d01777717bf9738fc37a553b","before":"38aea2531d3471aa0feaa8058ef31108a0283b29"},"public":true,"created_at":"2026-04-11T08:18:59Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310497691","type":"IssueCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","issue":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/1339","repository_url":"https://api.github.com/repos/nearai/ironclaw","labels_url":"https://api.github.com/repos/nearai/ironclaw/issues/1339/labels{/name}","comments_url":"https://api.github.com/repos/nearai/ironclaw/issues/1339/comments","events_url":"https://api.github.com/repos/nearai/ironclaw/issues/1339/events","html_url":"https://github.com/nearai/ironclaw/issues/1339","id":4091980474,"node_id":"I_kwDORHZ7Z87z5qq6","number":1339,"title":"\"no supported isa found for arch `armv7`?","user":{"login":"slient2010","id":3945781,"node_id":"MDQ6VXNlcjM5NDU3ODE=","avatar_url":"https://avatars.githubusercontent.com/u/3945781?v=4","gravatar_id":"","url":"https://api.github.com/users/slient2010","html_url":"https://github.com/slient2010","followers_url":"https://api.github.com/users/slient2010/followers","following_url":"https://api.github.com/users/slient2010/following{/other_user}","gists_url":"https://api.github.com/users/slient2010/gists{/gist_id}","starred_url":"https://api.github.com/users/slient2010/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slient2010/subscriptions","organizations_url":"https://api.github.com/users/slient2010/orgs","repos_url":"https://api.github.com/users/slient2010/repos","events_url":"https://api.github.com/users/slient2010/events{/privacy}","received_events_url":"https://api.github.com/users/slient2010/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[],"state":"open","locked":false,"assignees":[],"milestone":null,"comments":1,"created_at":"2026-03-18T00:42:55Z","updated_at":"2026-04-11T08:16:04Z","closed_at":null,"assignee":null,"type":null,"active_lock_reason":null,"sub_issues_summary":{"total":0,"completed":0,"percent_completed":0},"issue_dependencies_summary":{"blocked_by":0,"total_blocked_by":0,"blocking":0,"total_blocking":0},"body":"Hello,\n\nI’m trying to compile this project on my raspberry pi, but unlucky, got the error below. Can we support or any plan to get this run on raspberry?\n\nOS information \n\n```text\npi@raspberrypi:~/ironclaw $ cat /etc/os-release \nPRETTY_NAME=\"Raspbian GNU/Linux 12 (bookworm)\"\nNAME=\"Raspbian GNU/Linux\"\nVERSION_ID=\"12\"\nVERSION=\"12 (bookworm)\"\nVERSION_CODENAME=bookworm\nID=raspbian\nID_LIKE=debian\nHOME_URL=\"http://www.raspbian.org/\"\nSUPPORT_URL=\"http://www.raspbian.org/RaspbianForums\"\nBUG_REPORT_URL=\"http://www.raspbian.org/RaspbianBugs\"\n```\n\nRust version\n\n``` text\npi@raspberrypi:~/ironclaw $ rustc --version\nrustc 1.94.0 (4a4ef493e 2026-03-02)\n```\n\nCompile error information \n\n``` text\nerror: failed to run custom build command for `cranelift-codegen v0.115.1`\n\nCaused by:\n  process didn't exit successfully: `/home/pi/ironclaw/target/debug/build/cranelift-codegen-38470dd2d57660b9/build-script-build` (exit status: 101)\u0000\u0000\u0000\u0000\u0000  --- stderr\n\n  thread 'main' (1761) panicked at /home/pi/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cranelift-codegen-0.115.1/build.rs:56:53:\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000  error when identifying target: \"no supported isa found for arch `armv7`\"\n  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000warning: build failed, waiting for other jobs to finish...\n```","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/1339/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/nearai/ironclaw/issues/1339/timeline","performed_via_github_app":null,"state_reason":null,"pinned_comment":null},"comment":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229051742","html_url":"https://github.com/nearai/ironclaw/issues/1339#issuecomment-4229051742","issue_url":"https://api.github.com/repos/nearai/ironclaw/issues/1339","id":4229051742,"node_id":"IC_kwDORHZ7Z878EjVe","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2026-04-11T08:16:04Z","updated_at":"2026-04-11T08:16:04Z","body":"Do you have 32 bit OS running? \n\nCranelift only supports 64 bit \n\nWe can disable wasm modules and let you use http only but which with v2 engine is way more useful ","pin":null,"reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229051742/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}},"public":true,"created_at":"2026-04-11T08:16:04Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}},{"id":"8310445302","type":"IssueCommentEvent","actor":{"id":175486,"login":"ilblackdragon","display_login":"ilblackdragon","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","avatar_url":"https://avatars.githubusercontent.com/u/175486?"},"repo":{"id":1148615527,"name":"nearai/ironclaw","url":"https://api.github.com/repos/nearai/ironclaw"},"payload":{"action":"created","issue":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2315","repository_url":"https://api.github.com/repos/nearai/ironclaw","labels_url":"https://api.github.com/repos/nearai/ironclaw/issues/2315/labels{/name}","comments_url":"https://api.github.com/repos/nearai/ironclaw/issues/2315/comments","events_url":"https://api.github.com/repos/nearai/ironclaw/issues/2315/events","html_url":"https://github.com/nearai/ironclaw/pull/2315","id":4242673060,"node_id":"PR_kwDORHZ7Z87RnNbD","number":2315,"title":"fix(ux): actionable auth errors and improved CLI help for new users (#1852)","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":10248775942,"node_id":"LA_kwDORHZ7Z88AAAACYt_pBg","url":"https://api.github.com/repos/nearai/ironclaw/labels/size:%20XL","name":"size: XL","color":"B71C1C","default":false,"description":"500+ changed lines"},{"id":10248776107,"node_id":"LA_kwDORHZ7Z88AAAACYt_pqw","url":"https://api.github.com/repos/nearai/ironclaw/labels/risk:%20high","name":"risk: high","color":"F44336","default":false,"description":"Safety, secrets, auth, or critical infrastructure"},{"id":10248776259,"node_id":"LA_kwDORHZ7Z88AAAACYt_qQw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20agent","name":"scope: agent","color":"006B75","default":false,"description":"Agent core (agent loop, router, scheduler)"},{"id":10248776283,"node_id":"LA_kwDORHZ7Z88AAAACYt_qWw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel","name":"scope: channel","color":"00838F","default":false,"description":"Channel infrastructure"},{"id":10248776296,"node_id":"LA_kwDORHZ7Z88AAAACYt_qaA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/cli","name":"scope: channel/cli","color":"00897B","default":false,"description":"TUI / CLI channel"},{"id":10248776330,"node_id":"LA_kwDORHZ7Z88AAAACYt_qig","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/web","name":"scope: channel/web","color":"00796B","default":false,"description":"Web gateway channel"},{"id":10248776362,"node_id":"LA_kwDORHZ7Z88AAAACYt_qqg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20channel/wasm","name":"scope: channel/wasm","color":"00695C","default":false,"description":"WASM channel runtime"},{"id":10248776391,"node_id":"LA_kwDORHZ7Z88AAAACYt_qxw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool","name":"scope: tool","color":"1565C0","default":false,"description":"Tool infrastructure"},{"id":10248776444,"node_id":"LA_kwDORHZ7Z88AAAACYt_q_A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builtin","name":"scope: tool/builtin","color":"1976D2","default":false,"description":"Built-in tools"},{"id":10248776498,"node_id":"LA_kwDORHZ7Z88AAAACYt_rMg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/wasm","name":"scope: tool/wasm","color":"1E88E5","default":false,"description":"WASM tool sandbox"},{"id":10248776569,"node_id":"LA_kwDORHZ7Z88AAAACYt_reQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20tool/builder","name":"scope: tool/builder","color":"42A5F5","default":false,"description":"Dynamic tool builder"},{"id":10248776608,"node_id":"LA_kwDORHZ7Z88AAAACYt_roA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db","name":"scope: db","color":"4A148C","default":false,"description":"Database trait / abstraction"},{"id":10248776633,"node_id":"LA_kwDORHZ7Z88AAAACYt_ruQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/postgres","name":"scope: db/postgres","color":"6A1B9A","default":false,"description":"PostgreSQL backend"},{"id":10248776653,"node_id":"LA_kwDORHZ7Z88AAAACYt_rzQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20db/libsql","name":"scope: db/libsql","color":"7B1FA2","default":false,"description":"libSQL / Turso backend"},{"id":10248776672,"node_id":"LA_kwDORHZ7Z88AAAACYt_r4A","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20llm","name":"scope: llm","color":"4527A0","default":false,"description":"LLM integration"},{"id":10248776791,"node_id":"LA_kwDORHZ7Z88AAAACYt_sVw","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20orchestrator","name":"scope: orchestrator","color":"0D47A1","default":false,"description":"Container orchestrator"},{"id":10248776821,"node_id":"LA_kwDORHZ7Z88AAAACYt_sdQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20worker","name":"scope: worker","color":"01579B","default":false,"description":"Container worker"},{"id":10248776865,"node_id":"LA_kwDORHZ7Z88AAAACYt_soQ","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20config","name":"scope: config","color":"E65100","default":false,"description":"Configuration"},{"id":10248776942,"node_id":"LA_kwDORHZ7Z88AAAACYt_s7g","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20setup","name":"scope: setup","color":"827717","default":false,"description":"Onboarding / setup"},{"id":10248777012,"node_id":"LA_kwDORHZ7Z88AAAACYt_tNA","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20sandbox","name":"scope: sandbox","color":"00BFA5","default":false,"description":"Docker sandbox"},{"id":10248777215,"node_id":"LA_kwDORHZ7Z88AAAACYt_t_w","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20ci","name":"scope: ci","color":"546E7A","default":false,"description":"CI/CD workflows"},{"id":10248777318,"node_id":"LA_kwDORHZ7Z88AAAACYt_uZg","url":"https://api.github.com/repos/nearai/ironclaw/labels/scope:%20docs","name":"scope: docs","color":"78909C","default":false,"description":"Documentation"},{"id":10248777536,"node_id":"LA_kwDORHZ7Z88AAAACYt_vQA","url":"https://api.github.com/repos/nearai/ironclaw/labels/contributor:%20core","name":"contributor: core","color":"FF8A65","default":false,"description":"20+ merged PRs"}],"state":"open","locked":false,"assignees":[],"milestone":null,"comments":2,"created_at":"2026-04-11T02:34:30Z","updated_at":"2026-04-11T08:13:46Z","closed_at":null,"assignee":null,"type":null,"active_lock_reason":null,"draft":true,"pull_request":{"url":"https://api.github.com/repos/nearai/ironclaw/pulls/2315","html_url":"https://github.com/nearai/ironclaw/pull/2315","diff_url":"https://github.com/nearai/ironclaw/pull/2315.diff","patch_url":"https://github.com/nearai/ironclaw/pull/2315.patch","merged_at":null},"body":"## Summary\nAddresses #1852 with targeted UX improvements for non-technical users:\n\n- **`src/llm/error.rs`**: `AuthFailed` now includes provider-specific guidance with env var name, signup URL, and `ironclaw onboard --step provider` hint. Covers all providers (4 new tests)\n- **`src/cli/mod.rs`**: Top-level `--help` now has \"Getting started\" and \"Common commands\" sections; `onboard`/`config`/`models`/`doctor`/`login` long_about improved\n- **`src/cli/models.rs`**: Unknown provider errors list available providers; warns when API key env var not set after switching; model list footer shows how to switch\n\n## Test plan\n- [x] `cargo fmt`, `cargo clippy --all-features` zero warnings\n- [x] All 4037 unit tests pass\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/2315/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/nearai/ironclaw/issues/2315/timeline","performed_via_github_app":null,"state_reason":null},"comment":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229023567","html_url":"https://github.com/nearai/ironclaw/pull/2315#issuecomment-4229023567","issue_url":"https://api.github.com/repos/nearai/ironclaw/issues/2315","id":4229023567,"node_id":"IC_kwDORHZ7Z878EcdP","user":{"login":"ilblackdragon","id":175486,"node_id":"MDQ6VXNlcjE3NTQ4Ng==","avatar_url":"https://avatars.githubusercontent.com/u/175486?v=4","gravatar_id":"","url":"https://api.github.com/users/ilblackdragon","html_url":"https://github.com/ilblackdragon","followers_url":"https://api.github.com/users/ilblackdragon/followers","following_url":"https://api.github.com/users/ilblackdragon/following{/other_user}","gists_url":"https://api.github.com/users/ilblackdragon/gists{/gist_id}","starred_url":"https://api.github.com/users/ilblackdragon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilblackdragon/subscriptions","organizations_url":"https://api.github.com/users/ilblackdragon/orgs","repos_url":"https://api.github.com/users/ilblackdragon/repos","events_url":"https://api.github.com/users/ilblackdragon/events{/privacy}","received_events_url":"https://api.github.com/users/ilblackdragon/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2026-04-11T08:13:38Z","updated_at":"2026-04-11T08:13:38Z","body":"Addressed in 282e695f: added snapshot-style assertions over the rendered AuthFailed messages for each important provider (nearai, openai, anthropic, ollama, openai_compatible, tinfoil, bedrock, plus the generic unknown-provider fallback). Tests use `insta::assert_snapshot!` with inline snapshots and render through `LlmError::AuthFailed.to_string()` so the outer `#[error(..)]` format string is also covered. Future edits to the user-facing auth guidance now require a deliberate test update.","reactions":{"url":"https://api.github.com/repos/nearai/ironclaw/issues/comments/4229023567/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}},"public":true,"created_at":"2026-04-11T08:13:38Z","org":{"id":29134221,"login":"nearai","gravatar_id":"","url":"https://api.github.com/orgs/nearai","avatar_url":"https://avatars.githubusercontent.com/u/29134221?"}}]