Commit Graph
219 Commits
Author SHA1 Message Date
775872cac0 fix(panel): days-view timeseries charts show dates, not '02:00' (#622)
formatBucket guessed hourly vs daily from the timestamp string
(bucket.endsWith('T00:00:00.000Z')) and then rendered LOCAL getHours(), so
every daily midnight-UTC bucket rendered as the viewer's local hour — '02:00'
at UTC+2 — across the 7d/30d/90d windows. Granularity is now derived from the
data's own bucket spacing (bucketGranularity: min gap >2h = daily) and daily
buckets render as a short UTC date. The 24h/hourly view is unchanged. Added
minTickGap so the 90d axis doesn't crowd.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-21 03:51:41 +02:00
7b84162ae9 feat(github-app): App credentials, installation tokens, and a Select repo picker (#621)
* feat(github-app): App credentials, installation tokens, and a Select repo picker

RoboCo was 100% PAT-based. A singleton Fernet-encrypted github_app_credentials
row (migration 077, telegram-credentials pattern) now stores the App id +
private key; github_app_auth mints RS256 app JWTs and caches installation
tokens until 5 minutes before expiry. Projects can bind an installation
(projects.github_installation_id): get_decrypted_token returns a minted
installation token for bound projects and falls back to the stored PAT on
any minting failure, so all ten token consumers work unchanged.

CEO-gated routes expose credentials CRUD plus installation/repo listing, and
the New Project dialog gains a Select repo picker (disabled with a HelpTip
until the App is configured) that fills the git URL and binds the
installation; manual URL + PAT stays the default path.

* test(panel): mock the GitHub App credentials card in the settings page test

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-21 00:54:20 +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
8cb233c1e8 fix(panel): forms catch up with the backend (#614)
New Project claimed GitLab/Gitea were 'planned' while both providers are
fully shipped, showed a hardcoded GitHub badge, and never sent git_provider
at all — a non-GitHub project could not be created without an immediate
edit. It now carries the same forge Select the edit dialog has; the edit
dialog's own 'GitLab support is planned' tooltip is corrected too.

Also: the git actions panel's hardcoded 'main' (wrong PR-eligibility and
target label for master-default and env-ladder projects) is replaced by the
project's resolved head branch; acceptance criteria become editable in the
edit-task dialog; three feature flags get their missing descriptions; and
three forms swap raw-UUID text inputs for the existing Task/Agent selectors.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 20:38:36 +02:00
fbec679878 fix(panel): AI Providers mix grid derives from the live roster (#613)
The per-agent override grid rendered a hand-maintained AGENT_GROUPS literal
that had drifted: ux-dev-2 and all four PR reviewers were absent, so their
model overrides could not be viewed or edited at all (the backend accepts
any slug). The grid now derives its sections from useAgentDefinitions()
with the same team helpers the Fleet page uses, org-ranked ordering, a
loading skeleton, and group HelpTips. The static offline-fallback maps
(agent-utils, use-agents, mock-data) get the missing agents too.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 20:38:23 +02:00
7248e5b722 fix(panel): charts get real axes, humanized ticks, and dark-theme tooltips (#611)
Every chart hand-rolled its own k-only formatter (22596k-style ticks, left-
clipped y labels), used recharts' default white tooltip (invisible header on
the dark theme), and two charts pinned a numeric XAxis interval that collapses
short series to a single tick. The agent Token Activity chart had no axes at
all and blanked its tooltip date on purpose.

One shared formatTokens/formatBucket (lib/format.ts) and one shared themed
tooltip style (components/charts/chart-tooltip.tsx) now feed all 10 charts;
axis widths/margins sized to the labels; preserveStartEnd tick intervals.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 20:29:45 +02:00
3c5ee46347 feat(tg): Mini App V6 — premium overhaul (#609)
* feat(tg): Mini App V6 — premium overhaul (design system, Chat parity, Metrics drilldown, CEO verbs)

Design system: native type with tabular-numeral heroes (mono demoted to
the wordmark), borderless elevated cards, floating dock, Telegram
window-chrome painting via the theme bridge; Inbox moves behind a header
bell with humanized notifications (UUIDs resolve to task names).

Chat: honest Mine/Fleet split — participant-scoped CEO threads with real
unread counts and mark-read, watched fleet threads with reply-as-CEO on
task-linked conversations (watch-only otherwise), markdown transcripts,
live pulse flashes, and a pinned Secretary live chat on the panel's SSE
session runtime.

Metrics: new tab with period-segmented spend hero, by-agent/team/model
breakdowns, delivery + efficiency health, and a per-agent drilldown over
usage time-series (agent_slug) + member scorecard.

Board: tg-native grouped pipeline replacing the MobileTaskBoard wrapper;
task sheet gains the CEO decide verbs (approve / request changes /
unblock).

Security: /api/dashboard router now require_panel_token-gated at router
level (mirrors /api/usage), closing unauthenticated metrics exposure.

* fix(tg): restore Share Tech Mono brand voice, Phosphor icon set, borderless avatars

The mono returns as the numeral/brand voice (.tg-display — heroes, stat
values, wordmark) while labels stay native sentence case. The hand-drawn
duotone glyphs and lucide feature icons are replaced by Phosphor (MIT):
duotone at rest via an IconContext at the shell, filled weight on the
dock's active tab; row glyph maps (board statuses, inbox kinds, approval
kinds, quick actions) all move over. Team avatar tiles drop their borders
— tint-only squircles.

* fix(tg): fleet avatar strip breathes — spaced tiles instead of overlap

* polish(tg): taste-skill audit pass — em-dash purge, one icon family, separator rationing

Applied the design-taste audit against the cockpit: every em-dash in
visible UI copy rewritten (periods/commas/colons), the remaining lucide
chrome (carets, arrows, send, close, spinners) moved to Phosphor so the
tg tree ships one icon family (send is the native paper-plane, carets
bold), the hand-rolled chevron SVG deleted, and metadata lines rationed
to a single middle-dot separator.

* polish(tg): pipeline chip strip scrolls without a visible scrollbar

* fix(tests): metrics observability fixture uses a relative timestamp

The hardcoded _T0 (2026-06-20) aged out of the service's 30-day window
exactly 30 days later, detonating the suite on every branch. Two days
back from now() stays inside every window (30d metrics, 7d scorecards)
permanently.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 17:29:22 +02:00
a5d8c6bd5b feat(video): CEO can preview a video authoring task's frames before approving (#608)
A source=video authoring task reaches awaiting_ceo_approval with no MP4
yet — rendering only happens after it completes — so the CEO had nothing
to review. Two CEO-gated routes now serve the request_render preview
frames: GET /video/preview-frames/{task_id} lists them per orientation
(parsed from the self-describing .previews/{task8}/{orientation}/ filenames
rather than the render_preview marker, which only holds the last call's
single orientation), and .../{orientation}/{filename} streams a frame's
PNG behind the existing path-confinement guard. The task-detail Overview
gains a Video preview card — a 9:16/1:1 toggle + prev/next/scrubber frame
stepper with composition id, duration, and a dirty badge — shown for a
video task with preview frames or awaiting CEO approval.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 10:51:43 +02:00
RoboCo Release Manager 31418c9a32 chore(release): 0.26.0 2026-07-20 05:19:54 +00:00
4a8050bf43 fix(panel): the /tg surface is exempt from the global 401→login redirect (#600)
The app-level agent-roster sync fires /api/agents on every surface; on
/tg it races the initData sign-in, takes the cloud-auth 401, and the
interceptor bounced the Telegram webview to the password /login page it
cannot complete — hijacking the cockpit into the dashboard. The Mini App
owns its auth UX (initData sign-in + its own wall), so the redirect now
exempts /tg via an exported, tested path predicate.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 02:01:10 +02:00
bab53e31ca perf(panel): kanban virtualization + row memoization + scorecards batch endpoint (#594)
* perf(panel): kanban virtualization + row memoization + scorecards batch endpoint

The audit's remaining phases: the kanban card lists render through
@tanstack/react-virtual windows (columns are the dnd drop targets, cards
only drag — no sortable conflict) with memoized columns/cards and a
stabilized handleAction; the task table's desktop row and mobile card are
extracted and memoized (the table itself already client-paginates to 100).
Backend: the per-member scorecard N+1 (~20 requests x 3 queries per poll)
collapses into GET /dashboard/metrics/members backed by
get_all_member_scorecards with grouped rollup/overlay SQL shared with the
single-agent path.

* test(metrics): shared-DB-safe scorecard tests — unique seeds, delta assertions

The two new batch-scorecard tests assumed a private DB: fixed ceo/system
slugs collided with other tests' seeds (ix_agents_slug) and a global
exactly-one CEO lookup + exact roster count broke in the one-process
suite. Unique slugs, subset/disjoint assertions, dead count constant
dropped.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 19:42:24 +02:00
700dbcd285 feat(tg): Mini App V5 — brand typography, icon depth, motion, detail sheets (#583)
* feat(tg): Mini App V5 — brand typography, icon depth, motion, detail sheets

Share Tech Mono (the vendored motion-brand face) becomes the cockpit's
display voice via next/font/local scoped to #tg-shell; icon tiles, circle
actions, and avatars get gradient/ring depth; a dependency-free motion
vocabulary lands (spend count-up, tab rise-in, staggered sections,
sparkline draw-in, sheet slide-up with native BackButton dismiss); the
Board tab gains a tap-through task sheet (ACs, open findings, PR link),
Today's fleet opens a full-roster sheet, and Board/Inbox join the
/tg?demo=1 fixtures.

* feat(tg): custom RoboCo icon set + operations ring

The cockpit stops using stock lucide on its hero surfaces: a hand-drawn
duotone icon set (speedometer, seal, bell, kanban, brand-cursor bubble,
rocket, double-check, broom, robot head) covers the tab bar and the Today
ring. The ring itself stops duplicating the tab bar and becomes real
operations: Ship deep-focuses the release proposal in Approvals, Ack all
bulk-acknowledges pending notifications, Sweep runs the stale-branch
cleanup across every git-configured project behind a confirm sheet, and
Fleet opens the roster.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 18:59:54 +02:00
c7605b0d77 feat(tg): premium Mini App cockpit — spend hero, charts, avatars (#582)
* feat(tg): premium Today — spend hero, trend, quick actions, fleet avatars

The cockpit home stops being flat cards and becomes a real app surface:

- Spend HERO: the day's cost at 40px with a signed delta-vs-yesterday
  chip and a live 7-day amber area sparkline (hand-rolled inline SVG, no
  charting lib in the Mini App bundle).
- Quick-action ring: circular Approve (amber + needs-you badge) / Board /
  Inbox / Chat, the wallet-style primary-verb row.
- Needs-you as a rich amber gradient banner (top items + draft chips)
  instead of a plain section.
- Fleet as live avatar tokens (stable per-name hue, pulse dot) over the
  working list.
- "Shipped this week" day-bars (today emphasized) + week total.

Backend: /telegram/today gains spend.series (7-day cost) + delta_pct and
a velocity series (per-day completed tasks) — two cheap grouped-by-day
queries, same DB-only ethos, degrading to zeros on error.

* feat(tg): color-code approval rows by kind

TgRowIcon gains a tone prop; the approvals list tints each tile per kind
(amber Release / sky X post / violet Video / emerald Roadmap) so a mixed
queue reads as color-coded instead of a monochrome column.

* feat(tg): sender/peer avatars on Inbox + Chat

Inbox notifications and Chat conversation rows adopt the fleet-avatar
language: a per-name-hued initials token leads each card, unread inbox
items carry a subtle primary tint, and both cards move to the rounded-2xl
surface — so every tab now shares one visual system. Board keeps the
shared MobileTaskBoard (already grouped/pill-styled, and reused outside
the cockpit).

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 11:51:06 +02:00
Renn F a072b980bc fix(tg): show Open-from-Telegram wall for empty initData in production
Opening /tg in a plain browser loads telegram.org's script, which
defines window.Telegram.WebApp with EMPTY initData (no real launch
behind it). Production posted that empty string to webapp-auth → 422 →
"Couldn't sign in". The dev path already guarded this (cb55f2b9); the
prod path didn't. Now a bridge with no initData that isn't the dev mock
shows the "Open from Telegram" wall instead of erroring — the cockpit is
a phone-from-Telegram surface, and a desktop browser gets the wall, not
a failed auth.
2026-07-19 10:48:05 +02:00
96401f4c10 feat(forge): Phases 2+2.1+3 — Gitea + GitLab providers, per-call routing, local-merge fallback (#575)
* feat(forge): Phase 2 — Gitea provider, per-call routing, host registry

Gitea support lands behind the Phase-1 seam:

- GiteaProvider (services/forge/gitea.py): Gitea v1 transport addressed
  by instance host (api base from the project's git_url). Where Gitea's
  wire contract diverges from GitHub's, the provider adapts responses
  back into the shapes GitService already classifies (ShapedResponse):
  `token` auth scheme, duplicate-PR 409→422 with the "already exists"
  text GitService keys on, commit statuses reshaped into check_runs /
  workflow_runs envelopes, APPROVE→APPROVED review mapping, Do-keyed
  POST merge, merge-method repo keys, label-color '#' prefix,
  client-side head/base PR filtering. Deliberate postures per the spec:
  zero-workflows fail-open (statuses-free repo → no_ci_configured) and
  merge_branch as a shaped 501 (env-sync cascade lands on missing_ref;
  the shared local-git fallback is Phase-2.1).
- ForgeRouter (services/forge/router.py): GitService._forge now routes
  per call from RepoRef.host — every existing call site unchanged in
  shape. RepoRef gains an optional host; _parse_git_url returns the
  host-stamped ref and it is threaded through GitService/release
  executor instead of being rebuilt from strings (helpers re-signatured
  to take RepoRef).
- Host registry (services/forge/registry.py): in-memory host→provider
  map, self-healing — ProjectService.get/get_by_slug re-register on
  every read; provider_for resolves gitea projects by git_url host.
- Registration validation now accepts git_provider="gitea"; GitLab
  remains recognized-but-rejected. Panel: the read-only Forge badge
  becomes a real picker (Auto-detect / GitHub-GHE / Gitea / GitLab
  disabled).

Plain git (clone/fetch/push) needs no changes — the Basic-auth
extraheader works on Gitea unchanged. Gates: mypy 392 files, xenon A,
full unit suite 6356 green, integration suite 2257 green.

* feat(forge): live-Gitea contract suite + scheme support + slash-safe refs

Hardening from running the provider against a real dockerized Gitea
1.22.6 (the spec's Phase-2 contract suite, now committed as the
env-gated tests/e2e_smoke/test_gitea_live.py — self-seeding: creates its
own repo, pushes real commits, and drives PR open → duplicate reshape →
list/filter → diff → review → labels → commit-status CI reshapes →
squash merge → branch delete → release, plus a live verification of the
x-access-token Basic-auth git-CLI claim).

Two real findings fixed:
- Branch refs weren't URL-encoded — every RoboCo branch carries slashes
  (feature/backend/...), and Gitea's router 404s on the extra path
  segments. list_ci_runs + delete_branch_ref now quote the ref
  (regression-pinned in the unit suite).
- The API base hardcoded https; a LAN instance serving plain http is a
  real deployment shape. GiteaProvider gains a scheme (recorded per host
  by the registry from the project's git_url).

ShapedResponse moves to forge/shaping.py (shared by the upcoming GitLab
transport, which needs its text override for diff reassembly).

* feat(forge): Phase 3 GitLab provider + Phase 2.1 local-merge fallback

GitLabProvider (services/forge/gitlab.py): GitLab v4 transport addressed
by host+scheme, subgroup-safe (the MR project path packs into
RepoRef.owner, URL-encoded per call). Adapters translate MR semantics
into the GitHub shapes GitService classifies: iid→number,
source/target_branch→head/base with a merged bool, per-file diffs
reassembled into unified-diff text (ShapedResponse text override,
3-page cap), approve-vs-note review routing (GitLab has no
request-changes verb), pipelines/statuses reshaped into
workflow_runs/check_runs, merge-method repo-key mapping, duplicate-MR
409→422. Reviewer mirroring is skipped (needs numeric ids RoboCo
doesn't store); provisioning stays Phase 4. gitlab.com now auto-detects
at registration like github.com; self-hosted GitLab sets the provider
explicitly (panel picker enabled).

Phase 2.1: neither Gitea nor GitLab has GitHub's server-side merges
API — their merge_branch returns a shaped 501 and
GitService.sync_env_branch now runs the shared local-git fallback
(_local_merge_branch: throwaway clone → ancestor check → merge → push;
a conflict aborts with the remote untouched; same status vocabulary as
the merges-API path).

Also aligns the whole tree with the full gate's tests/-scoped mypy
(provider-test responder typing, e2e_smoke's stale owner/repo shapes).
Gates: mypy 1229 files clean, xenon A, unit suite 6393 green, forge
suites 85 green, panel typecheck/lint clean.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 08:12:34 +02:00
baa87d584a feat(tg): Mini App V4 — Today brief, native approvals, live data, bot tier, chat bridges (#576)
* feat(tg): P0 — dev mock bridge + Telegram-native foundations

Mini App V4 phase 0. The (tg) shell gains the groundwork every later
phase builds on:

- Dev mock bridge: outside Telegram, a development build falls back to a
  no-op WebApp object and skips the webapp-auth POST (the regular panel
  session cookie authorizes API calls), so the cockpit is workable in a
  plain browser. Production keeps the "Open from Telegram" wall.
- Telegram theme adoption: themeParams map onto the shadcn CSS variables
  scoped to #tg-shell (desktop dashboard untouched), colorScheme drives
  the dark class, themeChanged re-applies live. Non-hex values are
  dropped at the trust boundary.
- Viewport/swipe correctness: shell height rides Telegram's own
  --tg-viewport-stable-height (100dvh fallback), vertical swipe-to-close
  disabled so list scrolling can't dismiss the app.
- Native chrome bindings: TgWebAppProvider context plus useMainButton /
  useBackButton declarative hooks and a null-safe haptics helper —
  consumers never touch window.Telegram directly.

* feat(tg): P1 — Today home tab + one-round-trip /telegram/today brief

Mini App V4 phase 1: the cockpit now opens on a "Today" brief answering
"does anything need me?" in one glance.

Backend: GET /api/telegram/today (CEO-gated, rate-limited) returns the
whole brief in one round trip via the new TgCockpitService — needs-you
items (awaiting-CEO + blocked tasks capped for a phone screen, held-draft
counts across release/X/video/roadmap queues), a fleet snapshot with
per-agent current-task titles, today's spend from the day rollup
(degrading to zeros on a usage hiccup, mirroring the CEO overview), and
ship state. Deliberately DB-only: no live GitHub calls, no readiness
snapshot (that path clones), no orchestrator singleton — the CI
red/green proxy is the set of open ci_watch fix tasks.

Panel: TgTodayTab is the new default tab (Gauge icon) — needs-you rows
and draft chips deep-link into the tab that acts on them (with a haptic
tap), fleet/spend/ship render as dense cards, 45s refetch until the P3
WebSocket wiring lands.

* fix(tg): dev mock engages when the CDN bridge loads outside Telegram

Live browser smoke caught it: a bare tab still loads telegram-web-app.js,
so window.Telegram.WebApp EXISTS outside Telegram — just with empty
initData. The dev fallback keyed on a null bridge only, so a dev browser
went down the real-auth path and posted empty initData instead of
mounting the mock. The fallback now treats bridge-with-no-initData the
same as no bridge (a real Telegram launch always carries initData);
production behavior is unchanged.

* feat(tg): P2 — native approvals card stack

Mini App V4 phase 2: the Approvals tab stops stacking the four desktop
queue cards and becomes a phone-native flow — one normalized list across
release proposal / X drafts / video drafts / proposed roadmap items, and
a full-context detail per item:

- Release: version/bump/gate badges, changelog draft, gaps, migration
  notes, in-flight + failed-execute banners; approve runs the fail-closed
  executor, reject requires a substantive change request (10 chars).
- X: editable body with the live 280 counter, replied-to mention quoted;
  approve sends the edited body only when actually edited.
- Video: cut-toggled player (blob-fetched through the authed client — a
  bare <video src> would 401), per-platform caption edits with 280/2200
  counters; approve sends only checked-in edits.
- Roadmap: the PO's full pitch (description, rationale, ACs); approve
  materializes into the backlog per item.

The detail's primary action rides Telegram's native MainButton and back
navigation rides the BackButton, with visible fallbacks outside Telegram
(dev mock, old clients). Haptics fire on outcomes. An acted-on item
vanishes from the refetched queue, popping back to the list by
construction. A failed queue source is surfaced ("list may be
incomplete" / "couldn't load") instead of masquerading as an empty
queue — caught live in the browser smoke.

* feat(tg): dev demo mode — /tg?demo=1 renders canned cockpit data

Development-only: with the flag param present, the Today brief and the
four approval queues resolve typed fixtures (dynamically imported, so
production bundles never carry them) instead of hitting the backend —
the cockpit is fully browsable with zero stack running. Mutations still
go to the real API and fail loudly; it's a showroom, not a simulator.

* feat(tg): P3 — cockpit rides /ws/system live

Chat adopts the desktop A2A invalidate-on-frame idiom over the shared
ref-counted /ws/system socket: every a2a.message frame refreshes the
conversation list and the affected thread, missed-frame gaps are healed
by a reconnect refetch, and the 10s thread poll turns off entirely while
the socket is up (it remains the fallback). The Today brief refreshes on
each USAGE_SNAPSHOT push so the spend line tracks the sweeper live, with
the 45s poll as the socket-down fallback. No new sockets, no backend
changes — the WS gate already accepts the cloud-auth session cookie.

* feat(tg): P4 — deterministic bot command tier + self-syncing menu

Mini App V4 phase 4 (deterministic half): three new bot commands beside
/status /queue /task —

- /agents: who's mid-task right now, from the same TgCockpitService
  fleet snapshot the Today brief renders (now public `fleet()`).
- /usage: today's spend from the day rollup.
- /blocked: awaiting-you + blocked tasks, deep-linked into the panel,
  capped per section, titles HTML-escaped.

BOT_COMMANDS is the single registry driving /help AND a once-per-process
Bot API setMyCommands sync on the first poll cycle (new client method,
best-effort), so the Telegram command menu can never drift from what the
code implements. The interactive tier (/secretary, /newtask riding a
live Intake interview in-thread) is specced but not in this commit.

* feat(tg): direction-C styling pass — Telegram palette, RoboCo voice

The cockpit stops wearing default-shadcn and gets its own visual
language on top of the P0 themeParams bridge (colors stay CSS-variable
driven, so inside Telegram everything still adopts the user's theme):

- Shared primitives (components/tg/ui.tsx): TgSection grouped cards with
  tracked micro-label headers, TgRow list rows (44px targets, press
  feedback, 1/2-line clamp), TgRowIcon glyph tiles, TgStat tabular-nums
  figures. Every tab composes the same three, so density and rhythm are
  identical across the surface.
- Shell renders a centered 430px column (sm:border-x) — the phone UI no
  longer stretches across a desktop dev browser.
- Tab bar: tighter type, active stroke-weight shift, backdrop blur.
- Today: needs-you count badge, divided task rows with inline blocked
  marker, fleet as mono-named rows, spend/ship as stat tiles.
- Approvals rows as icon-tile cards; detail header gains the kind glyph.
- Inbox/Chat rows aligned to the same card language.

* feat(tg): P5 — /secretary and /newtask live-chat bridges

The bot's interactive tier: both commands bridge the CEO's Telegram chat
into the same in-process runtimes the panel drives — the persistent
Secretary container and the scoped Intake interview.

There is no synchronous send→reply seam (replies land on the session's
single-consumer relay queue), so each bridged session runs one long-lived
consumer task (roboco/services/telegram_bridge.py) that drains
PrompterLiveRegistry.stream and pushes one Telegram message per completed
turn. While a session is live, plain chat text IS the conversation;
/end closes it.

/newtask resolves the intake scope (single project auto-picked, multiple
offered as a tap-to-pick keyboard holding the initial text), and the
interview happens in-thread. A draft proposal renders as a card with
Send-to-Board / Discard buttons: confirm routes through the normal
board-review path (PrompterService.confirm_live_draft, route=board) and
PARKS the session — board feedback later streams straight back into the
same thread, closing the redraft loop from the phone. MegaTask batches
still confirm in the panel only.

The consumer's open stream arms the registry's 60s keepalive, so the
bridge runs its own idle TTL (same setting, parked sessions exempt).
State is per-process in-memory by design (the _PENDING_REPLIES posture);
intake/secretary containers are process-wide singletons, so a bridged
session preempts a live panel session of the same kind by construction.

* feat(tg): cockpit skin — RoboCo dark deck with a constant amber accent

The cockpit no longer inherits the dashboard's white default outside
Telegram: #tg-shell carries its own standing skin (deep slate surfaces,
amber primary) so the Mini App looks like RoboCo everywhere. Inside
Telegram the themeParams bridge now overrides SURFACE tokens only —
background/card/text/hint/border repaint to the user's Telegram theme
while --primary/--ring stay RoboCo amber: Telegram's surfaces, RoboCo's
voice. Demo fixtures also rewritten to neutral content (they previously
depicted unbuilt forge work and already-shipped roadmap items as live).

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 07:29:04 +02:00
461a6e1ae7 feat(forge): Phase 1 — GitProvider seam, GitHub transport extracted (#571)
* feat(forge): Phase 0 — git_provider column + registration-time forge validation

Pointing a project at a GitLab/Gitea git_url used to fail silently, several
steps deep, at first PR. New pure policy module (foundation/policy/forge.py)
detects the provider from the git_url host and validates at the
ProjectService create/update chokepoint: github auto-detects and
auto-stamps, explicit git_provider=github is the GitHub Enterprise escape
hatch, gitlab/gitea are recognized-but-not-yet-supported, unknown hosts get
a loud rejection with guidance. An update changing git_url does NOT inherit
a stored auto-stamped provider (restating the override is required), so a
host swap can't smuggle the escape hatch past validation. Migration 075
adds the nullable projects.git_provider column; the panel project dialogs
show the detected forge. Phase 0 of the forge-providers spec.

* feat(forge): Phase 1 — GitProvider seam, GitHub transport extracted

roboco/services/forge/: base.py holds the pure contracts (RepoRef +
GitProvider ABC, stdlib-only — a later GitLabProvider is implemented by
reading this file alone), github.py the httpx transport (20 endpoint
methods behind one shared request-plumbing helper set), registry.py the
wiring (git_provider column -> provider, failing loud on gitlab/gitea).
GitService keeps its exact public surface and all response
classification; its 26 inline REST call sites route through a lazy
_forge property (several suites build GitService via __new__, so an
__init__-set attribute breaks them). github_provisioning and
release_executor ride the same provider. Zero behavior change — the
pre-existing suites pass unmodified; per-project provider resolution
lands with the second provider.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 19:11:56 +02:00
7e01c0cecf feat(marketing): project-branded drafts + project badges on the X/video queues (#570)
Item B+C of the video/X per-project targeting spec, plus the
company_goals.company_name field they depend on (migration 075).

- CompanyGoalsService.resolve_product_name is the single fallback chain
  (project name -> charter company_name -> RoboCo); XEngine and
  VideoEngine both call it and their prompt builders are pure functions
  taking product_name — release posts/videos stop hardcoding RoboCo.
- The X and video queue responses carry project_slug/project_name via one
  shared unloaded-guard helper (api/schemas/project_fields.py); both
  panel queues render a shared ProjectBadge so multi-project drafts are
  tellable apart.
- Business -> Goals editor gains the company-name input.
- Fixes a pre-existing test-isolation leak: the company-goals routes test
  commits the charter singleton into the session-scoped test DB and
  polluted later suites; it now deletes the row on teardown.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 19:11:03 +02:00
388bab2488 feat(forge): Phase 0 — git_provider column + registration-time forge validation (#569)
* feat(forge): Phase 0 — git_provider column + registration-time forge validation

Pointing a project at a GitLab/Gitea git_url used to fail silently, several
steps deep, at first PR. New pure policy module (foundation/policy/forge.py)
detects the provider from the git_url host and validates at the
ProjectService create/update chokepoint: github auto-detects and
auto-stamps, explicit git_provider=github is the GitHub Enterprise escape
hatch, gitlab/gitea are recognized-but-not-yet-supported, unknown hosts get
a loud rejection with guidance. An update changing git_url does NOT inherit
a stored auto-stamped provider (restating the override is required), so a
host swap can't smuggle the escape hatch past validation. Migration 075
adds the nullable projects.git_provider column; the panel project dialogs
show the detected forge. Phase 0 of the forge-providers spec.

* fix(panel): mock-mode forge detection extracts the real host

CodeQL js/incomplete-url-substring-sanitization: the substring check
matched github.com anywhere in the URL. Extract the hostname (URL parse
or scp-form regex, mirroring forge.py) and require an exact match.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 19:10:43 +02:00
Renn F fc6d6f6458 fix(a2a): CEO pairs join the switchboard matrix; sections collapsible
_SWITCHBOARD_SLUGS reused is_human_only_role (spawn semantics) and dropped
the CEO before can_a2a_direct — which allows CEO -> anyone — ever ran, so
the static pair matrix had no CEO pairs and a Renzo filter emptied the
switchboard. Only prompter/secretary/system are excluded now; CEO pairs
get their own 'CEO Direct' section (matrix 70 -> 93). Every switchboard
section header is now a collapse toggle (Radix Collapsible, default open).
2026-07-18 15:55:40 +02:00
f0782cb858 chore(panel): selective dependency updates — Next family held at the 16.1.x pin (#560)
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-18 08:12:43 +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
Renn F 29b375f1b5 fix(panel): session links target the owning task — /work-sessions/<id> never existed 2026-07-18 05:41:07 +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
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
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
Renn F 0843bbaaaa 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.
2026-07-16 15:47:08 +02:00
Renn F 33cb4d7104 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.
2026-07-16 06:59:22 +02:00
RoboCo Release Manager 3e8b3d82e6 chore(release): 0.25.0 2026-07-16 04:03:09 +00:00
Renn F e939d73844 fix(panel): hide the Report-CEO button when no handler is wired
The prop was never passed by any consumer, leaving a silent no-op
button on urgent flags; it now renders only when a real handler
exists.
2026-07-15 18:05:58 +02:00
Renn F dcd2f84e69 feat(panel): dense tooltip pass — work surfaces (270 tips)
Task lifecycle actions get a 25-action tip map, every dialog field and
git panel control annotated; fixes two round-1 disabled-button tips
that could never fire and a double data-state stamp on tab-progress.
2026-07-15 18:04:00 +02:00
Renn F f9dbb30a4e feat(panel): dense tooltip pass — org surfaces + layout chrome (253 tips)
Every sidebar destination, metric derivation, sortable column, queue
action, and dialog control explains itself; mobile tab bar shares the
sidebar's descriptions so the two nav surfaces can't drift.
2026-07-15 18:04:00 +02:00
Renn F e7d8e7ecb5 fix(panel): AI routing breathing room — wider column gutters, roomier override rows
Key band gets a 14-unit gutter between the key column and self-hosted;
mix-mode rows gain horizontal separation, vertical rhythm, and taller
row padding.
2026-07-15 17:57:42 +02:00
Renn F 57254b3ce2 feat(panel): dense tooltip pass — projects, products, social, KB, business, settings, notifications (178 tips)
Density bar: every dialog field, column header, filter, badge, and
status pill explains itself — grounded in the backend (ladder
normalization, rate-limit park/probe, self-hosted fallback, reindex
scope). Disabled buttons span-wrap so tips fire; tabs re-assert
data-state.
2026-07-15 17:56:12 +02:00
Renn F 84563e84b6 feat(panel): worked-example tooltip on the environment ladder
The section label now explains the typical dev/slave -> prod/master
two-rung setup inline, when to add middle rungs, and what no ladder
means.
2026-07-15 17:31:45 +02:00
Renn F 5fefb0f2c0 fix(panel): environment ladder speaks promotion flow, not git jargon
First rung badges 'PRs land', last badges 'release', middles drop the
fake rung-N names, and a 'promotes to' connector shows the direction
between rows; helper copy and the dialogs' default-branch hint follow
the same vocabulary.
2026-07-15 17:30:35 +02:00
Renn F 77229b10b1 fix(panel): agent cards — intrinsic auto-fill grid, roomier cards
repeat(auto-fill, minmax(17rem, 1fr)) replaces the breakpoint ladder:
one column on a phone through eight on a 27", consistent card width
across every team section at any viewport. Cards gain padding and a
base-size title.
2026-07-15 17:17:19 +02:00
Renn F 3d1c20e95a feat(panel): tooltip sweep — overview, agents, A2A, journals, auditor, metrics
Derivation tips on every key-metric, scorecard, and quality-metrics
figure; the cryptic member-scorecard headers get full decodes; avatar
initials, truncated ids, and toggle buttons gain accessible names;
title-only hints upgrade to the HelpTip idiom throughout.
2026-07-15 16:40:13 +02:00
Renn F c37516f640 feat(panel): tooltip sweep — projects, products, social, KB, business, settings, notifications
All 34 feature flags get verified one-line tips; secret inputs state
the write-only contract; KB index types get canonical descriptions;
switch tips ride Labels so Radix data-state stays intact. Also fixes
the scorecard SectionLabel swallowing props, which made tooltips on it
silently inert.
2026-07-15 16:33:22 +02:00
Renn F d1b02e3747 feat(panel): tooltip sweep — tasks, kanban, task assistant, git, work sessions (58 tips, 22 files)
Full import-graph walk per page: filter-chip and plan-section icon
buttons gain their first accessible names, per-state description maps
for git/docs/finding/priority badges, truncation-gated full-value tips,
disabled buttons explain their gate. Stateful Radix triggers use the
task-tabs data-state re-assertion pattern.
2026-07-15 16:30:26 +02:00
Renn F 14ee9b3b49 fix(panel): phantom agent states in dashboard + metrics counters
by_state.running / a.state 'running'/'stopped' never exist in the
orchestrator enum (offline/starting/active/waiting/idle/stopping), so
these counters were structurally zero — same class as the agents-page
Active bug, remaining two sites.
2026-07-15 16:07:55 +02:00
Renn F 9b0a8b0fae fix(panel): agents page — truthful counts, merged stat row, dense org grid
Total Agents now counts the roster (backend total_agents is live
containers only) and Active reads by_state.active — running/ready never
existed in the backend enum, so the counter was structurally zero.
Board + Main PM fold into one leadership band, cards compress to a
status dot + single detail line, and the grid densifies with per-team
count badges.
2026-07-15 16:06:38 +02:00
Renn F 2efc2f9542 fix(panel): compact task-status tiles beside the donut; journal ids copyable + task-linked
Five near-empty stat cards become a 2x3 tile band sharing the row with
the status donut. Journal entry cards and the entry page render full
ids with the shared CopyButton and a /tasks quick-link badge, with the
entry-card anchor restructured so links no longer nest.
2026-07-15 16:03:11 +02:00
Renn F 614c22ed70 fix(panel): AI providers layout — org-grouped two-column override list; key cards beside self-hosted
Per-agent mix rows group under Board / Main PM / cell / intake headers
in a two-column grid with compact selects; Grok + Ollama key cards
stack in the left column with the self-hosted section to their right.
Save-mix and inherit semantics untouched.
2026-07-15 16:01:06 +02:00