diff --git a/CHANGELOG.md b/CHANGELOG.md index b080ba04..3169d9c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,34 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +## [0.14.0] - 2026-06-29 + +### Added + +- **Multi-level MegaTask sequencing — a batch now runs in the right order, structurally, not by luck.** A MegaTask that spans several cells (and may mix per-cell projects from different products or OSS libraries) can now be routed per-cell without standing up a Product for it: each root-subtask carries an ad-hoc per-cell project map (`task_cell_projects`, migration 052, with a panel per-cell project picker), then cuts `feature/main_pm/{root}` and opens a root→master PR per repo exactly like a Product fan-out. On top of that map the dependency graph now carries the sequencing edges that collision and migration ordering need, enforced in the DAG rather than hoped for in the prompt: a dev task declares its collision surface (`intends_to_touch` globs, `adds_migration`, `touches_shared`, migration 046) on `delegate`; file-overlap serializes (more-important first), migration-adders chain serially, and a shared-surface edit runs after each non-shared task it overlaps — independent tasks still run in parallel — with cell-task wave chains and a by-osmosis edge completing the multi-level chain. Two new gate-level verbs close the "agent started out of order / drifted behind base" hole that no amount of prompting fixed: `sync_branch` (a dev gate verb that rebases the task branch onto its base and force-pushes, through the gate — raw git stays denied), and an `i_am_done` behind-base submit gate that structurally refuses to submit a task whose branch has fallen behind its base. Single-task intake is byte-for-byte unchanged. + ### Fixed +- **A whole-codebase logic-gap audit — roughly 140 concurrency, scoping, signal, and lifecycle gaps fixed.** The dominant body of this release. The categories: **cross-repo PR scoping** — `pr_number` and `branch_name` are per-repo but were stored and looked up unscoped, so two tasks on different repos sharing a PR number could merge the wrong repo's PR or skip the org's own in-flight integration PR; every PR-merge and branch-ownership lookup is now `project_id`-scoped, and `close_pull_request` / `pr_target` make `project_id` mandatory. **Advisory locks closing TOCTOU races** — per-agent on claim, per-parent on `delegate`, per-task on `open_pr` (preventing a milestone double-emit), plus an atomic server-side Redis probe-failure counter and a single-transaction `replace_chunks` (delete+insert) closing a reindex race. **Audit-row transactionality** — status-transition audit rows and the rework counter are written in-session in the caller's transaction (the old fire-and-forget path is gone), so the audit trail can't diverge from the state change. **Signal gaps** — `pr_fail` now pushes the reviewer's issues to the owning cell PM (the re-submit loop where a PM respawned into `needs_revision` blind and re-submitted the same PR is closed), and `fail_qa` routes a `needs_revision` dev task back to the dev, never the pool. **Asyncio cleanup** — `OptimalService.close()` cancels its startup indexing task before the periodic task and the plugin clear, so it can't write against closed plugins. **Conventions standard** — the validator now times out and reaps on hang, and the gate fails closed on resolution errors (a broken standard can no longer silently disable the gate). **WebSocket** — fan-out is non-blocking with finally-disconnect, idle-timeout, and dead-socket reaping on send error. **Orchestrator runtime** — it drains its fire-and-forget background set on shutdown and stops in lifespan shutdown before closing the DB; the probe-resume loop actually revives parked agents; the grok auth token is refreshed before expiry and parked (not crash-retried) when missing. **Release executor** — every subprocess (git/make/gh/clone) is deadline-bounded and it fails closed on a git add/commit before push. Dozens more across org-memory (private-leak closures, playbook index/unindex as a post-commit step so the RAG corpus never leads the status transaction), the reaper, the provider-park/overload break, and the live-chat bridges. The full categorized tracker lives in `docs/internal` (gitignored). + +- **The 2026-06-27 live-run meltdown cluster — root-caused and closed.** A run hit several compounding wedges at once, each TDD-fixed and verified green: a `main_pm` assigned a `code`-typed task is a structural impossibility (a coordinator PM does no coding) and is now hard-rejected at the gate; `cell_pm_complete` resolved a merge by global `pr_number` and merged the wrong repo's PR (closed by the cross-repo `project_id` scoping above); `submit_root` re-submitted an unchanged PR into an infinite `pr_fail` loop (now hard-gated); `fail_qa` bounced a dev task to the pool instead of back to the dev; a `note(scope='handoff')` with an empty section crashed the note path and tripped a PM respawn loop; the MegaTask four-layer hierarchy (umbrella → root → cell → dev) hit a depth cap sized for three layers; and the durable respawn counter's persist raced under fire-and-forget (an atomic upsert closes it). + +- **The CEO, prompter, and secretary can no longer be spawned as agent containers.** These are human-only roles (the CEO is the human; the prompter is the on-demand intake interviewer; the secretary is the on-demand chief-of-staff) with no delivery lifecycle, yet a `_dispatch_a2a_work` path that spawned any notification target — plus an `_is_agent_active('ceo')` that always returned false — could nonetheless launch them and burn a container on a role that has no work to do. A chokepoint in `spawn_agent` plus a dispatcher skip on human-only assignees closes it at both the spawn and the dispatch layer. + - **The PM-respawn loop breaker now survives an orchestrator restart.** The circuit breaker that stops RoboCo from respawning the same PM on the same wedged task forever (`_pm_respawn_tracker`) lived only in memory, so a deploy/crash/OOM reset a task's strike count to 1 and re-burned the whole threshold — four full agent spawns × container cost — against the still-broken task before the gate fired again. The counter is now write-through-persisted to a new `respawn_tracker` table (migration 051) on every mutation and restored at startup, validated against live tasks so a stale counter can't resurrect against a fixed one. Best-effort and inert when empty (a DB hiccup degrades to exactly the prior in-memory behaviour); it can only ever suppress a spawn, never manufacture one. +- **The `mypy` / `ruff` quality gate is green again, with no `type: ignore` suppressions in `tests/`.** A round of pre-existing type errors in the test suite (ORM `.id` passed where `uuid.UUID` was expected, missing annotations, `None`-attribute accesses) and every remaining `# type: ignore` in `tests/` are cleared, so `make quality` passes cleanly and the no-suppression convention holds. + +### Security + +- **Phase 5 — the live-chat bridges now enforce the CEO-signed panel token.** The intake (`prompter_live`) and secretary (`secretary_live`) panel-facing endpoints were the only API surface that ran unauthenticated at the route layer — their SSE stream (`GET /stream`) carried no identity at all (browser `EventSource` cannot set headers), and the start / status / messages / stop endpoints took no auth dependency. They now require the existing CEO-signed HMAC panel token (`require_panel_token`, the HTTP sibling of the WS `_require_panel_token`): nginx already injects `X-Agent-Token` on `/api/` in prod, so the browser never holds the secret and no panel/nginx change was needed; in dev a missing token is allowed but a forged one is still rejected. This closes the last ungated panel-facing surface using the existing scheme verbatim — no new auth, no client changes. + +- **Agent-token gates and secret-scrubbing hardened across the API.** The HMAC agent-token gate is now enforced on the `do` content routes and the WebSocket streams (not just the a2a message routes); the orchestrator signs its own `X-Agent-Token` on self-API calls; 422 error logs are scrubbed of secrets; the a2a / dashboard / orchestrator routes are gated; and SSE runs one session per query. With the Phase 5 bridge gate above, no panel-facing or inter-agent HTTP surface is now unauthenticated when auth is required. + +### Changed + +- **The local LLM was bumped to `glm-5.2` and the Ollama fleet defaults swapped off minimax.** The in-house RAG / hybrid-retrieval model and the default fleet model assignment move to `glm-5.2:cloud`; a stale minimax default that no longer matched the running fleet is cleared. + ## [0.13.0] - 2026-06-26 ### Added diff --git a/README.md b/README.md index 449bf7d4..5f4e9fb1 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.13.0 +ROBOCO_VERSION=latest # or a pinned release, e.g. 0.14.0 ``` The orchestrator spawns the matching pre-built agent images on demand — no build toolchain or source compile on your host. diff --git a/docs/deploy/deployment.md b/docs/deploy/deployment.md index 8589432a..cffd424b 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.13.0 +ROBOCO_VERSION=latest # or a pinned release, e.g. 0.14.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/panel/package.json b/panel/package.json index 9287e356..b00b2f42 100644 --- a/panel/package.json +++ b/panel/package.json @@ -1,6 +1,6 @@ { "name": "roboco-panel", - "version": "0.13.0", + "version": "0.14.0", "private": true, "packageManager": "pnpm@10.25.0", "scripts": { diff --git a/pyproject.toml b/pyproject.toml index 7dc58a7b..6bee0371 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "roboco" -version = "0.13.0" +version = "0.14.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 9480c340..ba0ceefd 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.13.0" +__version__ = "0.14.0" # Core exports from roboco.config import settings diff --git a/roboco/config.py b/roboco/config.py index 21ee099b..6994407d 100644 --- a/roboco/config.py +++ b/roboco/config.py @@ -28,7 +28,7 @@ class Settings(BaseSettings): # ========================================================================== # Application # ========================================================================== - app_version: str = "0.13.0" + app_version: str = "0.14.0" debug: bool = False environment: str = Field( default="development", pattern="^(development|staging|production)$" @@ -668,7 +668,7 @@ class Settings(BaseSettings): agent_image_tag: str = Field( default="", description=( - "Tag for pre-built agent images (e.g. 'latest' or '0.13.0'). Empty " + "Tag for pre-built agent images (e.g. 'latest' or '0.14.0'). Empty " "leaves the tag implicit (':latest'); only meaningful with " "agent_image_registry set." ), diff --git a/uv.lock b/uv.lock index 13a14ada..560a812e 100644 --- a/uv.lock +++ b/uv.lock @@ -2404,7 +2404,7 @@ wheels = [ [[package]] name = "roboco" -version = "0.13.0" +version = "0.14.0" source = { editable = "." } dependencies = [ { name = "alembic" },