Commit Graph
154 Commits
Author SHA1 Message Date
Renzo FandGitHub dd4c3c3ed0 docs(release): prepare 0.27.0 — curated changelog, rag/map sweep, concat rebuild script (#662) 2026-07-23 11:33:02 +02:00
Renzo FandGitHub d4b7e1e7b8 fix: post-finale completeness sweep — routing surface, provider config, budgets, compose env, interactive exemption (#661) 2026-07-23 09:41:27 +02:00
98a96bcd21 chore(backup): env-gated off-disk mirror + restore drill doc (#645)
The pg_dump sidecar wrote its dumps to the same disk it protects — one
disk failure lost both. Setting ROBOCO_BACKUP_MIRROR_DIR in .env to a
path on a different disk (external/remote mount) arms a mirror step after
every successful dump: tmp+rename copy, mirror pruned to the same
BACKUP_KEEP, unwritable mirror logs-and-skips without blocking the
primary. Unset, the script never attempts a copy — no fake off-disk
copies on the same disk. Docs gain the mirror setup and a quarterly
restore drill (throwaway pgvector container, pg_restore, row-count
sanity check).

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-22 20:29:50 +02:00
17de29545a [6788ce7f] Silent bug sweep: concurrency, state integrity, engine edge-cases, panel data freshness (#638)
* [943d8c4d] Frontend data freshness and approval-queue reliability audit (#631)

* [233a8b0f] WebSocket reconnect message-loss audit and fix (#625)

* [233a8b0f] fix(panel): add REST catch-up to useNotificationStream on WS reconnect

connection.ts has no message buffering/replay, so a notification published
while the CEO bell's socket was down (disconnected/reconnecting) was lost
forever instead of merely delayed. Add a reconnect-triggered GET
/notifications?unread_only=true catch-up folded into the existing
notification_id dedup so a notification delivered both via catch-up and
live WS is never double-counted, and make clearMessages drop the held
catch-up batch too. use-a2a-live.ts and use-rate-limit-websocket.ts were
audited and already have working reconnect-triggered REST fallbacks
(verified via a2a/page.tsx, rate-limit-banner.tsx, usage-overview-panel.tsx
and their existing F083 tests) so no fix was needed there.

* [233a8b0f] docs(panel): add comprehensive WebSocket hooks reference and reconnect architecture guide

Add panel/docs/frontend/hooks.md with full API reference for useWebSocket, useNotificationStream (with new REST catch-up behavior), useAgentStream, useA2ALiveStream, and useConnectionStatus. Include examples, best practices, and testing guidance.

Add panel/docs/architecture/websocket-reconnect.md documenting the message-loss mitigation pattern: Strategy 1 (REST catch-up for events, used by useNotificationStream) and Strategy 2 (REST invalidation for state, used by A2A/rate-limit consumers), plus the dedup logic ensuring no notification is double-counted on reconnect.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [d5315683] fix(frontend): add distinct toast feedback for silently-swallowed x-post and release-proposal statuses, plus regression tests for all 4 approval queues (#626)

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>

* [cd953838] Data-hook null-guard audit and API client 429 retry-by-method fix (#630)

* [cd953838] fix(panel): gate 429 retry by HTTP method, add hook null-guard regression tests

* [cd953838] chore(conventions): waive test-fixture wrapper in hooks null-guard test

* [cd953838] docs(frontend): document API rate-limit retry behavior and null-guard audit results

Added `docs/frontend/api-rate-limiting.md` to document the 429 retry strategy: GET/PUT auto-retry, POST/PATCH/DELETE require X-Idempotency-Key header. Updated `docs/frontend/hooks.md` to confirm the data-hook null-guard audit found all hooks already have correct `enabled` guards and include a regression test suite for the board-review poll on/off behavior and enabled-guard assertions.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>

* [4534c71a] Backend concurrency, state-machine, and engine audit (#634)

* [41de844a] fix(lifecycle): sync CLAIM_RULES with runtime + clear stale claimant on PM hand-off (#627)

Two confirmed state-machine gaps found while auditing lifecycle.py,
task_lifecycle.py, the _ESCALATABLE_TO_BLOCKED bypass, and every
_REVIEW_QUEUE_STATES entry point:

- lifecycle.py's CLAIM_RULES/claim-ActionSpec/StatusTransition table
  did not grant CELL_PM/MAIN_PM re-claim of AWAITING_PM_REVIEW even
  though task.py's runtime _ROLE_CLAIM_STATUSES already granted it
  and claimed the spec agreed -- the two tables had silently drifted,
  breaking i_will_plan re-claim on an awaiting_pm_review task.

- docs_complete's _maybe_advance_to_pm_review pre-assigns a specific
  owning PM via assigned_to but left claimed_by/active_claimant_id
  pointing at the outgoing documenter, unlike every sibling transition
  into a review-queue state. A stale active_claimant_id makes
  content_actions.py's _active_claim_violation wrongly reject the
  newly-assigned PM's own content writes before it formally claims.
  Reassign claimed_by + active_claimant_id to the owning PM alongside
  assigned_to.

Adds a regression test asserting the documenter's stale claim does not
survive the docs_complete -> awaiting_pm_review hand-off.

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>

* [0c46f666] Engine dedup race + sequencing.py edge-case audit (#628)

* [0c46f666] fix(sequencing): dedup race audit + collision-edge fallback bug

Audited the list-open-then-originate dedup pattern across six engines:
RoadmapEngine, XEngine.run_cycle, DepUpdateEngine, and CIWatchEngine each
run inside exactly one sequential orchestrator-loop asyncio task (no other
call site invokes run_cycle), so they cannot race with themselves; their
in-cycle dedup sets/keys are correctly built before any commit. SelfHealEngine
is the same shape. VideoEngine.open_video_task is genuinely different: it is
reachable from the release-publish hook, the feature-spotlight hook, and the
on-demand POST /video/request route, so two overlapping calls for the same
occasion can both pass the "no open task yet" check before either commits.
Fixed by wrapping the check+insert in a short-lived Redis mutex (reusing
HeartbeatMutex) keyed by occasion, mirroring XPostService's existing
lock pattern, with a regression test proving only one of two concurrent
calls creates a task.

Verified ReleaseExecutor's half-landed retry path (release_commit_sha):
apply_version_bumps and write_changelog_entry both run as uncommitted
working-tree edits before commit_and_push's single `git add -A` + commit,
so a bumped-version-without-changelog state can never reach origin (and
therefore can never be observed by a fresh retry clone) - confirmed correct
with a real-git-repo regression test, no fix needed.

Fixed sequencing.py's dev_task_collision_edges: the `if edges: return edges`
short-circuit dropped the same-assignee-lane fallback entirely whenever ANY
surfaced sibling pair produced a collision edge, even for a completely
unrelated same-assignee pair with no declared surface. Now the fallback
always runs, skipping only pairs the analyzer already ordered (so the two
mechanisms can never disagree on direction for the same pair).

Verified sequencing.py rule 3 (all-shared batch generates no edges): correct
by inspection (_shared_last_edges skips every pair when both are shared) and
confirmed with a regression test - no fix needed.

* [0c46f666] docs(reference): concurrency audit summary - engine races, fixes, verified patterns

---------

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [8f7f167a] Redis mutex pre-lock write audit (#629)

* [8f7f167a] Redis mutex pre-lock write audit: add cross-session regression test for XPostService.approve

Audited x_post_service.py, video_post_service.py, release_proposal.py, and
heartbeat_mutex.py for the pre-lock DB-write anti-pattern (a session write
that happens before the SET NX / HeartbeatMutex acquire returns a token,
letting a losing racer's stale write clobber a winner's committed state).

XPostService.approve, VideoPostService.approve, and
ReleaseProposalService.approve/reject already implement the correct
validate-pure-pre-lock, apply-under-lock pattern (the XPostService fix
already shipped per CHANGELOG.md: "X edited_body write deferred into the
single-flight lock (M5)"). HeartbeatMutex holds no AsyncSession at all, so
the anti-pattern is structurally inapplicable there.

Adds a genuine cross-session concurrency regression test to
test_x_post_service.py (a real second DB connection, not an in-process
mock) mirroring VideoPostService's existing cross-session test, proving a
concurrently-committed post survives and the CEO's edited body never lands
on the just-posted row.

* [8f7f167a] Remove redundant inline comments flagged by QA in cross-session regression test

Both comments restated what the surrounding docstrings already say
explicitly, per QA findings F-dbadd8f0 (line 294) and F-27ac051e (line
631) — no behavior change, tests re-verified green against a sandbox
Postgres.

* [8f7f167a] Remove inline trailing comments flagged by QA (correct file this time)

QA findings F-e6f3e6a6 and F-24189858 cited tests/unit/services/
test_x_post_service.py:294 and :631 across 5 revision rounds, but that
file never contained the flagged comment text — a repo-wide grep for
the exact quoted strings shows both comments actually live in the
mirrored tests/unit/services/test_video_post_service.py file, in its
own cross-session concurrency regression tests (the caption-edit and
tiktok-skip tests). Removed both there:
- "# externally visible to the "concurrent" session below" on the
  db_session.commit() call
- "# never attempted without credentials" on the tiktok_poster.calls
  assertion

Both restated what the surrounding docstrings/test names already say;
no behavior change. Verified with the full make quality gate against a
sandbox Postgres/Redis: 13,717 passed, 94.41% coverage, clean except
one pre-existing unrelated failure in tests/unit/api/test_cloud_auth.py
::test_login_route_parses_oauth2_form_not_query_params, which connects
to the app's default localhost:5432 Postgres (not the db_session
sandbox fixture) and is unreachable in this sandboxed environment —
structurally unrelated to the auth subsystem this task never touches.

* [8f7f167a] Redis mutex pre-lock write audit (round 7): add cross-session regression tests for reject() lock protection

Round-7 QA findings F-7eb9fbcb, F-06f39a2e, and F-4d56e49b claim
XPostService.reject(), ReleaseProposalService.reject(), and
release_executor._await_proc() lack lock protection / a CancelledError
handler — but their cited line ranges (255-267, 429-454, 241-257)
describe a pre-fix, shorter version of these functions that predates
commit fb293a787d, already on this branch. At current HEAD:

- x_post_service.py reject() (lines 275-299) acquires _LOCK_PREFIX,
  re-reads under the lock, applies markers.set_x_reject_reason() +
  CANCELLED only inside the critical section, releases in finally.
- release_proposal.py reject() (lines 460-486) does the identical
  dance with _RELEASE_LOCK_PREFIX.
- release_executor.py _await_proc() (lines 257-265) already has an
  `except asyncio.CancelledError` block that kills + reaps the child
  and re-raises, mirroring the TimeoutError handler, with an existing
  dedicated regression test
  (test_await_proc_kills_child_on_outer_cancellation).

The one genuine gap: neither reject() path had a cross-session
(real second DB connection, not an in-process mock) regression test
proving the in-lock re-read catches a concurrent approve/publish that
completes mid-lock-wait — only approve() had one. Added
test_reject_concurrent_approve_completes_during_lock_wait to both
test_x_post_service.py and test_release_proposal_status_guards.py,
mirroring the existing approve() cross-session test: a second engine
commits COMPLETED between reject's pre-lock read and lock acquisition,
and the test asserts the CANCELLED write / reject-reason marker never
lands on the just-completed row.

No production code changed — verified via 103 targeted tests green
against a sandbox Postgres/Redis, plus `make -o sync gate` clean.

* [8f7f167a] Regenerate stale lifecycle artifacts (restore auditor waive_finding)

foundation-check was the only failing gate: the committed lifecycle artifacts
were missing the auditor's waive_finding verb that the lifecycle source
defines, so make quality regenerated them and failed on the diff — nothing to
do with the mutex fix (which passes ruff/mypy/tests/coverage/bandit clean).
make lifecycle restores the drift; this is what the 8 revision rounds kept
missing.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>

* [d615f2e3] fix(tests): sync stale CLAIM_RULES pinning assertions with lifecycle.py (#635)

test_claim_rules_match_pre_gateway_table still asserted the pre-audit
two-member frozenset for CELL_PM/MAIN_PM claim rules. CLAIM_RULES in
lifecycle.py already grants both roles claim rights on
Status.AWAITING_PM_REVIEW (added by the state-machine exhaustiveness
audit) so a PM can re-claim its own review-queue task after a respawn.
Updated both assertions to include AWAITING_PM_REVIEW, matching the
actual dict. Grepped the repo for sibling stale copies of the old
literal; found none beyond this test.

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [3c4e7a35] fix(quality-gate): reflow CONCURRENCY_AUDIT.md and stub occasion lock in video tests (#636)

Root cause: PR #634's CI failed at the markdown-reflow check (make quality
Makefile:285) on CONCURRENCY_AUDIT.md — a hard-wrapped audit doc left over
from the merged "Engine dedup race + sequencing.py edge-case audit" unit
(PR #628). Fixed with `make reflow-docs` (the exact remedy the CI output
itself named).

Running the full local `make quality` (with a sandbox Postgres/Redis to
get past DB-gated skips) surfaced a second real regression from that same
PR #628 unit: it added a Redis-backed HeartbeatMutex occasion lock to
VideoEngine.open_video_task, but two pre-existing test files
(tests/unit/runtime/test_video_render_loop.py and
tests/integration/test_video_routes.py) call open_video_task without
stubbing that lock, so they failed closed against the suite's
deliberately-unreachable test Redis (_no_live_redis). Fixed by applying
the same lock-stub pattern tests/unit/services/test_video_engine.py
already uses for its own occasion-lock tests: an autouse HeartbeatMutex
stand-in fixture in test_video_render_loop.py, and wrapping the two
route-level video-request tests in test_video_routes.py with the file's
existing _LOCKED patch pair (already used by every other lock-dependent
test in that file).

The one remaining local failure,
test_cloud_auth.py::test_login_route_parses_oauth2_form_not_query_params,
is a pre-existing environment gap unrelated to this branch: it needs a
real Postgres reachable at localhost:5432 (which .github/workflows/ci.yml
provides as a service container) but this dev sandbox has no such binding
— confirmed unrelated to any of the four merged audit units.

make quality now passes clean: 13729 passed, 0 regressions, 94.49% coverage.

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [ddc8121f] regenerate lifecycle artifacts for awaiting_pm_review claim rules and waive_finding intent (#637)

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

---------

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>

* fix(sequencing): drop lane-fallback edges that would cycle against analyzer edges

The dev-task collision fallback unioned the analyzer's authoritative edges
with same-assignee lane edges, deduping only the direct pair. A lane chain
through an unsurfaced middle sibling could still contradict an analyzer edge
transitively (the shared-last migration order inverts plain priority order),
closing a 3-cycle that made add_dependency raise ConflictError and wedged
every later delegate to that parent. Fallback edges are now accepted only
when they can't close a cycle against the edges already kept; a regression
test reproduces the exact scenario.

Also strip pre-merge cruft: remove the root CONCURRENCY_AUDIT.md working
report, delete the near-duplicate websocket-reconnect.md doc, fix the stale
a2a/page.tsx doc citation, correct the api-rate-limiting doc to state
idempotency-key retry is unimplemented, and fix two lifecycle.py comments
that referenced a guard function which never existed.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-22 08:11:28 +02:00
Renn F 0296ec6fde fix(settings): ceo_name write-through renames the CEO agent row
The ceo_name setting only reached the panel header — the CEO agent row
kept its seeded name, so agent selectors (create-flag, assignee pickers,
kanban) still showed the old name after a rename. Setting ceo_name now
updates the CEO row's name in the same transaction, resolved via the
shared duplicate-tolerant get_agent_by_role lookup. The seed path never
overwrites existing rows, so a custom name survives restarts. The
agent-facing docs/map entries drop the personal name. Write-through
tests pin ancient created_at on fixture rows so they stay deterministic
against CEO rows other suite tests may have committed.
2026-07-22 03:31:01 +02:00
73c05cfa5e feat(a2a): CEO can DM the Auditor and PR reviewers (#623)
* feat(a2a): CEO can DM the Auditor and PR reviewers

A mid-flight PR reviewer or Auditor that's stuck was unreachable — the CEO
had no way to DM them. Both roles now carry dm/read_a2a, so the CEO can open
a 1:1 and they can reply in-thread through the existing CEO-reply path.

Scoped deliberately: the Auditor stays a silent observer to its peers — it
gains no peer-initiation surface (can_a2a_direct routes it through
_check_auditor_a2a, which refuses every initiation target; it can only reply
inside a CEO-opened DM). PR reviewers keep their owning-PM scope. Intake and
Secretary stay excluded — they have their own dedicated chat pages.

NO_COMMS_ROLES drops to {prompter, secretary}; the panel's EXCLUDE_NON_DM_ROLES
matches. KB/docs updated so the 'auditor/pr_reviewer have no dm' claim isn't
left stale.

* test(a2a): smoke guard checks _NO_COMMS_ROLES, not a hardcoded 'auditor'

The dm() runtime guard no longer names the auditor (it now carries dm to
reply to the CEO); it refuses the canonical _NO_COMMS_ROLES set. Assert on
that set so the smoke test tracks the guard, not a stale role name.

* chore(foundation): regenerate verb tables for auditor/pr_reviewer dm+read_a2a

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-21 05:54:29 +02:00
e125ef08aa feat(settings): CEO display name is configurable, Renzo hardcode removed (#612)
The header chip and the Settings User Info card rendered a literal 'Renzo'.
The name now lives in the system_settings store under ceo_name (validated:
trimmed, non-empty, max 60 chars) with the same client-served default the
transcript-retention card uses, editable inline from the User Info card.
Agent prompts already refer to 'the CEO' generically, so no prompt rewiring;
the two agent-facing RAG docs drop the name too. License/CLA copyright is
untouched.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 20:38:44 +02:00
39ad09febc docs(map): refresh 15 slices for forge/Mini-App/train deltas; re-sync the concat (#597)
Slice updates cover the forge package + provider parity, Telegram cockpit
V4/V5 surfaces, panel perf (virtualized kanban, scorecards batch route),
close_task_pr_best_effort + dependents guard, NO_COMMS_ROLES + CEO A2A
refusal at conversation creation, notification ack-TTL, docs-site config,
pr_labels base-branch signature, taste-skill prompt layers, prompter
history exclusions, live forge e2e suites, and migrations 075/076.
_complete_map.md is regenerated from the slices — the committed concat
had drifted ~8KB behind its own sources.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 19:00:52 +02:00
862c0b27cd docs(rag): close the corpus drift — findings/collision/fast-path/forge/env-ladder coverage (#596)
Seventeen-page sweep of the agent-facing KB against shipped behavior:
required covers_parent_criteria and per-AC criteria_verified reach the
QA/PM/task-tools pages (the QA docs also named non-callable pass_review/
fail_review — the MCP tools are pass/fail); collision_context lands in the
QA/gate/planning evidence docs; the possibilities matrix gets its own
architecture page + config entry; the auditor page gains its missing
waive_finding and playbook-curation verbs; git-pr-types.md is rewritten
off the long-dead is_root_pr model; PR/workspace/git-error pages stop
assuming GitHub (forge-agnostic + env-ladder semantics).

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 19:00:48 +02:00
fc41dfa40e fix(security): active guard enforcement, CEO A2A target check, notification expiry (#595)
* fix(security): guard goes active; CEO A2A respects no-comms roles; ack notifications expire

ROBOCO_GUARD_PASSIVE_MODE defaults to false in both compose files — the
deferred post-calibration flip; fail_secure stays off and the env override
remains the rollback. can_a2a_direct no longer short-circuits the CEO past
the no-comms set (auditor/pr_reviewer/prompter/secretary), now canonical
in foundation.policy.communications.NO_COMMS_ROLES and shared with the
content-actions gate; the A2A service refuses at conversation creation
instead of silently suppressing the wake. Ack-required notifications get
expires_at stamped from ROBOCO_NOTIFICATION_ACK_TTL_HOURS (default 48,
0 disables), so the re-escalation sweeper's expires_at query matches rows
for the first time.

* refactor(notification): extract _ack_and_expiry — xenon rank back under B

The expires_at stamping pushed _create_notification_with_session to
rank C; the requires_ack + expiry derivation moves into a helper with
the same semantics and comments.

* test(conftest): dispose the global DB engine after every test

Production code reaching get_db_context()/get_engine() lazily creates the
process-global engine bound to the current event loop; with per-test
function-scoped loops, any later test touching the global path inherits a
dead-loop engine and dies with 'Future attached to a different loop' —
the order-dependent class that has been wandering the suite (cloud_auth
login, metrics, tasks-routes, full-lifecycle) whenever collection order
shifts. An autouse fixture now close_db()s after every test, keeping the
global path loop-local; no-op when untouched.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 18:46:44 +02:00
ec6558e168 [2a86d1f5] CI-watch: fix the CI regression on roboco-api (#563)
* [e530aa5e] Diagnose and fix roboco-api CI failure (run 29629255153) (#561) (#562)

* [e530aa5e] fix(tests): narrow None before indexing validate_init_data() result in telegram_initdata self-check

CI run 29629255153 failed on mypy, not the historical pydantic-settings
issue (uv.lock already pins 2.14.2). The __main__ self-check block in
test_telegram_initdata.py indexed the dict[str, object] | None return
of validate_init_data() without narrowing away None first.

* [e530aa5e] docs(qa): document CI fix for mypy type narrowing in telegram_initdata test

Explains the root cause (mypy type error in __main__ block), the solution (None narrowing before indexing), and the safe pattern for future test self-checks that call functions returning optional types.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [0884b737] Diagnose and fix Python quality gate + e2e lifecycle smoke CI failures on PR #563 (#564) (#565)

* [0884b737] fix(tests): isolate ROBOCO_SDK_URL for scripted e2e-smoke agents

tests/e2e_smoke/harness.py already isolates ROBOCO_AGENT_TOKEN from the
host environment (the #503/#504 fix) but left ROBOCO_SDK_URL leaking
through. flow_server/do_server both default it to
http://localhost:9000 and forward every rejection there for the
per-verb circuit breaker; inside a real spawned agent container that
port is a live SDK loopback, so the breaker records genuine attempts
for the ephemeral test-agent IDs and trips circuit_open mid-test
(test_sandbox_on_demand.py::test_request_sandbox_guard_chain_over_real_api,
which deliberately causes 3 rejections in a row). Point it at a
guaranteed-refused loopback address so every environment gets the same
fail-open bypass a bare CI runner already gets by having nothing
listening on 9000 at all.

* [0884b737] docs(changelog): document e2e-smoke harness ROBOCO_SDK_URL isolation fix

Document the fix that isolates ROBOCO_SDK_URL in the ScriptedAgent harness to prevent the per-verb circuit breaker from leaking state into ephemeral test-agent identities when the e2e-smoke suite runs inside a live agent container. This ensures the suite passes consistently regardless of whether it runs on bare CI or inside a spawned agent.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [3b9a1771] Diagnose and fix ALL make quality + e2e-smoke stage failures on PR #563; confirm real CI green (round 3) (#566) (#567)

* [3b9a1771] fix(e2e-smoke): match real embedding dimension when seeding fake journal chunk

test_c3_deleted_journal_unindexed inserted a 4-dim placeholder vector
into chunks_journals, but the e2e stack's app lifespan eagerly creates
that table with the real settings.embedding_dimensions (1024) before
the test runs, so the insert failed with "expected 1024 dimensions,
not 4". Derive _SMOKE_DIM from settings.embedding_dimensions instead
of a hardcoded constant so the seeded vector always matches the
table's actual column width.

* [3b9a1771] docs(qa): document e2e-smoke embedding dimension fix in round 3 CI diagnosis

Recorded the root cause, solution, and pattern for the final e2e-smoke test failure found in comprehensive sandbox testing: the test seeded a 4-dim placeholder vector but the app's eager lifespan init created chunks_journals with the real 1024-dim embedding column. Updated _SMOKE_DIM to derive from settings.embedding_dimensions instead of a hardcoded constant.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
2026-07-18 16:13:47 +02:00
ff78618b76 feat: Telegram messages get real formatting + push DMs at draft origination (#568)
* feat(telegram): HTML-styled bot messages + push DMs at held-draft origination

* fix(telegram): attr-context escaping, balance-aware truncation, send observability; docs

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 15:54:49 +02:00
713f91320a feat: Journals joins the Agents hub — third tab (#559)
* feat(panel): Journals joins the Agents hub as its third tab

* docs(map): Journals tab on the Agents hub; registry retargets

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 07:52:28 +02:00
4480dfe8d1 feat: Agents hub — Fleet + Conversations tabs, /a2a merged in, DM quick-action (#558)
* feat(panel): Agents hub — Fleet and Conversations tabs, /a2a redirect, DM quick-action

* fix(panel): validate deep-linked DM targets against roster and exclusions; re-arm the dm latch

* docs(map): panel entries for this wave

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 07:06:32 +02:00
1a726d4c34 feat: customizable Quick Actions on the Overview dashboard (#557)
* feat(panel): customizable Quick Actions on Overview — registry, defaults, persisted picker

* fix(panel): legacy bar actions join the quick-action defaults; empty state; persist-contract test

* docs(map): panel entries for this wave

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 07:05:20 +02:00
02601fef22 feat: Workstation card grids — view toggle, sorting, agent-card visual language (#556)
* feat(panel): Workstation card grids with persisted view toggle and sorting

* fix(panel): stable multiplier sort with cell-label fallback after adversarial review

* docs(map): panel entries for this wave

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 07:04:38 +02:00
c40a7a39c3 feat: Telegram V3 — Mini App cockpit (initData auth + /tg surface) (#554)
* feat(telegram): Mini App auth — initData validation mints the cloud-auth session cookie

* feat(panel): /tg Mini App cockpit — approvals, inbox, read-only board, A2A chat

* fix(telegram,panel): unconditional webapp-auth rate limit, future-dated initData rejection, anchored /tg matcher

* docs(map,rag): Telegram Mini App auth route, initData validator, (tg) surface

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 02:47:59 +02:00
3b88c706dd docs(map): full _complete_map rebuild from current slices; motion-commit debt cleared (#553)
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 02:09:08 +02:00
9fbec78126 feat: Telegram V2 — inbound commands + actionable approve/reject from chat (#551)
* feat(telegram): V2 inbound — command router, actionable approve/reject keyboards, chat-gated poll loop

* fix(release,x,video,telegram): terminal-state guards on approve/reject; sender-identity check

* docs(map,rag): Telegram V2 inbound surfaces and terminal-state approve/reject guards

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 00:47:09 +02:00
c9c1f62981 feat: HyperFrames-grade video design bar — vendored craft references + catalog vocabulary (#550)
* feat(motion): demo-register visual design bar, vendored craft references, catalog vocabulary index

* fix(motion): correct design-bar composition claims after adversarial fact-check

* docs(map,rag): design-bar, vendored references, and catalog index on the video-engine surfaces

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 00:46:01 +02:00
c5dfbf6063 feat: Workstation page — Products + Projects unified behind one sidebar entry (#549)
* feat(panel): Workstation page — Products and Projects as tabs behind one sidebar entry

* docs(map): workstation page, extracted views, and the local-filter scroll trade-off

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 00:45:21 +02:00
496c24d186 feat: git hygiene (branch/preview reaping + cleanup sweep) and panel charts; work sessions under Git (#548)
* feat(panel): session-start, 7d overview spend, and 30d business spend charts

* feat(panel): surface work sessions as a Git page tab (route was orphaned)

* feat(git): reap spent task branches and render previews at lifecycle chokepoints; guarded stale-branch sweep

* feat(panel): stale-branch cleanup button on the Git page

* fix(git,panel): cursor-resumable sweep, force-delete spent refs, local filter state

* docs(map,rag): branch/preview reaping, cleanup sweep, git-tab work sessions, wave-2 charts

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 00:44:48 +02:00
885d6bbe83 feat: CEO-grade A2A — New DM composer, CEO-DM wake, docs scrub (#547)
* feat(panel): CEO New-DM composer and direct-thread replies on the A2A page

* docs(agents): remove dm-the-CEO teaching; fix Board/HoM dead-end escalation recipes

* feat(a2a): CEO-authored DMs wake offline recipients via the a2a_request dispatch path

* fix(a2a,panel): wake only read_a2a-capable roles; case-insensitive header defaults; wider DM picker exclusions

* docs(map): CEO-DM wake mechanics, requires_ack override, A2A composer components; comms-model update

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 00:44:00 +02:00
9b4ce6b9c8 fix: wave 1 quick wins — agent names, scroll bounce-back, chart empty states, model-pin preservation, UUID spawn normalization (#546)
* fix(panel): notifications show agent names, metrics charts get empty states

* fix(panel): stop expand/collapse scroll bounce-back; add floating scroll-jump buttons

* fix(llm): provider mode switches preserve per-agent model pins

* fix(api): normalize agent UUID to slug at the orchestrator route boundary

* fix(panel,docs): align routing-card copy and map docs with preserved-pin mode switches

* fix(panel): drop dead unfiltered scroll hook, re-observe on Suspense swap, name system sender

* docs(map): reflect preserved-pin mode switches, UUID-slug normalization, panel wave-1 deltas

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 00:42:13 +02:00
aa15dc40cc Feature/video artifact verification (#537)
* fix(release): CI wait polls the prod rung; escape the header tooltip apostrophe

get_latest_ci_conclusion defaults to the ladder's head rung, so
wait_for_ci searched slave for a release commit that lives on master
and timed out after 40 minutes with the run already green. The wait
now passes the prod branch explicitly. Also fixes the
react/no-unescaped-entities error that turned master's Panel CI red.

* fix(panel,video): dead dialog triggers behind tooltips; dotted composition ids render

HelpTip nested inside a Dialog/AlertDialog trigger puts the trigger's
click handler on the Tooltip root, which renders no DOM — the agents
Spawn item and the KB Reindex-All / Delete-index confirms were dead.
Tooltips now wrap the triggers. The video renderer accepts interior
single dots in composition ids (release-0.25.0) with '..' still
unrepresentable, and propose_video refuses an unrenderable id at
authoring time.

* fix(dispatch): restart-safe PM review turns

A leaf task in awaiting_pm_review had no periodic pickup: the closure
dispatcher bailed on childless tasks and skipped PR-bearing review
tasks as already-promoted, assuming the submit-time PM session was
still alive — an assumption every restart breaks. Proven live on the
docs-sync leaf after the 0.25.0 redeploy, which also dependency-blocked
its sibling dev task. Childless awaiting_pm_review tasks now flow to
the PM's review turn, and the merge turn respawns its PM when none is
active.

* feat(video): verify the rendered artifact, not the source

The 14s release-0.25.0 cut shipped with only one of four scenes visibly
registering: the dev authored DOM, the smoke asserted DOM, QA read code —
nobody consumed the rendered MP4 before the CEO did. Close that loop, and
the reject loop behind it:

- sidecar frames mode: POST /render with frames=1..32 renders the cut,
  ffprobes the REAL duration, extracts midpoint-sampled keyframe PNGs
  (timestamps in filenames), streams a tar.gz back with X-Video-Duration
- request_render do-verb (developer/QA, request_sandbox's shape): renders
  the caller's ACTUAL composition — dev's own worktree (head_sha/dirty
  provenance), QA a read-only git-archive export of the assembled branch —
  extracts frames to the container-shared .previews/ path, stamps the
  render_preview marker, returns the paths as envelope evidence
- gate: i_am_done on a source=video task refuses without a stamped
  render_preview (Requirement.RENDER_VERIFIED; canonical source string
  moved to foundation as markers.VIDEO_TASK_SOURCE; mirrored in the
  possibilities-matrix fast path so it cannot bypass the check)
- QA claim_review evidence carries video_context (composition id, the
  dev's preview, a re-render instruction) so review checks output
- dev spawn prompt block + a 4th authoring AC order Read-every-frame
  verification before submitting
- reject -> re-author: a CEO reject with a reason opens a fresh authoring
  task carrying the verbatim feedback + a revise-in-place pointer at the
  existing composition (best-effort, never fails the reject) — rejection
  feedback no longer dies on the cancelled draft

E2E: rendered the committed release-0.25.0 composition through the new
frames mode locally — the returned keyframes show exactly the reported
failure (blank frame at 5.8s, only 'Env ladder' by 12.8s), the check the
fleet was missing.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-16 19:49:26 +02:00
Renn F 129c0041f2 docs: document the slave batch — CLAUDE.md subsystems, env-ladder era docs/map, CHANGELOG backfill
CLAUDE.md gains the five undocumented subsystems (env-branches ladder +
EnvSyncEngine, Telegram bridge, possibilities matrix, collision map,
PR labeler) and their flags; docs/map and the pr-creation workflow now
describe head/prod ladder resolution instead of single default_branch;
CHANGELOG's [Unreleased] covers all sixteen merged PRs plus this
hardening basket.
2026-07-15 08:25:51 +02:00
Renn FandRenzo F e07ebf2b93 chore(lifecycle): regenerate artifacts for waive_finding verb
Generated by 'make lifecycle' + scripts/regenerate_verb_tables.py — the
foundation-check drift gate requires these in sync with the spec.
2026-07-14 08:56:55 +02:00
f03859c64c [4cfd99c2] Backend: docs-divergence engine, feature flag, release seam, and compose wiring (#507) (#513)
* [fe5c049b] Register docs-sync feature flag and compose wiring (#505)

* [fe5c049b] Register docs-sync feature flag and compose wiring

* [fe5c049b] feat(config): wire ROBOCO_DOCS_SYNC_ENABLED flag and compose defaults

* [fe5c049b] docs(config): document ROBOCO_DOCS_SYNC_ENABLED flag and compose defaults

---------




* [687574d2] Implement docs-sync engine and release-proposal seam (#506)

* [687574d2] Add docs-sync engine and release-proposal publish seam

* [687574d2] Restore task.py safeguards deleted by docs-sync engine commit and filter docs_sync version in SQL

* [687574d2] docs(map): add engine-docs-sync architecture map and cross-references

* [687574d2] docs(config): update docs-sync flag, cap settings, and changelog entry

---------




* [3e7cd5a8] Fix task.py regressions from docs-sync PR (#509)

* [3e7cd5a8] fix(task): restore deleted auditor alerts and revert descendant cast form in task.py

* [3e7cd5a8] docs(task-service): restore auditor alerts and cast notes in map and changelog

---------




* [e6e23c1f] Enforce docs_sync_max_per_cycle cap in docs_sync_engine.py (#510)

* [e6e23c1f] Enforce docs_sync_max_per_cycle cap in DocsSyncEngine

* [e6e23c1f] docs(docs-sync): document docs_sync_max_per_cycle enforcement in engine map, README, and docstring

---------




* [e4b7dd0f] Revert task.py cast regressions from docs-sync PR (#511)

* [e4b7dd0f] fix(task): revert cast regressions in supersede and descendants

* [e4b7dd0f] docs(map): correct PR #511 cast regression entry in task-service slice map

* [e4b7dd0f] docs(backend): add SQLAlchemy UUID cast pattern note and inline comments in task.py

---------




* [1fdfe711] Fix Python quality gate on docs-sync PR (#512)

* [1fdfe711] Fix ruff formatting in task.py and add coverage tests for docs-sync surface

* [1fdfe711] fix(task): use generic JSON .as_string() accessor in list_open_docs_sync_tasks and correct test patch targets

* [1fdfe711] docs(task-service): record docs-sync JSON accessor fix and list_open_docs_sync_tasks map entry

---------




---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
2026-07-14 02:22:37 +02:00
Renn FandRenzo F 6336e82082 [sandbox-ext] Phase 4: panel extension picker + allowlist docs
Project edit dialog (Sandbox section) exposes a per-service extension
picker — Switches from the allowlist grouped under each enabled service
(postgres: pgvector/PostGIS/pg_trgm/citext/uuid-ossp; redis: RediSearch/
RedisJSON/RedisBloom; mongo has none), mirroring the backend
SANDBOX_ENGINE_FEATURES allowlist. State holds a per-service Set; payload
builds sandbox_extensions only for enabled services with non-empty picks
(empty {} clears the column, mirroring sandbox_services' always-send —
exclude_unset + no exclude_none means an explicit {} writes NULL). The
picker renders only for opted-in services with activatable features.

Types: Project.sandbox_extensions (Record<string,string[]> | null),
ProjectUpdate.sandbox_extensions? (not on ProjectCreate, mirroring
sandbox_services). Mock create seeds null.

Docs name the allowlist (the security containment — no plpython3u), the
no-default-set rule (opters set explicitly, existing opters stay bare), the
standing-vs-per-call union, cache-by-features, kitchen-sink image selection,
and the recommendation to set the full set in project settings so agents
request subsets. sandbox-db.md gains an Extensions section; task-tools.md
and config-reference.md updated; CLAUDE.md sandbox paragraph extended.

Gate: panel typecheck + lint + prettier clean, 516 tests pass.
2026-07-13 20:05:45 +02:00
a3524da5f8 [90c9474c] Auditor revival: scheduled audit trigger and reactive alert producers (#499)
* [927e64d5] Backend slice: auditor scheduled trigger and reactive alert producers (#496)

* [1f2cdb4b] Reactive alert producers at QA-fail and rework (#492)

* [1f2cdb4b] feat(services): add auditor-targeted rework alert producers at QA-fail and rework chokepoints

* [1f2cdb4b] test(services): fix mypy typing in auditor alert producer unit tests

* [1f2cdb4b] docs(backend): document reactive auditor rework alert producers in map and role docs

---------

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [5173415f] Scheduled audit trigger, config, and sweep prompt (#493)

* [5173415f] Add scheduled audit trigger, interval config, sweep prompt, and focused tests

* [5173415f] Allow ROBOCO_AUDIT_INTERVAL_SECONDS=0 to disable scheduled sweeps

* [5173415f] docs(audit): document scheduled auditor sweeps and ROBOCO_AUDIT_INTERVAL_SECONDS

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [a26c18b9] E2E smoke test for auditor triggers (#495)

* [a26c18b9] Add e2e smoke test for auditor scheduled and reactive triggers

* [a26c18b9] docs(tests): add e2e smoke test catalog and changelog entry for auditor triggers

---------

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [3bc47cdc] Fix _fresh_orchestrator state for auditor trigger e2e tests (#497)

* [3bc47cdc] fix(tests): initialize orchestrator state in _fresh_orchestrator helper

* [3bc47cdc] docs(changelog): add _fresh_orchestrator test harness fix entry

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [8323cd50] Fix e2e smoke regression on assembled cell PR #496 (#498)

* [8323cd50] fix(e2e_smoke): repair auditor-trigger smoke tests and harden harness

* [8323cd50] docs(tests): document e2e smoke harness hardening for PR #498

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

---------

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [37e6d999] Backend: repair failing CI checks on auditor revival PR #499 (#503)

* [6e79bada] Triage and fix Python quality gate and Analyze (python) failures (#501)

* [6e79bada] fix(task): replace type ignore with forward-reference cast for SQLAlchemy Mapped UUID in get_all_descendants

* [6e79bada] fix(notification_delivery): add generic type arguments to dict return types in get_ack_status and get_delivery_summary

* [6e79bada] docs(changelog): add Python quality gate type-hygiene fixes to Unreleased

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [50e7e104] Triage Analyze (javascript-typescript) failure on backend-only diff (#500)

* [50e7e104] Split CodeQL workflow so JS/TS analyzer only runs on panel changes

* [50e7e104] docs(backend): document split CodeQL workflow triggers and branch protection notes

---------

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [203c426b] Triage and fix e2e lifecycle smoke (scripted agents) failure (#502)

* [203c426b] fix(orchestrator): pre-initialize _instances in __new__ so __init__-bypass tests survive _dispatch_audit_work; allow audit_interval_seconds=0; mount /api/notifications in e2e harness

* [203c426b] fix(e2e_smoke): restore ROBOCO_AGENT_TOKEN isolation and clarify /api/notifications mount comment

* [203c426b] docs(map): document orchestrator __new__ pre-init and e2e harness token isolation for auditor-revival smoke fix

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [48cb05c2] Fix remaining e2e lifecycle smoke (scripted agents) failure on auditor-revival PR #503 (#504)

* [48cb05c2] Harden AgentOrchestrator __new__ pre-init for auditor dispatch state

* [48cb05c2] Document auditor-dispatch pre-init rationale in AgentOrchestrator __new__

* [48cb05c2] docs(orchestrator): extend __new__ pre-init docs for auditor-dispatch state

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>

* [90c9474c] intake: ambient workspace note + dedupe scope clones by git_url

Two intake follow-ups folded into 90c9474c's spec Notes:

(a) _resolve_intake_ambient now prepends a workspace note so the intake
    agent knows its cwd holds clones of every project in the scope (the
    primary at cwd, siblings alongside under /data/workspaces) and drafts
    against the real trees via Grep/Glob/Read, not from memory.

(b) _clone_intake_scope dedupes slugs by git_url before cloning. A
    multi-project scope can list several projects pointing at one repo
    (a monorepo's cell-projects share a git_url); cloning each produced
    redundant identical workspaces. Mirrors CI-watch's per-git_url dedupe:
    keep the first slug per non-empty git_url; a project with no/empty
    git_url is never collapsed onto another so distinct local repos still
    clone. The dedupe is a pure static helper (_dedupe_slugs_by_git_url)
    with unit coverage.

---------

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-13 15:41:00 +02:00
192524265c [f309463f] Systematic tooltip and aria-label pass across the entire panel (#484)
* [001c9a7a] Author tooltip/aria-label spec for the panel (#469) (#473)

* [001c9a7a] docs(ux_ui): add tooltip/aria-label classification spec for panel controls

* [001c9a7a] docs(ux_ui): commit missing tooltip/aria-label spec content

Prior commit's message claimed to add the spec but only touched
unrelated generated lifecycle prompt files — the actual spec file was
never git-added. This commits the real content.

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>

* [dbe222aa] Implement tooltip and aria-label sweep across all panel surfaces (#478)

* [6f991331] Add aria-label + matching tooltip per tooltip-aria-label-spec.md (#476)

* [6f991331] feat(panel): add aria-label + matching tooltip to 8 icon-only controls per tooltip-aria-label-spec.md §1a/§1b, wrap assignee-avatar initials in a full-name tooltip

* [6f991331] docs(accessibility): add icon-only controls pattern guide for aria-label + matching tooltip

Documented the implemented pattern for accessible icon-only controls across 8 components (bell, back-arrow, menu, toggle, drag-handle, move-forward, settings, review-link) plus the assignee-avatar tooltip. Covers when to apply the pattern, naming conventions, state-dependent labels, testing approach, and rationale for local TooltipProvider scope.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [e34da833] Fix notification-bell.tsx and assignee-avatar.tsx, re-verify all 9 claimed tooltip/aria-label retrofits (#480)

* [e34da833] test(notifications): add regression coverage confirming the bell button's aria-label/title/Tooltip and re-verify the other 8 tooltip-aria-label-spec controls by direct file read

* [e34da833] docs(ux_ui): update tooltip-aria-label-spec.md status to "implemented" with test coverage summary

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [09414273] fix(header): wrap refresh button in Tooltip; correct spec.md and accessible-icon-buttons.md doc-accuracy issues (#483)

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [f309463f] fix: missing tooltip/Link/ArrowLeft imports + dedupe command-center tooltip import, drop redundant native title on refresh button, reflow doc prose

- kanban-card.tsx, header.tsx: import TooltipProvider (used but undefined -> eslint react/jsx-no-undef, blocked Panel lint + QA image panel build)
- task-header.tsx: import Link (next/link) and ArrowLeft (lucide-react) for the back button tooltip
- command-center.tsx: remove the duplicate tooltip primitive import block (kept the one with TooltipProvider; tsc duplicate-identifier)
- header.tsx: drop native title= on the refresh button now that a Radix Tooltip carries the hint (header test expects no native title)
- docs/frontend/components/accessible-icon-buttons.md: reflow hard-wrapped prose (python gate make reflow-docs)

* [f309463f] chore: regenerate lifecycle artifacts + verb tables (reconcile after master merge)

The branch's generated intro prose in agents/prompts/_generated/lifecycle-*.md
and verbs.md had drifted to unwrapped lines (master is wrapped). The foundation-
check gate (make lifecycle + regenerate_verb_tables + git diff --exit-code) caught
the drift. Re-rendered via the canonical generators; no hand-edits.

* [f309463f] Close remaining a11y gaps: aria-labels on task-table row-expand + pagination, titles on work-session truncated task-id/branch, secretary Start loading label

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-13 06:38:48 +02:00
1114ee5ea0 [77719d3f] A2A team telemetry: coordination event notifications for 5 event types (#477)
* [13d03d5c] Add 5 coordination-event notification producers + wire at chokepoints (#472) (#474)

* [13d03d5c] Add 5 coordination-event notification producer methods

* [13d03d5c] Wire reassignment/collision/unblock/dependency-revival notifications

* [13d03d5c] Wire stale-claim-reaped notification into orchestrator reaper

* [13d03d5c] fix(runtime): guard reaper's UUID annotation + defensive attr access

The stale-claim-reaped notification hook added a runtime-unquoted
`UUID` type annotation (only imported under TYPE_CHECKING, so the
module raised NameError on import) and a direct `t.assigned_to`
attribute access that crashes against the minimal test doubles the
existing reaper test suite uses. Quote the annotation and switch to
getattr-defensive access, matching `_assignee_is_provider_parked`'s
existing convention in the same file.

* [13d03d5c] test(notification): unit coverage for 5 coordination-event producers

One test per new send_* method (reassignment, collision-sequencing,
unblock, dependency-revival, stale-claim-reaped) following the
existing _FakeDb/_patch_db_context pattern, asserting subject/body/
related_task_id/priority/recipient-count, plus a no-recipients no-op
case for reassignment.

* [13d03d5c] test(task): prove reassign + unblock don't double-fire notifications

Two chokepoint-level tests mocking NotificationService at its defining
module: a repeated reassign() to the same already-current target skips
the notification (guarded by comparing against the pre-mutation
assignee), and a repeated unblock() on the same task only notifies
once since the second call short-circuits on the status!=BLOCKED
guard.

* [13d03d5c] style(task): ruff format the collision-sequencing wiring block

No behavior change — reflows the newly-added _notify_collision_sequencing
call site to satisfy ruff format's line-length rules.

* [13d03d5c] docs(backend): add coordination-event notification producers guide

Documented the 5 new NotificationService producers (reassignment, collision-sequencing,
unblock, dependency-revival, stale-claim-reaped) with fire conditions, double-fire
prevention mechanisms, and implementation patterns. Updated backend README to link the
new services guide for developers integrating new coordination events.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [3ee8150b] Frontend: render coordination-event notifications + e2e smoke coverage (#475)

* [69777c3a] test(e2e-smoke): add coverage for soft-block + unblock coordination notifications (#471)

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>

* [8eb82639] Render 5 coordination-event notification types with task deep-links (#470)

* [8eb82639] feat(notifications): add APPROVAL type icon and deep-link component test

Add missing APPROVAL member to the frontend NotificationType enum to
match backend roboco/models/base.py, wire its icon into the existing
typeIcons Record in the notifications page, and add a component test
covering type rendering and the task deep-link.

* [8eb82639] docs(notifications): document 5 coordination-event types and APPROVAL enum addition

Added comprehensive reference guide explaining the 5 notification types
(TASK_ASSIGNMENT, BLOCKER_ESCALATION, REVIEW_REQUEST, DOCUMENTATION_REQUEST,
APPROVAL), their visual identities (icon + color), use cases, and
deep-linking behavior to related tasks. Updated panel README with quick
reference table. TypeScript Record pattern ensures exhaustive type coverage
at build time.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [a27de2a8] fix(docs): reflow hard-wrapped notification-types.md to pass markdown gate (#479) (#481)

The Python quality gate on assembled PR #477 was red because the newly
added docs/frontend/components/notification-types.md (introduced by the
frontend coordination-event rendering commit) had manually wrapped prose
paragraphs, which scripts/reflow_md.py --check rejects as part of make
quality. Reflowed the file with scripts/reflow_md.py --apply (whitespace
only, no content change) so the check passes. ruff format/check, mypy,
xenon, vulture, bandit, and the full pytest suite (10284 passed) all
confirmed green on this commit; notification.py, task.py, and
orchestrator.py are untouched.

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [705419d5] Remove duplicate unblock notification and fix its dependent tests (#485) (#488)

* [705419d5] fix(notifications): remove duplicate unblock notification, fix its tests

The /unblock route was still calling delivery.notify_assignee_of_unblock()
(TASK_ASSIGNMENT) after TaskService.unblock() already sent the
send_unblock_notification() ALERT wired in by an earlier task — a real
duplicate notification on every unblock. Delete the route-layer call and
the now-dead NotificationDeliveryService.notify_assignee_of_unblock
method, fix the integration test that mocked it, and fix/extend the e2e
notification-coordination-events test to assert the persisted ALERT rows
(exact subjects) for both the direct-unblock and dependency-revival
producers instead of the old TASK_ASSIGNMENT assertion.

* [705419d5] docs(backend): update coordination-events doc for unblock duplicate removal

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [6c142a73] docs(changelog): document restored coordination-event notification producers and add collision-sequencing double-fire test (#489) (#490)

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>

* [77719d3f] Seed system agent in e2e harness to fix unblock/dependency-revival notifications

The e2e harness's seed_company omitted the system sentinel agent that
production seeds via initial_data.py. The unblock and dependency-revival
notification producers default to from_agent="system", which
_resolve_agent_uuid looks up by slug in the DB. With no system row the
resolver returns None and _create_notification silently skips the
notification, so the two ALERT assertions got 0 rows instead of 1.

The soft-block test passed because it uses NotificationDeliveryService
which creates the notification directly with a real agent UUID as
from_agent, bypassing the slug resolution path entirely.

* [77719d3f] Use foundation UUID for system agent to avoid slug collision

The first attempt seeded the system agent with a random UUID. Other
tests (_seed_system_and_secretary, _seed_video_agents) check by the
fixed foundation UUID via session.get(AgentTable, uuid); not finding
it they INSERT their own system row, hitting ix_agents_slug. Using the
foundation UUID makes their check find the seed_company row and skip.

* [77719d3f] Fix dependency-revival notification event loop mismatch

The dependency-revival test calls _unblock_dependents directly via
stack.run_db, which creates a new asyncio event loop. Inside,
_notify_dependency_revival -> NotificationService._create_notification
opened its own session via get_db_context(), which reuses the singleton
_DbHolder engine — bound to the FastAPI server's event loop. The
asyncpg connection raised 'Future attached to a different loop' and the
exception was silently caught + logged as a warning, so the notification
never persisted and the test saw 0 rows.

Fix: add an optional db_session parameter to _create_notification and
the two send methods. When provided, use the caller's session directly
and skip the internal commit (the caller owns the transaction). The
TaskService's _notify_unblock and _notify_dependency_revival now pass
self.session, keeping the notification in the same event loop + session
as the task transition.

* [77719d3f] Scope system-agent seeding to notification tests only

Seeding the system sentinel in seed_company (commits 3bba7b32/617b7890)
fixed the 0-notification bug but caused 3 i_documented gateway_timeout
failures: every e2e test now paid notification-creation latency for
system-origin notifications that were previously silently skipped,
pushing the already-slow i_documented verb past its 120s timeout.

Move system-agent seeding out of seed_company and into a scoped
_seed_system_agent helper called only by the two coordination-event
tests that exercise send_unblock_notification /
send_dependency_revival_notification (both resolve from_agent='system'
via DB lookup). dev_lifecycle and state_machine tests revert to the
pre-fix behavior (system-origin notifications silently skipped, no extra
latency).

The event-loop fix (commit 7b95d77d: pass db_session=self.session to
_create_notification) is unchanged — dependency_revival still needs it
because stack.run_db creates a new event loop while _DbHolder.engine is
bound to the FastAPI server loop.

* [77719d3f] Fix reassignment notification deadlock + suppressed-notification commit regression

Two fixes in notification.py / task.py:

1. Cross-session self-deadlock in send_reassignment_notification:
   TaskService.reassign() flushes an uncommitted row lock on the task,
   then calls _notify_reassignment -> send_reassignment_notification ->
   _create_notification(db_session=None) which opens a SEPARATE session
   via get_db_context() and INSERTs a notification with related_task_id
   FK -> tasks.id. The FK key-share lock blocks on the request session's
   uncommitted exclusive lock, but the request can't commit until the
   notify returns -> 120s verb hard-cut. Fix: pass db_session=self.session
   so the notification joins the verb's own transaction, same pattern as
   the unblock/dependency-revival fix in 7b95d77d.

2. Suppressed-notification commit regression: the 7b95d77d refactor moved
   await db.commit() out of _create_notification_with_session into
   _create_notification's db_session=None branch, where it ran
   unconditionally — even when _create_notification_with_session returned
   early (suppressed: unresolvable from_agent / no recipients /
   refire-guard / dedup-hit). Fix: _create_notification_with_session now
   returns bool (False at each early return, True after delivery);
   _create_notification commits only when created is True.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-13 06:38:15 +02:00
cea3e56628 feat(lifecycle): revision findings ledger — structured failure feedback, persisted and delivered down the chain (#486)
* feat(lifecycle): revision findings ledger — structured QA/PR/PM/CEO failure feedback, persisted and delivered down the chain

Every bounce used to survive only as flattened prose: rounds overwrote each
other in notes_structured, request_changes persisted nothing, two raw
dev_notes appends were silently destroyed by the next handoff note, and the
dev prompt pointed at fields (qa_notes via evidence(), pm_notes) the API
never delivered. Agents re-interpreted and re-discovered every failure
before they could start fixing it.

- task_review_findings (migration 071, append-only): file/line/severity/
  criterion(AC-id-validated)/expected/actual/fix/evidence per finding, with
  origin (qa|pr_gate|pm|ceo), round, and an open->addressed->verified
  lifecycle (waived reserved); new tasks.pm_notes + PmReviewContent give
  request_changes a structured home
- producers: fail_review/pr_fail/request_changes take findings=[...] (prose
  issues shimmed+merged for one release, deprecation-logged); ceo_reject
  validates its reason (no 500), lands an origin=ceo finding, and bumps
  round+audit on branchless coordination roots; guardrails at the verb
  chokepoint (nudge >5, hard reject >10, field caps, traversal-safe file);
  the dev_notes data-loss appends are removed; new task.request_changes +
  task.ceo_reject audit events close rework attribution
- delivery: qa_notes/pr_reviewer_notes/pm_notes carry the deterministic
  [F-id8] rendering; claim briefings, evidence(), the REVISION_REQUIRED
  spawn prompt, PM triage bounced-blocks, and A2A bodies deliver open
  findings; round-N+1 QA and gate reviewers get the full prior ledger;
  panel Findings tab + bounced-xN chip; metrics pm_rejects/ceo_rejects +
  findings counts; vault task notes render a Findings section (fail-open)
- resolution closes for every origin: i_am_done and submit_up/submit_root
  take resolved_findings gated by FINDINGS_ADDRESSED (owner-gated so a
  stale non-owner PM can never mutate the ledger); pass_review/pr_pass/
  complete verify-stamp same-transaction; ceo_approve stamps best-effort
- 24 real-DB integration tests drive the full loop through the real
  choreographer; full suite 12856 green

* docs: revision findings ledger sweep — CLAUDE.md, map, RAG corpus

- CLAUDE.md: new ledger section + corrected request_changes row
- docs/map/review-findings.md (new subsystem map) + surgical updates to
  task-service/pr-gate-review/metrics-observability/vault/panel maps
- docs/rag: producers' findings contract across qa/pr-reviewer/developer/
  cell-pm/main-pm/ceo role docs (the PM docs were missing request_changes
  entirely), verb references, and a new architecture/review-findings.md
  disambiguating ledger findings from convention findings

* test(e2e): resubmit resolves the pr_fail finding per the ledger contract

The scripted pr_fail revision loop resubmitted submit_up without
resolved_findings — correctly rejected now that FINDINGS_ADDRESSED gates
the PM resubmit verbs (green locally, red only in CI since the e2e suite
skips without ROBOCO_E2E_SMOKE=1). The scripted PM now reads the open
ledger row pr_fail persisted (new open_finding_ids arc helper) and
resolves it on resubmit, asserting the open set drains — exercising the
coordinator half of the new contract end to end.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 22:54:42 +02:00
d03181ab48 feat(vault): Obsidian vault V2 — janitor, archival, weekly report, KB ingest, Bases + sync runbook (#482)
* feat(vault): V2 — create-seam + drift janitor, archival, weekly org-report, KB ingest, Bases views + sync runbook

Implements the vault V2 canonical spec end to end (the splice guard shipped
separately and is reused at KB-ingest time):

- materialize-on-create: TaskService.create writes each task's note best-effort
  from the moment it exists; the transition-touch stops no-oping on live work
- drift janitor (services/vault_janitor.py + hourly _vault_janitor_loop): daily
  changed-task re-projection, random drift sample, archival pass — restart-proof
  via RoboCo/_meta/.janitor_state.json, 200/cycle caps, per-item isolation,
  processed-only resume markers, self-repairing state file
- archival: vault_archive_days (30, 0=off) moves old terminal tasks' notes to
  RoboCo/Archive/<year>/Tasks/<project>/ — one write_task code path for janitor
  and rebuild, id8 lookup across Tasks/+Archive/, alias links keep moves safe
- weekly org-report: VaultWriter.write_org_report renders Reports/<ISO-week>.md
  from MetricsService/UsageService (numbers duplicated into frontmatter for
  trend queries), once per ISO week, with a best-effort CEO notification
- KB ingest: IndexType.VAULT_NOTES + VaultNotesIndexPlugin + _vault_kb_loop
  embed the CEO's RoboCo/Notes into the RAG corpus — injection guard as a hard
  gate (flagged notes quarantined with an idempotent callout), traversal- and
  symlink-contained at both config and engine layers, content-hash dedup,
  50-ingest/cycle cap, frontmatter stripped; reaches roboco_kb_search, the
  mentor default domain, claim-time briefings (kind vault_note), and the panel
  KB browser; no migration (chunks table auto-creates; migration 030's
  CHUNK_TABLES tuple appended per the chunks_playbooks precedent)
- Bases views (Task Board.base, Reports.base — schema verified against the
  Obsidian docs) + the Mac sync runbook vault asset
- config/flags/compose: vault_archive_days, vault_report_enabled (flags card),
  vault_kb_enabled (flags card; NAS compose arms it, registry ships it off),
  vault_kb_dirs (+ overlap/traversal validator), vault_kb_interval_seconds
- e2e smoke (tests/e2e_smoke/test_vault_v2.py): real create-seam, real janitor
  cycle incl. archival + state, real KB engine + real guard

* docs: vault V2 sweep — map, RAG corpus, CLAUDE.md

- docs/map/vault.md: V1+V2 — janitor/archival/report/KB data flows, new files,
  config, health posture
- docs/map/orchestrator.md + task-service.md: the two new loops, the create
  seam, the three janitor queries
- docs/rag/architecture/obsidian-vault.md: agent-facing what-changed (notes
  from creation, archive link-safety, CEO notes retrievable, weekly report)
- docs/rag/architecture/config-reference.md: the five new settings
- CLAUDE.md: vault paragraph covers V1+V2; flags-card list mentions the vault
  report/KB flags

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 15:51:19 +02:00
5a0fce7da4 docs: v0.23.0 agent-facing sweep — map, RAG corpus, CLAUDE.md (#468)
New map + RAG entries for the vault subsystem; sequence gate, lineage
merge, gate diff-base, CI guard, playwright MCP, dispatcher prefilter,
backup sidecar, and the 300/100 budget reflected across docs/map,
docs/rag, and CLAUDE.md; stale claims fixed (agent-ux 'no extra tools',
old budget defaults). No redirects needed — nothing publicly published
moved.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 10:19:32 +02:00
f7f411e112 fix(infra): release builds all 17 registry images; pg_dump backup sidecar (#461)
* fix(infra): release builds all 17 registry images; pg_dump backup sidecar

release.yml was missing roboco-agent-grok-prompter and
roboco-agent-grok-secretary (both FROM the bare local roboco-agent-grok
tag, so agent-grok now builds explicitly ahead of the loop, mirroring
the agent-base special case) — a fresh registry pull could never
succeed. Both compose files gain a backup sidecar on the data network:
pg_dump -Fc on start and every 24h, crash-safe tmp+rename, newest-14
rotation, restore walkthrough in docs/backend/ops/database-backups.md.

* chore(docs): reflow hard-wrapped prose inherited from the six-PR merge train

* chore(foundation): regenerate lifecycle artifacts; reflow inherited prose

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 09:20:57 +02:00
6e57066bd6 [a360b6e3] Redesign A2A page with conversation-first layout and agent identity (#401)
* [54b94e44] A2A page: filter controls + agent identity consistency (#387) (#392)

* [54b94e44] feat(a2a): add filter bar and unify agent avatars + pulse across views

Adds a status (active/all) + free-text search filter bar above the A2A
switchboard/list content, backed by a shared a2a-filter-utils module so
both A2ASwitchboard's pairs and A2AConversationList's conversations
narrow identically. Extracts A2APairCard's pulse-flash state into a
reusable usePulseFlash hook and exports its PairAvatar so the classic
conversation list now renders the same two-participant avatar and
emerald pulse-flash affordance the switchboard already had.

* [54b94e44] docs(a2a): add comprehensive filtering and avatar documentation

Documented the new A2A filter bar, filter utilities, pulse-flash hook, and
conversation list API changes. Includes examples, testing guidance, and
migration notes for the pulses prop requirement.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [54417f0c] UX/UI: design A2A conversation-first layout and agent identity (#399)

* [f612a5ab] Add conversation-first layout, agent identity, and live-stream affordance spec (#384)

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>

* [7ed2ef71] docs(ux_ui): add filter-control design spec for A2A conversations (#383)

Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>

* [f563bbc9] Implement conversation-first A2A layout, identity colors, connection states, transcript motion, and empty/error states (#423) (#427)

* [f563bbc9] feat(a2a): conversation-first layout, team-color identity, connection states, transcript motion, empty/error states

Implements docs/ux_ui/design/02-conversation-first-layout-agent-identity-live-stream.md:
- xl:+ collapsible Context pane (identity cards, linked-task summary, no-task hint), persisted via the existing zustand ui-store
- getAgentTeamColor + TEAM_COLOR_CLASSES in agent-utils.ts, applied to PairAvatar, the transcript row avatar, and the context pane
- A2AConnectionBadge/A2AConnectionBanner rendering all four ConnectionState values distinctly with a motion-reduce-guarded pulsing dot and a dismissable reconnecting/disconnected strip
- A2ATranscript: transform/opacity-only new-row entrance transition, scrolled-up "New messages" pill, split hasSelection/empty/error states with a scoped Retry
- Unit tests for every new pure helper and component

* [f563bbc9] docs(a2a): conversation-first layout, team-color identity, connection states, transcript motion, empty/error states

Document the new conversation-first A2A layout features:
- Agent team-color system (getAgentTeamColor, TEAM_COLOR_CLASSES) for six cell buckets
- A2AContextPane component with identity cards, linked task summary, no-task hint
- Connection state rendering (A2AConnectionBadge, A2AConnectionBanner) for all four ConnectionState values
- Transcript entrance motion with transform/opacity-only transitions and prefers-reduced-motion guards
- Split empty/error states (no selection, no messages, fetch error with scoped retry)
- Page-level integration with xl:+ responsive grid layout

Includes component API, usage examples, testing guidance, accessibility notes, and design rationale.

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [478f027c] Implement A2A conversations filter control per conversations-filter-control.md (#445) (#448)

* [478f027c] feat(a2a): add multi-dimension Popover filter panel for A2A conversations

Replace the free-text search + active/all toggle with the Popover-triggered
filter control from conversations-filter-control.md: Agent multi-select
checkboxes, a Task id-fragment input with a "No linked task" toggle, Status
toggle buttons, and a date range, plus an active-filter chip row and Clear
all. Filtering applies to both the switchboard (Agent only) and conversation
list (all four dimensions) per the design doc's per-view rules.

* [478f027c] docs(a2a): add comprehensive filter-control guide covering component API, filter dimensions, and per-view rules

Documents A2AFilterBar component and filter utilities with:
- Component API and props
- All 4 filter dimensions (Agent, Task, Status, Date range)
- Per-view rules (Switchboard vs List)
- Usage examples and parent setup
- Filter logic and match predicates
- Testing guide and accessibility notes
- Design notes on client-side filtering limitation
- Links to related components and the design spec

Helps developers understand, use, and maintain the A2A conversations
filter control without needing to read the design doc or component source.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 07:46:29 +02:00
58354a364e [e56e6543] Reorder sidebar, rename A2A, remove Notifications entry, 2-col objectives (#394)
* [11e82f2e] Frontend: sidebar reorder, A2A rename, Notifications removal, 2-col objectives (#393)

* [946802b2] Objectives editor: 2-column desktop grid, 1-column mobile (#385)

* [946802b2] feat(goals-tab): objectives editor 2-col grid on desktop, 1-col mobile

* [946802b2] docs(goals-tab): document ObjectivesEditor responsive grid layout

Added comprehensive JSDoc comment explaining the 2-column desktop / 1-column mobile responsive grid layout for objective cards. Documents the grid-cols-1 / md:grid-cols-2 classes, the gap spacing, and clarifies that the '+ Add objective' button sits as a full-width sibling below the grid rather than as a grid item. Includes a visual layout structure for future reference.

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [639c0d54] Sidebar: reorder + dividers + A2A rename + remove Notifications (#389)

* [639c0d54] feat(panel): sidebar dividers, A2A rename, remove Notifications entry

Group navItems into six sections rendered with a visible Separator
between each group in SidebarNav (shared by desktop + mobile Sheet),
rename the /a2a entry from "A2A Live" to "A2A", and drop the
Notifications entry from the sidebar (/notifications stays reachable
via the header's NotificationBell). Adds sidebar.test.tsx covering
group dividers, the rename, the removed entry, item order, and the
collapsed icon-only state.

* [639c0d54] docs(sidebar): add navigation structure and grouping documentation

Document the six-group sidebar organization with dividers, the A2A rename
from "A2A Live", and the removal of Notifications from the sidebar. Covers
visual behavior across desktop expanded/collapsed and mobile states,
data structure rationale, and testing. Explains that Notifications remains
accessible via the header NotificationBell.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

* [92a46054] Fix sidebar: exact flat order + move Business to footer (#415)

* [11a612e2] Flatten sidebar nav order + move Business to footer (#413)

* [11a612e2] fix(panel): flatten sidebar navItems + move Business to footer

* [11a612e2] docs(sidebar): update navigation structure documentation for flat navItems + Business in footer

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [dc518639] revert(business): drop out-of-scope 2-col objectives grid from PR #415 (#418)

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [e2b50b06] Re-implement 2-column objectives grid in goals-tab.tsx (#435)

Branch rebuilt from the root fork point so the assembled delta against
master contains ONLY this task's work: the responsive objectives grid
(grid-cols-1 md:grid-cols-2) and its test. The prior branch inherited the
root's sidebar work into the against-master view, which the PR gate
correctly flagged as an AC4 violation.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 07:42:37 +02:00
eefaca1d3b [3dfc43a1] Task detail overhaul: markdown, navigation, collapsible sections, timestamps (#410)
* [35a27c3d] UX/UI: design task-detail overhaul (#404)

* [39ea1900] docs(ux_ui): add content-readability spec for markdown, collapsible sections, timestamps (#388)

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>

* [71f9aec6] docs(ux_ui): add task navigation/structure design spec (#400)

Adds docs/ux_ui/design/task-navigation-structure.md covering the
breadcrumb trail, prev/next sibling navigation, and a distinct visual
treatment for the read-only constraints section, grounded in the real
task-detail components and existing amber/Lock read-only tokens.

Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>

* [9baa1c34] Frontend: implement task-detail overhaul (#408)

* [13b6c723] Task detail: inline timestamps + breadcrumb + prev/next navigation (#390)

* [13b6c723] feat(panel): add inline absolute timestamps, task breadcrumb, and prev/next list nav to task detail

Adds a shared formatAbsoluteTimestamp helper used inline (with tooltip)
next to relative time on progress updates and checkpoints in
tab-progress.tsx, progress-timeline.tsx, and checkpoint-card.tsx.
Adds TaskBreadcrumb (renders only when task.parent_task_id is set) and
TaskListNav, which reads a new taskListNav context in the
scroll-restoration zustand store — populated by the Tasks list page from
TaskTable's live filtered/sorted order — to move to the adjacent task.
When no list context exists for the session or the current task isn't
part of the captured order, both nav buttons render disabled with an
explanatory tooltip (the documented fallback).

* [13b6c723] docs(guide): task detail navigation, timestamps, breadcrumb, and prev/next behavior

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [40acdd31] Task detail: collapsible markdown sections + distinct Constraints styling (#407)

* [40acdd31] feat(panel): collapsible task-detail sections + distinct Constraints styling

Wrap the Description, per-field Notes, and Plan cards in a new
CollapsibleSection (Radix Collapsible + tw-animate-css fade/slide, so
collapse/expand only animates opacity/transform) so a long task no longer
forces continuous scrolling. Restyle the read-only Constraints card with an
amber accent border, background tint, and ShieldAlert icon so it reads as
distinct from authored content. Existing edit/preview toggles are
force-open while active and otherwise unchanged. Adds a global
prefers-reduced-motion override in globals.css.

* [40acdd31] docs(panel): CollapsibleSection component API and usage guide

Documents the new CollapsibleSection wrapper component used for independent collapse/expand of task-detail sections (Description, Constraints, Notes, Plan). Covers component API, controlled vs. uncontrolled state patterns, animation behavior (fade+slide, transform/opacity only), prefers-reduced-motion handling, and usage examples across task-description.tsx / tab-notes.tsx / tab-plan.tsx.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [73f8311f] fix(task-table): remove exhaustive-deps suppression on visible-order effect (#409)

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

* [eb417ef1] Fix: apply auto-collapse thresholds to Progress and Acceptance Criteria surfaces (#429)

* [4e855d24] Apply content-readability-spec collapse thresholds to Progress and Acceptance Criteria surfaces (#416)

* [4e855d24] feat(task-detail): auto-collapse long progress/checkpoint/AC content per readability spec

* [4e855d24] refactor(task-detail): remove inline JSX section-marker comments per no-inline-comments convention

* [4e855d24] docs(task-detail): document content-readability-spec collapse thresholds for CollapsibleSection

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [3c90ef34] Wire content-readability thresholds into CollapsibleSection, tab-progress, acceptance-criteria (#430)

* [3c90ef34] test(task-detail): add AC4 combined readability test — 30+ progress entries + long acceptance-criteria list

* [3c90ef34] docs: enhance content-readability thresholds documentation and code comments

- Enhance panel/src/lib/content-readability.ts with usage examples and clarified intent
- Enhance CollapsibleSection with auto-collapse logic explanation and precedence rules
- Enhance TabProgress's RECENT_OPEN_COUNT logic with dual-threshold explanation
- Add comprehensive architecture guide: panel/docs/CONTENT_READABILITY_THRESHOLDS.md covering thresholds, components, testing, and implementation notes

The readability feature prevents long-history tasks (30+ updates, 20+ criteria) from rendering fully expanded, keeping pages navigable. Tests confirm 32 progress updates default to 2 open, and long criteria lists collapse while short ones stay expanded.

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [fc04d84a] Round-3 revision: fix 4 named gaps on task-detail overhaul, one dev leaf per fix (#455)

* [cac9b603] fix(panel): fall back to task.created_at for missing written_at stamp in tab-notes.tsx (#446)

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>

* [31dd4f99] Remove ArrowLeft back button from task-header.tsx (#441)

* [31dd4f99] Remove ArrowLeft back button and Link wrapper from task-header.tsx, drop now-unused imports

* [31dd4f99] docs(task-navigation): mark spec as implemented, clarify ArrowLeft button removal

Update task-navigation-structure.md to reflect v0.21.0+ implementation:
- Status changed from "proposed" to "implemented"
- Clarified that ArrowLeft back button was removed from task-header.tsx
- Noted that breadcrumb and prev/next navigation now provide all navigation
- Constraints section styling with amber tint and ShieldAlert icon is complete
- Referenced related guide documentation for task-detail-navigation features

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [75fd7444] Wire content prop into EditableNoteCard's CollapsibleSection (#449)

* [75fd7444] feat(panel): wire content prop into EditableNoteCard's CollapsibleSection

Pass the note field's current value into CollapsibleSection's content
prop and derive EditableNoteCard's initial sectionOpen state from
exceedsReadabilityThreshold, so long notes default collapsed with an
expand affordance while short notes render fully expanded.

* [75fd7444] docs(panel): document EditableNoteCard's content-driven collapse pattern in collapsible-section.md

Updated docs/frontend/components/collapsible-section.md to reflect how EditableNoteCard in tab-notes.tsx uses both controlled mode (force-open while editing) and content-driven initialization (seed sectionOpen from content length). Added a new "Combined: controlled + content-driven initialization" example showing this pattern for future developers extending editable-content sections.

Pattern: long notes default collapsed with expand affordance, short notes default expanded, edit forms always visible during editing.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [18ada610] docs(ux-ui): reconcile prev/next nav design spec with shipped list-order behavior (#453)

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

* [3dfc43a1] round-3 fixes: reconcile nav spec, Alt+Arrow shortcuts, CHANGELOG

The breadcrumb section of task-navigation-structure.md now describes the
shipped single-ancestor design (and drops the stale DropdownMenu claims);
Alt+ArrowLeft/Right on TaskListNav mirror the visible prev/next buttons,
suppressed while an editable element has focus, with tests; the
user-facing CHANGELOG entry lands under Unreleased. Also reflows the
round-1 content-readability-spec so the prose gate is green branch-wide.

* [3dfc43a1] blank line between Unreleased and 0.22.0 sections

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 07:41:15 +02:00
f0f09b2204 [1197c975] Re-add Playwright chromium to FE/UX QA images and add browser-verification prompt guidance (#406)
* [3e552255] Re-add Playwright chromium to QA images + prompt guidance (#395) (#405)

* [3e552255] feat(docker): re-add Playwright chromium-headless-shell to QA images

* [3e552255] ci(docker): add Playwright QA image build + headless smoke check workflow

* [3e552255] fix(ci): scope agent-image-smoke.yml trigger to paths only, add PR comment

The workflow was gated by `branches: [master]` on both push and
pull_request, but this repo's task-hierarchy PRs open against nested
parent feature branches, not master, until root->master assembly - so
the workflow never fired on a dev-level PR and produced zero evidence.
Drop the branch filter (path scoping is sufficient) and post the
size-delta table + smoke-check output as a PR comment via
actions/github-script, since no agent role has gh CLI or GitHub API
read access to pull check-run output directly.

* [3e552255] fix(ci): post agent-image-smoke PR comment even on step failure

The 'Post results as a PR comment' step only had
`if: github.event_name == 'pull_request'`, which GitHub implicitly ANDs
with success() — so if the docker build or headless-launch smoke check
failed, the PR comment (the only evidence-delivery path QA/PM has, since
no agent role can read the Checks tab) silently never posted. Added
always() so a partial report always lands on the PR.

This commit also re-lands the branches-filter removal + PR-comment step
from 747e4d74 to make sure this fix actually reaches the remote PR
branch — QA's needs_revision at 05:48 came after that commit's local
timestamp (05:43) but still saw the pre-fix workflow, indicating the
prior push never reached GitHub.

* [3e552255] docs(qa): add browser verification guide and CHANGELOG entry for Playwright chromium

- Added comprehensive QA browser verification guide at docs/backend/qa/browser-verification.md covering setup, examples for fe-qa/ux-qa, limitations, and troubleshooting
- Updated CHANGELOG.md with Unreleased entry documenting Playwright chromium-headless-shell re-add to agent-qa-fe and agent-ux images, CI smoke workflow, and links to QA guidance
- Guide explains when to use browser verification (rendered output, computed styles, a11y, visual design), how to launch headless chromium, and provides practical examples for both FE and UX QA use cases
- References built-in guidance in fe-qa.md/ux-qa.md identity prompts and CI smoke workflow verification

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [9aafe8f5] Revision: fix reflow-check CI failure and resolve orchestrator Playwright-allowance gap (#421)

* [9973237c] fix(docs): separate reflow-joined metadata lines in browser-verification.md (#419)

scripts/reflow_md.py --check treats two adjacent non-blank lines as one
paragraph and flags it as hard-wrapped prose needing a join. Insert a
blank line between the `**For:**` and `**Purpose:**` metadata lines so
each stays its own single-line paragraph; verified the reflow is now a
no-op and the non-whitespace token sequence is unchanged.

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [cc355a2b] docs(qa): add no-op analysis for orchestrator.py Playwright allowance (#422)

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [a506cc10] docs(qa): quote real Dockerfile Playwright snippets in no-op analysis (#424)

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [2b35dd4e] Resolve merge conflict, confirm green CI, add Playwright MCP registration, re-verify 5 ACs (#447) (#450)

* [2b35dd4e] docs(changelog): resolve Unreleased/0.22.0 ordering conflict, keep Playwright entry

* [2b35dd4e] feat(runtime): register Playwright MCP server for fe-qa/ux-qa, per CEO round-3 note

Adds @playwright/mcp to agent-qa-fe and agent-ux images, wired via a wrapper
entrypoint that points the server at each image's already-baked
chromium-headless-shell instead of downloading a second browser. The
orchestrator registers the `playwright` MCP server only for the qa role on
the frontend/ux_ui teams, so be-qa and ux-dev never see it. Updates the QA
identity prompts and docs/backend/qa/browser-verification.md to document the
structured mcp__playwright__* tools in place of hand-scripted Bash+Python,
adds CI smoke coverage (binary + baked-chromium resolution + a real
panel-page screenshot from inside the ux-qa image), and records the change
in CHANGELOG.md.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [1197c975] type the mcpServers extraction so mypy's no-any-return passes

* [1197c975] extract role-scoped MCP registration — orchestrator back under the complexity budget

The playwright branch pushed _generate_mcp_config to rank C in the merge
ref; docs/research/playwright registrations move to one helper, behavior
identical.

* [1197c975] basename-sanitize the MCP config filename

CodeQL's path-injection query re-fired on the (moved, unchanged) config
write; agent ids are orchestrator-issued, but the filename now rides the
same os.path.basename sanitizer _grok_usage_json established.

* [1197c975] basename the agent id variable itself — the sanitizer shape CodeQL models

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 07:39:53 +02:00
7f138d3bf5 [e4ed92d6] Video pipeline per-project requests, re-render action, composition preview (#403)
* [7f2c881a] Project-scope video pipeline + re-render + preview proxy (#386) (#396)

* [7f2c881a] feat(video): scope on-demand video requests + render loop to project_id

Require project_id on VideoRequestBody (404 when unresolvable or not
opted into the video engine), thread it through VideoEngine.open_video_task
via a shared resolve_authoring_project helper, and resolve the render
loop's motion/ workspace from the authoring task's own project_id instead
of the hardcoded self_heal_project_slug.

* [7f2c881a] fix(video): cast task.id to UUID before VideoEngine.rerender calls

mypy flagged task.id as sqlalchemy.sql.sqltypes.UUID[Any] rather than
uuid.UUID in the three rerender tests; cast to UUID per the codebase's
established idiom (cast("UUID", obj.id)) used elsewhere for the same
SQLAlchemy Mapped-attribute inference gap.

* [7f2c881a] docs(video): API endpoints for project-scoped requests, re-render, and preview proxy

Add comprehensive API documentation for the new project-scoped video engine endpoints:
- POST /api/video/request: on-demand video authoring scoped to project_id (breaking change)
- POST /api/video/pipeline/{task_id}/rerender: CEO-triggered re-render with idempotency key clearing
- GET /api/video/preview/{task_id}/{file_path}: CEO preview proxy with path-traversal confinement

Document project-scoping architecture: authoring tasks and render loop now resolve from task's own project_id instead of hardcoded self_heal_project_slug.

Add migration guide covering breaking change to VideoRequestBody schema (project_id now required), error handling changes (404 on unresolvable/non-opted-in projects), and client migration steps.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [8f959c3b] docs(ux_ui): add project picker, re-render control, and composition preview panel spec (#381) (#398)

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>

* [1fb5b5cb] Project picker, re-render button, and composition preview panel (#397) (#402)

* [1fb5b5cb] feat(video): project picker, re-render button, and composition preview panel

* [1fb5b5cb] docs(video): add comprehensive guide for project picker, re-render button, and composition preview panel

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [a512f364] Add video_engine_enabled to ProjectSummaryResponse (#412) (#414)

* [a512f364] feat(api): surface video_engine_enabled on ProjectSummaryResponse

* [a512f364] docs(api): document video_engine_enabled on ProjectSummaryResponse

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [03607ab9] Fix re-render control gating/placement and project picker filter (#434)

* [f2f3e89f] Fix RerenderControl gating/placement across queue and strip views (#431)

* [f2f3e89f] feat(video): widen RerenderControl gating and share it across queue/strip views

Extracts RerenderControl into a shared panel/src/components/dashboard/
video-rerender-control.tsx component, widens its gate from
render_status === 'failed' to source_task_id + composition_id present
(matching what the backend rerender endpoint actually requires), adds a
confirm dialog before firing the mutation, and wires the same component
into video-pipeline-strip.tsx for still-in-flight rendering/render_failed
rows.

* [f2f3e89f] docs(video): enhance RerenderControl JSDoc with gating logic and usage examples

Add comprehensive JSDoc to the RerenderControl component covering its
purpose, gating logic (render for any source_task_id + composition_id,
regardless of render_status), three visual button states (idle/loading/
error), confirm-dialog guard behavior, and usage examples for both
video-post-queue.tsx and video-pipeline-strip.tsx contexts. Explains
why the backend's rerender endpoint doesn't require a failed render and
how the component prevents accidental re-renders.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [404d8ed3] Filter project picker to video-engine-enabled projects (#432)

* [404d8ed3] feat(panel): filter video-request project picker to opted-in projects

Add video_engine_enabled to the client ProjectSummary type, give
ProjectSelector a videoEngineOnly filter prop, default RequestVideoDialog's
picker to the current video-enabled project with a friendly empty-state
when none exist, and cover the filter with a new project-selector test.

* [404d8ed3] docs(panel): add ProjectSelector component API reference with videoEngineOnly filter

Document the reusable ProjectSelector component with its props, filtering behavior,
and new videoEngineOnly filter for video-engine-enabled projects. Follows the
existing component documentation pattern from page-refresh-provider.

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [519a4088] fix(panel): import missing RerenderControl in video-post-queue and correct stale doc (#436)

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>

* [8e912c3e] Reflow hard-wrapped video UX design doc to pass quality gate (#439)

* [ccfe2015] docs(ux_ui): reflow video request composition-controls spec to one line per paragraph (#438)

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>

* [99c3ed9c] docs(backend): reflow hard-wrapped prose in video-engine-endpoints.md and video-project-scoping.md (#442)

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>

* [c2e98fc0] docs(backend): strip stray trailing whitespace in video-engine-endpoints.md fence (#451)

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>

* [9c7bc11a] Reflow all 3 hard-wrapped docs on this branch and verify quality gate (#457)

* [9c7bc11a] test(scripts): guard reflow_md.py --check wiring into make quality

* [9c7bc11a] docs(standards): document markdown reflow quality gate workflow and verification

Added comprehensive guide explaining the one-logical-unit-per-line markdown prose standard, how the reflow check integrates into make quality, the three reflowed files (video-engine-endpoints.md, video-project-scoping.md, composition-controls spec), and the regression test added to ensure wiring stability. This task verifies all three ACs are satisfied: reflow_md.py --check exits 0, make quality passes (non-DB portions), and the three files are whitespace-only reflowed.

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: UX/UI Documenter <ux-doc@roboco.tech>

---------

Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: UX/UI Documenter <ux-doc@roboco.tech>

* [002f0cdd] docs(rag): document reflow-check zero-diff troubleshooting path (#459) (#460)

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [e4ed92d6] fix rerender missing-task test — assert the empty queue it creates

The test never seeds; the trailing assertion expected a phantom video
post. Broken since the branch's first commit but never executed — every
earlier CI run short-circuited at a pre-pytest gate step.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: UX/UI Documenter <ux-doc@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 07:39:17 +02:00
4d52f6ff59 [1f6a06a2] PR-review gate: verify ACs literally and require green CI before pr_pass (#428)
* [a1bde3b9] Add CI-status guard to pr_pass + update pr_reviewer prompt (#417) (#420)

* [a1bde3b9] feat(gateway): CI-status guard on pr_pass + reviewer prompt update

* [a1bde3b9] docs(pr-gate-review, worksession-git): document CI-status guard on pr_pass

Updated two architecture documentation files to reflect the new CI-status guard:

**pr-gate-review.md:**
- Documented _ci_status_guard method: blocks pr_pass on failing/pending/unscheduled/error CI with reviewer-aware pr_fail remediation
- Documented _resolve_ci_status: best-effort GitHub check-runs lookup with fail-open behavior
- Updated _pr_pass_blocked description: now returns (rejection_envelope, ci_note) tuple
- Updated _record_gate_verdict_for/verdict to note ci_status field stamping on pr_pass
- Added ci_note parameter documentation for evidence tracking when no CI is configured
- Updated Logical Tree to show new methods
- Added Config Flags note: CI guard is always armed, fails open on config gaps
- Added two regression risks: check-runs-only limitation, fail-open design

**worksession-git.md:**
- Documented GitService.get_pr_ci_status(project_slug, pr_number): CI status lookup with state classification
- Documented supporting methods: _ci_status_prereqs, _fetch_check_runs, _classify_check_runs, _classify_zero_check_runs
- Each method notes its fail-open behavior and configuration gap handling

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [e8f275d7] test(gateway): lock the 7-AC-to-test map + assert pr_reviewer prompt content (#425) (#426)

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>

* [24b4237e] Fix reflow-check, CI-status classification, and noqa suppression (#440) (#443)

* [24b4237e] fix(gateway): classify unreachable/nonexistent CI-status repo as no_ci_configured, remove test noqa, reflow pr_reviewer.md

Split GitService.get_pr_ci_status's PR-head-sha lookup into a dedicated
helper so a config gap (missing project/git_url/token) or an unreachable/
nonexistent repo/PR (network error or 404) classifies as no_ci_configured
(pr_pass passes through and stamps the evidence note) while a genuine
GitHub API failure on a real, reachable repo (any other non-2xx, or an
unparseable body) stays the fail-closed error state. Replaced the
`# noqa: PLR2004` in test_git_pr_ci_status.py with a named HTTP-status
range constant, updated the config-gap tests to assert the new
classification, and added tests for the unreachable-repo and real-repo-
API-error branches. Reflowed agents/prompts/roles/pr_reviewer.md's one
hard-wrapped continuation line so it passes make reflow-check.

* [24b4237e] docs(gateway): update pr-gate-review.md for CI-status classification refactor

Updated the internal architectural map to reflect the new CI-status classification
scheme introduced in PR #440. Configuration gaps (missing project/git_url/token) and
unreachable/nonexistent repos (404 or network error) now explicitly classify as
no_ci_configured and pass through with evidence stamps. Genuine GitHub API failures
on reachable repos classify as error and stay fail-closed (retryable).

- Clarified _ci_status_guard behavior: config gaps/unreachable repos pass through
  with distinct classification; only real API failures stay fail-closed
- Updated Config Flags section to describe the new three-way classification
- Updated Regression Risks section to document the new explicit classification scheme
- Noted that _resolve_ci_status now wraps git.get_pr_ci_status and interprets its result dict

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>

* [1f6a06a2] round-3 fixes: pr_gate back to xenon rank A; 404 means no CI, not error

Eight extracted helpers bring the module average from B(5.05) to A(4.04)
with every external contract untouched (170 gate tests byte-identical).
The CI-status guard now classifies a 404 on the check-runs or workflows
endpoints as no_ci_configured (pass-through with evidence note) —
a repo without Actions is not a transport failure — reserving the
fail-closed error state for network/5xx/auth failures, with pinning
tests for all four shapes. The e2e fake-GitHub router gains check-runs
and workflows routes so the scripted lifecycle exercises the guard's
green-CI success branch end to end.

* [1f6a06a2] merge master; align gate-diff-base tests with the tuple contract

The merged tree is the first integration of the CI-status guard with the
preferred-parent diff-base guard: _pr_pass_blocked now returns
(rejection, ci_note), so the diff-base tests unpack it instead of
asserting on a bare result. Both guards verified live in the merged
pr_gate (preferred_parent threading and _ci_status_guard present).

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 07:38:06 +02:00
08c02e2251 [79d686f0] Add page-scoped refresh button to the navbar (#351)
* [870467e6] Frontend: page-scoped refresh provider, hook, and navbar button (#347)

* [55376b8a] Create page-scoped refresh provider and context (#327)

* [55376b8a] feat(panel): add page-scoped refresh context and provider

* [55376b8a] docs(frontend): add page-refresh-provider component documentation

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* [a0c02d0f] Add public usePageRefresh hook (#332)

* [a0c02d0f] test(hooks): assert usePageRefresh is exported from hooks barrel

* [a0c02d0f] feat(hooks): add public usePageRefresh hook with provider and tests

* [a0c02d0f] fix(panel): move hook test wrappers to components and rename providers.tsx to unshadow barrel

* [a0c02d0f] docs(panel): document usePageRefresh hook and PageRefreshProvider API

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>

* [5f28dd9b] Add navbar refresh button and remove inline dashboard refresh buttons (#336)

* [5f28dd9b] Align PageRefreshProvider with active hook API and remove inline dashboard refresh buttons

* [5f28dd9b] Remove unused scope-keyed PageRefreshProvider, context, and associated tests

* [5f28dd9b] Address QA revision: add header refresh tests, page-scoped label, remove dead provider code and .venv symlink, revert formatting-only changes

* [5f28dd9b] Remove remaining inline dashboard refresh buttons and committed .venv symlink

* [5f28dd9b] docs(frontend): update page-refresh provider docs and panel README for navbar refresh button

* [5f28dd9b] fix(panel): remove .venv symlink, ignore root .venv entries, and thin task-detail page data fetch into useTaskDetail hook

* [5f28dd9b] Extract GitBrowser data fetching into useGitBrowser hook and add tests; verify .venv cleanup and task-detail thin hook usage

* [5f28dd9b] fix(panel): remove root .venv symlink, restore .gitignore anchored rule, and revert lifecycle.json formatting noise

* Delete .venv

---------

Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>

* [b8e1de1b] Fix navbar refresh button disabled state when registry is empty (#356) (#358)

* [b8e1de1b] fix(panel): derive navbar refresh disabled state from registry, not unused prop

PageRefreshProvider now computes `disabled` from whether any refresh
callback is currently registered (registry size > 0) instead of a
static, never-passed `disabled` prop that left the button permanently
enabled. header.tsx now destructures `disabled` from usePageRefresh()
and disables the button on `disabled || loading`. Updated the tests
that asserted the old always-enabled-by-default behavior and added a
new header test asserting the button is disabled with zero registered
callbacks.

* [b8e1de1b] docs(panel): document PageRefreshProvider disabled state derived from registry

Updated documentation to reflect the refactored PageRefreshProvider behavior: the `disabled` state is now derived from whether any refresh callbacks are currently registered (empty registry = disabled), rather than a static `disabled` prop. Clarified in both panel/README.md and the full component guide that the navbar refresh button disables when no callbacks are registered and when a refresh cycle is in progress. Updated API documentation to remove the now-removed `disabled` prop from PageRefreshProviderProps and updated code examples and test coverage descriptions to reflect the new callback-driven semantics.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>

* test(panel): mock usePageRefresh in tests predating the provider

Merge-skew: the page-refresh feature makes CommandCenter and the agent
detail page call usePageRefresh; three tests merged from master render
them without the new provider. Mock the hook module, matching the
files' stub-everything style.

---------

Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech>
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-09 05:27:14 +02:00
47c927c598 feat(gateway): root-owned acceptance criteria via declare_coverage (#357)
The coverage gates had no vocabulary for criteria only the root itself
can satisfy (the supersede PR from feature/main_pm/*, closing the
contributor's PR): once a Main PM declared coverage for the legitimate
cell criteria, the idle gate demanded a cell for the impossible ones
too, so they got pushed into a cell task and the cell PM (correctly)
escalated. declare_coverage now accepts the PM's own task: self-declared
criteria count as claimed for the idle gate and satisfied for the
roll-up (the roll-up actor is their owner by construction), surface as
claimed_by=root in the briefing, and both PM prompts say to never hand
a cell a criterion it cannot satisfy inside its own cell.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-09 03:17:20 +02:00
94dd6b6d53 chore(prompts): decomposition minimalism — fewest subtasks the work needs (#348)
CEO doctrine after an axios bump became a planning parent plus two
sequenced same-branch code children: single-concern work is ONE subtask
covering change, verification, and PR; same-branch sequenced siblings
are one task wearing two ids (the gateway serializes sibling code
subtasks anyway, so the split buys zero parallelism); the i_will_plan
sub_tasks list is a checklist, not a delegation quota.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-09 02:25:32 +02:00
Renn F 5e7c498d00 fix(runtime): auto-submit is unconditional; refusals brief the fallback PM
The PR-gate turn cut (#295) already auto-submitted assembled tasks, but
its refusals fell back to a PM spawn silently -- in production the PM
turns the cut was meant to remove kept happening with no visible cause
(live case: an AC-coverage refusal). The flag is gone (the fallback is
the safety net), the umbrella/branchless exclusion uses the canonical
batch predicates, and every refusal reason now rides into the spawned
PM's prompt so the fallback starts informed.
2026-07-08 22:16:50 +02:00
f48d088c08 fix(gateway): working exits for wedged agents + declare_coverage roll-up unblock (#341)
A live task burned 5+ hours because every exit was locked. unclaim now
works from verifying and needs_revision (service guard + lifecycle edge);
the circuit breaker and the i_am_done push-failure remediate name the
working chain ending in unclaim(); sync_branch(stash=true) clears the
DIRTY_WORKSPACE dead-end (pop-conflict preserves the stash); blocking a
task QA already owns now says to idle instead of listing states; the
orchestrator auto-block logs real errors and skips states where blocking
is meaningless instead of force-blocking them.

declare_coverage (cell/main PM) retroactively stamps parent-AC refs on a
child that implements them -- closing the roll-up deadlock where the
declaring child was cancelled and its re-delegated replacement completed
the work uncredited. Cancelling a ref-declaring child now warns and
surfaces the orphaned criteria.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-08 21:40:22 +02:00
47d78f50ee feat(sandbox): on-demand provisioning via request_sandbox verb (#338)
* feat(sandbox): on-demand request_sandbox verb replaces eager provisioning

Sandboxes were provisioned at every agent spawn for opted-in projects,
so every role paid the sidecar spin-up and a provisioning failure
refused the spawn. Provisioning now happens when an agent asks: the
request_sandbox do-verb (dev + QA) reaches the orchestrator through
ContentActionsDeps, ensure_sandbox provisions idempotently with an
in-memory per-agent cache (evicted at teardown and janitor sweep), and
creds return in the envelope payload including ready-to-export
ROBOCO_TEST_* values. Spawn now only injects a marker env naming the
available services plus a briefing line; sandbox failures can no longer
refuse a spawn. Teardown lifecycle unchanged.

* feat(sandbox): harden request_sandbox + Phase 3 wiring proof and docs

Hardening from adversarial review: ensure_sandbox now provisions the
project's full opted-in set on first request (a later superset can
never tear down a live sandbox mid-use), serializes per-agent behind an
asyncio lock (a client timeout-retry no longer races its own in-flight
provision), and verifies container liveness on every cache hit (a dead
sandbox evicts and re-provisions instead of serving dead creds). MCP
client budget 720->1080s for the full-set cold case. Phase 3: e2e smoke
wiring test (manifest grants + guard-chain envelopes over the real
API), sandbox-db/tools/map docs and CLAUDE.md rewritten for on-demand.

* feat(sandbox): release sandboxes when the agent's work ends

CEO directive: sidecars must not dangle once the agent is done. The six
work-ending verbs (i_am_done, unclaim, i_am_idle, pass_review,
fail_review, i_documented) now release the caller's sandbox best-effort
on their success path via release_sandbox (lock + teardown + cache
evict; a no-sandbox agent costs a dict lookup). Container removal and
the janitor remain the backstop; a re-request provisions fresh.

* test(sandbox): monkeypatch the release hook instead of method assignment

mypy method-assign rejected the direct AsyncMock assignments; the prior
static gate ran before this test file landed.

* test(sandbox): guard envelope evidence for mypy in verb tests

* chore(prompts): regenerate verb tables for request_sandbox

* chore: resolve merge with master (breadcrumbs + statement budget)

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-08 16:40:02 +02:00
4fb0059556 fix: journals/learnings never reached the RAG corpus + git-readonly slug 404s (#339)
* fix(rag): per-index chunk floors — journals and learnings were never indexed

The global 200-char garbage floor (sized for code/doc chunks) discarded
every templated journal note and most distilled org-memory lessons,
silently: ingest returned success with zero chunks, so agent journals
and learnings were never retrievable via RAG. IndexConfig now carries a
per-type min_chunk_length (journals 40, learnings 80, others unchanged).

* fix(mcp): git-readonly tools default project_slug from the container env

Agents 404ed /api/git/status with 'Project not found: roboco' — the
tools made the LLM supply the slug and six doc examples taught a slug
that matches no registered project. The tools now fall back to the
ROBOCO_PROJECT_SLUG the orchestrator already injects, and the stale
examples are corrected.

* feat(rag): startup backfill re-ingests zero-chunk journals and learnings

Before the per-index chunk-floor fix, ingest() returned success with
chunk_count=0 for undersized content: every historical journal entry and
distilled learning below the (then-global) 200-char floor was durably
recorded in journal_entries but silently never got a chunks_journals /
chunks_learnings row, and no exception meant the existing dead-letter
(rag_index_failures) never saw it either.

Extends the startup reconcile (roboco/api/app.py _reconcile_rag_indexes)
with a new pass: backfill_unindexed_journals (roboco/services/
rag_index_failures.py) queries journal_entries for rows missing from each
vector table and re-ingests them through the same live code paths
(_reindex_journal_entry / record_learning).

Journals and learnings are backfilled independently since a LEARNING entry
can clear the (lower) JOURNALS floor while still failing the (higher)
LEARNINGS floor — a learning's doc_source is a content hash, not the entry
id, so presence there is checked by hashing each candidate the same way
LearningsIndexPlugin.record_learning does and batch-querying chunks_learnings
for those exact sources.

Bounded to 200 rows per pass per boot (converges over restarts on a larger
backlog) and best-effort per row (one failure never aborts the pass). Rows
still under the current floor are excluded by a length filter in the SELECT
so they are never retried forever, and private entries are excluded from
the JOURNALS pass exactly like the live indexing path.

* test(rag): scope backfill assertions to their own rows

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-08 16:03:00 +02:00
0bf0cd69b3 fix(release): close the 0.19.0 scan findings — sandbox mongo tag, flow-verb timeout walls, video hardening (#329)
- mongo:8-alpine → mongo:8 (tag never existed; a mongo-opted project could spawn no agents) + a Docker Hub tag-existence e2e guard for every sandbox engine
- flow-verb timeouts at both walls: shared SLOW_VERBS policy (i_am_done / submit_up / submit_root / open_pr / i_will_work_on get the 900s server budget); the MCP client now outlasts the server budget (+10s headroom, orchestrator-injected env) so agents receive the middleware's clean 504 envelope instead of dying at the old flat 30s client timeout
- cancellation safety: the quality gate kills+reaps its child on CancelledError; create_pr records the PR via a shield-with-wait-out helper so the write can neither be skipped nor race get_db's rollback
- video engine: renderer sidecar isolated on a render-only network, 2g/2cpu caps, 570s render watchdog with exit-on-hang, 512MB tar decompression cap, CEO notification on terminal render failure, reject under the approve mutex (fail-closed on Redis-down)
- dead python-jose dependency removed (drops ecdsa and its unfixable Minerva advisory PYSEC-2026-1325); panel --font-mono now a real monospace stack

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-08 03:26:12 +02:00