Chief

July 6, 2026 Β· View on GitHub

This is the complete engineering spec, written for an AI coding agent (Claude Code) to implement step by step. Rule of execution: ONE STEP = ONE COMMIT. Do not batch steps. Do not skip ahead. Working name: chief. Repo name candidates (check availability): agent-chief / chiefd / cortexd.


0. Product Definition

Chief is the Chief of Staff for all your agents and information sources. Everything flows into it; it thinks for itself; then it does one of three things:

  1. πŸ”” Interrupt the human β€” only when worth it, at the right moment, and arriving with a plan.
  2. πŸ€– Dispatch work to downstream agents β€” and verify results before reporting.
  3. πŸ“š Curate into memory β€” facts and intents that aren't worth mentioning now, waiting to be connected later.

Positioning:

  • Not another agent platform (OpenClaw is the limbs and channels; Chief is the prefrontal cortex).
  • Chief does not build pipes. It defines a standard ingest protocol; any source connects itself.
  • The interrupt decision is always two-axis: content worthiness Γ— scene tolerance.

Hooks:

  • EN: Your agents don't need more power. They need a chief of staff.
  • The "kill 'all clear' reports" feature deserves its own README section β€” every heartbeat user has suffered this.

1. Design Principles (highest authority during implementation)

  1. Time-to-first-wow < 60s. uvx chief demo must work with zero keys / zero config. Reject any design that adds friction to first contact.
  2. Default to not interrupting. Under any uncertainty (low-confidence scene, borderline score), degrade to a gentler route. The trust damage of a false interrupt is asymmetric to a missed one.
  3. Policy is readable and editable. Everything learned distills into POLICY.md. Manual edits take top priority, effective immediately.
  4. Local-first. User model, memory, feedback all live locally (SQLite + markdown). LLM judge is pluggable: local (Ollama) or cloud (DeepSeek / Anthropic / OpenAI).
  5. Dispatch must be verified. An agent claiming "done" is a claim, not a proof. Every dispatch result passes a verifier before it is reported.
  6. Small and sharp. Anything in Β§13 (out of scope) must not appear in code; new ideas go to ROADMAP.md.

2. Architecture

sources/agents ──▢ Ingest ──▢ Brain Loop ──┬─▢ πŸ”” Interrupt (with a plan)
 (webhook/MCP/built-ins)  (triageβ†’associateβ†’decide) β”œβ”€β–Ά πŸ€– Dispatch (verify, then report)
                                β–²                   └─▢ πŸ“š Curate (memory)
                                β”‚                            β”‚
                     Scene Engine + Memory β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ (recalled by future events)
                                β–²
                     Feedback loop (user reactions / dispatch results)

Runtime: one resident process (chief run) = async event loop + scheduled jobs (digest, nightly distillation).

3. Data Models (core/schema.py, all pydantic)

class Event(BaseModel):
    id: str                      # evt_{yyyymmdd}_{hhmm}_{4hex}, generated at ingest
    source: str                  # submitter id, e.g. "flight-watcher"
    topic: str                   # hierarchical, e.g. "travel.flight_change"; unit of learning
    summary: str                 # <= 200 chars
    detail: str | None = None
    suggested_action: str | None = None      # actionability source
    evidence: list[str] = []                 # urls or local paths
    claimed_urgency: Literal["low","medium","high"] | None = None  # advisory only, never trusted
    expires_at: datetime | None = None       # value-decay deadline
    dedup_key: str | None = None             # hash of summary if absent
    received_at: datetime

class Decision(BaseModel):
    event_id: str
    route: Literal["interrupt","digest","dispatch","curate","drop"]
    score: float | None = None
    components: dict[str, float] | None = None  # urgency/relevance/actionability/novelty/confidence
    scene: str
    scene_confidence: float
    cost: float
    matched_rules: list[str] = []
    reason: str                  # one line; goes to audit log and user-facing explanation
    stage: int                   # 1=hard rules, 2=classifier, 3=LLM judge
    dispatch_task_id: str | None = None

class Task(BaseModel):
    id: str
    origin_event_id: str
    goal: str                    # objective for the executor agent
    executor: Literal["claude_code","openclaw","shell","noop"]
    acceptance: str              # natural-language acceptance criteria
    acceptance_cmd: str | None = None   # if present, exit code 0 = pass
    status: Literal["pending","running","done","failed","rejected"] = "pending"
    result_summary: str | None = None
    attempts: int = 0            # max 2; then downgrade to interrupt (ask the human)

class MemoryItem(BaseModel):
    id: str
    origin_event_id: str | None
    text: str                    # one-line fact/intent, e.g. "user wants to watch XX's next SDK release"
    topic: str
    embedding: list[float]
    created_at: datetime
    last_hit_at: datetime | None = None
    hit_count: int = 0
    ttl_days: int = 90           # expired items move to archive table, excluded from association

class SceneState(BaseModel):
    scene: Literal["sleeping","deep_work","meeting","commuting","social","leisure","idle"]
    confidence: float            # < 0.6 = low confidence β†’ downgrade route one level
    signals: dict[str, Any]      # raw provider snapshot, for audit
    at: datetime

Storage: single SQLite file ~/.chief/state.db with tables events, decisions, tasks, memory, memory_archive, feedback, topic_weights, scene_log; plus ~/.chief/POLICY.md and ~/.chief/USER.md (profile summary).

4. Module Specs

4.1 Ingest (ingest/)

  • HTTP webhook: POST /v1/events (Event without id/received_at) β†’ returns Decision. Default port 8787, simple bearer token.
  • MCP server (fastmcp), tools: propose(event) -> Decision, feedback(event_id, signal), digest(now=False), policy(action, text?), stats(days=7).
  • Built-in zero-config sources (ingest/sources/, independent coroutines):
    • github_notifications: via gh api notifications (offered in wizard when gh auth detected), poll 5 min.
    • rss: any RSS url pasted in wizard, poll 30 min.
    • Sources ONLY fetch β†’ convert to Event β†’ submit through the unified entry. No judgment logic inside sources.
  • Normalization at entry: generate id, fill dedup_key, infer missing topic via cheap LLM call (cached).

4.2 Brain Loop (core/brain.py)

For each incoming Event, in order:

  1. Triage: dedup by dedup_key within 24h; merge events with same topic AND embedding cosine > 0.92 within a 10-min window (concat summaries, merge evidence).
  2. Associate: query memory top-3 by event embedding (cosine > 0.78); on hit, inject MemoryItem.text into decision context, update hit stats. A memory hit boosts relevance (see 4.4).
  3. Decide: get SceneState from the scene engine, run the worthiness engine (4.4), produce Decision, route (4.5).
  4. Full audit trail: decisions table + ~/.chief/logs/audit.jsonl.

4.3 Scene Engine (context/)

Provider interface:

class ContextProvider(Protocol):
    name: str
    def sample(self) -> dict[str, Any]: ...

v1 built-in providers (graceful degradation per platform; unavailable β†’ skip):

providersignalsimplementation
clocklocal time, quiet-hours flagpure code
calendarcurrent / next-15-min event typeics url or gcal (optional)
os_focusmacOS Focus / Windows DNDmacOS via defaults/Shortcuts bridge; skip if unreadable
screen_lockscreen locked?per-platform API
activitykeyboard/mouse idle secondsper-platform API
foreground_appapp name only (never content)macOS NSWorkspace; opt-in, default OFF

Inference rules (context/infer.py, pure rules, 30s cache):

sleeping   : quiet hours AND screen locked > 30min                          conf 0.9
meeting    : calendar meeting in progress OR foreground = meeting app       conf 0.85
deep_work  : calendar focus block OR (foreground=IDE AND >25min AND active) conf 0.75
commuting  : v1: calendar "commute" event only                              conf 0.7
social     : DND=personal mode OR weekend evening + mobile active           conf 0.5
leisure    : foreground = entertainment app OR weekend daytime idle         conf 0.6
idle       : fallback                                                       conf 0.4
  • confidence < 0.6 β†’ interrupt auto-degrades to digest (Principle 2).
  • Scene policy table (defaults; overridable in POLICY.md):
sceneinterrupt thresholdmax delivery leveloutside night whitelist
sleeping0.95ring→ digest
meeting0.90silent push
deep_work0.85silent push
commuting0.60ring (voice-friendly summary)
social0.70vibrate
leisure0.50vibrate
idle0.45ring

Delivery levels: terminal print < desktop notification < Telegram silent < Telegram ring.

4.4 Worthiness Engine (core/scorer.py, three stages)

  • Stage 1 β€” hard rules (Β΅s): quiet hours (except night-whitelist topics) β†’ digest; muted topics β†’ drop; dedup β†’ drop; zero-information templates (regex all (good|clear|normal)|nothing (new|to report)|check(ed)? complete + embedding similarity > 0.85 against a canned "empty report" set, both required) β†’ drop; POLICY.md user rules β†’ direct route.
  • Stage 2 β€” cheap classifier (ms): compare against engaged_set / dismissed_set historical vectors. dismissed-sim > 0.88 with no engaged record β†’ drop; engaged-sim > 0.88 β†’ skip judge, route by historical same-class mean; otherwise β†’ stage 3.
  • Stage 3 β€” LLM judge: pluggable backends (judge/: ollama / deepseek / anthropic / openai adapters). System prompt is a stable prefix (prompt-caching friendly):
[system]  (stable, cacheable)
You are the gatekeeper of the user's attention. Your sole duty is to protect it.
Your default answer is "do not disturb".
For each candidate event output JSON:
{"urgency":0-1,"relevance":0-1,"actionability":0-1,"novelty":0-1,"confidence":0-1,
 "dispatchable":true|false,"dispatch_goal":"one-line goal if dispatchable else null",
 "memorize":"one-line fact/intent worth remembering, else null",
 "reason":"one line"}
urgency = does value decay with time; relevance = match to user's goals;
actionability = what the user can do right now; novelty = new info vs recently delivered;
confidence = verifiability of evidence;
dispatchable = is there prep work an agent can complete without the user.
Output JSON only. Exaggeration and flattery are dereliction of duty. Temperature 0.

[context]  (semi-stable, cache per day)
User profile: {USER.md summary}
Recently delivered: {summaries of last-24h interrupt+digest}
Associated memory: {hit MemoryItem.text, or "none"}

[user]  (per call)
Current scene: {scene} (confidence {conf})
Candidate event: {Event JSON}
  • Composition: score = Ξ£(w_topic[dim]Β·comp[dim]) βˆ’ scene_cost; on memory hit, relevance Γ—1.2 (cap 1.0).
  • Routing: score β‰₯ scene threshold β†’ interrupt; 0.40–threshold β†’ digest; < 0.40 with no lasting value β†’ drop; < 0.40 but memorize != null β†’ curate.
  • If dispatchable=true and route ∈ {interrupt, digest}: run dispatch FIRST, merge result into the event, then deliver ("arrive with a plan"). Dispatch timeout 10 min β†’ deliver as-is, never block.

4.5 Three Output Paths

Interrupt (delivery/): deliver at the level allowed by the scene policy. Message template: {summary}\n{plan (if dispatch result)}\n[Do it] [Later] [Mute this kind] β€” the three buttons ARE the feedback capture. v1 channels: terminal, desktop (plyer), Telegram bot.

Dispatch (dispatch/):

  • executor=claude_code: subprocess claude -p "{goal}\nAcceptance: {acceptance}" --output-format json, configurable workdir.
  • executor=openclaw: write into OpenClaw's task-injection interface (implemented inside the skill).
  • executor=shell: whitelisted command templates only (v1 ships read-only/query templates; arbitrary shell is forbidden).
  • Verification: if acceptance_cmd present β†’ run it, exit 0 = pass; else LLM second-opinion ("Does this result satisfy the acceptance criteria? Answer pass/fail + one reason"). fail β†’ retry once β†’ fail again β†’ downgrade to interrupt asking the human.

Curate (memory/): memorize != null β†’ store MemoryItem (local embedding: sentence-transformers/bge-small-en-v1.5; use bge-m3 for mixed Chinese-English, configurable). At digest time, run one batch association pass over the day's digest pool; cross-event combinations become the digest's "Connections" section.

4.6 Feedback & Learning (core/learner.py)

Signal enum for feedback table:

signaltriggereffect
actedtapped [Do it] / clicked linktopic 5-dim weights EMA positive Ξ±=0.2; add to engaged_set
readexpanded > 10sweak positive Ξ±=0.1
promotedigest item "should have pinged me"urgency weight +0.3 (capped)
dismissed_fastswiped away < 30snegative Ξ±=0.2; add to dismissed_set
muted[Mute this kind] / natural languagetopic muted in POLICY.md, effective immediately
timeoutinterrupt with no reaction 24hweak negative Ξ±=0.05
task_ok / task_faildispatch verification resultadjust dispatch propensity for executor+topic
  • Threshold tuning: 7-day interrupt dismissed_fast ratio > 40% β†’ global threshold +0.02/day; < 15% β†’ βˆ’0.01/day; bounded [0.35, 0.95].
  • Nightly distillation (03:00): LLM translates the day's weight changes into one human-readable line appended to POLICY.md, format - {rule} (learned {date}, source: {stats}). Unparseable POLICY lines are ignored with a warning, never crash.
  • Shadow mode: first 7 days (or until 50 feedback samples), every interrupt degrades into the digest, annotated ⚑ would have: interrupted you (score x.xx, scene xx), with βœ“/βœ— quick-grade buttons. Graduation produces a Tact Report.

4.7 Demo Replay Mode (demo/) β€” TOP PRIORITY FEATURE

uvx chief demo: zero dependencies (no keys; judge uses pre-recorded fixture results β€” fully offline). Replays "a day in the life of an engineer" at 1 event / 2s, rich-rendered route + reason per event, ends with the Tact Report.

Fixture demo/day_of_engineer.json, 24 events. Anchor points (fill remaining 14 with realistic noise β€” newsletters, dependency-update notices, calendar reminders β€” keeping dramatic pacing: setup #5 β†’ payoff #19):

#timeeventexpected decisiondramatic beat
107:10heartbeat "all clear"πŸ—‘ dropopening statement: kill empty reports
308:00digest timeπŸ“° digest sent (4 overnight items)waking-up scene
509:30user tells an agent "remember to check XX's next SDK release"πŸ“š curateplant the setup
810:15deep_work; newsletter arrivesπŸ“° digestscene protection
910:40deep_work; CI fails on mainπŸ€– dispatch(claude_code fix) β†’ verify pass β†’ silent push "fixed, PR awaiting review"dispatch + verify + plan
1312:30competitor ships new versionπŸ“° digest
1614:00meeting; flight delayed 2.5hπŸ€– dispatch rebooking lookup first β†’ πŸ”” silent push with 3 optionstwo-axis + arrive with a plan
1916:20RSS: "XX releases SDK 2.0"association hits #5 β†’ πŸ€– dispatch summary β†’ πŸ“° evening digest "Connections"proof of thinking
2119:00another "all clear"πŸ—‘callback
2423:30sleeping; marketing emailπŸ—‘closing

Final report: Today: 24 events in β†’ 14 blocked Β· 6 batched Β· 3 handled (all verified) Β· interrupted you exactly once. Demo exit line: Connect real sources? Run: chief init

4.8 Onboarding (cli/init.py)

uvx chief init interactive wizard (questionary), every question skippable with sensible defaults:

  1. LLM backend (default local if ollama detected; else guide DeepSeek/Anthropic key entry)
  2. Delivery channel (default desktop; Telegram needs bot token β€” link a 30s illustrated guide)
  3. Digest times (default 08:00 / 18:30)
  4. Quiet hours (default 23:00–08:00) + night whitelist topics (default: family, production_incident)
  5. Detect gh auth status β†’ one-click GitHub notifications; ask for RSS url (skippable) Generates ~/.chief/config.toml + initial POLICY.md + USER.md template; then chief run. chief install-service emits launchd/systemd units.

4.9 OpenClaw Skill (skills/openclaw/)

SKILL.md instruction: when heartbeat finds something worth telling the user, it MUST NOT message directly; it calls Chief's MCP propose and obeys the returned Decision. Include a delivery callback script so interrupts ride OpenClaw's existing channels.

5. CLI Surface

chief demo                 # offline replay (Β§4.7)
chief init                 # wizard (Β§4.8)
chief run                  # resident process
chief digest --now
chief status               # scene / queue / today's stats
chief policy [edit|show]
chief report [--days 7]    # Tact Report
chief install-service

6. Repo Layout

chief/
β”œβ”€β”€ README.md            # hook + demo GIF + 60s quickstart + 3-tier showcase links
β”œβ”€β”€ SPEC.md              # this document
β”œβ”€β”€ PROGRESS.md          # step tracking table (see Β§8)
β”œβ”€β”€ ROADMAP.md
β”œβ”€β”€ pyproject.toml       # uv-managed; entry chief=cli.main:app (typer)
β”œβ”€β”€ cli/                 # main.py, init.py
β”œβ”€β”€ core/                # schema.py, brain.py, scorer.py, learner.py, state.py
β”œβ”€β”€ context/             # providers/*.py, infer.py
β”œβ”€β”€ judge/               # base.py, ollama.py, deepseek.py, anthropic.py, openai.py, fixtures.py, prompts.py
β”œβ”€β”€ ingest/              # http.py, mcp_server.py, sources/{github.py, rss.py}
β”œβ”€β”€ dispatch/            # executor.py, acceptance.py
β”œβ”€β”€ memory/              # store.py, associate.py
β”œβ”€β”€ delivery/            # terminal.py, desktop.py, telegram.py
β”œβ”€β”€ demo/                # day_of_engineer.json, runner.py
β”œβ”€β”€ skills/openclaw/     # SKILL.md, hook.py
β”œβ”€β”€ policy/              # POLICY.template.md, USER.template.md
β”œβ”€β”€ tests/
└── docs/                # architecture.md, protocol.md (the ingest protocol, standalone), decisions.md (ADRs)

Stack: Python 3.12 + uv + typer + pydantic + fastmcp + FastAPI (webhook) + aiosqlite + sentence-transformers + rich + questionary + plyer + python-telegram-bot.

7. Execution Rules for the Coding Agent

  1. ONE STEP = ONE COMMIT. Complete a step, make all its tests pass, commit with the exact message given, update PROGRESS.md in the same commit, then move on. Never batch, never skip.
  2. Write the test skeleton for a step BEFORE its implementation. The acceptance criteria of each step ARE its test cases.
  3. On any decision this spec doesn't cover: choose the simpler option and record a one-line ADR in docs/decisions.md.
  4. All LLM prompts live in judge/prompts.py. No prompt strings scattered elsewhere.
  5. Every module docstring's first line cites the spec section it implements (e.g. Implements SPEC Β§4.3).
  6. Anything listed in Β§13 appearing in code is a violation. New ideas β†’ ROADMAP.md.

8. PROGRESS.md Format

Maintain this table; update the row in the same commit that completes the step:

| Step | Title | Status | Commit | Date |
|------|-------|--------|--------|------|
| 1 | Project scaffold | βœ… | abc1234 | 2026-07-06 |
| 2 | Core schemas & storage | ⏳ in progress | | |
| 3 | ... | ⬜ | | |

9. Implementation Steps

Priority order = step order. Phase 1 delivers the offline demo (the wow); Phase 2 makes it real; Phase 3 makes it think and act; Phase 4 ships it.

Phase 1 β€” Brain Trunk & Offline Demo (Steps 1–7)

Step 1 Β· Project scaffold

  • pyproject (uv), typer CLI skeleton with all Β§5 subcommands stubbed, repo layout of Β§6 with empty modules, pytest + ruff configured, GitHub Actions CI (lint + test), PROGRESS.md initialized.
  • Accept: uvx --from . chief --help lists all subcommands; CI green on empty test suite.
  • Commit: chore: project scaffold, CLI skeleton, CI

Step 2 Β· Core schemas & storage

  • All Β§3 pydantic models; aiosqlite state layer creating the 8 tables; audit JSONL writer.
  • Accept: round-trip tests for every model (create β†’ persist β†’ load β†’ equality); db file created at configured path.
  • Commit: feat(core): schemas and sqlite state layer

Step 3 Β· Stage-1 hard rules + POLICY.md parser

  • Quiet hours, muted topics, dedup, zero-information detection (regex + canned-set embedding, both required), POLICY.md user-rule parsing (bad lines ignored with warning).
  • Accept: table-driven tests covering every rule, incl. night-whitelist passthrough and unparseable POLICY lines.
  • Commit: feat(scorer): stage-1 hard rules and policy parser

Step 4 Β· Scene engine (clock + calendar) + inference + policy table

  • Provider protocol; clock & calendar providers; Β§4.3 inference rules with confidence; scene policy table with POLICY.md override; low-confidence downgrade.
  • Accept: frozen-time tests produce expected SceneState for each of the 7 scenes; confidence 0.5 forces interruptβ†’digest downgrade.
  • Commit: feat(context): scene engine with pluggable providers

Step 5 Β· Judge interface + fixture backend + scoring & routing

  • judge/base.py interface; judge/fixtures.py (returns pre-recorded component scores keyed by event id β€” powers the offline demo); Β§4.4 composition, thresholds, routing incl. curate branch and dispatch flagging (dispatch itself stubbed as noop).
  • Accept: routing unit tests for all five routes; memory-hit relevance boost verified with a mocked hit.
  • Commit: feat(judge): scoring composition and routing with fixture backend

Step 6 Β· Demo fixture + replay runner

  • Complete 24-event day_of_engineer.json per Β§4.7 (fill the 14 noise events; keep pacing); rich-rendered replay at 1 event/2s with --fast flag for tests; final Tact Report rendering.
  • Accept: chief demo runs fully offline end-to-end; visual smoke test via --fast.
  • Commit: feat(demo): offline day-of-engineer replay

Step 7 Β· Demo routing regression (full-table)

  • tests/test_demo_routing.py: assert the route of ALL 24 events matches the fixture's expected table; this is the permanent regression harness.
  • Accept: full-table assertion green; intentionally flipping one expected route makes it fail.
  • Commit: test(demo): full-table routing regression

🏁 Phase 1 gate: uvx chief demo delivers the 60-second wow, fully offline.

Phase 2 β€” Real Judge, Delivery, Feedback (Steps 8–13)

Step 8 Β· Real LLM judge backends

  • ollama / deepseek / anthropic / openai adapters behind the Step-5 interface; Β§4.4 prompt in prompts.py with stable-prefix structure; config-driven selection; JSON-mode + retry-on-malformed.
  • Accept: against a live backend (or recorded HTTP cassettes), demo-script routing agreement β‰₯ 20/24; malformed-JSON retry test.
  • Commit: feat(judge): ollama/deepseek/anthropic/openai backends

Step 9 Β· Stage-2 embedding classifier

  • Local embedding model wiring; engaged/dismissed vector sets; Β§4.4 stage-2 shortcuts; triage-merge (Β§4.2 step 1) now using real embeddings.
  • Accept: seeded-set tests for both shortcut paths and pass-through; merge test for near-duplicate events.
  • Commit: feat(scorer): stage-2 similarity classifier and triage merge

Step 10 Β· Delivery: terminal + desktop

  • Delivery-level abstraction per Β§4.3 table; terminal and plyer desktop channels; scene-capped level selection.
  • Accept: level-capping unit tests (meeting caps at silent push); manual smoke on dev machine.
  • Commit: feat(delivery): terminal and desktop channels with level caps

Step 11 Β· Delivery: Telegram + feedback buttons

  • Bot channel; silent vs ring modes; [Do it][Later][Mute this kind] inline buttons wired to feedback capture.
  • Accept: integration test with telegram test double: button callback β†’ correct signal row in feedback table.
  • Commit: feat(delivery): telegram channel with inline feedback

Step 12 Β· Learner: signals, EMA, threshold tuning

  • Full Β§4.6 signal table; EMA weight updates; bounded global threshold tuning; engaged/dismissed set maintenance from signals.
  • Accept: simulated 4Γ— dismissed_fast on one topic measurably lowers its future score; threshold bounds respected under extreme ratios.
  • Commit: feat(learner): feedback signals and weight adaptation

Step 13 Β· Shadow mode + Tact Report

  • 7-day/50-sample shadow gating; digest annotation with would-have decisions and βœ“/βœ— grading; graduation report; chief report.
  • Accept: time-travel test: shadow β†’ feed 50 graded samples β†’ graduates β†’ real interrupts enabled; report renders correct counts.
  • Commit: feat(learner): shadow mode and tact report

🏁 Phase 2 gate: real LLM decisions delivered to a real phone, learning from real reactions.

Phase 3 β€” Dispatch, Memory, Ingest (Steps 14–20)

Step 14 Β· Dispatch executors (claude_code + shell whitelist)

  • Task lifecycle; claude_code subprocess executor; shell whitelist templates (query-only); attempts/downgrade plumbing.
  • Accept: fake-executor lifecycle tests pendingβ†’runningβ†’done/failed; whitelist rejects non-template commands.
  • Commit: feat(dispatch): task lifecycle and executors

Step 15 Β· Dispatch verification

  • acceptance_cmd runner; LLM second-opinion verifier; retry-once-then-ask-human downgrade.
  • Accept: cmd pass/fail paths; LLM-verifier fail β†’ retry β†’ downgrade produces an interrupt asking the human.
  • Commit: feat(dispatch): verification and downgrade

Step 16 Β· Arrive-with-a-plan

  • dispatchable flow: dispatch before delivery, merge result into message, 10-min timeout delivers as-is (never block).
  • Accept: timeout test (mock slow executor) delivers original within deadline; happy path shows plan in message.
  • Commit: feat(brain): plan-attached interrupts

Step 17 Β· Memory: curate + associate

  • MemoryItem store with TTL/archive; brain-loop association (Β§4.2 step 2) with relevance boost; digest-time batch association β†’ "Connections" section.
  • Accept: replay #5β†’#19 chain with real embeddings: curate then hit then Connections entry; TTL expiry excludes archived items.
  • Commit: feat(memory): curation and association

Step 18 Β· Ingest protocol: webhook + MCP

  • FastAPI POST /v1/events with bearer auth; fastmcp server exposing Β§4.1 tools; entry normalization incl. topic inference.
  • Accept: curl round-trip returns valid Decision; MCP tools exercised via client test; missing-topic event gets inferred topic.
  • Commit: feat(ingest): webhook and MCP endpoints

Step 19 Β· Built-in sources: GitHub + RSS

  • gh-notifications and RSS pollers as pure fetchβ†’Event converters.
  • Accept: fixture-fed converter tests produce well-formed Events; poller respects intervals (mock clock).
  • Commit: feat(ingest): zero-config github and rss sources

Step 20 Β· Onboarding wizard + service install

  • Β§4.8 wizard; config.toml generation; install-service units; chief run wiring everything.
  • Accept: scripted wizard run (pexpect) on clean HOME produces working config; fresh-machine path to first real decision < 10 min (manual check, documented).
  • Commit: feat(cli): onboarding wizard and service install

🏁 Phase 3 gate: a stranger can install, connect a real source, and watch Chief think, dispatch, and remember.

Phase 4 β€” Ecosystem & Release (Steps 21–24)

Step 21 Β· Digest polish + nightly distillation

  • Digest with Connections section and shadow annotations; 03:00 distillation job appending human-readable POLICY lines.
  • Accept: distillation test turns a weight-change log into a well-formed POLICY line; digest golden-file test.
  • Commit: feat(digest): connections section and nightly distillation

Step 22 Β· OpenClaw skill

  • SKILL.md + hook per Β§4.9; delivery callback riding OpenClaw channels.
  • Accept: skill lint passes; documented manual test transcript against a local OpenClaw.
  • Commit: feat(skills): openclaw integration

Step 23 Β· Docs + README

  • README (hook, demo GIF placeholder, 60s quickstart, kill-all-clear section, shadow-mode trust story); docs/protocol.md ("How to connect your agent to Chief" β€” the protocol-definer artifact); docs/architecture.md.
  • Accept: README quickstart verified on clean machine < 60s to demo; protocol.md sufficient for a third party to POST a valid event without reading source.
  • Commit: docs: readme, ingest protocol, architecture

Step 24 Β· Release assets

  • Demo GIF generation script (vhs or asciinema+agg), reproducible; version 0.1.0 tag; release checklist (ClawHub submission, awesome-list PRs).
  • Accept: make demo-gif reproduces the README GIF; uvx agent-chief demo works from the published package (test PyPI).
  • Commit: chore(release): v0.1.0 demo assets and checklist

Phase 5 β€” Trust & Distribution (v3.1 amendment, Steps 25–31)

v3.1 execution order interleaves these with the original steps: 8 β†’ 9 β†’ 10 β†’ 11 β†’ 12 β†’ 13 β†’ 25 β†’ 26 β†’ 27 β†’ 28 β†’ 14 β†’ 15 β†’ 16 β†’ 17 β†’ 18 β†’ 19 β†’ 20 β†’ 29 β†’ 30 β†’ 21 β†’ 23 β†’ 31 β†’ 24. Original Step 22 is absorbed by Step 29 (mark "merged into 29" in PROGRESS.md). Hostile reviews after Steps 13, 28, 20, and 31.

Step 25 Β· Golden dataset + eval harness

  • Build eval/golden.jsonl: ~200 labeled events (expand from the demo fixture + synthesize diverse scenes/topics/edge cases), each with expected route and a one-line rationale.
  • Eval runner computes routing agreement rate, bucketed by route / topic / scene. Strictly separate CAPABILITY evals (golden set, improvable, report the number) from REGRESSION evals (the demo 24, must stay 100%, wired into CI).
  • CLI: chief eval [--backend X] β†’ markdown report to eval/reports/.
  • Accept: fixture backend scores 100% on regression; a real backend produces a bucketed agreement report; CI fails if regression < 100%.
  • Commit: feat(eval): golden dataset and evaluation harness

Step 26 Β· Decision trace + cost accounting

  • Every Decision records: per-stage latency, tokens in/out, cached tokens (read from API usage fields), and USD cost via a per-backend price table (model DeepSeek cache-hit vs cache-miss pricing explicitly).
  • CLI: chief trace <event_id> replays the full decision chain (stages, rules matched, scores, prompt version, cost).
  • Tact Report gains a cost dimension: % events reaching LLM, cache hit rate, total judgment cost.
  • Accept: trace renders a complete chain; unit tests for cost math incl. cache-hit/miss price gap; report shows all three metrics.
  • Commit: feat(trace): decision tracing and cost accounting

Step 27 Β· Prompt governance

  • Migrate all prompts in judge/prompts.py to versioned Jinja2 templates (provider-agnostic variables). Prompt version is stamped into every Decision audit record.
  • chief eval --compare <promptV1> <promptV2> produces a diff report: agreement delta + list of flipped samples.
  • Rule (add to CONTRIBUTING.md): no prompt change merges without an eval diff report.
  • Accept: changing one word in a template yields a diff report with flipped samples; version appears in audit log.
  • Commit: feat(judge): versioned prompt templates with eval-gated changes

Step 28 Β· Failure injection + graceful degradation

  • Chaos tests: judge returns malformed JSON, times out, or the backend is fully down.
  • Degradation policy: when no backend is available, fall back to rules-only conservative routing (all borderline events β†’ digest, never interrupt), mark decisions degraded=true in audit, auto-recover when backend returns. chief status shows degradation state.
  • Accept: with backend killed, no crash, all events get conservative routes with degraded flag; recovery test passes.
  • Commit: feat(core): failure injection and graceful degradation

Step 29 Β· Dual skill packaging (absorbs old Step 22)

  • Ship BOTH: an OpenClaw skill (per old Β§4.9) and a Claude Code skill. Add a chief lite mode: judgment-only (stages 1–3 + routing, no learner, no delivery daemon) so the skill form works standalone with minimal setup.
  • Accept: both SKILL.md files lint clean; documented manual test transcript for each host.
  • Commit: feat(skills): claude-code and openclaw skill packaging

Step 30 Β· Upstream integration examples

  • examples/integrations/: two runnable examples showing the ecosystem position "noisy upstream agents β†’ Chief as the judgment layer": (a) a stock-analysis-bot style feed (daily_stock_analysis-like, fixture-driven), (b) a generic webhook template any agent can copy.
  • Each: one runnable script + a README section explaining the flow end-to-end.
  • Accept: both scripts run end-to-end on fixture data producing visible Decisions.
  • Commit: docs(examples): upstream source integrations

Step 31 Β· README v2 β€” quantified first screen

  • Rewrite README: first screen leads with NUMBERS generated from real eval/demo output (interception rate, interrupts/day, % events reaching LLM, cache hit rate, judgment cost) β€” include a script that regenerates every number; then demo GIF placeholder, 60s quickstart.
  • Promote "explainable judgment" to a first-class selling point (reason + components + chief trace for every decision). Keep the kill-all-clear section. Add the skills + integrations sections.
  • Accept: every number in README is reproducible via make readme-metrics.
  • Commit: docs(readme): quantified value proposition

Phase 6 β€” Product Surface (v3.2 amendment, Steps 32–36)

Owner directive (2026-07-06): as an open-source project the concept is clear; as a product for ordinary people three things are missing β€” a real UI, out-of-the-box sources, and a natural feedback mechanism. Β§13 revised accordingly (local-only console; connectors for ingest).

Step 32 Β· Natural feedback β€” "should/shouldn't have interrupted me"

  • Two first-class signals: should_interrupt (this deserved my attention) and should_not_interrupt (this didn't). Learner effects stronger than passive signals; wired through the existing feedback table, MCP feedback tool, webhook POST /v1/feedback, and Telegram buttons.
  • Accept: simulated feedback measurably moves the topic's future score in the right direction on both signals; HTTP + MCP paths covered by tests.
  • Commit: feat(learner): natural feedback signals

Step 33 Β· Local web console

  • Served by chief run (and standalone chief ui) on 127.0.0.1, token-gated, zero build toolchain (one static HTML+JS file shipped in the wheel). Views: today (digest queue + recent decisions with reason/score/cost), history (searchable decisions, per-event trace), rules (POLICY.md view/edit), tasks (pending dispatch approve/reject), sources (connector status), and πŸ‘/πŸ‘Ž natural-feedback buttons on every decision.
  • Accept: endpoint tests for every /api route (auth incl. 401); UI file lints as valid HTML; POLICY.md edits from the UI take effect on the next decision; approve/reject transitions a pending task.
  • Commit: feat(ui): local web console

Step 34 Β· Connector framework + Composio

  • ingest/connectors/ registry (name β†’ adapter). First adapter: Composio β€” HMAC-verified inbound webhook (POST /v1/connectors/composio, v3 envelope: metadata.trigger_slug + data), trigger_slug β†’ topic mapping (GitHub/Gmail/Slack families), summary extraction with graceful fallback. Registry leaves documented slots for future channels (zapier, n8n, MCP-push).
  • Accept: signature verification rejects tampered payloads; GitHub/Gmail/Slack trigger fixtures produce well-formed Events routed by the real pipeline; unknown slugs still ingest with a generic topic.
  • Commit: feat(ingest): connector framework with composio adapter

Step 35 Β· One-click connect

  • chief connect <source> CLI: writes config, prints the exact next actions (Composio dashboard steps / tokens), verifies inbound reachability where possible; chief sources lists connector status. The console's Sources view mirrors it.
  • Accept: chief connect composio --secret X round-trips config and a signed test event; chief sources reflects it.
  • Commit: feat(cli): one-click source connection

Step 36 Β· Product docs + v0.3.0

  • README/zh gain the console screenshot placeholder + connectors section; CHANGELOG 0.3.0; version bump; tag v0.3.0 (release automation from v3.1 does the rest).
  • Accept: release-check green from the wheel incl. chief ui assets; v0.3.0 release live with artifacts.
  • Commit: chore(release): v0.3.0 β€” product surface

Step 37 Β· Preference-learning eval (reward loop)

  • eval/learning.py: a simulated user with hidden per-topic preferences; Chief starts at uniform weights and is corrected only by the Β±1 natural-feedback signal. Measure routing agreement vs the user's truth over rounds β†’ a learning curve. reward = feedback, policy = weighted routing, training = EMA, eval = agreement. No labels, no gradient (SPEC Β§13 holds). chief eval --learning β†’ markdown report. Console gains a Learning view (per-topic learned drift + feedback tally, /api/learning).
  • Accept: agreement rises from <50% to β‰₯95% and is monotonic + deterministic; wanted topics' weights rise and unwanted fall; a no-feedback user causes no learning; CLI + API + console covered.
  • Commit: feat(eval): preference-learning reward loop

10. Config Sample (~/.chief/config.toml)

[llm]      backend = "deepseek"   model = "deepseek-v4-flash"   # or ollama/qwen3-4b
[delivery] channels = ["desktop","telegram"]   telegram_token = ""   chat_id = ""
[digest]   times = ["08:00","18:30"]
[quiet]    hours = "23:00-08:00"   whitelist = ["family","production_incident"]
[dispatch] claude_code_workdir = "~/work"   enabled = true
[context]  foreground_app = false            # privacy-sensitive, default OFF

11. Release Assets Checklist

  1. README hero GIF: three events, three fates (#9, #16, #1) from demo mode.
  2. 2-min video script: "the morning briefing" (record after Step 24).
  3. Two deep-dive posts: the association chain (#5β†’#19 full trace), and dispatch verification ("done is a claim, not a proof").
  4. docs/protocol.md as a standalone artifact β€” the protocol-definer posture.
  5. ClawHub submission + awesome-list PRs.

12. Naming Note

Project name Chief. Before creating the repo, check availability of agent-chief, chiefd, cortexd on GitHub and PyPI; prefer the shortest available. All code, docs, comments, commit messages in English.

13. Explicitly OUT of scope (appearing in code = violation)

Revised by the owner in v3.2 (2026-07-06). Two items were re-scoped, the rest remain absolute:

  • a local web console (served by chief run on 127.0.0.1 only, single user, token-gated) is now IN scope β€” "Web UI" here always meant a hosted multi-user product, which stays forbidden;
  • Slack/Gmail/GitHub as ingest sources (via connectors) are IN scope β€” the ban below is on delivery through chat apps, which stands.
  • Always-on microphone / screen-content understanding / geofencing (keep the provider interface ready; the hardware layer is a future premium provider)
  • Hosted/multi-user UI, accounts, cloud sync, telemetry
  • Arbitrary shell execution, homegrown agent executors
  • Slack / Discord / WeChat delivery (ingest via connectors is allowed)
  • Real-time association (at-ingest lookup + digest-time batch only)
  • Heavy ML (EMA + threshold tuning is enough and stays explainable)