diff options
| author | Claudomator Agent <agent@claudomator> | 2026-07-11 01:19:24 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator> | 2026-07-11 01:19:24 +0000 |
| commit | 858756244129fa326af53495561882b51815f4d3 (patch) | |
| tree | 2b87249c4741887a798eafc9d75413855ccfb89b /docs | |
| parent | 6214aa83b0b0b127b9da716e6e8b8290f3e752f0 (diff) | |
docs: add workflow and entry points diagram
Covers all entry points verified against current code:
- REST API (POST /api/tasks + /run), chatbot MCP (submit_task,
create_story, accept_story), GitHub webhook, Story REST API,
agent spawn_subtask (parent_task_id vs depends_on/role paths),
propose_epic, propose_role_config, StoryOrchestrator's internal
spawning (ensureEvaluators/ensureArbitration/ensureFixAttempt/
spawnNestedFixAttempt/processRetro), Scheduler retry/escalation
and ask_user-timeout escalation, claudomator run <file> CLI.
Includes task state machine diagram and recursive arbitrated-review
cycle showing that builder-role READY means awaiting arbitration,
not done. All diagrams use Mermaid (renders on GitHub).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/workflow-and-entry-points.md | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/docs/workflow-and-entry-points.md b/docs/workflow-and-entry-points.md new file mode 100644 index 0000000..d9312c5 --- /dev/null +++ b/docs/workflow-and-entry-points.md @@ -0,0 +1,273 @@ +# Claudomator: Workflow & Entry Points + +This document diagrams Claudomator's overall workflow, all entry points into the +task-execution pipeline, the task state machine, and where the recursive +arbitrated-review mechanism sits relative to that state machine. + +--- + +## 1. Entry Points Overview + +Every arrow below eventually reaches `executor.Pool`, the single choke-point +that bounds concurrency and gates spend. + +```mermaid +flowchart TD + subgraph CLI["CLI (claudomator run)"] + CLI_RUN["claudomator run <file.yaml>\n(internal/cli/run.go)"] + end + + subgraph REST["REST API"] + API_CREATE["POST /api/tasks\n(creates PENDING task)"] + API_RUN["POST /api/tasks/{id}/run\n(submits PENDING → QUEUED)"] + API_CREATE --> API_RUN + end + + subgraph CHATBOT["Chatbot MCP (/chatbot/mcp)"] + CB_SUBMIT["submit_task tool\n(internal/api/chatbotmcp.go)"] + CB_STORY["create_story tool\n(creates Story row)"] + CB_ACCEPT["accept_story tool\n(REVIEW_READY → DONE)"] + CB_LIST["list_stories / get_story tools\n(read-only)"] + end + + subgraph WEBHOOK["GitHub Webhook"] + WH["POST /api/webhooks/github\ncheck_run / workflow_run failure\n(internal/api/webhook.go)"] + end + + subgraph STORY_API["Story REST API"] + ST_CREATE["POST /api/stories\n(creates Story row)"] + ST_ACCEPT["POST /api/stories/{id}/accept\n(REVIEW_READY → DONE)"] + end + + subgraph AGENT_TOOLS["Agent Back-channel Tools\n(MCP /mcp or native tool-use loop)"] + AG_SPAWN["spawn_subtask\n(no role= → child with parent_task_id)\n(role= → sibling with depends_on)"] + AG_EPIC["propose_epic\n(creates/updates Epic, groups Stories)"] + AG_RC["propose_role_config\n(creates draft role_configs version)"] + end + + subgraph ORCHESTRATOR["StoryOrchestrator (poll, 15 s)\n(internal/scheduler/story_orchestrator.go)"] + SO_EVAL["ensureEvaluators\n(4 Evaluator tasks per builder node)"] + SO_ARB["ensureArbitration\n(1 Arbitration/planner task)"] + SO_FIX["ensureFixAttempt / spawnNestedFixAttempt\n(new builder task after rejection)"] + SO_RETRO["processRetro\n(retro task after story DONE)"] + end + + subgraph SCHEDULER["Scheduler (poll, ~10 s)\n(internal/scheduler/scheduler.go)"] + SC_FAIL["processTask\n(retry / escalate FAILED role-typed tasks)"] + SC_TIMEOUT["tickAskUserTimeouts\n(auto-answer + resume BLOCKED tasks\noutstanding > ask_user_timeout)"] + end + + POOL(["executor.Pool\n(bounded dispatcher)"]) + + %% CLI path + CLI_RUN -->|"Pool.Submit() directly"| POOL + + %% REST path + API_RUN -->|"Pool.Submit()"| POOL + + %% Chatbot MCP paths + CB_SUBMIT -->|"creates task + auto-submits"| POOL + CB_STORY -->|"story row only;\nStoryOrchestrator spawns Builder"| ORCHESTRATOR + CB_ACCEPT -->|"story REVIEW_READY → DONE;\ntriggers processRetro next tick"| ORCHESTRATOR + + %% Webhook path + WH -->|"creates task + auto-runs"| POOL + + %% Story REST paths + ST_CREATE -->|"story row only"| ORCHESTRATOR + ST_ACCEPT -->|"REVIEW_READY → DONE"| ORCHESTRATOR + + %% Agent tool paths + AG_SPAWN -->|"Pool.Submit() for new child/sibling"| POOL + AG_EPIC -->|"storage only\n(no task spawned)"| POOL + AG_RC -->|"storage only\n(no task spawned)"| POOL + + %% StoryOrchestrator → Pool + SO_EVAL -->|"Store.CreateTask + Pool.Submit()"| POOL + SO_ARB -->|"Store.CreateTask + Pool.Submit()"| POOL + SO_FIX -->|"Store.CreateTask + Pool.Submit()"| POOL + SO_RETRO -->|"Store.CreateTask + Pool.Submit()"| POOL + + %% Scheduler → Pool + SC_FAIL -->|"Pool.Submit() or Pool.SubmitResume()"| POOL + SC_TIMEOUT -->|"Pool.SubmitResume()"| POOL + + %% Orchestrator watches Pool results + POOL -->|"task reaches READY/COMPLETED/FAILED;\norchestrator polls"| ORCHESTRATOR + POOL -->|"task reaches FAILED;\nscheduler polls"| SCHEDULER + POOL -->|"task stays BLOCKED;\nscheduler polls"| SCHEDULER +``` + +--- + +## 2. Detailed Entry Point Reference + +| Entry Point | Code Location | What It Creates | Who Submits to Pool | +|---|---|---|---| +| `claudomator run <file>` | `internal/cli/run.go` | Task(s) from YAML | `cli` directly via `Pool.Submit` | +| `POST /api/tasks` + `POST /api/tasks/{id}/run` | `internal/api/taskops.go` | PENDING task, then QUEUED | `api.Server` via `Pool.Submit` | +| Chatbot MCP `submit_task` | `internal/api/chatbotmcp.go` | Task, immediately submitted | `chatbotmcp` via `Pool.Submit` | +| Chatbot MCP `create_story` | `internal/api/chatbotmcp.go` | Story row | `StoryOrchestrator` (next tick) | +| Chatbot MCP `accept_story` | `internal/api/chatbotmcp.go` | Story status DONE | `StoryOrchestrator` (triggers retro) | +| `POST /api/stories` | `internal/api/stories.go` | Story row | `StoryOrchestrator` (next tick) | +| `POST /api/stories/{id}/accept` | `internal/api/stories.go` | Story status DONE | `StoryOrchestrator` (triggers retro) | +| `POST /api/webhooks/github` | `internal/api/webhook.go` | Task auto-tagged `["ci","auto"]` | `api.Server` via `Pool.Submit` | +| Agent `spawn_subtask` (no `role=`) | `internal/executor/agentmcp.go`, `internal/agentloop/tools.go` | Child task with `parent_task_id` | `Pool.Submit` | +| Agent `spawn_subtask` (with `role=`) | same | Sibling task with `depends_on`, `Agent.Role` set, no Type/Model | `Pool.Submit`; role resolved at dispatch | +| Agent `propose_epic` | same | Epic row; groups existing stories | storage only, no task | +| Agent `propose_role_config` | same | Draft `role_configs` version | storage only, no task | +| `StoryOrchestrator.ensureEvaluators` | `internal/scheduler/story_orchestrator.go` | 4 Evaluator tasks per builder node | `Store.CreateTask` + `Pool.Submit` | +| `StoryOrchestrator.ensureArbitration` | same | 1 `planner`-role Arbitration task | same | +| `StoryOrchestrator.ensureFixAttempt` | same | New root builder task after rejection | same | +| `StoryOrchestrator.spawnNestedFixAttempt` | same | New nested builder task after rejection | same | +| `StoryOrchestrator.processRetro` | same | `retro`-role task after story DONE | same | +| `Scheduler.processTask` | `internal/scheduler/scheduler.go` | Re-queues FAILED role-typed task at same or next tier | `Pool.Submit` / `Pool.SubmitResume` | +| `Scheduler.tickAskUserTimeouts` | same | System-authored fallback answer; resumes BLOCKED task | `Pool.SubmitResume` | + +--- + +## 3. Task State Machine + +```mermaid +stateDiagram-v2 + [*] --> PENDING : task created + + PENDING --> QUEUED : /run submitted\n(Pool.Submit) + + QUEUED --> RUNNING : dispatcher picks slot + + RUNNING --> READY : execution succeeded\n(exit 0) + RUNNING --> BLOCKED : agent called ask_user\nor subtasks outstanding + RUNNING --> FAILED : execution failed\n(non-zero exit / error) + RUNNING --> TIMED_OUT : timeout exceeded + RUNNING --> CANCELLED : /cancel called + RUNNING --> BUDGET_EXCEEDED : spend cap hit + + BLOCKED --> QUEUED : answer provided\n(POST /answer or\nscheduler timeout) + BLOCKED --> READY : all subtasks COMPLETED\n(maybeUnblockParent) + + READY --> COMPLETED : human/chatbot accept\n(non-builder-role)\nOR\nfinalizeArbitration approves\n(builder-role) + READY --> PENDING : human/chatbot reject + + FAILED --> QUEUED : /resume or\nScheduler retry/escalate + TIMED_OUT --> QUEUED : /resume + CANCELLED --> QUEUED : /resume + BUDGET_EXCEEDED --> QUEUED : /resume + + COMPLETED --> [*] +``` + +### Key: READY does not mean "done" for builder-role tasks + +For **non**-builder-role tasks (the common case), `READY → COMPLETED` is a +simple human or chatbot accept via `POST /api/tasks/{id}/accept`. + +For **builder**-role tasks inside a story pipeline, `READY` means +**"awaiting arbitration"** — the `StoryOrchestrator` intercepts every `READY` +builder node and drives it through the review cycle before ever promoting it to +`COMPLETED`. A builder task at `READY` without a completed arbitration is +deliberately left there until the review is done. + +``` +builder READY ──► ensureEvaluators spawns 4 tasks + │ + ▼ + 4 Evaluators → COMPLETED (auto-accepted) + │ + ▼ + ensureArbitration spawns 1 planner task + │ + ▼ + Arbitration → COMPLETED (auto-accepted) + │ + ┌───────────┴───────────┐ + │ APPROVED │ REJECTED + ▼ ▼ + builder READY builder stays READY (superseded) + → COMPLETED new fix-attempt task spawned + (story → REVIEW_READY (story → NEEDS_FIX → IN_PROGRESS) + if root node) +``` + +--- + +## 4. Recursive Arbitrated-Review Cycle + +The review mechanism is **depth-agnostic**: it applies identically at the root +and at any nested depth. `processStory` walks the entire task tree every tick +and applies `processBuilderNode` to every `READY` builder-role node found. + +```mermaid +flowchart TD + subgraph Story["Story (IN_PROGRESS)"] + ROOT["root_task_id\n(Builder, READY)"] + end + + subgraph ReviewCycle["Review Cycle (per builder node, any depth)"] + EVAL["4 Evaluator tasks\nevaluator_quality\nevaluator_security\nevaluator_correctness\nevaluator_performance\n\n(depends_on: builder node)"] + ARB["1 Arbitration task\nrole: planner\n\n(depends_on: all 4 evaluators)"] + APPROVE{"Approved?"} + FIX["Fix-attempt task\n(same role, new builder;\ndepends_on: rejected node)"] + end + + subgraph Terminal["Terminal states"] + COMP["builder node → COMPLETED\n(if root: story → REVIEW_READY)"] + HUMAN["POST /api/stories/{id}/accept\n(REVIEW_READY → DONE)"] + RETRO["processRetro: retro-role task\nreflects, proposes role_configs drafts"] + DONE["story → DONE"] + end + + ROOT -->|"StoryOrchestrator\nensureEvaluators"| EVAL + EVAL -->|"all COMPLETED\nensureArbitration"| ARB + ARB -->|"COMPLETED\nfinalizeArbitration reads\nreport_verdict event"| APPROVE + APPROVE -->|"Yes"| COMP + APPROVE -->|"No"| FIX + FIX -->|"next tick: new node at READY\nrecursive review restarts here"| EVAL + COMP --> HUMAN + HUMAN --> DONE + DONE -->|"processRetro"| RETRO + + NOTE["Nested builder nodes (parent_task_id != ''):\nsame cycle, spawnNestedFixAttempt\ninstead of ensureFixAttempt;\nno story-status change at nested level"] + style NOTE fill:#f5f5f5,stroke:#999,color:#333 +``` + +--- + +## 5. executor.Pool Internal Flow + +Once work reaches the pool, the internal path is: + +```mermaid +flowchart LR + SUBMIT["Pool.Submit(task)\nor Pool.SubmitResume(task, exec)"] + QUEUE["workCh buffered channel\n(FIFO, no priority ordering)"] + DISPATCH["dispatch() goroutine\nwaits for semaphore slot\nchecks budget gate\nchecks depends_on satisfied"] + RUNNER["ContainerRunner\n(Docker; git clone; claude -p)\nor LocalRunner (OpenAI-compatible HTTP)"] + RESULT["handleRunResult\nwrite execution row\nupdate task state\nbroadcast WebSocket event\ncascade-fail dependents if terminal-fail"] + + SUBMIT --> QUEUE --> DISPATCH --> RUNNER --> RESULT + RESULT -->|"READY or COMPLETED"| DONE2(["task observable\nvia WS / REST"]) + RESULT -->|"BLOCKED"| BLOCKED2(["workspace preserved\nin sandbox_dir;\nawait answer"]) + RESULT -->|"FAILED etc."| FAILED2(["Scheduler picks up\non next poll tick"]) +``` + +--- + +## Notes + +- **`propose_epic` / `propose_role_config`** are agent tools that write to + storage only — they do not create tasks or trigger any further scheduling. + They exist to let a running agent record planning-layer artifacts (epic + groupings; improved role system prompts for human review) as a side-effect of + its execution. + +- **`story.RootTaskID` is immutable** after story creation. When a builder node + is rejected and replaced, `task.CurrentAttempt(store, anchorID)` walks the + `depends_on` chain forward to find the current live node. Every orchestrator + caller resolves through this before acting. + +- **Scheduler vs. StoryOrchestrator**: the `Scheduler` handles generic + role-typed task lifecycle (FAILED → retry/escalate; BLOCKED → timeout + resume). The `StoryOrchestrator` handles story-specific pipeline orchestration + (Builder → Evaluators → Arbitration). They run in the same process but poll + independently and write to separate concerns. |
