Bug description
When Hermes workers use model.openai_runtime: codex_app_server, Kanban-dispatched tasks can complete their actual work but fail the board handoff because Codex's workspace sandbox cannot write the Kanban SQLite DB outside the task workspace.
Observed result: the worker prints a successful final answer in its task log, but kanban_complete / kanban_block cannot update the board DB, so the dispatcher later marks the task crashed / blocked.
This is a sandbox integration issue, not a domain-task failure.
Environment
- OS: Ubuntu 24.04 / Linux
- Codex CLI:
codex-cli 0.130.0
- Hermes runtime:
model.openai_runtime: codex_app_server
- Worker profiles tested:
backend-worker, frontend-worker, fullstack-worker
- Sandbox: Bubblewrap/AppArmor working; profile configs use
default_permissions = ":workspace"
Reproduction shape
- Configure worker profiles to use Codex app-server runtime.
- Ensure direct profile smoke passes:
hermes -p backend-worker chat -q 'Run a harmless shell command and reply ok' --yolo -Q
- Create a disposable Kanban board and a scratch task assigned to a Codex-runtime worker:
BOARD=codex-runtime-orchestration-smoke
hermes kanban boards create "$BOARD" --switch || hermes kanban boards switch "$BOARD"
hermes kanban --board "$BOARD" create "Smoke backend-worker Codex app-server runtime via Kanban" \
--assignee backend-worker \
--workspace scratch \
--priority 100 \
--max-runtime 5m \
--max-retries 1 \
--body "Smoke only. Print CODEX_HOME and pwd, then call kanban_complete with summary 'backend-worker codex runtime smoke ok'. If that fails, call kanban_block with exact reason."
hermes kanban --board "$BOARD" dispatch --max 1
- Poll / inspect:
hermes kanban --board "$BOARD" list
hermes kanban --board "$BOARD" show <task_id>
hermes kanban --board "$BOARD" runs <task_id>
hermes kanban --board "$BOARD" log <task_id>
Actual behavior
Worker logs show the underlying smoke succeeded, e.g. CODEX_HOME and pwd printed correctly. But the handoff fails because the sandbox cannot write the board DB:
Blocked: sqlite3 could not open the kanban database for writing from this sandbox
...
unable to open database file
or:
The smoke check passed ...
I could not mark the Kanban task complete because writing to the board database failed under the sandbox
Dispatcher then records the run as crashed / blocked, even though the task itself completed.
Expected behavior
A Kanban worker running on Codex app-server should be able to:
- keep Codex sandboxing enabled,
- execute inside its assigned scratch/worktree workspace,
- call Hermes MCP callback tools like
kanban_complete / kanban_block, and
- write only the current board DB/handoff path needed for task lifecycle updates.
It should not require danger-full-access or :danger-no-sandbox.
Proposed fix
When spawning codex app-server for a dispatcher-spawned worker (HERMES_KANBAN_TASK present), pass narrow Codex config overrides:
if HERMES_KANBAN_TASK:
board_dir = dirname(HERMES_KANBAN_DB)
codex app-server \
-c 'sandbox_mode="workspace-write"' \
-c 'sandbox_workspace_write.writable_roots=["<board_dir>"]' \
-c 'sandbox_workspace_write.network_access=false'
Why board dir rather than all of ~/.hermes/kanban:
HERMES_KANBAN_DB is already pinned by the dispatcher to the current board.
- Per-board directory is the minimal writable root needed for SQLite
kanban.db, journals/WAL files, logs/events if needed.
- It preserves board isolation and avoids giving workers write access to unrelated boards or the whole Hermes home.
Security posture:
- sandbox remains enabled (
workspace-write),
- no
danger-full-access,
- no
:danger-no-sandbox,
- no network by default,
- only the current board directory becomes an extra writable root.
Validation performed locally
After applying the narrow writable-root bridge locally:
- Direct worker runtime smoke passed for all workers.
- MCP callback smoke passed (
web_extract via Codex runtime).
- Real Kanban dispatcher smoke passed on a disposable board:
backend-worker: task reached done
frontend-worker: task reached done
fullstack-worker: task reached done
- Targeted tests passed:
for:
python -m pytest tests/agent/transports/test_codex_app_server_runtime.py tests/agent/transports/test_codex_app_server_session.py -q
Local patch touched:
agent/transports/codex_app_server.py
tests/agent/transports/test_codex_app_server_runtime.py
website/docs/user-guide/features/codex-app-server-runtime.md
Happy to open a PR with this patch if maintainers agree with the writable-root approach.
Bug description
When Hermes workers use
model.openai_runtime: codex_app_server, Kanban-dispatched tasks can complete their actual work but fail the board handoff because Codex's workspace sandbox cannot write the Kanban SQLite DB outside the task workspace.Observed result: the worker prints a successful final answer in its task log, but
kanban_complete/kanban_blockcannot update the board DB, so the dispatcher later marks the taskcrashed/blocked.This is a sandbox integration issue, not a domain-task failure.
Environment
codex-cli 0.130.0model.openai_runtime: codex_app_serverbackend-worker,frontend-worker,fullstack-workerdefault_permissions = ":workspace"Reproduction shape
hermes -p backend-worker chat -q 'Run a harmless shell command and reply ok' --yolo -QActual behavior
Worker logs show the underlying smoke succeeded, e.g.
CODEX_HOMEandpwdprinted correctly. But the handoff fails because the sandbox cannot write the board DB:or:
Dispatcher then records the run as
crashed/blocked, even though the task itself completed.Expected behavior
A Kanban worker running on Codex app-server should be able to:
kanban_complete/kanban_block, andIt should not require
danger-full-accessor:danger-no-sandbox.Proposed fix
When spawning
codex app-serverfor a dispatcher-spawned worker (HERMES_KANBAN_TASKpresent), pass narrow Codex config overrides:Why board dir rather than all of
~/.hermes/kanban:HERMES_KANBAN_DBis already pinned by the dispatcher to the current board.kanban.db, journals/WAL files, logs/events if needed.Security posture:
workspace-write),danger-full-access,:danger-no-sandbox,Validation performed locally
After applying the narrow writable-root bridge locally:
web_extractvia Codex runtime).backend-worker: task reacheddonefrontend-worker: task reacheddonefullstack-worker: task reacheddoneLocal patch touched:
agent/transports/codex_app_server.pytests/agent/transports/test_codex_app_server_runtime.pywebsite/docs/user-guide/features/codex-app-server-runtime.mdHappy to open a PR with this patch if maintainers agree with the writable-root approach.