Spec: autonomous CEO slot (per-repo, gated on .agent/ceo.md) #238

Closed
opened 2026-06-24 23:13:18 +00:00 by igor · 2 comments
Collaborator

Goal

Add a CEO pass to the tick that turns the interactive /ceo role into an autonomous one. For repos that opt in (by containing .agent/ceo.md), periodically run an LLM "CEO" that grooms the Agent queue, reviews bot PRs, triages feedback, and emails a board digest — all within a mandate-defined authority that never ships code itself. Companion to the interactive /ceo persona; both read the same .agent/ceo.md (Porksicle's is merged: joshtronic/porksicle.com:.agent/ceo.md).

This is "just another igor pass," driven by mandate + judgment instead of a scored heuristic — the gap between igor's existing auto-filing (SEO/deps, scored in shell) and actual product strategy.

Gate (opt-in, per-repo)

A repo gets a CEO iff .agent/ceo.md exists at its root — read via the Forgejo contents API (same mechanism as the CLAUDE.md onboarding check) or from the synced clone. No file → no CEO. This mirrors the WEBSITE_REPO / SEO_AGENTIC_SITES opt-in philosophy, but per-repo via a tracked file. Optionally also behind a global CEO_ENABLED / CEO_RECIPIENTS env that no-ops when unset (matching the SEO/market/sports email siblings).

Where in the cascade (bin/tick.sh)

After PR-review pickup + Igor's own work, alongside the maintenance/SEO analysis block. It's analysis-shaped: read-only to code; its side effects are issues / PR reviews / email, never commits. Iterate bot-accessible repos that have the mandate file (decoupled from the worker-validation gate like the other analysis passes), throttled per-repo.

Cadence + state (discretionary-state.json)

Per-repo .ceo object: { last_daily, last_weekly, last_digest, failures }, mirroring the existing daily/weekly slot mechanics (midnight rollover for daily, ISO-week Monday-anchored + self-healing for weekly, 5-hard-failure/day cap, cooldown).

  • Daily (light steering): first tick after midnight. Early-exit when nothing's actionable.
  • Weekly (deep strategy): first tick of the ISO week — adds re-prioritization + the full digest.

What a CEO run does

Gather (harness, via lib/forgejo.sh + the synced clone): the mandate .agent/ceo.md; open bot PRs (esp. those awaiting a first review on current HEAD); open Agent issues + queue depth; recent feedback signal if wired; the project memory.

Invoke Claude (lib/claude.sh) with a new bin/lib/ceo-directive.md (sibling of sports-digest-directive.md) + the gathered context + the mandate.

LLM-decides / harness-executes (same split as the SEO/maintenance passes — keeps all write side-effects harness-controlled). Claude returns STRUCTURED output, e.g.:

{ "pr_reviews":     [{ "pr": N, "verdict": "approve|request_changes", "body": "..." }],
  "issues_to_file": [{ "title": "...", "body": "...", "labels": [...], "priority": "..." }],
  "digest_md":      "...",
  "escalations":    [ "..." ] }

The harness validates + executes via the Forgejo API: post PR reviews, create deduped Agent issues, send the digest (lib/email.sh), stamp state.

Authority guardrails (HARNESS-ENFORCED, not just prompted)

The CEO surface is propose + review, never ship — this encodes the mandate's "board-only = merging/shipping" line structurally:

  • Allowed: create issues, post PR reviews (approve / request-changes), comment, label.
  • Forbidden (harness must never call these for the CEO): merge PRs, push to any branch, modify protected branches, touch .forgejo/workflows, secrets, or the deploy host.
  • Caps: ≤ N open CEO-filed Agent issues per repo; respect "one bot PR per repo at a time" (Discovery already enforces); dedup issues by title/signature so it doesn't re-file every tick.

Digest (lib/email.sh)

Event-driven daily (only when actionable: merge-ready PR, blocker, decision needed) + always weekly. To CEO_RECIPIENTS (or reuse SEO_PRIMARY_EMAIL). Content: what was delegated, what merged since last, what's merge-ready awaiting the board, blockers, decisions needed.

Dependencies / order

  1. #226 (stale local clones) first — the CEO must read current repo state (PR diffs, the mandate, the queue). Building on a stale clone repeats the #43 failure.
  2. Then this CEO slot.

Open questions (board calls — @joshtronic)

  • Identity: v1 = same igor bot files + reviews + works (talks to itself via the tracker; fine). v2 = a distinct CEO bot user for cleaner separation?
  • Merge line: mandate v1 = board-only; confirm the harness hard-blocks CEO merges (recommended yes).
  • Digest channel: email (reuse SMTP2GO) vs a pinned Forgejo "board" issue vs both?
  • Dedup strategy for re-filing: title hash, or a marker label?

Why this shape

Reuses igor's existing machinery end-to-end (cascade, claude.sh, forgejo.sh, email.sh, discretionary slots, directive files). LLM-decides / harness-executes keeps guardrails structural, not prompt-dependent. Deliberately not Agent-labeled — igor shouldn't autonomously rewrite its own tick loop; this is a careful human/PR change.

## Goal Add a **CEO pass** to the tick that turns the interactive `/ceo` role into an autonomous one. For repos that opt in (by containing **`.agent/ceo.md`**), periodically run an LLM "CEO" that grooms the `Agent` queue, reviews bot PRs, triages feedback, and emails a board digest — all within a mandate-defined authority that **never ships code itself**. Companion to the interactive `/ceo` persona; both read the same `.agent/ceo.md` (Porksicle's is merged: `joshtronic/porksicle.com:.agent/ceo.md`). This is "just another igor pass," driven by **mandate + judgment** instead of a scored heuristic — the gap between igor's existing auto-filing (SEO/deps, scored in shell) and actual product strategy. ## Gate (opt-in, per-repo) A repo gets a CEO **iff `.agent/ceo.md` exists at its root** — read via the Forgejo contents API (same mechanism as the CLAUDE.md onboarding check) or from the synced clone. No file → no CEO. This mirrors the `WEBSITE_REPO` / `SEO_AGENTIC_SITES` opt-in philosophy, but per-repo via a tracked file. Optionally also behind a global `CEO_ENABLED` / `CEO_RECIPIENTS` env that no-ops when unset (matching the SEO/market/sports email siblings). ## Where in the cascade (`bin/tick.sh`) After PR-review pickup + Igor's own work, alongside the maintenance/SEO **analysis** block. It's analysis-shaped: read-only to *code*; its side effects are issues / PR reviews / email, **never commits**. Iterate bot-accessible repos that have the mandate file (decoupled from the worker-validation gate like the other analysis passes), throttled per-repo. ## Cadence + state (`discretionary-state.json`) Per-repo `.ceo` object: `{ last_daily, last_weekly, last_digest, failures }`, mirroring the existing daily/weekly slot mechanics (midnight rollover for daily, ISO-week Monday-anchored + self-healing for weekly, 5-hard-failure/day cap, cooldown). - **Daily (light steering):** first tick after midnight. Early-exit when nothing's actionable. - **Weekly (deep strategy):** first tick of the ISO week — adds re-prioritization + the full digest. ## What a CEO run does **Gather (harness, via `lib/forgejo.sh` + the synced clone):** the mandate `.agent/ceo.md`; open bot PRs (esp. those awaiting a first review on current HEAD); open `Agent` issues + queue depth; recent feedback signal if wired; the project memory. **Invoke Claude (`lib/claude.sh`)** with a new `bin/lib/ceo-directive.md` (sibling of `sports-digest-directive.md`) + the gathered context + the mandate. **LLM-decides / harness-executes** (same split as the SEO/maintenance passes — keeps all write side-effects harness-controlled). Claude returns STRUCTURED output, e.g.: ``` { "pr_reviews": [{ "pr": N, "verdict": "approve|request_changes", "body": "..." }], "issues_to_file": [{ "title": "...", "body": "...", "labels": [...], "priority": "..." }], "digest_md": "...", "escalations": [ "..." ] } ``` The harness validates + executes via the Forgejo API: post PR reviews, create deduped `Agent` issues, send the digest (`lib/email.sh`), stamp state. ## Authority guardrails (HARNESS-ENFORCED, not just prompted) The CEO surface is **propose + review, never ship** — this encodes the mandate's "board-only = merging/shipping" line structurally: - **Allowed:** create issues, post PR reviews (approve / request-changes), comment, label. - **Forbidden (harness must never call these for the CEO):** merge PRs, push to any branch, modify protected branches, touch `.forgejo/workflows`, secrets, or the deploy host. - **Caps:** ≤ N open CEO-filed `Agent` issues per repo; respect "one bot PR per repo at a time" (Discovery already enforces); **dedup** issues by title/signature so it doesn't re-file every tick. ## Digest (`lib/email.sh`) Event-driven **daily** (only when actionable: merge-ready PR, blocker, decision needed) + **always weekly**. To `CEO_RECIPIENTS` (or reuse `SEO_PRIMARY_EMAIL`). Content: what was delegated, what merged since last, what's **merge-ready awaiting the board**, blockers, decisions needed. ## Dependencies / order 1. **#226 (stale local clones) first** — the CEO must read *current* repo state (PR diffs, the mandate, the queue). Building on a stale clone repeats the #43 failure. 2. Then this CEO slot. ## Open questions (board calls — @joshtronic) - **Identity:** v1 = same `igor` bot files + reviews + works (talks to itself via the tracker; fine). v2 = a distinct CEO bot user for cleaner separation? - **Merge line:** mandate v1 = board-only; confirm the harness hard-blocks CEO merges (recommended yes). - **Digest channel:** email (reuse SMTP2GO) vs a pinned Forgejo "board" issue vs both? - **Dedup strategy** for re-filing: title hash, or a marker label? ## Why this shape Reuses igor's existing machinery end-to-end (cascade, `claude.sh`, `forgejo.sh`, `email.sh`, discretionary slots, directive files). LLM-decides / harness-executes keeps guardrails **structural**, not prompt-dependent. Deliberately **not** `Agent`-labeled — igor shouldn't autonomously rewrite its own tick loop; this is a careful human/PR change.
Author
Collaborator

Board decisions + reconciliation (Josh, 2026-06-24) — read before implementing

Reviewed against the binding review that shipped today (which this spec was written
as if didn't exist). The four board calls + the corrections:

1. CEO's role on PRs → strategy only, NOT code review. The binding review
(do_review_tick, live) already owns code-correctness AND the hand-to-board
(APPROVE → requests Josh). The CEO must NOT post its own approve/request_changes
verdict — that duplicates the reviewer and hits the self-review 422. Per Josh:
"CEOs don't review code, they ask 'how does this help us accomplish goal X?'" So
the CEO weighs in on a PR only as a strategic comment, and consumes the
binding review's APPROVED set
as the digest's "merge-ready awaiting the board."

2. Identity → same igor bot (v1). No distinct CEO user — "zero reason to set
up 100 bots." Fine, because the CEO defers code-review (no self-review 422 to
dodge).

3. Digest → email (reuse SMTP2GO / CEO_RECIPIENTS).

4. Scope → Phase 1 = north-star + digest only. And a reframing from Josh worth
baking in: a digest of existing work is a weak first act — the CEO's foundational
job is to establish/refresh the strategy and surface the board's open questions.

The mandate .agent/ceo.md is the north star. So Phase 1, each run:

  • No mandate / a stub → propose a full strategy draft + the questions the
    board needs to answer (the CEO's signature move).
  • Mandate exists (porksicle) → digest current state against the priorities,
    surface strategic questions, weekly propose mandate refinements.
    All emailed; write-light (the .agent/ceo.md only changes when Josh blesses it —
    the CEO may open the mandate PR, its one allowed authored doc). Bootstrap:
    opt-in on a stub .agent/ceo.md so the CEO's first run can flesh it out.

Settled defaults (not forks):

  • Merge line → the harness hard-blocks any CEO merge/push (structural). Confirmed.
  • Dedup → HTML-comment marker + skip-if-open (matching SEO/maintenance/onboarding),
    not a title hash.

Stale dependency: #226 is already closed (fixed by #227 today). The real
rule it was reaching for still holds: the CEO must read origin/<ref> (git
show / API), never the clone's working tree — those stay arbitrarily stale.

Build order: Phase 1 (north-star + digest, email, write-light) → Phase 2 (queue
grooming: capped + deduped Agent issues) → Phase 3 (strategic PR comment,
reconciled with the binding review). Each human-reviewed; deliberately NOT
Agent-labeled (a new tick-loop pass is a careful change).

## Board decisions + reconciliation (Josh, 2026-06-24) — read before implementing Reviewed against the binding review that shipped today (which this spec was written as if didn't exist). The four board calls + the corrections: **1. CEO's role on PRs → strategy only, NOT code review.** The binding review (`do_review_tick`, live) already owns code-correctness AND the hand-to-board (APPROVE → requests Josh). The CEO must NOT post its own approve/request_changes verdict — that duplicates the reviewer and hits the self-review 422. Per Josh: *"CEOs don't review code, they ask 'how does this help us accomplish goal X?'"* So the CEO weighs in on a PR only as a **strategic comment**, and **consumes the binding review's APPROVED set** as the digest's "merge-ready awaiting the board." **2. Identity → same `igor` bot** (v1). No distinct CEO user — "zero reason to set up 100 bots." Fine, *because* the CEO defers code-review (no self-review 422 to dodge). **3. Digest → email** (reuse SMTP2GO / `CEO_RECIPIENTS`). **4. Scope → Phase 1 = north-star + digest only.** And a reframing from Josh worth baking in: a digest of existing work is a weak first act — **the CEO's foundational job is to establish/refresh the strategy and surface the board's open questions.** The mandate `.agent/ceo.md` *is* the north star. So Phase 1, each run: - **No mandate / a stub** → propose a full strategy draft + the questions the board needs to answer (the CEO's signature move). - **Mandate exists (porksicle)** → digest current state *against the priorities*, surface strategic questions, weekly propose mandate refinements. All emailed; write-light (the `.agent/ceo.md` only changes when Josh blesses it — the CEO may open the mandate PR, its one allowed authored doc). **Bootstrap:** opt-in on a *stub* `.agent/ceo.md` so the CEO's first run can flesh it out. **Settled defaults (not forks):** - Merge line → the harness **hard-blocks** any CEO merge/push (structural). Confirmed. - Dedup → HTML-comment marker + skip-if-open (matching SEO/maintenance/onboarding), not a title hash. **Stale dependency:** `#226` is **already closed** (fixed by `#227` today). The real rule it was reaching for still holds: the CEO must read **`origin/<ref>`** (git show / API), never the clone's working tree — those stay arbitrarily stale. **Build order:** Phase 1 (north-star + digest, email, write-light) → Phase 2 (queue grooming: capped + deduped `Agent` issues) → Phase 3 (strategic PR comment, reconciled with the binding review). Each human-reviewed; deliberately NOT Agent-labeled (a new tick-loop pass is a careful change).
Author
Collaborator

Two corrections from the Porksicle side (2026-06-25), for whoever builds this:

  1. Gate path changed: .agent/ceo.md/CEO.md (repo root). .agent/ is the harness's per-run scratch dir, so worker PRs kept staging the mandate's deletion — three times (porksicle #49 / #52 / #54). Moved it to the root (porksicle PR #55). The CEO slot's opt-in gate and its reads must key off /CEO.md, not .agent/ceo.md.

  2. Drop "CEO posts PR reviews" from this spec. Refined model: a CEO doesn't review code — per-PR QA is the automated review tick's job. The CEO pass should groom/file issues, triage feedback, send the digest, make strategic calls — not review PRs. (The igor-can't-review-its-own-PR identity limit reinforces this.)

**Two corrections from the Porksicle side (2026-06-25), for whoever builds this:** 1. **Gate path changed: `.agent/ceo.md` → `/CEO.md` (repo root).** `.agent/` is the harness's per-run scratch dir, so worker PRs kept staging the mandate's *deletion* — three times (porksicle #49 / #52 / #54). Moved it to the root (porksicle **PR #55**). The CEO slot's **opt-in gate and its reads must key off `/CEO.md`**, not `.agent/ceo.md`. 2. **Drop "CEO posts PR reviews" from this spec.** Refined model: **a CEO doesn't review code** — per-PR QA is the automated review tick's job. The CEO pass should *groom/file issues, triage feedback, send the digest, make strategic calls* — not review PRs. (The `igor`-can't-review-its-own-PR identity limit reinforces this.)
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
joshtronic/igor#238
No description provided.