feat(opencode): hide TODO paths from orchestrator (ADR-22)
In recent runs the orchestrator skipped @pm and edited TODO/ files
itself, despite the workflow.md anti-pattern warning. Root cause: the
workflow doc literally taught the orchestrator the path layout
(`./TODO/<ID>.md`), making self-help a discoverable shortcut.
Fix: remove the recipe. The orchestrator now never constructs or reads
any per-issue TODO path. All TODO operations go through @pm dispatches;
@pm returns the absolute file path of every issue it touches, and the
orchestrator captures and reuses those paths downstream.
- Phase 1 loses the TODO-existence and depends-on checks (former steps
3 and 9 of the recent edit) — Phase 1 is now git/worktree-only.
- Phase 2 expands @pm's existing dispatch into a `Validate run
prerequisites` operation that returns either {ok: true,
issue_file_path, issue: {...}} or {ok: false, error_code, message}
with error_code in {tracker_missing, issue_not_found,
dependency_unmet, dependency_missing}. depends-on enforcement moves
here.
- Phase 7 split_needed exit, Phase 9 TODO Update, Phase 9 Commit TODO
Changes, and Failure Handler all reference @pm-returned paths or use
`git add ./TODO/` blanketly (safe because Phase 1 verified clean tree
and only @pm writes there during a run).
- pm.md gains a path-return rule: every read returns issue_file_path,
every write returns the modified paths. Run-Prerequisite Output
format documented with all four error codes.
- ADR-22 captures the rationale; routing matrix updates Phase 1/2 rows;
pipeline diagram labels updated.
The fix is discoverability-only — no permission deny on TODO/, per
explicit user direction. The schema lives in agents/pm.md, which the
orchestrator does not load.
Refs: config/opencode/workflow-design.md ADR-22
This commit is contained in:
parent
3e515d54eb
commit
a3e0de6d04
3 changed files with 140 additions and 44 deletions
|
|
@ -114,15 +114,59 @@ Statuses: `Todo`, `In Progress`, `Done`.
|
|||
## Capabilities
|
||||
|
||||
You can:
|
||||
- **View** an issue by ID — read `<TODO_DIR>/<ID>.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 `<TODO_DIR>/<ID>.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 `<TODO_DIR>/*.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 `<TODO_DIR>/<NEW-ID>.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: <PARENT-ID>`), 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 `<TODO_DIR>/<NEW-ID>.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: <PARENT-ID>`), 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 — <text>` 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 — <text>` 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/<ID>.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": "<one-line description suitable for the orchestrator to surface verbatim>"
|
||||
}
|
||||
```
|
||||
|
||||
Error code semantics:
|
||||
- `tracker_missing` — `<TODO_DIR>/` or `<TODO_DIR>/README.md` is absent.
|
||||
- `issue_not_found` — `<TODO_DIR>/<ID>.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.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue