mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
master
1098
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a605607be4 | Merge pull request #753 from rennf93/dependabot/github_actions/docker/login-action-4.5.2 | ||
|
|
107f7af0e6 | Merge pull request #752 from rennf93/dependabot/github_actions/actions/stale-11 | ||
|
|
472d561ce2 |
chore(deps): bump github/codeql-action from 4 to 4.37.3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4 to 4.37.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4...v4.37.3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.37.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> |
||
|
|
a1f7b2e339 |
chore(deps): bump docker/login-action from 4 to 4.5.2
Bumps [docker/login-action](https://github.com/docker/login-action) from 4 to 4.5.2. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v4...v4.5.2) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 4.5.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> |
||
|
|
fd11df2293 |
chore(deps): bump actions/stale from 10 to 11
Bumps [actions/stale](https://github.com/actions/stale) from 10 to 11. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v10...v11) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '11' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> |
||
|
|
06f50bd37f | chore(release): 0.28.0 v0.28.0 | ||
|
|
666f261a1a | fix(kimi): spawn's own prepared instance no longer counts against the concurrency cap (#714) | ||
|
|
f99215213a | Claude Doctor Updates | ||
|
|
95e7d5df7c |
fix(kimi): cap concurrent Kimi agents to protect the shared auth chain
Every Kimi container redeems the same rotating refresh-token chain; Moonshot rotates with a short reuse grace, so two containers refreshing near-simultaneously fork the chain and a later stale redemption revokes the whole family - fleet-wide re-login (observed twice in production, each after paired spawns). With one consumer at a time refreshes are strictly sequential and the chain stays coherent, so the spawn gate now skips-and-retries Kimi spawns past ROBOCO_KIMI_MAX_CONCURRENT (default 1), sharing the provider-parked bail path. The compose files also gain the four Kimi tunables their environment blocks silently dropped - documented .env overrides never reached the orchestrator container. |
||
|
|
1c3c313c1e |
fix(coroner): playbook-kind postmortems stop rendering dead buttons
A kind=playbook process change drafts into the playbook queue at propose time and both approve/reject refuse it - but legacy rows carry no marker status, so the response defaulted to proposed and the panel rendered approve/dismiss buttons that bounce forever. The list route now derives not_applicable for playbook-kind changes regardless of stored status, which the panel already renders as its Drafted-as-playbook badge. |
||
|
|
87332346ce |
docs(kimi): README and KB parity for the Kimi provider
README gains the Kimi setup block, tech-stack row, and checklist entry (the provider prose also finally names Codex/Gemini, which it had skipped). The agent-facing KB's provider enum sentence catches up too - it still called OPENAI reserved and omitted GEMINI - and gains the Kimi runtime detail plus a config-reference section for the four Kimi settings. |
||
|
|
6374bbbed0 |
feat(kimi): Kimi K3 provider on the official kimi-code CLI (#713)
* feat(kimi): Kimi K3 provider on the official kimi-code CLI (Wave 1) ModelProvider.KIMI routes through KimiCliProvider driving Moonshot's kimi CLI on a Kimi subscription (OAuth device-code, no metered key). One-shot delivery roles only (V1), interactive ban wired in both guard lists. Auth: one shared RW auth mount; containers symlink credentials/ and oauth/ (the CLI's cross-process refresh-lock dir) into a container-local KIMI_CODE_HOME so every container and the host redeem the SAME rotating refresh chain - live-verified that per-copy chains cross-invalidate after the reuse-grace window. No orchestrator refresh daemon; an expires_at preflight exits 78. Config renderer mirrors the login-managed provider/model blocks field-for-field (live-captured; the model value is the CLI-side name, never the raw API id), plus per-role deny rules and the bash-guard as a PreToolUse hook via a wrapper script (an env key on a hooks entry makes the CLI silently drop ALL hooks - live-verified). Usage capture sums wire.jsonl usage.record 4-bucket events; sniff classifies rate-limit/auth from structured error text only, mapped to the shared 75/78 park contract. Image installs the CLI latest-at-build (no version pin, by policy) with the resolved version stamped as provenance, binary split to /usr/local away from mutable state. Migrations 090 (enum) + 091 (provider seed); catalog, pricing, routing mode, and orchestrator park/usage wiring mirror the codex integration. * feat(kimi): surface sweep + fleet-wide pin drop (Wave 2) Compose x3 gain the agent-kimi-image service and the orchestrator's read-write ~/.kimi-code mount + kimi-usage dir; .env.example documents the Kimi block. Panel mirrors ModelProvider.KIMI and adds the kimi routing mode (catalog filter, mode button, mix-picker group, badge) with tests; provider routes gain the kimi remediation entry. CLAUDE.md and docs/map document the runtime. Per the no-pins policy, agent-grok/ gemini/codex Dockerfiles drop their version pins for latest-at-build with resolved-version provenance stamps (grok resolves 0.2.112 vs the old 0.2.56 pin - verified by real builds of all four images). --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
eb470dfb33 |
fix(x): Barfly drafts become standalone link-posts
X 403s programmatic replies into conversations that don't mention the account (every non-Enterprise tier), which is Barfly's entire discovery surface - so its drafts now carry commentary plus the conversation's /i/web/status/ URL instead of a reply target. Handles are stripped (mentions in plain posts are rejected too), the 280 budget accounts for the URL, the exploration prompt asks for standalone commentary, and the reject->redraft path re-appends a dropped link. |
||
|
|
98ee4d79e5 |
fix(x): thread mention replies and audit every post outcome
x_reply drafts (the one case X's 2026-02-23 reply policy allows - the author summoned us) now post as real threaded replies via the carried mention id; barfly drafts stop sending in_reply_to_tweet_id entirely. Failed posts log a server-side warning and write an x_post.post_failed audit row; successes write x_post.posted - at the _post chokepoint so both approve routes are covered. |
||
|
|
709759fc9b | chore(deps): bump prompt-toolkit to 3.0.53 | ||
|
|
48feb033d6 |
fix(api): carry orchestration_markers in the task response
TaskResponse declares the field, but task_to_response never assigned it,
so every GET /tasks response served null. The orchestrator fetches its
work over that endpoint and builds prompts from the returned dict, so
all twelve marker reads in orchestrator.py resolved to `{}`.
Live symptom: a Barfly explorer was told "SCREENED CANDIDATES: (none)"
and correctly refused to invent a tweet — while BarflyEngine only opens
an exploration task when it HAS candidates, so the ones it gathered were
sitting on the row the whole time. The gateway path reads markers off the
ORM object directly, which is why propose_conversation_replies could see
what the prompt could not.
Same blindness applied to every other marker consumer on the dispatch
path: the spotlight's seen-features dedup, pest control's evidence
snapshot, coroner's autopsy subject, and the dev-redispatch's
original_developer lookup.
|
||
|
|
42f8a5d18e |
fix(board): nothing_to_propose exit for Board Program explorers + PR checks on slave-based PRs (#712)
* fix(board): give Board Program explorers a nothing_to_propose exit Every propose_* verb requires at least one item, so an explorer that legitimately found nothing — Barfly with no worthwhile X conversations, Coroner with no autopsy subject — had no way to close its exploration task. It declined, called i_am_idle(), and the task stayed PENDING forever: the dispatcher re-matched it every tick and respawned the board agent (~$0.61 a spawn, ~3 per 5-minute respawn-breaker cooldown window, indefinitely), and BoardProgramEngine's one-open-cycle dedup wedged that whole program shut, since the ledger row only closes once its exploration task goes terminal. nothing_to_propose(task_id, reason) is the explicit exit. task_id is required rather than inferred: one explorer role owns several independently-cadenced programs (head_marketing owns six) and each assigns its exploration task to the same agent, so several are open at once by design and guessing "the caller's oldest" completes the WRONG cycle — stamping its reason onto an unrelated program's ledger while the task actually being worked stays wedged. Resolution validates the named task exists, carries a registered program source, is assigned to the caller, and is non-terminal, then gates on the program's declared explorer role from the registry, so a program registered later needs no edit here. The reason lands on board_program_cycles (migration 089) and renders into the next cycle's LEARN context, replacing a bare "proposed 0, approved 0" with why. That write runs in its own savepoint: it flushes on the same session as the completion, and a bare try/except around a same-session flush leaves the transaction pending-rollback, so a DB blip there would discard the completion at the post-response commit while the verb reported success. All fourteen exploration prompts offer the exit, pinned by a registry-parametrized test that fails when a future program is unwired. * ci: fire PR checks on slave-based PRs, not master alone All five gating workflows declared `pull_request: branches: [master]`, but every fleet PR targets slave — cell->root, root->slave, and the CEO's own. So `pull_request` never fired for any of them, and their only coverage was the `push` trigger, which is gated on branch PREFIX (feature/bug/chore/docs/hotfix). A branch named anything else got zero checks — not a red run, an absent one — and a PR with no required check present merges on a false green. PR #711 shipped that way on a `fix/` branch. Basing on the branch a PR merges INTO rather than what its head is named makes coverage independent of branch naming, so a non-conforming prefix can only ever cost the redundant push run, never the whole gate. The same five also omitted slave from `push` (ci.yml aside, which added it for the release gate's fail-closed CI read), so the panel suite, both CodeQL analyses, and the e2e smoke never ran on the trunk master is cut from. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
a7b970a3b2 |
feat(board): materialize program items as Main-PM roots, make reports actionable (#711)
Two coupled gaps in the Board Program output path. Approved items were created unowned and in BACKLOG. Nothing dispatches BACKLOG, and once activated a cell PM claimed the parentless task as a root, where _cell_pm_complete resolves its merge target through resolve_parent_branch — which for a parentless task falls through to the project head rung. The result was a cell branch merging straight into the trunk, bypassing the Main-PM root, the root->master PR and the CEO gate (live: PRs #703 and #704 both targeted slave directly). All eight materializers now create a PENDING, main-pm-assigned root with team=Team.MAIN_PM, matching what approve_and_start does for an intake draft. The team is load-bearing, not cosmetic: _next_hint_pr_fail, _deliver_pr_fail_to_owner, delegate's wave-chain dispatch and the PR layer label all key on it, and a cell-teamed root drops the 'do NOT re-submit the root' steer that exists because of PR #138's infinite pr_fail loop. The item's own cell survives as a delegation hint in the description, which is what the Main PM's briefing renders. Periscope, Sentinel and Coroner produced artifacts with no way to act on them — three panel surfaces carried explicit 'no approve/reject UI' comments while each item already held a machine-readable suggested action. They now have per-item approve and dismiss, modelled on the roadmap queue: idempotent per item, CEO-gated, deep-copy-before-mutate so SQLAlchemy's dirty check still fires, and every decision recorded through record_decision so it reaches the next cycle's prompt. Approving materializes through the same corrected Main-PM-owned path. Target project resolves to each engine's own existing anchor — RoboCo's project for Periscope and Sentinel, the incident's project for Coroner — and fails with a clean invalid_state naming what is unresolvable rather than guessing at a repo. Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
66f0287d11 |
[3e7a5064] Eval harness: wire the real-spawn path so python -m roboco.eval run works end-to-end (#703)
* [431e73b7] Wire the real-spawn path: OrchestratorStageSpawner + disposable MCP config (#701)
* [431e73b7] Wire the eval harness real-spawn path: OrchestratorStageSpawner + disposable MCP config
_generate_mcp_config now prefers settings.api_url when set (both
PROJECT_HOST_PATH branches), so spawned MCP servers resolve to the
harness's disposable orchestrator URL instead of the real production
hostname or 127.0.0.1:port. OrchestratorStageSpawner.__init__ replaces
the NotImplementedError with a real AgentOrchestrator() constructed the
same way the production dispatcher builds it. The runner module
docstring + __main__.py docstring/run-subparser help drop the
NOT-YET-FUNCTIONAL wording. A new unit test pins the no-production-reach
guarantee: with settings.api_url patched, the MCP config's
ROBOCO_API_URL/ROBOCO_ORCHESTRATOR_URL point at the disposable URL (not
production), and the agent UUID is the real fixed UUID from
foundation.identity.AGENTS.
* [431e73b7] docs(eval): reflect the wired real-spawn path in tests map, CLAUDE.md, and CHANGELOG
---------
Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
* [5cc75f71] Fix disposable orchestrator container-reachability + document real-UUID isolation design (#705)
* [5cc75f71] Fix disposable orchestrator container-reachability + document real-UUID isolation
* [5cc75f71] docs(eval-harness): document container-reachability fix + real-UUID isolation in map docs
---------
Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
* [d96ec059] Rewrite stale eval-spawner pinning test to assert wired behavior (test-only, CI-green for PR #703) (#707) (#708)
* [d96ec059] test(eval): assert OrchestratorStageSpawner constructs a real AgentOrchestrator
Rewrite the stale pinning test that asserted the PRE-wiring
NotImplementedError (removed by commit
|
||
|
|
b3dda00e41 |
[d700055f] Replace StubObjectivesSection with live charter objective cards (#702) (#704)
* [d700055f] feat(scorecard): replace StubObjectivesSection with live charter objective cards Delete the StubObjectivesSection placeholder (fake 'Revenue growth'/'Customer retention' labels) and render a real ObjectivesSection with three positional charter objective cards, each showing its live metric against its target: first_pass_yield (90%), median_lead_time_hours (<24h), escaped_defects (0). - CockpitSummary: add optional first_pass_yield and escaped_defects fields (backend companion item not yet shipped; UI renders 'No data yet' until then) - ObjectivesSection: positional mapping objectives[i].metric -> metric i, documented as a positional-by-convention assumption; canonical fallback labels when the charter objectives array is empty/shorter than 3; 'No data yet' italic muted fallback for null/undefined metrics (SpeedSection pattern); DeliveryMetric card styling; first_pass_yield formatted as pctOrDash does - SpeedSection kept as-is; ObjectivesSection references the same median_lead_time_hours value as a peer card alongside the other two - Tests: buildSummary carries the new fields; cover present/missing metrics and absence of the fake stub labels; existing lead-time/null tests adjusted for the now-shared value and multi-card 'No data yet' * [d700055f] docs(scorecard): document live ObjectivesSection and new CockpitSummary fields Add panel/docs/frontend/company-scorecard-card.md covering the new ObjectivesSection: the three positional charter objective cards (first_pass_yield 90%, median_lead_time_hours <24h, escaped_defects 0), the positional-by-convention mapping, canonical fallback labels, the 'No data yet' fallback pattern, and the two new optional CockpitSummary fields with the backend companion-item caveat. Add a Key Symbols row for CompanyScorecardCard/ObjectivesSection in docs/map/panel.md. --------- Co-authored-by: roboco-app[bot] <302741806+roboco-app[bot]@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 PM <fe-pm@roboco.tech> |
||
|
|
80ccf415cb |
feat(cockpit): expose first_pass_yield and a real escaped-defects metric (#709)
The Company Scorecard renders three charter objectives but the cockpit summary only ever carried one of the metrics, so two cards read "No data yet" permanently. first_pass_yield is a pass-through — MetricsService.get_org_scorecard() already computes it on the same 30d/org scope the rest of the delivery block uses, and CockpitService.summary simply never forwarded it. escaped_defects is new. The obvious definition — a blocker finding opened on a task that already reached a terminal state — is unimplementable: every producer of a task_review_findings row fires as part of a bounce whose transition requires a non-terminal task, so it would read zero forever, and a permanently-green card is the same fabrication the panel change removes. What it counts instead: a blocker still at 'addressed', never 'verified', on a task that has since completed. That is reachable because stamp_addressed_verified only bulk-verifies rows matching its OWN origin, so a blocker raised by one origin and never re-confirmed by that origin survives to completion on the developer's word alone. docs/map/metrics-observability.md documents what a zero actually means: the one reachable trigger is a PM-origin blocker on a task escalated to the CEO rather than completed by the PM, since escalate_to_ceo carries no findings-resolved precondition and ceo_approve verifies only ceo-origin rows. It also records that the count is per-finding over a rolling 30-day window, which is not the same unit as the charter's "per release". Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
0fe21b1f97 |
fix(ci): exempt roboco-app[bot] from the CLA check (#710)
The App the fleet pushes and opens PRs under authors the sync/merge commits GitService creates when a task branch is brought up to date with its base, so it is a committer on essentially every fleet PR. Unlike the agent identities — whose roboco.tech emails map to no GitHub account, so CLA Assistant matches them by the display names already allowlisted — the App maps to a real account, and CLA Assistant demands a signature it cannot give: an App cannot post the sign-off comment as itself. The result is a permanently red `cla` check on fleet PRs (currently #703 and #704) that no amount of rework can clear, which sends the PR reviewer round another revision loop with nothing to fix. Exempting it is also correct on the merits: the CLA exists to obtain copyright assignment from human contributors, and the App commits on the copyright holder's own behalf. Same form as the dependabot[bot] entry. Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
a36a180b73 |
fix(orchestrator): let a failed Board Program exploration retry (#706)
`_board_dispatched` is an in-memory, never-expiring set of (agent_slug, task_id). The solo exploration dispatchers consulted it, so an exploration whose propose verb rejected was never retried for the life of the process — Periscope, Sentinel, Scales and Barfly each spawned once on 2026-07-25, failed, and sat PENDING until the stack restarted. The guard was written for the two-reviewer board REVIEW pass, where it is correct: a reviewer has no verb to advance the task, so a respawn can only loop. An explorer is the opposite — `propose_*` is exactly such a verb, so a respawn can and should advance it. Drop it from the 15 `_dispatch_*_exploration` functions. Bounding falls to `_pm_respawn_should_gate`, which is what actually bounds a loop: DB-persisted, reset by a status change, and cooled down so a deploy that fixes the cause lets the work resume. `_dispatch_board_reviewer` keeps the set (its `_board_review_complete` reads it as a has-run signal) as does vault curation (same-process race guard behind its own durable marker). The 14 `*_dispatch_is_one_shot` tests asserted the old contract on the false rationale that board roles have no progression verb; they now assert a second tick re-attempts. Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
879afc14a4 |
Board Program LEARN context, ruff 0.16, and verb-rejection observability (#700)
* fix(board): LEARN decisions name the item, not its per-cycle index A cycle's reject reasons are rendered into the NEXT cycle's exploration prompt, but the ref recorded alongside each reason was the item's stored id (item-0/item-1) — a per-cycle index that means something different every cycle and appears nowhere the explorer can resolve. The reason survived the loop; what it was about did not. Record the item's title instead, via a shared learn_ref() helper (falls back to the id when title-less, and reads target_task_title for Scales, whose items name the live task they mutate). * chore(lint): satisfy ruff 0.16 — keyword-only signatures and markdown formatting The dev toolchain resolved ruff 0.16.0, which stabilises PLR0917 (too many positional arguments) and formats python code blocks inside markdown. Both fired repo-wide and neither had anything to do with the code they flagged. - 36 signatures gain a `*` so their tail arguments are keyword-only, and the 104 call sites that passed them positionally are converted. mypy was the safety net for the static ones; the full suite caught nine more that only bind at runtime (the MCP tool functions, whose real callers already pass named JSON arguments). - 28 markdown files reformatted by 0.16's code-block formatter. - One RUF036 (`None` mid-union) autofixed in the GitLab provider. * fix(gateway): log the reason when a verb rejects A rejected envelope rides an HTTP 200, its body is never logged, and there is no trace table — so in the access log a verb an agent could not satisfy looks identical to one that worked. On 2026-07-25 four Board Programs (Periscope, Sentinel, Scales, Barfly) each POSTed their propose verb three or four times, persisted nothing, and left their exploration tasks PENDING; the reason was unrecoverable afterwards, from the logs or from the agents' own transcripts. Log error/message/remediate/missing plus the calling agent at envelope_to_response — the one chokepoint every v1 flow and do route returns through. Success envelopes stay silent. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
401f8a2cc9 |
feat(board): Board Programs — the complete twelve-program catalog (Phases 1-3) (#699)
* feat(board): Pest Control — the first project-scoped Board Program The Product Owner hunts latent defects (what the org records but nobody reads): a weekly cycle — accelerated off-schedule when the trailing-7-day rework rate crosses pest_rework_threshold, with the cheap dedup/scope gates evaluated before the metrics queries — opens one held exploration task against the least-recently-explored opted-in project (deterministic round-robin; opted_in_projects gains a stable ORDER BY), with server- assembled evidence in the spawn prompt (rework hotspots, recurring-findings and waived-minor ledger aggregates, all capped) plus prior-cycle LEARN context. The PO calls the new PO-only propose_bug_hunt verb once: ≤5 items, evidence required per item, targets validated against pest_control participation. CEO decides per item — approve materializes a BACKLOG task (source pest_control, never auto-starts), reject records the reason; both feed the LEARN ledger by exploration task id; all-terminal completes the cycle. Telegram queue pushes carry working Approve/Reject handlers mirroring the roadmap kind. Doctrine: board.md Pest Control section + product-owner verb entry + regenerated verb tables. * feat(panel): Pest Control review queue Command Center gains the pest review queue (per-item approve/reject with reason, mirroring the roadmap queue); the Programs card and the project settings participates-in checkboxes pick the new program up registry-driven — the settings section renders for the first time now that a project-scoped program exists. * feat(board): Periscope — HoM market-research brief program Weekly org-scoped cycle: a solo HoM spawn researches the market (web research with mandatory source URLs — uncited findings are rejected) and files one structured brief via the new HoM-only propose_market_brief verb: headline, cited findings, threats/opportunities, positioning note, all soup-checked and screened through the injection guard at persist time (web-derived text later reaches prompts; flags recorded, content never dropped). A brief is a report, not a proposal: the verb completes the exploration in the same call (the x_feature asymmetry), the cycle ledger auto-closes, and the CEO gets a best-effort notification with no approve/reject surface (periscope deliberately never joins Telegram's action kinds). The latest brief is injected into the roadmap exploration prompt — Periscope feeds Printer, the first cross-role program input. * feat(panel): Market Briefs tab (read-only) Business page gains a Market Briefs tab listing Periscope briefs — headline, cited findings, threats/opportunities — read-only by design; a report has nothing to approve. * feat(board): Coroner — event-triggered Auditor postmortems The first EVENT program: no cron — three best-effort hooks open an autopsy when a task bounces to its 3rd revision (the audit chokepoint), is cancelled after work started, or is budget-blocked; all gated on arming + one-open-autopsy dedup, none can fail the underlying transition. A solo Auditor spawn reads the incident (server-assembled findings + transition context) and files one propose_postmortem: incident summary, root cause, failed stage (validated against the real status vocabulary), and ONE process change — a playbook-kind change drafts via PlaybookService directly into the normal pending-curation queue; the briefed draft_playbook manifest grant was deliberately NOT added, preserving the existing 'auditor curates but never drafts' invariant test. Complete-at-propose (report asymmetry), cycle ledger auto-closes, CEO notified link-only. Integrated as a union with Periscope across the shared program surfaces. * feat(panel): Coroner postmortems card Read-only postmortems list under Business → Programs — incident, root cause, failed stage, process change; nothing to approve, the process-change artifact (a draft playbook) rides the existing curation queue. * feat(board): Sentinel — Auditor drift-watch quality reports Weekly org-scoped cycle: a solo Auditor spawn receives a server-assembled drift context (waived-findings trend, open findings by severity, conventions-violation hotspots, top spend — all capped, pure ORM) and files one propose_quality_report: headline, 1-7 area-validated items with evidence and suggested actions, overall assessment. Report semantics — complete-at-propose, cycle auto-closes, CEO notified display-only (never on Telegram's approve/reject surface); items are structured so a later convert-to-task control is cheap. Integration adopts Sentinel's module- level dict-dispatch for board-program routing (xenon-driven), folding all prior programs in; app router mounting extracted to a helper for the same budget. * feat(panel): Quality Reports tab (read-only) Business page gains the Sentinel quality-reports tab — headline, per-area observations with evidence and suggested actions; read-only, a report has nothing to approve. * feat(board): Spackle — gap-fill audit program Biweekly project-scoped PO cycle over the half-shipped surface area: API routes without panel surfaces (and vice versa), armed flags without docs, docs promises the code doesn't keep, dead-end tabs — the inventory diffing is the PO's own read-tool work, ordered by the spawn prompt with file:line citations required; the server injects only prior-cycle LEARN and the rotation target. Rotation is now a shared module-level helper (pick_rotation_target, parameterized by source) both project-scoped engines use — pest_control delegates to it, behavior-identical, with a cross-pollution test proving the two programs' rotations stay independent. propose_gap_fill mirrors the bug-hunt verb (≤5 items, two-sided evidence required, participation gate); per-item CEO decide materializes BACKLOG source=spackle tasks; full Telegram kind incl. approve/reject handlers. All seven program routers now mount from one helper. * feat(panel): Spackle gap-fill review queue Command Center gains the gap-fill queue mirroring the pest-control one — per-item approve/reject with the two-sided gap evidence rendered. * feat(board): Scales — monthly portfolio rebalance Org-scoped PO cycle over the stale backlog: the spawn receives a capped stale-task snapshot (BACKLOG/PENDING unclaimed >30 days) plus the charter and prior-cycle LEARN, and files one propose_rebalance — 1-7 items, each a resolvable task_ref with action reprioritize (validated new priority) or cancel, rationale required. Per-item CEO decide: approve EXECUTES the action (audited priority update, or the normal cancel path) — the first program whose materializer mutates existing tasks instead of creating them; reject records the reason; LEARN by exploration task id; all-terminal completes the cycle. Full Telegram decide-kind wiring. Integrated as the eight-program union (registry, dict dispatch, routers helper, teardown enumerations). * feat(panel): Scales rebalance review queue Command Center gains the rebalance queue — per-item approve/reject with the action, target task, and rationale rendered. * feat(board): Mirror — quarterly positioning audit Project-scoped HoM cycle over messaging surfaces: README claims vs shipped reality, docs-site promises vs code, charter alignment — the audit is the HoM's own read-tool work with citations required; the server injects the charter, prior-cycle LEARN, and the shared rotation target. propose_ messaging_fixes mirrors the gap-fill verb (≤5 items, drift evidence naming claim + contradicting reality, participation gate); per-item CEO decide materializes BACKLOG source=mirror documentation tasks; full Telegram decide-kind wiring. Nine-program union across the shared surfaces. * feat(panel): Mirror messaging-fixes review queue * feat(board): Megaphone — HoM standing editorial calendar Cron cycle (3 days, org-scoped, gated on X credentials — drafting content nobody can post is pointless): the HoM receives a shipped-this-week digest plus Unreleased changelog bullets and files one propose_editorial_post (angle-validated, ≤280, brand voice) that materializes a held x_editorial draft through the SAME X-queue origination chokepoint release posts use — zero new approval surface, notifications and CEO decide for free. Complete-at-propose; cycle auto-closes. Ten-program union. * feat(panel): x_editorial source labels in the X queue surfaces * feat(board): Librarian — proactive playbook mining Biweekly org-scoped Auditor cycle: mines recurring non-private learning journals (≥2-count grouping with a recency fallback) against the existing playbook-title inventory and files one propose_playbook_drafts — 1-3 drafts, each with the repeated-pattern evidence that justifies it, duplicate titles rejected in-batch and against the live store. Drafts are created via PlaybookService directly (the Coroner precedent — the 'auditor curates but never drafts' do-verb invariant stays intact and tested) and land in the normal pending-curation queue the Auditor's own triage already surfaces; no new panel surface. Complete-at-propose; display-only CEO notification. Eleven-program union. * feat(board): War Room — release campaign planning EVENT program with a REAL originator (unlike coroner's stub): a release publish hooks a campaign brief beside the release-post seam, and the CEO's run-now originates on demand — the cron loop never fires it. The HoM designs a 2-6 post arc (teaser → launch → follow-up → spotlight; 280-cap, future strictly-ascending publish_after, stage vocabulary) and one propose_campaign call materializes each post as a held x_campaign draft through the X-queue chokepoint. V1 is manual-cadence by design: publish_ after renders as queue guidance and the CEO approves each post at its moment — nothing auto-posts, ever; the auto-schedule upgrade is a documented ceiling. Twelve-program union: full registry complete. * feat(panel): x_campaign labels + publish-after guidance in the X queue * feat(board): Barfly — adjacent-conversation replies Cron cycle (2 days, org-scoped, X-credentials gated): the engine searches X for conversations where RoboCo is relevant but unmentioned (new OAuth- signed search_recent on the client; queries + candidate cap configurable), screens every fetched tweet through the injection guard (stored unclamped — a clamp was truncating the candidate under the envelope, caught by the dev's own tests), dedupes via the existing x_seen_mentions ledger (no migration; also prevents double-drafting against the mentions poll), and opens one held HoM exploration carrying the screened candidates. propose_ conversation_replies enforces candidate-id-only replies (≤5, 280-cap); each materializes a held x_barfly draft through the X-queue chokepoint, threaded via a new in_reply_to seam on post_tweet that only x_barfly drafts use. The X redraft machinery is now dict-dispatch over per-source extractors with reply-ref carry for x_barfly. Thirteen-program registry. War Room's test fakes gained the new abstract search_recent stub. * feat(board): Dogfood — the PO walks the product The fourteenth and final registry entry, completing the catalog. EVENT program (release-publish hook beside the war-room hook + CEO run-now, both through the same real originator; the cron loop never fires it), project- scoped with shared rotation. The permission surface is the careful part: the PO's dogfood spawn — and ONLY that spawn — gets the Playwright MCP mounted, via a task-scoped fail-closed probe mirroring the video-authoring precedent (a PO spawned for roadmap/pest/scales never sees browser tools; tested both ways); the PM agent image bakes chromium unconditionally like the ux image, the mount stays task-gated in code. The walk targets the rotation target's live surfaces (panel_base_url only when the target is the org's own project, honest degradation otherwise); propose_friction_ fixes files ≤5 walked-path-evidenced items; per-item CEO decide materializes BACKLOG source=dogfood tasks; full Telegram decide kind. Also: megaphone/librarian/war_room arming keys restored to the settings validator — their panel toggles would have been rejected (dropped in earlier unions; the same silent-arming class the drill killed once already). * feat(panel): Dogfood friction review queue * chore(board): final whole-branch sweep fixes The night's closing adversarial pass over the integrated fourteen-program registry found ONE functional defect — the war-room test fakes' post_tweet predated Barfly's in_reply_to_tweet_id kwarg (LSP violation, the only red in an otherwise fully green gate) — plus doc/test drift, all fixed: the source-parity test completes to fourteen (spackle/mirror were silently absent while its neighboring comment claimed full coverage), the PO identity doc gains its missing Dogfood verb, the auditor quick-list gains propose_postmortem, three stale comments corrected (rotation docstring, panel registry header, X source enumerations), the dogfood release-hook gains the exception-swallow test its four sibling hooks already had, and the CHANGELOG's Unreleased section documents the whole Board Programs train. Full make quality: exit 0, all gates green. * docs: full documentation sweep for the Board Programs train CLAUDE.md's roadmap-engine entry superseded by the Board Program registry entry (all fourteen programs, arming, scoping, LEARN, guardrails) with the role verb tables and playwright row refreshed; docs/rag gains the agent- facing architecture doc plus full propose_* call-shape sections in the three board role docs, and corrects the strategy-engine section to shipped reality (only idle→roadmap is wired); docs/map covers the registry + all twelve engines with flags, gotchas, and drift notes. The 0.27.0 reference inventory confirmed only the release-executor's canonical set carries the version — left for the 0.28.0 cut. * feat(board): human titles + descriptions on every program surface Raw registry keys rendered as bare panel labels — an operator reading x_feature had no idea what enabling or running it does. The registry dataclass gains title/description (test-enforced non-empty for every entry, unique titles), the API passes them through, and every surface renders title-with-description-tooltip instead of the key: the Programs card (label, toggle hint, run-now toast), and the project settings participates-in/excluded-from checkboxes. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
e77c3b7a63 |
feat(board): Board Program registry — Phase 1 (engine, LEARN ledger, per-project scoping, panel) (#689)
* feat(board): Board Program registry — generic trigger/dedup/originate/LEARN engine
One registry (foundation/policy/board_programs.py) + one BoardProgramEngine +
one orchestrator loop replace the bespoke roadmap/spotlight loops, behavior-
preserved: same sources, dispatch routing, one-open-cycle dedup (ledger rows
auto-close when their exploration task goes terminal, so x_feature's
complete-at-propose flow can't wedge), and live per-program interval
overrides with the tick capped at 1h.
program_armed() is the single arming chokepoint: the settings-store
board_program.<key>.enabled override when present, else the legacy flag —
routed through BoardProgramEngine, RoadmapEngine.run_cycle, and XEngine's
spotlight gate, so the panel toggle can never be a silent no-op against a
legacy boot flag.
LEARN: board_program_cycles (migration 087) accrues per-item CEO decisions
(exact attribution by exploration_task_id where the caller holds it) and
feeds the last closed cycles back into both exploration prompts. The
strategy engine's idle signal now opens a roadmap cycle (enabled+dedup
respected) instead of only nudging.
Per-project scoping (migration 088, projects.board_programs, dual polarity):
plain keys opt a project INTO project-scoped programs; "!key" opts it OUT
of an org-scoped program's outputs (default eligible — parity). Enforced at
propose_roadmap (names the excluded project) and defensively at materialize;
validation rejects unknown keys and meaningless polarity both directions.
API: GET /api/board-programs + POST /api/board-programs/{key}/run-now
(CEO-gated); settings keys for both migrated programs.
* feat(panel): Board Programs card + per-project program controls
Business page gains a Programs tab: per-program rows (role, trigger, scope,
open-cycle badge), enabled switch on the settings-store key, Run now
(disabled while a cycle is open). The edit-project dialog gains the
program controls next to the CI-watch/video toggles: participates-in
checkboxes for project-scoped programs, excluded-from checkboxes for
org-scoped outputs.
* test(board): full-gate hermeticity — mypy casts + shared-DB purge fixtures
make quality runs one pytest process over all suites against the shared
persistent DB: integration collects before unit, so the board-programs API
test's committed run-now state (settings-store overrides, an open cycle row,
its board_roadmap task) poisoned 13 downstream unit tests that pass in
isolation. The polluter now purges its own committed state in fixture
teardown, and the four consumer files get an autouse per-test purge
(board_program.% settings keys, ledger rows, open exploration tasks) so
they are hermetic regardless of collection order. Also the four
cast("UUID", ...) sites the tests-scope mypy run requires.
* feat(panel): re-home per-project program controls onto the settings page
Wave C deleted the edit-project dialog these controls originally landed in;
they now live on the project settings page's budget/ops card next to the
CI-watch/video toggles — participates-in switches for project-scoped
programs, excluded-from switches for org-scoped outputs, dual-polarity
tooltips, order-independent dirty tracking. Nine makeProject test fixtures
gain the required board_programs field the rebase left behind.
---------
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
|
||
|
|
cbddfc7cb3 |
[c71959c3] Video: release 0.27.0 (#698)
* [c71959c3] feat(motion): add release-0.27.0 panel-demo clip Extends the panel-demo kit register (kit/) with a v0.27.0 release composition: identity cold open, four feature cards (WAF real-IP resolution, Telegram Mini App V6, Codex/Gemini providers, cost-tiered routing) flipping from in-progress to completed, a camera that pushes per beat via pk-camera data-shots, a cursor that clicks and travels via pk-cursor data-waypoints, a receipt stats overlay, a shipped toast, and the roboco.tech outro. Ships vertical.html, square.html, props.js, captions.json, and a vitest smoke test mirroring release-0.26.0's invariants. * [c71959c3] fix(motion): implement genuinely non-uniform card-beat gaps in release-0.27.0 clip The four .pk-card animation-delay values were uniform 5.0s/10.0s/15.0s/20.0s in both vertical.html and square.html -- byte-for-byte identical to release-0.26.0's metronomic spacing the craft bar bans -- despite the prior decision_log claiming 5.5s/6.4s/5.3s varied gaps had been added. Card delays are now 5.0/10.5/16.9/22.2 (real 5.5s/6.4s/5.3s gaps). Camera data-shots, cursor data-waypoints, and the stats/toast/outro tail in both files are re-warped with a shared piecewise-linear time function anchored at each card's new beat, so nothing desyncs and the tail still fits inside the fixed 40s runtime. Added a regression test asserting the four card gaps are not all identical. * [c71959c3] docs(motion): add release-0.27.0 composition section to README --------- Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech> Co-authored-by: UX/UI Documenter <ux-doc@roboco.tech> |
||
|
|
069bb621f4 | docs(changelog): the x-drafting fix bullet belongs under Unreleased, not 0.27.0 | ||
|
|
037114338a |
fix(x): release-post drafts stop parroting changelog bullets
Highlights reorder to marketing order (Added/Changed before Fixed/Security — the drafting model anchors on highlight #1 and the changelog opens with Security), the prompt bans verbatim highlight copying and internal plumbing jargon, and the deterministic fallback becomes a generic announcement that can never quote a raw bullet. |
||
|
|
29243771e4 | sync: master → slave | ||
|
|
18eaacb9ea | chore(release): 0.27.0 v0.27.0 | ||
|
|
e5fb976069 | chore(panel): prettier reflow of the usage mocks | ||
|
|
902fc9a78c |
chore(models): finish the opus-5 replacement — no stale opus-4 fixtures remain
Every fixture, comment example, and panel mock that presented an Opus 4.x id as current now carries claude-opus-5 (or the current sonnet/haiku ids in the panel usage mocks). The only deliberate claude-opus-4 survivors are the pricing-table fragment and its tests — they price the historical usage rows, which would otherwise re-read as $0. |
||
|
|
3c87596685 |
chore(models): move the opus alias to claude-opus-5
Claude Opus 5 released today — same $5/$25 sticker, 1M context; Opus 4.8 moves to legacy. The pricing table gains a dedicated claude-opus-5 fragment (the claude-opus-4 substring doesn't cover it, so without the row the fleet's Opus usage would silently cost-track as $0 — exactly what test_opus_is_priced now guards). |
||
|
|
9b407d764d |
fix(ci): docs-redirects-only commits fire a CI run so the release gate gets a verdict
The push/pull_request paths filters covered docs/panel/motion but not docs-redirects/**, so a redirect-stub commit landing as the slave tip produced no CI run and the fail-closed release-readiness gate read "unknown" — blocking every release proposal until unrelated code landed. |
||
|
|
803638e8e9 |
chore(models): upgrade the opus alias to claude-opus-4-8
MODEL_MAP["opus"] moves off claude-opus-4-6 to the newest Opus tier at the same price; pricing already matched via the claude-opus-4 fragment, and a new test_opus_is_priced guard keeps the alias priced. Fixtures, panel mocks, and docs follow. |
||
|
|
f90caf6f9b |
docs(redirects): point the old communications-and-journals URL at its renamed target
The docs site renamed the page to journals-and-notifications when the channel/session comms subsystem was removed; the stub still redirected the old Pages URL to the dead path, landing visitors on a 404. |
||
|
|
2a9339225c |
docs(release): 0.27.0 prep — changelog through Wave C, map + rag + CLAUDE.md current (#697)
Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
8f01446243 |
chore(panel): one disclosure primitive, DialogFooter everywhere, three dialog widths (Wave C-2) (#695)
* feat(panel): promote project settings to a full page The edit-project dialog carried ~30 fields across 7 concerns in one flat scroll with a per-tab width swap — outgrown. Project settings now live at /projects/[id]/settings as a card-per-concern grid (the settings page's own pattern) with per-card save and Conventions as a page-level tab at natural width; the list Edit action routes there, and a slim quick-edit dialog (name/cell/active) replaces the kitchen-sink. * chore(panel): one disclosure primitive, DialogFooter everywhere, three dialog widths collapsible-section moves to ui/ as the single sectioned-disclosure primitive (task dialogs' raw Collapsible and create-project's ad-hoc showAdvanced converge onto it); every hand-rolled dialog footer becomes DialogFooter; dialog widths collapse from ten ad-hoc classes to three named sizes, with deliberate outliers annotated. No behavioral change. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
646be2351e |
feat(panel): promote project settings to a full page (#696)
The edit-project dialog carried ~30 fields across 7 concerns in one flat scroll with a per-tab width swap — outgrown. Project settings now live at /projects/[id]/settings as a card-per-concern grid (the settings page's own pattern) with per-card save and Conventions as a page-level tab at natural width; the list Edit action routes there, and a slim quick-edit dialog (name/cell/active) replaces the kitchen-sink. Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
9d39005c58 |
[73275ff0] Panel consistency & UX wave: forms audit, command palette, kanban merge, responsiveness (#694)
* [170c9578] Frontend: Panel consistency & UX wave (forms audit, command palette, kanban merge, responsiveness) (#688) * [f1957610] Stream1-A: Project form sync (#667) * [f1957610] feat(panel): expose codegen_command in create-project dialog Add the Codegen Command input to create-project-dialog.tsx, mirroring the field already present in edit-project-dialog.tsx. All other fields named in this task (git_provider, github_installation_id, environments, protected_branches, video_engine_enabled, monthly_budget_usd with gt=0 client validation, sandbox_extensions) were already implemented on this branch's base by prior work, and the ProjectCreate/ProjectUpdate types in types/index.ts already match the backend ProjectCreateRequest/ProjectUpdateRequest schemas exactly -- no further changes were needed there. * [f1957610] docs(forms): add project-fields-audit reference for future field consistency Create a living audit of which project configuration fields are exposed in the create vs. edit dialogs, mapping to the backend ProjectCreateRequest/ProjectUpdateRequest schemas. This serves as a future reference to prevent field-sync gaps and documents the intentional asymmetry (create focuses on git setup, edit adds autonomy/maintenance toggles). Includes a checklist for adding new project fields in the future. --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [515697f4] feat(panel): settings save feedback + forms-audit.md living reference (#669) Add per-toggle confirmation toasts to the four Settings-page prefs (notifications, sound, auto refresh, refresh interval) so an immediate write is never indistinguishable from a silent failure. These prefs stay on the already-shipped client-persisted useUIStore pattern (CHANGELOG.md "Settings preferences persist as real client prefs instead of 422-ing as theater") rather than settingsApi, since the backend _VALIDATORS allowlist deliberately excludes them and the parent task scoped this stream as needing no backend schema changes. Check in docs/forms-audit.md: a living form x field x verdict table covering Stream1-A (project dialogs), Stream1-B (task dialogs), and this settings work, with a header note that future backend schema changes require a row update. Fixes the project-slug help text (now correctly says letters/numbers/hyphens, not just hyphens). Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> * [80a215a4] Stream2-A: Command palette component (#670) * [80a215a4] feat(panel): Cmd+K command palette component Radix Dialog + combobox pattern searching tasks/agents/projects/pages, localStorage recents under roboco-cmd-recents, keyboard nav (arrows/ Enter/Escape), mounted globally in the dashboard layout. * [80a215a4] fix(panel): restore fields dropped from ui-store.ts by prior merge Stream1-C's merge stripped notificationsEnabled, soundEnabled, autoRefresh, refreshIntervalSeconds, a2aContextOpen, quickActionIds, productsView, and projectsView from the shared UI store, breaking typecheck for settings/quick-actions/products/projects/a2a/notification consumers repo-wide. Restored per already-committed tests + consumers. * [80a215a4] docs(panel): add command palette reference guide Documents the global Cmd+K search feature: usage (keyboard shortcuts, search categories, recents), architecture (CommandPalette component, useCommandPalette hook, fuzzy-match and recents helpers), data flow, and verification against live API data. --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [cdc371d1] Stream4-A: Responsiveness audit and fix — wide-content pages (#671) * [cdc371d1] fix(panel): bump Button sm size to 36px touch-target floor Button's size="sm" variant was h-8 (32px), used as literal row-action buttons on the overview page's CEO Approval/PR Review queues and other controls across settings/metrics/agents/a2a. Bump to h-9 (36px) to meet the touch-target floor everywhere at once, keeping the smaller horizontal padding/gap intact for visual density. * [cdc371d1] fix(panel): make AlertDialog scroll its body at short viewport heights AlertDialogContent lacked the max-h-[85vh]/overflow-y-auto that the sibling DialogContent already has, and AlertDialogFooter lacked DialogFooter's sticky bottom-0 pinning. A tall description at a short viewport height (mobile landscape) could clip the action buttons off screen with no way to reach them. Affects the settings page's GitHubAppCredentialsCard/FeatureFlagsCard confirm dialogs (and every other AlertDialog app-wide). Ports DialogContent's already-solved scroll pattern onto AlertDialogContent/Footer. * [cdc371d1] fix(panel): wrap Scorecards Members table in ResponsiveTable metrics/scorecards-tab.tsx's 9-column Members table was a bare <Table> with no mobile-card fallback, unlike its sibling tables in the same file (Rework, SpawnWaste) and sessions-table.tsx, which already use the established ResponsiveTable wrapper. Add a MemberCard component and wrap the table so it stacks as cards below md instead of forcing a cramped in-card horizontal scroll on a 375px viewport. --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> * [b25fca69] Stream2-B: Header integration for command palette (#675) * [b25fca69] Wire header search into Stream2-A command palette: click trigger via useUIStore.setCommandPaletteOpen, remove disabled input and Coming Soon tooltip remnants * [b25fca69] Wire header search into Stream2-A command palette: click trigger via useUIStore.setCommandPaletteOpen, remove disabled input and Coming Soon tooltip remnants --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> * [e4ce5b9a] Stream3-A: Tasks page List|Kanban tabs + kanban embed (#674) * [e4ce5b9a] feat(tasks): add List|Kanban tabs to tasks page sharing URL filter state Add top-level List|Kanban Tabs above the tasks page filter bar. List tab renders the existing TaskFilters+TaskTable unchanged; Kanban tab embeds the existing DevKanban/QaKanban/PrReviewKanban/PmKanban views via nested sub-tabs (dev/qa/pr-review/pm), mirroring the standalone /kanban page's own tab styling (tooltip-wrapped triggers, pickTab helper). Both tabs read/write `tab`/`view` query params through the page's existing updateParams pattern, so all filters persist across tab switches. The four kanban view wrappers gain an optional controlled team/onTeamChange pair so the team filter is shared bidirectionally with the List tab's team filter, while staying backward compatible (uncontrolled, initialTeam-only) for the standalone /kanban route. KanbanBoard's dnd-kit drag-and-drop and mobile single-column navigation are untouched. * [e4ce5b9a] docs(tasks): add tasks-page-tabs.md documenting List|Kanban tab structure and shared filter state --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [18c52802] feat(panel): redirect /kanban to Tasks kanban tab, remove sidebar entry, swap bottom tab bar to Agents (#679) Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> * Stream4-B: Responsiveness fixes — remaining dashboard pages (#676) * [fee25542] fix(a11y): bump sub-36px icon-sm touch targets to 36px on remaining pages Both kb-search-bar.tsx (Knowledge Base search clear button) and self-hosted-section.tsx (Settings token show/hide button) used Button size="icon-sm" (32px) for an absolutely-positioned input adornment, below the 36px minimum touch-target size. Bumped both to size="icon" (36px, matching the 36px input height) and adjusted the absolute-position offset so the button still sits fully inside each input's existing right padding reservation. Audited every remaining dashboard page (everything Stream4-A's wide-content/table fixes didn't already cover): no un-wrapped wide tables remain (every <Table> already rides ResponsiveTable), and every DialogContent across the repo already inherits or supplies max-h-[*vh] + overflow-y-auto, so dialogs stay usable at small viewport heights. * [fee25542] fix(a11y): re-land sub-36px touch target and overflow fixes after sync_branch reset them again Re-applies the fda2ac0c fix content a third time -- sync_branch's rebase+force-push reset the branch and working tree back to the stale |
||
|
|
987eb09c78 |
fix(workspace): role-aware worktree refresh at every spawn (#692)
* fix(workspace): refresh a present per-task worktree at every respawn ensure_worktree_self_heal treated an already-present worktree as a pure no-op (venv-link + chown only), so a worktree created once at first claim or first claim_review stayed frozen at that commit across every later respawn even as new commits landed on origin — the root mechanism behind a live multi-round QA/PR-gate bounce loop, where the reviewer kept re-examining its own stale round-1 checkout. _ensure_worktree_before_spawn now classifies the caller's role (WORKTREE_AUTHOR_ROLES: developer/documenter, mirroring the gateway commit tool's RBAC) and _refresh_present_worktree compares local HEAD against origin/<branch>: behind-or-equal fast-forwards for every role (never discarding an author's uncommitted edits to do it); strictly ahead is always left alone; diverged only resets for a pure reader, whose local history can never be anything but a stale prior-round checkout. conventions_check_for_task's list-vs-content gap (list from git objects, content from the physical worktree) is closed as a side effect: the reviewer's worktree is now current as of spawn, and the branch under review gains no further commits while it sits in awaiting_pr_review. * fix(workspace): refresh re-added worktrees; fail the dirty guard toward preservation - A pruned worktree re-added from a surviving local ref now runs the same fetch-and-classify refresh as a present one, so an evicted reviewer worktree cannot resurrect a stale checkout. - A failing git status reads as dirty, never clean: the guard that protects an author's uncommitted edits fails toward preservation. - The hard reset verifies the worktree is actually on the task branch first; a detached or drifted worktree is left alone with a warning. - The conventions-check docstring states the remaining second-claim ceiling instead of claiming full closure. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
e97f46af6e |
fix(git): reviewer reads must prefer origin over a diverged local ref (#690)
* fix(git): reviewer reads must prefer origin over a diverged local ref _resolve_head_ref (GitService.diff/list_changed_files/read_file_at_branch) kept local priority on ANY divergence from origin, real or rewritten. A reviewer's clone parked on pre-rebase history after the branch's routine force-push sync stayed frozen there across every subsequent review round, while origin held every fix commit — QA repeatedly bounced work that had already landed. Every caller here is a reader, never the branch's own author mid-write, so origin now wins whenever it carries anything the local ref lacks; local keeps priority only when it strictly contains origin (unpushed commits, or equal). The read-only git MCP surface (roboco_git_log) hit the same staleness through a separate path: /api/git/log resolved the requested branch as a bare name straight off whatever the caller's own clone had on disk, with no fetch at all. It now routes through the same fixed _resolve_head_ref. * test(e2e): give the armed flow-verb timeout real headroom The armed value is also verb-2's entire execution budget (claim + every claim guard + set_plan + start + tracing gate), which grows as guards land; 1s flaked on loaded CI runners while passing locally. The cancel-and-release semantics only need the timeout far below the hang. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
eb0dcb6ecb |
fix(orchestrator): task-scoped oscillation breaker for escalate/unblock ping-pong (#685)
* fix(orchestrator): task-scoped oscillation breaker for escalate/unblock ping-pong An escalation ping-pong oscillates a task between two agents (cell PM escalate_up -> BLOCKED -> main PM unblock -> restored -> respawn -> escalate again). The per-(agent, task) respawn gate never trips on it: the restored side is dispatched by _dispatch_claimed_without_agent, which consults no respawn counter at all, so one side of the round trip always has fuel regardless of the other's strikes — and even a tripped main-PM counter only stalls the task silently at blocked instead of surfacing the oscillation. - Strikes are counted task-scoped at the unblock() chokepoint (agent-agnostic; legitimate needs_revision rework never calls unblock, so it structurally cannot trip this), durable in the existing orchestration_markers column — no migration. - Progress between round-trips (commits / revision_count advancing) resets the count: real forward motion is not an oscillation. - On trip: the task is blocked with a HUMAN resolver (the budget-breach posture), both dispatchers stop respawning onto it, further unblock() refuses until an admin override clears the marker, and the CEO notification names both agents and the cycle count. - _notification_has_live_work now treats a HITL-blocked related task as no live work, closing the same loop for the admin-route escalation path. * fix(orchestrator): wire the oscillation trip to the dispatchers and make recovery reachable - TaskResponse serializes blocker_resolver_type: the dispatchers' HITL-blocked skip and the notification-path live-work check now actually fire over the wire instead of only against in-process rows. - The oscillation marker clears on every human transition out of BLOCKED (snapshot or not), and the human unblock route treats a tripped task as the requested intervention: clears the marker and proceeds, while the agent gateway verb keeps refusing. - The progress fingerprint includes the terminal-children count, so a coordination root whose children advanced between escalations resets instead of accruing toward a false trip. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
23ae0ca217 |
fix(gateway): covers_parent_criteria hint that teaches the shape; CEO pause/resume (#686)
* fix(gateway): teach the delegate remediate + PM prompt the covers_parent_criteria shape; allow CEO through the plain pause route - A child draft rejected for missing covers_parent_criteria now gets a copy-pasteable corrected skeleton with the parent's real criteria inlined, and the PM delegation guidance shows the field as part of every child draft — a PM no longer loops on a rejection that named the field but never showed the shape. - The plain pause route now authorizes the CEO tier like its sibling lifecycle routes; agent-side pause restrictions are unchanged. * fix(gateway): delegate-coverage hint heals and degrades on legacy parents - The coverage-reject path self-heals a criteria-bearing parent whose ids are empty or out of length before rendering the hint, so the skeleton always shows real references; the renderer itself also falls back to quoted criterion texts for any criterion without an id instead of emitting a placeholder or truncating the listing. - The remediate names both legal reference forms (id or exact text) again. - Route comments state the pause/resume check as deliberately CEO-only instead of claiming a precedent whose role set is wider. * test(gateway): real TaskTable rows in the remediation hint round-trips mypy over tests/ rejects a SimpleNamespace where unknown_ac_refs takes a TaskTable; instantiating the ORM row directly needs no session and types cleanly. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
4b2546ae19 |
fix(findings): path-shaped file refs + per-round collapsible findings (#687)
* fix(findings): enforce path-shaped file refs; group panel findings by round - The findings chokepoint rejects a file that is not a repo-relative path shape (prose like a PR reference validated before, and the panel then rendered a doomed file-content fetch for it) — narrative belongs in evidence, the remediate says so. - The task-detail Findings tab groups findings into per-round collapsible sections (newest expanded) and only attempts a code snippet for a path-shaped file ref, so historical prose refs render as plain metadata instead of a broken loader. * fix(findings): admit client-repo path conventions; teach the file-less option - The shape gate reviews arbitrary client projects, not just this repo: plus and at-sign join the character class so SvelteKit route files, @types dirs, and @2x assets stay citable. Spaces stay excluded — they are the prose signal. - The file-rejection remediate names the file-less option for cross-cutting findings. - The client mirror notes its deliberate non-ASCII divergence from the server gate (unicode server-pass renders snippetless, fail-open). --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
21910d75ea |
chore(board): revive dormant board wiring — research key, pitch flow, auditor playbooks (#684)
* chore(compose): pass research key/provider + provisioning token/org through to the orchestrator ROBOCO_RESEARCH_API_KEY / ROBOCO_RESEARCH_PROVIDER and ROBOCO_PROVISIONING_TOKEN / ROBOCO_PROVISIONING_ORG were absent from every compose environment stanza, so .env values never reached the container: research silently ran on the NullProvider (empty results forever) and any approved pitch died on ProvisioningDisabledError. .env.example also falsely claimed the provisioning creds are panel-managed. * feat(board): pitch CEO notification + auditor playbook-draft surfacing A proposed pitch now nudges the CEO (APPROVAL notification + Telegram link to the Pitches tab, best-effort — a send failure never fails the verb). auditor_triage surfaces the oldest pending playbook draft once anomalies are clear — the curation verbs were granted but nothing ever pointed the Auditor at the review queue; the scheduled audit prompt names the discovery path. * docs(prompts): pitch doctrine section + auditor reply-only-dm drift fix board.md never mentioned the pitch verb, so no board agent ever had a reason to call it — it gets a dedicated section mirroring the roadmap/spotlight ones, plus a roadmap-exploration escape hatch (needs-its-own-repo ideas pitch instead). product-owner.md gains its missing propose_roadmap + pitch entries. The flat 'Auditor has no dm' claims are corrected to the real grant: never initiates, reply-only in a CEO-opened thread. Doctrine guarded by a prompt-content test. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
71f5426e40 |
fix(git): never discard committed local work in rebase_onto_base (#683)
The shared rebase primitive (dev sync_branch verb, PM/CEO rebase path,
submit-freshen, merge-conflict resolver) opened with fetch -> checkout ->
unconditional reset --hard origin/<head_branch>. The dirty-tree gate
protects uncommitted edits only; the reset silently rewound past every
committed-but-unpushed commit — and the commit do-verb never pushes, so
mid-rework a dev routinely has exactly that. The force-with-lease push
then republished the truncated branch as authoritative (the lease
matched the freshly-fetched, never-moved origin ref).
rebase_onto_base now classifies local vs origin/<head> post-fetch:
- behind/equal: reset --hard origin as before (origin loses nothing)
- strictly ahead: reset skipped — the rebase runs from the local tip and
the lease'd push publishes the previously-doomed commits
- diverged: a patch-equivalence probe (rev-list --right-only
--cherry-pick) first rescues the self-inflicted residue of a prior
rebase whose force-push failed (treated as ahead, self-heals on
retry); only genuine two-sided divergence returns a new
{status: diverged, local_only, origin_only} — no reset, no rebase,
no push, neither side silently discarded
- an absent local ref is recovered from origin (branch + checkout,
never reset)
Callers: the sync_branch verb maps diverged to an actionable envelope
steering to i_am_blocked (stash-preserved note included); the
submit-freshen hard-rejects it like conflicts; the merge-conflict
resolver already escalates any non-rebased/superseded status to the
CEO and degrades gracefully (pinned by test, no code change).
New real-git suite (bare origin + clone, no subprocess mocking)
asserts origin-side outcomes: ahead-publishes, behind-adopts,
diverged-refuses-untouched, absent-ref recovery, superseded,
conflicts, and wedge self-heal on retry via a rejecting pre-receive
hook.
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
|
||
|
|
3516d925fe |
fix(tasks): reconcile acceptance_criteria_ids at the update chokepoint (#682)
Every post-create rewrite of acceptance_criteria (task PATCH route, prompter update_live_draft / _patch_batch_child / update_live_batch) routes through TaskService.update()'s generic field loop, which overwrote the criteria without touching acceptance_criteria_ids — leaving ids mismatched or empty, and an empty id list silently disabled the parent-coverage gate entirely. - New pure _reconcile_ac_ids: one id per new criterion; text-unchanged criteria keep their id (children and findings reference criteria by id or exact text — a blanket re-mint would orphan every live reference), new/reworded text mints fresh, dropped criteria drop theirs. create() now stamps through the same helper (explicitly supplied ids still win). - update() derives acceptance_criteria_ids whenever acceptance_criteria is rewritten without an explicit id list. - The parent-coverage gate self-heals a criteria-bearing row whose ids are empty/out-of-length (re-stamp in place) instead of returning early and silently waiving coverage for the whole subtree. Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
c4ba351ae0 |
fix(sequencing): reachability-aware claim bar + sequence_held surfacing (#681)
Three coupled claim-path bugs from the 2026-07-24 live incident, fixed at the shared root: - The edge-agnostic sequence bar phantom-held a task behind an unrelated, never-connected same-parent sibling that coincidentally shared a lower raw sequence (stamp_wave_sequence stamps from a partial per-task view). _claim_blocked_by_sequence now branches on is_batch_root_subtask: a MegaTask root-subtask (globally-computed Kahn wave, a deliberate staged-release barrier) keeps the strict rule unchanged; every other same-parent context routes through the pure sequence_blocker_id, which only blocks on a real transitive predecessor via dependency_ids UNIONED with completed_dependency_ids. A task with no same-parent dependency edge at all falls back to the raw bar unchanged (#452 preserved). - The hold surfaced as claim()'s bare None and was misdiagnosed by the verb runner as a concurrent-transition invalid_state. New sequence_hold_reason + a proactive _sequencing_claim_guard return a dedicated Envelope.sequence_held naming the blocker, on both the PENDING and NEEDS_REVISION reclaim paths. - give_me_work offered tasks the claim gate then rejected: both offer paths (list_pending_for_agent, _drop_dependency_held) now consult the bar via the exact claim predicate (is_pending_claim_blocked, extended to NEEDS_REVISION). Co-authored-by: Renn F <rennf93@users.noreply.github.com> |