Commit Graph
584 Commits
Author SHA1 Message Date
Omar SobhandClaude Opus 5 0785ac9c79 feat(missions): document reader + three-tab IA for the mission page
The mission page made its own output unreadable. Reviewing a research
brief meant scrolling a 300px <pre> nested inside a 260px run box nested
inside the page scroller (plus a 4th scroll region for the description) —
and the text was capped at 6,000 chars server-side with no way to fetch
the rest, so a 53kB brief showed ~11% of itself and silently dropped the
remainder. Eight flat tabs (overview/phases/tasks/team/live/artifacts/
benchmarks/pane) mixed lifecycle, work items, people, telemetry, outputs
and infra at one level, so nothing indicated where the deliverable lived.

Reader:
- GET /api/missions/{id}/documents lists every agent output (titles +
  sizes, no bodies); GET .../documents/{run_id}/{index} returns one in
  full. Scoped to the mission so a run id from elsewhere can't be read.
- MissionOutputReader: rail (documents grouped by phase) · document ·
  outline (headings, click to jump). Exactly one scroll container per
  column, never nested. Copy + download .md.
- MarkdownBlock gains fenced code blocks (agent output is full of ```rust,
  previously mangled into paragraphs), h4-h6, heading anchors, and an
  outlineOf() helper.

Information architecture:
- Three primary tabs with shallow sub-views: RUN (phases/tasks/live) ·
  OUTPUT (documents/artifacts/benchmarks) · SETUP (overview/team/pane).
- PhaseRunsList shows a short excerpt with no inner scrollbar and points
  at the reader for the full text.
- The header description is clipped, not scrollable; its full text now
  has a home in Setup → Overview.

Missions list:
- /api/missions returns MissionListItem — Mission flattened plus
  phases_total/phases_done/current_phase, so the JSON stays a strict
  superset. Cards render a progress bar and "Coding · 1/2" instead of a
  bare status dot.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-28 15:16:14 +02:00
Omar SobhandClaude Opus 5 d676a9e089 fix(deploy): ship the immutable main-<sha> tag, not the mutable :latest
ci / gates (push) Successful in 6s
ci / rust (push) Failing after 12s
ci / frontend (push) Successful in 27s
ci / e2e (push) Skipped
ci / publish (push) Skipped
`docker-compose pull server frontend` pulls `:latest`, and the registry
served a STALE manifest for that mutable tag: a deploy pushed
`main-9bc5f6a` correctly, but the gateway's `pull :latest` reported
"image is up to date" and left the previous image running. The verify
step caught it (running 9f2349 = main-0a647c0, expected bbf19f7e), so
the deploy failed loudly rather than silently — but it still could not
ship.

Immutable tags always resolve correctly, so pull `main-<sha>` and retag
it to `:latest` locally on the gateway, then recreate with `--no-deps`
and no compose pull. `:latest` is now just a local alias satisfying the
compose file's image reference; the sha tag is the source of truth.

Also switch the recreate to `--no-deps` (compose v1 has no
`--no-recreate-deps`) so a server/frontend deploy stops recreating
postgres.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-28 14:26:22 +02:00
Omar SobhandClaude Opus 5 9bc5f6a142 fix(missions): bind graph nodes to claws via attrs, not a dropped top-level key
`inject_node_agents` wrote the claw alias as a top-level `"agent"` key on
each graph node, but `cm_topology::Node` only deserializes `{id, role,
level, attrs}` — serde silently dropped it. `TurnRequest::agent` came back
`None` and every mission turn fell back to `ZEROCLAW_DEFAULT_AGENT`
(`scout`), running with scout's workspace and tools instead of the
mission's claws. The runtime trace confirms it: every turn logged
`"agent_alias":"scout"`.

That is why mission agents reported an "empty greenfield" workspace and
emitted artifacts inline instead of writing them: scout is jailed to
`/zeroclaw-data/.zeroclaw/agents/scout/workspace` and cannot see
`/mission/repo`. The per-mission provisioning and `workspace.path` pinning
shipped earlier were correct — they were just applied to agents that
nothing ever drove.

- bind into `node.attrs["agent"]` (top-level key kept for display/debug)
- extract the DB-free `apply_node_agents` and add a regression test that
  round-trips through the real `TopologyGraph` deserializer, which is the
  guard that was missing
- log loudly in `topology_exec::run_turn` when a node falls back to the
  default agent, instead of silently swapping in a different agent

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-28 14:02:08 +02:00
Omar SobhandClaude Opus 4.8 0a647c0bfa fix(deploy): mkdir frontend/public/dl before staging the node binary
ci / rust (push) Failing after 9s
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 30s
ci / e2e (push) Skipped
ci / publish (push) Skipped
rsync --delete excludes frontend/public/dl/, so the directory does not exist
on the build host and the `cp` of clawmates-node into it aborted the deploy
(set -e) before anything was pushed.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-28 10:29:04 +02:00
Omar SobhandClaude Opus 4.8 06ae608d0c fix(missions): provision claws into the mission's own daemon + reload the pin
Mission turns execute against the per-mission runtime container, but claws
were provisioned via RuntimeProvisioner::from_env() — i.e. the GLOBAL gateway.
That daemon loads config once at boot and never re-reads the file, so the
per-mission daemon had no claw_* agents at all: querying it for a mission
claw's risk_profile returned 404 while the global daemon returned 200. With
the alias unresolvable, the daemon silently fell back to the default `scout`
agent, which is jailed to the global workspace — agents reported "the scout
agent workspace" and "/mission/repo isn't accessible", produced no files, and
burned tokens. This is the deeper cause behind the empty-output runs; the
tool-allowlist and workspace-pin fixes were necessary but not sufficient.

- RuntimeProvisioner::for_gateway(url) — aim the provisioner at a specific
  gateway (mirrors ZeroClawDriveExecutor::from_env_for_gateway); from_env now
  delegates to it.
- mission_orchestrator captures the per-mission endpoint from ensure_container
  and provisions every claw there, falling back to the global gateway only
  when there is no per-mission runtime (dev/no-docker).
- workspace.path is file-only (the config prop API cannot set a PathBuf), and
  the daemon never re-reads the file, so pin_agent_workspaces is now followed
  by restart_container(): restart + wait for /health to answer. Agents created
  through the daemon's own config API are already persisted to that file, so
  they survive; the pairing code is re-minted on every launch.
  The readiness probe inspects the /health BODY — exec_capture only fails on
  docker errors, so a curl that cannot connect still "succeeds".

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-28 10:27:54 +02:00
Omar SobhandClaude Opus 4.8 bf32da949f fix(deploy): ship server/frontend via registry push, not save|load
ci / gates (push) Successful in 6s
ci / rust (push) Failing after 10s
ci / frontend (push) Successful in 26s
ci / e2e (push) Skipped
ci / publish (push) Skipped
The gateway compose pulls server/frontend from the web-01 registry
(100.94.185.103:5000, tags main-<sha> + :latest), so `docker save | docker
load` + a local retag does NOT stick — the next `docker-compose up` pulls
`:latest` and silently reverts to the last-pushed image (a green edge on the
old image hid this). Rewrite the server/frontend path to: build on the build
host → tag :latest + :main-<sha> → push to the registry → `compose pull +
up --force-recreate` in /opt/clawmates (the real project dir, not the stale
/root/clawmates) → verify the RUNNING image id equals the pushed one (fail
loudly on mismatch instead of trusting HTTP 200). Agent :dev images stay on
the save|load path (not in any registry).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-28 10:01:05 +02:00
Omar SobhandClaude Opus 4.8 bf4ef4c4bf fix(missions): reap all mission resources on delete (no hanging claws/files)
ci / gates (push) Successful in 24s
ci / rust (push) Failing after 10s
ci / frontend (push) Successful in 27s
ci / e2e (push) Skipped
ci / publish (push) Skipped
DELETE /api/missions/{id} was a bare `DELETE FROM missions` relying on FK
cascades that only cover mission-owned tables. Everything the mission
provisioned leaked: per-mission runtime container, host workspace dir,
teams (created lifecycle=permanent, so no cascade + skipped by the
ephemeral-teardown path), and every claw's ZeroClaw config, .brain files,
and DB rows. Observed live with 0 missions in the DB: 174 orphaned gateway
claw configs, 7 orphaned teams, 31 agents, 39 .brain files, 6 workspace
dirs, a 4-day-old orphaned container, and 123 detached topology_runs.

delete() now calls reap_mission_resources() before the row delete:
- resolve the mission's teams (mission_teams) → claws (team_members)
- per claw: deprovision_claw (gateway) + rm .brain files + hard_purge (DB),
  reusing the manual agent-reap pattern in routes/claws.rs
- delete the permanent-lifecycle teams (team_members cascades)
- delete the mission's topology_runs (else they linger with mission_id
  nulled by the cascade and accumulate)
- teardown_container(), now extended to also rm the /mission/repo workspace
  dir and tolerate an already-gone container (idempotent for the sweeper +
  delete paths)

Runtime-side steps are best-effort (Postgres authoritative; fleet sweeper
reconciles daemon config); DB purges are logged on failure but never block.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-28 09:28:56 +02:00
Omar SobhandClaude Opus 4.8 11e1379c5f fix(build): normalize /etc/clawmates seed-dir perms for the nonroot user
The server COPYs templates/ and skills/ then drops to USER 65532. When the
build context arrives with mode-700 dirs (e.g. rsync -a preserving a dev's
local perms), COPY bakes 700 into the image and the nonroot runtime user
can't read them — the skills/team-template builtin seed silently skips
("Permission denied (os error 13)"). chmod -R a+rX after the COPYs makes
the seed dirs readable regardless of source perms.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-28 09:07:42 +02:00
Omar SobhandClaude Opus 4.8 34409bca0c fix(missions): grant coding tools + pin claw workspace to /mission/repo
Mission agents were burning ~275K tokens producing nothing: the coder had
only file_read and its workspace was the empty ephemeral sandbox, so it
dumped a full spec inline instead of writing files. Two root causes:

1. Risk-profile allowlists used pre-0.8 tool names. `coding_readwrite`
   allow-listed `file_write` (renamed to `file_edit` in ZeroClaw 0.8, and
   `file_write` now refuses on ephemeral workspaces) and omitted file_edit
   / content_search / glob_search / git_operations — the exact tools the
   phase prompt tells agents to use. Since allowed_tools is a strict
   allowlist, agents were effectively read-only. Documents the correct
   profiles in agent.config.example.toml (they only lived in host config;
   the live runtime profiles were corrected via its config API).

2. workspace.path never got set. `agents.<alias>.workspace.path` is an
   Option<PathBuf> the ZeroClaw Configurable macro skips from prop
   enumeration, so provision_claw's set_prop always 404'd and the whole
   call errored into a swallowed eprintln. Removes the dead set_prop and
   pins the workspace out-of-band: MissionRuntimeProvisioner::
   pin_agent_workspaces patches the shared config file on the per-mission
   container (format-preserving via toml_edit, atomic temp+mv); the daemon
   applies it on the same reload that surfaces the freshly-provisioned
   claws. Covered by unit tests for the TOML stamp.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-28 08:40:18 +02:00
Omar Sobh 6d5e7c87d7 fix(claws): point per-mission workspace at /mission/repo + tool-inventory preamble
ci / gates (push) Successful in 18s
ci / frontend (push) Successful in 53s
ci / rust (push) Successful in 3m30s
ci / publish (push) Successful in 4m6s
ci / e2e (push) Skipped
Two stacked issues after risk_profile was fixed:

1. Claws had file_edit + 46 other tools available, but the templates
   trained the agents to expect file_read/file_write (older ZeroClaw
   tool names). Result: agent output kept saying "I only have file_read"
   and dumped implementations into the context window as text.

2. Even with file_edit, the sandbox pointed at
   /zeroclaw-data/.zeroclaw/agents/<alias>/workspace/ — NOT
   /mission/repo where the checked-out mission repo actually lives.
   unrestricted_filesystem=false blocked agents from reaching it.

Fixes:
- provision_claw now takes workspace_path. mission_orchestrator passes
  /mission/repo — pins the per-claw workspace via
  agents.<alias>.workspace.path to the bind-mount path so file_edit /
  content_search / glob_search operate on the mission's git checkout.
- phase_task_text prepends an explicit tool inventory (file_edit,
  content_search, glob_search, git_operations, git_forge, ...) plus a
  WORKSPACE line pinned at /mission/repo. Each phase directive is
  rewritten to reference file_edit / git_operations explicitly and to
  call out "do NOT paste code in your reply expecting the platform to
  save it."
2026-07-24 13:14:30 -07:00
Omar Sobh 84572186e9 fix(runtime_provision): use team-template risk_profile, not hardcoded toolfree
ci / gates (push) Successful in 6s
ci / publish (push) Successful in 4m4s
ci / frontend (push) Successful in 38s
ci / rust (push) Successful in 3m48s
ci / e2e (push) Skipped
The provisioner was hardcoding risk_profile=toolfree for every claw,
which the ZeroClaw config explicitly configures to EXCLUDE every
usable tool (shell, file_read, file_write, http_request, browser).
Result: coder/tester/committer claws had zero tools and produced text
in the context window with no ability to actually write files or run
tests — exactly what the last mission summary showed.

Fixes:
- provision_claw now takes risk_profile: &str, passed through from
  the team template (development teams already had coding_readwrite,
  which now actually gets applied).
- Research team templates updated from toolfree → research_readonly
  (file_read) and papers_research → research_web_readonly
  (file_read + web_search + web_fetch). Applied to both the on-disk
  TOML files and the live DB rows.
- Added RuntimeProvisioner::default_risk_profile_for_role for
  auto-provision code paths that lack a template context — picks
  coding_readwrite for coder-like roles, research_readonly otherwise.
- Split rebind_model out of provision_claw so the model-change UI
  path doesnt inadvertently clobber the existing risk_profile.

Templates DB fixup for missions launched pre-deploy is already
applied via manual UPDATE.
2026-07-23 19:44:27 -07:00
Omar Sobh 9a23c851e0 missions: collapse each run turn + collapse phase summary card
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 27s
ci / rust (push) Successful in 4m30s
ci / e2e (push) Skipped
ci / publish (push) Successful in 36s
- RunOutputPanel: each turn now renders as <details> with a 1-line
  peek in the summary. First turn open by default (so operators see
  something without a click), subsequent turns collapsed. Same shape
  applies to research + coding runs (shared component).
- PhaseSummaryCard: click the header to collapse the whole card;
  narrative peek shows in the collapsed state. State persisted per
  phase_id in localStorage so it stays remembered across visits.
- PhaseSummaryCard Section: cap max height at 280px with internal
  scroll so long tooling / sources / next-action lists dont blow
  out the card height.
2026-07-23 19:26:28 -07:00
Omar Sobh c4ecb9baa4 missions: collapsible header + scrollable tabs + wrap phase controls
ci / rust (push) Successful in 3m7s
ci / e2e (push) Skipped
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 36s
ci / publish (push) Successful in 2m36s
- Mission header title/description now collapsible via chevron next
  to the title. Persisted in localStorage so it stays hidden across
  mission switches once the operator has read it — clears more room
  for phases/tasks/team panels below.
- Tabs row: overflow-x auto + per-tab flex:none + whiteSpace:nowrap
  so 8+ tabs scroll horizontally instead of wrapping and cutting off.
- Phase card action row (retry/security/benchmark buttons): flexWrap
  wrap so long button rows stack cleanly instead of overflowing.
- Phase card status row wraps too, and the card itself gets
  overflow:hidden + minWidth:0 so long content stays inside the
  border and the parent tab-panel scroll handles vertical growth.
2026-07-23 18:40:13 -07:00
Omar Sobh 71f66e0164 fmt: single-line if
ci / publish (push) Successful in 4m10s
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m20s
ci / e2e (push) Skipped
2026-07-23 16:55:21 -07:00
Omar Sobh 50a1aeb446 fmt: phase_summarizer
ci / gates (push) Successful in 5s
ci / rust (push) Failing after 9s
ci / frontend (push) Successful in 26s
ci / e2e (push) Skipped
ci / publish (push) Skipped
2026-07-23 16:55:06 -07:00
Omar Sobh 5c63ef0ed3 missions: phase-completion summary card (Claude Opus 4.8 synthesized)
ci / gates (push) Successful in 6s
ci / rust (push) Failing after 9s
ci / frontend (push) Successful in 26s
ci / e2e (push) Skipped
ci / publish (push) Skipped
New phase_summarizer background worker fires on any mission_phase
transition to a terminal state (completed/failed). Aggregates every
topology_runs.checkpoint.outputs[] + mission_tasks + mission_artifacts
bound to that phase and asks Claude Opus 4.8 to produce a structured
JSON card:

  { narrative, metrics, sources, tooling, next_actions }

Rendered inline on the mission page under each completed phase via
new PhaseSummaryCard component. Metrics grid is kind-specific:
research surfaces insights/sources/int_cards/artifacts, coding
surfaces cards_picked_up/commits/tests/issues, benchmark surfaces
regressions/improvements, security surfaces findings-by-severity.

New table: mission_phase_summaries (migration 0060), unique per
phase_id — regenerates on retry.
New endpoint: GET /api/missions/{id}/phases/{phase_id}/summary.

Model overridable via CLAWMATES_SUMMARIZER_MODEL. Reuses the
ANTHROPIC_API_KEY prod already carries for mission_refiner.
2026-07-23 16:54:38 -07:00
Omar Sobh 1be3430bf2 fix(mission_runtime): remove ZEROCLAW_WORKSPACE env — it was hijacking config-dir
ci / publish (push) Successful in 3m49s
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m25s
ci / e2e (push) Skipped
Deprecated ZEROCLAW_WORKSPACE env var (schema.rs:17467) is used by
the daemon as a legacy config-dir pointer that overrides everything
else. Setting it to /mission/repo made the mission daemon compute
its config dir as /mission/repo/.zeroclaw (empty) and fall back to
defaults — zero agents loaded.

This is the actual root cause of Unknown agent errors on WS. The
seed-mount + admin/paircode/new + per-node-agent-injection fixes
we shipped earlier were correct but couldnt take effect because
the daemon wasnt reading our bind-mounted config at all.

Per-agent workspace pinning belongs in config.toml as
agents.<alias>.workspace, not env.
2026-07-23 13:47:59 -07:00
Omar Sobh 6d60691f5a fmt: phase_runner inject_node_agents
ci / publish (push) Successful in 4m12s
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m25s
ci / e2e (push) Skipped
2026-07-23 09:56:50 -07:00
Omar Sobh 3b243588b8 fix(phase_runner): inject per-node claw agent aliases into topology graph
ci / rust (push) Failing after 10s
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
ci / e2e (push) Skipped
ci / publish (push) Skipped
The topology graph shipped from team.graph only carries node.role,
not node.agent. The executor then defaults to alias_for(role) which
falls to ZEROCLAW_DEFAULT_AGENT (scout) — no such agent → 400.

Look up team_members(node_id → claw_id) at enqueue time and stamp
node.agent = claw_<hex> onto every node. Executor now dials the
specific claw provisioned for THIS teams role.

Was masked pre-C3 because the shared runtime hit the same 400 —
never noticed because no one clicked through to a real run there.
2026-07-23 09:56:26 -07:00
Omar Sobh aea732e712 fix(phase_runner): re-mint pairing code on every launch
ci / gates (push) Successful in 7s
ci / frontend (push) Successful in 40s
ci / rust (push) Successful in 3m36s
ci / e2e (push) Skipped
ci / publish (push) Successful in 2m33s
Pairing codes are single-use / expiring — a mission that reuses an
existing runtime container on a retry needs a fresh code, not the
stale one from the initial launch. Drop the runtime_endpoint gate
so ensure_container always fires, and its fast path re-mints via
/admin/paircode/new for existing containers.
2026-07-23 09:29:49 -07:00
Omar Sobh 8ba9bf0c1c fix(mission_runtime): re-add shared /zeroclaw-data mount for agent library
ci / publish (push) Successful in 2m29s
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 37s
ci / rust (push) Successful in 3m6s
ci / e2e (push) Skipped
Fresh runtimes had zero agents in their config so WS handshake with
?agent=scout returned 400. Bind-mount the shared runtimes data dir
so per-mission gateways inherit the seeded claw_* agents.

Per-mission pairing (minted via /admin/paircode/new) still works
against the shared devices.db — each mission gets its own accepted
token. Concurrency caveat on sqlite sessions.db documented in the
const doc comment.
2026-07-22 18:24:11 -07:00
Omar Sobh 70e7ab3ad6 fix: rename remaining scrape_pairing_code call site
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m24s
ci / e2e (push) Skipped
ci / publish (push) Successful in 2m26s
2026-07-22 13:51:26 -07:00
Omar Sobh 54bba1e113 fix(mission_runtime): mint pairing code via admin endpoint, not log scrape
ci / gates (push) Successful in 7s
ci / frontend (push) Successful in 30s
ci / rust (push) Failing after 41s
ci / e2e (push) Skipped
ci / publish (push) Skipped
Fresh gateways sometimes boot claim-ing already paired (no
pairing_code in the log banner), which broke the log-scrape approach.
Instead, docker exec into the container and hit the localhost
/admin/paircode/new endpoint that always mints a fresh one-time
code and returns JSON we can parse.
2026-07-22 13:50:57 -07:00
Omar Sobh 5f4407e889 fmt: import ordering
ci / publish (push) Successful in 2m46s
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m22s
ci / e2e (push) Skipped
2026-07-22 13:07:35 -07:00
Omar Sobh 37f3f5abfd fix: mission_runtime_pairing_code in single-row mapping + fmt
ci / gates (push) Successful in 7s
ci / rust (push) Failing after 10s
ci / frontend (push) Successful in 28s
ci / e2e (push) Skipped
ci / publish (push) Skipped
2026-07-22 13:07:17 -07:00
Omar Sobh b569688e04 fix(mission_runtime): per-mission auto-pair via container log scrape (C3 auth)
ci / gates (push) Successful in 10s
ci / e2e (push) Skipped
ci / publish (push) Skipped
ci / rust (push) Failing after 23s
ci / frontend (push) Successful in 38s
The seed-mount approach didnt work: even with the shared runtimes
data dir bind-mounted, a fresh gateway instance mints a new pairing
key and requires re-pairing. The topology_worker connect returned
401 forever.

New approach — per-mission gateways self-pair:
- Provisioner tails container logs after start, extracts the
  X-Pairing-Code from the boot banner
- Persists it on missions.runtime_pairing_code (migration 0059)
- topology_worker constructs ZeroClawDriveExecutor with THAT code
  via from_env_for_gateway_with_code, which triggers the lazy
  /pair handshake on first turn and caches the returned bearer

Drops the shared-runtime data-dir mount — each per-mission gateway
now owns its own state, restoring the C3 isolation guarantee.
2026-07-22 13:06:25 -07:00
Omar Sobh 0210f5bf51 cleanup(missions): strip refresh debug scaffolding
ci / gates (push) Successful in 16s
ci / frontend (push) Successful in 36s
ci / rust (push) Successful in 3m13s
ci / e2e (push) Skipped
ci / publish (push) Successful in 2m53s
Removes refreshClicks counter + console.log now that the fetch-hang
was root-caused (fetch: cache no-store) and fixed. Keeps the
updated-at timestamp indicator as ongoing visual feedback.
2026-07-22 06:57:45 -07:00
Omar Sobh 033fcb98f1 fmt: mission_runtime seed_dir
ci / frontend (push) Failing after 38s
ci / gates (push) Successful in 6s
ci / rust (push) Successful in 3m22s
ci / e2e (push) Skipped
ci / publish (push) Skipped
2026-07-22 01:54:13 -07:00
Omar Sobh c4e7ca8aa4 fix(mission_runtime): seed per-mission gateway with shared pairing state
ci / rust (push) Failing after 10s
ci / gates (push) Successful in 5s
ci / frontend (push) Failing after 25s
ci / e2e (push) Skipped
ci / publish (push) Skipped
Fresh mission runtime containers had no ZEROCLAW pairing token so
the topology_worker got 401 Unauthorized on WS connect. Mount the
shared runtimes /root/clawmates-runtime/data as /zeroclaw-data so
the gateway boots pre-paired and accepts the servers ZEROCLAW_TOKEN.

Seed dir overridable via CLAWMATES_RUNTIME_SEED_DIR.

Known caveat: sqlite sessions dir is shared across concurrent
mission runtimes. Fine while topology_worker runs sequentially per
mission; next iteration should copy-on-write per-mission.
2026-07-22 01:53:54 -07:00
Omar Sobh 827b829993 debug(api): log fetch lifecycle for all missions API calls
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m24s
ci / e2e (push) Skipped
ci / publish (push) Successful in 35s
Adds [api] arrow logs on entry, resolve, and error paths so we can
see in devtools console EXACTLY which endpoint hangs and for how long.
2026-07-22 00:54:39 -07:00
Omar Sobh d207c2c043 fix(missions): swap cache:no-store for query cache-buster (hang fix)
ci / frontend (push) Successful in 26s
ci / e2e (push) Skipped
ci / publish (push) Successful in 34s
ci / gates (push) Successful in 7s
ci / rust (push) Successful in 3m23s
fetch(url, { cache: no-store }) was hanging forever through the
edge proxy on the mission API endpoints — requests never reached
postgres and the client-side loading state was stuck true, making
Refresh appear broken. Regressed in d42398d.

Switch to a per-request _t=Date.now() query param on GETs — same
cache-defeat effect, doesn't change fetch semantics.
2026-07-22 00:20:33 -07:00
Omar Sobh b7f0b46971 debug(missions): loud refresh diagnostic + drop disabled attr
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m21s
ci / e2e (push) Skipped
ci / publish (push) Successful in 35s
The refresh button was suspected of being inert when loading is
somehow stuck true. Removes disabled and renders a bulletproof
click counter + loading state next to the icon:

  clicks:0 · updated 14:05:12 · idle

- clicks bumps SYNCHRONOUSLY in onClick before any await, so a
  non-zero counter proves the click event reaches the handler
- console.log fires alongside for devtools verification
- disabled={loading} removed; if load happens to hang, at least
  the user can click again to retry

Temporary scaffolding — will collapse once the root cause is clear.
2026-07-21 23:47:15 -07:00
Omar Sobh d42398d3a8 missions: no-store fetch + visible updated-at timestamp on refresh
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 25s
ci / rust (push) Successful in 4m35s
ci / e2e (push) Skipped
ci / publish (push) Successful in 37s
- api client: cache: no-store so manual Refresh guarantees a fresh
  server response (was potentially hitting stale HTTP cache).
- MissionCanvas: renders "updated HH:MM:SS" next to the refresh
  button; the timestamp bumps on every successful load so the click
  is visibly acknowledged even when nothing else on the page changed.
2026-07-21 23:27:51 -07:00
Omar Sobh 5a1fcba403 fix(mission_runtime): full-uuid container names + assertion fix
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 3m20s
ci / e2e (push) Skipped
ci / publish (push) Successful in 4m5s
UUIDv7 encodes time in the leading bytes so 12-hex prefixes are
NOT unique across missions minted in the same second. Docker
accepts up to 253 chars; use the full uuid.
2026-07-21 22:53:51 -07:00
Omar Sobh bb5cfc1519 fmt: mission_runtime sweeper
ci / frontend (push) Successful in 25s
ci / rust (push) Failing after 2m24s
ci / e2e (push) Skipped
ci / publish (push) Skipped
ci / gates (push) Successful in 5s
2026-07-21 22:34:05 -07:00
Omar Sobh 69a6e4e7f2 missions: sweeper + socket-proxy NETWORKS grant + mount ordering (C3 slice 4-5)
ci / gates (push) Successful in 6s
ci / rust (push) Failing after 10s
ci / frontend (push) Successful in 27s
ci / e2e (push) Skipped
ci / publish (push) Skipped
- mission_runtime::spawn_sweeper: force-removes runtime containers
  for missions terminal for >=30 min, clears runtime_endpoint. Wired
  into clawmates-server main().
- docker-compose socket-proxy: NETWORKS=1 so bollard.connect_network
  can attach containers to clawmates_edge for provider egress.
- phase_runner ordering: ensure_checkout BEFORE ensure_container so
  the mission dir exists before docker mounts it.
- provisioner: mkdir_p the mission dir defensively for research-only
  missions that skip checkout entirely.
2026-07-21 22:33:44 -07:00
Omar Sobh 82966a8004 missions: topology_worker dials per-mission runtime endpoint (C3 slice 3)
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 28s
ci / rust (push) Failing after 1m23s
ci / e2e (push) Skipped
ci / publish (push) Skipped
When a topology_run is bound to a mission whose runtime_endpoint is
set, the worker constructs ZeroClawDriveExecutor against that URL
instead of the env-derived shared gateway. Falls back to shared for
non-mission runs and pre-C3 missions.

With slices 1-3 combined, a mission launched after this deploy will:
  1. get its per-mission container spawned during on_launch
  2. have its checkout dropped into /var/lib/clawmates-missions/<id>
     which is bind-mounted to /mission inside that container
  3. run its agents against ZEROCLAW_WORKSPACE=/mission/repo — so
     they can see and edit only this missions repo, no bleed-over.
2026-07-21 22:31:42 -07:00
Omar Sobh 7649b213ad missions: wire per-mission runtime container into launch + retry (C3 slice 2)
ci / frontend (push) Successful in 32s
ci / gates (push) Successful in 6s
ci / rust (push) Failing after 10s
ci / e2e (push) Skipped
ci / publish (push) Skipped
- mission_orchestrator::on_launch now calls ensure_container after
  the repo checkout, persists the container_name + endpoint on the
  missions row. Non-fatal — logs and continues on docker errors so
  dev-mode + tests keep working.
- phase_runner::launch_phase does the same as a fallback for any
  mission whose runtime_endpoint is null (pre-C3 or torn down).

Nothing reads the endpoint yet; slice 3 swaps topology_worker over.
2026-07-21 22:30:22 -07:00
Omar Sobh f648bcd26e fix: use NetworkConnectRequest for bollard 0.19
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 38s
ci / rust (push) Failing after 1m35s
ci / e2e (push) Skipped
ci / publish (push) Skipped
2026-07-21 22:18:41 -07:00
Omar Sobh 2c593c32ae fix: bollard 0.19 imports for mission_runtime
ci / rust (push) Failing after 1m6s
ci / e2e (push) Skipped
ci / publish (push) Skipped
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
2026-07-21 22:18:15 -07:00
Omar Sobh 5d24fd3460 missions: schema + provisioner skeleton for per-mission runtime containers (C3 slice 1)
ci / e2e (push) Skipped
ci / publish (push) Skipped
ci / gates (push) Successful in 7s
ci / rust (push) Failing after 14s
ci / frontend (push) Successful in 29s
- migration 0058: adds missions.runtime_container_name + runtime_endpoint
- new mission_runtime module (bollard): ensure_container /
  teardown_container. Container is spawned on clawmates_core +
  clawmates_edge networks with just /var/lib/clawmates-missions/{id}
  bind-mounted so agents scoped to /mission/repo can only see this
  missions repo.
- provider API keys forwarded from the server envs so per-mission
  runtimes inherit them.
- Mission struct + repo helpers updated for the two new columns +
  set_runtime_binding().
- Unit tests cover container naming determinism + entropy.

Not wired to the orchestrator yet — that lands in slice 2.
2026-07-21 22:17:41 -07:00
Omar Sobh e5c0e5ec1a phase_runner: ensure repo checkout on every phase launch
ci / frontend (push) Successful in 51s
ci / gates (push) Successful in 5s
ci / rust (push) Failing after 3m16s
ci / e2e (push) Skipped
ci / publish (push) Skipped
Moves ensure_checkout into launch_phase so retries + new phase
launches all trigger the clone/fetch. mission_orchestrator still
does its own checkout at initial launch time, so first-launch
timing is unchanged; this covers the retry + additional-phase
paths.
2026-07-21 20:34:35 -07:00
Omar Sobh 1e91a19707 missions: fix repo checkout for retries + tokenize git.redclaw.dev clones
ci / gates (push) Successful in 7s
ci / frontend (push) Successful in 37s
ci / rust (push) Successful in 3m40s
ci / e2e (push) Skipped
ci / publish (push) Successful in 2m37s
- mission_orchestrator: run ensure_checkout BEFORE the team_id
  short-circuit. Previously, a re-launched or retried mission bailed
  out at the team_id=already-bound guard and skipped repo checkout
  entirely, so agents ran against an empty workspace.
- mission_workspace: inject GITEA_TOKEN into git.redclaw.dev URLs so
  clone auth works from the server container. Redact any token
  echoed back on failure.
- refresh buttons on MissionCanvas + MissionsList now spin the icon
  while loading so clicks are visibly acknowledged.
- refresh-spinner keyframe added to motion.css.

Requires operator on gw-04: sudo chown 65532:65532 /var/lib/clawmates-missions
(applied 2026-07-21 pre-commit).
2026-07-21 20:33:33 -07:00
Omar Sobh f1f3de4db0 missions: cargo fmt for run-output endpoint
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 27s
ci / rust (push) Successful in 4m30s
ci / e2e (push) Skipped
ci / publish (push) Successful in 4m3s
2026-07-21 19:14:35 -07:00
Omar Sobh e2956cdfed missions: surface run output on terminal phase runs
ci / gates (push) Successful in 7s
ci / rust (push) Failing after 10s
ci / frontend (push) Successful in 25s
ci / e2e (push) Skipped
ci / publish (push) Skipped
Adds GET /api/topology-runs/{id}/output — trimmed view of the
runs checkpoint (totals + per-turn output previews, capped at
12 turns × 6kB each). The full checkpoint blob can be hundreds
of KB so it was never viable to send through mission polling.

Phase card run rows now expose a "show output" toggle for any
terminal run (completed/failed/cancelled), rendering turns,
tokens, records count, and per-turn agent text. Running rows
still get the live activity stream from the prior slice.

Diagnostic value: on a mission that "completed" without visible
work, this immediately shows whether the agents produced real
output (workspace missing / instructions vague / etc.) or
whether nothing ran at all.
2026-07-21 15:26:26 -07:00
Omar Sobh 66e57c5c1c missions: live activity stream per running run on phase cards
ci / publish (push) Successful in 4m22s
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 25s
ci / rust (push) Successful in 4m18s
ci / e2e (push) Skipped
Adds a "show activity" toggle to any running topology_run row on
the phase card. Expanded rows mount a compact SSE tail from
/api/topology-runs/{id}/events, rendering step/reasoning/tool
events as they arrive — same stream the LIVE tab consumes, just
scoped to one run.

Extracted the phase-runs list into PhaseRunsList to keep
MissionCanvas under the 1250-line budget.
2026-07-21 14:53:04 -07:00
Omar Sobh 94fecb526c missions: retry failed phases + auto-purge on re-launch
ci / gates (push) Successful in 5s
ci / rust (push) Successful in 4m30s
ci / e2e (push) Skipped
ci / frontend (push) Successful in 26s
ci / publish (push) Successful in 2m40s
Every re-attempted phase now starts with a clean slate:

  - phase_runner::launch_phase DELETEs prior status IN ('failed',
    'cancelled') topology_runs for the phase before enqueuing the
    new ones. Completed runs are kept for audit; only the failure
    noise from earlier attempts goes.
  - POST /api/missions/{id}/phases/{phase_id}/retry — resets a
    failed/cancelled phase to 'pending' (auth-scoped to the calling
    workspace + guarded on mission.status='running'). phase_runner
    picks it up on the next 10s tick.
  - MissionCanvas phase card grows a coral 'Retry' button, visible
    only when phase.status='failed' and mission.status='running'.
    Click → resets + refreshes; the prior failed run rows disappear
    from the card as soon as phase_runner enqueues the new attempt.

Design: auto-purge in phase_runner rather than a separate 'clear
failed runs' endpoint. Users don't have to manually clean up before
retrying; the runner does it as part of the natural work of firing
a fresh attempt.

Verified: cargo check + tsc + eslint --quiet all green.
2026-07-21 13:14:39 -07:00
Omar Sobh a1d1097b52 ci + ops: cargo-build retry wrapper + runtime systemd unit
ci / rust (push) Successful in 4m27s
ci / e2e (push) Skipped
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 26s
ci / publish (push) Successful in 3m39s
Two durability fixes closing recurring flakes:

CI flake wrapper (broker + server Dockerfiles):
  Wrapped the cargo build step in a 3-attempt retry loop with
  linear backoff (10s / 20s). Directly targets the crates.io
  transient network errors that keep hitting CI on the runners
  ('curl failed: SSL_ERROR_SYSCALL, errno 0'). Each build only
  loses time on transient failures; a real compile error still
  fails all 3 attempts and surfaces the last error normally.

Runtime systemd unit (deploy/clawmates-runtime/):
  Replaces the manual 'docker run' that had been starting the
  ZeroClaw runtime with no persistence for its network topology.
  Ephemeral prod fixes at 09:30 PDT 2026-07-21 (task #38) were:
    - anthropic.default provider block added to
      /root/clawmates-runtime/data/.zeroclaw/config.toml (already
      durable — bind-mounted from host)
    - docker network connect clawmates_edge clawmates-runtime
      (NOT durable — vanishes on container recreate)
  New systemd unit clawmates-runtime.service (installed +
  enabled on gw-04):
    - ExecStart docker-runs the container attached to
      clawmates_core, then connects clawmates_edge in the same
      shell command, then docker waits.
    - Bind-mounts both /root/clawmates-runtime/data and
      /var/lib/clawmates-missions (for security_scan +
      benchmark_runner).
    - --rm so upgrading is just docker pull + systemctl restart.
    - Restart=on-failure with 5s backoff.

Closes task #38 and preemptively closes the CI flake pattern.
2026-07-21 12:53:30 -07:00
Omar Sobh f5bba67e38 missions: surface per-phase run errors on the phase card
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 37s
ci / rust (push) Successful in 3m2s
ci / e2e (push) Skipped
ci / publish (push) Successful in 4m3s
Adds inline failure debugging to the Phases tab. When you see a
phase card marked FAILED, click the collapsed error summary and the
full topology_run.error text expands under it — the exact stack
trace / provider error / whatever the worker recorded.

Backend:
  - TopologyRunSummary gains mission_phase_id + team_id + error
    fields. list_by_mission SELECT extended; other constructor
    (list_recent) explicitly passes None for the new fields.
  - GET /api/missions/{id}/runs response now carries all of the
    above so the frontend can attribute failures per phase.

Frontend:
  - MissionRunSummary type mirrors backend additions.
  - MissionCanvas fetches runs alongside mission on load +
    auto-refresh; indexes by mission_phase_id in a memoized Map.
  - Each phase card renders a per-run row: colored status pill
    (running / completed / failed), short run id, finished_at
    timestamp. For failed runs, a <details> collapses the error
    text — first line as summary, full 4kB in a monospace <pre> on
    expand.

Directly unblocks the "phase says Failed but there's no info to
debug" report. Both research and coding phases get this — the code
path is phase-kind-agnostic.
2026-07-21 12:32:39 -07:00
Omar Sobh 277189ea9b missions: phase_runner — actually execute mission phases
ci / rust (push) Successful in 3m37s
ci / e2e (push) Skipped
ci / publish (push) Successful in 4m7s
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
Root-cause fix for "we hit launch, waited overnight, nothing ran."
mission_orchestrator materialized teams + agents fine, but nothing
enqueued the actual work — mission_phases stayed 'pending' forever
and topology_runs count for the mission was 0.

New crates/cm-api/src/phase_runner.rs — background worker on 10s
poll that does three things:

  1. start_pending_phases — for every mission_phase with
     status='pending' AND parent mission.status='running' AND all
     lower-order phases already 'completed', enqueue one
     topology_runs row per team whose (mission_id, purpose) matches
     the phase kind:
       phase=research → teams with purpose='research'
       phase=coding   → teams with purpose='coding'
       phase=benchmark → teams with purpose='coding' (fallback)
       phase=security_scan → teams with purpose 'security' | 'coding'
     Each run gets a phase-kind-specific task text combining the
     mission title/description + a directive for that phase.
     Flips phase to 'running' after enqueue.
  2. close_finished_phases — SQL sweep that flips phases whose
     topology_runs are all terminal to 'completed' (or 'failed' if
     any run failed).
  3. close_finished_missions — same shape for missions whose phases
     are all terminal.

Spawned alongside task_card_worker in clawmates-server main.rs.

Ordering enforced by mission_phases.order_idx — a coding phase
doesn't fire until its research phase completes.

Idempotent: every state transition is guarded so double-firing on a
race is safe. When a mission has no matching teams for a phase (bad
wizard state), the phase stays pending and the runner logs a skip
rather than getting stuck in a fail loop.

Existing topology_worker picks up the queued runs and drives them
through the ZeroClaw executor as usual.
2026-07-21 09:17:18 -07:00