Issue Lifecycle
The issue lifecycle is the fix workflow of the Engineering Harness. It takes an issue in JeffSteinbok/octo from Jeff’s approval through a written plan, an implementation, and a pull request, using GitHub labels to record where each issue is. Octo’s coding agent runs the workflow; Claude Code writes the code; Jeff approves each stage and merges.
⚠️ Nothing merges without Jeff’s approval. Three manual gates are always required:
- Jeff adds
ilc:approvedto start planning- Jeff adds
ilc:plan-approvedto start implementation- Jeff clicks Merge on the PR — there is no auto-merge
How it is triggered
A GitHub webhook sends issue events to Octo’s Gateway. The github-issues hook forwards an event to the coding agent only when an allowlisted GitHub account (Jeff’s, or one other) adds one of three trigger labels to an issue: ilc:approved, ilc:plan-approved or ilc:pr-needs-work. (The older ilc:plan-pending label is still accepted.) Every other event is dropped, so opening an issue does nothing on its own.
The coding agent does not do the work in its own session. For each event it starts a lifecycle subagent, which handles that issue from then on and reports progress in a Discord thread for the issue (#N — <short title>) in the #coding channel. The hook’s final reply goes to Jeff over iMessage.
State machine
stateDiagram-v2
[*] --> Opened : issue opened
Opened --> PlanWorking : Jeff adds ilc:approved\n(ilc:plan-working)
PlanWorking --> PlanComplete : plan posted as an issue comment\n(ilc:plan-complete)
PlanWorking --> NeedsInput : issue too vague to plan\n(ilc:needs-input)
NeedsInput --> PlanWorking : Jeff clarifies
PlanComplete --> Implementing : Jeff adds ilc:plan-approved\n(ilc:impl-working)
Implementing --> NeedsInput : Claude Code fails or can't finish
Implementing --> ImplComplete : Claude Code commits and pushes\n(ilc:impl-complete)
ImplComplete --> PRDraft : lifecycle subagent opens the PR\n(ilc:pr-draft)
PRDraft --> PRReview : PR marked ready for review\n(ilc:pr-review)
PRReview --> Merged : Jeff merges the PR
PRReview --> Implementing : Jeff adds ilc:pr-needs-work
Merged --> [*]
Opened --> [*] : closed without approval
Labels
Every lifecycle label starts with ilc: (issue lifecycle), so the labels group together and don’t collide with other labels. An issue has only one lifecycle label at a time: the subagent always removes the current label before adding the next.
Plan phase
| Label | Set by | Meaning |
|---|---|---|
ilc:approved | Jeff | Start planning |
ilc:plan-working | Octo | Writing the plan |
ilc:plan-complete | Octo | Plan posted on the issue, waiting for Jeff’s review |
ilc:needs-input | Octo or Jeff | Blocked, waiting for information or a decision |
ilc:plan-approved | Jeff | Plan approved, start implementation |
Implementation phase
| Label | Set by | Meaning |
|---|---|---|
ilc:impl-working | Octo | Claude Code is writing, testing and committing the code |
ilc:impl-complete | Octo | Code pushed to the branch, PR not open yet |
PR phase
| Label | Set by | Meaning |
|---|---|---|
ilc:pr-draft | Octo | PR opened, not yet marked ready |
ilc:pr-review | Octo | PR ready for Jeff’s review and merge |
ilc:pr-needs-work | Jeff | PR has review comments, go back to implementation |
Who does what during implementation
The work is split in two:
- Lifecycle subagent (the orchestrator) owns everything around the code: label changes, posts to the issue’s Discord thread, issue comments, and opening and updating the PR. It never writes code itself.
- Claude Code runs through ACP (the Agent Client Protocol, which lets OpenClaw start an external coding agent) with
sessions_spawn({ runtime: "acp", agentId: "claude" }). It works in a separate git worktree on afix/N-short-descriptionbranch, writes the code, runs the tests, commits, and pushes. It doesn’t touch labels, the Discord thread or the PR.
If Claude Code can’t be started, fails, or reports that it couldn’t finish, the subagent does not fall back to writing the code. It sets ilc:needs-input and posts the error to the thread.
The exact handoff is in agents/coding/skills/issue-lifecycle/SKILL.md, under “Code Work — Claude Code via ACP”.
What happens at each step
Jeff adds ilc:approved
- Finds or creates the issue’s Discord thread and posts that work has started
- Replaces
ilc:approvedwithilc:plan-working - Reads the issue and posts a plan as an issue comment: what changes, which files, the approach, and any risks or open questions (if the issue is too vague to plan, sets
ilc:needs-inputinstead) - Replaces
ilc:plan-workingwithilc:plan-complete - Asks Jeff in the thread to review the plan
Jeff adds ilc:plan-approved
- Removes
ilc:plan-approvedandilc:plan-completeand addsilc:impl-working(first, so the same event can’t start the work twice) - Posts to the thread that implementation has started
- Starts Claude Code for each affected repo; it branches from the latest
main, implements the plan, tests, commits and pushes - Replaces
ilc:impl-workingwithilc:impl-complete - Opens a PR from the pushed branch (the PR body includes
Closes JeffSteinbok/octo#N) and replacesilc:impl-completewithilc:pr-draft - When the PR is ready for review, replaces
ilc:pr-draftwithilc:pr-reviewand posts the PR link to the thread
Jeff adds ilc:pr-needs-work
- Replaces
ilc:pr-needs-workwithilc:impl-working - Reads the PR review comments
- Starts Claude Code with those comments; it fixes, tests, commits and pushes to the existing PR branch
- Moves the issue through
ilc:impl-completeback toilc:pr-reviewand tells Jeff in the thread that the PR is ready for another review