# milestone-workflow: tracks and preflight

## Dependency tracks (step 1)

- List the issues with `--state all` and search for open PRs that close each one. Buckets: **build**; **resume** (open issue with an open PR from a trusted author: run `fix-pr-review-loop` on it, never a fresh build); **skip** (closed). A PR's author is trusted per `work-on-issue` step 0: the invoking user (`gh api user`, type `User`) or an `author_association` of `OWNER`, `MEMBER`, or `COLLABORATOR`, read with `gh api repos/<owner>/<repo>/pulls/<n> --jq '.user.login, .user.type, .author_association'`. Any other open PR that closes the issue is listed in the run plan as **untrusted** with its number and author, and is never checked out, looped, or merged. `work-on-issue` step 0 stops a build on any open PR that fixes the issue, whatever its author, so the run plan never offers a build for that issue: exclude it and its hard descendants as **blocked pending decision (untrusted PR #X)**, and tell the user that a build can start only after a maintainer closes that PR. When that PR is closed before the run plan is approved, re-read the open PRs and put the issue in the build bucket.
- Settle every cross-bucket edge explicitly and list each one in the run plan. Predecessor merged (closed with a merged PR): the edge is satisfied; remove it from the track graph and list it as satisfied. Predecessor in resume: finish its loop first, and with merging on merge it under the `SKILL.md` step 5 criterion; with merging off, a hard edge excludes the dependent and its hard descendants as **blocked pending merge of PR #X**. Predecessor closed without a merged PR: exclude the dependent and its hard descendants as **blocked pending decision** and ask whether to reopen or re-scope.
- Read `**Depends on:**` (hard) and `**Runs after:**` (ordering-only); an explicit `none` is authoritative. For a missing field, infer only that edge kind from the prose and label every inferred edge; flag an unclear kind for plan review.
- Reject missing references, self-references, and cycles across both edge kinds. A reference outside the milestone is not rejected: it resolves like a cross-bucket edge. Closed with a merged PR satisfies it (listed as satisfied); open, or closed without a merged PR, excludes the dependent and its hard descendants as blocked, with the reason named.
- Tracks are `{ issues: [...], after: [<index>...], runsAfter: [<index>...] }`; `after` and `runsAfter` hold 0-based indices into the `tracks` array. The run plan shows each track's 1-based display number beside its index, and the script's log lines use the display number. One `issues` array only when every serial edge is hard; ordering-only chains stay separate tracks joined by `runsAfter`. Unrelated tracks start together; multiple hard predecessors stay separate `after` entries.

## Preflight (step 3)

- `gh auth status` succeeds and `gh api repos/<owner>/<repo> --jq .permissions.push` is `true`.
- Default `reviewMode: 'github'` needs `.github/workflows/claude.yml` (from `templates/claude-workflow/workflows/claude.yml`, `CLAUDE_CODE_OAUTH_TOKEN`, the Claude GitHub App, a `runs-on` you have) and working Actions; `reviewBot: 'codex'` needs a workflow that answers `@codex` by its contents, such as `codex.yml` or the minimal `codex-review.yml`, plus the secrets the `fix-pr-review-loop` preflight lists. Without a bot, pass `reviewMode: 'subagent'` or `reviewLoop: false`. Actions-hosted CI is a separate dependency.
- With a `targetBranch`: validate the name per `work-on-issue` step 1 ("Target"), and `git ls-remote --heads origin "refs/heads/<branch>"` must list exactly one line. A missing or invalid branch stops the run; never create it or fall back to the default branch.
- With merging on, the account must be allowed to squash-merge and delete branches on the target branch; otherwise relax protection or run `merge: false`.
- Every issue stamped on an external CLI, as Build model or Validate model: `command -v codex` plus `codex login status` (Codex), `command -v agent` plus `agent status` (Cursor). A Validate model on the Cursor CLI blocks that issue. A missing binary or signed-out status blocks only that issue at run time; fix it here or restamp through `execution-plan-review`. An issue with no Execution block stops the run: send it through `execution-plan-review`.
- With `skipValidate`: predict each listed issue's outcome with read-only checks and show the predicted fallbacks in the run plan. A listed issue validates when it has an in-run predecessor (a hard `after` or `runsAfter` track, or an earlier issue in its own track), no `[C..]` prefix, or no Execution block; when a session record's `branch` differs from the run's base; or, for footer evidence, when the run has a `targetBranch`, no trusted edit added a `Validated with LLM:` line, that line names a pipeline build harness (`milestone-pipeline`, `Codex`, or `Cursor`) or no harness, or the base branch changed after the issue was created (`gh api "repos/<owner>/<repo>/activity?ref=refs/heads/<base>&per_page=1"`). For a session record, compare the base head (`gh api repos/<owner>/<repo>/branches/<base> --jq .commit.sha`) with `baseSha`, and look for body edits, title renames, or comments after `validatedAt`. Footer evidence cannot show the branch the prior validation traced or the hard constraints it returned: name that limit on every footer issue in the run plan. The run's own skip-check decides; a predicted or real refusal validates normally and never blocks.
- The target repo's CLAUDE.md covers package manager and test commands.
