Commit Graph
8 Commits
Author SHA1 Message Date
Omar SobhandClaude Opus 5 a02e0cba69 feat(missions): Continuous Research harvests at launch, and cards launch by clicking
deploy / test (push) Successful in 4m21s
deploy / build (push) Successful in 5m14s
The card shipped in e20b321 could not actually be used. Three things were
missing, each of which failed at a different distance from its cause.

**1. `default_team_template` was parsed and never read.** Every recipe declares
one; `WorkflowRecipe` carries the field; nothing consumed it. A mission created
from a card with no explicitly chosen team was rejected at LAUNCH with "no
team_id, no team_template_id, no config.phase_teams" — one step removed from the
real cause, which is that creation ignored the recipe. Create now resolves it
via `team_templates::get_by_key`, only when the caller named no team of any
kind, so an explicit choice still wins. A test asserts every shipped recipe
names a template that has a `templates/teams/<key>.toml`, because a mismatch
there produces an unlaunchable card.

**2. The harvest ran nowhere.** `harvest_for_mission` existed and nothing called
it. `on_launch` now runs it for `continuous_research` missions, before the
phases start, and threads the blob store through from `main` (the route already
had it on `AppState`; the scheduler needed it). Deliberately non-fatal: a
harvest that fails still starts the phases, because the phase is what reports
whether today was quiet or broken and those must stay distinguishable — but
never silent, so both outcomes log their counts.

**3. Nothing wrote the manifest.** `templates/teams/continuous_research.toml`
has pointed its reader role at `ContinuousResearch/<date>/harvest.jsonl` since it
was authored, and the file did not exist — agents aimed at a path nothing
produced. `run_to_vault` now writes it beside the notes and stages it, but only
for a mission-attributed run. `Harvest` carries the shelved `Paper`s to build
it; re-parsing the notes we had just written would have been a parse of our own
output and one more place for the two to drift.

Also: the blob root. `storage.data_dir` defaults to "./data" and the container's
cwd is `/`, so the server tried to create `/data` as uid 65532 and EVERY shelve
failed with "storage io: Permission denied". The image now creates
/var/lib/clawmates-blobs owned by 65532 so a mounted volume inherits it rather
than arriving root:root. Kept off /var/lib/clawmates-missions on purpose: that
tree is swept, and a paper shelved there would be deleted out from under its own
catalogue note.

Proven end to end on a real mission: 15 candidates, 2 already held, 13 shelved,
0 failed; branch auto-merged as additive-only; manifest on vault `main` with
every documented key. The "already held" counts are the seen-set deduping across
topics within a single run, which is the behaviour the whole design exists for.

The project brief now comes from the mission description — `phase_task_text`
already places it under BRIEF verbatim, so no new field was needed.

346 tests pass.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-17 15:05:37 -07:00
Omar SobhandClaude Opus 5 e20b321055 feat(missions): Continuous Research is a mission type, not just a team checkbox
deploy / test (push) Successful in 4m6s
deploy / build (push) Successful in 5m32s
`templates/teams/continuous_research.toml` has existed with three well-written
roles since it was authored, but no workflow recipe pointed at it — every
recipe in templates/workflows/ defaults `default_team_template = "rust_sdlc"`.
So the only way to reach it was as a checkbox under Advanced. It is now a
Step-1 card: the registry loads it at boot and `GET /api/workflows` serves it,
with no frontend change (MissionWizard renders whatever the endpoint returns).

Both phases are kind `research`, deliberately, rather than new `read`/`script`
kinds. An unrecognised kind falls through `purposes_for`'s `_ => ["mission"]`
and is absent from `PRODUCING_KINDS`, so it would get the generic directive AND
be exempt from the empty-delivery rule — a phase that produces nothing and
still passes. That is the shape this codebase keeps paying for; two `research`
phases differentiated by `task` keep both guards.

`commit_policy = "always"`, not `on_green_tests`: the vault is prose with no
suite, so a test gate would find nothing to run and land every branch `-wip`.

The harvest is NOT an agent phase. `continuous_research.rs` calls the existing
`library::run_to_vault` — arXiv search, seen-set check, PDF shelf, vault note,
attributed by `mission_id` — because that path is deterministic, takes seconds,
and owns the `corpus_items` seen-set that is the whole reason a recurring
mission knows what it already covered. An agent redoing it would be slower and
would lose that.

The manifest path is not invented either: the team template has told
`signal_harvester` to write `ContinuousResearch/<date>/harvest.jsonl` all along.
This makes the code produce what the prompt already promised, and a test pins
the path and every documented key so the two cannot drift into an agent reading
a file nothing writes.

DEFAULT_CORPUS / DEFAULT_VAULT_URL exported rather than duplicated, so the
route and the launch hook cannot disagree about which vault.

344 tests pass.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-17 14:18:35 -07:00
Omar SobhandClaude Opus 5 b5032a732a fix(phase_runner): collect the agent's work BEFORE judging it
deploy / test (push) Successful in 4m24s
deploy / build (push) Successful in 5m15s
`Sandbox::for_mission` builds the judge's verification copy from the HOST
checkout. In copy mode the agents write inside the container, and their work
only reached the host when `sync_out` ran — in the capture sweep, AFTER the
phase closed. So every phase was judged against a tree that did not yet contain
the pass being judged, and the judge truthfully reported nothing there.

Mission 01a00cfa is the proof. Research pass 2 wrote a 434-line
IMPLEMENTATION_BRIEF.md, `cargo test` passed, and it was pushed to a clean
branch (clawmates/mission-01a00cfa-c69f39fd-i2 at 563cdd21). Its verdict:

    failed after 2 pass(es) — met=false — research/IMPLEMENTATION_BRIEF.md
    does not exist anywhere

logged one line BEFORE `captured (+434/-0 across 1 file(s))`. A phase that
succeeded was failed because the evidence had not been collected yet.

This hid because it only bites a phase judged on its OWN pass. The v2 coding
verdict cited real commits (339a5bd, 167671f) — research had already synced
that work to the host in an earlier phase.

`evaluate_finished_phases` now runs `sync_out` first, and on failure leaves the
phase `evaluating` for the next sweep rather than recording a verdict nobody
could stand behind — the same policy the capture sweep already applies, for the
same reason. microVM keeps its carve-out: `microvm_executor` collects out of
the guest over this same path before the VM is destroyed.

Research goes to 3 passes. On 01a00cfa it got no real attempts out of two: one
spent on a fabricated commit claim the judge correctly rejected, one on this
bug.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-16 17:09:05 -07:00
Omar SobhandClaude Opus 5 a582dea4fc feat(workflow): research plans, coding builds — the split was a fiction
deploy / test (push) Successful in 4m1s
deploy / build (push) Successful in 1m36s
`rust_sdlc` gives the research team coding roles and a writable /mission/repo,
so research implemented what it found and the coding phase then opened a clean
tree, produced +0/-0 and failed on the empty-delivery rule. Mission 01a00c57
ended exactly that way: research shipped both INT items itself (+276/-57),
coding delivered nothing.

Worse than the wasted phase is WHERE the code landed. Research ran under a gate
that does not check tests, so its two source changes reached a branch with
`tests_status: null` — never compiled by the gate, never run. Keeping
implementation in the coding phase is what puts it behind `on_green_tests`.

Research now carries a `task` that scopes it to the brief and says plainly that
editing crates/ is not its job this phase, plus a `done_when` describing what
the brief must CONTAIN. The no-source-edits constraint deliberately lives in
the prose and NOT in `done_when`: "and nothing else" phrasings measurably make
a judge invent requirements it was never given.

Coding gets the counterpart `task`: implement the brief's items, one commit
each, tests green. Stated explicitly because a phase that finds a clean tree
and no instructions has historically written a REPORT about the work instead of
doing it — four documentation commits and one implementation, on the run that
became the haiku baseline branch.

Research also gets `commit_policy = "on_green_tests"` as a safety net, so
source it writes anyway still has to pass the suite.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-16 16:48:05 -07:00
Omar SobhandClaude Opus 5 53da4d7e6d fix(runtime): a mission could not build the repo it was given
deploy / test (push) Successful in 4m20s
deploy / build (push) Successful in 5m21s
`clawmates-runtime` shipped with `gcc` and `make` but no `cmake`, no `g++` and
no `python3-dev`. Measured on clawhdf5, three probes:

  no cmake        → "is `cmake` not installed?"        exit 101 after 13s
  no python3-dev  → "cannot find -lpython3.11"          exit 101 at link
  with both       → cargo test PASSES                   exit 0 after 69s

This is not only the delivery gate. The AGENTS run in this image, so a coding
phase was writing Rust it had no way to compile or test — which reframes the
last run's 11 agent commits as unverifiable by construction.

`images/agent-toolchain/Dockerfile` (the microVM path) has had `cmake
build-essential` all along, and its own header warns about precisely this:
"if `cargo` is present in one image and absent in another, the same mission
passes or fails depending on which backend it landed on, and nothing says why."
Both images now install the same set — it was missing `python3-dev` too.

`images/runtime-toolchain.Dockerfile` is a thin local overlay so the laptop can
run today without recompiling zeroclaw from the fork; it is meant to be deleted
once a runtime image built from the corrected deploy/ Dockerfile is published.

Also: a build failure is no longer reported as a red suite. Both are cargo exit
101, and `verify_tests` mapped every non-zero to `Failed(code)` — so a missing
toolchain was recorded as the USER's tests failing. It now returns
`CouldNotRun` with the reason when the output shows a compile or link failure.
Deliberately narrow: a failing `assert!` still reads as red, because letting
broken code past `on_green_tests` is the expensive direction to be wrong in.
Both directions are pinned by tests built from today's two real samples.

And the coding phase finally has a loop: `research_and_code.toml` declared
`loop = "until_no_more_int_items"`, which `phase_config.rs` lists as
DECLARED_BUT_UNREAD. Iteration is driven by `max_iterations` + `done_when`, and
with `max_iterations = 1` and no `done_when` the phase ran ONCE and was never
judged — reporting `completed` whatever it produced. Now 3 passes against a
stated goal, wording per the measured rule (say what the tree must CONTAIN).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-16 11:02:07 -07:00
Omar SobhandClaude Opus 5 49bcf53b84 feat(missions): wire the workflow registry so phase config reaches the database
workflow_registry.rs had zero call sites -- lib.rs declared the module and
nothing ever called load() or get(). So templates/workflows/*.toml was never
read, and because the client's TEMPLATE_PRESETS carries only {kind, order_idx}
with no config, PhaseSpec.config defaulted to Value::Null and every
wizard-created mission stored a null mission_phases.config.

Every per-phase setting was therefore inert. `loop = "until_no_more_int_items"`
and `commit_policy = "on_green_tests"` described a scheduler that does not
exist AND had no path to the database. benchmark_runner and security_scan
already read phase_config(); they were reading from null.

- Mission create derives phases from the recipe when none are sent, and
  backfills config per phase (matched on kind+order_idx, then kind) when the
  caller sends shape without config. An explicit config always wins.
- phases_for_create takes Option<&WorkflowRecipe> rather than reaching for the
  global, because the registry resolves its directory relative to the process
  cwd -- which under cargo test is the crate root, not the repo root.
- GET /api/workflows serves the catalog; the wizard fetches it and falls back
  to TEMPLATE_PRESETS. Adding a TOML now adds a template with no FE change.
- load() runs at boot so a malformed recipe appears in the boot log instead of
  silently producing a mission with no phase config.

Also fixes a latent bug in all five recipes: `default_team_template` was
written below the first [[phases]] block, and TOML scopes a bare key after a
table header INTO that table -- so it parsed as
phases[last].config.default_team_template and the real field was always None.
Invisible while the registry was dead code. Moved above the phases, with a
test asserting it neither returns None nor leaks into a phase config.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-30 12:43:32 -07:00
Omar SobhandClaude Opus 5 285d0c82f2 chore: delete dead scaffolding and stop fabricating claw capability cards
Tier 0 of the prompt-ablation pass -- subtraction only, none of this
reached a model.

- cm-brain: drop ClawBrain::export_markdown (zero callers).
- workflows: drop the `task_preamble` keys. No Rust code ever read them --
  WorkflowPhase.config is an opaque serde_json::Value -- so the comment
  calling the preamble "the belt, the skill the suspenders" described a belt
  that was never implemented. (`commit_policy` is unread for the same reason;
  left in place as documentation pending a decision.)
- mcp_door: derive the unknown-tool error from EXPOSED_TOOLS. The literal had
  drifted to naming one of the three tools the door exposes.
- Dashboard.tsx: drop TEAM_TEMPLATES/COMPANY_TEMPLATES, defined and never
  referenced, and disconnected from the real templates/teams/*.toml.

The substantive one: GET /api/claws/{id}/compartments returned hardcoded
strings for tools/capabilities/safety, identical for every claw. Every card
read "Network: none" and "Shell . blocked" regardless of the claw's real
risk_profile -- which is the actual capability boundary, so the card was
most wrong exactly where it mattered, on a coding_readwrite claw that does
have shell. Now derived from the claw's effective risk_profile (its team's
setting, else the same role-derived default the provisioner applies), with
the allowlists mirroring [risk_profiles.*] in the runtime config.

Note: cm-topology/src/heuristics.rs was slated for deletion here as unused.
It is not -- routes/topology.rs:43 serves it and p0_endpoints.rs:302 asserts
it. Left alone.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-30 10:44:33 -07:00
Omar SobhandClaude Opus 4.7 565f6cae65 slice 4: 5 workflow templates as TOML recipes + mission-launch orchestrator
ci / gates (push) Successful in 4s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m20s
ci / e2e (push) Skipped
ci / publish (push) Successful in 3m50s
Two things land together:

1. Ships the 5 workflow recipes as TOML files under
   templates/workflows/*.toml:
     - research_only          (hub_spoke research → MD + PDF)
     - research_and_code      (research → coding loop until INT-XX drained)
     - security_hardening     (scan → research patches → coding with
                               reviewer approval + full MCP bundle)
     - refactor               (single-pass coding with dep audit
                               preamble + before/after benchmarks)
     - benchmark              (author + baseline benchmarks per stack)
   Each declares phases[], per-phase config, default_team_template.
   Loaded read-only into an in-memory registry (workflow_registry)
   via OnceLock — no DB row per recipe.

2. Ships the mission-launch orchestrator that closes the loop from
   Slice 3.5d's mechanics. When a mission transitions draft→running,
   `mission_orchestrator::on_launch`:
     - Reads mission.team_template_id (skips if unset)
     - Loads the team template detail (roles + skills bindings)
     - Builds a topology graph from role slots via cm_topology::build
     - Inserts the teams row + stamps template_id/version/risk_profile/mcp_bundles
     - For each role: agent insert, model binding, runtime provision
       (opt-in via RuntimeProvisioner::from_env), brain_seed::ingest
       (Slice 3.5d), agent_template_link::upsert (Slice 3.5d),
       team_members bind, audit trail
     - UPDATE missions SET team_id = ...
   Wired into routes::missions::set_status when prior.status='draft'
   and new='running'. Failures log + are non-fatal (mission still
   flips to running so the user can inspect + retry).

With this, Slice 3.5d's brain-seed + link machinery actually gets
populated, and the MCP skills server's template-defaults-merge path
(Slice 3.5b/d) starts serving real bindings to real agents.

Follow-ups (Slice 5-8):
  - Task-card parser watches run events for TASK/COMPLETED markers
    → mission_tasks rows
  - PDF renderer worker turns MD artifacts into PDFs
  - Before/after benchmark runner honors phases[].config.benchmark
  - Security scan MCP bundle exposes cargo-audit/gitleaks/trivy/semgrep
  - Level-up endpoints diff learned-vs-seeded via agent_template_link

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-07-19 14:34:26 -07:00