Commit Graph
622 Commits
Author SHA1 Message Date
Renn F c49dcebae4 fix(toolchain): raise requires-python floor to 3.13 so the resolver matches the runtime
RoboCo's code imports tomllib (3.11+) and the stack runs on 3.13, but requires-python declared >=3.10. The toolchain resolver picks the lowest satisfying version, so it provisioned agent workspaces of the self-hosted roboco-api project at Python 3.10 — an interpreter the suite cannot even be collected under, leaving the workspace .venv unusable and the gate running in an ad-hoc fallback env. Raising the floor to >=3.13 makes resolve_target_python return 3.13, matching the agent image. Re-locks to drop the now-unreachable 3.10-3.12 backports; a guard test pins the repo's own resolution to 3.13.
2026-06-22 13:02:59 +02:00
Renn F a9b28ad402 Merge branch 'master' of https://github.com/rennf93/roboco 2026-06-22 12:38:05 +02:00
16789c1ca7 Feature/architectural conventions standard (#243)
* feat(conventions): standard schema models + effective-map merge

* feat(conventions): tree-sitter Python classifier + placement checks

* feat(conventions): TS classifier, hygiene/custom checks, runner + CLI

* feat(conventions): ROBOCO_CONVENTIONS_ENABLED flag + cache table + migration

* feat(conventions): repo auto-scan + scaffold draft renderer

* feat(conventions): ConventionsService (cache/baseline/ambient/scaffold/restore)

* feat(conventions): auto-scaffold on project registration (flag-gated)

* feat(conventions): TaskDescription.constraints + auto-baseline attach

* feat(conventions): ambient architecture-map injection at spawn

* test(conventions): subprocess CLI smoke for the agent-image entrypoint

* feat(conventions): block i_am_done on block-level convention violations

* feat(conventions): block pr_pass on unresolved convention violations

* feat(conventions): surface convention findings into QA evidence

* docs(prompts): convention awareness for PO/Intake/Dev/QA/PR-reviewer

* feat(conventions): panel Conventions tab + flag toggle + parity

* test(conventions): end-to-end block, fix, and waiver through the gate

* refactor(conventions): extract pr_pass guards to keep pr_gate under the gate

* style(conventions): format the baseline-constraints attach in task.create

* test(conventions): type-annotate test helpers for the full mypy gate

* build(conventions): ignore types-PyYAML in deptry (mypy-only type stub)

* docs(conventions): document the standard in CLAUDE.md + PM prompt awareness

* fix(conventions): baseline constraints are non-suppressible (dedup-append)

* feat(conventions): scaffold on first workspace clone (threaded workspace)

* feat(conventions): multi-project ambient map for PO/Intake (per-product)

* feat(conventions): persist findings + violations-feed route (migration 044)

* feat(conventions): panel violations feed in the Conventions tab

* test(conventions): intake-spawn mock accepts the ambient layer kwarg

* fix(docker): ollama-init best-effort pull, gate startup on cached models present

A degraded/slow ollama registry made the model manifest re-check fail under
set -e, so ollama-init exited 1 and blocked the orchestrator's
service_completed_successfully gate — taking the whole stack down even though
both models were already cached. Pulls are now best-effort; success is gated on
the models being present, so a flaky registry can't down a cached deployment.

* refactor(content): drop dead TaskDescription.with_baseline_constraints

The structured baseline-merge helper had zero production callers. Project-task baseline constraints are attached by the wired string backstop (TaskService._attach_baseline_constraints), and a real task is free-form prose that cannot form a valid TaskDescription (requires a non-trivial objective + non-empty the_work), so the helper was unreachable from any live path — a leftover from the structured-merge -> string-append design pivot. Removing it leaves a single enforcement path. The constraints field itself stays: it is a member of the well-formed-spec schema (Objective / What This Builds / The Work / Notes / Constraints / Acceptance Criteria), rendered by render_markdown and unit-tested.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-06-22 12:37:46 +02:00
Renn F 589fd79f38 fix(docker): ollama-init best-effort pull, gate startup on cached models present
A degraded/slow ollama registry made the model manifest re-check fail under
set -e, so ollama-init exited 1 and blocked the orchestrator's
service_completed_successfully gate — taking the whole stack down even though
both models were already cached. Pulls are now best-effort; success is gated on
the models being present, so a flaky registry can't down a cached deployment.
2026-06-22 11:36:15 +02:00
01e10ad693 Feat/provider overload break (#242)
* feat(conventions): standard schema models + effective-map merge

* feat(orchestrator): park provider on persistent server overload (529/500)

A 429 rate limit already parks a provider — queue its spawns, probe until it
recovers — but a persistent 529/500/503 overload had no such break: the run
died and the orchestrator crash-retried straight back into the overload,
burning tokens in a respawn loop.

Generalize the park to provider-unavailability. On a non-graceful Anthropic
agent exit, match the API's overload markers (overloaded_error /
internal_server_error / "API Error: 5xx") against the dead container's own
output and park the provider with kind="overloaded"; the existing spawn gate
already queues any parked provider, and the probe-resume loop revives the task
when it recovers. Grok keeps its exit-75 path; both now route through one
_park_provider_unavailable helper. Markers are kept specific so an agent that
merely writes about HTTP 500/529 can't trip the break.

Fix the recovery probe to require a 2xx: it treated any non-429 as recovered,
so a probe that itself got a 529 would have resumed agents straight back into
the overload — wrong for the new path and for a 429 that lifts into a 5xx.

Gated by ROBOCO_OVERLOAD_BREAK_ENABLED (default on; off => crash-retry).

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-06-22 03:49:45 +02:00
Renn F ba8b877a05 Updated Makefile 2026-06-22 03:07:47 +02:00
Renn F f6295fb31e feat(settings): expose toolchain matching in the Feature Flags card
Adds toolchain_match_enabled to FEATURE_FLAGS so the panel's Settings ->
Feature Flags card can arm/disarm it (overriding the env default at the next
backend restart). The card is data-driven; only a one-line description blurb is
added. Validator, effective-value, and startup overlay auto-wire from the
tuple.
2026-06-22 03:02:27 +02:00
Renn F e4b6c8bdfd chore(compose): enable toolchain matching in the personal compose files
ROBOCO_TOOLCHAIN_MATCH_ENABLED on (default true, .env-overridable) in the
orchestrator env of docker-compose.yml + docker-compose.yaml only;
docker-compose.registry.yml is intentionally left off so the published default
stays conservative until live-verified.
2026-06-22 03:02:26 +02:00
Renn F 8b20afbc33 refactor(content): extract _write_journal_note to keep note() under the gate
The scope='handoff' branch added to note() pushed its cyclomatic complexity to
rank C (full-package xenon, --max-absolute B). Extract the non-handoff journal
validate+persist body into _write_journal_note so note() is a thin dispatch and
both stay within bound. Behavior-preserving; note tests unchanged.
2026-06-22 02:15:34 +02:00
Renn F 7d50405ccb fix(toolchain): type-annotate test helpers for the full mypy gate
make quality runs mypy over tests/ too; the toolchain test fixtures
(monkeypatch params, the fake-subprocess factories) were missing annotations.
No behavior change.
2026-06-22 01:59:41 +02:00
Renn F 78c6981089 test(toolchain): end-to-end interpreter-match + loud-fail guard
Ties the pieces at the logic level: a target whose .python-version (3.13)
conflicts with requires-python (>=3.14) provisions against 3.14, a collection
error records 'broken', and the gate guard then refuses the pass. Flag-off
provisions exactly as today and never blocks.
2026-06-22 01:54:50 +02:00
Renn F 9a988cebd1 feat(toolchain): build-essentials in the agent runtime for target interpreters
The agent runner stage had build-essential only in the builder. When toolchain
matching is on, uv provisions the workspace against the target project's Python
and must compile an sdist for any dependency lacking a wheel for that version —
so build-essential now stays at runtime. The runner already permits uv managed
python downloads (only the builder pins UV_PYTHON_PREFERENCE=only-system, for
RoboCo's own 3.13 venv). Verified locally: in the built image,
`uv venv --python 3.14 && uv run python` prints Python 3.14.6 while the system
interpreter stays 3.13.
2026-06-22 01:54:32 +02:00
Renn F 0ea9f48187 feat(toolchain): block QA/dev/PR gates when the suite cannot be executed
Adds _toolchain_broken_guard: when toolchain matching is on and the acting
agent's workspace recorded a 'broken' status (the project's suite cannot be
collected under the provisioned interpreter), i_am_done, pass_review, and
pr_pass refuse with an i_am_blocked remediation instead of letting the role
"pass" on a source read. fail_review / pr_fail stay available. git gains
toolchain_status_for_task (resolves the actor's workspace, reads the marker,
fail-open None). Inert when the flag is off; a missing/unknown status never
strands a task.
2026-06-22 01:49:44 +02:00
Renn F 036416878d feat(toolchain): provision against the target interpreter + runnability smoke
When toolchain matching is enabled, install_dev_deps resolves the target's
Python and provisions the venv against it (uv sync --extra dev --python <v>,
uv auto-downloads), then runs a runnability smoke (uv run pytest --collect-only)
and records {python, status} in a .git/.roboco-toolchain marker the gates read.
Precision over recall: only a pytest collection error (the interpreter-mismatch
signature) reports 'broken'; pytest-absent / timeout report 'unknown' so a gate
never blocks on an inconclusive smoke. Flag off => provisioning is unchanged.
2026-06-22 01:40:06 +02:00
Renn F 6df083c594 feat(toolchain): flag + WorkSession toolchain columns + migration
ROBOCO_TOOLCHAIN_MATCH_ENABLED (default-off) gates the whole subsystem.
Adds work_sessions.toolchain_python / toolchain_status (nullable VARCHAR(20))
recording the interpreter the workspace was provisioned with and whether the
target's suite can be executed (ok | broken | unknown). Migration 042 verified
with a real upgrade head + downgrade -1 + re-upgrade on a throwaway Postgres.
2026-06-22 01:31:45 +02:00
Renn F 9274cd4584 feat(toolchain): resolve target interpreter from requires-python
Pure resolver (services/toolchain.py) that derives the Python version an agent
should provision a target workspace with. Defends against uv's resolution
order — a .python-version file overrides requires-python during interpreter
selection — by honoring the pin only when it satisfies requires-python, else
resolving a concrete version from requires-python for the caller to pass via
--python. This is the root cause behind the live guard-core-app failure (pin
3.13 vs packages needing 3.14). Promotes packaging to a direct dependency.
2026-06-22 01:25:47 +02:00
Renn F 77c94b74da docs(prompts): announce the note-section obligations to agents
The obligations added in 8cf69781 (dev_notes@i_am_done, quick_context@delegate,
pr_reviewer_notes@pr verbs, auditor@i_am_idle) were only discoverable at
runtime via the gate's remediate field. Surface them upfront so agents satisfy
them on the first call instead of looping into a tracing_gap:

- base.md: the gap-key reference gains rows for dev_notes>=min /
  quick_context>=min / pr_reviewer_notes>=min (parity with the journal rows).
- developer.md: a note(scope='handoff') step before i_am_done, and dev_notes
  added to the i_am_done precondition list.
- cell_pm.md / main_pm.md: fill quick_context (done+next) before the first
  delegate (it persists across the whole queue).
- auditor.md: must record an observation before i_am_idle.

The pre-write cases (dev_notes, quick_context, auditor) carry the real loop
risk; pr_reviewer / doc notes are satisfied by the verb's own argument, so the
base.md row alone suffices for those.
2026-06-21 20:45:29 +02:00
Renn F 8cf697816f feat(content): obligate role note sections like journals
Completes the note(scope='handoff') write-path (WIP 23e6ee57): every role
with a dedicated note section is now obligated to populate it, the same way
journals are obligated.

Obligations (foundation.policy.tracing):
- DEV_NOTES / PR_REVIEWER_NOTES / QUICK_CONTEXT_MIN_CHARS requirements +
  checkers, wired onto i_am_done (dev_notes), delegate (quick_context), and
  pr_pass / pr_fail / post_pr_review (pr_reviewer_notes).
- Fixes a latent bug: the docs-notes checker read dev_notes instead of
  doc_notes (the documenter's section); the i_documented shim now feeds
  doc_notes to match.

Auditor: a session-scoped note obligation on i_am_idle — the auditor owns no
delivery task and has no delivery verb, so it must have recorded an
observation within the window before going idle (JournalService.has_recent_entry).

Write-then-gate: persisted sections (dev_notes / quick_context) are
pre-written by the agent's note(scope='handoff') before the gated verb;
argument-borne sections (doc_notes / pr_reviewer_notes) are checked through a
SimpleNamespace shim, the same pattern qa_notes already uses.

Config: dev/pr_reviewer/quick_context min-chars (40/40/30), panel-tunable.
Plus per-gap remediation hints and full coverage (write-path routing,
ownership, validation->remediation, each obligation, the doc_notes fix).

Full make-quality green: 9777 passed, 95.6% coverage.
2026-06-21 20:31:42 +02:00
Renn F 23e6ee579b feat(content): note(scope='handoff') write-path for role note sections (WIP)
Agents could not author dev_notes / quick_context / auditor_notes — note() only wrote the journal, so those sections were always empty (the root cause of 'nobody leaves notes'). This adds the write path: note(scope='handoff') routes by role to the section's content model via the apply_structured_note chokepoint (content_type_for_role + TaskService.record_section_note), threaded through the do-server note tool and /api/v1/do/note. ruff/mypy/format clean.

WIP checkpoint before a fresh session: no unit tests yet and the obligations (tracing VERB_REQUIREMENTS) are not wired — do not deploy until completed + gated. See the project_notes_mandate_feature memory for the full design and remaining work.
2026-06-21 19:35:24 +02:00
Renn F 4e4d25d8f9 fix(panel): size the task-id chip and branch metadata to match their siblings
The task-detail header id chip and the Branch metadata card both rendered at text-xs, visibly smaller than the neighbouring controls and cards. Bump both to text-sm, align the id chip's height with the adjacent status/team selects, and let the long branch name span two columns so it reads at the same size as the other metadata.
2026-06-21 09:41:09 +02:00
Renn F 0a1aa3e7a5 fix(panel): treat the "active" agent state as up so Spawn is hidden while running
The agent card gated Spawn on [running, ready, starting, waiting_long], omitting the "active" state that the status badge renders as a first-class green state — so an agent shown as "active" still offered Spawn instead of View/Stop. Gate on the terminal/down states instead, so every up state (active, running, idle, paused, …) hides Spawn and shows View Details + Stop.
2026-06-21 09:41:08 +02:00
Renn F 61d6cde40f fix(docker): pre-create ~/.claude.json in the agent base image
Claude Code prints "configuration file not found at ~/.claude.json" (3x) at startup when that sibling file is absent. The orchestrator only bind-mounts the host copy when it exists, and delivery agents (unlike intake/secretary, which pre-create it in Python) launch the CLI directly with no wrapper — so when the host file is gone the warning floods every agent's logs. Pre-create an empty {} config in the image; the host mount still overrides it when present and the CLI self-heals the file.
2026-06-21 09:40:54 +02:00
Renn F 2aa00ca124 test(content): assert transition notes via markers, not quick_context
Three integration tests still asserted the old quick_context soup format for
completion_notes / escalation_notes — update them to read the new
orchestration_markers transition_note (the source moved in the prior commit).
2026-06-21 08:14:55 +02:00
Renn F 3a67fa4a0f fix(content): anti-soup guard on pitch slug + restore pitch test intent
Sweep found pitch slug was the one content-tool free-text field still outside
the anti-soup guard (a product could be slugged 'wip'/'asdf'). Add it to the
guard (min 2, so real short slugs like 'ui' pass). Also fix two pitch tests
that were false-passing on a 1-char title (the title guard rejected first):
give them substantive fields so they actually exercise the role gate
(not_authorized) and the non-cell-target rejection (invalid_state).
2026-06-21 08:08:28 +02:00
Renn F 33a3ea3d4a fix(content): move lifecycle-transition notes off quick_context into markers
The structure-everything sweep found four more writers packing key:value soup
into quick_context (the human ResumptionNote field), same anti-pattern as the
already-fixed approve_and_start_notes:
  - _record_completion_notes  -> completion_notes:<text>
  - escalate_to_ceo           -> escalation_notes:<text>
  - ceo_approve               -> ceo_approval_notes:<text>
  - ceo_reject                -> ceo_rejection:<reason>

Route them through a unified orchestration_markers['transition_notes'] dict
(keyed by event) via markers.set_transition_note, so quick_context carries only
the structured ResumptionNote and the panel never shows raw <event>:<text> soup.
Adds the typed accessor + a roundtrip test.
2026-06-21 08:04:15 +02:00
Renn F 0740dc141e feat(panel): clickable Branch/PR links + branch copy button
The Branch value in the task-detail card and the Branch/PR badges in the task
list were static text. Make them open the real thing on GitHub, keeping their
exact look:

- New repo-url helper normalizes a project git_url (https/ssh, with/without
  .git) into web URLs for a branch (/tree/<branch>) and PR (/pull/<n>),
  returning null so callers fall back to a plain label.
- Task-detail Branch card: the branch is now a link to its GitHub tree URL and
  gains a copy button (reuses CopyButton); PR was already linked.
- List-row git badge (git-status-badge): the PR badge links to task.pr_url
  (or the built pull URL) and the Branch badge links to the branch tree URL.
  The row's click handler already ignores <a> clicks, so opening a branch/PR
  never toggles the row. git_url is threaded via a projectGitUrls map from the
  tasks page, alongside the existing projectNames map.

panel typecheck + eslint clean.
2026-06-21 07:15:58 +02:00
Renn F c3ee5f09ba fix(panel): copyable task-id chip + stable, non-shifting task header
The task header rendered 'Task #<uuid>: <title>' as one click-to-edit <h1>,
so the UUID could not be selected/copied (clicking it entered title-edit) and
the editable field silently dropped the id. Worse, the title + status + team +
type all shared one flex-wrap row with auto-width dropdowns, so a long title or
a wider selected label shoved the controls — and the Actions button — to new
positions on every render.

Restructure for stability:
- Title is its own row, editable (no UUID), and truncates on overflow — it can
  never push the controls or Actions.
- A read-only #<short-id> chip with a copy button (reuses CopyButton, which has
  the LAN/http clipboard fallback) copies the FULL uuid.
- Status and team dropdowns are fixed-width (w-40 / w-36), so changing the
  selected value's label width can't shift a neighbor.
- Actions is pinned top-right (shrink-0) and never moves regardless of title
  length or dropdown contents.

panel typecheck + eslint clean.
2026-06-21 06:38:13 +02:00
Renn F 8463808616 fix(lifecycle): let a PM recover its rejected coordination task from needs_revision
The in-path PR-review gate created a deadlock: when an assembled cell→root /
root→master PR fails the gate (pr_fail) — or qa_fail / ceo_reject fires — the
PM-owned coordination task lands in needs_revision, which was developer-claim-
only. So the task had no actor and no exit but cancel, and the cell PM escalated
in a loop (8KB of [ESCALATED] dev_notes on one task). Pre-gate, the PM simply
re-delegated from in_progress; the gate routed the failure through the PM's own
task instead.

Add NEEDS_REVISION to the CELL_PM / MAIN_PM claim rules so the PM re-claims via
i_will_plan, revises the plan, and re-delegates the fixes — pr_fail/qa_fail
already reassign the failed task to its owning PM and the revision dispatcher
re-spawns it; the claim rule was the only missing piece.

Scope is by give_me_work routing (offers only the caller's own assigned tasks),
the same mechanism that scopes a developer's leaf-revision — NOT a gateway-only
ownership gate, which would violate the spec=gateway parity invariant and can't
use task_type anyway (main-PM coordination roots can be code-typed). Regenerates
panel/lib/lifecycle.json.
2026-06-21 06:09:59 +02:00
Renn F 3ff6967b9d fix(content): keep coordination notes off dev_notes / quick_context
Two agent-authored fields were leaking non-developer content into the
human note columns the panel renders:

- apply_escalation appended '[ESCALATED] From X to Y\nReason: ...' to
  dev_notes (the developer's space). On a re-escalation loop a stuck cell
  PM grew one task's dev_notes to ~8KB across 5 escalations. It now writes
  a structured orchestration_markers['escalation'] record; the target
  still learns the reason from the escalate notification.
- approve_and_start string-packed 'approve_and_start_notes:<text>' into
  quick_context (raw key:value soup). It now writes
  orchestration_markers['approve_and_start_notes'], leaving quick_context
  for the human ResumptionNote only.

Adds typed marker accessors (get/set_escalation, get/set_approve_and_start_notes)
and refactors _record_pr_review under the complexity bound by extracting
_compose_review_body. Documents update_task_with_message as the legacy
A2A-protocol log (dev_notes is intentional there, not pollution).
2026-06-21 05:36:50 +02:00
Renn F 4c85cc6dfa feat(content): anti-soup guard on the flow verbs' free-text
Extends structured-content enforcement from the content tools to every
flow verb that carries agent free-text, closing the last hole where a
dev/PM could pass word soup: i_am_blocked(reason), i_am_done(notes),
submit_up/submit_root/complete(notes), escalate_up/escalate_to_ceo(reason),
pass_review(notes), fail_review/pr_fail(issues), pr_pass(notes),
i_documented(notes), delegate(title/description). Plans (i_will_plan /
i_will_work_on) keep their existing >=150-char approach + sub_task gates
and are skipped here so recovery re-entry with thin values still works.

Shared helpers on the choreographer: _free_text_soup (bare envelope, list
aware) and _soup_or_decision_env (folds the soup check into a verb's
existing spec-gate return so no verb gains a return or tips the xenon
bound). reject_trivial now also catches all-filler multi-token strings.

base.md documents the broadened rule for agents.
2026-06-21 05:11:20 +02:00
Renn F 96ca53eff2 feat(content): extend anti-soup guard to pitch title, open_session topic, pr_update, note narratives
The universal guard covered say/dm/note-text/progress/notify/pitch
problem+solution. Close the remaining content-tool fields the agent
authors: pitch title, open_session topic, pr_update title/body (when
supplied), and the decision/reflect narrative sub-fields of note
(rationale/context/what_done/...). Narrative fields are only checked
when the agent fills them — an omitted field keeps its tolerant
'(not provided)' placeholder so a thin note still records and never
trips the do-server circuit breaker. Fold pr_update's no-fields + soup
checks into one helper to stay under the return-count bound.
2026-06-21 04:46:28 +02:00
Renn F b1f1a91066 feat(content): reject all-filler multi-token strings in reject_trivial
The single non-empty/non-placeholder gate caught a lone banned token
(wip) and below-floor strings, but multi-token soup made entirely of
placeholders (wip wip, tbd / na, todo todo todo) slipped through both
checks. Add an all-tokens-filler test that strips edge punctuation per
token and rejects when every meaningful token is banned — without
flagging real prose that merely contains a filler word (none of the
tests failed). Strengthens every content model + gateway anti-soup
guard that composes reject_trivial.
2026-06-21 04:42:26 +02:00
Renn F db74f546a3 feat(content): universal anti-soup guard on every agent free-text field 2026-06-21 04:20:07 +02:00
Renn F b086dc2c41 feat(content): QA pass/fail stores a structured QaNote 2026-06-21 03:54:33 +02:00
Renn F 11c1d9eee7 feat(content): structured PR-review findings + generated GitHub comment 2026-06-21 03:47:17 +02:00
Renn F b97465a631 test(content): assert pr_fail issues land in pr_reviewer_notes (gate path) 2026-06-21 03:23:07 +02:00
Renn F c47c886508 test(content): mypy-clean structured-model construction + lint fixes 2026-06-21 03:07:58 +02:00
Renn F 6a14312a0c feat(panel): PR Reviewer + Documenter note cards with verdict pill 2026-06-21 03:02:46 +02:00
Renn F 2e490a06d7 feat(content): PR reviewer writes its own slot, stops clobbering QA/Dev 2026-06-21 02:56:53 +02:00
Renn F 0fba4eed01 refactor(content): move orchestration markers off quick_context to typed jsonb 2026-06-21 02:48:07 +02:00
Renn F 7e74112fcc feat(content): structured-note persistence + derived TEXT mirror chokepoint 2026-06-21 02:00:21 +02:00
Renn F 31c2d5adc6 feat(content): add doc_notes column for the documenter's own slot 2026-06-21 01:58:43 +02:00
Renn F a6e09f6d75 feat(content): task structured-note fields + orchestration-marker accessors 2026-06-21 01:55:36 +02:00
Renn F dd03c218d1 feat(content): migration for structured note columns + quick_context split 2026-06-21 01:51:48 +02:00
Renn F f0368af757 feat(content): structured content schema models + renderers 2026-06-21 01:44:07 +02:00
Renn F 8fa6104af1 fix(panel): clear color indicators for agent status badges
The agent cards rendered active / stopped / offline all in the same grey:
the state-badge color map had no `active` or `offline` entry (both fell to the
grey fallback) and `stopped` was also grey. Give them distinct, legible colors:
active/running → green, offline → grey, stopped/paused → amber (attention, not
alarming), error → red; move idle to blue so grey unambiguously means offline.
Add active/offline/paused icons (Activity / PowerOff / Square).
2026-06-21 00:12:51 +02:00
Renn F 7662804f1c fix(orchestrator): spawn the owning PM for a gate-failed assembled PR
The PR-review gate's pr_fail sends a cell->root / root->master PR back to
needs_revision, still owned by the cell/main PM. The dispatch side
(_dispatch_revision_coordination_roots) re-spawns the owning PM for any
PM-owned needs_revision task, but the readiness gate
(_readiness_check_role_for_status) only waived the dev/doc role restriction for
coordination tasks — and a gate-failed assembled PR has a project + branch, so
it is NOT coordination. Result: dispatch routed the cell PM, readiness refused
it ("state=needs_revision requires role in {developer,documenter} but agent
be-pm is cell_pm"), and the task deadlocked. Pass owner_is_pm (derived from the
task's assignee, like the dispatch side) so the readiness waiver also fires for
a PM-owned revision, not just coordination roots. QA stays excluded.
2026-06-21 00:04:24 +02:00
Renn F 33ed0209c8 fix(reaper): don't reap a live container the instance registry forgot
The stale-claim reaper skips a task whose assignee holds an ACTIVE instance,
but that check reads the in-memory _instances registry — lost on an
orchestrator restart while the agent's container keeps running. The reaper then
released a task out from under a live agent it had merely forgotten (registry
amnesia). On a registry MISS, fall back to asking Docker directly
(_inspect_container_state); a still-running container is spared. A known
instance (active or stopped) stays authoritative, and an uninitialised registry
(unit-test harness) keeps the prior behaviour.
2026-06-20 22:51:03 +02:00
Renn F 6f3ba2f42e fix(bash-guard): block package-env mutations targeting /app (all providers)
An agent that ran `uv sync` / `pip install` against /app rebuilt the
orchestrator's MCP-gateway venv (/app/.venv) — breaking its own gateway tools
(every roboco-flow/-do/-git verb), stranding it, and getting its task reaped.
Deny package-mutation verbs (uv sync/lock/add/remove, uv pip, pip install)
whose target resolves to /app (cd /app, --project/--directory /app, /app/.venv,
UV_PROJECT_ENVIRONMENT=/app). Placed outside the ROBOCO_GUARD_SKIP_GIT gate so
it fires for every provider — the Claude PreToolUse hook and the grok exfil
hook alike. Reads of /app and workspace installs are untouched.
2026-06-20 22:51:02 +02:00
Renn F b342de2913 feat(panel): show + filter tasks by project and product
The task list had no way to see or filter by which project/product a task
belongs to. Add a "Project / Product" column to the table (resolving the id
to a name, with a "(product)" hint for fan-out tasks) and Project + Product
multi-select filters alongside Status/Team/Type, URL-backed and client-side
like the others. Options and names come from the projects/products lists.
2026-06-20 21:14:49 +02:00