mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
ce676234bb05c4d55e7513dcb5d5ecfd6ea0fcc6
27
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
925e00d483 |
chore(comms): purge dead say()/channel refs the first sweep missed
The #306 teardown finalize (
|
||
|
|
7cb00611e1 |
chore(comms): finalize #306 teardown — changelog + purge dangling refs
#306 removed the channels/sessions/messages subsystem but left dangling references. Agents were still told to call removed verbs at spawn, and a maintenance script referenced a dropped table. - prompts (roles/identities/teams/base): drop say/open_session/link_session/ channels() and the dead Channels sections; comms rows now teach A2A (dm + read_a2a); renumber the PM workflow steps the removed open_session step left behind - scripts/reset_runtime_state.sql: drop the dropped chunks_conversations table - models/events.py: mark the retired SESSION_*/MESSAGE_SENT enum members inert - mcp/{do,flow}_server.py: drop the dead SESSION_CLOSED error-map key - panel: drop channels_read/write from AgentPermissions; remove dead channel: KB source branch - pyproject.toml: refresh a ruff-exemption example off the removed verb kwargs - CHANGELOG: record #306 under [Unreleased] |
||
|
|
16789c1ca7 |
Feature/architectural conventions standard (#243)
* feat(conventions): standard schema models + effective-map merge * feat(conventions): tree-sitter Python classifier + placement checks * feat(conventions): TS classifier, hygiene/custom checks, runner + CLI * feat(conventions): ROBOCO_CONVENTIONS_ENABLED flag + cache table + migration * feat(conventions): repo auto-scan + scaffold draft renderer * feat(conventions): ConventionsService (cache/baseline/ambient/scaffold/restore) * feat(conventions): auto-scaffold on project registration (flag-gated) * feat(conventions): TaskDescription.constraints + auto-baseline attach * feat(conventions): ambient architecture-map injection at spawn * test(conventions): subprocess CLI smoke for the agent-image entrypoint * feat(conventions): block i_am_done on block-level convention violations * feat(conventions): block pr_pass on unresolved convention violations * feat(conventions): surface convention findings into QA evidence * docs(prompts): convention awareness for PO/Intake/Dev/QA/PR-reviewer * feat(conventions): panel Conventions tab + flag toggle + parity * test(conventions): end-to-end block, fix, and waiver through the gate * refactor(conventions): extract pr_pass guards to keep pr_gate under the gate * style(conventions): format the baseline-constraints attach in task.create * test(conventions): type-annotate test helpers for the full mypy gate * build(conventions): ignore types-PyYAML in deptry (mypy-only type stub) * docs(conventions): document the standard in CLAUDE.md + PM prompt awareness * fix(conventions): baseline constraints are non-suppressible (dedup-append) * feat(conventions): scaffold on first workspace clone (threaded workspace) * feat(conventions): multi-project ambient map for PO/Intake (per-product) * feat(conventions): persist findings + violations-feed route (migration 044) * feat(conventions): panel violations feed in the Conventions tab * test(conventions): intake-spawn mock accepts the ambient layer kwarg * fix(docker): ollama-init best-effort pull, gate startup on cached models present A degraded/slow ollama registry made the model manifest re-check fail under set -e, so ollama-init exited 1 and blocked the orchestrator's service_completed_successfully gate — taking the whole stack down even though both models were already cached. Pulls are now best-effort; success is gated on the models being present, so a flaky registry can't down a cached deployment. * refactor(content): drop dead TaskDescription.with_baseline_constraints The structured baseline-merge helper had zero production callers. Project-task baseline constraints are attached by the wired string backstop (TaskService._attach_baseline_constraints), and a real task is free-form prose that cannot form a valid TaskDescription (requires a non-trivial objective + non-empty the_work), so the helper was unreachable from any live path — a leftover from the structured-merge -> string-append design pivot. Removing it leaves a single enforcement path. The constraints field itself stays: it is a member of the well-formed-spec schema (Objective / What This Builds / The Work / Notes / Constraints / Acceptance Criteria), rendered by render_markdown and unit-tested. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com> |
||
|
|
77c94b74da |
docs(prompts): announce the note-section obligations to agents
The obligations added in
|
||
|
|
4c85cc6dfa |
feat(content): anti-soup guard on the flow verbs' free-text
Extends structured-content enforcement from the content tools to every flow verb that carries agent free-text, closing the last hole where a dev/PM could pass word soup: i_am_blocked(reason), i_am_done(notes), submit_up/submit_root/complete(notes), escalate_up/escalate_to_ceo(reason), pass_review(notes), fail_review/pr_fail(issues), pr_pass(notes), i_documented(notes), delegate(title/description). Plans (i_will_plan / i_will_work_on) keep their existing >=150-char approach + sub_task gates and are skipped here so recovery re-entry with thin values still works. Shared helpers on the choreographer: _free_text_soup (bare envelope, list aware) and _soup_or_decision_env (folds the soup check into a verb's existing spec-gate return so no verb gains a return or tips the xenon bound). reject_trivial now also catches all-filler multi-token strings. base.md documents the broadened rule for agents. |
||
|
|
db74f546a3 | feat(content): universal anti-soup guard on every agent free-text field | ||
|
|
df5e579916 |
docs: add the full build-session video, count 22 agents, ground resource usage
Add the 2.5-hour 'Working with RoboCo' build session (a conversation to a shipped feature) as a second hero thumbnail beside the 26-min intro. Update the agent count from 20 to 22 across the README, CLAUDE.md, usage, the base agent prompt, the how-to guide, and the org-structure RAG doc: the standing org gains the PR Reviewer (board-level, read-only), and the on-demand Intake and Secretary are now counted. The org-structure doc gains the PR Reviewer in the hierarchy, count table, board team, and communication matrix. The historical 0.1.0 changelog entry is left as-is. Rewrite the resource-usage section: drop the unmeasured per-agent RAM ceiling (RAM is low and agents run few-at-a-time) and lead with storage — the image set's shared base layer — which is what docker prune reclaims. |
||
|
|
46d89b58fe |
feat: company-in-a-box — goal-aware company layer (0.4.0) (#171)
* feat(goals): company charter singleton — data layer (Business Goals slice 1)
First slice of the company-in-a-box "Business Goals" phase: a single CEO-owned
charter row (north star + objectives + constraints + operating policy) that
will be injected into every agent's context_briefing so all work is goal-aware.
- CompanyGoalsTable: singleton table (all-zeros id), JSON objectives /
constraints / operating_policy, updated_at / updated_by.
- migration 032: create + seed the singleton row (offline-renderable; column
server-defaults fill an INSERT of just the id).
- CompanyGoalsService: get() (empty defaults when unset) + upsert() (singleton,
partial update, caller commits).
- tests: empty defaults, roundtrip, singleton + partial-update preservation.
Next slices (mapped, not yet built): briefing injection (BriefingInputs +
build_context_briefing + EvidenceRepo), API route (GET any / PUT CEO-only),
panel /goals page, and base/Board/PM prompt mentions.
* feat(goals): inject the company charter into every agent briefing (slice 2)
The charter is now goal-aware context for every agent:
- BriefingInputs gains company_goals; build_context_briefing surfaces it.
- EvidenceRepo.company_goals(): single-row lookup returning a COMPACT charter
(north star + objectives + constraints + operating policy; audit columns
dropped, lists capped) or None when unset, so an empty charter never bloats
the per-verb briefing.
- _briefing_for wires it into every context_briefing.
Tests: briefing surfaces company_goals (defaults None); repo returns None for an
absent/empty charter and the compact dict when set.
* feat(goals): company charter API — GET any agent, PUT CEO-only (slice 3)
- routes/company_goals.py: GET returns the charter (any authenticated agent —
it drives every briefing); PUT is CEO-only (403 otherwise), partial update via
model_dump(exclude_unset=True), explicit commit.
- schemas/company_goals.py: response + partial-update models.
- registered at /api/company-goals.
- tests: GET open to any role, CEO update persists + is readable, non-CEO 403.
* feat(goals): make the company charter actionable in agent prompts (slice 5)
Agents already receive company_goals in the briefing (slice 2); now tell them to
act on it:
- base.md: universal "Align with the company charter" section — favour work and
trade-offs that advance the objectives, honour the constraints, flag conflicts;
never a license to leave your role.
- board / main_pm / cell_pm: role-specific lines tying triage / cell-routing /
subtask decomposition to the charter.
Prompts are composed at spawn from base.md + roles/*.md directly (compose_prompt),
so no _generated regeneration is needed.
* feat(goals): company charter panel page (slice 4)
CEO-facing editor for the charter at /company-goals:
- lib/api/company-goals.ts: get / update (PUT) client.
- company-goals-card.tsx: edit north star + constraints (one per line) +
objectives / operating_policy (JSON, parsed + validated with toast errors);
display derives from server state (no set-state-in-effect).
- (dashboard)/company-goals/page.tsx + a "Company Goals" sidebar nav link.
tsc --noEmit + eslint clean. Completes Phase 1 (Business Goals): data, briefing
injection, API, prompts, panel.
* fix(test): make test_app route assertions robust to FastAPI 0.137 _IncludedRouter
FastAPI 0.137 stopped flattening include_router into app.routes — each include is
now an _IncludedRouter (a BaseRoute with no .path), so `{r.path for r in
app.routes}` raised AttributeError and the two router-registration tests failed
(the bump arrived via the claude-agent-sdk update in uv.lock). Add
_registered_paths(): OpenAPI schema paths (the stable public contract) plus each
included router's prefix, which also covers the websocket /ws mount (never in the
schema). Drops the now-incorrect type: ignore[attr-defined].
* feat(research): pluggable web search/fetch for Board + PM agents
Add a provider-agnostic web-research capability so the Board and PMs can
ground decisions in current external evidence the knowledge base can't
answer.
- ResearchService selects a provider adapter from config: Tavily, Brave,
and Exa adapters plus a NullProvider that degrades gracefully when no
key is set. Result count and fetched-content size are clamped to caps.
- /api/research/search and /api/research/fetch: role-gated to Board + PMs
(and the CEO), with a per-agent/day Redis quota that fails open.
- roboco-search MCP server (web_search / web_fetch) calls those routes;
the provider key stays server-side and agent containers never egress.
Mounted per role by the orchestrator, behind a master switch.
- Charter-aware prompt guidance for Board, Main PM, and Cell PM.
Additive: with no key configured it is a no-op and the existing delivery
lifecycle is unchanged.
* feat(pitch): Board pitch -> CEO approve -> auto-provision repos
Add an additive origination path so a product can be proposed, approved,
and stood up without manual repo/Project setup.
- Pitch entity + migration (pitches table); PitchService create/list/
reject/approve.
- GitHubProvisioningService: the one place that creates repos (POST
/orgs/{org}/repos). Server-side token/org; when unconfigured the whole
approve path is inert and nothing is created.
- On approval: provision one repo per target cell, register a Project per
repo, create a Product when multi-cell, and seed one Main-PM delivery
task — all reusing the existing Product / coordination-task machinery.
- /api/pitches: Board authors (PO/HoM), CEO approves/rejects, Board+PM+CEO
view. Errors mapped via a single translator.
Additive: the delivery lifecycle is untouched; with no provisioning token
the capability is a no-op. Agent-facing pitch tool + panel are follow-ups.
* feat(strategy): dormant autonomous strategy engine (engine 2)
Add a second, optional engine that watches the company against its
standing goals and surfaces what needs the CEO — without touching the
delivery lifecycle (engine 1).
- StrategyEngine.assess() reports observations: the company is idle while
goals stand, and tasks stranded in 'blocked' past a threshold.
- run_cycle() notifies the CEO (notify-only; it never spends, builds, or
auto-approves — originating work stays a CEO decision).
- Orchestrator runs it on its own interval, started/stopped with the other
background loops; the loop returns immediately unless enabled.
DORMANT by default (strategy_engine_enabled=False): the loop never runs and
a standard deployment is unchanged. Auto-origination is a further opt-in.
* docs(changelog): record Business Goals, Web Research, Pitch->Provision, and the dormant strategy engine under Unreleased
* feat(secretary): wire the Secretary role end-to-end (foundation)
Add SECRETARY as a distinct role — the CEO's conversational chief-of-staff,
governed separately from the Prompter (which stays read-only/human-only).
This is the role foundation only; authority, the live agent, and the panel
land in following commits.
- foundation/identity: Role.SECRETARY (board level), seeded secretary-1 agent,
role-level mapping.
- journaling read tier (ALL — it advises the CEO), role_config entry,
per-role model (opus), prompt-layer mapping + roles/secretary.md.
- i_am_idle gains SECRETARY so the role has a verb surface.
- migration 034: add 'secretary' to the agentrole enum (mirrors 025).
- Role-registry tests updated for the new role.
Inert by itself (nothing spawns it yet); additive — existing roles unchanged.
* feat(secretary): directives + gate-list authority (backend)
The Secretary acts only under CEO command. Low-risk directives (relay a
dictated message) execute immediately; high-impact ones — charter edits,
task start/cancel/override, pitch approval, announcements — are recorded
pending and run only after the CEO confirms (the gate list).
- secretary_directives table (migration 035) as the command audit + queue.
- SecretaryService: read company state; submit (direct->run, gated->queue +
notify CEO); confirm/reject; execution runs with the CEO as actor through
the existing services (the Secretary never holds CEO authority itself).
- /api/secretary: submit + state/task reads (Secretary or CEO); list/confirm/
reject (CEO only). Writes commit explicitly.
* feat(secretary): live conversational agent (container + bridge)
Stand up the Secretary as a persistent Claude-SDK container the CEO chats
with, mirroring the Intake agent and reusing its driver/session machinery.
- secretary_driver: build_secretary_options exposes read_company_state /
read_task / submit_directive as SDK tools that call /api/secretary/* with
the agent's HMAC token; backend-call logic is module-level + tested.
- secretary_main: container entrypoint (receiver + relay) reusing IntakeDriver.
- orchestrator: start/spawn/reap secretary session + run-cmd builder; no
workspace clone (reads state via API), mints a role=secretary token.
- secretary_live routes: panel <-> container bridge over the live registry.
- agent-secretary image (Dockerfile + compose build service).
Inert until a session is started; additive — intake and all agents unchanged.
* feat(secretary): panel chat + directive confirmation queue
The CEO's Secretary surface: a live chat (SSE) to talk to the Secretary, and
a 'Needs your confirmation' queue listing gated directives the Secretary
proposed — each with Confirm / Reject. Adds the sidebar nav entry.
- lib/api/secretary.ts: live (start/stream/status/send/stop) + directive
(list/confirm/reject) + state clients (all as the CEO).
- hooks/use-secretary.ts: drives one chat, accumulating SSE token deltas.
- secretary page: chat pane + pending-directive cards.
Completes the Secretary end-to-end (role + authority + live agent + panel).
* feat(pitch): agent-facing pitch tool + pitches panel
Complete the pitch path: the Board can now author pitches through the gateway,
and the CEO reviews/approves them in the panel.
- content_actions.pitch (Board-only) -> PitchService.create, returning an
Envelope; wired as a do-tool (do_server + /api/v1/do/pitch + schema) and
added to the Board's do-tools.
- Panel /pitches page: lists pitches with CEO Approve & provision / Reject;
sidebar nav entry.
Pitch (Phase 4) is now end-to-end: author -> CEO approve -> auto-provision.
* feat(cockpit): read-only 'is the business winning?' summary
A pure aggregation for the CEO over existing data — no new state, no writes.
- CockpitService.summary(): charter north-star/objectives, delivery counts
(in-flight/blocked/awaiting-CEO), 30-day spend vs the charter's budget cap,
pending pitches, and the strategy engine's signals (what needs you). Stamped
basis='proxy' — performance is a proxy until real launches.
- GET /api/cockpit/summary (CEO / Board / Main PM / Secretary).
- Panel /cockpit page + sidebar nav.
Reuses goals + usage + StrategyEngine.assess(); reads only.
* docs(changelog): add the Secretary and Cockpit to Unreleased
* fix(test): isolate the company-goals empty-defaults test from committed state
The shared test DB persists committed writes across tests; a route test
commits a charter, so the unit test's 'unset' assertion must establish its
own clean precondition rather than assume global emptiness.
* fix(gateway): lower evidence_repo complexity to rank A (xenon gate)
company_goals()'s 4-way `or` emptiness check tipped the module average to
rank B; `any(...)` is equivalent and keeps the module under the gate's A bar.
* chore(compose): mirror agent-secretary-image build into docker-compose.yaml
Both compose files are byte-identical and tracked; .yaml carries the same
agent-secretary-image build service already present in docker-compose.yml.
* chore(lifecycle): regenerate artifacts for secretary i_am_idle
The secretary role gained i_am_idle in the lifecycle spec; regenerate the
generated prompt/doc/json artifacts so foundation-check stays green.
* docs(changelog): cut the company-in-a-box phases to 0.4.0
Label the six additive phases (business goals, web research, pitch-provision,
strategy engine, secretary, cockpit) as 0.4.0; tag v0.4.0 is held until the
branch merges to master so it points at the release commit.
---------
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
|
||
|
|
b9082a5c70 |
Fix: agent idle deadlock and lifecycle hardening (#96)
* fix(panel): cap dialog height and pin footer so actions stay reachable
Shared DialogContent now caps at max-h-[85vh] with overflow-y-auto, and the
footer is sticky to the bottom. Long content (e.g. a pasted change-request
note) no longer pushes the submit/cancel buttons past the viewport — the body
scrolls while the actions stay visible. No-op on dialogs that already fit.
* feat(notifications): suppress duplicate same-purpose notifications at send
A notification is not created when an unacknowledged one with the same purpose
— same sender, same type, same task, overlapping recipients — already exists.
Body text is not compared, so rewording cannot defeat it; a different type,
task, sender, or an already-acked recipient all still send through. Stops
agents that loop re-issuing the same signal from piling up unread that
soft-blocks the recipient's idle path.
* fix(gateway): stop board/PM lifecycle verbs from 500-crashing
Two unguarded crashes that wedged the org in respawn/escalate loops:
- escalate_to_ceo dereferenced None.status when the verb runner declined the
escalation (task not in awaiting_pm_review — e.g. a board agent escalating a
blocked task). It now returns a clean invalid_state. The message/remediate
build moved to a helper so the function stays within the complexity gate.
- The coordination-root git ops (pr_target, pr_merge, PR update, branch-token
resolve) called UUID(str(task.project_id)) directly, which raised on a
coordination/integration task (project_id is None — 'badly formed hexadecimal
UUID string'). They now resolve through _project_for_task, which falls back to
the product's repo for project-less roots.
* refactor(intake): split out _block_to_chunk per-block classifier
Extract the per-block classification from _blocks_to_chunks so each function
stays within the xenon cyclomatic-complexity gate (was rank C). Behaviour is
unchanged — verified by the existing intake_driver tests.
* feat(gateway): make the i_am_idle unread soft-block satisfiable
The soft-block on unread A2A / @mentions had no clearing path, so once those
briefing fields populated an agent could never idle — a whole-org deadlock.
Keep the guard (it is correct) and add the missing clear paths:
- New read_messages content verb (schema -> route -> handler ->
a2a.mark_all_read -> MCP tool -> role do_tools): bulk-zeroes the caller's
unread A2A and stamps read_at. The idle hint now points to it.
- list_unread_mentions returns UNACKED MENTION-type notifications (each @mention
already raises one via messaging._notify_mentions) instead of raw,
unconditional mentions, so they clear via the existing notify_ack. No schema
migration needed.
The soft-block is now satisfiable: A2A via read_messages, mentions and
notifications via notify_ack.
* fix(tests): repair notification-dedup db.scalar mocks + prompter agent seeding
The notification send-dedup added a db.scalar() purpose-lookup to
_create_notification; the two hand-rolled _FakeDb test stubs (test_notification,
test_a2a_priority_tristate) had no scalar() method → AttributeError. Add
scalar() returning None (no duplicate) so creation proceeds.
Separately, the prompter '& Start' route tests assign the draft to a fixed
product-owner / main-pm AGENT_UUID but only seeded system + CEO, so the
assigned_to FK failed in isolation (and main-pm flaked in the full suite). Seed
both via idempotent merge() in _seed_project_and_ceo.
* fix(git): gitignore .pnpm-store + flag GH001 push rejection as permanent
A dev once committed the ~115 MB pnpm store → GitHub GH001 (>100 MB) pre-receive
reject → open_pr retry-loop. Two root fixes:
- Add .pnpm-store/ to .gitignore — an ignored dir can't be staged by any git add.
- push() restates a GH001 / file-size rejection as an unmistakable PERMANENT
error pointing at i_am_blocked, so the agent stops blind-retrying a push that
can never succeed (it otherwise mis-reads the raw output as a transient timeout).
The per-verb retry cap (open_pr: 5) already bounded the burn; this ends it.
* fix(gateway): accept a PM decision note as satisfying the complete/submit_up reflect gate
A cell/main PM that wrote a fresh decision but no separate reflect note bounced
on the reflect tracing-gate indefinitely (re-confirmed live: cell PMs looped on
cell_pm_complete -> journal:reflect until reaped, burning tokens — worse because
each respawn resets the per-verb retry cap). For a PM closing/submitting a task
the decision note already documents the close; the separate reflect is the
redundant artifact weak-model PMs forget. Accept a fresh decision as satisfying
reflect for complete + submit_up — the gate still requires a decision +
substantive notes, so the close stays documented.
NOTE (enforcement tradeoff, flagged for CEO review): this intentionally relaxes
the PM complete/submit_up gate. It does NOT touch the developer i_am_done gate.
* feat(gateway): refuse i_am_idle when a PM still owns a task awaiting its review
A cell/main PM once tried to 'send work back' by DMing the developer and going
idle — but a DM changes no task state, so the task stayed awaiting_pm_review and
the orchestrator just re-dispatched the PM in a loop. i_am_idle now refuses (like
the pending-assignment guard) when a PM owns an awaiting_pm_review task, with a
clear remediation: complete() to finish, or reassign()/delegate() to route it
back. PM-only; devs/QA/doc unaffected. Pairs with the reflect-gate relaxation so
the PM can actually complete instead of looping.
* feat(gateway): push a prior-work handoff digest into task-scoped briefings
A freshly spawned or respawned agent previously started cold on every
lifecycle hand-off: the prior worker's PR, commits, acceptance status and
journal highlights lived in task evidence but were pull-on-demand, so each
new role agent re-explored the codebase from scratch — wasted tokens and
fragile context loss across respawns.
build_task_handoff() composes a compact, DB-only digest (no git diff) and
_briefing_for() now attaches it to context_briefing whenever the caller
already holds the task row. The digest is built only from a passed-in task,
so there are zero extra fetches: every resumption entry point (give_me_work
and pm_give_me_work, i_will_work_on, i_will_plan, triage/triage_all,
i_am_done, submit_up, escalate_up, complete) threads the loaded task, while
id-only correction/rejection paths cleanly omit it.
Every field is type-guarded so a partial row never leaks a non-serialisable
value into the envelope.
* docs(prompts): tell agents to resume from the briefing handoff before re-exploring
The base prompt described the success envelope but never told agents to act
on context_briefing, so a respawned or hand-off agent would re-scan the
whole repo and re-derive the plan even when the briefing already carried the
prior worker's PR, commits, acceptance status and journal highlights.
Adds a 'Resume from your briefing' section that walks each task_handoff
field and instructs the agent to continue from it — and to read the unread
A2A / mention / notification lists, which are messages addressed to them.
Pairs with the gateway change that now pushes task_handoff into every
task-scoped briefing.
* feat(tasks): remember cleared dependencies so the unblock briefing can surface them
When an upstream dependency completed, _unblock_dependents removed its id from
the dependent's dependency_ids to let it be claimed — destroying the only
record of which upstream task had just landed. The revived dependent then
re-discovered that work from cold.
Adds tasks.completed_dependency_ids (Alembic 026, uuid[] default '{}'):
_unblock_dependents now appends the cleared id there instead of only dropping
it, and the briefing handoff digest surfaces it so the agent picking the task
back up knows its blocker cleared because that upstream work shipped. The base
prompt documents the field.
Migration round-trip verified against postgres (upgrade adds the column,
downgrade drops it).
* docs(prompts): instruct PMs to split oversized tasks into per-concern subtasks
A subtask carrying a long acceptance list or spanning multiple layers/files
drove repeated QA failures and a PM revision loop — QA can't pass a partial,
and the dev keeps re-touching unrelated parts. Nothing in the PM prompts told
them to decompose by size/concern.
cell_pm gets a 'Sizing' rule: one subtask = one focused concern with ~2-4
criteria and its own dev->QA pass; decompose anything larger before
delegating, sequencing with dependencies. main_pm gets a matching reminder to
scope each cell's slice to that cell's layer rather than handing a cell a
cross-layer monolith that just pushes the problem down a level.
* fix(gateway): mirror the task= kwarg on ChoreographerHelpers helper signatures
The handoff-digest change added a keyword-only task= parameter to
_briefing_for and _build_tracing_gap in _impl, but the ChoreographerHelpers
base that the role mixins inherit still declared the old signatures, so the
composed Choreographer had two incompatible base definitions (mypy [misc]).
Sync the base declarations to match.
* fix(tasks): keep the owner on a substitute-out so the task isn't orphaned
build_substitute_update unconditionally nulled assigned_to, so any
substitute that routes to PENDING (max_retries, low_context, out_of_scope_*)
— the path a verb hitting repeated 500s or its retry limit takes — left the
task pending AND unassigned. The dispatcher only respawns a pending task when
it has an owner, so the task went dormant: no agent ever picked it back up.
Keep the task with its current owner instead. A substitute-out is almost
always a transient stall, so the task re-dispatches to the SAME agent, which
resumes from the briefing handoff. Only the task_complete -> PM-review handoff
changes owner (unchanged).
* feat(a2a): suppress duplicate unread A2A messages at send
A respawned or retrying agent could re-emit the same DM, stacking identical
copies on the recipient's inbox and re-bumping the unread count — noise that
the recipient then has to clear. The notification path already dedups; A2A did
not.
send_chat_message now suppresses a send when an identical message from the
same sender is still unread in the conversation, keyed on (conversation,
sender, message_kind, content). Genuinely different messages are never
collapsed (verified: distinct content still produces distinct rows), so this
avoids the earlier per-pair over-suppression. No migration.
* fix(panel): default the notifications view to Unread, not All
Landing on the All tab buried new notifications under everything already
seen — the most-reported annoyance. The Unread tab is the actionable view, so
make it the default; the All/Pending tabs are one click away.
* fix(panel): show clone progress during intake prep instead of a frozen pill
The first clone of a repo can take a few minutes, during which the intake
form showed only a static 'Preparing the agent…' button — indistinguishable
from a hang. Add a progress region while preparing: an elapsed timer, a
saturating progress bar (approaches but never reaches 100% until the agent
actually answers), and staged copy (spinning up → cloning → first-clone-takes-
a-while → reading the codebase) so the wait reads as work, not a freeze.
* feat(docs): index workspace-authored docs that never reached the RAG store
Docs written through roboco_docs_write land at /app/docs on the orchestrator
and index fine. But a documenter can also write docs with Edit/Write directly
in its own clone (README, CHANGELOG, workspace markdown); those resolve to a
/app/docs path that doesn't exist on the orchestrator, so the indexer reads
nothing and the docs never become searchable — a cross-container miss with no
shared mount to bridge it.
On docs completion, capture each listed doc's committed content out of the
branch (new GitService.read_file_at_branch, via git show) and write it
server-side under /app/docs before indexing, so workspace-authored docs reach
RAG too. Docs already present server-side are skipped; absolute paths and
unreadable/uncommitted files are passed over best-effort.
* feat(prompter): survive a browser reload by reconnecting to the live intake chat
The intake chat lived entirely in React state, so a page reload wiped it and
dropped the human back to the scope form — even though the agent container
outlives the page. Now the chat persists a small TTL'd slice (session id,
messages, scope, draft) to localStorage and, on mount, reconnects: it asks the
new GET /live/{id}/status whether the session is still running and, if so,
restores the history and reopens the SSE stream; if dead or expired it clears
and shows the form. A full reload doesn't run React effect cleanup, so the
navigate-away reap never fires on refresh and the session stays up.
Backend adds the status endpoint + PrompterLiveRegistry.is_alive; localStorage
is cleared on confirm, start-another, and SPA navigate-away.
* chore: remove internal session-bookkeeping refs from code comments (part 1)
Strip leaked task/finding numbers, Wave/Phase/cluster/audit labels from
docstrings and comments across services, foundation policy, runtime, mcp,
api schemas, and agent_sdk — they mean nothing to a repo reader and expose
process internals. Wording preserved; only the labels dropped. Done by hand,
one comment at a time (no scripted rewrite). _impl.py follows separately.
* chore: remove internal session-bookkeeping refs from code comments (part 2)
Finishes the manual scrub: the choreographer _impl.py docstrings/comments plus
the remaining dogfood-run ('smoke-N') labels across runtime, mcp, foundation,
api schemas, services, and agent factories. Reworded to describe the bug or
behaviour in plain words; every label dropped. The repo source is now free of
task/finding numbers, Wave/Phase/cluster/audit/smoke labels. By hand, one
comment at a time.
---------
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
|
||
|
|
9f8834155a |
Feature: prompter gold upgrade (#84)
* feat(prompter): make the assistant a RoboCo insider and fully wire launch
The Prompter's intelligence lived in two thin static prompts, so it asked
generic checklist questions and produced a flat task. The launch path was
also only half-wired: the panel called the generic task-create endpoint with
no project, bypassing the Prompter's own confirm flow.
Interview brain
- Rewrite the chat system prompt with RoboCo's org model, the task-spec
standard, a dimensions playbook, and a reflect-back, 1-2-questions-per-turn,
auto-stop discipline.
- Inject the live projects/products list each turn so the assistant grounds
questions in real surfaces and resolves the target itself.
- Replace the brittle phrase-match readiness with a parsed roboco-meta control
block (parse_readiness); the block is stripped from the visible reply and the
turn now returns draft_ready + scale.
Structured GOLD draft
- Add first-class draft fields (objective, what_this_builds, the_work, notes)
carried in the existing draft_data JSONB — no migration.
- Compose the GOLD markdown description deterministically from those fields
(compose_description); the model never hand-formats the body.
Adaptive routing + wired launch
- Confirm now runs through the Prompter confirm endpoint with the human's
project/product choice and edited structured draft.
- Single-cell targets a project and the cell team; a multi-cell feature targets
a product and becomes a Main-PM coordination root that fans out.
Frontend
- Turn-envelope draft_ready (drop the duplicated phrase-match), structured
draft card, confirm dialog with a project/product picker and a per-cell
The Work editor, and the corrected priority labels (0 highest .. 3 lowest).
* fix(prompter): commit session writes so they survive across requests
Session create returned 201 but the row was never durably committed, so the
immediately-following /messages call could not find it and 404'd. The prompter
routes were the only write surface that never called db.commit() — every other
write route (tasks, a2a, groups, docs, product) commits explicitly rather than
rely on the request-teardown auto-commit, which is sensitive to middleware and
teardown ordering under the production server.
- Commit explicitly in all four prompter write routes (create session, send
message, get/generate draft, confirm).
- Fix _get_session's NotFoundError: it passed a full sentence as resource_type,
producing the doubled "... not found not found" message; now uses the
(resource_type, resource_id) signature.
- Panel: when a message hits a session the server no longer has, start a fresh
session and retry once instead of dead-ending on a stale id.
Add a regression test that gives each request its own non-committing session —
the real cross-request boundary the shared-session integration tests never
crossed. It reproduces the production 404 without the route commit and passes
with it.
* refactor(prompter): drop the "GOLD" jargon for plain wording
"GOLD" was informal shorthand for "a good/well-formed spec" that should never
have been baked into the LLM prompts, comments, and docstrings as if it were a
defined term. Replace it everywhere with plain language ("a well-formed task",
"a complete task spec", "the markdown description", "structured spec fields").
No behaviour change.
* feat(intake): add the intake interviewer agent role (static definition)
Phase 1 of the intake-agent feature: a new first-class `prompter` role — the
intake interviewer the CEO chats with to draft a task. This commit defines the
role across every foundation layer (no runtime yet); spawning + the live
session come next.
- identity: Role.PROMPTER, RoleLevel.INTAKE (lowest authority), an AGENTS row
(intake-1) on the board team, ROLE_LEVEL entry. Deliberately NOT in
BOARD_ROLES — it interviews, it does not review.
- lifecycle: gets i_am_idle like every agent (its only verb); no
delivery-lifecycle intents.
- journaling: ReadTier.OWN — isolated, reads only its own journal.
- role_config: human-only manifest — note + evidence only, no say/dm/notify/
channels; allows_subagent=True (research), allows_write=False.
- agents_config derives it automatically and correctly excludes it from
TASK_CREATOR_ROLES (it drafts, it never creates tasks).
- seed presentation ("Intake"); regenerated lifecycle artifacts.
- role system prompt: read the code first, single-CEO awareness, propose
rather than interrogate — written against the failures we saw.
- docs: roster count 19 -> 20, org charts, verb-surface table, usage roster.
All foundation drift checks pass; role/manifest/permission tests green.
* feat(intake): migrate agentrole enum to add 'prompter'
ALTER TYPE agentrole ADD VALUE IF NOT EXISTS 'prompter' so the intake agent
row seeds/spawns against a migrated production DB. Forward-only (postgres
can't drop enum values), guarded for offline mode — matches migration 012.
* style(intake): ruff format the role additions
* fix(intake): unguard the agentrole migration so it renders offline
The enum-migration-parity test renders 'alembic upgrade head --sql' (offline)
and greps for ALTER TYPE ... ADD VALUE. The is_offline_mode() guard skipped
emitting it, so the parity check couldn't see 'prompter'. Drop the guard —
PG16 permits ADD VALUE in a transaction, same as migration 020's backfill.
* feat(intake): the live-session driver (Claude Agent SDK loop)
Phase 2 begins. The intake agent isn't a one-shot `claude -p`; it's a live
Claude Code session the human chats with. This driver is the container's loop:
open one long-lived claude-agent-sdk ClaudeSDKClient, then per human message
run a turn (query + receive_response) and stream its events out, keeping
conversation context in-process — verified against the real SDK (v0.2.94).
- StreamChunk + normalize(): map SDK messages (StreamEvent text deltas,
AssistantMessage text/thinking/tool_use blocks, ResultMessage→session_id) to
panel-facing chunks. Duck-typed, so it works on real SDK objects and on test
fakes alike — SDK-free, fully unit-tested.
- IntakeDriver.run(): the loop, with injected session/source/sink seams; a turn
failure surfaces as an error chunk without killing the session.
- SdkIntakeSession + build_intake_options: the only SDK-coupled code (lazy
import; needs the live claude binary, so excluded from coverage).
- Add claude-agent-sdk dependency + mypy ignore-missing-stubs.
Relay, panel SSE, and the persistent on-demand spawn are the next steps.
* Updated uv.lock
* feat(intake): the panel<->agent live bridge (registry, routes, entrypoint, image)
Wires the live intake chat end to end (Phase 2 integration layer):
- prompter_live.py: the orchestrator-side per-session registry — open/close,
push (agent->panel), stream (SSE drain), deliver (panel->container). In-process
(the orchestrator is single-process). 7 unit tests.
- routes/prompter_live.py: GET /live/{id}/stream (SSE), POST /live/{id}/messages
(deliver), POST /live/{id}/events (relay in); registered under /api/prompter.
5 integration tests.
- agent_sdk/intake_main.py: the container entrypoint — a POST /turn receiver
(the driver's MessageSource) + a relay-poster EventSink + the ClaudeSDKClient
session, run concurrently. 4 unit tests on the wiring helpers.
- docker/agent-prompter.Dockerfile: FROM base, ENTRYPOINT = the driver (not the
one-shot `claude` the other agents use).
Remaining for Phase 2: the orchestrator persistent-spawn path (scope->workspace
clone, CMD = driver, registry.open on spawn, reap-on-confirm) — the deploy-side
piece, best finalized against a buildable image.
* feat(intake): orchestrator persistent spawn + start/stop for the live chat
Add the task-free spawn path for the intake (prompter) agent: one fixed
intake-1 container running the Agent-SDK driver (image ENTRYPOINT, not
claude -p), one live session at a time.
- spawn_intake_session clones the scope's repo(s) via WorkspaceService
(project -> one; product -> each distinct project, primary first),
composes the intake-1 prompt, resolves the model, and builds docker run
via _build_intake_run_cmd: no settings/hook mount (driver owns 9000),
no MCP config, no -w; registers the live relay and best-effort delivers
the opening message once the receiver is up.
- reap_intake_session closes the relay and stops the container.
- Routes: POST /live/start (project XOR product) and POST /live/{id}/stop.
- ROLE_MODEL_MAP[prompter]=opus; intake-1 -> roboco-agent-prompter image map.
- Replace the budget-sweep try/except/continue with _fetch_budget_status,
which logs the swallow at debug instead of silently dropping it.
25 new tests; docker + the clone are mocked. End-to-end container spawn is
pending a built image and the stack.
* feat(intake): wire /prompter to the live agent — scope form + SSE chat
Replace the Ollama chat loop on /prompter with the spawned-agent flow.
- IntakeForm: pick scope (project XOR product) + opening message + Start
before the chat; the agent clones that scope and reads the real code.
- use-prompter rewritten as the live brain (lib/api/prompter-live.ts): Start
spawns via POST /live/start, then an EventSource on /live/{id}/stream
streams the agent working — token deltas fill the assistant bubble,
tool_use/thinking drive a live activity line, a draft event renders the
existing DraftProposalCard. Messages go via POST /live/{id}/messages.
- Chat UX unchanged (Keep Chatting / Review & Confirm / ConfirmDialog reused);
reap-on-confirm and reap-on-leave call POST /live/{id}/stop.
- Drop the dead Ollama prompterApi client; trim prompter.ts to shared types.
Frontend gate green (tsc --noEmit, lint, build). The draft event + the
/live/{id}/confirm endpoint are the Phase 4 backend seam.
* feat(intake): confirm draft -> backlog task + agent draft emission
Complete the live intake vertical: the agent proposes a structured draft and
Review & Confirm turns it into a task.
- Draft emission: the prompter prompt instructs the agent to emit a fenced
roboco-draft JSON block when the spec is ready; the driver parses it into a
'draft' event over the existing relay -> the panel's DraftProposalCard. The
panel strips the raw block from the chat bubble.
- Fix a double-text bug: with include_partial_messages the reply arrives as
both StreamEvent deltas and the final AssistantMessage; the driver now takes
text from deltas only and the AssistantMessage for thinking/tool_use/draft.
- POST /live/{id}/confirm -> confirm_live_draft, reusing a draft->task core
extracted from confirm_draft; reaps the session on success.
- Both prompter confirm paths create at BACKLOG, not pending: backlog is the
holding area a draft waits in until it's reviewed and promoted to pending
(TaskService.activate). The legacy Ollama confirm was creating at pending,
skipping that gate — fixed.
- Remove the dead 'context' bootstrap param from the Ollama session-create
chain (schema + route + method + tests), superseded by the live scope form.
- No suppressions: replace every type:ignore/noqa across the intake surface
with a real fix (ORM .id -> UUID(str(x)); fakes -> monkeypatch.setattr;
lazy imports -> pyproject per-file ignore; union-attr -> recipients[0]).
Full make quality green; frontend tsc + lint green.
* build(intake): add the agent-prompter image builder to compose
The orchestrator references roboco-agent-prompter (AGENT_IMAGES + the
_ensure_agent_image dockerfile map) and docker/agent-prompter.Dockerfile
exists, but docker-compose.yml built every other agent image up front and
left this one out — so the image wasn't pre-built for a stack bring-up.
Mirror the other specialized agent-*-image builders: build from
docker/agent-prompter.Dockerfile, tag roboco-agent-prompter, depend on
agent-base-image.
* Created docker-compose.yaml for the NAS
* fix(intake): non-blocking /live/start so spawn never times out
The start POST awaited the whole spawn — workspace clone + first-time image
build + docker run — which blew past the panel's 60s HTTP timeout ('Request
timed out. The server may be busy.') and triggered a duplicate send. Found on
the 2026-06-09 NAS smoke.
- start_intake_session opens the live relay synchronously, then spawns the
container in the background (_spawn_intake_container_guarded). The route
returns the session id immediately; the panel opens the SSE stream right away.
- A background spawn failure is pushed onto the relay as an 'error' event and
closes the session, so the panel shows it instead of hanging.
- spawn_intake_session stays as the synchronous variant for direct callers/tests.
- Panel shows a 'Preparing the agent…' indicator until the first event arrives.
18 intake-spawn tests green; tsc + lint green. E2E re-validates on next smoke.
* fix(intake): propose_draft MCP tool + lock the agent down
Smoke 2026-06-09 exposed two compounding problems: the agent never reliably
emitted the draft (it narrated the spec instead of typing the magic fence), and
it had inherited the CEO's entire Claude Code env — Write/Edit/Bash + Gmail/
Notion/Calendar/Drive MCP — because bypassPermissions ignored the allowlist and
the mounted ~/.claude leaked the host MCP config.
- propose_draft: build_intake_options now registers an in-process SDK MCP tool
(create_sdk_mcp_server + @tool). The agent calls it to submit the draft; the
driver turns that ToolUseBlock into a 'draft' event (_is_propose_draft /
_draft_from_tool_input, tolerant of nested/flat/JSON-string input). The fenced
roboco-draft block stays as a fallback.
- Lockdown: strict_mcp_config=True + setting_sources=[] (ignore host MCP +
settings); permission_mode 'dontAsk' + a can_use_tool gate enforcing a hard
allowlist (Read/Grep/Glob/Task + propose_draft) replaces bypassPermissions.
- Prompt: call propose_draft (not a fence); the draft's downstream chain is
backlog -> Board (PO + HoM) -> CEO approve -> Main PM, and the agent's job ends
at the draft (it never routes or hands off).
SDK API verified against the installed claude-agent-sdk. Driver detection unit-
tested; the SDK-construction is validated on the next NAS smoke (incl. that
setting_sources=[] doesn't break the mounted-~/.claude auth).
* fix(intake): panel UX cluster from the smoke (#3/#4/#6/#12)
- #3 message boundaries: a tool call now ends the current text bubble, so the
agent's words before and after a tool render as separate messages instead of
one merged wall (the 'two waves merged into one bubble' the CEO saw).
- #4 activity indicator: promoted from tiny grey text to a prominent primary-
tinted pill so 'watch it work' is actually visible.
- #12 End chat: a header button (any chat state) reaps the agent and resets to
the form, reusing startAnother (which already stops the session). Backend
POST /live/{id}/stop already existed.
- #6 log noise: the opening-message delivery retry logs at debug, not error —
those failures are expected until the container receiver is up.
- Also fix a latent test gap from the #1 commit: the live-route test's fake
orchestrator now exposes start_intake_session (the route's non-blocking entry).
Frontend tsc + lint green; live-route + prompter_live tests green.
* fix(intake): render markdown in the chat bubbles (#8)
The agent emits rich markdown (### headers, **bold**, tables, lists) but the
bubble rendered raw text, so it was illegible (CEO-flagged on the smoke). Render
assistant content with react-markdown + remark-gfm (GFM tables) in a prose
container. Adds react-markdown + remark-gfm to the panel.
* feat(intake): #14 — two start routes (Board review vs straight to Main PM)
Per the CEO spec, the draft confirm now starts the task at PENDING with an
explicit assignment instead of parking it at backlog:
- route="board" (Board review & Start): assigned to the Product Owner, so the
orchestrator dispatches the full Board review (PO + Head of Marketing) before
the Main PM picks it up.
- route="main_pm" (Approve & Start): assigned straight to the Main PM, who
delegates to the cells (Board review skipped).
create_task_from_draft gains status + assigned_to params (default BACKLOG, so the
legacy confirm_draft is unchanged); confirm_live_draft + the /live/{id}/confirm
request carry the route. Service tests cover both routes.
* feat(intake): #14 draft-card buttons — Board review vs Approve & Start
Three buttons on the draft card now (CEO spec): Keep chatting / Board review &
Start / Approve & Start. The two action buttons confirm directly with their
route — launchTask(route) sends route to POST /live/{id}/confirm, which starts
the task at pending assigned to the Board (PO+HoM) or straight to the Main PM.
Supersedes the ConfirmDialog review step (scope is chosen up front in the form),
so it's removed from the page flow. The ConfirmDialog component + its sub-editors
are now unused — flagged for a follow-up cleanup, left in place to avoid churn.
tsc + lint green.
* fix(intake): keep the live SSE stream bound to its relay session
The orchestrator opened the relay session twice per live chat — once on the
request path (before the start call returns) and again inside the background
container spawn. The SSE stream binds to the session's queue the moment the
panel connects, so the second open swapped in a fresh queue and stranded the
stream: the agent replied normally, but its events went to the new queue while
the panel kept reading the old one, so the chat looked frozen on "Preparing…".
The second open was always redundant (the relay is opened by the caller before
the spawn). Remove it, and make open() idempotent so a live session is never
replaced out from under a stream that is already connected to it.
* fix(intake): draft-card launch buttons silently did nothing
The launch path required a `description` field, but the prompter draft schema
intentionally has none — it sends `objective` + the structured spec and the
backend composes the description (compose_description). `editableDraft.description`
was therefore undefined, so `description.trim()` inside launch validation threw a
TypeError that propagated out of the button's onClick. Clicking "Board review &
Start" / "Approve & Start" did nothing, with no feedback — the wall blocking the
whole confirm → task → reap flow.
- Map a proposed draft's description from `objective` as a fallback.
- Make launch validation null-safe.
- Replace the silent early-return with a toast that names what's missing, so a
blocked launch is never a dead, feedback-less button again.
* fix(intake): steer the agent to ask inline, not via AskUserQuestion
The intake's job is to ask clarifying questions, so it reached for the
AskUserQuestion tool — which isn't wired to the live chat panel and isn't in its
allowlist. The bare deny left it to stumble ("let me clarify… — no worries, let
me just lay it out") and waste a visible turn.
- Prompt: spell out that it asks by writing in the chat (the human reads every
message live) and that no question/prompt tool is available to it.
- Gate: give AskUserQuestion a specific deny message that nudges it to ask inline,
so even a reflex attempt degrades gracefully.
Also refresh the now-stale "what happens after propose_draft" section: the draft
card has three choices (Keep chatting / Board review & Start / Approve & Start)
and produces a pending task — not the old two-button "backlog" description.
* feat(intake): copy buttons on agent messages and the draft card
The CEO asked for a way to save the agent's plan/spec elsewhere "just in case" —
a cheap manual backstop until refresh-durability lands.
- New CopyButton: async Clipboard API when available, plus a legacy
textarea+execCommand fallback. The fallback is load-bearing — the panel is
served over plain http on a LAN IP, where navigator.clipboard is absent
(clipboard needs a secure context), so the modern API alone would never copy.
- Copy button under each assistant message (copies its text).
- Copy button on the draft card (copies the full spec as markdown: title,
objective, what-this-builds, the-work per cell, notes, success criteria).
* feat(intake): unbuffer logs + log each turn so the container isn't a black box
Debugging the intake smoke was painful for two reasons: (a) the orchestrator
block-buffered stdout, so `docker logs` lagged minutes behind reality, and (b)
the intake container logged only "session opened" then went silent for the whole
conversation (the chat streams to the relay, not stdout).
- Set PYTHONUNBUFFERED=1 on the orchestrator and agent-base images so structured
logs reach `docker logs` in real time instead of in large delayed chunks.
- Log each intake turn: "turn received" (with char count) and "turn streamed"
(chunk count + whether a draft was emitted), so the container logs show the
conversation's shape at a glance.
* chore(intake): remove the dead ConfirmDialog draft editor
The three-button draft card (Keep chatting / Board review & Start / Approve &
Start) replaced the old review-modal confirm flow, leaving ConfirmDialog and its
sub-editors (StringListEditor, TheWorkEditor) referenced by nothing but the
barrel export. Remove the three files and the export — typecheck + lint confirm
no remaining references.
* fix(intake): coerce bad draft enums on confirm instead of hard-failing
The intake agent is an LLM and will emit off-enum values — e.g. task_type="feature",
which is not a valid TaskType (code/documentation/research/planning/design/
administrative). `_coerce_draft_enums` called `TaskType(value)` directly, which
raised, and the confirm 400'd with "Draft has invalid or missing required fields:
'feature' is not a valid TaskType". That forced the agent to discover the valid
values and self-correct in-chat — unacceptable: clicking "Approve & Start" must
never blow up on a cosmetic enum guess.
Coerce each enum to a sane default on invalid/missing (task_type→code,
nature→technical, complexity→medium); team falls back to the first valid cell in
the_work, then backend. `_lead_cell_team` now skips invalid cell names too. The
confirm/launch action no longer hard-fails on an enum the model got wrong.
* fix(intake): draft card no longer renders above the user's latest message
attachDraft fell back to "the last assistant message anywhere" when the current
turn had no streamed text yet (propose_draft called first). That last message was
often the PREVIOUS turn's — sitting above the user's "Yes, propose it" — so the
draft card rendered above the user's message. Attach only to the current turn's
streaming message; otherwise append a fresh assistant message so the card always
lands at the bottom of the thread.
* test(intake): guard draft enum coercion + invalid-cell skipping
Regression tests for the confirm-time enum coercion: an off-enum task_type
("feature") / nature / complexity coerce to code/technical/medium instead of
raising, and _lead_cell_team skips invalid cell names. Locks in that a bad enum
guess from the agent can never 400 the launch again.
* fix(intake): stop the agent fumbling through Claude Code meta-tools
In smoke it reflexively probed CC built-ins before reaching propose_draft —
plan mode + ExitPlanMode (it announced a written plan and waited instead of
emitting the draft), ToolSearch, Write — each correctly denied by the lockdown
but stumbly, and it only proposed after explicit CEO nudges.
- Gate: ExitPlanMode now gets a specific deny nudge ("you don't use plan mode;
call propose_draft"), and the generic deny names the actual toolset instead
of a bare "not available", so any probe degrades into guidance.
- Prompt: forbid plan mode/ExitPlanMode/ToolSearch explicitly and spell out
"you do not plan and wait — call propose_draft directly when the spec is
ready," plus an anti-pattern bullet.
* feat(intake): make the container logs transparent mid-turn
`docker logs` on the intake container was a black box: only turn start/end, while
the agent read the codebase and spawned 20+ subagents invisibly (the conversation
streams to the relay, not stdout), and the benign 3x ~/.claude.json warning was
the only thing visible.
- Driver logs each tool call mid-turn ("Intake tool use" with the tool name) and
the draft emission, plus a tools count in the turn-streamed summary. Text deltas
stay unlogged (they'd spam). Now the logs show the turn's real shape.
- Pre-create ~/.claude.json ({}) at container boot so the CLI's "config not found"
warning (printed 3x, self-healed anyway) stops drowning the real logs.
* fix(intake): render markdown in user messages + scope copy to code blocks
Two display fixes from the smoke:
- User messages collapsed newlines (plain {content} in a div) and rendered no
markdown — a "1.\n2.\n3." answer showed as one run-on line. Render user AND
assistant bubbles through a shared GFM markdown body that inherits the bubble's
text color, so lists / newlines / styling render correctly on both.
- Copy was blanketed on every assistant message; scope it to KEY parts — a copy
button on fenced code blocks (the draft card keeps its own). Removed the
per-message button.
* fix(intake): prevent duplicate tasks from a double-click on launch
Clicking a draft launch button twice fired two confirms and created duplicate
tasks. Add a synchronous re-entry guard (a ref — no stale-closure window) at the
top of launchTask so a second click returns immediately, and disable + spin the
draft-card buttons while a launch is in flight so it's visually clear it's working.
* docs(how-to): lead task creation with the Task Assistant flow
Rewrite "1 · It starts with you" to walk the Prompter/Task Assistant path —
scope form, the agent reading the codebase, its grounded analysis, the draft
card, and the created task — then flow into the Board review. Replaces the old
manual task-definition form shots.
Image placeholder: images/prompter_draft_card.png (the 3-button card) is
referenced but not yet captured — TODO comment marks it for the next smoke run.
A second comment flags an optional re-capture of prompter_run_2 after the
markdown-rendering fix.
* fix(intake): restore assistant message text contrast
The markdown refactor dropped `dark:prose-invert` and made text inherit the
bubble's color, but the assistant bubble had no explicit text color — so its text
rendered near-invisible (dark-on-dark on bg-muted). Give the assistant bubble an
explicit text-foreground; the user bubble already carries text-primary-foreground,
and [&_*]:!text-inherit now resolves to a readable color on both.
* fix(intake): coerce draft priority too — confirm 500'd on priority="high"
The enum-coercion fix covered task_type/nature/complexity/team, but priority is a
non-enum int field handled by `int(draft_data.get("priority", 2))`, and the agent
guesses a word ("high") as readily as a number — so int("high") raised ValueError
and the confirm 500'd. Same class of bug, one field missed.
Add _coerce_priority: map words (urgent/high/medium/low → 0/1/2/3), clamp numbers
to 0-3, default to 2 (medium) on anything else. The launch can no longer crash on
any field the LLM guessed. + regression test.
* fix(intake): draft card shows distinct cells, not one badge per work item
the_work has one entry per work item, so a cell with several items rendered its
badge repeatedly ("Board-led across Backend Backend Backend Frontend Frontend
…"). De-dupe to distinct teams so the card reads "Board-led across Backend
Frontend" — and the "Cell:" vs "Board-led across" label keys off distinct count.
* docs(how-to): hero the teaser gif + resolve the Prompter/Task Assistant thread
- Move the 12s teaser gif to the top as the hero — it was buried between the
"prefer video" link and the first screenshot.
- Name the connection: the Task Assistant IS the Prompter, so section 1 (using
the tool) and the rest (RoboCo building it) read as one story — you use the
tool the company built for itself, then watch the build.
- Re-anchor the section 1 → Board transition to follow the Prompter's own
journey, instead of implying section 1's example task is the one reviewed next.
* Included images for how-to.md
* docs(how-to): align agent count to 20 (matches README + CLAUDE.md)
The how-to said "18 agents" with UX/UI at one dev and no Intake — stale against
the authoritative count. Bump 18→20 (prose + spelled-out eighteen→twenty), give
UX/UI 2 devs, and add the Intake line to the org tree (Intake leads section 1, so
it belongs in the tree). README + CLAUDE.md already say 20.
* ci(release): publish all RoboCo images to GHCR + Docker Hub
The release published only the orchestrator to GHCR. Build and push the full set
the stack needs — agent-base, the 8 agent images, orchestrator, and panel — to
BOTH ghcr.io/rennf93/* and docker.io/renzof93/*, at :<version> and :latest, so
consumers can pull instead of compose-building.
- agent-base builds first (the agent images build FROM roboco-agent-base, a local
tag), then the rest; push only after every build succeeds.
- Image names mirror the docker-compose `image:` values 1:1.
- Free disk on the runner first (11 images is space-heavy).
- Needs a DOCKERHUB_TOKEN repo secret for the Docker Hub login.
- SECURITY.md updated to reference both registries.
* ci(release): use short SHA as the image tag on manual dispatch
A workflow_dispatch runs against a branch, and the branch name (e.g.
feature/prompter-gold-upgrade) was used verbatim as the image tag — but "/" is
illegal in a Docker tag, so the first build failed instantly with "invalid
reference format". Releases still tag from the release tag; manual dispatch now
always uses the short SHA, which is a valid tag.
---------
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
|
||
|
|
fd4df51572 |
docs: correct doc-vs-code drift across the canonical docs
A documentation audit against the code surfaced several stale claims: - Agent count: the roster is 19 AI agents (the UX/UI cell has two devs, ux-dev-1 + ux-dev-2), not 18 / a single UX dev. Fixed in README, CLAUDE.md, base.md, and docs/ux_ui. - API: domain routes are mounted under /api, not /api/v1 (the /api/v1 prefix is the agent gateway only); dropped the non-existent /api/v1/test group; fixed the orchestrator-status path in deployment.md. - Quick Start uvicorn target is roboco.api.app:app (the api package deliberately does not export app). - Verb table: the developer PR verb is open_pr (renamed from submit_for_qa); the lifecycle's canonical module is foundation/policy/lifecycle.py (enforcement/task_lifecycle.py is a shim). - Backend team stack: vector store is PostgreSQL + pgvector (via piragi), not Qdrant; mypy targets roboco/, not src/. - .env.example: replaced the phantom Qdrant/OpenAI blocks with the real Ollama/RAG settings. |
||
|
|
4f7dd7a336 |
docs(prompts): E4 clarify TodoWrite vs progress() distinction
TodoWrite is Anthropic's private session-local scratchpad — agents use it to track their own immediate next steps. It does NOT surface to the panel's Progress tab and is NOT a substitute for progress(task_id, message, percentage). Smoke run 3 didn't show this conflation yet, but Wave D's new progress() directive risks it. - base.md gets the canonical "TodoWrite vs progress()" callout - developer.md + documenter.md (the two roles with progress()) get inline reminders in their verb tables: "NOT TodoWrite" Spec ref: docs/superpowers/specs/2026-05-12-post-smoke-3-fixes-design.md section E4. |
||
|
|
6806516015 |
refactor(gateway): rename submit_for_qa to open_pr; pin atomic preconditions
Pre-fix, submit_for_qa opened a PR (side effect) and returned OK with next='call i_am_done' — agents read the verb name, assumed they were done with QA handoff, never called i_am_done, and PRs ended up orphaned (PR #12 in the 2026-05-08 trace). Two changes: 1. Rename submit_for_qa -> open_pr so the verb name matches the semantic. The PR opens here; the actual QA handoff happens at i_am_done. Renamed across: - choreographer/_impl.py (method) - mcp/flow_server.py (tool registration + _TOOLS dict) - api/routes/v2/flow_dev.py (route + handler) - api/schemas/v2/flow.py (OpenPrRequest) - services/gateway/verb_gates.py (_STATE_VERBS) - services/gateway/role_config.py (developer flow manifest) - services/gateway/content_actions.py (commit-success next= hint) - agent_sdk/server.py (post-tool guidance map) - runtime/orchestrator.py (developer prompt) - agents/prompts/{base,roles/developer,_generated/*}.md - tests/unit/gateway/test_submit_for_qa.py -> test_open_pr.py - tests/unit/api/routes/v2/test_flow_dev.py - tests/unit/gateway/test_verb_gates.py - tests/unit/api/test_correlation_id.py - tests/unit/mcp_servers/test_flow_server.py - tests/integration/test_full_lifecycle_real_db.py 2. New regression test (test_open_pr_does_not_create_pr_if_no_commits) pins the atomic invariant: preconditions (assignee, commits, no-prior-PR) must be checked BEFORE git.create_pr/push_branch run. Any future re-ordering breaks the test. Tests: 3128 passing (3127 + 1 new), 100% coverage, ruff clean. Note: TaskService.submit_for_qa() (the v1-layer service method) is INTENTIONALLY not renamed — it's a different layer used by the v1 routes. The rename here is only the gateway verb surface. |
||
|
|
85ef124c8f | Quality Gates | ||
|
|
4829f93a68 |
fix(gateway): unblock task claim; full Phase 0/1/2 remediation
Resolves the 100% claim-failure rate introduced by the gateway rewrite
(commit
|
||
|
|
1da4ac4b2e |
chore(prompts): restore role identity framing + anti-patterns
Each role file now has a six-section structure (Identity / Inputs / Your
verbs / Workflow / Anti-patterns / When the gateway returns an error).
The Identity section gives a hard role prior with explicit negative space
("you do NOT write code", "you do NOT merge"), recovering the framing
that was lost in the gateway slim-down. The Anti-patterns section names
the gateway error codes the role will hit if they step out
(PARENT_NOT_CLAIMED, SUBTASK_CAP, PM_CANNOT_EXECUTE_CODE, NO_COMMITS,
NO_PR, NOT_SELF_VERIFIED, etc.), so agents expect the system to catch
them. base.md now centralizes the envelope contract, ground rules, and
channel-slug convention so per-role files stay focused.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
33464a207a |
docs(prompts): forbid Bash curl/git for gateway-covered ops; PMs don't implement
Live smoke runs showed agents reaching for `Bash curl /api/...` and `Bash git ...` even though the slim role prompts named the gateway verbs, and main_pm took ownership of an implementation task and tried to commit code from the PM seat. - base.md and every role prompt: explicit ground rule that direct curl-to-orchestrator and raw-git invocations are forbidden — every commit/push/PR/transition/journal/comms call goes through the gateway verbs. The deny-list line in base.md now also covers curl/wget to the orchestrator's /api/... (was GitHub-only before). - main_pm.md and cell_pm.md: explicit "you do not implement tasks yourself" rule. Implementation belongs to developers; PMs delegate. |
||
|
|
62bda0c497 |
Gateway/full (#9)
* chore(gateway): scaffold gateway package and test layout
* feat(config): add gateway feature flags, coordination thresholds, commit-validator settings
* feat(gateway): add standardized response envelope with ok/error variants
* feat(gateway): add remediation hint catalog for tracing-gap and invalid-state errors
* feat(gateway): add per-role flow/do tool catalog with developer, qa, doc, pm, board configs
* feat(db): add gateway columns — active_claimant_id, heartbeat, pre_block snapshot, acceptance_criteria_status, qa_evidence_inspected
* feat(db): create gateway_triggers table for dispatcher decision logging
* feat(db): align canonical skill set; substitute qa_review -> code_review across agent seeds
* fix(db/008): make skill alignment in-place + idempotent; preserve column and existing custom skills
* feat(gateway): add claimant_lock for single-active-agent invariant with heartbeat staleness
* feat(gateway): add trigger_filter with stale-cleanup, claimant-queue, and cooldown rules
* feat(gateway): add tracing_gate with plan, progress, journal, acceptance_criteria, qa requirements
* refactor(gateway): drop per-file ruff ignores; refactor tracing_gate with dispatch table + GateContext
* feat(gateway): add merge_chain to resolve PR target by branch hierarchy depth
* feat(gateway): add commit_validator with min-length, banned-words, and conventional-shape hints
* feat(gateway): add evidence_builder for verb-response evidence and capped context_briefing
* feat(gateway): add Choreographer skeleton with per-phase verb signatures and DI protocols
* feat(runtime): add spawn_manifest builder for per-role pre-loaded tool registration
Introduces SpawnInputs dataclass + build_for_role(inputs) + write_manifest()
in roboco/runtime/spawn_manifest.py; reads role_config for allowed verbs/tools,
emits JSON manifest that SDK shim reads at container startup to eliminate ToolSearch.
* feat(runtime): wire gateway pre-spawn check (trigger_filter + claimant_lock) into orchestrator behind ROBOCO_GATEWAY_ENABLED flag
- Add GatewayTriggerTable SQLAlchemy ORM model to roboco/db/tables.py
(matches existing table from migration 007_gateway_triggers_table)
- Add module-level gateway_pre_spawn_check() + helpers to orchestrator.py
(gated: returns ("spawn", "gateway disabled") immediately when flag is False)
- Wire gateway check into _safe_spawn() — the single dispatcher choke-point
for all agent spawns; QUEUE or DROP outcome logs and returns None (no spawn)
- ROBOCO_GATEWAY_ENABLED defaults to False; legacy behaviour is unchanged
* feat(agent_sdk): load tool-manifest.json at startup behind ROBOCO_GATEWAY_ENABLED flag (no agent-visible change yet)
Adds load_tool_manifest() to the SDK server that reads env at call-time
so gateway-enabled agents can obtain their pre-registered tool list at
startup; returns None when the flag is off, leaving the legacy briefing
path completely unchanged.
* fix(optimal_brain): skip indexing when source ID is None to eliminate roboco://journals/None spam
- Add `build_doc_source(kind, id_)` module-level helper in indexes/base.py that
returns None when id_ is None instead of producing a "roboco://journals/None" URI
- Update abstract `build_source_uri` return type to `str | None` so subclasses
can legitimately signal a missing ID
- Short-circuit `ingest()` and `_prepare_docs_for_batch()` in BaseIndexPlugin
when `build_source_uri` returns None (debug log, no push to vector store)
- Fix JournalsIndexPlugin.build_source_uri: `kwargs.get("entry_id")` returns the
kwarg value even when it is None, so fall back to doc_id before calling
build_doc_source
- Fix ConversationsIndexPlugin.build_source_uri: return None when session_id is
None rather than producing "roboco://conversations/None-unknown"
- Add 9 unit tests with a piragi-free conftest that stubs sys.modules
* fix(agent_sdk): inject X-Agent-ID header on notification-poller requests
Both `_check_pending_a2a` and `_auto_ack_a2a_notifications` in
`roboco/mcp/a2a_server.py` were calling the main API without identity
headers, causing orchestrator `Missing X-Agent-ID header` warnings on
`GET /api/v1/notifications/pending-a2a` and the ack-a2a POST.
Add module-level `AGENT_ROLE` constant (mirrors the existing `AGENT_ID`
pattern) and pass `{"X-Agent-ID": AGENT_ID, "X-Agent-Role": AGENT_ROLE}`
on both requests.
* fix(git): use ROBOCO_PUBLIC_BASE_URL for commit-trailer Links instead of hardcoded localhost
* fix(test_runner): call uv run pytest/ruff directly; add make to orchestrator Dockerfile as backstop
FileNotFoundError was propagating as a raw 500 when a project had `make test`
configured but make was not installed in the orchestrator container.
Two fixes:
1. Catch FileNotFoundError in _run_command and re-raise as ValidationError (400)
with a clear message telling the operator to reconfigure the project command
(e.g. replace 'make test' with 'uv run pytest').
2. Add `make` to the orchestrator Dockerfile runner-stage apt-get so projects
that legitimately use make targets continue to work without reconfiguration.
* fix(api/git): resolve project by slug or UUID in git_log endpoint
Add _resolve_project_slug() helper to git routes that tries UUID
lookup first and falls back to slug, matching the pattern already
used in project routes. Apply to all four read-only git endpoints:
status, log, branches, diff.
* fix(a2a): auto-create conversation when conversation_id absent; reject empty IDs in URL builder
* fix(agent_sdk): default subagent model to parent agent's model from spawn manifest, not hardcoded haiku
Inject CLAUDE_CODE_SUBAGENT_MODEL env var into every agent container at
spawn time. Claude Code ≥2.1.x reads this variable to override the
default Task (Agent) subagent model, which otherwise hard-codes
claude-haiku-4-5-20251001. When the parent runs on a non-Anthropic
provider (e.g. Ollama Cloud / minimax-m2.7:cloud) that Anthropic model
is unreachable, so subagent dispatch fails.
The value follows the same provider-aware translation already used for
the --model CLI flag: Anthropic short names go through MODEL_MAP, and
non-Anthropic identifiers are passed verbatim. To avoid calling the
class by name inside a @staticmethod, the shared translation logic is
extracted to the module-level _resolve_agent_cli_model() helper;
_resolve_cli_model() now delegates to it.
Verified: CLAUDE_CODE_SUBAGENT_MODEL is present and honoured in the
Claude Code 2.1.123 binary (grep confirmed the env-var lookup pattern
`if(process.env.CLAUDE_CODE_SUBAGENT_MODEL) return KK(…)`).
* chore(makefile): add quality and quality-fast targets composing every PR gate
* chore(quality): add import-linter dependency and gateway boundary contract
* test(property): scaffold tracing-completeness assertion (filled in Phase 4)
* Format test file to pass ruff check
* fix(gateway): drop Protocol scaffolding from choreographer skeleton; del-statements on unused stub args; clear vulture whitelist
* linting
* feat(gateway): Phase 1 dev cutover — ChoreographerDeps + give_me_work
Add ChoreographerDeps frozen dataclass (7 deps: task, work_session, git,
a2a, journal, audit, evidence_repo), refactor Choreographer.__init__ to
accept the bundle, implement give_me_work + _briefing_for via
evidence_builder.build_context_briefing, and add property accessors for
all deps. All Phase 2-4 stubs gain del-statements and still raise
NotImplementedError. 3 tests added and passing; mypy/ruff/vulture clean.
* feat(gateway): implement i_will_work_on handling pending, claimed, and needs_revision recovery
* feat(gateway): implement i_have_committed with plan-required precondition
Replaces the NotImplementedError stub with the real implementation: looks up
the agent's active task, enforces plan presence before recording, calls
task.add_progress, and returns a structured Envelope. Adds 3 unit tests
(records progress, no active task → invalid_state, no plan → tracing_gap).
* feat(gateway): implement i_am_done with smart catch-up and skill resolution
* feat(gateway): implement i_am_blocked (struggle + escalate) and i_am_idle (with unread soft-block)
* feat(gateway): add ContentActions for commit, note, say, dm, evidence with auto-inject and validation
* feat(api/v2): add /api/v2/flow/dev/* endpoints delegating to Choreographer
Six intent-verb endpoints (give_me_work, i_will_work_on, i_have_committed,
i_am_done, i_am_blocked, i_am_idle) under /api/v2/flow/dev/, each a thin
handler that delegates to Choreographer. Includes Pydantic request schemas,
EvidenceRepo Phase 1 stub (all methods return []), get_choreographer FastAPI
dep wired with all 7 service deps, and 8 unit tests (all passing).
* feat(api/v2): add /api/v2/do/* endpoints for commit, note, say, dm, evidence
* feat(mcp): add roboco-flow MCP server for intent verbs (Phase 1: dev verbs implemented)
* feat(mcp): add roboco-do MCP server for smart-wrapped content tools
* feat(runtime): mount per-agent tool-manifest.json on developer-container spawn; gateway flag enabled for devs only
* docs(prompts): rewrite developer role prompt for gateway-only verbs (~15 lines vs 49)
* chore(mcp): confirm dev manifest excludes legacy task/journal/notify/a2a tools (Phase 1 cutover; servers retired in Phase 4)
* feat(gateway): implement claim_review with inline evidence (kills #15) and qa_evidence_inspected tracking
* feat(gateway): implement pass_review with qa_notes/learning/evidence tracing gates
* feat(gateway): implement fail_review with issue list, tracing gates, and dev A2A handoff
* feat(api/v2): add /api/v2/flow/qa/* endpoints (claim_review, pass, fail, give_me_work, i_am_idle)
* feat(mcp): add QA verbs (claim_review, pass, fail) to roboco-flow MCP server
* docs(prompts): rewrite QA role prompt for gateway verbs; explicitly warn against grep-the-commit anti-pattern
* feat(runtime): enable gateway flag for QA-role spawns (Phase 2 cutover)
* feat(gateway): implement claim_doc_task and i_documented with file-list and notes-min-chars gates
* feat(gateway): implement triage (cell PM) and triage_all (main PM) with priority order
* feat(gateway): implement unblock with pre_block_state restoration (kills #23)
* feat(gateway): implement cell_pm_complete with auto-merge to parent branch (kills #22 for cell scope)
* feat(gateway): implement main_pm_complete (open master PR + escalate to CEO)
* feat(gateway): add complete() dispatcher routing to cell_pm_complete or main_pm_complete by role
* feat(gateway): implement escalate_up routing by role.escalation_target
* feat(api/v2): add /api/v2/flow/{documenter,cell_pm,main_pm}/* endpoints
* feat(mcp): add Doc + PM verbs to roboco-flow MCP server (claim_doc_task, i_documented, triage, triage_all, unblock, complete, escalate_up)
* docs(prompts): rewrite Doc, Cell PM, and Main PM role prompts for gateway verbs
* feat(runtime): enable gateway flag for Doc, Cell PM, and Main PM roles (Phase 3 cutover)
* test(integration): full pending->awaiting_ceo_approval test through dev/QA/doc/cell-PM/main-PM gateway path
* chore(tests): rename unused args to _args in flow_server tests
Cleared RUF059 lint blocker for Phase 3 closeout. The destructured args was only consumed in URL-asserting tests; one variant only checks kwargs["json"], so its args is now _args.
* chore: untrack docs/superpowers/ + add to .gitignore
Plans + spec were inadvertently swept into commits 5d41a4b and de0c5b5 by subagent 'git add -A' calls. Removed from index and gitignored going forward; files remain on disk for ongoing reference. They still exist in history of those two commits — invoke a follow-up filter-repo if a full purge is desired.
* feat(gateway): implement Board escalate_to_ceo with role allow-list
Allows main_pm, product_owner, and head_marketing to escalate tasks to
CEO. Enforces awaiting_pm_review state and journal:decision tracing gate.
Closes Phase 4 Task 1.
* feat(gateway): implement board_triage prioritizing strategic root tasks
Adds Choreographer.board_triage and TaskService.list_strategic_for_board.
PO and Head Marketing get curated lists of strategic-nature root tasks
in awaiting_pm_review. Closes Phase 4 Task 2.
* feat(gateway): implement auditor_triage surfacing long-running blocked-task anomalies
Adds Choreographer.auditor_triage and TaskService.list_long_running_blocked.
The Auditor surfaces tasks blocked >30min as anomalies for reflect-note
observation. Closes Phase 4 Task 3.
* chore(tests): add return + arg type annotations to gateway tests
All gateway test functions now have -> None and parameter annotations. Cleared 63 mypy [no-untyped-def] errors that pre-existed since Phase 1. Mypy now clean across tests/unit/gateway/.
* feat(api/v2): add /api/v2/flow/{board,auditor}/* endpoints
Board: triage, escalate_to_ceo, i_am_idle.
Auditor: triage, i_am_idle (read-only role).
Adds EscalateToCeoRequest schema with reason min_length validation.
Closes Phase 4 Task 4.
* feat(mcp): add Board + Auditor verbs to roboco-flow MCP server
Adds escalate_to_ceo MCP tool used by Board (PO + Head Marketing) and Main PM. Updates the implemented set in _validate_role_compatibility. Auditor uses the existing triage tool with role-routing in URL.
Closes Phase 4 Task 5.
* docs(prompts): rewrite Board (PO, Head-Marketing, Auditor) prompts for gateway verbs
All 3 board identity files + roles/board.md now use the slim, gateway-aware shape (no ToolSearch directive, no state-tool table). Auditor is explicit about its read-only scope. Closes Phase 4 Task 6.
* feat(runtime): enable gateway manifest for ALL roles (Phase 4 cutover)
Adds product_owner, head_marketing, auditor to GATEWAY_ENABLED_ROLES. Every spawned agent now gets a gateway manifest mounted at /app/tool-manifest.json. The legacy briefing path is dead. Closes Phase 4 Task 8.
* test(property): implement tracing-completeness assertion across smoke-test batch
Replaces Phase 0 stub. Asserts the 6 tracing-contract requirements on every
completed task: audit_log agent_id non-null per state-transition row,
DEVELOPER:TASK_REFLECTION journal entry, QA:LEARNING journal entry,
CELL_PM/MAIN_PM:DECISION_LOG journal entry, acceptance_criteria_status
covering every criterion with a referencing_artifact_id, and
qa_evidence_inspected = true.
Uses an in-memory ephemeral Postgres test DB (`roboco_test_<pid>_<rand>`)
provisioned per pytest session, not SQLite — the production schema relies
on Postgres-only types (UUID, ARRAY) the SQLite dialect cannot compile.
Tests requesting db_session/smoke_test_batch are auto-skipped when no
Postgres is reachable on localhost:5432; ROBOCO_TEST_DB_HOST/PORT/USER
override the endpoint.
Schema is built via Base.metadata.create_all + manual ALTER for the
acceptance_criteria_status / qa_evidence_inspected columns, NOT via
`alembic upgrade head`. This sidesteps two pre-existing layer-drift items
that block any fresh migration run today:
1. Migration 001 declares the agentrole Postgres enum with lowercase
values (qa, developer, ...) but the SQLAlchemy ORM binds
Enum(AgentRole) to the StrEnum's uppercase NAMES — production DBs
mask this by being bootstrapped via create_all and stamped at 001.
2. Migration 008 runs UPDATE agents SET skills WHERE id over an
agents.skills column that no migration in this chain ever creates.
Documented in conftest.py so a future migrations cleanup can find them.
Also notes that acceptance_criteria_status/qa_evidence_inspected are in
the DB schema (per migration 006) but are NOT mapped on the ORM TaskTable
nor on the Pydantic Task model — services that read them via
`task.qa_evidence_inspected` rely on those values being set on raw rows.
The property test uses raw SQL to read the columns directly, matching the
DB-level contract.
Closes Phase 4 Task 11.
Side change: pyproject.toml — adds asyncpg.* to the existing
[[tool.mypy.overrides]] ignore_missing_imports list (asyncpg ships no
py.typed marker), matching the convention used for redis, anthropic,
piragi, etc.
Test count: 1; backend: Postgres (localhost test DB).
* style(mcp/flow_server): single-line _post call after format pass
* fix(db): map 7 gateway columns from migration 006 to TaskTable + Task model
active_claimant_id, last_heartbeat_at, pre_block_state, pre_block_assignee, pre_block_metadata, acceptance_criteria_status, qa_evidence_inspected: present in DB since migration 006 but absent from the ORM mapping. Gateway code (tracing_gate, choreographer, claimant_lock) reads these via task.<attr>; without the mapping, runtime would AttributeError. Closes PHASE4-BUG-A.
* fix(db): repair alembic chain — neutralize 008, add 009 enum reconcile, ORM uses values_callable
Three coordinated changes that close PHASE4-BUG-B:
1. roboco/db/tables.py — introduce _str_enum() helper that wraps Enum() with values_callable=lambda obj: [m.value for m in obj]. Apply to all 23 StrEnum-typed mapped columns. ORM now serializes by .value (lowercase) to match alembic 001's declared enum values; default Enum() was using .name (uppercase) which never matched.
2. alembic/versions/008_align_skills.py — replace with documented no-op. The original migration referenced agents.skills, a column that has never existed in any migration (the agents table has capabilities, not skills). The substitution intent (qa_review -> code_review) was already satisfied statically in roboco/agents_config.py.
3. alembic/versions/009_enum_reconcile.py — new migration that:
- Adds missing enum values: agentrole.system, team.fullstack, taskstatus.quarantined.
- Detects uppercase drift from a Base.metadata.create_all bootstrap and rebuilds agentrole/team/taskstatus enums with lowercase members + USING lower(col::text)::enum on every column referenced. No-op if already lowercase.
Tests stay green: 281 passed.
* feat(services): backfill 36 gateway-shaped methods for Choreographer
The gateway Choreographer was wired to call methods that the underlying
services did not expose. This adds them as thin wrappers + queries (most
alias canonical methods; a handful are gateway-specific variants).
TaskService — 26 methods: aliases (submit_verification, submit_qa,
list_blocked_for_team, list_blocked_all_teams,
list_awaiting_pm_review_for_team, list_assigned_for_agent), agent
queries (agent_for, qa_agent_for_team, documenter_for_team,
cell_pm_for_team, get_active_task_for_agent, list_paused_for_agent),
triage queries (list_awaiting_main_pm_all, all_subtasks_terminal),
state setters (set_plan, mark_evidence_inspected, mark_agent_idle),
QA/Doc claim variants (qa_claim, doc_claim, qa_pass, qa_fail),
PM completion (cell_pm_complete with merge_commit), unblock with
state restore (unblock_with_restore), and escalation
(escalate, escalate_up_to_role). Also adds GatewayAgentView
dataclass that unifies DB and config-derived agent attributes.
JournalService — 4 methods: existence checks (has_decision_for_task,
has_learning_for_task, has_reflect_for_task) + write_struggle.
GitService — 4 methods: branch-keyed entry points (create_pr,
pr_merge, pr_target, diff) plus push_branch helper. Each derives
project + workspace from the task that owns the branch / PR.
WorkSessionService — 2 methods: files_changed + has_unpushed_commits.
PR existence is the proxy for pushed (no per-commit push column).
Choreographer: switched git.push(branch_name) call to push_branch()
to dispatch to the new gateway-shaped helper.
* test(services): unit tests for 36 gateway-backfill methods
Adds happy-path + edge tests for every method added in the prior
backfill commit. Total 61 new tests across:
- tests/unit/services/test_task.py (36)
- tests/unit/services/test_journal.py (8)
- tests/unit/services/test_git.py (10)
- tests/unit/services/test_work_session.py (7)
Each test mocks at the session boundary (no DB) and stubs adjacent
service methods via a dynamic _bind helper to avoid mypy
[method-assign] noise without resorting to type:ignore comments.
* test(gateway): switch dev catch-up assertion to push_branch
The Choreographer's catch-up sequence was renamed from git.push(branch)
to git.push_branch(branch) when GitService got a gateway-shaped helper
in the prior commit. This updates the existing assertion to match.
* feat(mcp): add roboco-git-readonly server with status/log/diff/branches
Slim FastMCP server exposing the four read-only git tools every role
needs (status, log, diff, branch_list) by forwarding to /api/v1/git/*
on the orchestrator. Replaces the read-only half of the legacy
roboco-git server; write operations now go through gateway verbs in
roboco-flow / roboco-do.
The endpoint shapes mirror the panel-facing API (project_slug,
include_remote, staged/file_path) so the same backend handlers serve
both human and agent traffic.
* refactor(mcp): delete legacy task/journal/notify/a2a/message/project servers
Phase 4 cutover: agents now reach every state-changing surface through
the gateway (roboco-flow intent verbs + roboco-do content tools), with
roboco-git-readonly + roboco-optimal + roboco-docs covering reads. The
seven legacy MCP servers + their handler trees are dead code from the
agent side, so they're removed:
roboco/mcp/task_server.py (1020 LOC)
roboco/mcp/journal_server.py (512 LOC)
roboco/mcp/notify_server.py (440 LOC)
roboco/mcp/a2a_server.py (790 LOC)
roboco/mcp/message_server.py (682 LOC)
roboco/mcp/project_server.py (667 LOC)
roboco/mcp/tasks/ (handlers+utils) (~4300 LOC)
roboco/mcp/test/ (in-container runner; replaced by gateway evidence
+ manual smoke)
roboco/mcp/git/ (full server; read-only half migrates to the new
slim roboco-git-readonly module, write half is
owned by gateway verbs)
Orchestrator updates:
- _generate_mcp_config registers only roboco-flow, roboco-do,
roboco-git-readonly, roboco-optimal, and (for docs roles) roboco-docs.
No more per-role legacy fan-out.
- base_allow flips to mcp__roboco-flow__*, mcp__roboco-do__*,
mcp__roboco-optimal__*, mcp__roboco-git-readonly__*. Role-specific
allow lists are reduced to file IO scoping, since gateway verbs
enforce role policy server-side.
- TRACEABILITY_TRIGGER_TOOLS rewritten in terms of the gateway servers
(mcp__roboco-flow__* / mcp__roboco-do__*) instead of the now-deleted
per-tool list.
Test fix: tests/unit/services/test_a2a.py imported _handle_send_chat_message
from the deleted a2a_server. The four MCP-layer URL-builder tests (empty
conversation_id guard) are dropped — the equivalent boundary now lives
in /api/v2/do/* which has its own integration coverage. The two
service-layer nil-UUID guard tests are kept; they exercise A2AService
directly and remain meaningful (the panel still uses the v1 chat surface,
where a buggy caller could pass the nil UUID).
Net: ~9600 LOC removed from roboco/mcp/. quality-fast green:
338 tests pass, mypy clean, ruff clean. No /api/v1/* router changes —
those endpoints stay live for the panel UI which still uses every
lifecycle action; agents have no prompts that name them so the path is
dead code from the agent side.
* docs(claude.md): replace legacy MCP listing with gateway/verb-surface section
Phase 4 cutover: agents go through roboco-flow + roboco-do (gateway), not the deleted task/journal/notify/a2a/message/project servers. Document the verb surface per role + the Envelope response shape so future Claude Code sessions land in the correct mental model. Closes Phase 4 Task 13.
---------
Co-authored-by: Renn F <rennf93@users.noreply.github.com>
|
||
|
|
e4b4ac6d33 | Fixed some ggit operations and that. Still needs work. PR problem | ||
|
|
8e201901c0 | I mean, it's at a good place rn... | ||
|
|
0023c25d60 | Added git workflow + fixing some issues | ||
|
|
f1c5b7958c |
Add Project & Workspace MCP System with role-based permissions
- Add roboco_project_* tools (list, get, create, update) with CEO bypass - Add roboco_workspace_* tools (ensure, status, list) for workspace management - Add project_slug and requires_git fields to TaskCreateInput schema - Validate project exists and cell matches when creating git-enabled tasks - Register project MCP server in orchestrator with proper permissions Workspace permissions by role: - Developer: Write to own workspace only - QA: Read-only access to all cell workspaces - Documenter: Write to all cell workspaces (add docs to dev branches) - Cell PM: Write to own workspace, project_update for own cell - Main PM: Full project access (create, update all, workspace_list all) - CEO: Full bypass on all permission checks Also includes: - Git templates for commits, branches, PRs (separation of concerns) - Updated blueprints with project/workspace tools documentation - Updated RAG docs with project tools reference |
||
|
|
16dda8134b | Task sequence and RAG LLM improvements | ||
|
|
22d1401020 | First git integration implementation + KB fixes and upgrades | ||
|
|
f9398e4caa | RAG Implementation | ||
|
|
1116e597d0 | RAG expansion + Optimal API | ||
|
|
8c3bf9e22c | Huge refactoring but stuff is working again; minus some issues here and there. |