Workflow
One Muyan Pilot task is one runtime outcome: when X, should Y, actually Z. A task starts as a GitHub Issue and ends as a merged PR (or anai-blocked scene that needs a human). This page covers the complete
automatic chain and the project’s GitHub workflow vocabulary.
The complete chain
State machine (the six delivery states; the review/fix loop stays on the same PR — only its head may advance):ai-blocked with the
concrete scene (command, return code, stdout/stderr, branch, worktree,
session file), and the automatic loop never touches it again — a human
decides the next step.
Rules of the chain:
- One task = one run = one feature branch = one worktree = one PR. The PR number never changes during the review/fix loop; only its head may advance.
- Only the two opened-PR states are picked up automatically:
ai-pr-opened(awaiting review) andai-fix-needed(awaiting the next review session).ai-readyis claimed as new work;ai-blockedis never auto-recovered. - The review/fix loop is bounded (5 rounds). Exhausting rounds with
findings, or a review that cannot be verified, marks the Issue
ai-blocked; the PR, branch and worktree stay intact. - Base advances are absorbed by a plain
git mergeof the latestorigin/<base>on the task branch (conflicts resolved manually), followed by a full test rerun. No force push, no auto conflict resolution, no push of the protected branch. - Git transport (Issue #114): git data operations (fetch, push —
including
.github/workflows/*.yml) go over SSH (git@github.com:owner/repo.git); GitHub API operations (Issue, PR, label, comment, merge) stay on theghtoken. The task worktree inherits the deployment checkout’s singleoriginremote, so a new bootstrap worktree has an SSHgit remote -vby construction; a broken transport fails the pre-start check (no HTTPS fallback).
Delivery labels
GitHub labels are the external state machine. They are not created by commits — initialize them per repository (see Getting started):Run marker and run_id
Every task attempt generates onerun_id (8 hex chars, e.g.
e07383c2) and reuses it for every step of the attempt; a retry of the
same Issue generates a new one. The same id appears in:
- every journal line of the attempt (prefix
[e07383c2]); - the Issue/PR comments: visible field
run_id=e07383c2plus the hidden machine-readable marker<!-- muyan-pilot:run=e07383c2 -->; - the feature branch and worktree name
(
.worktrees/<...>-issue-<n>-e07383c2); - the PR body — the stable marker
<!-- muyan-pilot:run=e07383c2 -->is part of the PR contract; the Runner rejects a PR without it.
PR body contract: Fixes #N
The PR description must contain Fixes #<issue-number> (it may be on the
first line). GitHub reads the body (not the PR title) and closes the Issue
natively when the PR merges into the default branch. The Runner verifies
the keyword during PR acceptance and fails fast when it is missing.
Epics, Release tasks and P0 priority
The project organizes multi-task work with plain GitHub primitives — no DAG, no priority numbers, no separate queues:- Epic: a coordination Issue that groups a set of related tasks (for
example the v0.1 release checklist). It is marked with the
ai-epiclabel. An Epic is not itself a directly executable task: the actual work is split into independentai-readyIssues, each with one runtime outcome, one PR, one review and one merge. The Epic is closed only after its sub-Issues are done and the release evidence exists on the remote (tag, merged PRs) — typically with a finalFixes #<epic>commit/PR. - Release task: a regular
ai-readyIssue whose job is release reconciliation — checking that the sub-Issues are merged, the version tag exists on the remote, and the release evidence is complete. Like any task it delivers through one PR (or closes the Epic when the evidence is already on the remote). - P0 priority: the plain
p0GitHub label marks an urgent Issue (a production outage). It is NOT a delivery state — it only orders the ready pickup, never changes the Issue granularity, any delivery state, or the terminal-state semantics, and the Runner never adds or removes it. The ready pickup order is fixed:ai-ready+p0→ai-ready+bug→ plainai-ready(threegh issue listscans sharing the exact same exclusions and blockedBy semantics). P0 obeys every existing exclusion rule and the single-slot constraint: a blocked P0 is skipped (falling back to the bug/plain scans) and an in-flight P0 is resumed by the restart scan. A failed P0 run entersai-blockedALONE (the claim label is removed; theai-readyresidue is excluded by every ready scan), so no tick re-claims it — no infinite retry. There is no priority number or weighted queue.
blockedBy
relation (gh issue edit N --add-blocked-by M); the Runner reads the
field and skips Issues with open blockers. Depends on #N lines in an
Issue body are not parsed.