Commit Graph
5 Commits
Author SHA1 Message Date
Omar SobhandClaude Opus 5 f7f3dfe495 feat(fleet): GLM as a real microVM backend, and per-role models for claws
Three threads, all of which end at the same place: a mission whose verifier does
not share a model with the coder it reviews.

**GLM has a credential contract now.** `microvm_credential_for` returned one env
var name, which quietly assumed every provider reads its secret from the same
place Anthropic does. It returns a `Credential { source, target }` instead —
z.ai's key lives in the server's `ZAI_API_KEY` and Claude Code reads it as
`ANTHROPIC_AUTH_TOKEN`, and collapsing those two names is what forces a guess at
the other end. A wrong guess here sends one provider's credential to another
provider's endpoint.

`images/agent-glm` is the same CLI at the same pinned version as `agent-claude`
with `ANTHROPIC_BASE_URL` baked in. The split is deliberate: the ENDPOINT is a
property of the image, the CREDENTIAL is a property of the turn. That makes the
dangerous mix-up unrepresentable — a GLM VM cannot be handed an Anthropic
subscription token, and a claude VM cannot be pointed at z.ai. Asserted both
ways, because "the GLM VM must not carry CLAUDE_CODE_OAUTH_TOKEN" is the
property that costs a credential if it ever stops holding.

Kimi stays refused. `KIMI_API_KEY` is set and Moonshot serves an
Anthropic-compatible API, but I have not verified its base URL against the
running service, and this function is precisely where guessing a URL is
expensive. It becomes an arm the day someone measures it.

`api.z.ai` joins the node's default egress allow-list. A default that cannot
run the images we ship is a trap rather than a policy — the alternative is an
operator discovering it as a hung agent with no model access.

**Per-role models for claws** (migration 0071). `template_roles` had no model
column, so `mint_team_from_template` bound every role of every mission team to
one literal — a template whose whole point is an independent reviewer minted a
reviewer sharing a model with the coder. A role may now name its own; roles that
say nothing still take the mint's default, so every template written before this
behaves exactly as it did. The literal is now that default rather than a
hardcode.

**A harness scenario for the roster flow.** `verify-mission-delivery.sh roster`
runs the whole Slice 5 loop — planner proposes, human approves, mission runs —
and asserts the roster LANDED on the mission row rather than trusting the API's
answer. That distinction is not theoretical: the first live approval returned an
error while leaving the proposal marked approved.

Built and proven on tank ahead of the deploy: `clawmates/agent-glm:dev` reports
`2.1.223` and `BASE=https://api.z.ai/api/anthropic`, and
`fc-build-rootfs.sh … glm 8G` boots a VM from it that has git, can write
/mission, and answers `claude --version`.

533 tests pass, clippy clean. Migration 0071.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-06 17:14:53 -07:00
Omar SobhandClaude Opus 5 3b943df3c2 fix(templates): a template stopped accepting edits once it minted an agent
ci / gates (push) Successful in 6s
ci / rust (push) Failing after 9s
ci / frontend (push) Failing after 34s
ci / e2e (push) Skipped
ci / publish (push) Skipped
`upsert_builtin` replaced the role set with DELETE + reinsert. That looks
equivalent to an upsert and is not: `agent_template_link` carries a plain FK
on (template_id, role_slot), so the delete is rejected as soon as one agent
has been minted from the template, rolling back the whole transaction.

The failure mode was silent and self-targeting. The loader logs the error and
continues, so the on-disk TOML and the DB drifted apart — and only for the
templates someone had actually used. Running the smoke mission against
insight_research is what put it on the boot log:

    failed to load insight_research.toml: violates foreign key constraint
    "agent_template_link_template_id_role_slot_fkey"

which also means that template never received the skill-name fix.

- Upsert each role in place via ON CONFLICT (template_id, slot), the table's
  primary key.
- Prune only slots the TOML dropped, and skip a slot still referenced by a
  live agent with a log line. Keeping one stale role row is a smaller failure
  than discarding every edit to the template.
- Regression test drives the real sequence — upsert, mint an agent, link it,
  upsert again — and asserts both the prompt and skill edits land. Verified to
  fail without the fix with the same 23503 the server logged.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-31 20:26:38 -07:00
Omar Sobh f0dd0147f6 templates: 5 research team templates + category filtering
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m28s
ci / e2e (push) Skipped
ci / publish (push) Successful in 2m45s
Adds the operator's five categorized research team archetypes:

  1. codebase_research — code archeologist, architecture mapper,
     flow tracer, vault scribe. Produces Obsidian vault entries
     under Codebases/<repo>/ that make future missions faster.
  2. papers_research — domain scout, paper reader, library curator.
     Pulls arXiv / Semantic Scholar / conference proceedings, keeps
     a structured local library under Papers/<topic>/.
  3. insight_research — implementation tracker, novelty hunter,
     publication drafter. Bidirectional loop that spots
     publication-worthy novelty in our own implementations of
     external papers.
  4. continuous_research — signal harvester, ranker, digest writer.
     Standing sweep of RSS + arXiv daily + GitHub trending; produces
     a rolling ContinuousResearch/<date>/digest.md.
  5. continuous_improvement — brain inspector, improvement proposer,
     improvement evaluator. Standing self-audit that files level-up
     proposals for the operator to review + measures the outcome.

Each template ships with role system_prompts + brain_seeds authored
in the same voice as the existing backend/frontend/etc templates —
evidence-first, redlines called out, no invention.

Schema + code:
  - 0057_team_templates_category.sql — new column with
    CHECK (research | development | security | ops). Existing rows
    default to 'development'.
  - team_templates::UpsertBuiltin + TeamTemplate carry category
    (with default_category = 'development' fallback for
    Serialize/Deserialize compatibility).
  - team_template_loader reads `category = "..."` from the TOML;
    absent defaults to 'development' so old templates keep working.
  - Wizard step 3 filters:
      Research teams panel → templates.filter(t.category==='research')
      Development teams panel → templates.filter(t.category==='development')
    Operator can no longer accidentally pick backend as their
    "research team".

Test fixture updated with category="development".

The templates ship in the server image via the existing
`COPY templates /etc/clawmates/templates` line — no Dockerfile
change needed.
2026-07-21 04:54:52 -07:00
Omar Sobh 3f77370a6e fix: remove unused sqlx::Row imports in team_templates
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 26s
ci / rust (push) Successful in 4m23s
ci / e2e (push) Skipped
ci / publish (push) Successful in 2m36s
2026-07-19 12:45:37 -07:00
Omar SobhandClaude Opus 4.7 9ba5c06a1a slice 3: 6 team templates seeded from TOML recipes
ci / rust (push) Failing after 11s
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 28s
ci / e2e (push) Skipped
ci / publish (push) Skipped
Team templates are the canonical rosters + tool bundles that mint
concrete teams for a mission. Every builtin ships as a TOML recipe
under templates/teams/*.toml, loaded into the DB at server boot.

Migration 0048 adds:
  - team_templates    (id, key, name, stack, default_topology,
                       risk_profile, mcp_bundles, version, source,
                       workspace_id)
  - template_roles    (m2m: template_id + slot; system_prompt,
                       skills[], brain_seed)
  - teams gets template_id + template_version for level-up lineage

Ships 6 builtins:
  - rust_sdlc  — planner/coder/tester/reviewer/committer for Rust
  - backend    — api_designer/db_engineer/coder/tester/committer
                 (Postgres, DuckDB, graph DBs, wire protocols)
  - frontend   — designer/coder/tester/committer (React + Tailwind + ShadCN)
  - mobile     — designer/coder/tester/committer (Expo, RN, iOS, Android)
  - gpu        — arch_analyst/kernel_author/bench_engineer/coder/committer
                 (CUDA, Metal, ROCm from Rust)
  - threejs    — scene_designer/coder/shader_author/perf_engineer/
                 committer (three.js, WebGL, WebGPU)

Each role has a versioned system_prompt + skill list + brain_seed
markdown. Skills column is a name array today; Slice 3.5a promotes it
to a typed m2m join with the real skills catalog.

Server boot:
  - team_template_loader::load_builtins reads TOML from
    /etc/clawmates/templates/teams (container) or templates/teams (dev),
    upserts idempotently. Deterministic uuid per template key (sha256
    of a fixed namespace + key) so ids are stable across boots.
  - Dockerfile copies templates/ to /etc/clawmates/templates.

Read API:
  - GET /api/team-templates       — list all
  - GET /api/team-templates/{id}  — detail with roles

Wizard:
  - Step 3 rewired from a raw team_id text field to a template picker
    with "LLM auto-provision" as the default option + one card per
    builtin, showing stack, topology, risk profile, and description.
  - Mission create now passes team_template_id (not team_id) so phase
    execution knows which template to mint from.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-07-19 12:41:15 -07:00