diff --git a/CHANGELOG.md b/CHANGELOG.md index 23d34630..d660fde2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +## [0.11.0] - 2026-06-24 + ### Added - **MegaTask — describe several tasks in one intake chat and ship them as one sequenced batch.** When the CEO wants several pieces of work at once — even across projects that don't share a codebase (e.g. a SaaS app, its open-source core engine, and a framework adapter) — the intake modal now offers a third scope, **MegaTask**, beside Single cell and Board-led. You pick the repos it spans; the intake agent reads them all and proposes the whole batch in one hand-off (the new `propose_batch` tool), one draft per task, each carrying its own project plus a collision surface (which files it touches, whether it adds a migration, whether it edits a widely-shared component). A deterministic analyzer (`SequencingService`) turns those surfaces into conflict-free **waves** — file-overlap and migration-adding tasks are serialized, a shared-surface edit runs after what it overlaps, independent tasks run in parallel — and the Board reviews the batch once. On confirm RoboCo creates a branchless **umbrella** task (the Main PM's coordination + board-review + CEO-approve unit) over N **root-subtasks**, each a real coordination root with its own project, branch, and PR, wired with the analyzer's dependencies so the existing dependency-gate dispatches the waves in order. The umbrella assembles no PR of its own, is exempt from the branch gate, and completes only when every root-subtask is terminal (then it escalates to the CEO). On the Board route the root-subtasks are held until the umbrella is approved, then released. Surfaced as a core capability — no feature flag — branded "MegaTask" across the panel, prompts, and docs; internal names stay technical (`batch_id`, `SequencingService`). Adds `tasks.batch_id` + the three collision-surface columns (migration 046), `confirm_live_batch` + `POST /prompter/live/{session}/confirm-batch`, multi-project intake spawn (`project_ids`), the `propose_batch` tool on both intake runtimes (Claude SDK driver + grok CLI server), and the panel's MegaTask scope + Review-MegaTask card. diff --git a/CLAUDE.md b/CLAUDE.md index ad80ccbe..4e45c5c1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -118,6 +118,8 @@ Note: the Next.js control panel now lives at `roboco/panel/` inside this repo (n On a Python workspace, `WorkspaceService` runs `uv sync --extra dev` (not plain `uv sync`) so the clone's `.venv` carries the full gate toolchain (ruff/mypy/xenon/pytest) — the lint/type/complexity tools live in the `dev` **extra**, which plain `uv sync` skips. Without it an agent's `make quality` fails on `ruff: command not found` and the agent can't gate its own work. +Because the clone is shared across a dev's tasks, a **fresh claim** git-resets the workspace to a clean tree (`git reset --hard`) before checking out the new task's branch — discarding abandoned uncommitted cruft from a finished task while preserving all commits and the gitignored `.venv`. A resume short-circuits before this, so committed work is never reset. + ## Git Workflow ### Branch Naming Convention @@ -155,6 +157,10 @@ When a developer claims a task, a **WorkSession** is created that tracks: - PR number/URL when created - Merge status and who merged +A task has at most **one active WorkSession**: re-claiming a task (pool release, reaper unclaim, escalation redirect) supersedes any prior agent's stale active session, enforced both at the service layer and by a DB partial-unique index (migration 047). Without it, duplicate active sessions made the one-row active lookup raise and crashed the claim/plan flow into a respawn loop. + +A developer's clone is shared across all their tasks, so push and PR-head operate on the task's **recorded branch by name**, independent of the clone's current checkout — fixing the `BRANCH_MISMATCH` / "No commits between" failures when the clone was parked on a later task's branch. A missing local task-branch ref is first recovered from `origin/` before the push-by-name. + ### Git Credentials Git authentication is managed **per-project** through encrypted GitHub PATs: @@ -243,6 +249,8 @@ All status transitions are validated through the enforcement layer. Key restrict **Unclaim Operation**: Agents can release claimed tasks back to the pool using `unclaim()`. This transitions `claimed` → `pending` and optionally reassigns to another agent. +**Board never owns a coordination root**: a Board role (Product Owner / Head of Marketing) is never assigned a Main-PM coordination root (delivery root or MegaTask root-subtask) via escalation or reassignment — Board roles have no `unblock` verb, so such a hand-off would deadlock. The transition is diverted to the pool for a role-matched Main-PM reclaim. + ### Git Integration Requirements All tasks follow git workflow. PR is created BEFORE QA review (not after) so QA can review the real PR diff on GitHub and downstream PM/CEO approval chain off a PR that already exists: @@ -309,6 +317,8 @@ commits: list[CommitRef] # All commits made for this task The Auditor has silent read access to ALL channels. +Agent learnings (`note` scope='learning') broadcast as knowledge-share notifications only to other **agents** — the human / human-driven roles (CEO, prompter, secretary) are excluded, since agent knowledge-sharing is noise in a human's inbox. + ## Key Principles 1. **Everything is a task** - All work is tracked and documented @@ -343,7 +353,7 @@ Each agent gets a **spawn manifest** at `/app/tool-manifest.json` listing the ve | prompter | (none beyond `i_am_idle` — not a delivery-lifecycle role; intake interviewer, human-only) | | secretary | (none beyond `i_am_idle` — human-only chief-of-staff; reads company state + runs gated CEO directives) | -Content tools (do_server) — most roles: `commit`, `note`, `say`, `dm`, `evidence`. Auditor is restricted to `note` (scope=reflect) + `evidence`. The `pr_reviewer` posts its change-request on the PR itself (no agent comms). The `prompter` (intake) and `secretary` are restricted to `note` + `evidence` — human-only, no `say`/`dm`/`notify`. +Content tools (do_server) — most roles: `commit`, `note`, `say`, `dm`, `evidence`. Auditor is restricted to `note` (scope=reflect) + `evidence`. The `pr_reviewer` posts its change-request on the PR itself (no agent comms). The `prompter` (intake) and `secretary` are restricted to `note` + `evidence` — human-only, no `say`/`dm`/`notify`. The `note`/journal write returns as soon as the entry is persisted; RAG indexing (Ollama embedding) runs fire-and-forget, so the tool no longer times out under concurrent load. ### MCP servers running per agent container @@ -359,7 +369,7 @@ Every verb returns a standardized **Envelope**: - ok: `{status, task_id, next, evidence?, context_briefing}` - error: `{error, message, remediate, missing}` -The `next` field tells the agent what to call next; the `remediate` field on errors tells them exactly how to fix and retry. Agents should not guess state — trust the response. +The `next` field tells the agent what to call next; the `remediate` field on errors tells them exactly how to fix and retry. Agents should not guess state — trust the response. The verb runner re-checks the task after each composed atomic action and, on a concurrent mid-verb state change, fails fast with a clean `INVALID_STATE` (re-fetch + re-issue) rather than crashing on a `None` dereference. ## Agent Providers @@ -373,7 +383,7 @@ Agent backends are pluggable. `roboco/llm/providers/` defines an `AgentProvider` **Self-healing CI loop (default-off).** RoboCo can watch its own repository's CI (a single named workflow) and, on a detected regression, open a fix task that is held out of dispatch until the CEO approves it (it terminates at `awaiting_ceo_approval`), then dispatch it through the normal delivery flow. It is dormant by default and armed by `ROBOCO_SELF_HEAL_ENABLED` plus a second opt-in `ROBOCO_SELF_HEAL_ORIGINATE_ENABLED`; origination is bounded by `ROBOCO_SELF_HEAL_MAX_OPEN_TASKS` / `_MAX_PER_CYCLE` so it can't flood the backlog. It never auto-merges or self-deploys (`roboco/services/self_heal_engine.py`). -**Feature flags / company-in-a-box.** Env-gated, default-off subsystems toggle from the panel's Settings → Feature Flags card (`panel/src/components/settings/feature-flags-card.tsx`) instead of hand-editing env: web research (`ROBOCO_RESEARCH_ENABLED`), the strategy engine (`ROBOCO_STRATEGY_ENGINE_ENABLED`), pitch provisioning (`ROBOCO_PROVISIONING_*`), external / internal PR review, the agent-runtime toolchain match (`ROBOCO_TOOLCHAIN_MATCH_ENABLED`), the architectural-conventions standard (`ROBOCO_CONVENTIONS_ENABLED`), and the self-heal flags above. A toggle persists in the settings store and takes effect on the next backend restart; an unset flag falls back to its environment / config default. +**Feature flags / company-in-a-box.** Env-gated, default-off subsystems toggle from the panel's Settings → Feature Flags card (`panel/src/components/settings/feature-flags-card.tsx`) instead of hand-editing env: web research (`ROBOCO_RESEARCH_ENABLED`), the strategy engine (`ROBOCO_STRATEGY_ENGINE_ENABLED`), pitch provisioning (`ROBOCO_PROVISIONING_*`), external / internal PR review, the agent-runtime toolchain match (`ROBOCO_TOOLCHAIN_MATCH_ENABLED`), the architectural-conventions standard (`ROBOCO_CONVENTIONS_ENABLED`), gateway-health recovery (`ROBOCO_GATEWAY_HEALTH_ENABLED`), and the self-heal flags above. A toggle persists in the settings store and takes effect on the next backend restart; an unset flag falls back to its environment / config default. ## Architectural Conventions Standard @@ -491,7 +501,7 @@ Server-side events reach these sockets through `roboco/api/websocket_bridge.py`, ### Rate limiting & usage - **Provider rate limits** are tracked in Redis (`RateLimitStateTracker`, `roboco/services/gateway/`). On a provider 429 an agent calls `i_am_blocked(reason="rate_limited")`; the spawn gate then **queues** (never drops) further work for that provider, and a background probe-and-resume loop in the orchestrator clears the limit and revives parked agents when it lifts. -- **Provider overloads** reuse the same park-and-probe break. A persistent model-API overload (HTTP 529 / 500 / 503 — the SDK already retries transient ones) parks the provider exactly like a 429 instead of crash-retrying the agent straight back into the overload and burning tokens; the overload is detected orchestrator-side from the dead container's log markers, and the background loop revives the parked work when it recovers. Gated by `ROBOCO_OVERLOAD_BREAK_ENABLED` (default-on). +- **Provider overloads** reuse the same park-and-probe break. A persistent model-API overload (HTTP 529 / 500 / 503 — the SDK already retries transient ones) parks the provider exactly like a 429 instead of crash-retrying the agent straight back into the overload and burning tokens; the overload is detected orchestrator-side from the dead container's log markers, and the background loop revives the parked work when it recovers. The same break also catches the **Claude session-limit** 429 (the org's 5-hour usage window): an agent exiting with a 0-token session-limit rejection parks the provider and is auto-revived when the window resets, instead of fleet-wide crash-respawning straight back into the limit. Gated by `ROBOCO_OVERLOAD_BREAK_ENABLED` (default-on). - **Gateway-health recovery** closes a blind spot in the stale-claim reaper: the heartbeat is bumped only by gateway verbs, so a broken-but-alive agent (a corrupted `/app/.venv` so no gateway tool imports) goes heartbeat-stale yet keeps its container up, and the reaper's live-skip would protect it forever. On a stale-heartbeat live container the reaper now probes the gateway out-of-band (`_probe_gateway_health` → `docker exec` the gateway venv imports) and, once broken past `ROBOCO_GATEWAY_HEALTH_GRACE_SECONDS` (a transient probe miss is tolerated), kills + evicts it (`_maybe_recover_broken_gateway`) so it falls through to release + respawn; healthy or inconclusive probes spare it. Gated by `ROBOCO_GATEWAY_HEALTH_ENABLED` (default-on). It is the third leg beside the shipped bash-guard `/app` block (prevents the self-corruption) and the reaper Docker-liveness fallback (stops over-reaping live containers). - **PM coordinator concurrency.** A Main / Cell PM plans and delegates many root tasks in parallel — the actual work then runs in the delegated children/cells, not in the PM's own hands. The claim-time concurrency guards that keep a *developer* to one task at a time (`already_active` / `paused`, in `roboco/services/gateway/claim_guards.py`) are therefore **skipped for the coordinator PM roles** (`_COORDINATOR_ROLES = {main_pm, cell_pm}`, consulted in `_run_claim_guards`); only a genuine upstream **sequence dependency** (`unmet_dependency`, which parks the task back to `pending`) holds a PM's root back. Without this a single PM that claimed one root could never plan a second — it thrashed between its claimed roots and respawned forever, burning tokens for zero progress (the live `i_am_idle`-auto-paused-umbrella deadlock). The `paused` guard also excludes the target task itself, so a PM re-entering its own paused umbrella never self-blocks. - **Token usage** is captured per agent session from the Claude Code transcript via the SDK server's `/usage/sync` (hook → orchestrator finalize → `agent_spawn_sessions` → `daily_usage_rollups` → dashboard). Cost uses provider-aware pricing in `roboco/billing/pricing.py` (Anthropic priced; local/Ollama intentionally `$0`). The token sweep also publishes `USAGE_SNAPSHOT` to `/ws/system`, so the dashboard's "Token Usage & Cost" panel updates live and falls back to HTTP polling when the stream is down. diff --git a/README.md b/README.md index 3f904614..338e216c 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ Choose the registry and version with two env vars (defaults shown): ```bash ROBOCO_REGISTRY=ghcr.io/rennf93 # or docker.io/renzof93 -ROBOCO_VERSION=latest # or a pinned release, e.g. 0.10.0 +ROBOCO_VERSION=latest # or a pinned release, e.g. 0.11.0 ``` The orchestrator spawns the matching pre-built agent images on demand — no build toolchain or source compile on your host. diff --git a/agents/prompts/roles/board.md b/agents/prompts/roles/board.md index 9356f3c8..a1cb8f71 100644 --- a/agents/prompts/roles/board.md +++ b/agents/prompts/roles/board.md @@ -10,6 +10,8 @@ If you find yourself reaching for `Bash git`, `Edit`, or any execution tool, sto When the briefing carries `company_goals`, that charter is your reference for triage and escalation: prioritize, accept, and reject work by how well it advances the CEO's stated objectives and respects the charter's constraints. +**You cannot resolve blockers — you have NO `unblock` verb.** Only PMs can unblock. Your only outward verbs are triage, notify, and (PO/HoM) `escalate_to_ceo` — nothing that unblocks. So if a *blocked* task is ever assigned to you as its owner, that is a mis-assignment, not your work to do — and sitting on it does nothing but respawn-loop you. Move it off your seat immediately: PO/HoM call `escalate_to_ceo(task_id, reason='blocked task mis-assigned to Board — needs a PM to unblock')` so the CEO routes it to a PM who can unblock; the Auditor (no escalation verb) records it with `note(scope='reflect', text='blocked task mis-assigned to Board — CEO should route to a PM', ...)`. Never quietly hold a blocked task. + ## Inputs you start with - Your `task_id` (if you were spawned to triage a specific task) and `agent_id` are pre-baked. diff --git a/agents/prompts/roles/cell_pm.md b/agents/prompts/roles/cell_pm.md index cdc168d5..7f1bbb73 100644 --- a/agents/prompts/roles/cell_pm.md +++ b/agents/prompts/roles/cell_pm.md @@ -164,6 +164,10 @@ The PM journal is what makes the cell legible to Main PM and CEO. Skipping entri 6. ✅ `note(scope='decision', task_id=...)` written — submit-up rationale (gateway-required). 7. ✅ `notes` argument to `submit_up` >= 20 chars (gateway-enforced). +## When a branch is behind its base + +A task branch is brought current with its base automatically when it is CLAIMED — neither you nor your devs have a rebase, pull, or merge verb. If a dev reports (or `roboco_git_status` shows) the cell branch behind its base at `submit_up` time, do NOT create a "rebase the branch" subtask and do NOT improvise git surgery — bringing a branch current is a platform/PM action, never a subtask. Escalate it up the same way a dev would: `escalate_up(task_id, reason='branch behind base — needs rebase')` so a role that can actually bring it current handles it. + ## Channels **Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. diff --git a/agents/prompts/roles/developer.md b/agents/prompts/roles/developer.md index 495d8d15..f30d4e2e 100644 --- a/agents/prompts/roles/developer.md +++ b/agents/prompts/roles/developer.md @@ -10,6 +10,7 @@ You write code; you do not coordinate. If you find yourself thinking "let me als - Your `task_id` and `agent_id` are pre-baked into the gateway session — every verb knows who you are. - **Your workspace — the ONLY directory you operate in.** The path convention is exactly `/data/workspaces////`. Concretely, with your `project_slug` from the task, your team, and your own slug, that is e.g. `/data/workspaces/roboco/backend/be-dev-1/`. Your container's working directory is already set there on spawn — you do not need to `cd` or hunt for it. **Do NOT probe for it.** Do not `ls /`, `ls /data`, `find / -name ...`, or guess at sibling paths. Your clone, your branch, and every file you may edit are under that one directory. Stay inside your own cell workspace; another cell's or another agent's workspace is off-limits (and Edit/Write are permission-locked to yours anyway). +- **Your workspace is one persistent clone, shared across all your tasks.** On a fresh claim it is git-reset to a clean tree before your new branch is checked out — so abandoned uncommitted changes from a finished task are discarded (real commits are preserved), and you start clean every time. You never need to clean it yourself. `push`/`open_pr` operate on YOUR task's branch BY NAME regardless of which branch the shared clone is currently checked out on — trust the verb; you do NOT need to `git checkout` back to your branch first. - **Secrets / config values:** there are none for you to find in the environment. `env`/`printenv` is DENIED and the bash-guard hook will block it — running it wastes budget and trips the guard, it does not reveal anything. Reading credential files (`.git/config`, `.netrc`, `.git-credentials`) is denied too. If your task genuinely needs a secret value (an API key, a test fixture token, a connection string), the sanctioned path is: that value must be provided to you **in the task description / acceptance criteria**. If it is not there and the task can't proceed without it, `i_am_blocked(reason='need value', blocker_type='question', what_needed='')` and your PM supplies it — you never go looking for it in the container. - Your verb manifest is loaded — MCP verbs (`mcp__roboco-flow__*`, `mcp__roboco-do__*`) are already registered. Built-in tools (`Edit`, `Write`, `Read`, `Bash`, etc.) are loaded and ready — use them directly. Do NOT call `ToolSearch` (it does not gate built-in tools and is not available here). Always make file changes with `Edit`/`Write`; never rewrite a whole file via shell redirection. - Acceptance criteria, dev notes, parent context: call `evidence(task_id)` to fetch the task body and PR diff (if any). @@ -119,6 +120,10 @@ Beyond placement and hygiene, the Architectural Conventions Standard also enforc If a finding is a genuine false positive, clear it by committing a `waiver` in `.roboco/conventions.yml` in your branch — accountable and reviewed in the PR. Do not silence it any other way. +## When your branch is behind its base + +Your task branch is brought current with its base automatically when you CLAIM it — you have NO rebase, pull, or merge verb at the agent layer. If the base moves ahead while you work and `roboco_git_status` shows your branch behind at submit time, do NOT create a task to "rebase" a branch and do NOT improvise git surgery (`Bash git rebase`/`merge`/`pull` are denied and are never your job). Escalate instead: `i_am_blocked(reason='branch behind base — needs rebase', blocker_type='internal')` and let the platform/PM bring it current. (Unclaim + re-claim rebuilds the branch fresh from the current base, but only do that on explicit instruction — it discards any uncommitted-only work.) + ## Channels **Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. diff --git a/agents/prompts/roles/main_pm.md b/agents/prompts/roles/main_pm.md index 20ef5736..d646d727 100644 --- a/agents/prompts/roles/main_pm.md +++ b/agents/prompts/roles/main_pm.md @@ -165,6 +165,10 @@ You are the integration layer between Cells and CEO. Your journal is what tells 6. ✅ `note(scope='decision', task_id=root_id)` written — complete-rationale (gateway-required). 7. ✅ `notes` argument to `complete` >= 20 chars (gateway-enforced). +## When a branch is behind its base + +A task branch is brought current with its base automatically when it is CLAIMED — there is no rebase, pull, or merge verb anywhere at the agent layer. If `roboco_git_status` shows a cell branch or your root branch behind its base when you go to `complete` it, do NOT create a subtask to "rebase" the branch and do NOT improvise git surgery — bringing a branch current is a platform action, never a unit of work you decompose and delegate. Escalate it: `escalate_up(task_id, reason='branch behind base — needs rebase')` so a role that can actually bring it current handles it. A "rebase subtask" is always a mistake. + ## Channels **Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. diff --git a/docs/deploy/deployment.md b/docs/deploy/deployment.md index a8c93328..7e3b62b6 100644 --- a/docs/deploy/deployment.md +++ b/docs/deploy/deployment.md @@ -30,7 +30,7 @@ Two variables choose what you pull (defaults shown): ```bash ROBOCO_REGISTRY=ghcr.io/rennf93 # or docker.io/renzof93 -ROBOCO_VERSION=latest # or a pinned release, e.g. 0.10.0 +ROBOCO_VERSION=latest # or a pinned release, e.g. 0.11.0 ``` The orchestrator then spawns the **matching** pre-built agent images on demand (it reads `ROBOCO_AGENT_IMAGE_REGISTRY` / `ROBOCO_AGENT_IMAGE_TAG`, which the registry compose wires to the same registry and version). Pin `ROBOCO_VERSION` to a release tag in production so an upstream `latest` push can't silently change your fleet. diff --git a/docs/models/resilience.md b/docs/models/resilience.md index cf124a09..e9eb0a6f 100644 --- a/docs/models/resilience.md +++ b/docs/models/resilience.md @@ -28,6 +28,7 @@ When a provider pushes back, retrying immediately just burns tokens against a wa - A **rate limit (HTTP 429)** parks the provider. The agent reports `i_am_blocked(reason="rate_limited")`, the spawn gate stops launching new work for that provider, and a background loop probes for recovery. - A **persistent overload (HTTP 529 / 500 / 503)** parks the same way. The model SDK already retries genuinely transient blips; a *persistent* overload is detected from the dead container's log markers and parked rather than crash-retried straight back into the overload. This is gated by `ROBOCO_OVERLOAD_BREAK_ENABLED`, which is **on by default**. +- A **Claude session limit** — the org's rolling 5-hour usage window — parks the same way. Hitting it terminates the agent container with a 429 before the agent can report it, so RoboCo detects it from the dead container's exit (like an overload) and parks the provider instead of crash-respawning the whole fleet straight back into the limit; the queued work auto-revives when the window resets. Also covered by `ROBOCO_OVERLOAD_BREAK_ENABLED`. The crucial property: **work is queued, never dropped.** Parked tasks wait; the background probe-and-resume loop requires a real `2xx` from the provider before it lifts the park and revives the parked agents. When the provider recovers, the queued work flows again on its own — you don't restart anything. diff --git a/docs/rag/troubleshooting/blocked-tools.md b/docs/rag/troubleshooting/blocked-tools.md index df865f56..333c57b1 100644 --- a/docs/rag/troubleshooting/blocked-tools.md +++ b/docs/rag/troubleshooting/blocked-tools.md @@ -15,7 +15,7 @@ | `git checkout` of a task branch | None — branch is auto-checked-out by `i_will_work_on(task_id)` (devs) or `i_will_plan(task_id, plan)` (PMs) | | Open a PR | None — PR is opened by the choreographer when the dev calls `open_pr(task_id)` | | Merge a PR | `complete(task_id, notes)` (PMs only) — Cell PM merges leaf PR; Main PM merges parent and escalates to CEO | -| `git fetch` / `git pull` / `git rebase` | None at the agent layer — task branches are short-lived; if yours diverged, `unclaim` and re-`claim` | +| `git fetch` / `git pull` / `git rebase` | None at the agent layer — there is no pull/rebase verb. If your branch is **behind its base**, ESCALATE (`escalate_up` / `i_am_blocked`) — that, not unclaim, is the fix. Use `unclaim` + re-`claim` only to rebuild a branch fresh from the current base, and only on instruction | ## Write/Edit Outside Workspace diff --git a/docs/rag/troubleshooting/git-errors.md b/docs/rag/troubleshooting/git-errors.md index 4a6856d3..5421dfdc 100644 --- a/docs/rag/troubleshooting/git-errors.md +++ b/docs/rag/troubleshooting/git-errors.md @@ -49,6 +49,28 @@ If your workspace is dirty, the verb returns an envelope telling you to either ` **Fix:** `commit(message=..., files=...)` at least once, then call `open_pr(task_id)` again. +## Branch Behind Its Base / master + +**Symptom:** `roboco_git_status` shows `behind > 0` against the base branch when you go to submit. + +**Cause:** The base (cell branch or master) advanced after your branch was cut, so your branch is stale. + +**Fix:** You have no rebase verb — do **not** create a rebase task or improvise with shell git. Bringing the branch current is a platform/PM action. Escalate: devs `i_am_blocked(reason="branch behind base — needs rebase")`; PMs `escalate_up(...)`. + +## src refspec does not match any (during open_pr) + +**Error:** `src refspec '' does not match any` + +**Cause:** A re-provisioned or shared clone can be missing the local branch ref even though the commits are already on origin. + +**Fix:** Just call `open_pr(task_id)` again — the gateway recovers the ref from origin, or returns an explicit "unclaim and re-claim to rebuild" instruction. Follow whichever the envelope gives you; don't switch branches by hand. + +## Updates were rejected / non-fast-forward (on push) + +**Cause:** Your branch is behind its remote, so the push can't fast-forward. + +**Fix:** Escalate rather than improvise — devs `i_am_blocked(...)`, PMs `escalate_up(...)`. There is no agent-layer pull/rebase to reconcile it. + ## NO_PR on pass / fail **Cause:** The PR was never created — usually because `open_pr(task_id)` did not run cleanly. diff --git a/docs/rag/workflows/pr-creation.md b/docs/rag/workflows/pr-creation.md index 131a5edf..8841da69 100644 --- a/docs/rag/workflows/pr-creation.md +++ b/docs/rag/workflows/pr-creation.md @@ -30,6 +30,8 @@ The transition enforces (`enforcement/task_lifecycle.py`): If any precondition is missing, the verb returns an envelope explaining what's missing and how to remediate. +The push and the PR head always target **the task's own branch by name**, independent of whatever the shared clone happens to be checked out on. So a `No commits between` or wrong-branch worry at `open_pr` is the verb's job to resolve — never switch branches by hand to "fix" it. + ## PR Title and Body Generated from templates in `roboco/templates/git/pr_internal.py` and `roboco/templates/git/pr_root.py`. You don't write the body by hand — it's filled with task title, acceptance criteria, the dev's notes, and the standard traceability links. diff --git a/docs/troubleshooting/common-issues.md b/docs/troubleshooting/common-issues.md index 464120bb..184ef00e 100644 --- a/docs/troubleshooting/common-issues.md +++ b/docs/troubleshooting/common-issues.md @@ -8,7 +8,8 @@ When something goes wrong, the failure is almost always one of a handful of thin |---------|-------------------|-----| | Agents spawn but do nothing useful (tool-discovery churn) | The role's tool-manifest didn't mount; the agent falls back to discovering verbs | Check the manifest mount (below) | | Agent containers respawn in a loop, MCP servers stuck "pending" | MCP server launched without `--no-sync` against a workspace clone | Already fixed in the orchestrator; verify your image is current (below) | -| A provider's agents go quiet all at once | The provider is **parked-and-probing** after a 429/overload — not hung | Wait; it self-resumes. See [Resilience](../models/resilience.md) | +| A provider's agents go quiet all at once | The provider is **parked-and-probing** after a 429 / overload / session-limit — not hung | Wait; it self-resumes. See [Resilience](../models/resilience.md) | +| A task sits **blocked: "branch behind base / needs rebase"** | The agent's branch fell behind its base while it worked; there is no agent-layer rebase verb, so the agent escalates instead of improvising | Rebase it yourself from the panel **Git** tab (below) | | KB / `ask_mentor` returns nothing | Ollama unhealthy or models not pulled | Check `ollama-init` logs (below) | | Agent containers exit immediately | `~/.claude` not mounted, or a Grok token expired | Check the mount / refresh the token (below) | | Clone fails, agent can't reach the repo | Missing or invalid project PAT, or HTTPS URL with no token | Set the project token (below) | @@ -31,7 +32,7 @@ The fix is already in the code: every MCP server is launched with `uv run --no-s ## A "quiet" provider is parked, not hung -If every agent on one provider goes silent at the same moment, it is almost never a crash. On a provider 429, or a persistent overload (HTTP 529/500/503), RoboCo **parks** that provider's work and runs a background probe that resumes it the moment the provider recovers — it does not crash-retry into the wall and burn tokens. You'll see an amber banner in the panel; the work revives on its own. +If every agent on one provider goes silent at the same moment, it is almost never a crash. On a provider 429, a persistent overload (HTTP 529/500/503), or a Claude session-limit (the rolling 5-hour usage window), RoboCo **parks** that provider's work and runs a background probe that resumes it the moment the provider recovers — it does not crash-retry into the wall and burn tokens. You'll see an amber banner in the panel; the work revives on its own. !!! note "Don't restart to 'unstick' it" Restarting the orchestrator throws away the park-and-probe state and the parked agents' context. Leave it alone — it self-heals. The full mechanism, the banner, and the `ROBOCO_OVERLOAD_BREAK_ENABLED` flag are documented in [Resilience](../models/resilience.md). @@ -73,6 +74,10 @@ When the orchestrator won't come up at all on a fresh deploy: - **Migrations.** Schema changes ship as Alembic migrations under `alembic/versions/`. The API applies them on startup (and falls back to `create_all` on a fresh DB), but after pulling a change that adds a migration you can apply it explicitly with `docker compose exec orchestrator alembic upgrade head`. - **Startup is slow on purpose.** The FastAPI lifespan does ~30–60s of document indexing before the API answers, and the orchestrator polls `/health` for up to 120s before starting its dispatch loop. An "All connection attempts failed" early in the logs usually just means a dependent service hadn't finished its healthcheck yet — give the startup sequence time before treating it as an error. +## A task is stuck on a branch behind its base + +Agents have no rebase, pull, or merge verb — a task branch is brought current with its base only at claim. If the base (a cell branch, or master) moves forward while the agent works, the branch falls behind, and the agent escalates rather than improvising git surgery: the task surfaces **blocked** with a reason like *"branch behind base — needs rebase."* That escalation is by design — bringing the branch current is your call, not a unit of work the company decomposes. Rebase it from the panel **Git** tab — select the branch and **Rebase** it onto its base (or master) — and the task resumes on the next dispatch. (Automatic rebase-at-spawn, so a stale branch never reaches you at all, is on the roadmap.) + ## Next → [Security](./security.md) — the trust model and how to harden a deployment, or back to the [troubleshooting index](./index.md). diff --git a/panel/package.json b/panel/package.json index 88c4eec7..8ddf5c98 100644 --- a/panel/package.json +++ b/panel/package.json @@ -1,6 +1,6 @@ { "name": "roboco-panel", - "version": "0.10.0", + "version": "0.11.0", "private": true, "packageManager": "pnpm@10.25.0", "scripts": { diff --git a/pyproject.toml b/pyproject.toml index 61dab3ad..2b2a5213 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "roboco" -version = "0.10.0" +version = "0.11.0" description = "AI Agents Company - A virtual organization of AI agents functioning as a software development workforce" authors = [ {name = "Renzo Franceschini", email = "rennf93@users.noreply.github.com"} diff --git a/roboco/__init__.py b/roboco/__init__.py index c3d39a3b..66acfddb 100644 --- a/roboco/__init__.py +++ b/roboco/__init__.py @@ -5,7 +5,7 @@ A virtual organization of 25 AI agents + 1 human CEO, designed to operate as a complete software development workforce. """ -__version__ = "0.10.0" +__version__ = "0.11.0" # Core exports from roboco.config import settings diff --git a/roboco/config.py b/roboco/config.py index 1513f620..213c5887 100644 --- a/roboco/config.py +++ b/roboco/config.py @@ -28,7 +28,7 @@ class Settings(BaseSettings): # ========================================================================== # Application # ========================================================================== - app_version: str = "0.10.0" + app_version: str = "0.11.0" debug: bool = False environment: str = Field( default="development", pattern="^(development|staging|production)$" @@ -548,7 +548,7 @@ class Settings(BaseSettings): agent_image_tag: str = Field( default="", description=( - "Tag for pre-built agent images (e.g. 'latest' or '0.10.0'). Empty " + "Tag for pre-built agent images (e.g. 'latest' or '0.11.0'). Empty " "leaves the tag implicit (':latest'); only meaningful with " "agent_image_registry set." ), diff --git a/uv.lock b/uv.lock index 2288e538..b75fdc47 100644 --- a/uv.lock +++ b/uv.lock @@ -2404,7 +2404,7 @@ wheels = [ [[package]] name = "roboco" -version = "0.10.0" +version = "0.11.0" source = { editable = "." } dependencies = [ { name = "alembic" },