diff --git a/README.md b/README.md index 2f46ec7..ac16254 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ live in a small per-project profile file ## What's in the box -Seven skills, each with the agents it primarily dispatches: +Eight skills, each with the agents it primarily dispatches: | Skill | Trigger | Output | Mandatory? | |-------|---------|--------|------------| @@ -25,6 +25,7 @@ Seven skills, each with the agents it primarily dispatches: | `debug` | Bug observed | RED test in working tree + cause analysis | Mandatory RED-first for any bug | | `fieldtest` | Orchestrator-dispatched post-audit, surface-touching cycle | example fixtures + friction spec | Optional | | `docwriter` | API surface stable across N cycles | rustdoc / docstring sweep | Optional | +| `boss` | User types `/boss` | autonomous-orchestrator session — dispatches the other skills until done-state or bounce-back | User-invoked, never auto-dispatched | Vocabulary is configurable. AILang calls a cycle a *milestone* and a sub-cycle an *iteration*; your project may call them diff --git a/docs/design.md b/docs/design.md index 829d53d..fdf9075 100644 --- a/docs/design.md +++ b/docs/design.md @@ -108,13 +108,9 @@ The relationship: ## What's out of scope for this plugin -- **Boss/autonomous-orchestrator mode**: AILang's `boss` skill - is gated to a user-invoked `/boss` for autonomous queue - execution. Whether that generalises across projects is - unclear (queue mechanics depend on issue-tracker semantics). - Not in the initial skeleton; possibly added later as - `skills/boss/` if a cross-project autonomous mode crystallises. - **Issue-tracker integration**: the plugin can read the - profile's `issue_tracker.kind` and adjust commit-marker - suggestions, but does not directly call Gitea / GitHub / - Linear APIs. + profile's `issue_tracker.kind` and `issue_tracker.list_cmd` + and invoke the configured listing command, but does not + directly call Gitea / GitHub / Linear APIs. The `boss` skill + uses the configured shell command for queue reads, so the + plugin remains transport-agnostic. diff --git a/docs/profile-schema.md b/docs/profile-schema.md index ba31cf8..24709ce 100644 --- a/docs/profile-schema.md +++ b/docs/profile-schema.md @@ -82,8 +82,16 @@ read as opaque strings the agent should fetch / execute, e.g. | `only_orchestrator_commits` | bool | `true` | If true, no agent commits; the orchestrator commits. | | `issue_tracker.kind` | enum | `none` | One of `gitea`, `github`, `linear`, `none`. | | `issue_tracker.close_marker` | string | `"closes #N"` | Marker the orchestrator includes in commit bodies to auto-close issues. | +| `issue_tracker.url` | string | (empty) | Human-browsable URL of the issue list — surfaced in notifications and cross-references. | +| `issue_tracker.list_cmd` | string | (empty) | Shell command that lists open issues. Used by the `boss` skill to read the forward queue. Examples: `tea issues ls --repo X/Y --state open`, `gh issue list --repo X/Y --state open`. | | `protected_branches` | list | `[main]` | Branches that are sacrosanct in the same sense as main. | +## `notifications` + +| Key | Type | Default | Description | +|-----------|--------|---------|-------------| +| `command` | string | (empty) | Shell command the `boss` skill invokes on done-state and bounce-back. Receives the message text as a single argument. Example: `"~/.claude/notify.sh"`. If empty, the orchestrator falls back to printing the notification in chat. | + ## `pipeline` Per-phase configuration. Each phase has its own sub-map. @@ -106,6 +114,8 @@ pipeline: debug: trigger: bug # observable misbehaviour red_first: true # RED test before any fix + boss: + user_invoked: true # autonomous-orchestrator mode, /boss ``` Phases not listed are disabled for the project. A project that diff --git a/skills/README.md b/skills/README.md index fabc08c..5867930 100644 --- a/skills/README.md +++ b/skills/README.md @@ -6,16 +6,17 @@ live with their dispatching skill, not in a parallel top-level directory — this is what makes the "no orphan agents" rule structurally true rather than only documented. -The skeleton commit ships this directory empty. The actual -skill bodies and agents are migrated from AILang's in-tree -`~/dev/ailang/skills/` and generalised against the profile -schema (`../docs/profile-schema.md`) and the agent template -(`../docs/agent-template.md`) in **iteration 1**. +Migration from AILang's in-tree `~/dev/ailang/skills/` runs in +iteration 1. The `boss` skill is the migration pilot +(landed); the remaining seven skills follow once the pattern +is approved. Expected layout after migration: ``` skills/ +├── boss/ (pilot: landed) +│ └── SKILL.md ├── brainstorm/ │ ├── SKILL.md │ └── agents/ @@ -51,6 +52,9 @@ skills/ └── docwriter.md ``` +`boss` has no agents — it is itself the dispatcher of the +others, not a dispatcher-of-subagents. + `install.sh` symlinks each skill's directory into `~/.claude/skills/` and each skill's `agents/` subdirectory into `~/.claude/agents/`, so Claude Code's diff --git a/skills/boss/SKILL.md b/skills/boss/SKILL.md new file mode 100644 index 0000000..a85c625 --- /dev/null +++ b/skills/boss/SKILL.md @@ -0,0 +1,287 @@ +--- +name: boss +description: User-invoked only (typed as `/boss`). Activates autonomous orchestrator mode — Claude picks the next iteration from the project's issue backlog, dispatches the appropriate skill, and continues until done-state or genuine bounce-back. Outside `/boss`, the default is interactive collaboration with the user, not autonomous queue execution. +--- + +# boss — autonomous orchestrator mode + +> **Violating the letter of these rules is violating the spirit.** + +## Overview + +Autonomous orchestrator mode is how the orchestrator moves a +project forward when the user is away. Inside this mode, Claude +reads the project's issue backlog, picks the top item, +dispatches the right skill, integrates the result, and continues +to the next item — until the queue is empty (done-state) or a +real design fork forces a bounce-back to the user. + +Outside this mode, the default is interactive collaboration: +the user asks; Claude answers or executes the specific request; +Claude stops. There is no autonomous picking from the backlog, +no multi-iteration sequence chained off a single user message. + +This skill exists to make the mode-switch explicit. Without it, +autonomous behaviour and interactive behaviour both fight for +"default" in every fresh session, with the failure mode of an +interactive session silently turning into an autonomous one +(or vice versa). + +## When to Use / Skipping + +Trigger: + +- The user explicitly types `/boss` in chat. + +Never auto-invoked. Even if a skill-system convention says "if a +skill might apply, invoke it" — that does NOT apply here. +`/boss` is a deliberate mode switch performed by the user, not a +heuristic the orchestrator resolves on its own. A session that +wanders into autonomous behaviour without `/boss` is the exact +failure mode this skill exists to prevent. + +May be exited mid-session by: + +- The user signalling "stop", "pause", or "back to chat". +- A bounce-back trigger firing (see "Direction freedom" below). +- Done-state (the queue is empty and the done-state procedure has + fired). + +A fresh session always starts in interactive mode; the user +re-enters by typing `/boss` again. + +## The Iron Law + +``` +NO AUTONOMOUS DISPATCH OUTSIDE A `/boss` SESSION. +DONE-STATE = QUEUE EMPTY, NOT SUB-GOAL COMPLETE. +BOUNCE-BACK ONLY ON THE FOUR NAMED TRIGGERS. +A NEW CYCLE NEVER STARTS AUTONOMOUSLY — IT IS A BOUNCE-BACK. +``` + +Universal rules (only-orchestrator-commits, main-is-sacrosanct, +language, RED-first for bugs) come from the project's `CLAUDE.md` +and apply regardless of mode. This skill does not restate them. + +## The Process + +### Step 1 — Read the queue + +The project's issue tracker is the forward queue. Read open +issues via the command configured under +`git.issue_tracker.list_cmd` in the project profile. If that +slot is empty, the project does not have an autonomously +addressable queue — bounce back to the user with that diagnosis. + +If the entire open backlog is empty: skip to Step 5 +(done-state). Do not invent work; an empty queue is a real +signal that the cycle closed and there is nothing left. + +### Step 2 — Pick the top item per direction-freedom rules + +See "Direction freedom" below. The rules permit picking +autonomously when the path is clear. If the top item is a real +fork (two substantive options with no clear default), bounce +back to the user. + +### Step 3 — Dispatch the appropriate downstream skill + +Pipeline: + +``` +brainstorm → planner → implement → audit → fieldtest + + + debug (bug-triggered) + + + docwriter (post-stability) +``` + +Match the chosen item to the right entry skill. If a cycle is +new and no spec exists: **bounce back first** (per §"Direction +freedom" trigger 4 — new cycles never start autonomously); only +after the user green-lights the cycle (and the session shape) +does `brainstorm` get dispatched. If a spec exists but no plan: +`planner`. If a plan exists: `implement`. If a cycle is closing: +`audit`. If the iteration is a bug-fix: `debug`. Read each +skill's `SKILL.md` trigger section if unsure. + +If the working tree is mid-flight (uncommitted changes left over +from a previous session): inspect first, then resume the right +step. Do not restart from scratch. + +### Step 4 — Loop, deciding on each agent output + +For each dispatched skill's result, decide: + +- **Continue** — agent output is good, next item is obvious, + the queue is non-empty. Most common case. +- **Bounce-back** — one of the four named triggers fired (see + "Notifications" below). Send a notify, stop the loop. +- **Done-state** — queue is empty AND the cycle is fully + ratified (audit clean, fieldtest clean if applicable). Go to + Step 5. + +### Step 5 — On done-state: Notify + +Run the procedure named in "Done-state notifications" below. + +## Direction freedom + +The orchestrator has authority to choose the next iteration, +refactor, or feature without asking. Wrong calls are contained, +not recovered: only the orchestrator commits, and only when the +working-tree state is consistent — so bad work doesn't reach +main in the first place. If a dispatched agent's output is +wrong, the remedy is `git checkout -- ` on the working +tree (orchestrator-side), never a rewind of main HEAD. main is +forward-only. + +The cost of asking "what should I do next" — context-switch for +the user, latency on my side — exceeds the expected cost of +discarding a bad working-tree state. So when the queue is +non-empty and the path is clear, just pick and proceed. + +Bounce back to the user only when: + +- A queued option requires a real design judgement I have not + made myself (genuine architectural fork, multiple substantive + options none of which is clearly default). +- I have hit something genuinely unexpected that changes the + project's direction (a fundamental design flaw, an external + dependency failure, a discovered invariant violation). +- The user has explicitly asked for a checkpoint. +- **The next item on the queue is a new cycle** — i.e. a top- + level work container (in the project's vocabulary: milestone, + epic, release, sprint root) that has no spec file yet and + would require dispatching `brainstorm` to even begin. + Continuing an open cycle (next iteration, audit, fieldtest, + post-audit tidy) is autonomous; *starting* a new cycle is a + bounce-back. + + **Why this is different from the other three triggers.** + Cross-cycle work is high-context work. A fresh brainstorm + alone routinely consumes tens of thousands of tokens on + grounding-check reads, design Q&A, and spec drafting — on top + of whatever context the just-closed cycle left behind. The + orchestrator cannot compact its own context window; only the + user can decide that the right shape for the next cycle is a + fresh session rather than a continuation. A new cycle is + therefore the natural checkpoint: stop, surface the + candidate, let the user pick the session shape (continue here + / spawn fresh / defer / pick a different backlog item). + +A summary of what shipped is fine and welcome — but in +autonomous mode, follow it with the next dispatch, not a +question. + +## Notifications + +Two states call for a notify; nothing else does: + +1. **Done-state.** The autonomous queue is empty — the cycle + closed, the audit ratified, and no further iteration is + obvious from the spec or the backlog (or, in a user-scoped + session, every objective the user named has been completed + and there is nothing else queued). A finished sub-goal + inside a still-open cycle is *not* done-state. If the next + iteration is obvious and substantive, keep dispatching — + "test suite landed", "audit clean before close-fixes", + "iter N done with N+1 in scope" are internal progress, not + notify events. + +2. **Problem-state.** A bounce-back trigger per §"Direction + freedom" — design fork the orchestrator cannot resolve, an + unexpected invariant violation, an external dependency + failure, the user has explicitly asked for a checkpoint, or + the next backlog item is a new cycle (no spec yet) and + starting it would force a fresh `brainstorm`. + +A user-specified stop trigger ("until X is done, then notify") +counts as done-state only if X is truly the last actionable +item in the autonomous queue at that moment. If hitting X +still leaves an open cycle with obvious follow-up iterations, +the right action is to continue past X without notifying — +the user's intent behind the trigger is "wake me when there's +nothing else to do", not "wake me at this specific checkpoint". + +Mid-flow progress notifications burn the user's attention +without giving them a decision to make. When in doubt, +continue. + +The notification command is configured under +`notifications.command` in the project profile and receives the +message text as a single argument. If the profile does not +configure one, fall back to printing the notification in chat. + +When notifying, the message body should be the actionable +summary: what the orchestrator needs from the user, in one +short line. Skip the "hi, I" framing — just the gist. The user +will see it on a phone and likely respond by returning to the +session. + +By default, the orchestrator acts autonomous. The notification +is the exception, not the rule. + +## Done-state notifications + +When the trigger is **done-state** (autonomous work has wrapped +up, nothing left to do for the user to react to), send one +text via the configured notification command. + +The text must be written for the **user-as-reader who did not +watch the orchestrator work**: + +- **Language: same as the rest of the repo.** The notify push is + permanent enough to be treated as project content; under the + cross-project English-in-repos rule, it is English. +- **No technical internals.** Don't name crates, function + identifiers, type names, agent names, or iteration codes + (`ct.1.7`, `18a`, etc.). Describe the *change in the project* + — what is now possible, fixed, or visible — not the mechanics. +- **No assumption of context.** The user is on their phone and + has not seen this session. Lead with the result; the *why* + is fine if it fits in one short clause. +- **Length: Telegram-pragmatic.** No hard cap, but a few + sentences, not an essay. If the work covered multiple + unrelated things, bullet them. +- **Tone: factual, not performative.** No celebration, no + "I have successfully…". Just "X works now, Y is fixed, Z is + new". + +**Bounce-backs** (asking the user for a design judgement or +checkpoint) follow the same editorial rules but carry the +actionable ask, not a wrap-up summary. + +## Common Rationalisations + +| Excuse | Reality | +|--------|---------| +| "Iteration just landed cleanly, let me notify so user can see" | Mid-flow notifications burn user attention without giving them a decision to make. Iteration-clean is internal progress, not a notify event. | +| "Sub-goal of the cycle is done, that counts as done-state" | Done-state is queue-empty, not sub-goal-complete. If the next iteration is obvious and substantive, keep dispatching. | +| "Top of the backlog has two equally good options, I'll just pick one" | If both are substantive and the choice is non-obvious, that's a real fork — bounce back. The cost of a wrong pick (working-tree discard) is higher than the cost of one short ping to the user. | +| "User said 'until X then notify', X just completed, notify now" | Check whether X was actually the last actionable thing. If hitting X leaves an open cycle with obvious follow-up, the user's intent was "wake me when there's nothing else to do" — continue. | +| "Notify can name the iteration code — user will figure out what it means" | No. The notify text is for the user-as-reader who did not watch the orchestrator work. Iteration codes, crate names, function identifiers, type names — all internal, all banned. | +| "The previous cycle closed cleanly and the next backlog item is obvious — just dispatch `brainstorm` and keep going" | No. Starting a new cycle is itself a bounce-back. A fresh `brainstorm` is high-context work, and the orchestrator cannot compact its own context — only the user can decide whether the next cycle wants a fresh session or a continuation of this one. The "obviousness" of the candidate is irrelevant; the *cross-cycle hop* is the checkpoint, not the topic choice. | +| "It's the same broad area as the cycle I just closed, that's not really a new cycle" | If there is no spec file for it yet and it would route through `brainstorm` to get one, it IS a new cycle for this rule's purposes. The rule keys on "needs a fresh spec", not on subjective continuity. | + +## Red Flags — STOP + +- About to send a notify on an internal-progress event (test suite landed, audit clean before close-fixes, iter N done with N+1 in scope). +- About to dispatch the next skill without checking that the previous output was actually good. +- About to autonomously dispatch in a session where the user did NOT type `/boss`. +- About to send a notify that names a crate, an iteration code, or an agent. +- About to bounce back at every iteration boundary "just to be safe" — that's reactive deference, not direction freedom. +- About to dispatch `brainstorm` on a backlog issue that does not yet have a spec file, without first bouncing back to the user. New cycles never start autonomously. + +## Cross-references + +- **Skill system index:** `../README.md` — skill table, agent + roster, pipeline diagram. +- **Universal rules:** the project's `CLAUDE.md` — orchestrator + role boundaries, commit discipline, design rationale, + authority over skills, feature acceptance, RED-first for bugs. +- **Queue:** the URL configured under `git.issue_tracker.url` in + the profile; CLI command at `git.issue_tracker.list_cmd`. +- **Downstream skills dispatched:** `../brainstorm`, + `../planner`, `../implement`, `../audit`, `../fieldtest`, + `../debug`, `../docwriter`. diff --git a/templates/project-profile.yml b/templates/project-profile.yml index b142632..37416ae 100644 --- a/templates/project-profile.yml +++ b/templates/project-profile.yml @@ -44,6 +44,11 @@ git: issue_tracker: kind: none # gitea | github | linear | none close_marker: "closes #N" + # url: "" # human-browsable issue list URL + # list_cmd: "" # e.g. "tea issues ls --repo X/Y --state open" + +notifications: + # command: "" # e.g. "~/.claude/notify.sh"; boss falls back to chat if empty pipeline: brainstorm: { gates: [planner] } @@ -51,5 +56,6 @@ pipeline: implement: {} audit: { mandatory_at: cycle_close } debug: { trigger: bug, red_first: true } + # boss: { user_invoked: true } # autonomous orchestrator mode, /boss # fieldtest: { boss_only: true, when: surface_touch } # docwriter: { boss_only: true, when: api_stable_across_n_cycles }