refactor(opencode): make workflow forge-agnostic and read TODO.md from bare repo

Drops all GitHub-specific tooling (gh CLI, draft PR creation) so the
workflow stops at git commit and leaves push/PR/MR to the user.

TODO.md is now expected to be a tracked file on the default branch.
Phase 1 verifies the repo is bare via `git rev-parse --is-bare-repository`,
resolves the default branch from HEAD / init.defaultBranch, and snapshots
TODO.md via `git show "$DEFAULT_BRANCH:TODO.md"` to a tempfile that @pm
reads in Phase 2. Phase 10 updates the live TODO.md inside the worktree
and commits the change separately. The /review command drops its PR
mode for the same reason; @pm documents the read-only-snapshot vs.
live-worktree path distinction.
This commit is contained in:
Harald Hoyer 2026-05-06 15:28:08 +02:00
parent 4ec1561af4
commit 2941faa822
3 changed files with 61 additions and 46 deletions

View file

@ -11,13 +11,22 @@ tools:
bash: false
---
You are a project management assistant. Your sole responsibility is reading and updating a local `TODO.md` file at the project root. You do **not** modify any other file under any circumstances.
You are a project management assistant. Your sole responsibility is reading and updating a `TODO.md` file. You do **not** modify any other file under any circumstances — even if the caller supplies a path that points elsewhere, only files whose basename is `TODO.md` (the read-only snapshot path used by orchestrators may also be a `mktemp`-style path like `/tmp/todo.XXXXXX.md`) are acceptable.
## File Location
The issue tracker lives at `./TODO.md` (relative to the working directory). If the file does not exist when an operation requires it:
- For read/list operations: report "TODO.md not found at <absolute path>" and stop.
- For create operations: create it with the header `# TODO\n\n` and proceed.
The caller supplies the TODO.md path in the prompt as an absolute path. There are two patterns:
1. **Read-only snapshot** — the caller has extracted TODO.md from a git ref (e.g. `git show main:TODO.md`) into a temp file like `/tmp/todo.abc123.md`. Read it but do **not** write to it. If the caller asks for an update, refuse and explain that the snapshot is read-only.
2. **Live worktree path** — the caller passes a path like `/path/to/worktree/TODO.md`. Both reads and writes are allowed.
The caller indicates the mode in the prompt (e.g. "read-only snapshot at ..." vs. "live file at ..."). When the mode is unclear, default to read-only and ask.
If no path is provided, fall back to `./TODO.md` relative to the current working directory. This fallback is for ad-hoc invocations only.
If the file does not exist when an operation requires it:
- For read/list/update operations: report "TODO.md not found at <absolute path>" and stop.
- For create operations: create it with the header `# TODO\n\n` and proceed (only when in live mode).
## TODO.md Schema

View file

@ -1,5 +1,5 @@
---
description: review changes [commit|branch|pr|@plan], defaults to uncommitted
description: review changes [commit|branch|@plan], defaults to uncommitted
subtask: true
---
@ -16,12 +16,11 @@ Classify the input into one of these modes:
| Pattern | Mode |
|---------|------|
| Empty / no arguments | **code:uncommitted** |
| Contains `github.com` or `pull` or is a bare number (e.g. `42`) | **code:pr** |
| Hex string 7-40 chars (e.g. `a1b2c3d`) | **code:commit** |
| File content provided via `@` reference (look for file contents in context) | **plan** |
| Otherwise, treat as branch name | **code:branch** |
Use best judgement when the input is ambiguous.
Use best judgement when the input is ambiguous. The command is forge-agnostic — review remote pull/merge requests by checking out the branch locally and passing the branch name (or by passing the merge-base commit).
---
@ -33,7 +32,6 @@ Run the appropriate git commands to get the diff:
- **code:uncommitted**: `git diff` + `git diff --cached` + `git status --short` (read untracked files too)
- **code:commit**: `git show $ARGUMENTS`
- **code:branch**: `git diff $ARGUMENTS...HEAD`
- **code:pr**: `gh pr view $ARGUMENTS` + `gh pr diff $ARGUMENTS`
Then:
1. Identify all changed files from the diff

View file

@ -1,5 +1,5 @@
---
description: "Fire-and-forget multi-agent workflow: plan, test, implement, PR"
description: "Fire-and-forget multi-agent workflow: plan, test, implement, commit"
agent: build
---
@ -13,21 +13,33 @@ If `$ARGUMENTS` is empty, stop immediately: "Usage: `/workflow <ISSUE-ID>` (e.g.
## Phase 1: Repo Setup
Verify you are at the bare repo root and the environment is ready.
Verify you are in a bare git repo and that the issue tracker exists.
1. Confirm `.bare/` directory exists in the current working directory. If not, stop: "Not at bare repo root. Run from the directory containing `.bare/`."
2. Verify the repo is hosted on GitHub: `git remote get-url origin 2>/dev/null | grep -qE '(github\.com|^git@github\.com:)'`. If the command fails (no `origin` remote, or origin is not on GitHub), stop: "Workflow requires a GitHub remote at `origin` (used by `gh pr create` in Phase 10)."
3. Run `gh auth status`. If auth is expired or missing, stop: "GitHub CLI auth expired. Run `gh auth login` before retrying."
4. Proceed to Phase 2 to get issue context before creating the worktree.
1. Verify the current repository is bare: `git rev-parse --is-bare-repository 2>/dev/null` must output `true`. If not, stop: "Workflow requires a bare git repository (set up with `git clone --bare` or the `.bare/` + `.git` file pattern)."
2. Capture the bare repo root for later worktree creation: `BARE_REPO_ROOT="$(pwd)"`.
3. Determine the default branch (source of TODO.md and base for new worktrees). Resolve in order:
a. `git symbolic-ref --short HEAD` — the bare repo's HEAD
b. `git config init.defaultBranch` — the configured default
c. fall back to `main`
Store as `DEFAULT_BRANCH`.
4. Verify TODO.md exists on the default branch: `git show "$DEFAULT_BRANCH:TODO.md" > /dev/null 2>&1`. If not, stop: "TODO.md not found on `$DEFAULT_BRANCH`. Commit a TODO.md there first — the workflow expects it to be a tracked file."
5. Snapshot TODO.md to a temp file so `@pm` can read it before any worktree exists:
```bash
TODO_READ_PATH="$(mktemp -t todo.XXXXXX.md)"
git show "$DEFAULT_BRANCH:TODO.md" > "$TODO_READ_PATH"
```
Pass `$TODO_READ_PATH` to `@pm` in Phase 2 (read-only context).
6. Proceed to Phase 2.
---
## Phase 2: Issue Context
Use `@pm` to fetch the issue matching `$ARGUMENTS` from `./TODO.md`:
Use `@pm` to fetch the issue matching `$ARGUMENTS` from the snapshot at `$TODO_READ_PATH`:
- Issue title, description, acceptance criteria
- Labels and priority
- Any existing branch name / PR link
- Any existing branch name
If the issue does not exist or `@pm` fails, stop with error.
@ -37,13 +49,13 @@ Derive a branch name: `<issue-id-lowercase>-<slugified-title>` (e.g. `abc-1-add-
## Phase 3: Repo Setup (continued)
From the bare repo root (the directory containing `.bare/`):
From `$BARE_REPO_ROOT`:
1. `git fetch origin`
1. If an `origin` remote is configured, run `git fetch origin` (best-effort; ignore failure if there is no remote).
2. Compute worktree directory: replace all `/` with `-` in the branch name (e.g. `feat/abc-1-foo` becomes `feat-abc-1-foo`)
3. Check if worktree directory already exists. If yes, enter it and verify `git status --porcelain` is empty. If dirty, stop: "Worktree exists but has uncommitted changes. Clean it up first."
4. If worktree does not exist: `git worktree add <dir-name> -b <branch-name> master`
5. Change working directory to the new worktree.
4. If worktree does not exist: `git worktree add <dir-name> -b <branch-name> "$DEFAULT_BRANCH"`
5. Change working directory to the new worktree. From here on, `./TODO.md` in the worktree is the **live, writable** copy that Phase 10 will update.
---
@ -88,7 +100,7 @@ Reviewers should evaluate testability:
3. If verdict is ACCEPTABLE from both (or JUSTIFIED COMPLEXITY from `@simplify`): proceed to Phase 6
4. If BLOCK or NEEDS WORK: revise the plan addressing findings, then re-review
5. **Convergence detection:** if reviewers return the same findings as the previous cycle, stop the loop early
6. If still unresolved after 3 cycles: note unresolved blockers and proceed anyway (they will be documented in the PR)
6. If still unresolved after 3 cycles: note unresolved blockers and proceed anyway (they will be documented in the workflow summary and commit message)
---
@ -184,7 +196,7 @@ Dispatch `@check` and `@simplify` in parallel to review the full implementation
Provide reviewers with:
- The original plan
- The full diff (`git diff master...HEAD`)
- The full diff (`git diff "$DEFAULT_BRANCH"...HEAD`)
- Any decisions or deviations from the plan
**Review loop (max 3 cycles):**
@ -193,57 +205,53 @@ Provide reviewers with:
3. If ACCEPTABLE: proceed to Phase 10
4. If issues found: fix them directly (no need to re-dispatch `@make` for small fixes), then re-review
5. **Convergence detection:** same findings twice = stop loop early
6. If unresolved after 3 cycles: document blockers, proceed to PR anyway
6. If unresolved after 3 cycles: document blockers, proceed to commit anyway
---
## Phase 10: Commit, PR, and Wrap Up
## Phase 10: Commit and Wrap Up
### Commit
- Stage all changes
- Write a conventional commit message summarizing the implementation
The workflow is forge-agnostic. It commits locally and stops. **Do not push, and do not open a pull/merge request** — the user chooses their forge and review workflow manually.
### Commit Code Changes
- Stage code changes (everything except `TODO.md` and `.opencode/workflow-summary.md`, which are committed separately below)
- Write a conventional commit message summarizing the implementation. Reference the TODO.md issue ID in the body (e.g. `Refs: ABC-1`).
- If changes are large/varied, use multiple atomic commits (one per logical unit)
### Draft PR
- `gh pr create --draft --title "<conventional title>" --body "<execution report>"`
- PR body should include:
- Summary of what was implemented
- Reference to TODO.md issue ID
- Acceptance criteria checklist (from issue)
- Files changed with brief descriptions
- TDD summary: X tasks with tests (RED→GREEN), Y tasks NOT_TESTABLE with justifications
- Any test quality escalations and their resolution
- Unresolved blockers (if any from review loops)
- Review cycle outcomes
### TODO Update
- Use `@pm` to update the issue in `./TODO.md`:
- Set the **PR** field to the draft PR URL
- Use `@pm` to update the issue in `./TODO.md` (worktree-local; this is the live, writable copy):
- Set **Branch** to the worktree branch name
- Set **Status** to `In Review`
- Add a comment with the PR link and a one-line summary
- Add a comment with the branch name, latest commit SHA, and a one-line summary
- If acceptance-criteria checkboxes were addressed by the implementation, ask `@pm` to check them off
- Commit the TODO.md change as a separate atomic commit: `chore(todo): update <issue-id> status and progress`
### Local Summary
- Write `.opencode/workflow-summary.md` in the worktree with:
- Run timestamp
- Issue reference and title
- Branch and PR link
- Branch name and final commit SHA(s)
- Summary of implementation
- TDD evidence (RED→GREEN per task, NOT_TESTABLE justifications)
- Review outcomes (plan review + final review verdicts)
- Unresolved items (if any)
- Files changed
- Commit the summary: `chore(workflow): summary for <issue-id>`
### Cleanup
- Remove the temp snapshot from Phase 1: `rm -f "$TODO_READ_PATH"`
---
## Failure Handling
At any phase, if an unrecoverable error occurs:
1. Write `.opencode/workflow-summary.md` with what was completed and what failed
1. Write `.opencode/workflow-summary.md` (in the worktree, if one exists) with what was completed and what failed
2. If any code was written, commit it with message `wip: incomplete workflow run for <issue-id>`
3. If a branch exists with commits, create the draft PR noting it is incomplete
4. Stop execution
3. Leave the branch and worktree intact for the user to inspect — do not push, do not delete
4. If a worktree exists, use `@pm` to add a comment on the issue in `./TODO.md` summarising what failed
5. Remove the temp snapshot if it was created: `rm -f "$TODO_READ_PATH"`
6. Stop execution
**Never hang on interactive prompts.** If any command appears to require input, treat it as a failure and follow the above procedure.