From e8f67d3140cfdbce6fbc075806e685485f7a1dfc Mon Sep 17 00:00:00 2001 From: graysurf <10785178+graysurf@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:08:47 +0800 Subject: [PATCH 1/2] feat(plan-issue-delivery): add runtime adapter installer --- .../plan-issue-delivery-main-agent-init.md | 30 +- prompts/plan-issue-delivery-subagent-init.md | 32 +- scripts/README.md | 4 + scripts/plan-issue-adapter | 582 ++++++++++++++++++ .../automation/plan-issue-delivery/SKILL.md | 83 ++- .../agents/plan-issue-implementation.md | 13 + .../.claude/agents/plan-issue-monitor.md | 13 + .../.claude/agents/plan-issue-orchestrator.md | 18 + .../.claude/agents/plan-issue-review.md | 13 + .../.codex/agents/plan-issue-monitor.toml | 11 + .../.codex/agents/plan-issue-reviewer.toml | 12 + .../home/.codex/agents/plan-issue-worker.toml | 12 + .../agents/plan-issue-implementation.md | 16 + .../.opencode/agents/plan-issue-monitor.md | 21 + .../.opencode/agents/plan-issue-review.md | 21 + .../prompts/plan-issue-orchestrator.txt | 10 + .../opencode/project/opencode.json | 23 + .../references/AGENT_ROLE_MAPPING.md | 66 ++ .../references/CLAUDE_CODE_ADAPTER.md | 64 ++ .../references/CODEX_ADAPTER.md | 65 ++ .../references/OPENCODE_ADAPTER.md | 68 ++ .../references/RUNTIME_LAYOUT.md | 3 +- .../test_automation_plan_issue_delivery.py | 54 ++ ...n_plan_issue_delivery_adapter_installer.py | 115 ++++ .../issue/issue-subagent-pr/SKILL.md | 13 + .../test_workflows_issue_issue_subagent_pr.py | 2 + .../scripts/plan-issue-adapter.json | 13 + 27 files changed, 1359 insertions(+), 18 deletions(-) create mode 100755 scripts/plan-issue-adapter create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-implementation.md create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-monitor.md create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-orchestrator.md create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-review.md create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-monitor.toml create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-reviewer.toml create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-worker.toml create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-implementation.md create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-monitor.md create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-review.md create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/prompts/plan-issue-orchestrator.txt create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/opencode.json create mode 100644 skills/automation/plan-issue-delivery/references/AGENT_ROLE_MAPPING.md create mode 100644 skills/automation/plan-issue-delivery/references/CLAUDE_CODE_ADAPTER.md create mode 100644 skills/automation/plan-issue-delivery/references/CODEX_ADAPTER.md create mode 100644 skills/automation/plan-issue-delivery/references/OPENCODE_ADAPTER.md create mode 100644 skills/automation/plan-issue-delivery/tests/test_automation_plan_issue_delivery_adapter_installer.py create mode 100644 tests/script_specs/scripts/plan-issue-adapter.json diff --git a/prompts/plan-issue-delivery-main-agent-init.md b/prompts/plan-issue-delivery-main-agent-init.md index d843b481..f86040c9 100644 --- a/prompts/plan-issue-delivery-main-agent-init.md +++ b/prompts/plan-issue-delivery-main-agent-init.md @@ -40,6 +40,15 @@ Execution context (fill before run) `$AGENT_HOME/out/plan-issue-delivery/{repo-slug}/issue-/plan/plan-integration-pr.md` - Plan integration mention URL path: `$AGENT_HOME/out/plan-issue-delivery/{repo-slug}/issue-/plan/plan-integration-mention.url` +- Role mapping reference: + `$AGENT_HOME/skills/automation/plan-issue-delivery/references/AGENT_ROLE_MAPPING.md` +- Canonical workflow roles: + - `implementation` + - `review` + - `monitor` +- Runtime adapter metadata: + - record `runtime_name` / `runtime_role` only when the active runtime + supports named child-agent roles PR grouping policy (scene-based; both are valid) @@ -59,8 +68,12 @@ Required workflow `MAIN_AGENT_INIT_SNAPSHOT_PATH` + `TASK_PROMPT_PATH` + `PLAN_SNAPSHOT_PATH` + `SUBAGENT_INIT_SNAPSHOT_PATH` + `DISPATCH_RECORD_PATH` + decision-scoped `REVIEW_EVIDENCE_PATH` artifacts under - `$AGENT_HOME/out/plan-issue-delivery/...` -> delegate to subagents with - required `PLAN_BRANCH` base-branch context -> if blocked, clarify and + `$AGENT_HOME/out/plan-issue-delivery/...` -> select the correct + `workflow_role` for each spawned task and persist it in sprint + manifests/dispatch records -> when the active runtime supports named + child-agent roles, also persist `runtime_name` / `runtime_role` -> + delegate to subagents with required `PLAN_BRANCH` base-branch context -> + if blocked, clarify and continue on the same task lane -> `ready-sprint` (pre-merge checkpoint) -> review each PR using the shared review rubric -> generate `REVIEW_EVIDENCE_PATH` from `REVIEW_EVIDENCE_TEMPLATE_PATH` -> execute @@ -89,6 +102,15 @@ Required workflow Mandatory subagent launch rule - For each task, use the rendered `TASK_PROMPT_PATH` from dispatch hints as the primary init prompt when spawning subagents. +- Use `workflow_role=implementation` for implementation-owned sprint lanes. +- Use `workflow_role=review` only for read-only audit/evidence helpers. +- Use `workflow_role=monitor` only for long-running CI/status watch helpers. +- Always record the chosen `workflow_role` in `DISPATCH_RECORD_PATH`. +- If the active runtime supports named child-agent roles, also record + `runtime_name` / `runtime_role`. +- If a named-role runtime falls back to a generic child agent, record + `runtime_role=generic` plus `runtime_role_fallback_reason` before spawning + the child agent. - Always copy `$AGENT_HOME/prompts/plan-issue-delivery-subagent-init.md` to sprint runtime and attach `SUBAGENT_INIT_SNAPSHOT_PATH`. - Always attach `PLAN_SNAPSHOT_PATH` from issue runtime workspace as fallback plan context. - Always attach `DISPATCH_RECORD_PATH` from sprint manifests for execution-fact traceability. @@ -128,6 +150,9 @@ Mandatory subagent launch rule - `SUBAGENT_INIT_SNAPSHOT_PATH` - `PLAN_SNAPSHOT_PATH` - `DISPATCH_RECORD_PATH` + - `workflow_role` + - optional `runtime_name` / `runtime_role` (or `runtime_role_fallback_reason` + when a named-role runtime falls back to generic) - `PLAN_BRANCH` - plan task section snippet/link/path (`#` or equivalent) - Do not start subagents with ad-hoc prompts that bypass the required dispatch bundle. @@ -137,6 +162,7 @@ Reporting contract (every update) - Phase: - Commands run: - Gate status: +- Workflow roles / runtime adapters: - Subagent assignments / PR references: - Blockers / risks: - Next required action: diff --git a/prompts/plan-issue-delivery-subagent-init.md b/prompts/plan-issue-delivery-subagent-init.md index 192e2d3c..de913cc2 100644 --- a/prompts/plan-issue-delivery-subagent-init.md +++ b/prompts/plan-issue-delivery-subagent-init.md @@ -41,6 +41,8 @@ Execution context (fill before run) - Subagent init snapshot path: `` - Plan snapshot path: `` - Dispatch record path: `` +- Workflow role: `` +- Runtime role: `` Required inputs from main-agent (must be attached) @@ -48,6 +50,12 @@ Required inputs from main-agent (must be attached) - Sprint-scoped subagent companion prompt snapshot (`SUBAGENT_INIT_SNAPSHOT_PATH`). - Issue-scoped plan snapshot fallback (`PLAN_SNAPSHOT_PATH`) copied from source plan. - Task-scoped dispatch record (`DISPATCH_RECORD_PATH`) with execution facts (worktree/branch/mode/group). +- `DISPATCH_RECORD_PATH` role facts: + - `workflow_role` should be `implementation` for implementation lanes + - if runtime adapter metadata is present, `runtime_role` should match the + assigned runtime role for this lane + - if a named-role runtime fell back to generic, `runtime_role_fallback_reason` + must explain why the preferred runtime role was unavailable - Required base branch context (`PLAN_BRANCH`) for PR targeting. - Plan task context for assigned IDs: - exact task section snippet and/or @@ -63,21 +71,25 @@ Delivery requirements created yet. 3. Resolve plan context in this order: assigned task snippet/link/path -> `PLAN_SNAPSHOT_PATH` -> source plan link/path (last fallback). 4. If plan references conflict, follow issue runtime-truth assignment (`Task Decomposition` row) and escalate ambiguities. -5. Validate `DISPATCH_RECORD_PATH` matches assigned task IDs, worktree, branch, execution mode, and `PLAN_BRANCH` before editing. -6. Keep changes within assigned task scope; escalate before widening scope. -7. If required context is missing/conflicting or another blocker stops progress, return a blocker packet with exact lane facts, the missing +5. Validate `DISPATCH_RECORD_PATH` matches assigned task IDs, worktree, branch, + execution mode, `PLAN_BRANCH`, and `workflow_role` before editing. +6. If `DISPATCH_RECORD_PATH` declares a non-implementation `workflow_role`, + stop and request corrected dispatch before editing. +7. Keep changes within assigned task scope; escalate before widening scope. +8. If required context is missing/conflicting or another blocker stops progress, return a blocker packet with exact lane facts, the missing input, current status, and the next unblock action needed from main-agent. -8. Run relevant tests for impacted areas and capture results. -9. Keep commits and PR description traceable to task IDs. -10. Surface risks early with concrete mitigation options. -11. Keep sprint PRs open for `ready-sprint` pre-merge review; do not self-merge. -12. Wait for required PR CI checks to finish before marking work ready for review/merge. -13. If PR CI fails, diagnose and fix the failures, push updates, and repeat until required checks pass (or escalate external blockers with +9. Run relevant tests for impacted areas and capture results. +10. Keep commits and PR description traceable to task IDs. +11. Surface risks early with concrete mitigation options. +12. Keep sprint PRs open for `ready-sprint` pre-merge review; do not self-merge. +13. Wait for required PR CI checks to finish before marking work ready for review/merge. +14. If PR CI fails, diagnose and fix the failures, push updates, and repeat until required checks pass (or escalate external blockers with evidence). Update format (every checkpoint) - Task IDs completed/in progress: +- Workflow role / runtime role: - Task lane facts (`Owner / Branch / Worktree / PR`): - Files/components changed: - Tests run + key results: @@ -91,4 +103,6 @@ Done criteria - PR content is reviewable, with clear task mapping and no hidden scope changes. - Required PR CI checks are passing, or external blockers are escalated with concrete evidence and mitigation options. - Sprint PR targets assigned `PLAN_BRANCH` and stays unmerged until main-agent review decision. +- If the active runtime fell back to a generic child agent, the fallback + rationale remains recorded in `DISPATCH_RECORD_PATH`. - If blocked, do not claim done; return the blocker packet and wait for main-agent clarification/unblock. diff --git a/scripts/README.md b/scripts/README.md index 74557d28..1ebc8db0 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -46,6 +46,7 @@ scripts/ │ │ └── shell.sh │ └── zsh-common.zsh ├── lint.sh +├── plan-issue-adapter ├── project-resolve ├── semgrep-scan.sh └── test.sh @@ -103,6 +104,9 @@ scripts/ - `scripts/check_plan_issue_worktree_cleanup.sh` - Checks leftover `plan-issue-delivery` worktree directories. +- `scripts/plan-issue-adapter` + - Explicit installer/sync/status entrypoint for optional `plan-issue-delivery` + runtime adapters (`codex|claude|opencode`). - `scripts/chrome-devtools-mcp.sh` - Launcher for chrome-devtools MCP server with repo env handling. - `scripts/project-resolve` diff --git a/scripts/plan-issue-adapter b/scripts/plan-issue-adapter new file mode 100755 index 00000000..fef9b937 --- /dev/null +++ b/scripts/plan-issue-adapter @@ -0,0 +1,582 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import re +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Any + + +SCRIPT_NAME = "plan-issue-adapter" +RUNTIMES = ("codex", "claude", "opencode") + + +@dataclass(frozen=True) +class ItemReport: + target: Path + kind: str + status: str + detail: str + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + prog=SCRIPT_NAME, + description=( + "Install, sync, or inspect optional runtime adapters for " + "plan-issue-delivery." + ), + formatter_class=argparse.RawTextHelpFormatter, + epilog=( + "Examples:\n" + f" {SCRIPT_NAME} status --runtime codex\n" + f" {SCRIPT_NAME} install --runtime claude --project-path /path/to/project\n" + f" {SCRIPT_NAME} sync --runtime opencode --project-path /path/to/project --apply\n" + ), + ) + parser.add_argument( + "action", + choices=("install", "sync", "status"), + help="Operation to perform.", + ) + parser.add_argument( + "--runtime", + required=True, + choices=RUNTIMES, + help="Runtime adapter to manage.", + ) + parser.add_argument( + "--project-path", + help="Project root for Claude Code / OpenCode adapters (default: $PROJECT_PATH or current directory).", + ) + parser.add_argument( + "--home-path", + help="Home root for the Codex adapter (default: $HOME).", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Preview actions only (default).", + ) + parser.add_argument( + "--apply", + action="store_true", + help="Write changes to disk.", + ) + parser.add_argument( + "--force", + action="store_true", + help="Allow install to overwrite drifted managed entries.", + ) + return parser + + +def repo_root() -> Path: + return Path(__file__).resolve().parents[1] + + +def runtime_template_root(runtime: str) -> Path: + root = ( + repo_root() + / "skills" + / "automation" + / "plan-issue-delivery" + / "assets" + / "runtime-adapters" + ) + mapping = { + "codex": root / "codex" / "home" / ".codex", + "claude": root / "claude-code" / "project" / ".claude", + "opencode": root / "opencode" / "project", + } + return mapping[runtime] + + +def die(message: str, exit_code: int = 1) -> None: + print(f"error: {message}", file=sys.stderr) + raise SystemExit(exit_code) + + +def resolve_project_path(raw: str | None) -> Path: + source = raw or str(Path.cwd()) + path = Path(source).expanduser().resolve() + if not path.is_dir(): + die(f"--project-path must point to an existing directory: {path}", exit_code=2) + return path + + +def resolve_home_path(raw: str | None) -> Path: + source = raw or str(Path.home()) + return Path(source).expanduser().resolve() + + +def read_text(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def normalize_text(text: str) -> str: + return text.rstrip() + "\n" + + +def write_text(path: Path, text: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8") + + +def sync_text_file( + *, + template_path: Path, + target_path: Path, + action: str, + apply: bool, + force: bool, + kind: str, + detail: str, +) -> ItemReport: + template_text = normalize_text(read_text(template_path)) + if not target_path.exists(): + if action == "status": + return ItemReport(target_path, kind, "missing", detail) + if apply: + write_text(target_path, template_text) + return ItemReport(target_path, kind, "create", detail) + + existing_text = normalize_text(read_text(target_path)) + if existing_text == template_text: + status = "synced" if action == "status" else "unchanged" + return ItemReport(target_path, kind, status, detail) + + if action == "status": + return ItemReport(target_path, kind, "drifted", detail) + + if action == "install" and not force: + return ItemReport( + target_path, + kind, + "skip", + f"{detail}; existing content differs, use sync or --force", + ) + + if apply: + write_text(target_path, template_text) + return ItemReport(target_path, kind, "update", detail) + + +def section_block(text: str, header: str) -> str | None: + pattern = re.compile( + rf"(?ms)^[ \t]*{re.escape(header)}[ \t]*\n.*?(?=^[ \t]*\[|\Z)" + ) + match = pattern.search(text) + if not match: + return None + return normalize_text(match.group(0)) + + +def upsert_section(text: str, header: str, block: str) -> str: + pattern = re.compile( + rf"(?ms)^[ \t]*{re.escape(header)}[ \t]*\n.*?(?=^[ \t]*\[|\Z)" + ) + block = normalize_text(block) + match = pattern.search(text) + if match: + updated = text[: match.start()] + block + text[match.end() :] + return normalize_text(updated) + + stripped = text.rstrip() + if not stripped: + return block + return stripped + "\n\n" + block + + +def load_codex_template() -> tuple[str, dict[str, str]]: + config_path = runtime_template_root("codex") / "config.toml" + template_text = normalize_text(read_text(config_path)) + section_headers = ( + "[agents.plan_issue_worker]", + "[agents.plan_issue_reviewer]", + "[agents.plan_issue_monitor]", + ) + sections: dict[str, str] = {} + for header in section_headers: + block = section_block(template_text, header) + if block is None: + die(f"missing managed section in Codex template: {header}") + sections[header] = block + return template_text, sections + + +def manage_codex_config( + *, + action: str, + apply: bool, + force: bool, + home_path: Path, +) -> list[ItemReport]: + template_text, managed_sections = load_codex_template() + target_path = home_path / ".codex" / "config.toml" + + if not target_path.exists(): + if action == "status": + return [ + ItemReport( + target_path, + "toml-file", + "missing", + "Codex config file with plan_issue agent role sections", + ) + ] + if apply: + write_text(target_path, template_text) + return [ + ItemReport( + target_path, + "toml-file", + "create", + "Codex config file with plan_issue agent role sections", + ) + ] + + original = read_text(target_path) + updated = original + reports: list[ItemReport] = [] + + # Merge only the managed plan_issue sections so unrelated user config stays intact. + for header, block in managed_sections.items(): + existing_block = section_block(updated, header) + label = header.strip("[]") + + if existing_block is None: + if action == "status": + reports.append(ItemReport(target_path, "toml-section", "missing", label)) + continue + updated = upsert_section(updated, header, block) + reports.append(ItemReport(target_path, "toml-section", "create", label)) + continue + + if normalize_text(existing_block) == normalize_text(block): + status = "synced" if action == "status" else "unchanged" + reports.append(ItemReport(target_path, "toml-section", status, label)) + continue + + if action == "status": + reports.append(ItemReport(target_path, "toml-section", "drifted", label)) + continue + + if action == "install" and not force: + reports.append( + ItemReport( + target_path, + "toml-section", + "skip", + f"{label}; existing section differs, use sync or --force", + ) + ) + continue + + updated = upsert_section(updated, header, block) + reports.append(ItemReport(target_path, "toml-section", "update", label)) + + if apply and action in {"install", "sync"} and normalize_text(updated) != normalize_text(original): + write_text(target_path, normalize_text(updated)) + return reports + + +def manage_codex_runtime( + *, + action: str, + apply: bool, + force: bool, + home_path: Path, +) -> tuple[Path, list[ItemReport]]: + template_root = runtime_template_root("codex") + target_root = home_path / ".codex" + reports = manage_codex_config( + action=action, + apply=apply, + force=force, + home_path=home_path, + ) + + for name in ( + "plan-issue-worker.toml", + "plan-issue-reviewer.toml", + "plan-issue-monitor.toml", + ): + reports.append( + sync_text_file( + template_path=template_root / "agents" / name, + target_path=target_root / "agents" / name, + action=action, + apply=apply, + force=force, + kind="toml-file", + detail=name, + ) + ) + return target_root, reports + + +def manage_claude_runtime( + *, + action: str, + apply: bool, + force: bool, + project_path: Path, +) -> tuple[Path, list[ItemReport]]: + template_root = runtime_template_root("claude") + target_root = project_path / ".claude" + reports: list[ItemReport] = [] + + for name in ( + "plan-issue-orchestrator.md", + "plan-issue-implementation.md", + "plan-issue-review.md", + "plan-issue-monitor.md", + ): + reports.append( + sync_text_file( + template_path=template_root / "agents" / name, + target_path=target_root / "agents" / name, + action=action, + apply=apply, + force=force, + kind="markdown-file", + detail=name, + ) + ) + return target_root, reports + + +def load_json(path: Path) -> dict[str, Any]: + try: + data = json.loads(read_text(path)) + except json.JSONDecodeError as exc: + die(f"invalid JSON in {path}: {exc}") + if not isinstance(data, dict): + die(f"expected a JSON object in {path}") + return data + + +def manage_opencode_json( + *, + action: str, + apply: bool, + force: bool, + project_path: Path, +) -> ItemReport: + template_path = runtime_template_root("opencode") / "opencode.json" + target_path = project_path / "opencode.json" + template_data = load_json(template_path) + desired_agent = template_data.get("agent", {}).get("plan-issue-orchestrator") + if not isinstance(desired_agent, dict): + die(f"missing agent.plan-issue-orchestrator in {template_path}") + + if not target_path.exists(): + if action == "status": + return ItemReport( + target_path, + "json-file", + "missing", + "opencode.json with plan-issue-orchestrator agent entry", + ) + if apply: + write_text(target_path, json.dumps(template_data, indent=2) + "\n") + return ItemReport( + target_path, + "json-file", + "create", + "opencode.json with plan-issue-orchestrator agent entry", + ) + + existing = load_json(target_path) + agent_block = existing.get("agent") + if agent_block is None: + agent_block = {} + existing["agent"] = agent_block + if not isinstance(agent_block, dict): + die(f"expected 'agent' to be a JSON object in {target_path}") + + current = agent_block.get("plan-issue-orchestrator") + if current == desired_agent: + status = "synced" if action == "status" else "unchanged" + return ItemReport( + target_path, + "json-entry", + status, + "agent.plan-issue-orchestrator", + ) + + if action == "status": + status = "missing" if current is None else "drifted" + return ItemReport( + target_path, + "json-entry", + status, + "agent.plan-issue-orchestrator", + ) + + if action == "install" and current is not None and not force: + return ItemReport( + target_path, + "json-entry", + "skip", + "agent.plan-issue-orchestrator; existing entry differs, use sync or --force", + ) + + agent_block["plan-issue-orchestrator"] = desired_agent + if apply: + write_text(target_path, json.dumps(existing, indent=2) + "\n") + status = "create" if current is None else "update" + return ItemReport( + target_path, + "json-entry", + status, + "agent.plan-issue-orchestrator", + ) + + +def manage_opencode_runtime( + *, + action: str, + apply: bool, + force: bool, + project_path: Path, +) -> tuple[Path, list[ItemReport]]: + template_root = runtime_template_root("opencode") + target_root = project_path + reports = [ + manage_opencode_json( + action=action, + apply=apply, + force=force, + project_path=project_path, + ) + ] + + reports.append( + sync_text_file( + template_path=template_root / ".opencode" / "prompts" / "plan-issue-orchestrator.txt", + target_path=project_path / ".opencode" / "prompts" / "plan-issue-orchestrator.txt", + action=action, + apply=apply, + force=force, + kind="text-file", + detail=".opencode/prompts/plan-issue-orchestrator.txt", + ) + ) + + for name in ( + "plan-issue-implementation.md", + "plan-issue-review.md", + "plan-issue-monitor.md", + ): + reports.append( + sync_text_file( + template_path=template_root / ".opencode" / "agents" / name, + target_path=project_path / ".opencode" / "agents" / name, + action=action, + apply=apply, + force=force, + kind="markdown-file", + detail=f".opencode/agents/{name}", + ) + ) + return target_root, reports + + +def summarize(reports: list[ItemReport]) -> str: + counts: dict[str, int] = {} + for report in reports: + counts[report.status] = counts.get(report.status, 0) + 1 + ordered = ( + "create", + "update", + "unchanged", + "skip", + "missing", + "drifted", + "synced", + ) + parts = [f"{key}={counts[key]}" for key in ordered if counts.get(key)] + return " ".join(parts) if parts else "none=0" + + +def print_report( + *, + action: str, + runtime: str, + mode: str, + target_root: Path, + reports: list[ItemReport], +) -> None: + print(f"action: {action}") + print(f"runtime: {runtime}") + print(f"mode: {mode}") + print(f"target_root: {target_root}") + print(f"summary: {summarize(reports)}") + for report in reports: + print( + f"- {report.status} [{report.kind}] {report.target}: {report.detail}" + ) + + +def validate_args(args: argparse.Namespace) -> None: + if args.apply and args.dry_run: + die("choose only one of --dry-run or --apply", exit_code=2) + if args.action == "status" and args.apply: + die("--apply is not valid with status", exit_code=2) + + if args.runtime == "codex" and args.project_path: + die("--project-path is not used with --runtime codex", exit_code=2) + if args.runtime in {"claude", "opencode"} and args.home_path: + die("--home-path is only valid with --runtime codex", exit_code=2) + + +def main() -> int: + parser = build_parser() + args = parser.parse_args() + validate_args(args) + + apply = bool(args.apply) + mode = "apply" if apply else "dry-run" + + if args.runtime == "codex": + home_path = resolve_home_path(args.home_path) + target_root, reports = manage_codex_runtime( + action=args.action, + apply=apply, + force=bool(args.force), + home_path=home_path, + ) + else: + project_path = resolve_project_path(args.project_path) + if args.runtime == "claude": + target_root, reports = manage_claude_runtime( + action=args.action, + apply=apply, + force=bool(args.force), + project_path=project_path, + ) + else: + target_root, reports = manage_opencode_runtime( + action=args.action, + apply=apply, + force=bool(args.force), + project_path=project_path, + ) + + print_report( + action=args.action, + runtime=args.runtime, + mode=mode, + target_root=target_root, + reports=reports, + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/skills/automation/plan-issue-delivery/SKILL.md b/skills/automation/plan-issue-delivery/SKILL.md index 16aeddbd..2579fad1 100644 --- a/skills/automation/plan-issue-delivery/SKILL.md +++ b/skills/automation/plan-issue-delivery/SKILL.md @@ -60,8 +60,26 @@ Inputs: - rendered `TASK_PROMPT_PATH` from `start-sprint` - sprint-scoped `SUBAGENT_INIT_SNAPSHOT_PATH` copied from `$AGENT_HOME/prompts/plan-issue-delivery-subagent-init.md` - issue-scoped `PLAN_SNAPSHOT_PATH` copied from the source plan at sprint start - - task-scoped `DISPATCH_RECORD_PATH` (for example `.../manifests/dispatch-.json`) with artifact paths + execution facts + - task-scoped `DISPATCH_RECORD_PATH` (for example `.../manifests/dispatch-.json`) with artifact paths + execution facts + + selected workflow-role metadata and optional runtime adapter metadata - plan task context per assignment (exact plan task section snippet and/or direct plan section link/path) +- Canonical workflow roles: + - `implementation`: sprint lanes that edit code, run tests, and own sprint PRs + - `review`: read-only PR review evidence, plan-conformance audits, and diff checks + - `monitor`: long-running CI/status polling and watch tasks +- Runtime role adapters: + - canonical workflow roles are repo truth; runtime-specific named roles are optional adapters + - reference mapping lives in `references/AGENT_ROLE_MAPPING.md` + - runtime examples such as Codex `[agents.]`, Claude Code + `.claude/agents/`, and OpenCode `opencode.json` belong to adapters, not + repo contract + - optional explicit installer/sync entrypoint: + `$AGENT_HOME/scripts/plan-issue-adapter --runtime [--apply]` +- Dispatch role traceability: + - prompt manifest rows must record `workflow_role` + - `DISPATCH_RECORD_PATH` must record `workflow_role` + - when a runtime uses named child-agent roles, also record `runtime_name` and `runtime_role` + - if a named-role runtime falls back to a generic child agent, record `runtime_role=generic` plus `runtime_role_fallback_reason` - Local rehearsal policy: - Default execution path is live mode (`plan-issue`) in this main skill. - If the user explicitly requests rehearsal, load `references/LOCAL_REHEARSAL.md` and run that playbook. @@ -73,7 +91,8 @@ Outputs: - Plan-scoped task-spec TSV generated from all plan tasks (all sprints) for one issue. - Sprint-scoped task-spec TSV generated per sprint for subagent dispatch hints, including `pr_group`. -- Sprint-scoped rendered subagent prompt files + a prompt manifest (`task_id -> prompt_path -> execution_mode`) generated at `start-sprint`. +- Sprint-scoped rendered subagent prompt files + a prompt manifest + (`task_id -> prompt_path -> execution_mode -> workflow_role [-> runtime_role]`) generated at `start-sprint`. - Runtime artifacts and worktrees are namespaced under `$AGENT_HOME/out/plan-issue-delivery//issue-/...`. - Issue-scoped main-agent init prompt snapshot (`MAIN_AGENT_INIT_SNAPSHOT_PATH`) is generated for deterministic @@ -85,7 +104,8 @@ Outputs: comment URLs traceable in issue-side sync actions. - Issue-scoped plan snapshot (`PLAN_SNAPSHOT_PATH`) is generated for dispatch fallback. - Sprint-scoped subagent companion prompt snapshot (`SUBAGENT_INIT_SNAPSHOT_PATH`) is generated for immutable dispatch. -- Task-scoped dispatch records (`DISPATCH_RECORD_PATH`) are generated per assignment for traceability. +- Task-scoped dispatch records (`DISPATCH_RECORD_PATH`) are generated per assignment for traceability, including canonical + `workflow_role` and optional runtime adapter fields (`runtime_name`, `runtime_role`, `runtime_role_fallback_reason`). - `plan-tooling split-prs` v2 emits grouping primitives only (`task_id`, `summary`, `pr_group`); `plan-issue` materializes runtime metadata (`Owner/Branch/Worktree/Notes`). - Live mode (`plan-issue`) creates/updates exactly one GitHub Issue for the whole plan (`1 plan = 1 issue`). @@ -120,6 +140,8 @@ Outputs: - `SUBAGENT_INIT_SNAPSHOT_PATH` artifact from `$AGENT_HOME/out/plan-issue-delivery/...` - `PLAN_SNAPSHOT_PATH` artifact from `$AGENT_HOME/out/plan-issue-delivery/...` - `DISPATCH_RECORD_PATH` artifact from `$AGENT_HOME/out/plan-issue-delivery/...` + - selected `workflow_role` + - optional `runtime_name` + `runtime_role` (or `runtime_role_fallback_reason` when a named-role runtime falls back to generic) - assigned `PLAN_BRANCH` base-branch context for sprint PR creation - plan task section context (snippet/link/path) - Ad-hoc dispatch prompts that bypass the required bundle are invalid. @@ -160,6 +182,9 @@ Failure modes: `REVIEW_EVIDENCE_PATH`, or generic non-evidenced decision text). - Subagent dispatch launched without required bundle (`TASK_PROMPT_PATH`, `SUBAGENT_INIT_SNAPSHOT_PATH`, `PLAN_SNAPSHOT_PATH`, `DISPATCH_RECORD_PATH`, plan task section snippet/link/path). +- Subagent dispatch launched without canonical `workflow_role` recorded in manifest/dispatch record. +- Active runtime uses named child-agent roles but dispatch omitted `runtime_name` / `runtime_role` metadata. +- Named-role runtime fell back to generic dispatch without `runtime_role_fallback_reason`. - Assigned task `Worktree` is outside `$AGENT_HOME/out/plan-issue-delivery/...`. - Final plan close gate fails (task status/PR merge not satisfied in live mode). - Final plan close gate fails because the integration PR (`PLAN_BRANCH -> DEFAULT_BRANCH`) is missing or unmerged. @@ -244,6 +269,27 @@ Failure modes: runtime-truth facts and existing PR linkage unless the row is intentionally updated first. +## Workflow Roles (Canonical) + +- Canonical workflow roles are repo-owned and runtime-agnostic: + - `implementation`: implementation-owned sprint lanes that edit code, + update tests, and own sprint PRs. + - `review`: read-only PR review evidence, merged-diff audit, and + plan-conformance analysis support for main-agent. + - `monitor`: `gh pr checks --watch`, CI/status polling, and other + long-running wait tasks that do not own merge decisions. +- Main-agent remains the decision authority even when `review` / `monitor` + child agents are used. +- Persist `workflow_role` in sprint prompt manifests and each + `DISPATCH_RECORD_PATH`. +- If the active runtime supports named child-agent roles, map `workflow_role` + through a runtime adapter and additionally persist `runtime_name` and + `runtime_role`. +- If a named-role runtime falls back to a generic child agent, record + `runtime_role=generic` plus `runtime_role_fallback_reason`; do not silently + downgrade. +- See `references/AGENT_ROLE_MAPPING.md` for runtime adapter examples. + ## Binaries (only entrypoints) - `plan-issue` (live GitHub orchestration) @@ -258,6 +304,11 @@ Failure modes: - Local rehearsal playbook (`plan-issue-local` and `plan-issue --dry-run`): `references/LOCAL_REHEARSAL.md` - Runtime layout and path rules: `references/RUNTIME_LAYOUT.md` +- Workflow-role to runtime-adapter mapping: `references/AGENT_ROLE_MAPPING.md` +- Runtime adapter installer/sync entrypoint: `$AGENT_HOME/scripts/plan-issue-adapter` +- Codex adapter guide + templates: `references/CODEX_ADAPTER.md` +- Claude Code adapter guide + templates: `references/CLAUDE_CODE_ADAPTER.md` +- OpenCode adapter guide + templates: `references/OPENCODE_ADAPTER.md` - Review evidence template for main-agent decisions: `skills/workflows/issue/issue-pr-review/references/REVIEW_EVIDENCE_TEMPLATE.md` - Shared task-lane continuity policy (canonical): @@ -277,11 +328,15 @@ Failure modes: `MAIN_AGENT_INIT_SOURCE_PATH` into `MAIN_AGENT_INIT_SNAPSHOT_PATH`; then resolve `DEFAULT_BRANCH`, create `PLAN_BRANCH`, and persist `PLAN_BRANCH_REF_PATH`. -4. Run `start-sprint`, ensure `MAIN_AGENT_INIT_SNAPSHOT_PATH` + +4. Run `start-sprint`, resolve `workflow_role` for each spawned task and, when + relevant, runtime adapter mapping, + ensure `MAIN_AGENT_INIT_SNAPSHOT_PATH` + `REVIEW_EVIDENCE_PATH` + `TASK_PROMPT_PATH` + `PLAN_SNAPSHOT_PATH` + `SUBAGENT_INIT_SNAPSHOT_PATH` + `DISPATCH_RECORD_PATH` artifacts - exist, dispatch subagents with `PLAN_BRANCH` base-branch context, keep task - lanes stable, and keep row state current via `link-pr`. + exist, record `workflow_role` (and optional runtime adapter metadata) in + prompt manifests/dispatch records, dispatch subagents with `PLAN_BRANCH` + base-branch context, keep task lanes stable, and keep row state current via + `link-pr`. 5. For each sprint: implement/clarify/follow-up on subagent-owned lanes -> `ready-sprint` (pre-merge review gate, PRs should still be open) -> main-agent review decisions + merge into `PLAN_BRANCH` -> `accept-sprint` -> @@ -395,11 +450,22 @@ Failure modes: - main-agent follows the locked grouping policy (default metadata-first auto with `--default-pr-grouping group`; switch only on explicit user request) and emits dispatch hints + - main-agent assigns canonical workflow roles before spawning: + - `implementation` for implementation-owned sprint lanes + - `review` for read-only audit/evidence helpers, when used + - `monitor` for CI/status watch helpers, when used + - if the active runtime supports named child-agent roles: + - map the canonical `workflow_role` through the active runtime adapter + - record `runtime_name` + `runtime_role` + - if the named-role adapter is unavailable, record `runtime_role=generic` + plus `runtime_role_fallback_reason` - main-agent starts subagents using dispatch bundles that include: - rendered `TASK_PROMPT_PATH` prompt artifact from dispatch hints - `SUBAGENT_INIT_SNAPSHOT_PATH` copied from `$AGENT_HOME/prompts/plan-issue-delivery-subagent-init.md` - `PLAN_SNAPSHOT_PATH` from issue runtime workspace - `DISPATCH_RECORD_PATH` from sprint manifest artifacts + - selected `workflow_role` + - optional `runtime_name` + `runtime_role` - `PLAN_BRANCH` (required base branch for sprint PRs) - assigned plan task section snippet/link/path (from plan file or sprint-start comment section) - subagents create or re-enter assigned worktrees/PRs and implement tasks on their assigned lanes @@ -607,6 +673,11 @@ comment URL. - Main-agent is orchestration/review-only. - Main-agent does not implement sprint tasks directly. - Sprint implementation must be delegated to subagent-owned PRs. +- Main-agent should always assign `workflow_role=implementation` to sprint + implementation lanes. +- Main-agent may assign `workflow_role=review` and `workflow_role=monitor` + only for read-only evidence gathering, audits, and status watches; these + roles do not replace main-agent decision authority. - Sprint comments and plan close actions are main-agent orchestration artifacts; implementation remains subagent-owned. - Allowed baseline exception: main-agent may open/manage exactly one non-implementation integration PR (`PLAN_BRANCH -> DEFAULT_BRANCH`) after all diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-implementation.md b/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-implementation.md new file mode 100644 index 00000000..8a0d405f --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-implementation.md @@ -0,0 +1,13 @@ +--- +name: plan-issue-implementation +description: Handles canonical workflow_role=implementation for plan-issue-delivery. Use for sprint lane coding, tests, and PR updates inside the assigned worktree. +tools: Read, Grep, Glob, Bash, Edit, Write +model: sonnet +--- + +You are the Claude Code adapter for canonical `workflow_role=implementation`. + +- Implement only the assigned task lane. +- Follow the dispatch bundle and prompt snapshots. +- Run edits and tests only inside the assigned worktree. +- Do not make orchestration, acceptance, or close-plan decisions. diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-monitor.md b/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-monitor.md new file mode 100644 index 00000000..89abf641 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-monitor.md @@ -0,0 +1,13 @@ +--- +name: plan-issue-monitor +description: Handles canonical workflow_role=monitor for plan-issue-delivery. Use for CI polling, required-check monitoring, and long-running wait tasks. +tools: Read, Grep, Glob, Bash +disallowedTools: Write, Edit +model: haiku +--- + +You are the Claude Code adapter for canonical `workflow_role=monitor`. + +- Stay read-only. +- Watch CI and required-check state. +- Report gate status, blockers, and next unblock actions with exact command context. diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-orchestrator.md b/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-orchestrator.md new file mode 100644 index 00000000..737af8b5 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-orchestrator.md @@ -0,0 +1,18 @@ +--- +name: plan-issue-orchestrator +description: Orchestrates the plan-issue-delivery workflow from the main thread. Use proactively for sprint orchestration, review gates, and final integration without direct product-code editing. +tools: Agent(plan-issue-implementation, plan-issue-review, plan-issue-monitor), Read, Grep, Glob, Bash +disallowedTools: Write, Edit +model: sonnet +--- + +You are the runtime adapter for the canonical `plan-issue-delivery` main agent. + +Your job is orchestration and review only. + +- Treat repo workflow docs and prompt snapshots as the source of truth. +- Dispatch implementation to `plan-issue-implementation`. +- Dispatch read-only audits to `plan-issue-review`. +- Dispatch long-running watches to `plan-issue-monitor`. +- Do not implement product-code changes directly. +- Keep dynamic issue/sprint/task facts in runtime artifacts, not here. diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-review.md b/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-review.md new file mode 100644 index 00000000..d0002d53 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-review.md @@ -0,0 +1,13 @@ +--- +name: plan-issue-review +description: Handles canonical workflow_role=review for plan-issue-delivery. Use for read-only PR audits, merged-diff checks, and plan-conformance evidence. +tools: Read, Grep, Glob, Bash +disallowedTools: Write, Edit +model: haiku +--- + +You are the Claude Code adapter for canonical `workflow_role=review`. + +- Stay read-only. +- Focus on review evidence, regressions, missing tests, and scope drift. +- Do not modify files, push commits, or merge PRs. diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-monitor.toml b/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-monitor.toml new file mode 100644 index 00000000..7977774b --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-monitor.toml @@ -0,0 +1,11 @@ +model_reasoning_effort = "medium" +sandbox_mode = "read-only" +approval_policy = "never" +developer_instructions = """ +You are the read-only monitor helper for plan-issue-delivery. +This local Codex adapter maps canonical `workflow_role=monitor`. + +Watch CI, required PR checks, and long-running status transitions. +Do not edit repository files, push commits, merge PRs, or close issues. +Report the current gate state, blocking checks, and the next unblock action with exact command/output context. +""" diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-reviewer.toml b/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-reviewer.toml new file mode 100644 index 00000000..2e918970 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-reviewer.toml @@ -0,0 +1,12 @@ +model_reasoning_effort = "high" +sandbox_mode = "read-only" +approval_policy = "never" +developer_instructions = """ +You are the read-only review helper for plan-issue-delivery. +This local Codex adapter maps canonical `workflow_role=review`. + +Prepare evidence for PR review, merged-diff audits, and plan-conformance checks. +Do not edit repository files, push commits, merge PRs, or close issues. +Prioritize regressions, missing tests, base-branch mismatches, and scope drift against the plan. +Return concise, traceable findings so the main agent can persist evidence artifacts and make decisions. +""" diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-worker.toml b/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-worker.toml new file mode 100644 index 00000000..c09b2132 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/agents/plan-issue-worker.toml @@ -0,0 +1,12 @@ +model_reasoning_effort = "high" +sandbox_mode = "danger-full-access" +approval_policy = "never" +developer_instructions = """ +You are the implementation-owned child agent for plan-issue-delivery. +This local Codex adapter maps canonical `workflow_role=implementation`. + +Operate only on the assigned task lane and worktree. +You may edit code, run tests, and update the assigned sprint PR. +Do not take orchestration actions such as sprint acceptance, plan close, or final merge decisions. +Keep changes scoped to the assigned task IDs and report blockers with exact lane facts. +""" diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-implementation.md b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-implementation.md new file mode 100644 index 00000000..2f1492b4 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-implementation.md @@ -0,0 +1,16 @@ +--- +description: Handles canonical workflow_role=implementation for plan-issue-delivery. Use for sprint lane coding, tests, and PR updates inside the assigned worktree. +mode: subagent +model: anthropic/claude-sonnet-4-20250514 +tools: + write: true + edit: true + bash: true +--- + +You are the OpenCode adapter for canonical `workflow_role=implementation`. + +- Implement only the assigned task lane. +- Follow the dispatch bundle and prompt snapshots. +- Run edits and tests only inside the assigned worktree. +- Do not make orchestration, acceptance, or close-plan decisions. diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-monitor.md b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-monitor.md new file mode 100644 index 00000000..f635a870 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-monitor.md @@ -0,0 +1,21 @@ +--- +description: Handles canonical workflow_role=monitor for plan-issue-delivery. Use for CI polling, required-check monitoring, and long-running wait tasks. +mode: subagent +model: anthropic/claude-haiku-4-20250514 +tools: + write: false + edit: false + bash: true +permission: + bash: + "*": "ask" + "gh pr checks*": "allow" + "gh run view*": "allow" + "git status*": "allow" +--- + +You are the OpenCode adapter for canonical `workflow_role=monitor`. + +- Stay read-only. +- Watch CI and required-check state. +- Report gate status, blockers, and next unblock actions with exact command context. diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-review.md b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-review.md new file mode 100644 index 00000000..40d93820 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-review.md @@ -0,0 +1,21 @@ +--- +description: Handles canonical workflow_role=review for plan-issue-delivery. Use for read-only PR audits, merged-diff checks, and plan-conformance evidence. +mode: subagent +model: anthropic/claude-haiku-4-20250514 +tools: + write: false + edit: false + bash: true +permission: + bash: + "*": "ask" + "git diff*": "allow" + "git log*": "allow" + "rg *": "allow" +--- + +You are the OpenCode adapter for canonical `workflow_role=review`. + +- Stay read-only. +- Focus on review evidence, regressions, missing tests, and scope drift. +- Do not modify files, push commits, or merge PRs. diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/prompts/plan-issue-orchestrator.txt b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/prompts/plan-issue-orchestrator.txt new file mode 100644 index 00000000..77d5a70e --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/.opencode/prompts/plan-issue-orchestrator.txt @@ -0,0 +1,10 @@ +You are the OpenCode adapter for the canonical `plan-issue-delivery` main agent. + +Operate as orchestration and review only. + +- Treat repo workflow docs and prompt snapshots as the source of truth. +- Route canonical `workflow_role=implementation` work to `plan-issue-implementation`. +- Route canonical `workflow_role=review` work to `plan-issue-review`. +- Route canonical `workflow_role=monitor` work to `plan-issue-monitor`. +- Do not implement product-code changes directly. +- Keep dynamic issue/sprint/task facts in runtime artifacts, not here. diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/opencode.json b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/opencode.json new file mode 100644 index 00000000..d4390b6c --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/opencode/project/opencode.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://opencode.ai/config.json", + "agent": { + "plan-issue-orchestrator": { + "description": "Main-thread adapter for plan-issue-delivery orchestration and review-only control flow.", + "mode": "primary", + "prompt": "{file:.opencode/prompts/plan-issue-orchestrator.txt}", + "tools": { + "write": false, + "edit": false, + "bash": true + }, + "permission": { + "task": { + "*": "deny", + "plan-issue-implementation": "allow", + "plan-issue-review": "allow", + "plan-issue-monitor": "allow" + } + } + } + } +} diff --git a/skills/automation/plan-issue-delivery/references/AGENT_ROLE_MAPPING.md b/skills/automation/plan-issue-delivery/references/AGENT_ROLE_MAPPING.md new file mode 100644 index 00000000..83477ef1 --- /dev/null +++ b/skills/automation/plan-issue-delivery/references/AGENT_ROLE_MAPPING.md @@ -0,0 +1,66 @@ +# Agent Role Mapping + +This document defines the canonical workflow-role contract for +`plan-issue-delivery` and shows how runtime-specific adapters may map those +roles to local child-agent features. + +## Canonical Roles + +- `implementation` + - Owns code changes, tests, and sprint PR updates for an assigned task lane. +- `review` + - Performs read-only audit work such as PR evidence gathering, merged-diff + checks, and plan-conformance analysis. +- `monitor` + - Performs read-only wait/watch work such as CI polling and required-check + monitoring. + +## Traceability Rules + +- `workflow_role` is required in prompt manifests and each + `DISPATCH_RECORD_PATH`. +- `runtime_name` / `runtime_role` are optional adapter fields and should be + recorded only when the active runtime supports named child-agent roles. +- If a named-role runtime falls back to a generic child agent, keep + `workflow_role` unchanged and record: + - `runtime_role=generic` + - `runtime_role_fallback_reason=` + +## Adapter Selection Policy + +- Codex, Claude Code, and OpenCode are peer runtime adapter candidates. +- No runtime adapter is the repo default. +- Choose the adapter that matches the active CLI, or use no named-role adapter + when the runtime does not support one. + +## Runtime Adapter Examples + +- Codex: + - `implementation -> plan_issue_worker` + - `review -> plan_issue_reviewer` + - `monitor -> plan_issue_monitor` + - guide + templates: `references/CODEX_ADAPTER.md` +- Claude Code: + - `implementation -> plan-issue-implementation` + - `review -> plan-issue-review` + - `monitor -> plan-issue-monitor` + - guide + templates: `references/CLAUDE_CODE_ADAPTER.md` +- OpenCode: + - `implementation -> plan-issue-implementation` + - `review -> plan-issue-review` + - `monitor -> plan-issue-monitor` + - guide + templates: `references/OPENCODE_ADAPTER.md` + +## Runtime Adapter Notes + +- Repo workflow docs should describe only canonical `workflow_role` behavior. +- Runtime-specific config stays outside the repo contract unless a project + explicitly chooses to version an adapter. +- Use the explicit installer/sync entrypoint when you want managed adapter setup: + - `$AGENT_HOME/scripts/plan-issue-adapter --runtime [--apply]` +- Runtimes without named child-agent role support can still execute the full + workflow by honoring `workflow_role` via prompt instructions alone. +- Runtime-specific installation examples: + - Codex: `references/CODEX_ADAPTER.md` + - Claude Code: `references/CLAUDE_CODE_ADAPTER.md` + - OpenCode: `references/OPENCODE_ADAPTER.md` diff --git a/skills/automation/plan-issue-delivery/references/CLAUDE_CODE_ADAPTER.md b/skills/automation/plan-issue-delivery/references/CLAUDE_CODE_ADAPTER.md new file mode 100644 index 00000000..05435332 --- /dev/null +++ b/skills/automation/plan-issue-delivery/references/CLAUDE_CODE_ADAPTER.md @@ -0,0 +1,64 @@ +# Claude Code Adapter + +This document shows how to map the canonical `plan-issue-delivery` +`workflow_role` contract onto Claude Code's native subagent system. + +## What Stays Canonical + +- The repo contract remains: + - `workflow_role=implementation|review|monitor` + - dispatch bundle and runtime artifacts + - `plan-issue-delivery-main-agent-init.md` + - `plan-issue-delivery-subagent-init.md` +- Claude-specific agent files are an adapter layer only. + +## Claude Code Features Used + +- Project-scoped subagents under `.claude/agents/` +- Optional user-scoped subagents under `~/.claude/agents/` +- YAML frontmatter fields such as `name`, `description`, `tools`, and `model` +- `Agent(...)` tool allowlists on the main-thread orchestrator agent + +## Recommended Mapping + +- Canonical `workflow_role=implementation` + - Claude subagent: `plan-issue-implementation` +- Canonical `workflow_role=review` + - Claude subagent: `plan-issue-review` +- Canonical `workflow_role=monitor` + - Claude subagent: `plan-issue-monitor` +- Optional Claude-only convenience entrypoint for the main thread: + - `plan-issue-orchestrator` + +## Project Adapter Files + +Templates live under: + +- `assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-orchestrator.md` +- `assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-implementation.md` +- `assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-review.md` +- `assets/runtime-adapters/claude-code/project/.claude/agents/plan-issue-monitor.md` + +Recommended install target in a project: + +- `.claude/agents/plan-issue-orchestrator.md` +- `.claude/agents/plan-issue-implementation.md` +- `.claude/agents/plan-issue-review.md` +- `.claude/agents/plan-issue-monitor.md` + +Optional installer/sync entrypoint: + +- `$AGENT_HOME/scripts/plan-issue-adapter install --runtime claude --project-path /path/to/project` +- `$AGENT_HOME/scripts/plan-issue-adapter sync --runtime claude --project-path /path/to/project --apply` +- `$AGENT_HOME/scripts/plan-issue-adapter status --runtime claude --project-path /path/to/project` + +## Usage Notes + +1. Keep dynamic issue/sprint/task facts in the dispatch bundle and prompt + snapshots, not in the Claude subagent files. +2. Use the orchestrator template only as a static capability router for the + main thread. It should not become a second source of workflow truth. +3. The implementation subagent should stay write-capable; review and monitor + templates stay read-only by tool restrictions. +4. If Claude-specific subagent files are unavailable, continue using the + canonical `workflow_role` contract without named runtime agents. diff --git a/skills/automation/plan-issue-delivery/references/CODEX_ADAPTER.md b/skills/automation/plan-issue-delivery/references/CODEX_ADAPTER.md new file mode 100644 index 00000000..570373e0 --- /dev/null +++ b/skills/automation/plan-issue-delivery/references/CODEX_ADAPTER.md @@ -0,0 +1,65 @@ +# Codex Adapter + +This document shows how to map the canonical `plan-issue-delivery` +`workflow_role` contract onto Codex's native child-agent role system. + +## What Stays Canonical + +- The repo contract remains: + - `workflow_role=implementation|review|monitor` + - dispatch bundle and runtime artifacts + - `plan-issue-delivery-main-agent-init.md` + - `plan-issue-delivery-subagent-init.md` +- Codex config under `~/.codex/` is an adapter layer only. + +## Codex Features Used + +- Local named child-agent roles in `~/.codex/config.toml` +- Per-role config files under `~/.codex/agents/` +- Runtime traceability fields such as `runtime_name` / `runtime_role` + +## Recommended Mapping + +- Canonical `workflow_role=implementation` + - Codex child-agent role: `plan_issue_worker` +- Canonical `workflow_role=review` + - Codex child-agent role: `plan_issue_reviewer` +- Canonical `workflow_role=monitor` + - Codex child-agent role: `plan_issue_monitor` +- The main Codex thread remains the orchestrator; no extra named orchestrator + role is required. + +## Adapter Templates + +Templates live under: + +- `assets/runtime-adapters/codex/home/.codex/config.toml` +- `assets/runtime-adapters/codex/home/.codex/agents/plan-issue-worker.toml` +- `assets/runtime-adapters/codex/home/.codex/agents/plan-issue-reviewer.toml` +- `assets/runtime-adapters/codex/home/.codex/agents/plan-issue-monitor.toml` + +Recommended install target on a machine running Codex: + +- `~/.codex/config.toml` +- `~/.codex/agents/plan-issue-worker.toml` +- `~/.codex/agents/plan-issue-reviewer.toml` +- `~/.codex/agents/plan-issue-monitor.toml` + +Optional installer/sync entrypoint: + +- `$AGENT_HOME/scripts/plan-issue-adapter install --runtime codex` +- `$AGENT_HOME/scripts/plan-issue-adapter sync --runtime codex --apply` +- `$AGENT_HOME/scripts/plan-issue-adapter status --runtime codex` + +## Usage Notes + +1. Keep dynamic issue/sprint/task facts in the dispatch bundle and prompt + snapshots, not in `~/.codex/config.toml`. +2. Because the Codex adapter is machine-scoped, treat these repo templates as + mergeable examples rather than project-truth config. +3. The installer merges only the managed `plan_issue_*` agent sections into an + existing `~/.codex/config.toml`; unrelated config stays untouched. +4. If named child-agent roles are unavailable, continue using the canonical + `workflow_role` contract without a Codex-specific adapter. +5. No runtime adapter is the repo default; choose Codex, Claude Code, OpenCode, + or no named-role adapter explicitly for the active environment. diff --git a/skills/automation/plan-issue-delivery/references/OPENCODE_ADAPTER.md b/skills/automation/plan-issue-delivery/references/OPENCODE_ADAPTER.md new file mode 100644 index 00000000..3081944c --- /dev/null +++ b/skills/automation/plan-issue-delivery/references/OPENCODE_ADAPTER.md @@ -0,0 +1,68 @@ +# OpenCode Adapter + +This document shows how to map the canonical `plan-issue-delivery` +`workflow_role` contract onto OpenCode's native agent system. + +## What Stays Canonical + +- The repo contract remains: + - `workflow_role=implementation|review|monitor` + - dispatch bundle and runtime artifacts + - `plan-issue-delivery-main-agent-init.md` + - `plan-issue-delivery-subagent-init.md` +- OpenCode config and agent files are an adapter layer only. + +## OpenCode Features Used + +- Project config in `opencode.json` +- Project agent markdown under `.opencode/agents/` +- Per-agent `mode`, `tools`, `permission`, `model`, and `prompt` +- `permission.task` to restrict which subagents a primary agent can invoke + +## Recommended Mapping + +- Canonical `workflow_role=implementation` + - OpenCode subagent: `plan-issue-implementation` +- Canonical `workflow_role=review` + - OpenCode subagent: `plan-issue-review` +- Canonical `workflow_role=monitor` + - OpenCode subagent: `plan-issue-monitor` +- Optional OpenCode-only convenience primary agent: + - `plan-issue-orchestrator` + +## Project Adapter Files + +Templates live under: + +- `assets/runtime-adapters/opencode/project/opencode.json` +- `assets/runtime-adapters/opencode/project/.opencode/prompts/plan-issue-orchestrator.txt` +- `assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-implementation.md` +- `assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-review.md` +- `assets/runtime-adapters/opencode/project/.opencode/agents/plan-issue-monitor.md` + +Recommended install target in a project: + +- `opencode.json` +- `.opencode/prompts/plan-issue-orchestrator.txt` +- `.opencode/agents/plan-issue-implementation.md` +- `.opencode/agents/plan-issue-review.md` +- `.opencode/agents/plan-issue-monitor.md` + +Optional installer/sync entrypoint: + +- `$AGENT_HOME/scripts/plan-issue-adapter install --runtime opencode --project-path /path/to/project` +- `$AGENT_HOME/scripts/plan-issue-adapter sync --runtime opencode --project-path /path/to/project --apply` +- `$AGENT_HOME/scripts/plan-issue-adapter status --runtime opencode --project-path /path/to/project` + +## Usage Notes + +1. Keep dynamic issue/sprint/task facts in the dispatch bundle and prompt + snapshots, not in `opencode.json`. +2. Use `permission.task` on the optional orchestrator agent to allow only the + three plan-issue subagents. +3. The installer merges only `agent.plan-issue-orchestrator` into an existing + `opencode.json`; unrelated keys stay untouched. +4. Keep `plan-issue-review` and `plan-issue-monitor` read-only by disabling + edit/write and tightening bash permissions. +5. If project-level OpenCode agents are unavailable, continue using the + canonical `workflow_role` contract without named runtime agents. diff --git a/skills/automation/plan-issue-delivery/references/RUNTIME_LAYOUT.md b/skills/automation/plan-issue-delivery/references/RUNTIME_LAYOUT.md index d2363a4e..908acb5f 100644 --- a/skills/automation/plan-issue-delivery/references/RUNTIME_LAYOUT.md +++ b/skills/automation/plan-issue-delivery/references/RUNTIME_LAYOUT.md @@ -49,7 +49,8 @@ This document defines the canonical runtime path layout for `plan-issue-delivery - Task-scoped dispatch record: - `DISPATCH_RECORD_PATH="$SPRINT_ROOT/manifests/dispatch-.json"` - Expected keys: `task_id`, `task_prompt_path`, `subagent_init_snapshot_path`, `plan_snapshot_path`, `worktree`, `branch`, - `execution_mode`, `pr_group`, `base_branch` + `execution_mode`, `pr_group`, `base_branch`, `workflow_role`, optional `runtime_name`, optional `runtime_role`, + optional `runtime_role_fallback_reason` ## Worktree Layout (Assigned Paths) diff --git a/skills/automation/plan-issue-delivery/tests/test_automation_plan_issue_delivery.py b/skills/automation/plan-issue-delivery/tests/test_automation_plan_issue_delivery.py index e63221be..6320fd64 100644 --- a/skills/automation/plan-issue-delivery/tests/test_automation_plan_issue_delivery.py +++ b/skills/automation/plan-issue-delivery/tests/test_automation_plan_issue_delivery.py @@ -16,6 +16,9 @@ def test_plan_issue_delivery_skill_enforces_main_agent_role_boundary() -> None: text = (skill_root / "SKILL.md").read_text(encoding="utf-8") assert "Main-agent is orchestration/review-only." in text assert "Main-agent does not implement sprint tasks directly." in text + assert "workflow_role=implementation" in text + assert "workflow_role=review" in text + assert "workflow_role=monitor" in text assert "subagent-owned PRs" in text assert "1 plan = 1 issue" in text assert "PR grouping controls" in text @@ -90,7 +93,10 @@ def test_plan_issue_delivery_skill_defines_runtime_workspace_policy() -> None: assert "PLAN_SNAPSHOT_PATH" in text assert "SUBAGENT_INIT_SNAPSHOT_PATH" in text assert "DISPATCH_RECORD_PATH" in text + assert "workflow_role" in text + assert "runtime_role_fallback_reason" in text assert "references/RUNTIME_LAYOUT.md" in text + assert "references/AGENT_ROLE_MAPPING.md" in text def test_plan_issue_delivery_skill_uses_shared_task_lane_policy() -> None: @@ -126,12 +132,15 @@ def test_plan_issue_delivery_prompts_align_runtime_and_dispatch_bundle() -> None subagent_prompt = (repo_root / "prompts" / "plan-issue-delivery-subagent-init.md").read_text(encoding="utf-8") main_agent_prompt = (repo_root / "prompts" / "plan-issue-delivery-main-agent-init.md").read_text(encoding="utf-8") + role_mapping = (skill_root / "references" / "AGENT_ROLE_MAPPING.md").read_text(encoding="utf-8") assert "PLAN_SNAPSHOT_PATH" in subagent_prompt assert "SUBAGENT_INIT_SNAPSHOT_PATH" in subagent_prompt assert "DISPATCH_RECORD_PATH" in subagent_prompt assert "$AGENT_HOME/out/plan-issue-delivery" in subagent_prompt assert "PLAN_BRANCH" in subagent_prompt + assert "workflow_role" in subagent_prompt + assert "runtime_role" in subagent_prompt assert "PLAN_SNAPSHOT_PATH" in main_agent_prompt assert "MAIN_AGENT_INIT_SNAPSHOT_PATH" in main_agent_prompt @@ -140,6 +149,9 @@ def test_plan_issue_delivery_prompts_align_runtime_and_dispatch_bundle() -> None assert "SUBAGENT_INIT_SNAPSHOT_PATH" in main_agent_prompt assert "DISPATCH_RECORD_PATH" in main_agent_prompt assert "PLAN_BRANCH" in main_agent_prompt + assert "workflow_role" in main_agent_prompt + assert "runtime_role" in main_agent_prompt + assert "runtime_role_fallback_reason" in main_agent_prompt assert "PLAN_BRANCH_REF_PATH" in main_agent_prompt assert "PLAN_INTEGRATION_PR_PATH" in main_agent_prompt assert "PLAN_INTEGRATION_MENTION_PATH" in main_agent_prompt @@ -159,6 +171,46 @@ def test_plan_issue_delivery_prompts_align_runtime_and_dispatch_bundle() -> None assert "--next-owner" in main_agent_prompt assert "--close-reason" in main_agent_prompt assert "--enforce-review-evidence" in main_agent_prompt + assert "implementation -> plan_issue_worker" in role_mapping + assert "review -> plan_issue_reviewer" in role_mapping + assert "monitor -> plan_issue_monitor" in role_mapping + + +def test_plan_issue_delivery_runtime_adapter_docs_and_templates_exist() -> None: + skill_root = Path(__file__).resolve().parents[1] + codex_doc = (skill_root / "references" / "CODEX_ADAPTER.md").read_text(encoding="utf-8") + claude_doc = (skill_root / "references" / "CLAUDE_CODE_ADAPTER.md").read_text(encoding="utf-8") + opencode_doc = (skill_root / "references" / "OPENCODE_ADAPTER.md").read_text(encoding="utf-8") + role_mapping = (skill_root / "references" / "AGENT_ROLE_MAPPING.md").read_text(encoding="utf-8") + + assert "~/.codex/config.toml" in codex_doc + assert "plan_issue_worker" in codex_doc + assert "No runtime adapter is the repo default" in codex_doc + + assert ".claude/agents/" in claude_doc + assert "plan-issue-orchestrator" in claude_doc + assert "plan-issue-implementation" in claude_doc + + assert "opencode.json" in opencode_doc + assert ".opencode/agents/" in opencode_doc + assert "permission.task" in opencode_doc + + assert "Codex" in role_mapping + assert "Claude Code" in role_mapping + assert "OpenCode" in role_mapping + assert "No runtime adapter is the repo default." in role_mapping + + codex_config = skill_root / "assets" / "runtime-adapters" / "codex" / "home" / ".codex" / "config.toml" + codex_worker = skill_root / "assets" / "runtime-adapters" / "codex" / "home" / ".codex" / "agents" / "plan-issue-worker.toml" + claude_template = skill_root / "assets" / "runtime-adapters" / "claude-code" / "project" / ".claude" / "agents" / "plan-issue-orchestrator.md" + opencode_config = skill_root / "assets" / "runtime-adapters" / "opencode" / "project" / "opencode.json" + opencode_prompt = skill_root / "assets" / "runtime-adapters" / "opencode" / "project" / ".opencode" / "prompts" / "plan-issue-orchestrator.txt" + + assert codex_config.exists() + assert codex_worker.exists() + assert claude_template.exists() + assert opencode_config.exists() + assert opencode_prompt.exists() def test_plan_issue_delivery_runtime_layout_tracks_main_agent_snapshot_artifacts() -> None: @@ -172,6 +224,8 @@ def test_plan_issue_delivery_runtime_layout_tracks_main_agent_snapshot_artifacts assert "PLAN_BRANCH_REF_PATH" in text assert "PLAN_INTEGRATION_PR_PATH" in text assert "PLAN_INTEGRATION_MENTION_PATH" in text + assert "workflow_role" in text + assert "runtime_role_fallback_reason" in text assert "syncs local `PLAN_BRANCH`" in text assert "syncs local `DEFAULT_BRANCH`" in text assert "issue runtime initialization" in text diff --git a/skills/automation/plan-issue-delivery/tests/test_automation_plan_issue_delivery_adapter_installer.py b/skills/automation/plan-issue-delivery/tests/test_automation_plan_issue_delivery_adapter_installer.py new file mode 100644 index 00000000..a02c5020 --- /dev/null +++ b/skills/automation/plan-issue-delivery/tests/test_automation_plan_issue_delivery_adapter_installer.py @@ -0,0 +1,115 @@ +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + + +def _repo_root() -> Path: + return Path(__file__).resolve().parents[4] + + +def _run_adapter(*args: str, cwd: Path | None = None) -> subprocess.CompletedProcess[str]: + repo = _repo_root() + cmd = [sys.executable, str(repo / "scripts" / "plan-issue-adapter"), *args] + return subprocess.run( + cmd, + cwd=str(cwd or repo), + text=True, + capture_output=True, + check=False, + ) + + +def test_plan_issue_adapter_claude_install_dry_run_does_not_write_files(tmp_path: Path) -> None: + project = tmp_path / "project" + project.mkdir() + + completed = _run_adapter( + "install", + "--runtime", + "claude", + "--project-path", + str(project), + ) + + assert completed.returncode == 0, completed.stderr + assert "mode: dry-run" in completed.stdout + assert "runtime: claude" in completed.stdout + assert "create" in completed.stdout + assert not (project / ".claude").exists() + + +def test_plan_issue_adapter_codex_install_merges_managed_sections(tmp_path: Path) -> None: + home = tmp_path / "home" + codex_root = home / ".codex" + codex_root.mkdir(parents=True) + config_path = codex_root / "config.toml" + config_path.write_text( + 'model = "gpt-5.4"\n\n[agents.existing]\ndescription = "keep-me"\n', + encoding="utf-8", + ) + + completed = _run_adapter( + "install", + "--runtime", + "codex", + "--home-path", + str(home), + "--apply", + ) + + assert completed.returncode == 0, completed.stderr + config_text = config_path.read_text(encoding="utf-8") + assert 'model = "gpt-5.4"' in config_text + assert '[agents.existing]' in config_text + assert "[agents.plan_issue_worker]" in config_text + assert "[agents.plan_issue_reviewer]" in config_text + assert "[agents.plan_issue_monitor]" in config_text + assert (codex_root / "agents" / "plan-issue-worker.toml").exists() + assert (codex_root / "agents" / "plan-issue-reviewer.toml").exists() + assert (codex_root / "agents" / "plan-issue-monitor.toml").exists() + + +def test_plan_issue_adapter_opencode_sync_merges_existing_json(tmp_path: Path) -> None: + project = tmp_path / "project" + project.mkdir() + opencode_path = project / "opencode.json" + opencode_path.write_text( + json.dumps( + { + "$schema": "https://opencode.ai/config.json", + "agent": { + "existing-agent": {"mode": "subagent"}, + "plan-issue-orchestrator": { + "description": "old", + "mode": "primary", + }, + }, + "theme": "custom", + }, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + + completed = _run_adapter( + "sync", + "--runtime", + "opencode", + "--project-path", + str(project), + "--apply", + ) + + assert completed.returncode == 0, completed.stderr + updated = json.loads(opencode_path.read_text(encoding="utf-8")) + assert updated["theme"] == "custom" + assert "existing-agent" in updated["agent"] + orchestrator = updated["agent"]["plan-issue-orchestrator"] + assert orchestrator["mode"] == "primary" + assert "plan-issue-delivery orchestration" in orchestrator["description"] + assert (project / ".opencode" / "prompts" / "plan-issue-orchestrator.txt").exists() + assert (project / ".opencode" / "agents" / "plan-issue-review.md").exists() diff --git a/skills/workflows/issue/issue-subagent-pr/SKILL.md b/skills/workflows/issue/issue-subagent-pr/SKILL.md index 9bda9d4d..37e1f7de 100644 --- a/skills/workflows/issue/issue-subagent-pr/SKILL.md +++ b/skills/workflows/issue/issue-subagent-pr/SKILL.md @@ -32,6 +32,7 @@ Inputs: - `PLAN_SNAPSHOT_PATH` when dispatch came from `plan-issue-delivery` - `SUBAGENT_INIT_SNAPSHOT_PATH` when dispatch came from `plan-issue-delivery` - `DISPATCH_RECORD_PATH` when dispatch came from `plan-issue-delivery` + (including `workflow_role` and optional runtime adapter metadata) - plan task section context (exact snippet and/or direct link/path) - Required implementation context in local rehearsal mode: - local rendered task prompt/artifacts and plan task context (no GitHub lookup for placeholder issues such as `999`) @@ -67,6 +68,11 @@ Failure modes: - `plan-issue-delivery` mode: missing `PLAN_SNAPSHOT_PATH` fallback artifact from dispatch. - `plan-issue-delivery` mode: missing `SUBAGENT_INIT_SNAPSHOT_PATH` companion prompt snapshot artifact from dispatch. - `plan-issue-delivery` mode: missing `DISPATCH_RECORD_PATH` assignment artifact from dispatch. +- `plan-issue-delivery` mode: `DISPATCH_RECORD_PATH` missing canonical + `workflow_role`. +- `plan-issue-delivery` mode: `DISPATCH_RECORD_PATH` declares a non-implementation + `workflow_role` for implementation execution. +- Named-role runtime fallback used but `runtime_role_fallback_reason` missing. - Missing/empty base-branch assignment from dispatch (for example `PLAN_BRANCH` not provided in `plan-issue-delivery` mode). - Context mismatch between issue artifacts and main-agent dispatch artifacts (scope, ownership, branch/worktree, execution mode). - Context mismatch where linked/opened PR base branch differs from assigned base branch. @@ -130,6 +136,8 @@ Failure modes: - `PLAN_SNAPSHOT_PATH` (required in `plan-issue-delivery` mode) - `SUBAGENT_INIT_SNAPSHOT_PATH` (required in `plan-issue-delivery` mode) - `DISPATCH_RECORD_PATH` (required in `plan-issue-delivery` mode) + - `workflow_role` from `DISPATCH_RECORD_PATH` + - optional `runtime_name` / `runtime_role` from `DISPATCH_RECORD_PATH` - plan task section snippet/link/path 3. Reconcile context and apply hard start gate: @@ -137,6 +145,11 @@ Failure modes: - Confirm assigned task facts align across sources: owner, branch, worktree, execution mode, task scope, and acceptance intent. - In `plan-issue-delivery` mode, confirm `DISPATCH_RECORD_PATH` facts match assigned task row (`Task Decomposition`) and runtime artifact paths. + - In `plan-issue-delivery` mode, confirm `workflow_role` is + `implementation` before starting implementation. + - If runtime adapter metadata is present, confirm it is internally + consistent; when `runtime_role=generic`, require a recorded fallback + rationale. - Confirm assigned base branch is present; in `plan-issue-delivery` mode it must match dispatched `PLAN_BRANCH`. - In `plan-issue-delivery` mode, enforce `WORKTREE` prefix: `$AGENT_HOME/out/plan-issue-delivery/`. diff --git a/skills/workflows/issue/issue-subagent-pr/tests/test_workflows_issue_issue_subagent_pr.py b/skills/workflows/issue/issue-subagent-pr/tests/test_workflows_issue_issue_subagent_pr.py index fb9d0397..d524cadb 100644 --- a/skills/workflows/issue/issue-subagent-pr/tests/test_workflows_issue_issue_subagent_pr.py +++ b/skills/workflows/issue/issue-subagent-pr/tests/test_workflows_issue_issue_subagent_pr.py @@ -19,6 +19,8 @@ def test_issue_subagent_pr_skill_mentions_worktree_isolation() -> None: assert "$AGENT_HOME/out/plan-issue-delivery" in text assert "SUBAGENT_INIT_SNAPSHOT_PATH" in text assert "DISPATCH_RECORD_PATH" in text + assert "workflow_role" in text + assert "implementation" in text def test_issue_subagent_pr_skill_requires_native_git_gh_commands() -> None: diff --git a/tests/script_specs/scripts/plan-issue-adapter.json b/tests/script_specs/scripts/plan-issue-adapter.json new file mode 100644 index 00000000..f4c440c2 --- /dev/null +++ b/tests/script_specs/scripts/plan-issue-adapter.json @@ -0,0 +1,13 @@ +{ + "smoke": [ + { + "name": "help", + "args": ["--help"], + "timeout_sec": 5, + "expect": { + "exit_codes": [0], + "stdout_regex": "(?im)^usage: plan-issue-adapter" + } + } + ] +} From 2af78723d68f0502fd15244bc16ab10b608a4ae5 Mon Sep 17 00:00:00 2001 From: graysurf <10785178+graysurf@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:13:14 +0800 Subject: [PATCH 2/2] fix(plan-issue-delivery): track codex adapter config template --- .../codex/home/.codex/config.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/config.toml diff --git a/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/config.toml b/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/config.toml new file mode 100644 index 00000000..45e7af69 --- /dev/null +++ b/skills/automation/plan-issue-delivery/assets/runtime-adapters/codex/home/.codex/config.toml @@ -0,0 +1,15 @@ +[agents] +max_threads = 6 +max_depth = 1 + +[agents.plan_issue_worker] +description = "Implementation lane owner for plan-issue-delivery sprint work." +config_file = "agents/plan-issue-worker.toml" + +[agents.plan_issue_reviewer] +description = "Read-only audit helper for PR review evidence and plan-conformance checks." +config_file = "agents/plan-issue-reviewer.toml" + +[agents.plan_issue_monitor] +description = "Read-only watcher for CI, PR checks, and long-running status polling." +config_file = "agents/plan-issue-monitor.toml"