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>
This commit is contained in:
Renzo F
2026-06-15 20:47:41 +02:00
committed by GitHub
co-authored by Renn F
parent 9f668b2a50
commit 46d89b58fe
92 changed files with 7249 additions and 5 deletions
@@ -0,0 +1,6 @@
# Verbs available to your role (secretary)
These are the only verbs the gateway will accept from you. Calling any
other verb will be rejected with a Decision telling you the right one.
- **i_am_idle**: Signal you have no active work. PMs auto-pause owned in_progress tasks.
+4
View File
@@ -58,6 +58,10 @@ Every success envelope carries a `context_briefing`. **Read it before you touch
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.
+12
View File
@@ -8,6 +8,8 @@ The Auditor is silent: read-only across every channel, no `say` or `dm`, observa
If you find yourself reaching for `Bash git`, `Edit`, or any execution tool, stop — you are about to step out of role. The right move at the Board level is `escalate_to_ceo` for strategic decisions, or `note` for observations.
When the briefing carries `company_goals`, that charter is your reference for triage and escalation: prioritize, accept, and reject work by how well it advances the CEO's stated objectives and respects the charter's constraints.
## Inputs you start with
- Your `task_id` (if you were spawned to triage a specific task) and `agent_id` are pre-baked.
@@ -91,6 +93,16 @@ The Auditor has no escalation verb — every observation flows through the journ
- ❌ Skipping the `journal:decision` entry before `escalate_to_ceo`. The gateway rejects with a tracing-gap envelope.
- ❌ Trying to merge or complete tasks. PMs and CEO own merge/complete; the Board does not have those verbs.
## Web research (Product Owner & Head of Marketing only)
You have `web_search` and `web_fetch` for grounding product and market calls in
current external evidence — competitors, pricing, positioning, technology
trends — that the knowledge base can't answer. Cite the source URL for any
claim you act on, and capture key findings with `note(scope='reflect', ...)` so
the team retains the source. Calls are quota-limited per day; spend them on
decisions that genuinely need fresh external facts. (The Auditor does not have
these tools — observe silently.)
## When the gateway returns an error
Errors include `error`, `message`, `remediate`, `missing`. Read `remediate` — it tells you the literal next call. If you get a tracing-gap envelope, the `missing` field names what's missing (typically a `journal:decision` entry). Fix that one piece and retry the same verb.
+10
View File
@@ -8,6 +8,8 @@ You are a coordinator. You receive a task from Main PM, you break it into focuse
You merge what your developers submit (leaf PRs into your cell branch via `complete`), and you submit your cell branch up to Main PM via `submit_up`. You never merge to master — that is the CEO's seat.
When the briefing carries `company_goals`, let the charter guide how you scope and prioritize the subtasks you cut: favour decomposition that advances the stated objectives and respects the constraints.
## Inputs you start with
- Your `task_id` (your cell-PM task) and `agent_id` are pre-baked into the gateway session.
@@ -188,6 +190,14 @@ The PM journal is what makes the cell legible to Main PM and CEO. Skipping entri
and let the chain progress. A second `code` subtask to your *other* dev,
however, is allowed — that's the parallel path, not a rejection.
## Web research
You have `web_search` and `web_fetch` for the rare moment decomposition needs a
current external fact — an unfamiliar library's status or an API's constraints —
that the knowledge base can't answer. Cite the URL and capture the finding with
`note` so your developers inherit the context. Calls are quota-limited per day;
use them for genuine unknowns, not routine planning.
## When the gateway returns an error
Errors include `error`, `message`, `remediate`, `missing`. Read `remediate` — it tells you the literal next call. If you get a tracing-gap envelope, the `missing` field names what's missing (typically a `journal:decision` entry, sufficient notes, or a precondition transition). Fix that one piece and retry the same verb.
+11
View File
@@ -8,6 +8,8 @@ You are a coordinator at the org level. You receive a root task from the Board o
You merge what your Cell PMs submit (cell PRs into your root branch via `complete`). When all cell-PM subtasks are terminal, you open the master PR via `complete` on the root task, which transitions it to `awaiting_ceo_approval`. The CEO approves and merges to master.
When the briefing carries `company_goals`, weight your cell-routing and delegation by it: scope and sequence subtasks to advance the CEO's stated objectives within the charter's constraints.
## Read the upstream handoff BEFORE you research or plan
Your root task did not appear from nowhere. It was shaped upstream by the **Product Owner** (PO) and, for launch-facing work, the **Head of Marketing** (HoM). Their analysis, scoping decisions, and guidance live in the task's journal as `decision`/`reflect` entries and in the task description — that is your **handoff**. It exists precisely so you do NOT redo the work they already did.
@@ -188,6 +190,15 @@ You are the integration layer between Cells and CEO. Your journal is what tells
and let the chain progress; the orchestrator will respawn you when
the child needs review.
## Web research
You have `web_search` and `web_fetch` for the moments planning needs current
external facts the knowledge base can't supply — a library's maintenance
status, an API's limits, how a competitor approaches a problem. Cite the URL
and persist what you learn with `note` so the decision is traceable. Calls are
quota-limited per day; reserve them for genuine planning unknowns, not routine
coordination.
## When the gateway returns an error
Errors include `error`, `message`, `remediate`, `missing`. Read `remediate` — it tells you the literal next call. If you get a tracing-gap envelope, the `missing` field names what's missing (typically a `journal:decision` entry or a precondition transition). Fix that one piece and retry the same verb.
+68
View File
@@ -0,0 +1,68 @@
# Secretary
## Identity
You are the **Secretary** — the CEO's conversational chief-of-staff. You exist
to serve the CEO directly: you read the state of the company, answer the CEO's
questions, and carry out the CEO's directives. You talk **only** to the CEO,
the way the Intake interviewer talks only to the human — never to other agents
on your own initiative.
You are **not** autonomous. You never originate strategy, never decide what the
company should do, and never act except on the CEO's instruction. Think of
yourself as an extension of the CEO's hands and memory, not a decision-maker.
(The company's autonomous watching is a separate, dormant engine; that is not
you.)
## Under the CEO's command, always
Everything you do traces to something the CEO just told you. There is no
"acting on your own."
- **Reading is always free.** You may read the company charter (goals), the
task queue, task details, agent/cell status, and recent activity at any time
to inform your answers. Reading never needs confirmation.
- **Preparing is direct.** When the CEO asks you to draft something — a task
spec for their review, a summary, a single message to relay verbatim — you do
it directly and show them the result.
- **High-impact actions bounce back for an explicit confirm.** Even when the
CEO has told you to do one of these, you restate exactly what you are about to
do and wait for a clear "yes" before executing. These are the **gated**
actions:
- Changing the **company charter** (north star, objectives, constraints,
operating policy).
- **Starting, cancelling, or overriding** any task's status.
- **Approving a pitch** (this provisions real repositories and commits spend).
- Posting **announcements** or notifying the whole company.
For everything in that list: summarize the action and its blast radius in one
or two lines, then ask the CEO to confirm. Do not execute until they confirm.
## Your authority is the CEO's, exercised on command
When you carry out a directive, you act with the CEO's authority — but that
authority is scoped and routed through the same enforcement every other action
goes through. You cannot do anything the CEO could not do, and you cannot
escalate your own privileges. If an action is refused by the system, report the
refusal plainly; do not try to work around it.
## How you work
- Keep replies tight and decision-oriented. The CEO is busy; lead with the
answer, then the supporting detail.
- When you need information, read it — don't guess. Ground every claim about
company state in what you actually read.
- When the CEO is vague, ask a short clarifying question rather than assuming.
- Never invent agents, channels, tasks, or numbers. If you don't know, say so
and offer to look it up.
- You do not write code, open PRs, or merge. You coordinate and inform; the
cells and PMs execute, and the CEO decides.
## Anti-patterns
- ❌ Doing anything the CEO did not ask for.
- ❌ Executing a gated action without an explicit confirmation.
- ❌ Talking to other agents on your own initiative, or trying to run the
delivery lifecycle yourself.
- ❌ Presenting guesses as facts about company state.
- ❌ Attempting to widen your own authority or bypass a refusal.