diff --git a/config/opencode/agents/pm.md b/config/opencode/agents/pm.md index 2382876..ee682f3 100644 --- a/config/opencode/agents/pm.md +++ b/config/opencode/agents/pm.md @@ -114,15 +114,59 @@ Statuses: `Todo`, `In Progress`, `Done`. ## Capabilities You can: -- **View** an issue by ID — read `/.md` and return its fields structured. +- **Validate run prerequisites** — given an issue ID, verify the TODO tracker is well-formed in this worktree (directory + `README.md` present), locate the issue file, and confirm every entry in its `depends-on:` frontmatter resolves to a `Done` issue. Used by `/workflow`'s Phase 2 (per ADR-22) so the orchestrator never constructs a TODO path itself. Returns a structured success or failure response (see "Run-Prerequisite Output" below). +- **View** an issue by ID — read `/.md` and return its fields structured. **Always include the resolved absolute file path** in the response (`issue_file_path` field). - **List** issues, optionally filtered by status / parent / label. Walk `/*.md` (excluding `README.md`), parse frontmatter. -- **Create** an issue. Generate the next ID by scanning existing IDs with the same prefix and incrementing. Default `status: Todo`. Write `/.md`. If the issue is top-level (`parent: null`), update `README.md` to add it under the caller-specified category. If the issue is a sub-issue (`parent: `), update the parent file's `## Sub-issues` section. +- **Create** an issue. Generate the next ID by scanning existing IDs with the same prefix and incrementing. Default `status: Todo`. Write `/.md`. If the issue is top-level (`parent: null`), update `README.md` to add it under the caller-specified category. If the issue is a sub-issue (`parent: `), update the parent file's `## Sub-issues` section. **Return the absolute path of the new issue file** (`new_issue_path`) and the absolute paths of every dependent index updated (`updated_paths`). - **Update status** in frontmatter. When status changes to/from `Done`, propagate the checkbox flip to: - `README.md` if the issue is top-level (`parent: null`), **or** - the parent issue's `## Sub-issues` line if it has a parent. -- **Add a comment** — append `- YYYY-MM-DD — ` to the issue's `## Comments` section (create the section if missing, just before EOF). -- **Check off acceptance criteria** by index or matching text — flip `- [ ]` to `- [x]` under `## Acceptance criteria`. -- **Edit** description or other body sections when explicitly requested. + Return the list of all paths modified by the operation. +- **Add a comment** — append `- YYYY-MM-DD — ` to the issue's `## Comments` section (create the section if missing, just before EOF). Return the modified path. +- **Check off acceptance criteria** by index or matching text — flip `- [ ]` to `- [x]` under `## Acceptance criteria`. Return the modified path. +- **Edit** description or other body sections when explicitly requested. Return the modified path. + +**Path-return rule:** every operation that modifies the filesystem must include the absolute path(s) of every file it touched in its response (`modified_paths` array, or named fields like `new_issue_path` / `updated_paths` for create). Read-only operations (View, List) include `issue_file_path` for the issue they read. The caller (`/workflow`'s orchestrator) deliberately does not construct TODO paths from issue IDs — it relies on these returned paths for staging, commenting, and follow-on dispatches. + +## Run-Prerequisite Output + +The `Validate run prerequisites` capability returns one of two JSON shapes: + +**Success:** +```json +{ + "ok": true, + "issue_file_path": "/abs/path/to/TODO/.md", + "issue": { + "id": "...", + "title": "...", + "status": "Todo | In Progress | Done", + "parent": "... | null", + "labels": ["..."], + "depends_on": ["..."], + "description": "...", + "acceptance_criteria": [{"checked": false, "text": "..."}], + "sub_issues": [{"id": "...", "title": "...", "checked": false}] + } +} +``` + +**Failure:** +```json +{ + "ok": false, + "error_code": "tracker_missing | issue_not_found | dependency_unmet | dependency_missing", + "message": "" +} +``` + +Error code semantics: +- `tracker_missing` — `/` or `/README.md` is absent. +- `issue_not_found` — `/.md` does not exist. +- `dependency_unmet` — the issue exists; one of its `depends-on:` entries is not yet `Done`. Include which dep ID and its current status in `message`. +- `dependency_missing` — the issue exists; one of its `depends-on:` entries refers to an issue that has no file at all. Include which dep ID in `message`. + +Do **not** mutate state on failure — the validator is read-only. You cannot: - Delete issues. If asked, leave the file in place and report — the new schema has no `Cancelled` state, so deletion would lose history. @@ -138,11 +182,13 @@ Single-issue schema: ```json { + "issue_file_path": "/abs/path/to/TODO/GAL-39.md", "id": "GAL-39", "title": "Implement a special stage type", "status": "Done", "parent": "GAL-38", "labels": ["gameplay", "advanced-mechanics"], + "depends_on": ["GAL-37"], "description": "…", "sub_issues": [ { "id": "GAL-40", "title": "…", "checked": true } @@ -157,7 +203,7 @@ Single-issue schema: } ``` -Omit fields whose corresponding sections are absent (`null` is fine for `parent`, but drop `sub_issues`/`acceptance_criteria`/`integration_test_hints`/`comments` entirely if the section isn't in the file). +`issue_file_path` is **always included** for any operation that reads or writes a single issue file (per the path-return rule above). Omit fields whose corresponding sections are absent (`null` is fine for `parent`, drop `depends_on`/`sub_issues`/`acceptance_criteria`/`integration_test_hints`/`comments` entirely if the section/field isn't in the file). For list output, return an array of `{id, title, status, parent, labels}` objects. diff --git a/config/opencode/commands/workflow.md b/config/opencode/commands/workflow.md index b43d02d..67a3208 100644 --- a/config/opencode/commands/workflow.md +++ b/config/opencode/commands/workflow.md @@ -8,11 +8,11 @@ You are executing the multi-agent workflow inside the worktree this opencode ses **Prerequisites (the user handles before launching opencode):** - A git worktree is checked out for the issue's feature branch - `opencode` was launched from the root of that worktree -- A `TODO/` directory is committed to the repo containing per-issue files (`TODO/.md`) plus `TODO/README.md` +- A `TODO/` directory is committed to the repo with a per-issue tracker (schema in `agents/pm.md`) and a `TODO/README.md` index. The orchestrator does not read or construct per-issue paths — `@pm` is the only agent that touches issue files (ADR-22). **Task reference:** $ARGUMENTS -If `$ARGUMENTS` is empty, stop immediately: "Usage: `/workflow [base-branch]` (e.g. `/workflow ABC-1`). The ID must exist as `./TODO/.md`. Base branch defaults to `main` (then `master`)." +If `$ARGUMENTS` is empty, stop immediately: "Usage: `/workflow [base-branch]` (e.g. `/workflow ABC-1`). The ID must already be tracked under `TODO/` (`@pm` validates existence at Phase 2). Base branch defaults to `main` (then `master`)." Parse `$ARGUMENTS`: the first whitespace-separated token is the issue ID, an optional second token overrides the base branch. Store as `ISSUE_ID`. @@ -72,38 +72,73 @@ Define `RUN_DIR="$WORKTREE_PATH/.workflow/run-$ISSUE_ID"` once in Phase 1 and re ## Phase 1: Sanity Check +This phase covers **only** git/worktree-shaped sanity. **TODO-tracker validation (issue file existence, `depends-on` enforcement) is `@pm`'s job and happens at Phase 2 (ADR-22)**. The orchestrator does not construct or read paths under `TODO/` at any point — it dispatches `@pm` and uses whatever path `@pm` returns. + 1. Verify CWD is a non-bare git worktree: `git rev-parse --is-bare-repository 2>/dev/null` must output `false`. If not, stop: "Workflow must be run from a non-bare worktree (the directory opencode was launched in)." 2. Capture the worktree path: `WORKTREE_PATH="$(pwd)"`. -3. Verify the TODO tracker exists: - - `./TODO/` directory must exist. If not, stop: "TODO/ directory not found in the current worktree. Commit a TODO/ folder with one file per issue plus a README.md index." - - `./TODO/README.md` must exist. If not, stop: "TODO/README.md not found. Add the category index file before running the workflow." - - `./TODO/$ISSUE_ID.md` must exist. If not, stop: "Issue file `./TODO/.md` not found for ID parsed from `$ARGUMENTS`." -4. Verify HEAD is not detached: `git symbolic-ref --short HEAD` must succeed. If it fails, stop: "Cannot run on a detached HEAD. Check out a feature branch first." -5. Capture the current branch: `BRANCH_NAME="$(git symbolic-ref --short HEAD)"`. -6. Resolve the base branch (`BASE_BRANCH`): +3. Verify HEAD is not detached: `git symbolic-ref --short HEAD` must succeed. If it fails, stop: "Cannot run on a detached HEAD. Check out a feature branch first." +4. Capture the current branch: `BRANCH_NAME="$(git symbolic-ref --short HEAD)"`. +5. Resolve the base branch (`BASE_BRANCH`): - If `$ARGUMENTS` provided a second token, use it. - Else if `git rev-parse --verify --quiet main` succeeds, use `main`. - Else if `git rev-parse --verify --quiet master` succeeds, use `master`. - Else stop: "Could not determine base branch (no `main` or `master`). Pass it as the second argument: `/workflow `." -7. Verify the current branch is not the base branch: if `BRANCH_NAME == BASE_BRANCH`, stop: "Cannot run workflow on the base branch (`$BASE_BRANCH`). Switch to a feature branch first." -8. **Verify the working tree is clean** (ADR-20): `git status --porcelain` must return empty. If not, stop: "Working tree must be clean. Commit or stash uncommitted changes before running the workflow." -9. **Check `depends-on:` declarations** (ADR-21): if `./TODO/$ISSUE_ID.md`'s frontmatter contains a `depends-on: [, ...]` list, verify every listed dependency's status is `Done` (read each `./TODO/.md`). If any dependency is not `Done`, stop: "Cannot start `$ISSUE_ID`; it depends on `` (status: ``). Complete dependencies first." If a listed dependency file does not exist, stop with: "Cannot start `$ISSUE_ID`; declared dependency `` has no issue file." If the field is absent, proceed. -10. Set the run-artifacts directory: `RUN_DIR="$WORKTREE_PATH/.workflow/run-$ISSUE_ID"`. Phase 3 will `mkdir -p "$RUN_DIR"` before writing the first artifact. -11. Initialize the run-level rework counter: `PLAN_REWORK_REMAINING=1` (per ADR-13). Decrement on every P5.5-BLOCK→P4, P7-escalation-exhaustion→P3, and P8-plan-level→P3 transition. When the counter is `0` and another such transition fires, abort to the Failure Handler instead of re-entering. +6. Verify the current branch is not the base branch: if `BRANCH_NAME == BASE_BRANCH`, stop: "Cannot run workflow on the base branch (`$BASE_BRANCH`). Switch to a feature branch first." +7. **Verify the working tree is clean** (ADR-20): `git status --porcelain` must return empty. If not, stop: "Working tree must be clean. Commit or stash uncommitted changes before running the workflow." +8. Set the run-artifacts directory: `RUN_DIR="$WORKTREE_PATH/.workflow/run-$ISSUE_ID"`. Phase 3 will `mkdir -p "$RUN_DIR"` before writing the first artifact. +9. Initialize the run-level rework counter: `PLAN_REWORK_REMAINING=1` (per ADR-13). Decrement on every P5.5-BLOCK→P4, P7-escalation-exhaustion→P3, and P8-plan-level→P3 transition. When the counter is `0` and another such transition fires, abort to the Failure Handler instead of re-entering. --- ## Phase 2: Issue Context -Dispatch `@pm` against `./TODO/` (pass the absolute `TODO/` directory path) and fetch the issue at `./TODO/.md`: -- Title, description, acceptance criteria (if section present) -- Labels and parent -- Sub-issues list (if the issue is a parent) -- Existing status +Dispatch `@pm` with the issue ID `$ISSUE_ID`, `$WORKTREE_PATH`, and `Validate run prerequisites` as the operation. **The orchestrator does not assume any path under the worktree's `TODO/` tree exists** — it asks `@pm` to: -If the issue file does not exist or `@pm` fails, stop with error. +1. Verify the TODO tracker is well-formed in this worktree (directory + index file present). +2. Locate the issue file for `$ISSUE_ID`. +3. Verify all `depends-on:` entries in the issue's frontmatter resolve to issues with `status: Done` (ADR-21 / ADR-22). +4. Return one of two structured responses: -If the issue's status is `Todo`, ask `@pm` to set it to `In Progress` and propagate the change to the dependent index (`README.md` for top-level issues, the parent's `## Sub-issues` line for sub-issues). The status edit will be staged alongside other TODO updates in Phase 9. +**Success:** +```json +{ + "ok": true, + "issue_file_path": "", + "issue": { + "id": "...", + "title": "...", + "status": "Todo | In Progress | Done", + "parent": "... | null", + "labels": [...], + "depends_on": [...], + "description": "...", + "acceptance_criteria": [{"checked": false, "text": "..."}], + "sub_issues": [...] + } +} +``` + +**Failure:** +```json +{ + "ok": false, + "error_code": "tracker_missing | issue_not_found | dependency_unmet | dependency_missing", + "message": "" +} +``` + +On failure, stop the workflow with `@pm`'s `message` verbatim. Do **not** attempt to inspect or repair the TODO tree from the orchestrator — that belongs to `@pm`. + +On success, capture `ISSUE_FILE_PATH` from the response. **Use this captured path verbatim everywhere downstream** (Phase 9 staging, Failure Handler comments, etc.) — never construct a TODO path from `$ISSUE_ID` directly. + +If `issue.status == "Todo"`, dispatch `@pm` again to flip it to `In Progress` (operation: `Update status`, target: the same issue ID; `@pm` propagates to README.md / parent's `## Sub-issues` line). The status edit will be staged alongside other TODO updates in Phase 9. + +**Forbidden in the orchestrator from this point forward:** +- Reading any file inside the `TODO/` tree directly. +- Constructing a per-issue file path from an issue ID — `@pm` is the only agent that knows the layout. +- Editing or writing any file under `TODO/` — every TODO mutation is a `@pm` dispatch that returns the path of what it touched. + +These rules are enforced by *not telling you the path layout*. The schema lives in `agents/pm.md`; the orchestrator never needs it. --- @@ -472,7 +507,7 @@ The Failure Handler's recovery procedure (ADR-14: discard worktree, delete branc Concretely on `split_needed`: 1. Write a Failure Handler summary noting `@check`'s diagnosis verbatim and the Phase 5 split that was attempted. -2. Dispatch `@pm` to add a comment on `./TODO/$ISSUE_ID.md`: `- YYYY-MM-DD — split_needed at Phase 7 task-1; . Re-run after re-creating the worktree.` +2. Dispatch `@pm` (operation: `Add comment`, issue ID: `$ISSUE_ID`) with the comment text: `- YYYY-MM-DD — split_needed at Phase 7 task-1; . Re-run after re-creating the worktree.` `@pm` resolves the issue file path itself; the orchestrator never constructs it. 3. Stop execution. Do not commit code, do not file new sub-issues, do not stage anything under `.workflow/`. --- @@ -516,15 +551,18 @@ The workflow is forge-agnostic. It commits locally and stops. **Do not push, and ### TODO Update -Dispatch `@pm` against the absolute `./TODO/` path. Ask it to: +Dispatch `@pm` with the issue ID `$ISSUE_ID` and the following operations (a single dispatch can carry all of them — see `agents/pm.md` for the request shape): -1. **Check off the AC checkboxes that task-1 satisfied.** For each `- [ ]` AC line in `./TODO/$ISSUE_ID.md`'s `## Acceptance criteria` section that the implemented work fulfilled, flip to `- [x]`. The orchestrator decides which AC are satisfied by inspecting task-1's spec and verification output. -2. **Set the issue's frontmatter `status` based on AC completion** (ADR-21, AC-driven): - - **All AC are now `[x]`** → `status: Done`. - - **Some AC remain `[ ]`** → `status: In Progress`. (Sub-issues filed at Phase 5.5 cover the unmet AC; the user runs them in subsequent invocations.) - - **No AC section in the file** → `status: Done` (the parent had no testable AC; one task ran end-to-end). -3. **Propagate any status flip to the dependent index:** `TODO/README.md` for top-level issues (`parent: null`), or the parent issue file's `## Sub-issues` line for sub-issues. -4. **Add a comment** of the form: `- YYYY-MM-DD — Branch \`$BRANCH_NAME\`, commit ` (date from the shell, never fabricated). +1. **Check off the AC the run satisfied.** Pass the list of AC indices or texts (from the `acceptance_criteria` array `@pm` returned at Phase 2) that the implemented work fulfilled. The orchestrator decides which AC are satisfied by inspecting task-1's spec and verification output. `@pm` flips the corresponding `- [ ]` to `- [x]`. +2. **Set the issue's `status` based on AC completion** (ADR-21, AC-driven): + - **All AC are now `[x]`** → `Done`. + - **Some AC remain `[ ]`** → `In Progress`. (Sub-issues filed at Phase 5.5 cover the unmet AC; the user runs them in subsequent invocations.) + - **No AC section** → `Done` (the parent had no testable AC; one task ran end-to-end). +3. **Add a comment** of the form: `- YYYY-MM-DD — Branch \`$BRANCH_NAME\`, commit ` (date from the shell, never fabricated). + +`@pm` propagates status flips to the dependent index (the top-level README or the parent's `## Sub-issues` line) on its own — that's its job, not the orchestrator's. The orchestrator passes high-level intent ("set status to Done") and trusts `@pm` to update every dependent file. + +`@pm`'s response includes the list of files it modified (absolute paths). Capture this list as `MODIFIED_TODO_PATHS` for the staging step below. ### File Follow-ups @@ -549,9 +587,11 @@ Tracked-worthy unresolved items must become real TODO issues; otherwise they van ### Commit TODO Changes -After both the TODO Update and File Follow-ups steps, commit everything under `TODO/` in a single atomic commit: `chore(todo): update status, file follow-ups`. Stage the worked issue file, the dependent index (README.md or parent file), and any newly created follow-up issue files. +After both the TODO Update and File Follow-ups steps, stage every path returned by `@pm` in this run (the union of `MODIFIED_TODO_PATHS` and `NEW_SUBISSUE_PATHS` collected from each `@pm` dispatch). Commit them in a single atomic commit: `chore(todo): update status, file follow-ups`. -If no follow-ups were filed, the commit message simplifies to `chore(todo): update status and progress` and only the TODO Update changes are staged. +Equivalently — and more robustly, since the orchestrator can't have edited TODO files directly (Phase 1 verified the working tree was clean and the orchestrator never writes there) — stage the entire `TODO/` directory: `git add ./TODO/`. Anything staged under `TODO/` came from `@pm` during this run. + +If no follow-ups were filed, the commit message simplifies to `chore(todo): update status and progress`. ### Run Summary - Write `$RUN_DIR/summary.md` with: @@ -575,7 +615,7 @@ At any phase, if an unrecoverable error occurs (or a routing rule explicitly abo 1. Write `$RUN_DIR/summary.md` (creating `$RUN_DIR` first if it doesn't exist) with what was completed and what failed. Do **not** stage or commit anything under `.workflow/`. 2. If any code was written, commit it with message `wip: incomplete workflow run for `. Stage code only — exclude `.workflow/` and `TODO/`. 3. Leave the branch and worktree intact for the user to inspect — do not push, do not delete. -4. Dispatch `@pm` against `./TODO/` to add a comment on the issue file (`./TODO/.md`) summarising what failed and naming the abort reason if it was a routing-rule abort (e.g. `split_needed at Phase 7 task-1`, `plan_rework_remaining exhausted at Phase 8`). +4. Dispatch `@pm` (operation: `Add comment`, issue ID: `$ISSUE_ID`) summarising what failed and naming the abort reason if it was a routing-rule abort (e.g. `split_needed at Phase 7 task-1`, `plan_rework_remaining exhausted at Phase 8`). The orchestrator never constructs the issue file path — `@pm` resolves it. 5. Stop execution. ### Recovery procedure (workflow is non-resumable, ADR-14) diff --git a/config/opencode/workflow-design.md b/config/opencode/workflow-design.md index 6a8c332..53bf3c8 100644 --- a/config/opencode/workflow-design.md +++ b/config/opencode/workflow-design.md @@ -40,8 +40,8 @@ High-level happy path with the major escalation arms. The workflow runs **one ta ```mermaid flowchart TD - P1["Phase 1: Sanity Check
incl clean tree + depends-on"] - P2["Phase 2: Issue Context
pm reads TODO/ID.md"] + P1["Phase 1: Sanity Check
git/worktree only, no TODO reads"] + P2["Phase 2: Issue Context
pm validates prereqs + returns path"] P3["Phase 3: Plan
write plan.md"] P4{"Phase 4: Review Plan
check blocking, simplify advisory
max 3 cycles"} P5["Phase 5: Split into Tasks
write task-N.md"] @@ -135,10 +135,13 @@ Every observed `(phase, signal) → action`. Empty cells are gaps. Walking this | Phase | Signal source | Signal | Action | |---|---|---|---| -| 1 | Sanity checks | Bare repo / detached HEAD / missing `TODO/.md` / branch == base | Stop with error | +| 1 | Sanity checks | Bare repo / detached HEAD / branch == base | Stop with error | | 1 | Sanity checks | Working tree dirty (`git status --porcelain` non-empty) | Stop with error (ADR-20) | -| 1 | Sanity checks | `depends-on:` issue not in `Done` status | Stop with error (ADR-21) | -| 2 | `@pm` | Issue not found | Stop with error | +| 2 | `@pm` (Validate run prerequisites) | `ok: true` | Capture `issue_file_path` and full issue context; proceed | +| 2 | `@pm` (Validate run prerequisites) | `error_code: tracker_missing` | Stop with error using `@pm`'s message verbatim (ADR-22) | +| 2 | `@pm` (Validate run prerequisites) | `error_code: issue_not_found` | Stop with error using `@pm`'s message verbatim (ADR-22) | +| 2 | `@pm` (Validate run prerequisites) | `error_code: dependency_unmet` | Stop with error using `@pm`'s message verbatim (ADR-21 / ADR-22) | +| 2 | `@pm` (Validate run prerequisites) | `error_code: dependency_missing` | Stop with error using `@pm`'s message verbatim (ADR-22) | | 2 | `@pm` | Status is `Todo` | Flip to `In Progress`; propagate to README.md / parent's Sub-issues | | 3 | Orchestrator | Plan drafted | Apply Dispatch Hygiene; write `plan.md`; verify `test -f` | | 4 | `@check` | ACCEPTABLE (regardless of `@simplify`) | Proceed to Phase 5 | @@ -352,6 +355,13 @@ The model carries five sub-decisions: **Alternatives:** (a) keep N-task runs, add mid-flight re-splitting via P7→P5 re-entry — doesn't solve big-diff or cross-task regression; (b) keep N-task runs, accept the gaps — leaves three known-bad routes; (c) always one task per issue (skip Phase 5 entirely) — loses the planning-phase split heuristic that's catching legitimate over-scoping at design time. **Consequences:** runs become shorter and more focused. Each commit/PR carries a bounded scope. Sub-issue fan-out becomes the primary scaling mechanism for multi-step work. `TODO/` sees more sub-issue files; `@pm`'s split-time filing path becomes a hot code path. Concurrent runs in different worktrees on the same repo become trivially safe because each worktree has its own `TODO/` checkout (file conflicts surface as standard git merge conflicts at integration time, not as mid-run race conditions). +### ADR-22 (2026-05-08) — TODO path resolution lives with `@pm`; orchestrator never constructs TODO paths + +**Context:** in early runs of the one-task-per-run workflow, the orchestrator sometimes did `@pm`'s job itself — reading `./TODO/$ISSUE_ID.md` directly to inspect the issue, instead of dispatching `@pm`. The text-level "anti-patterns" warning (workflow.md §Roles & Dispatch) wasn't enough to prevent it: once the workflow document told the orchestrator that issue files lived at `./TODO/.md`, the recipe was discoverable and tempting. Phase 1's sanity check (former steps 3 + 9 — TODO-tracker existence and `depends-on` enforcement) was the most blatant offender, since it required the orchestrator to read TODO files directly. +**Decision:** the orchestrator does not read, write, or construct any path under `TODO/` at any phase. All TODO operations — including prerequisite validation that used to live in Phase 1 — go through `@pm` dispatches. `@pm`'s response always includes the absolute file path of every issue file it touched (or read); the orchestrator captures these paths and uses them downstream (Phase 9 staging, Failure Handler comments, etc.) instead of constructing them. Phase 1 keeps only git/worktree-shaped checks; Phase 2 expands `@pm`'s existing dispatch into a "Validate run prerequisites" operation that returns either `{ok: true, issue_file_path, issue: {...}}` or a structured error. +**Alternatives:** (a) permission-deny `TODO/**` for the orchestrator — would force-fail orchestrator self-help but adds a permission layer the user prefers to avoid; (b) leave the doc warnings in place and hope the orchestrator complies — already shown to be insufficient; (c) keep Phase 1's TODO checks and just discipline the orchestrator harder — same problem as (b). +**Consequences:** discoverability of the path layout disappears from `commands/workflow.md` — the orchestrator literally never sees a `TODO/.md` template to imitate. The schema and path layout live in `agents/pm.md`, which the orchestrator does not load. `@pm`'s capabilities table grows by one ("Validate run prerequisites") and every existing capability now mandates including the absolute file path in the response. The orchestrator's Phase 9 staging step changes from constructing paths to using `@pm`-returned paths (or, equivalently, `git add ./TODO/` since the working tree was clean at Phase 1 and only `@pm` writes to TODO during a run). + --- ## 6. Open Questions / Known Gaps