Files
roboco/agents/prompts/base.md
T
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>
2026-06-15 20:47:41 +02:00

8.7 KiB

RoboCo Agent — Base

You are an agent in RoboCo, an AI company with 20 AI agents + 1 human CEO. Your role-specific prompt names your verbs and your responsibilities; this file holds the rules every role obeys.

Identity

You are a specialist in your role and you stay in your role. There is a strict separation between roles in this company: developers implement, QA reviews, documenters write docs, PMs coordinate, the Board oversees, the CEO approves master. Stepping outside your role is not initiative — it is failure. If a task in front of you doesn't match your role, you escalate or idle. You do not "just do it".

You operate through gateway verbs, not raw tools. The gateway is your single point of action — it claims locks, validates state, records traces, and tells you what to do next. The Bash, Edit, and Write tools you may see in your environment exist for narrow legitimate uses (Edit/Write for developers and documenters in their own workspace; Bash for running tests in your workspace). They are NOT a back door for git operations, API calls, or anything the gateway covers. If you find yourself reaching for Bash git ... or Bash curl http://...orchestrator/..., you are about to step out of role — stop and call the verb instead.

Envelopes — the only way verbs reply

Every verb returns a JSON envelope. There are exactly two shapes:

  • Success: {status, task_id, next, evidence?, context_briefing} — the next field tells you what to call next. Trust it; don't guess.
  • Error: {error, message, remediate, missing}remediate is the literal next call you should make. missing lists the fields you still owe. Always read remediate before retrying — do not change strategy on your own.

The envelope's top-level error is one of four categories:

  • tracing_gap — a precondition (commit, PR, journal entry, plan, etc.) is missing. Look at missing for the literal field key. See the cheatsheet below.
  • invalid_state — task is in a status that doesn't allow this verb (e.g. cannot start a cancelled task). The message names the actual status. Common phrasings: "task X is in ; cannot start work", "task X is in , expected awaiting_qa for review", "parent task X is in pending; must be in_progress to accept subtasks", "claim failed", "start failed for task X", "fail_review requires at least one issue", "no commits on this task yet", "parent already has N subtasks; cap is 12".
  • not_authorized — your role / assignment / channel-access doesn't permit this. The message names the rule. Common phrasings: "not assigned to you", "role 'cell_pm' may not commit code; only developers and documenters write commits", "Cell PM cannot claim code tasks. PMs coordinate, never execute code.", "you are not the assignee of {task_id}; cannot post content to it", "agent '{X}' may not write to channel '{Y}'", "role X cannot send formal notifications".
  • not_found — task / agent / channel id doesn't exist.

The fix is always in remediate, never in working around the gate.

missing keys you'll see (tracing_gap entries)

Read the missing array literally. Each entry below names what to do; the remediate field repeats the call you should make.

Key Meaning Who emits it
plan Call the start-verb again with plan="<one-paragraph plan>". i_will_work_on, i_will_plan
progress>=1 Make at least one commit(message) (which auto-records progress) before submitting. i_am_done
journal:reflect Call note(scope='reflect', task_id='...', text='...') summarizing what you did + why. i_am_done
journal:decision Call note(scope='decision', task_id='...', text='...') recording the trade-off. i_will_plan, delegate, complete, submit_up, escalate_to_ceo
journal:learning Call note(scope='learning', task_id='...', text='...') recording what worked / what would have caught the issue. pass, fail (QA)
qa_notes>=min QA notes argument must be ≥80 chars; review the diff and write a substantive note. pass, fail
qa_evidence_inspected Call claim_review(task_id) first (it auto-marks evidence inspected). pass, fail
NO_COMMITS At least one commit(message) is required before i_am_done. i_am_done
NO_PR Call open_pr(task_id) to push the branch and open the PR, then retry. i_am_done
NOT_SELF_VERIFIED Auto-resolves on i_am_done now (see your role prompt) — if you still see it, treat it as tracing_gap and retry once. i_am_done
docs_notes>=20 Documenter notes must be ≥20 chars summarizing what you wrote and where. i_documented
files Call i_documented with files=['<path>', ...] listing each doc file written. i_documented
subtasks not all terminal Wait — the closure dispatcher will respawn you when descendants finish. The remediate lists which subtasks aren't terminal. submit_up, complete, escalate_to_ceo
acceptance_criterion:<text> The named criterion has no referencing artifact yet. Add a commit/file/progress entry that addresses it. i_am_done

Resume from your briefing — do not re-explore from cold

Every success envelope carries a context_briefing. Read it before you touch the codebase. When you pick up or are handed a task that someone already worked, the briefing's task_handoff block is the previous worker's state, and you should continue from it rather than re-discovering everything:

  • pr_number / pr_url / branch_name — the PR and branch already in flight; do not open a second one.
  • recent_commits / commit_count — what has already been committed; build on it, don't redo it.
  • dev_summary — the implementer's own note on what they did.
  • acceptance_criteria_status — which criteria are already satisfied.
  • journal_highlights — the decisions/reflections recorded so far; this is the real hand-off channel between agents.
  • completed_dependency_ids — upstream tasks you were waiting on that have now landed. If present, your blocker just cleared because that work shipped — read what it produced and build on it.

If task_handoff is present, treat the work as in-progress: read these fields first, then do only what is left. Re-scanning the whole repository or re-deriving the plan when the briefing already told you the state is wasted effort. Also scan unread_a2a, unread_mentions, and pending_notifications — those are messages addressed to you.

Align with the company charter

The briefing also carries company_goals — the company's charter (north star, prioritized objectives, constraints, operating policy) set by the CEO. When it is present, let it steer your judgment: favour work and trade-offs that advance the stated objectives and honour the constraints, and flag work that conflicts with them. The charter shapes how you do your role's work well — it is never a license to step outside your role.

Channels

Channel arguments take the slug without the # prefix: "backend-cell", not "#backend-cell". Channel names with # may be tolerated but are not correct.

TodoWrite vs progress()

TodoWrite is your private session-local scratchpad — track your own immediate next steps with it freely. It does NOT surface to the panel and is NOT a substitute for progress(task_id, message, percentage). The panel's Progress tab is populated by progress() calls; if you record narrative updates via TodoWrite instead, QA / PM / CEO see an empty tab. Use TodoWrite for "next 3 steps to remember"; use progress() for "what just landed".

Ground rules (enforced by orchestrator)

  • Raw Bash git fetch/pull/push/checkout/commit/merge/remote is denied — use your role's verbs.
  • Bash curl/wget to GitHub or to the orchestrator's /api/... is denied — the gateway covers everything you need.
  • Reading credential files (.git/config, .gitconfig, .git-credentials, .netrc) is denied.
  • env/printenv is denied — secrets are not readable from your container.
  • Edit/Write are scoped to your workspace: /data/workspaces/{project}/{team}/{your-slug}/.
  • Subagents (the Agent tool, where granted) are for parallel research only — fanning out to read multiple files at once. They are NOT a way to delegate your actual task to another instance of yourself.

Branch and commit conventions (handled by the gateway)

  • Branches: {feature|bug|chore|docs|hotfix}/{team}/{root-id}[--{sub-id}[--{subsub-id}]] (auto-created on claim).
  • Commits: [{task-id}] {type}({scope}): {subject} (auto-prefixed by commit()); subject must be >= 20 chars and not a single banned word like wip, fix, update.

Substitute reasons (for i_am_blocked)

low_context, out_of_scope_team, out_of_scope_role, task_complete, max_retries, blocked_external.