name: scope-decompose-sonnet version: 2 description: Scope and decompose a spec into tagged tasks using Sonnet planning (CP-side apply-verified; WF-COMP-33 / WIA-47) runtime: spec-writing-sonnet-repo input: state: spec-reviewed-arch tags_forbidden: [] required_artifacts: [] output: state: scoped-tagged scope_budget: files_modified_max: 10 lines_diff_max: 500 wall_clock_seconds_max: 2400 nodes: # WF-COMP-33 / WIA-47 / WV-36..37 — the agent's only side effect is the # declared output `plan_manifest` plus the per-child item files under # `items/`; `finalize_run` reads, validates and applies them. # The agent does NOT call the CP and does NOT read any internal-service # CP credential file. - id: agent kind: agent outputs: [plan_manifest] template: spec-writing-sonnet-repo model: sonnet runtime_env: AGENT_REPO_URL: "git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes-agents.git" AGENT_BRANCH: "spec/${item.uuid}" AGENT_UPSTREAM_REPO_URL: "git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes.git" AGENT_UPSTREAM_BRANCH: "main" CP_URL: "https://agents.oreillyit.nz/api" # The deliverable is a directory of plan files on disk — NOT a git diff. AGENT_EMPTY_DELIVERABLE_CHECK: "false" prompt: | The agent-runtimes project repo is cloned at /workspace/project. Change to that directory before doing any work. Find the spec file in /workspace/project/spec/ (the spec was written in an earlier pipeline stage — look for a file whose name matches the concept title '${item.title}' as a slug, e.g. spec/.md). Read that spec carefully. YOUR ONLY DELIVERABLE FOR THIS STAGE IS THE DECOMPOSE PLAN. You do NOT call the CP, you do NOT run `cp-cli planner items decompose`, and you do NOT read any internal-service CP credential file (the path that the legacy `@1` flow used for its CP write is no longer mounted into your container). The CP applies your plan from disk at finalize time (spec/work-items-api.md WIA-47..47g). This node's declared output is the literal key `plan_manifest`; the CP packs the manifest plus every per-child item file in the same step folder. On-disk layout (every path is relative to your step folder): /variables/agent/plan_manifest # the manifest (plain text) /variables/agent/items/ # one item file per child `` matches `^[a-z0-9][a-z0-9-]{0,31}$` — lowercase letters, digits, and hyphen; 1–32 characters; no underscore. Use the literal `$root` as the parent header on every item; the CP substitutes the real item UUID server-side (WV-36 / WIA-47e). Write each item file with a quoted heredoc whose terminator cannot occur in the body — example for a child `c1`: cat > /variables/agent/items/c1 <<'__DECOMPOSE_ITEM_END__' parent: $root title: Implement payment service FP-1..3 spec_ids: FP-1,FP-2,FP-3 test_files: tests/payments/test_fp.py tags: airouter-eligible --- Write the payment service implementation covering FP-1, FP-2, FP-3. Acceptance criteria: - FP-1: idempotent payment capture - FP-2: refund flow __DECOMPOSE_ITEM_END__ Do NOT use `wfvar set ` for plan files — it passes the body as argv and breaks on backticks, quotes and `$(...)` and truncates at ARG_MAX. The file-write / heredoc path is byte-faithful. Manifest format — first line exactly `decompose/v1`, then one `local_id` per line, no blank lines, no header, no body. Example for the three-item plan above: decompose/v1 c1 c2 c3 Item header grammar (every line is `key: value`): - closed header set: {parent, title, spec_ids, test_files, tags} - `parent` and `title` are REQUIRED on every item - `parent` MUST be the literal `$root` (a work-item UUID is rejected as `parent_must_be_root` — privilege-escalation guard) - `title` is 1–200 bytes UTF-8, unique within one plan compared case- and whitespace-insensitively - `spec_ids` and `test_files` are comma-separated, at most 50 entries each; `spec_ids` entries match `^[A-Z][A-Z0-9-]{0,31}$` (e.g. `FP-1`, `WV-36`) - `test_files` entries have no leading `/`, no `..` segment, no SP or `,`, at most 250 bytes each (e.g. `tests/payments/test_fp.py`) - `tags` is optional; every tag MUST be exactly `airouter-eligible` (the only tag in DECOMPOSE_AGENT_TAG_ALLOWLIST — anything else is `tag_not_allowed`). Omit the `tags:` line for tasks Qwen3.6 cannot handle alone. - `---` ends the header block; every byte after it to EOF is the body, verbatim (printable ASCII + UTF-8 bytes 0x80–0xFF, plus LF and TAB as the only permitted control bytes; CR is rejected). The body must be ≤ 16,384 bytes UTF-8. - one header per item, no duplicates (`duplicate_header`) - unknown keys are rejected (`unknown_header`) - the file MUST be strict UTF-8, LF line endings (CRLF is rejected) Plan fan-out: 1–20 `local_id` lines in the manifest, exactly one `items/` file per manifest line (an orphan file or a missing file fails the apply as `orphan_item_file` / `manifest_entry_without_file`). Title / slug uniqueness: every planned title must be unique within the plan (`duplicate_title`); every planned slug (`decompose_validation.slugify(title)`, lowercase, non-alphanum runs collapsed to `-`, truncated to 80 chars) must also be unique (`duplicate_slug`). Two titles that differ only in case / whitespace are NOT distinct. "airouter-eligible" is the only tag you may use. Do NOT emit `auto-dispatch`, `integrated`, `ai-resolve` or `airouter-skip` — the validator rejects those at apply time. Workflow: 1. Read the spec carefully. 2. Decide the children (1–20). Draft a title, a one-paragraph body, and the `spec_ids` / `test_files` for each. 3. Write `/variables/agent/plan_manifest` with a heredoc (the heredoc terminator MUST be unique to that file — do not reuse `__DECOMPOSE_ITEM_END__` across files). 4. Write each `/variables/agent/items/` with a heredoc. 5. Confirm the manifest names exactly the files you wrote, and that no extra `items/*` files exist (an orphan file fails the apply). 6. Call task_complete. Do NOT PATCH the CP yourself.