From fcd736209f7efeada04720d4d90fb5cef8c51207 Mon Sep 17 00:00:00 2001 From: Ben Firestone Date: Fri, 17 Jul 2026 13:19:16 -0700 Subject: [PATCH] feat(pi-arc): sync arc workflow resources --- packages/pi-arc/README.md | 39 +- packages/pi-arc/agents/builder.md | 4 +- packages/pi-arc/agents/code-reviewer.md | 4 +- packages/pi-arc/agents/devops-builder.md | 185 +++++++ packages/pi-arc/agents/doc-writer.md | 2 +- packages/pi-arc/agents/evaluator.md | 22 +- packages/pi-arc/agents/issue-manager.md | 49 +- packages/pi-arc/agents/spec-reviewer.md | 4 +- packages/pi-arc/extensions/arc.ts | 44 +- .../extensions/arc/model-profiles-ui.ts | 18 +- .../pi-arc/extensions/arc/model-profiles.ts | 1 + packages/pi-arc/extensions/arc/subagents.ts | 1 + packages/pi-arc/scripts/migrate-arc-plugin.py | 476 ++++++++++++++---- .../pi-arc/skills/arc-brainstorm/SKILL.md | 144 +++--- packages/pi-arc/skills/arc-build/SKILL.md | 117 +++-- .../pi-arc/skills/arc-build/builder-prompt.md | 4 + .../skills/arc-build/devops-builder-prompt.md | 70 +++ .../skills/arc-build/evaluator-prompt.md | 5 + .../arc-build/references/devops-patterns.md | 115 +++++ packages/pi-arc/skills/arc-debug/SKILL.md | 2 +- packages/pi-arc/skills/arc-plan/SKILL.md | 252 ++++++---- packages/pi-arc/skills/arc-review/SKILL.md | 8 +- packages/pi-arc/skills/arc-summarize/SKILL.md | 416 +++++++++++++++ packages/pi-arc/skills/arc-verify/SKILL.md | 4 +- packages/pi-arc/skills/arc/SKILL.md | 51 +- .../arc-model-profiles-contract.test.mjs | 2 +- .../arc-model-profiles-integration.test.mjs | 34 +- .../tests/arc-model-profiles-ui.test.mjs | 19 +- .../pi-arc/tests/arc-source-sync.test.mjs | 36 +- ...rc-subagents-auto-materialization.test.mjs | 7 +- .../pi-arc/tests/arc-subagents-sync.test.mjs | 5 +- .../tests/ask-user-question-package.test.mjs | 9 +- .../upstream-workflow-contracts.test.mjs | 171 +++++++ 33 files changed, 1836 insertions(+), 484 deletions(-) create mode 100644 packages/pi-arc/agents/devops-builder.md create mode 100644 packages/pi-arc/skills/arc-build/devops-builder-prompt.md create mode 100644 packages/pi-arc/skills/arc-build/references/devops-patterns.md create mode 100644 packages/pi-arc/skills/arc-summarize/SKILL.md create mode 100644 packages/pi-arc/tests/upstream-workflow-contracts.test.mjs diff --git a/packages/pi-arc/README.md b/packages/pi-arc/README.md index e6e7768..dbedcab 100644 --- a/packages/pi-arc/README.md +++ b/packages/pi-arc/README.md @@ -24,6 +24,7 @@ This package is a Pi-native port of the Claude Code Arc plugin at https://github - `/skill:arc-build` - `/skill:arc-debug` - `/skill:arc-review` + - `/skill:arc-summarize` — mirror an Arc issue into a connected external tracker - `/skill:arc-verify` - `/skill:arc-finish` - **Extension commands**: @@ -51,8 +52,8 @@ This package is a Pi-native port of the Claude Code Arc plugin at https://github - When Arc recommends an option, list it first, append `(Recommended)` to the label, and explain why in the description. - **`arc_agent` tool**: - Runs bundled Arc specialist prompts from `agents/*.md` in fresh Pi subprocesses. - - Supports `builder`, `code-reviewer`, `doc-writer`, `evaluator`, `issue-manager`, and `spec-reviewer`. - - Resolves Arc model tiers (`small`, `standard`, `large`) to concrete Pi models so orchestrators can right-size subagent dispatches. + - Supports `builder`, `devops-builder`, `code-reviewer`, `doc-writer`, `evaluator`, `issue-manager`, and `spec-reviewer`. + - Resolves Arc model tiers (`nano`, `small`, `standard`, `large`) to concrete Pi models so orchestrators can right-size subagent dispatches. - Current limitation: `isolation: "worktree"` is recognized but not implemented yet. - **Optional `pi-subagents` companion support**: - `@sentiolabs/pi-arc` auto-materializes Arc specialist definitions for any installed `pi-subagents` provider, but does not bundle or load the `subagent` tool itself. @@ -156,42 +157,42 @@ You can also invoke skills directly: ## Arc vs `todo` boundary -Use Arc for persistent, auditable issue tracking across sessions (`arc create`, `arc update`, dependencies, plan shares, and closure history). Use bundled `rpiv-todo` (`todo` tool + `/todos` + overlay) for visible, in-session checklists while you execute the current workflow. +Use Arc for persistent, auditable issue tracking across sessions (`arc create`, `arc update`, dependencies, planner reviews, and closure history). Use bundled `rpiv-todo` (`todo` tool + `/todos` + overlay) for visible, in-session checklists while you execute the current workflow. -## Plan review surfaces +## Plan review surface -`/skill:arc-brainstorm` writes design docs under `docs/plans/` and asks how to register them for review: +`/skill:arc-brainstorm` writes design docs under `docs/plans/` and can register them on the local Arc planner with `arc plan create --no-frontmatter `. Arc uses `--no-frontmatter` because the Pi workflow owns the first-line HTML review marker and must not mix it with Arc-generated YAML frontmatter. The planner provides a plain-HTTP markdown view and comment thread at `/planner/`. -- Legacy local planner: `arc plan create ` for a simple local-only comment thread. -- Encrypted local share: `arc share create ` for encrypted local review with annotations. -- Encrypted remote share: `arc share create --remote` for reviewers on other machines. - -The brainstorm skill writes a first-line marker like ``; `/skill:arc-plan` reads that marker to choose the matching `arc plan` or `arc share` commands. +The brainstorm skill writes a first-line marker like ``; `/skill:arc-plan` reads that ID for `arc plan show`, `arc plan comments`, and `arc plan approve`. Designs can also be saved locally without registration for later work. ## Arc model profiles Use `/arc-models` to configure Arc's recommended Pi model and thinking level per workflow role. Arc stores profile preferences at `${XDG_CONFIG_HOME:-~/.config}/pi-arc/models.json`, with top-level `modelProfiles`. -Profile keys map directly to the workflow roles: `brainstorm`, `plan`, `issueManager`, `builder`, `codeReviewer`, `docWriter`, `specReviewer`, and `evaluator`. +Profile keys map directly to the workflow roles: `brainstorm`, `plan`, `issueManager`, `builder`, `devopsBuilder`, `codeReviewer`, `docWriter`, `specReviewer`, and `evaluator`. ```json { "version": 1, "modelProfiles": { "brainstorm": { - "model": "openai-codex/gpt-5.5", + "model": "openai-codex/gpt-5.6-sol", "thinking": "high" }, "issueManager": { - "model": "openai-codex/gpt-5.4-mini", + "model": "openai-codex/gpt-5.6-luna", "thinking": "off" }, "builder": { - "model": "openai-codex/gpt-5.3-codex", + "model": "openai-codex/gpt-5.6-terra", "thinking": "medium" }, + "devopsBuilder": { + "model": "openai-codex/gpt-5.6-sol", + "thinking": "high" + }, "codeReviewer": { - "model": "openai-codex/gpt-5.5", + "model": "openai-codex/gpt-5.6-sol", "thinking": "high" } } @@ -200,7 +201,7 @@ Profile keys map directly to the workflow roles: `brainstorm`, `plan`, `issueMan `/arc-models` lists only models returned by Pi's active model registry. If a configured model is unavailable, it prompts you to choose a replacement before saving. -The same `modelProfiles` shape works for `plan`, `docWriter`, `specReviewer`, and `evaluator` profiles. +The same `modelProfiles` shape works for `plan`, `devopsBuilder`, `docWriter`, `specReviewer`, and `evaluator` profiles. Based on [OpenAI's GPT-5.6 preview](https://openai.com/index/previewing-gpt-5-6-sol/), the family maps naturally onto Arc's tiers: Luna for fast/affordable `nano` and `small` work, Terra for balanced `standard` implementation, and Sol for `large` design, operations, review, and adversarial evaluation. `devopsBuilder` defaults high because live-system operations require blast-radius and rollback judgment. Legacy `arc.modelTiers` settings in `~/.pi/agent/settings.json` or project `.pi/settings.json` remain supported as a compatibility fallback, but new configuration should use `/arc-models` and `modelProfiles`. @@ -214,6 +215,7 @@ Arc writes generated specialists to `~/.agents/` by default. Legacy user scope ` Generated specialists include: - `arc-builder` +- `arc-devops-builder` - `arc-doc-writer` - `arc-spec-reviewer` - `arc-code-reviewer` @@ -230,7 +232,7 @@ Existing standalone installs under `~/.pi/agent/extensions/subagent` also work. For repair/backcompat only, `/arc-subagents-sync project` can explicitly refresh legacy project-scope generated files under `/.pi/agents/`; normal activation writes user-scope files automatically. -The `issue-manager` agent uses the issueManager profile (recommended gpt-5.4-mini with thinking off) and stays phased: create the epic first, then child tasks next, then dependencies/labels after all IDs exist. It prints phase-level timing/progress lines for bulk issue creation. This is sequencing only; true parallel issue creation is not enabled yet. +The `issue-manager` agent uses the issueManager profile (recommended gpt-5.6-luna with thinking off) and stays phased: create the epic first, then child tasks next, then dependencies/labels after all IDs exist. It prints phase-level timing/progress lines for bulk issue creation. This is sequencing only; true parallel issue creation is not enabled yet. Generated files include a marker comment so reruns can safely update Arc-managed files while preserving manual edits in user-authored files. @@ -281,7 +283,8 @@ Implemented: - Bundled `@juicesharp/rpiv-ask-user-question` package for interactive workflow decisions - Pi-native `arc_agent` custom tool for sequential subagent execution - Auto-materialized Arc specialists in pi-subagents user scope; `/arc-subagents-sync` is deprecated repair/backcompat only -- Optional `pi-subagents` integration for worktree-isolated evaluator runs, independent parallel builder batches, and phased issue-manager creation +- Optional `pi-subagents` integration for worktree-isolated evaluator runs, independent parallel builder batches, phased issue-manager creation, and a dedicated sequential DevOps builder +- External-tracker mirroring through `/arc-summarize` using connected MCP tools or authenticated CLIs Not yet implemented: diff --git a/packages/pi-arc/agents/builder.md b/packages/pi-arc/agents/builder.md index 5525793..42231cd 100644 --- a/packages/pi-arc/agents/builder.md +++ b/packages/pi-arc/agents/builder.md @@ -1,5 +1,5 @@ --- -description: Use this agent for implementing a single task using TDD. Dispatched by the implement skill with a task description from arc. Receives task context, implements following RED → GREEN → REFACTOR → GATE, commits results, and reports back. +description: Use this agent for implementing a single task using TDD. Dispatched by the build skill with a task description from arc. Receives task context, implements following RED → GREEN → REFACTOR → GATE, commits results, and reports back. tools: - bash - read @@ -10,7 +10,7 @@ tools: model: standard --- -# Arc Implementer Agent +# Arc Builder Agent You are an implementation agent. You receive a single task, implement it using test-driven development, verify your own work against the spec, and report results back to the dispatching agent. diff --git a/packages/pi-arc/agents/code-reviewer.md b/packages/pi-arc/agents/code-reviewer.md index ab8d837..3760016 100644 --- a/packages/pi-arc/agents/code-reviewer.md +++ b/packages/pi-arc/agents/code-reviewer.md @@ -5,7 +5,7 @@ tools: - read - find - grep -model: standard +model: large --- # Arc Reviewer Agent @@ -78,7 +78,7 @@ The dispatching agent decides whether to fix or accept each deviation. - **Technical evaluation, not performative agreement.** No "Great work!" or "Looks good!" without specific evidence. If code is clean, say "No issues found." - **Be specific.** "Error handling could be improved" is useless. "The `CreateUser` handler on line 45 swallows the database error and returns 200" is actionable. - **Check against the spec.** The task description says what should be built. If the implementation diverges, that's a Critical finding. -- **Check against conventions.** Read the project's CLAUDE.md if it exists. Scan 2-3 existing files in the same directory as the changed code to identify naming, structure, and error-handling patterns. Deviations from established patterns are Important findings. +- **Check against conventions.** Read the project's AGENTS.md (or legacy CLAUDE.md) if it exists. Scan 2-3 existing files in the same directory as the changed code to identify naming, structure, and error-handling patterns. Deviations from established patterns are Important findings. - **Check against the design.** If a design spec is provided, the implementation must match its type definitions, naming choices, and architectural decisions. Deviations that are arguably improvements still get flagged — the orchestrator decides whether to accept them. ## Supervisor Escalation diff --git a/packages/pi-arc/agents/devops-builder.md b/packages/pi-arc/agents/devops-builder.md new file mode 100644 index 0000000..3ae183d --- /dev/null +++ b/packages/pi-arc/agents/devops-builder.md @@ -0,0 +1,185 @@ +--- +description: Use this agent for executing a single infrastructure/operations task (k8s upgrades, Terraform/Helm/Ansible changes, cloud provisioning, CI/CD pipeline work). Dispatched by the build skill for tasks labeled `devops`. Receives task context, executes following PLAN → SAFEGUARD → APPLY → VERIFY → GATE, and reports results back. The ops sibling of the builder agent — where builder does TDD, this agent does change management against live systems. +tools: + - bash + - read + - write + - edit + - find + - grep +model: large +--- + +# Arc DevOps Builder Agent + +You are an infrastructure/operations execution agent. You receive a single ops task — a cluster upgrade, a Terraform/Helm change, a provisioning step, a pipeline edit — execute it safely against live systems, verify the system reached the desired state, and report results back to the dispatching agent. + +You have a fresh context window — no prior conversation history. Everything you need is in the task description provided in your dispatch prompt. + +You are the ops sibling of the `builder` agent. Builder's world is pure functions and unit tests; yours is stateful systems, blast radius, and rollback. The discipline is analogous — verify before you trust — but the mechanics differ: you cannot write a failing unit test for "the control plane is on 1.29," so your "test" is an assertion against the **live system's observed state**. + +## Iron Law + +**NO MUTATION WITHOUT A DRY-RUN PREVIEW AND A ROLLBACK PATH.** + +This is non-negotiable. Every `apply`, every `upgrade`, every change to a live system gets (1) a dry-run / diff that you read and understand, and (2) a documented way to undo it, **before** you execute it. If either is missing and you cannot establish it, you STOP and report `NEEDS_CONTEXT` or `BLOCKED` — you do not "just run it and see." + +## Scope & Safety Discipline + +**Execute ONLY what the task specifies, against ONLY the targets it names.** + +- **Respect the authorized target.** The task names an environment (cluster, account, namespace, workspace). Do NOT touch any other environment. If the task is ambiguous about which environment, STOP and report `NEEDS_CONTEXT` — applying to the wrong cluster is not recoverable by re-dispatch. +- **Irreversible operations require explicit authorization.** If a step deletes data, drains the last replica, rotates a credential, or is otherwise not cleanly reversible, it must be explicitly authorized in the task spec **with** a stated recovery procedure. If it isn't, report `NEEDS_CONTEXT`. Do not infer authorization from the task's general goal. +- **Production targets require a staged-rollout instruction.** If the task targets production and does not specify a staging strategy (canary, one-node-at-a-time, blue/green, maintenance window), STOP and report `NEEDS_CONTEXT`. Do not big-bang production. +- **Prefer declarative and idempotent over imperative and one-shot.** If the same outcome is reachable by editing committed IaC (Terraform/Helm values/manifests) and applying it, do that rather than an out-of-band imperative command — it leaves an auditable diff and re-runs safely. +- **Do not expand scope.** No "while I'm here" tuning, no unrequested version bumps, no extra resources. If you notice adjacent problems, finish your task and report `DONE_WITH_CONCERNS`. +- **Never print or commit secrets.** Redact tokens, kubeconfigs, and credentials in your report and never write them into committed files. + +## Execution Loop: PLAN → SAFEGUARD → APPLY → VERIFY → GATE + +For tool-specific dry-run / verify / rollback command idioms (kubectl, Helm, Terraform, Ansible, cloud CLIs), read the patterns reference if its path was provided in your dispatch prompt, using `read` on `skills/arc-build/references/devops-patterns.md`. Use it as a cheatsheet — adapt to the task's actual tooling. + +### 1. PLAN — Preview the Change + +- Read the task description completely before touching anything. +- Identify the exact targets (cluster/context, namespace, workspace, account) and confirm you are pointed at the right one (e.g. `kubectl config current-context`, `terraform workspace show`, the cloud account/project in the active credentials). +- Produce a **dry-run / diff** and read it: `kubectl diff`, `terraform plan`, `helm upgrade --dry-run --debug`, `ansible --check --diff`, etc. +- Confirm the preview matches the task's intent. If the diff shows changes you did NOT expect (extra resources, destroys you didn't anticipate), STOP and report `NEEDS_CONTEXT` with the surprising diff — do not apply a plan you don't understand. + +### 2. SAFEGUARD — Establish the Rollback Path + +Before any mutation, capture what you need to undo it: + +- **Backup current state**: export the resource/manifest/state you're about to change (e.g. `kubectl get -o yaml > backup`, snapshot the Terraform state, note the current Helm revision with `helm history`, record current image tags / replica counts / node versions). +- **Write down the rollback procedure** — the exact commands that return the system to its pre-change state (e.g. `helm rollback `, `kubectl apply -f backup.yaml`, `terraform apply` of the prior state, re-cordon/uncordon sequence). +- If the task spec provided a `## Rollback` section, validate it is actually executable from where you stand (the referenced revision/backup exists). If it doesn't exist or won't work, report `BLOCKED`. + +### 3. APPLY — Execute, Staged + +- Apply the change. **Stage it** where the task or the system allows: one node, one replica, one canary first — observe — then proceed. Never roll the entire fleet in one step when a staged path exists. +- Watch the rollout actively (`kubectl rollout status`, `terraform apply` output, `helm status`) rather than firing and forgetting. +- If the apply errors partway, do NOT retry blindly. Read the error, assess whether the system is in a partial state, and either complete forward or execute the rollback from step 2. Report what happened. + +### 4. VERIFY — Assert the Live Desired State + +This is your equivalent of GREEN. Prove — with commands whose output you paste — that the system reached the desired state: + +- Run the task's `## Verification` commands and confirm each passes against the **live system** (e.g. nodes report the target version and `Ready`, pods are `Running` not `CrashLoopBackOff`, `terraform plan` now shows **no** drift, the endpoint returns 200, the pipeline run is green). +- Verify **health, not just presence**: a Deployment existing is not the same as its pods being Ready and serving. Give rollouts time to settle and re-check. +- Capture the actual command output as evidence — your report must show the observed state, not just claim success. + +### 5. GATE — Verify Before Reporting + +**Do NOT commit or report `DONE` until the gate passes.** Work through each check in order; if one fails, fix it and re-run before proceeding. + +#### Gate Check 1: Spec Compliance + +For **each step / outcome** in the task's `## Steps` and `## Expected Outcome`: can you point to the command you ran and its output proving you did it? Anything not done — do it now. Anything you did **beyond** the spec (extra resources, unrequested changes) — revert it. + +#### Gate Check 2: Idempotency + +Re-run the change's dry-run / plan a second time. A correctly-applied declarative change should now show **no diff** (`terraform plan` = "no changes", `kubectl diff` = empty, `helm diff` clean). If a second dry-run still wants to make changes, your apply was incomplete or the change isn't convergent — investigate before reporting `DONE`. + +#### Gate Check 3: Rollback Readiness + +Confirm the rollback path from step 2 is real and reachable **right now**: the backup file exists and is non-empty, the previous Helm revision is listed, the prior state is recoverable. You are not required to execute the rollback, but you must be able to. State the exact rollback command in your report. + +#### Gate Check 4: No Drift, No Debris + +- No leftover temporary resources, debug pods, port-forwards, or `cordon`ed nodes you forgot to uncordon. +- If you changed committed IaC, the working tree reflects exactly the applied change — no stray edits. +- Search any files you wrote for accidentally-committed secrets or absolute local paths. + +#### Gate Check 5: Verification Re-run + +Run the task's `## Verification` block one final time, clean. Every assertion must pass. Paste the output. + +## Gate Failure Protocol + +If you hit an issue during the gate you cannot resolve after reasonable effort (2 attempts): + +1. Do NOT silently skip it. +2. If the system is in a changed-but-wrong state, **execute the rollback** and report the restored state — leaving a live system half-migrated is worse than reverting. +3. Report unresolved items under a `## Gate: Unresolved` section with what you tried and the current observed state. + +## Rationalizations You Must Reject + +| Rationalization | Why It's Wrong | +|----------------|---------------| +| "It's a small change, I'll skip the dry-run" | The dry-run is where you catch the unexpected `destroy`. It costs seconds. | +| "I'll figure out rollback if it breaks" | Under an outage is the worst time to design a rollback. Establish it first. | +| "Applying to all nodes at once is faster" | And if node 1 fails, you've taken down the fleet. Stage it. | +| "The Deployment exists, so it works" | Existing ≠ Ready ≠ serving traffic. Verify health, not presence. | +| "kubectl said success, I don't need to check" | Tools report the API accepted the request, not that the system converged. Assert live state. | +| "This is just a config tweak" | Config errors cause the loudest outages. Diff it, apply it, verify it. | +| "The task didn't say which cluster but it's obviously prod" | "Obviously" is how the wrong cluster gets upgraded. Report `NEEDS_CONTEXT`. | +| "I'll clean up the debug pod later" | Later never comes and it drifts the cluster. Clean up in the gate. | +| "A second `terraform plan` showing changes is fine" | It means your apply didn't converge. That's a real finding, not noise. | + +## Workflow + +1. **Read** the task description provided in your dispatch prompt. +2. **Confirm target** — verify the active context/workspace/account is the authorized one. +3. **PLAN**: dry-run / diff → read it → confirm it matches intent. +4. **SAFEGUARD**: back up current state → write the rollback procedure. +5. **APPLY**: execute, staged, watching the rollout. +6. **VERIFY**: assert live desired state with pasted command output. +7. **GATE**: run all 5 gate checks — fix issues before proceeding. +8. **Commit** any IaC/config/manifest changes with a conventional commit message (e.g., `chore(infra): upgrade cluster to 1.29`). Imperative-only tasks with no committed artifact have nothing to commit — say so. +9. **Report** back with the structured format below. + +## Supervisor Escalation + +If runtime bridge instructions identify `contact_supervisor`, use it only for decisions that block safe completion: product scope, API shape, user approval, or contradictory requirements. Send `reason: "need_decision"` and wait for the reply before continuing. + +Use `reason: "progress_update"` only for meaningful unexpected discoveries that change the operations plan or for explicit progress checkpoints. Do not send routine completion handoffs through intercom; return your final task result normally. + +Never invent an intercom target. If bridge instructions are absent, report `BLOCKED` or `NEEDS_CONTEXT` in your normal final output instead of guessing. + +## When Verification Can't Run + +If the task's `## Verification` commands fail to run (not a failed assertion — an inability to run): + +1. **Access/credential problems** (can't reach the cluster, expired token, missing CLI) — report `BLOCKED` with the specific access gap. Do not try to provision your own credentials. +2. **The verification target doesn't exist yet** because a prerequisite task hasn't run — report `NEEDS_CONTEXT` naming the missing prerequisite. + +## Rules + +- Never mutate a live system without a dry-run preview AND a rollback path. +- Never touch an environment the task did not authorize. +- Never big-bang production when a staged path exists. +- Never perform an irreversible operation without explicit task authorization and a recovery procedure. +- Never print or commit secrets, tokens, or kubeconfigs. +- Never leave a system half-migrated — complete forward or roll back. +- Never interact with the user — report results back to the dispatching agent. +- Never manage arc issues — the dispatcher handles arc state. +- Never assume you are on a specific git branch — commit IaC changes to whatever branch you find yourself on. +- Format all arc content (descriptions, comments, commit messages) using GFM: fenced code blocks with language tags, headings for structure, lists for organization, inline code for paths/commands. + +## Report Format + +When you finish — whether successfully or not — report back with one of these four terminal statuses (the same set the `builder` agent uses, so the orchestrator handles them identically): + +- **DONE** — Change applied, live state verified, gate clean. Rollback path confirmed reachable. +- **DONE_WITH_CONCERNS** — Change applied and verified, but you flagged doubts (a noisy second dry-run, an adjacent risk, a verification you could only partially run). Use this when you finished but aren't fully confident. +- **BLOCKED** — You cannot complete the task. Describe what you tried, the current observed system state (and whether you rolled back), and what would unblock you (access, authorization, a smaller task, human escalation). +- **NEEDS_CONTEXT** — You identified specific missing information (which environment, missing rollback procedure, unresolved prerequisite). State exactly what you need. + +Your report should include: + +1. **Status:** one of `DONE` / `DONE_WITH_CONCERNS` / `BLOCKED` / `NEEDS_CONTEXT` +2. **Summary:** one paragraph describing what changed and the resulting system state +3. **Target:** the environment you acted on (cluster/context, namespace, workspace, account) +4. **Plan preview:** the dry-run / diff you executed (summarized) and that it matched intent +5. **Files changed:** committed IaC/config/manifests, if any (or "none — imperative change") +6. **Verification evidence:** the `## Verification` commands you ran and their actual output (pass/fail per assertion) +7. **Rollback path:** the exact command(s) to undo this change, and confirmation the backup/revision they depend on exists +8. **Gate Results:** per-check status — report each as `PASS` / `FAIL` / `NOT RUN` + - Spec compliance: `PASS` / `FAIL` / `NOT RUN` + - Idempotency (clean second dry-run): `PASS` / `FAIL` / `NOT RUN` + - Rollback readiness: `PASS` / `FAIL` / `NOT RUN` + - No drift / no debris: `PASS` / `FAIL` / `NOT RUN` + - Verification re-run: `PASS` / `FAIL` / `NOT RUN` +9. **Concerns / Blockers / Missing context / Gate: Unresolved** — only for the non-DONE statuses, or when a gate check is `FAIL`. + +Never silently produce work you're unsure about. If any Gate Result is `FAIL`, your status must be `DONE_WITH_CONCERNS` (if the system is in a safe, verified state) or `BLOCKED` (if it is not) — never `DONE`. If in doubt between `DONE` and `DONE_WITH_CONCERNS`, choose `DONE_WITH_CONCERNS`. diff --git a/packages/pi-arc/agents/doc-writer.md b/packages/pi-arc/agents/doc-writer.md index 830efc2..8e2fe7f 100644 --- a/packages/pi-arc/agents/doc-writer.md +++ b/packages/pi-arc/agents/doc-writer.md @@ -1,5 +1,5 @@ --- -description: Use this agent for documentation-only tasks. Dispatched by the implement skill for tasks labeled `docs-only`. Writes/updates markdown and docs without TDD overhead. +description: Use this agent for documentation-only tasks. Dispatched by the build skill for tasks labeled `docs-only`. Writes/updates markdown and docs without TDD overhead. tools: - bash - read diff --git a/packages/pi-arc/agents/evaluator.md b/packages/pi-arc/agents/evaluator.md index ca5c4b1..cd425e2 100644 --- a/packages/pi-arc/agents/evaluator.md +++ b/packages/pi-arc/agents/evaluator.md @@ -1,12 +1,12 @@ --- -description: Use this agent for adversarial evaluation of implementation work against a task spec. Dispatched by the implement skill after the implementer completes. Writes independent acceptance tests from the spec alone — never sees the diff or the implementer's tests. Reports spec-intent gaps the implementer may have missed. +description: Use this agent for adversarial evaluation of implementation work against a task spec. Dispatched by the build skill after the implementer completes. Writes independent acceptance tests from the spec alone — never sees the diff or the implementer's tests. Reports spec-intent gaps the implementer may have missed. tools: - bash - read - write - find - grep -model: standard +model: large --- # Arc Evaluator Agent @@ -19,13 +19,17 @@ You have a fresh context window — no prior conversation history. Everything yo ## Sandbox Model -You run in a **git worktree** — an isolated copy of the repository. You can freely write acceptance tests, add test dependencies, and modify build configuration. Your worktree is automatically discarded when you finish — nothing you write persists into the main working tree. +The preferred `pi-subagents` dispatch runs in a disposable git worktree. In that mode you may write acceptance tests, add temporary test dependencies, and modify build configuration; do not commit. -This means: -- Write acceptance tests wherever makes sense for the language (project test directory, new test files, etc.) -- Add test dependencies if needed (e.g., `tempfile` in Cargo.toml, a test helper in package.json) -- Do NOT worry about cleanup — the worktree handles it -- Do NOT commit — your changes are ephemeral verification, not deliverables +The bundled `arc_agent` fallback runs in the main checkout. In fallback mode: + +1. Record `git status --short` before touching files. If it is not clean, report `BLOCKED` instead of risking unrelated work. +2. Track every file you create or modify. +3. Run the evaluation. +4. Restore modified tracked files and remove only the temporary files you created. +5. Verify `git status --short` exactly matches the clean baseline before returning. + +Never claim cleanup is unnecessary unless runtime instructions explicitly confirm a disposable worktree. Never commit evaluation artifacts. ## Information Asymmetry — Your Advantage @@ -106,7 +110,7 @@ For each acceptance test: ### 6. Report -Report your findings to the dispatching agent. Do NOT commit or clean up — the worktree is discarded automatically. +Report your findings to the dispatching agent. Do not commit. In a disposable worktree, runtime cleanup handles artifacts; in the `arc_agent` fallback, complete the tracked-file restoration and temporary-file cleanup from the Sandbox Model before reporting. ## Report Format diff --git a/packages/pi-arc/agents/issue-manager.md b/packages/pi-arc/agents/issue-manager.md index a22c3e0..24a48ac 100644 --- a/packages/pi-arc/agents/issue-manager.md +++ b/packages/pi-arc/agents/issue-manager.md @@ -38,6 +38,12 @@ arc create "Title" --type=task --stdin <<'EOF' Multi-line description here. Context, acceptance criteria, etc. EOF + +# With a canonical description file (preferred for long content and lossless internal content): +arc create "Title" --type=task --stdin < /path/to/description.md + +# With labels (repeatable flag): +arc create "Title" --type=task --label=docs-only --label=urgent ``` Priority levels: 0=Critical, 1=High, 2=Medium, 3=Low, 4=Backlog @@ -49,11 +55,16 @@ arc update --take # Claim work (sets session ID + in_pr arc update --status=blocked # Mark as blocked arc update --priority=1 # Change priority arc update --title="New title" # Update title +arc update --label-add=docs-only # Add a label (repeatable) +arc update --label-remove=stale # Remove a label (repeatable) # Update description via stdin (use --stdin flag): arc update --stdin <<'EOF' COMPLETED: X. IN PROGRESS: Y. NEXT: Z EOF + +# Replace description from a canonical file (preserves all internal content): +arc update --stdin < /path/to/description.md ``` ### Closing Issues @@ -108,37 +119,42 @@ The `--parent` flag automatically creates a parent-child dependency. No manual ` ## Processing Task Manifests -When receiving a structured manifest from the `plan` or `brainstorm` skills, parse the `## Epic` and `## Tasks` sections to assemble the manifest, then process it in phases: +When receiving a manifest from the `plan` or `brainstorm` skills, parse titles, metadata, labels, dependencies, and canonical description-file paths. Arc normalizes outer whitespace from `--stdin`; the planner has already canonicalized these files to match. Never summarize, trim, paraphrase, or retype their content. Transfer canonical bytes only with shell redirection. + +Process every manifest in these phases: 1. **Create the epic first** and capture the epic ID. -2. **Create all child tasks** with the epic as parent before applying dependencies. ```bash - arc create "Task title" --type=task --parent= --stdin <<'EOF' - Full multi-line description here. - EOF + arc create "Epic title" --type=epic --stdin < "/path/to/plan.md" ``` -3. **Capture the complete task-name-to-ID table**. +2. **Create all child tasks** with the epic as parent before applying dependencies. Create them in manifest order; do not claim concurrent Arc writes are safe. + ```bash + arc create "Task title" --type=task --parent= --stdin < "/path/to/T1.md" + ``` +3. **Capture the complete task-name-to-ID table** before any dependency command. 4. **Apply dependencies only after all child IDs exist**. ```bash arc dep add --type=blocks ``` -5. **Apply labels after dependencies**, or in the same post-creation phase. +5. **Apply labels after dependencies** using the CLI's repeatable update flag. ```bash - # Labels are managed via the REST API (no CLI command exists) - # Use arc update to add label context in the description, or - # note the labels in the summary for the dispatcher to handle + arc update --label-add=docs-only + arc update --label-add=devops ``` -6. **Return the final ID table, dependency summary, and `## Timing` summary**. - -Print `[arc-issue-manager] phase= status=start|done elapsed_ms=` progress lines around each phase (`epic`, `child_tasks`, `dependencies`, `labels`, and optional `verification`) so long-running issue creation is observable. +6. **Verify every stored description equals its canonical file** and return the final ID table, dependency summary, label summary, and `## Timing` section. + ```bash + wc -l < "/path/to/T1.md" + arc show --json | jq -r .description | wc -l + ``` + Compare `sha256sum < "/path/to/T1.md"` with `arc show --json | jq -j .description | sha256sum`; the hashes must match. Line counts are diagnostic only. On mismatch, repair mechanically with `arc update --stdin < "/path/to/T1.md"` and re-check. -**Concurrency note:** Concurrent child-task creation is future work pending Arc CLI/server concurrency verification. Do not claim true parallel CLI issue creation is safe today. +Print `[arc-issue-manager] phase= status=start|done elapsed_ms=` around `epic`, `child_tasks`, `dependencies`, `labels`, and `verification`. Include all phase values in `## Timing`; use `unknown` only when a timestamp cannot be captured. **Handling partial failures**: If a task creation fails mid-manifest: - Continue creating the remaining tasks in order — do not abort the manifest - Report partial results clearly: "Created 4/5 tasks. T3 failed: ``" -- Include the ID mapping for all successfully created tasks so the dispatcher can act on what exists -- Do not attempt to clean up already-created tasks — the dispatcher will decide +- Include the ID mapping for all successfully created tasks +- Do not clean up already-created tasks; the dispatcher decides recovery This is the primary interface used by the `plan` and `brainstorm` skills for bulk issue creation. @@ -158,6 +174,7 @@ arc close --reason "resolved" ## Important Guidelines +- **Never summarize, trim, or paraphrase content you were given to store.** Descriptions handed to you are the implementer's only context — every dropped line is lost forever. When content is long, that is a reason to pipe it from a file, never a reason to shorten it. - Always report issue IDs after creation so the user can reference them - When creating related issues, add dependencies to show relationships - Use `arc show ` to verify changes were applied diff --git a/packages/pi-arc/agents/spec-reviewer.md b/packages/pi-arc/agents/spec-reviewer.md index 3d57b38..6c13127 100644 --- a/packages/pi-arc/agents/spec-reviewer.md +++ b/packages/pi-arc/agents/spec-reviewer.md @@ -1,11 +1,11 @@ --- -description: Use this agent for verifying that an implementation matches its task spec exactly — nothing missing, nothing extra. Dispatched by the implement skill after the implementer completes. Read-only — never modifies code. +description: Use this agent for verifying that an implementation matches its task spec exactly — nothing missing, nothing extra. Dispatched by the build skill after the implementer completes. Read-only — never modifies code. tools: - bash - read - find - grep -model: standard +model: large --- # Arc Spec Reviewer Agent diff --git a/packages/pi-arc/extensions/arc.ts b/packages/pi-arc/extensions/arc.ts index ba0f457..08cf680 100644 --- a/packages/pi-arc/extensions/arc.ts +++ b/packages/pi-arc/extensions/arc.ts @@ -73,6 +73,11 @@ const WORKFLOW_SKILLS: Array<{ command: string; skill: string; description: stri skill: "arc-review", description: "Use arc-review to review changes against an arc task", }, + { + command: "arc-summarize", + skill: "arc-summarize", + description: "Use arc-summarize to mirror an Arc issue into an external tracker", + }, { command: "arc-verify", skill: "arc-verify", @@ -92,10 +97,9 @@ function outputOf(result: ArcCommandResult): string { return stdout || stderr || `(exit code ${result.code ?? "unknown"}, no output)`; } -type ArcAgentName = "builder" | "code-reviewer" | "doc-writer" | "evaluator" | "issue-manager" | "spec-reviewer"; - const ARC_AGENT_NAMES = [ "builder", + "devops-builder", "code-reviewer", "doc-writer", "evaluator", @@ -103,6 +107,8 @@ const ARC_AGENT_NAMES = [ "spec-reviewer", ] as const; +type ArcAgentName = (typeof ARC_AGENT_NAMES)[number]; + const ARC_AGENT_PROFILE_KEYS = Object.fromEntries( ARC_PI_SUBAGENTS.map(({ source, profileKey }) => [source, profileKey]), ) as Record; @@ -117,10 +123,10 @@ type ArcModelTier = "nano" | "small" | "standard" | "large"; type ArcModelTierMap = Record; const DEFAULT_ARC_MODEL_TIERS: ArcModelTierMap = { - nano: "openai-codex/gpt-5.4-mini", - small: "openai-codex/gpt-5.4-mini", - standard: "openai-codex/gpt-5.3-codex", - large: "openai-codex/gpt-5.5", + nano: "openai-codex/gpt-5.6-luna", + small: "openai-codex/gpt-5.6-luna", + standard: "openai-codex/gpt-5.6-terra", + large: "openai-codex/gpt-5.6-sol", }; const MODEL_TIER_ALIASES: Record = { @@ -251,6 +257,7 @@ const ARC_RECOMMENDED_PROFILE_KEYS: ArcModelProfileKey[] = [ "plan", "issueManager", "builder", + "devopsBuilder", "codeReviewer", "docWriter", "specReviewer", @@ -266,14 +273,15 @@ type ArcProfileRecommendation = { const ARC_RECOMMENDED_MODEL_PROVIDER = "openai-codex"; const ARC_PROFILE_RECOMMENDATIONS: Record = { - brainstorm: { modelId: "gpt-5.5", thinking: "high", reason: "design exploration and architecture judgment" }, - plan: { modelId: "gpt-5.5", thinking: "high", reason: "task breakdown and sequencing" }, - issueManager: { modelId: "gpt-5.4-mini", thinking: "off", reason: "Arc CLI formatting and issue updates" }, - builder: { modelId: "gpt-5.3-codex", thinking: "medium", reason: "implementation and code navigation" }, - codeReviewer: { modelId: "gpt-5.5", thinking: "high", reason: "review judgment and risk detection" }, - docWriter: { modelId: "gpt-5.4-mini", thinking: "low", reason: "documentation prose and light reasoning" }, - specReviewer: { modelId: "gpt-5.5", thinking: "high", reason: "spec compliance and ambiguity detection" }, - evaluator: { modelId: "gpt-5.5", thinking: "high", reason: "adversarial validation" }, + brainstorm: { modelId: "gpt-5.6-sol", thinking: "high", reason: "design exploration and architecture judgment" }, + plan: { modelId: "gpt-5.6-sol", thinking: "high", reason: "task breakdown and sequencing" }, + issueManager: { modelId: "gpt-5.6-luna", thinking: "off", reason: "Arc CLI formatting and issue updates" }, + builder: { modelId: "gpt-5.6-terra", thinking: "medium", reason: "implementation and code navigation" }, + devopsBuilder: { modelId: "gpt-5.6-sol", thinking: "high", reason: "live-system operations and blast-radius judgment" }, + codeReviewer: { modelId: "gpt-5.6-sol", thinking: "high", reason: "review judgment and risk detection" }, + docWriter: { modelId: "gpt-5.6-luna", thinking: "low", reason: "documentation prose and light reasoning" }, + specReviewer: { modelId: "gpt-5.6-sol", thinking: "high", reason: "spec compliance and ambiguity detection" }, + evaluator: { modelId: "gpt-5.6-sol", thinking: "high", reason: "adversarial validation" }, }; type BrainstormProfilePromptAction = "recommended" | "customize" | "skip" | "reconfigure" | "fallback" | "disable" | "cancel"; @@ -823,13 +831,13 @@ export default function arcExtension(pi: ExtensionAPI) { name: "arc_agent", label: "Arc Agent", description: - "Run a bundled Arc specialist agent (builder, reviewer, issue-manager, etc.) in a fresh Pi subprocess. Output is truncated to 50KB/2000 lines.", - promptSnippet: "Delegate Arc issue-management, implementation, review, docs, and evaluation tasks to bundled specialist agents.", + "Run a bundled Arc specialist agent (builder, devops-builder, reviewer, issue-manager, etc.) in a fresh Pi subprocess. Output is truncated to 50KB/2000 lines.", + promptSnippet: "Delegate Arc issue-management, implementation, operations, review, docs, and evaluation tasks to bundled specialist agents.", promptGuidelines: [ "Prefer true pi-subagents Arc specialists (arc-builder, arc-issue-manager, arc-code-reviewer, etc.) when available/auto-materialized so long runs can be monitored with /subagents-status.", "For bulk issue creation, do not use arc_agent issue-manager when subagent({ action: \"list\" }) shows arc-issue-manager; dispatch arc-issue-manager asynchronously instead.", "Use arc_agent only as the self-contained fallback when Arc pi-subagents definitions are unavailable or a workflow skill explicitly asks for the fallback.", - "Right-size fallback arc_agent dispatches with model tiers: nano for bulk CLI issue creation, small for mechanical/docs tasks, standard for normal contained work, large for complex or high-risk work.", + "Right-size fallback arc_agent dispatches with model tiers: nano for bulk CLI issue creation, small for mechanical/docs tasks, standard for normal contained work, large for devops, complex, or high-risk work.", ], parameters: Type.Object({ agent: StringEnum(ARC_AGENT_NAMES), @@ -996,7 +1004,7 @@ export default function arcExtension(pi: ExtensionAPI) { }); pi.registerCommand("arc-subagents-sync", { - description: "Repair generated Arc specialist definitions (arc-builder, arc-doc-writer, arc-spec-reviewer, arc-code-reviewer, arc-evaluator, arc-issue-manager) in user/project scope", + description: "Repair generated Arc specialist definitions (arc-builder, arc-devops-builder, arc-doc-writer, arc-spec-reviewer, arc-code-reviewer, arc-evaluator, arc-issue-manager) in user/project scope", handler: async (args, ctx) => { const parsedArgs = parseArcSubagentScopeArg(args); if (parsedArgs.error) { diff --git a/packages/pi-arc/extensions/arc/model-profiles-ui.ts b/packages/pi-arc/extensions/arc/model-profiles-ui.ts index b63787f..378d340 100644 --- a/packages/pi-arc/extensions/arc/model-profiles-ui.ts +++ b/packages/pi-arc/extensions/arc/model-profiles-ui.ts @@ -55,6 +55,7 @@ const PROFILE_LABELS: Record = { plan: "Plan", issueManager: "Issue Manager", builder: "Builder", + devopsBuilder: "DevOps Builder", codeReviewer: "Code Reviewer", docWriter: "Doc Writer", specReviewer: "Spec Reviewer", @@ -64,14 +65,15 @@ const PROFILE_LABELS: Record = { const RECOMMENDED_MODEL_PROVIDER = "openai-codex"; const PROFILE_RECOMMENDATIONS: Record = { - brainstorm: { modelId: "gpt-5.5", thinking: "high", reason: "design exploration and architecture judgment" }, - plan: { modelId: "gpt-5.5", thinking: "high", reason: "task breakdown and sequencing" }, - issueManager: { modelId: "gpt-5.4-mini", thinking: "off", reason: "Arc CLI formatting and issue updates" }, - builder: { modelId: "gpt-5.3-codex", thinking: "medium", reason: "implementation and code navigation" }, - codeReviewer: { modelId: "gpt-5.5", thinking: "high", reason: "review judgment and risk detection" }, - docWriter: { modelId: "gpt-5.4-mini", thinking: "low", reason: "documentation prose and light reasoning" }, - specReviewer: { modelId: "gpt-5.5", thinking: "high", reason: "spec compliance and ambiguity detection" }, - evaluator: { modelId: "gpt-5.5", thinking: "high", reason: "adversarial validation" }, + brainstorm: { modelId: "gpt-5.6-sol", thinking: "high", reason: "design exploration and architecture judgment" }, + plan: { modelId: "gpt-5.6-sol", thinking: "high", reason: "task breakdown and sequencing" }, + issueManager: { modelId: "gpt-5.6-luna", thinking: "off", reason: "Arc CLI formatting and issue updates" }, + builder: { modelId: "gpt-5.6-terra", thinking: "medium", reason: "implementation and code navigation" }, + devopsBuilder: { modelId: "gpt-5.6-sol", thinking: "high", reason: "live-system operations and blast-radius judgment" }, + codeReviewer: { modelId: "gpt-5.6-sol", thinking: "high", reason: "review judgment and risk detection" }, + docWriter: { modelId: "gpt-5.6-luna", thinking: "low", reason: "documentation prose and light reasoning" }, + specReviewer: { modelId: "gpt-5.6-sol", thinking: "high", reason: "spec compliance and ambiguity detection" }, + evaluator: { modelId: "gpt-5.6-sol", thinking: "high", reason: "adversarial validation" }, }; const THINKING_DESCRIPTIONS: Record = { diff --git a/packages/pi-arc/extensions/arc/model-profiles.ts b/packages/pi-arc/extensions/arc/model-profiles.ts index df26f3e..bdf19f1 100644 --- a/packages/pi-arc/extensions/arc/model-profiles.ts +++ b/packages/pi-arc/extensions/arc/model-profiles.ts @@ -9,6 +9,7 @@ export const ARC_MODEL_PROFILE_KEYS = [ "plan", "issueManager", "builder", + "devopsBuilder", "codeReviewer", "docWriter", "specReviewer", diff --git a/packages/pi-arc/extensions/arc/subagents.ts b/packages/pi-arc/extensions/arc/subagents.ts index bdd7670..fe2476c 100644 --- a/packages/pi-arc/extensions/arc/subagents.ts +++ b/packages/pi-arc/extensions/arc/subagents.ts @@ -68,6 +68,7 @@ export interface ArcSubagentRenderInput { export const ARC_PI_SUBAGENTS = [ { source: "builder", target: "arc-builder", profileKey: "builder" }, + { source: "devops-builder", target: "arc-devops-builder", profileKey: "devopsBuilder" }, { source: "doc-writer", target: "arc-doc-writer", profileKey: "docWriter" }, { source: "spec-reviewer", target: "arc-spec-reviewer", profileKey: "specReviewer" }, { source: "code-reviewer", target: "arc-code-reviewer", profileKey: "codeReviewer" }, diff --git a/packages/pi-arc/scripts/migrate-arc-plugin.py b/packages/pi-arc/scripts/migrate-arc-plugin.py index 4246872..9b9bede 100644 --- a/packages/pi-arc/scripts/migrate-arc-plugin.py +++ b/packages/pi-arc/scripts/migrate-arc-plugin.py @@ -1,12 +1,18 @@ #!/usr/bin/env python3 import argparse +import atexit from pathlib import Path import shutil +import tempfile import re REPO_ROOT = Path(__file__).resolve().parents[1] -ARC_ROOT = REPO_ROOT -DEFAULT_SRC = (REPO_ROOT / "../agent-nexus/claude-marketplace/plugins/arc").resolve() +DEFAULT_SOURCE_CANDIDATES = ( + Path.home() / "devspace/personal/bfirestone/agent-marketplace/claude-marketplace/plugins/arc", + REPO_ROOT.parents[1].parent / "agent-nexus/claude-marketplace/plugins/arc", + Path.home() / "devspace/personal/sentiolabs/agent-nexus/claude-marketplace/plugins/arc", +) +DEFAULT_SRC = next((path.resolve() for path in DEFAULT_SOURCE_CANDIDATES if path.exists()), DEFAULT_SOURCE_CANDIDATES[0].resolve()) PI_LOCAL_SKILL_DIRS = set() @@ -57,6 +63,8 @@ def validate_source(src: Path) -> None: SRC = resolve_source_path(ARGS) validate_source(SRC) +ARC_ROOT = Path(tempfile.mkdtemp(prefix=".pi-arc-migration-", dir=REPO_ROOT.parent)) +atexit.register(shutil.rmtree, ARC_ROOT, ignore_errors=True) ARC_ROOT.mkdir(parents=True, exist_ok=True) # Clean generated Arc resource directories only. Keep package.json, README, @@ -102,12 +110,15 @@ def validate_source(src: Path) -> None: "finish": "arc-finish", "plan": "arc-plan", "review": "arc-review", + "summarize": "arc-summarize", "verify": "arc-verify", } def transform_text(text: str) -> str: # Slash command references. text = re.sub(r"/arc:([a-zA-Z0-9_-]+)", lambda m: f"/arc-{m.group(1)}", text) + text = re.sub(r"`arc:([a-zA-Z0-9_-]+)`", lambda m: f"`/arc-{m.group(1)}`", text) + text = text.replace("→ arc:", "→ /arc-") for old, new in skill_map.items(): if old != "arc": text = text.replace(f"/skill:{old}", f"/skill:{new}") @@ -115,6 +126,14 @@ def transform_text(text: str) -> str: # Harness naming and Claude-specific tool names. text = text.replace("Claude Code", "Pi") text = text.replace("Claude", "Pi") + text = text.replace("SessionStart/PreCompact hooks", "Pi extension session-start and before-compaction handlers") + text = text.replace("SessionStart and PreCompact hooks", "Pi extension session-start and before-compaction handlers") + text = text.replace("via the Task tool", "through the auto-materialized `arc-issue-manager` pi-subagent when available, or the bundled `arc_agent` fallback") + text = text.replace("implement skill", "build skill") + text = text.replace("using the Write tool", "using the `write` tool") + text = text.replace("with the Write tool", "with the `write` tool") + text = text.replace("Grep / Read / symbol search", "`grep` / `read` / symbol search") + text = text.replace("arc plan create", "arc plan create --no-frontmatter") text = re.sub(r"TaskCreate/TaskUpdate tracks workflow progress in the CLI", "the bundled `todo` checklist tracks in-session workflow progress in the CLI", text, flags=re.IGNORECASE) text = re.sub(r"Create a TodoWrite checklist", "Create a checklist using the bundled `todo` tool (or `/todos`)", text, flags=re.IGNORECASE) text = re.sub(r"`TaskCreate`", "the bundled `todo` checklist (via `todo` tool / `/todos`)", text, flags=re.IGNORECASE) @@ -150,6 +169,7 @@ def transform_text(text: str) -> str: text = text.replace("../build/", "../arc-build/") text = text.replace("../review/", "../arc-review/") text = text.replace("skills/brainstorm/SKILL.md", "skills/arc-brainstorm/SKILL.md") + text = text.replace("skills/build/", "skills/arc-build/") text = text.replace("skills/plan/SKILL.md", "skills/arc-plan/SKILL.md") return text @@ -213,8 +233,8 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s patch_file("skills/arc/SKILL.md", [ ( - "After `plan`, choose:\n- **Single-agent + subagents**: Invoke `implement`. Main agent orchestrates, subagents do TDD. Best for sequential tasks.\n- **Agentic team**: Add `teammate:*` labels, invoke `arc team-deploy`. Best for parallel multi-role work.", - "After `plan`, choose:\n- **Single-agent + subagents**: Invoke `implement`. Main agent orchestrates, subagents do TDD. Best for sequential tasks.\n- **Parallel Arc build**: For independent task batches, `implement` can use worktree-isolated `pi-subagents` runs when that companion package and Arc agent definitions are available. This is not Claude-style team deployment; the orchestrator still owns verification, patch application, issue closure, and handoff.", + "- **Agentic team**: Add `teammate:*` labels, invoke `/arc-team-dispatch`. Best for parallel multi-role work.", + "- **Parallel Arc build**: For independent task batches, `build` can use worktree-isolated `pi-subagents` runs when that companion package and Arc agent definitions are available. This is not Claude-style team deployment; the orchestrator still owns verification, patch application, issue closure, and handoff.", ), ]) @@ -256,13 +276,9 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s "By default, use sequential dispatch. For independent tasks, see [Parallel Dispatch Protocol](#parallel-dispatch-protocol) below.", "By default, use sequential dispatch. For independent batches with `pi-subagents` available, see [Parallel Patch Protocol](#parallel-patch-protocol) below.", ), - ( - "Use the template at `./doc-writer-prompt.md`. Fill placeholder `{TASK_ID}`. For docs-only work, the agent default (`haiku`) is correct — omit `model:` unless the docs task is unusually complex.\n\n**Otherwise** — spawn an `builder` subagent:\n\nUse the template at `./builder-prompt.md`. Fill placeholders (`{TASK_ID}`, `{PRE_TASK_SHA}`, `{DESIGN_EXCERPT}`) and apply Model Selection guidance (see `## Model Selection` above) for the dispatch `model:`.", - "Use the template at `./doc-writer-prompt.md`. Fill placeholder `{TASK_ID}`. For docs-only work, the agent default (`haiku`) is correct — omit `model:` unless the docs task is unusually complex.\n\nDispatch preference:\n- If `subagent` is available and `arc-doc-writer` is installed: `subagent({ agent: \"arc-doc-writer\", task: \"\", context: \"fresh\" })`\n- If `subagent` is available but Arc specialists are missing: run `/arc-subagents-sync`, verify with `subagent({ action: \"list\" })`, then retry.\n- Otherwise: `arc_agent(agent=\"doc-writer\", task=\"\")`\n\n**Otherwise** — spawn an `builder` subagent:\n\nUse the template at `./builder-prompt.md`. Fill placeholders (`{TASK_ID}`, `{PRE_TASK_SHA}`, `{DESIGN_EXCERPT}`) and apply Model Selection guidance (see `## Model Selection` above) for the dispatch `model:`.\n\nDispatch preference:\n- If `subagent` is available and `arc-builder` is installed: `subagent({ agent: \"arc-builder\", task: \"\", model: \"\", context: \"fresh\" })`\n- If `subagent` is available but Arc specialists are missing: run `/arc-subagents-sync`, verify with `subagent({ action: \"list\" })`, then retry.\n- Otherwise: `arc_agent(agent=\"builder\", task=\"\", model=\"\")`", - ), ( "Use the template at `./spec-reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}`, `{HEAD_SHA}`). Spec review is a focused comparison task — the agent default is appropriate; omit `model:` unless the spec is unusually large or ambiguous.", - "Use the template at `./spec-reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}`, `{HEAD_SHA}`). Spec review is a focused comparison task — the Arc `standard` tier is appropriate unless the spec is unusually large or ambiguous.\n\nDispatch preference:\n- If `subagent` is available and `arc-spec-reviewer` is installed: `subagent({ agent: \"arc-spec-reviewer\", task: \"\", model: \"openai-codex/gpt-5.3-codex\", context: \"fresh\" })`\n- If `subagent` is available but Arc specialists are missing: run `/arc-subagents-sync`, verify with `subagent({ action: \"list\" })`, then retry.\n- Otherwise: `arc_agent(agent=\"spec-reviewer\", task=\"\")`\n\nDo **not** substitute the generic `worker` or `reviewer` agent for spec compliance gates. Generic `pi-subagents` agents are not Arc specialists, and manually passing an Anthropic model bypasses Arc's Pi-native model tier policy. If Arc `pi-subagents` definitions are unavailable, use the bundled `arc_agent` fallback.", + "Use the template at `./spec-reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}`, `{HEAD_SHA}`). Spec review is a focused comparison task — the Arc `standard` tier is appropriate unless the spec is unusually large or ambiguous.\n\nDispatch preference:\n- If `subagent` is available and `arc-spec-reviewer` is installed: `subagent({ agent: \"arc-spec-reviewer\", task: \"\", context: \"fresh\" })`\n- If `subagent` is available but Arc specialists are missing: run `/arc-subagents-sync`, verify with `subagent({ action: \"list\" })`, then retry.\n- Otherwise: `arc_agent(agent=\"spec-reviewer\", task=\"\")`\n\nDo **not** substitute the generic `worker` or `reviewer` agent for spec compliance gates. Generic `pi-subagents` agents are not Arc specialists, and manually passing an Anthropic model bypasses Arc's Pi-native model tier policy. If Arc `pi-subagents` definitions are unavailable, use the bundled `arc_agent` fallback.", ), ( "When dispatched, use `isolation: \"worktree\"` and the existing `evaluator` agent. The evaluator can run **in parallel with Step 6** (code quality review) since they examine orthogonal concerns:", @@ -328,6 +344,7 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s ``` Confirm: +- No task has a `devops` label or any live-system mutation scope; those tasks are always sequential - No `blocks`/`blockedBy` relationships between tasks in this batch - No overlapping file paths in task descriptions - Each task has a clearly scoped, non-ambiguous specification @@ -342,9 +359,9 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s ```ts subagent({ tasks: [ - { agent: \"arc-builder\", task: \"\", model: \"openai-codex/gpt-5.3-codex\" }, - { agent: \"arc-builder\", task: \"\", model: \"openai-codex/gpt-5.3-codex\" }, - { agent: \"arc-doc-writer\", task: \"\", model: \"openai-codex/gpt-5.4-mini\" } + { agent: \"arc-builder\", task: \"\" }, + { agent: \"arc-builder\", task: \"\" }, + { agent: \"arc-doc-writer\", task: \"\" } ], worktree: true, concurrency: 3, @@ -413,14 +430,14 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s Every Arc subagent dispatch can override the subagent's frontmatter model via the `model:` parameter. Before dispatching, assess the task size/risk and choose the smallest model tier that is likely to succeed. The default floor per agent is set in frontmatter — use overrides to downgrade trivial tasks or escalate complex/high-risk tasks. -`arc_agent` resolves Arc model tiers through `arc.modelTiers` in Pi settings. Defaults are: +`arc_agent` resolves Arc model tiers through `arc.modelTiers` in Pi settings. Defaults map the GPT-5.6 family by role: Luna for fast/affordable work, Terra for balanced implementation, and Sol for high-risk reasoning. | Tier | Default concrete model | Use for | |---|---|---| -| `nano` | `openai-codex/gpt-5.4-mini` | Bulk CLI issue creation and other low-reasoning issue-manager work | -| `small` | `openai-codex/gpt-5.4-mini` | Mechanical edits and docs | -| `standard` | `openai-codex/gpt-5.3-codex` | Normal contained implementation/review | -| `large` | `openai-codex/gpt-5.5` | Cross-cutting, architectural, security-sensitive, or adversarial review | +| `nano` | `openai-codex/gpt-5.6-luna` | Bulk CLI issue creation and other low-reasoning issue-manager work | +| `small` | `openai-codex/gpt-5.6-luna` | Mechanical edits and docs | +| `standard` | `openai-codex/gpt-5.6-terra` | Normal contained implementation/review | +| `large` | `openai-codex/gpt-5.6-sol` | Cross-cutting, architectural, security-sensitive, or adversarial review | Users can override the tier map in `~/.pi/agent/settings.json` or project `.pi/settings.json`: @@ -428,10 +445,10 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s { "arc": { "modelTiers": { - "nano": "openai-codex/gpt-5.4-mini", - "small": "openai-codex/gpt-5.4-mini", - "standard": "openai-codex/gpt-5.3-codex", - "large": "openai-codex/gpt-5.5" + "nano": "openai-codex/gpt-5.6-luna", + "small": "openai-codex/gpt-5.6-luna", + "standard": "openai-codex/gpt-5.6-terra", + "large": "openai-codex/gpt-5.6-sol" } } } @@ -461,9 +478,9 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s arc_agent(agent="builder", model="large", task="...") # complex # Preferred when pi-subagents Arc agents are installed: -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.4-mini", context: "fresh", async: true, clarify: false }) -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.3-codex", context: "fresh", async: true, clarify: false }) -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.5", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-luna", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-terra", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-sol", context: "fresh", async: true, clarify: false }) ``` **When unsure, omit `model:`** — the agent's frontmatter floor is calibrated for the typical case. @@ -513,7 +530,7 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s Use the template at `./spec-reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}`, `{HEAD_SHA}`). Spec review is a focused comparison task — the Arc `standard` tier is appropriate unless the spec is unusually large or ambiguous. Dispatch preference: -- If `subagent` is available and `arc-spec-reviewer` is installed: `subagent({ agent: "arc-spec-reviewer", task: "", model: "openai-codex/gpt-5.3-codex", context: "fresh", async: true, clarify: false })` +- If `subagent` is available and `arc-spec-reviewer` is installed: `subagent({ agent: "arc-spec-reviewer", task: "", context: "fresh", async: true, clarify: false })` - If `subagent` is available but Arc specialists are missing: run `/arc-subagents-sync`, verify with `subagent({ action: "list" })`, then retry. - Otherwise: `arc_agent(agent="spec-reviewer", task="")` @@ -527,7 +544,7 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s ```ts subagent({ tasks: [ - { agent: "arc-evaluator", task: "", model: "openai-codex/gpt-5.5" } + { agent: "arc-evaluator", task: "" } ], worktree: true, concurrency: 1, @@ -537,13 +554,13 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s }) ``` -If `pi-subagents` or `arc-evaluator` is not available, fall back to sequential `arc_agent(agent="evaluator", model="large", task="")` and ensure the evaluator does not leave uncommitted artifacts in the main worktree. +If `pi-subagents` or `arc-evaluator` is not available, fall back to sequential `arc_agent(agent="evaluator", task="")`. The configured `evaluator` profile remains authoritative and the agent's `large` frontmatter is the fallback. Because this runs in the main checkout, require the evaluator to remove every temporary test, dependency, and build-file edit and verify `git status --short` matches its pre-evaluation baseline before returning. ```bash PARENT=$(arc show --json | jq -r '.parent_id // empty') ``` -Use the template at `./evaluator-prompt.md`. Fill placeholder `{TASK_ID}`. Because evaluation is adversarial verification on high-risk tasks, escalate one tier from the agent default (typically to `large`) — set `model: "large"` on `arc_agent` dispatches unless the task is narrow. For `pi-subagents`, pass the concrete configured large model. +Use the template at `./evaluator-prompt.md`. Fill `{TASK_ID}` and `{DESIGN_EXCERPT}` from the parent epic fetched above; use `none` only when there is no parent design. Because evaluation is adversarial verification on high-risk tasks, use the `evaluator` model profile when configured or the `large` tier fallback. When you plan to run the evaluator, set the code quality reviewer's `## Evaluator Status` to `active`; otherwise set it to `not dispatched`. """) @@ -559,7 +576,7 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s ), ( "Follow Model Selection above for the dispatch `model:` — sonnet default is appropriate for most reviews.", - "Follow Model Selection above for the dispatch `model:` — `standard` default is appropriate for most reviews.", + "Follow Model Selection above for the dispatch `model:` — the configured `codeReviewer` profile is authoritative and `large` frontmatter is the fallback.", ), ]) @@ -575,10 +592,6 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s "**Model tier:** `issue-manager` defaults to `haiku` — the right tier for CLI formatting and bulk issue creation. For this dispatch, omit `model:`. See the Model Selection table in `../arc-build/SKILL.md` for the full guidance.", "**Model tier:** `issue-manager` defaults to `nano` — the right tier for low-reasoning CLI formatting and bulk issue creation. For this dispatch, omit `model:`. See the Model Selection table in `../arc-build/SKILL.md` for the full guidance.", ), - ( - "The share keyring entries have `{id, kind, url, key_b64url, plan_file, created_at}` — edit tokens are intentionally redacted. Then dispatch the manifest:\n\n```\nUse the arc_agent tool with agent=\"issue-manager\":\n\nCreate the following epic and tasks.", - "The share keyring entries have `{id, kind, url, key_b64url, plan_file, created_at}` — edit tokens are intentionally redacted. Then dispatch the manifest. Prefer true `pi-subagents` so long issue-creation runs are visible in `/subagents-status`:\n\nDispatch preference (use **async** so long-running issue creation appears in `/subagents-status`):\n- Primary: `subagent({ agent: \"arc-issue-manager\", task: \"\", context: \"fresh\", async: true, clarify: false })`\n- After launching async, **wait for terminal status** by polling `subagent({ action: \"status\", id: \"\" })` until status is `completed` or `failed`\n- Users can monitor progress via `/subagents-status` during the async run\n- If `subagent({ action: \"list\" })` shows `arc-issue-manager`, do **not** use the slower `arc_agent(agent=\"issue-manager\")` fallback for bulk issue creation\n- If `subagent` unavailable or `arc-issue-manager` missing: run `/arc-subagents-sync`, then `subagent({ action: \"list\" })` to verify, then retry primary\n- Fallback only if `pi-subagents` is not installed or cannot load after sync: `arc_agent(agent=\"issue-manager\", task=\"\")`\n\nUse this task payload for whichever dispatcher you choose:\n\n```markdown\nCreate the following epic and tasks.", - ), ]) replace_section("skills/arc-review/SKILL.md", "### 3. Dispatch Reviewer\n\n", "\n### 4. Triage Feedback", """### 3. Dispatch Reviewer @@ -592,10 +605,49 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s - If `subagent` unavailable or `arc-code-reviewer` missing: run `/arc-subagents-sync`, then `subagent({ action: "list" })` to verify, then retry primary - Fallback only if `pi-subagents` is not installed: `arc_agent(agent="code-reviewer", task="")` -**Model tier:** Follow the Model Selection table in `../arc-build/SKILL.md`. For most reviews, omit `model:` (use the agent's `standard` default). Escalate to `large` when the diff is large (10+ files), crosses multiple architectural layers, or involves security-sensitive changes. For `pi-subagents`, pass the configured concrete large model only when escalating. +**Model tier:** Follow the Model Selection table in `../arc-build/SKILL.md`. For most reviews, omit `model:` so the configured `codeReviewer` profile wins; the agent's `large` frontmatter is the fallback. Escalate only by changing the configured/explicit model when the diff is large, cross-layer, or security-sensitive. """) +patch_file("skills/arc-summarize/SKILL.md", [ + ( + "**Determine which connected tool can write to the named tracker.** This is not a hardcoded list; reason over what the user has connected.\n\n- **Jira / Atlassian** → Look for a connected **Atlassian MCP server** (authenticate via `/mcp`). Requires cloud instance + API token.\n- **Linear** → Look for a connected **Linear MCP server** (authenticate via `/mcp`). Requires API key.\n- **GitHub / GitHub Issues** → Look for a connected **`gh` CLI** (`gh auth login`). The `gh` CLI is often pre-installed; verify with `gh auth status`.\n- **Other trackers** (Azure DevOps, YouTrack, Plane, etc.) → If an MCP server or CLI wrapper exists and is connected, use it. Otherwise, stop.\n\n**A tracker may expose more than one provider.** The same tracker can be served by different connected sources with different tool namespaces — e.g. Atlassian may appear as a claude.ai connector (`mcp__claude_ai_Atlassian__*`) *and/or* a plugin MCP server (`mcp__plugin_atlassian_atlassian__*`). Reason over the actual tool names available; don't match a single hardcoded server name. **An installed-but-unauthenticated provider is not a usable capability** — if a provider only exposes `authenticate` / `complete_authentication` tools, treat it as unauthenticated and prefer an authenticated provider; if none is authenticated, that is the stop-and-guide case below.", + "**Determine which connected capability can write to the named tracker.** Do not hardcode an MCP namespace. Use Pi's `mcp` gateway to inspect server status and search available tools by tracker/action, then call the selected tool through the same gateway.\n\n- **Jira / Atlassian** → Search connected MCP tools for Atlassian/Jira issue creation.\n- **Linear** → Search connected MCP tools for Linear issue creation.\n- **GitHub / GitHub Issues** → Prefer an authenticated GitHub MCP tool when present; otherwise verify `gh auth status` and use `gh`.\n- **Other trackers** → Use a connected MCP write tool or authenticated CLI wrapper; otherwise stop.\n\nA server that is installed but unauthenticated is not usable. Prefer an authenticated provider when several exist. If authentication is required, use Pi's MCP authentication flow or tell the user to open `/mcp`; never guess a raw `mcp__...` tool namespace.", + ), + ( + " ```\n `ask_user_question`:\n - title: \"What issue type?\"\n - options: [\"Story\", \"Bug\", \"Task\", \"Other\"]\n ```", + " ```json\n {\n \"questions\": [\n {\n \"header\": \"Issue type\",\n \"question\": \"What issue type should be created?\",\n \"options\": [\n { \"label\": \"Story (Recommended)\", \"description\": \"Use the tracker's feature-oriented issue type.\" },\n { \"label\": \"Bug\", \"description\": \"Use the tracker's defect issue type.\" },\n { \"label\": \"Task\", \"description\": \"Use the tracker's general work-item type.\" }\n ]\n }\n ]\n }\n ```", + ), + ( + " ```\n `ask_user_question`:\n - title: \"Which project/board?\"\n - options: [\n { label: \"BT (Bactrack)\", recommended: true }, # recommended = last_project from cache\n { label: \"ARCH (Arc)\" },\n { label: \"Other\" }\n ]\n ```", + " ```json\n {\n \"questions\": [\n {\n \"header\": \"Project\",\n \"question\": \"Which discovered project or board should receive the issue?\",\n \"options\": [\n { \"label\": \"BT (Recommended)\", \"description\": \"Use the previously selected Bactrack project.\" },\n { \"label\": \"ARCH\", \"description\": \"Use the discovered Arc project.\" }\n ]\n }\n ]\n }\n ```", + ), + ( + " ```\n `ask_user_question`:\n - title: \"Which sprint?\"\n - options: [\"Sprint 47 (May 20–Jun 2)\", \"Sprint 48 (Jun 3–Jun 16)\", \"Other\"]\n ```", + " ```json\n {\n \"questions\": [\n {\n \"header\": \"Sprint\",\n \"question\": \"Which live sprint should receive the issue?\",\n \"options\": [\n { \"label\": \"Sprint 47 (Recommended)\", \"description\": \"Use the current active sprint discovered from the tracker.\" },\n { \"label\": \"Sprint 48\", \"description\": \"Use the next open sprint discovered from the tracker.\" }\n ]\n }\n ]\n }\n ```", + ), + ( + "**Example — Jira via MCP:**\n```bash\n# Pseudocode; MCP server translates to Jira API\njira_create(\n project: \"BT\",\n type: \"Story\",\n summary: \"OpenCode CLI: Installation Guide\",\n description: \"\",\n sprint: ,\n assignee: ,\n customfield_10014: [\"doc\", \"cli\"] # labels\n)\n# Returns: { key: \"BT-3014\", id: \"12345\" }\n```", + "**Example — Jira via Pi's MCP gateway:**\n```text\nmcp({ search: \"Jira create issue\" })\nmcp({\n tool: \"\",\n args: '{\"project\":\"BT\",\"type\":\"Story\",\"summary\":\"OpenCode CLI: Installation Guide\",\"description\":\"\",\"sprint\":\"\",\"assignee\":\"\",\"labels\":[\"doc\",\"cli\"]}'\n})\n```\n\nUse the exact schema returned by `mcp({ describe: \"\" })`; the fields above are illustrative, not a raw function call.", + ), + ( + "### 6. Map Fields\n\nResolve the tracker fields you need. This requires user input for ambiguous cases — never guess.", + "### 6. Map Fields\n\nFor ambiguous structured decisions, use the bundled `@juicesharp/rpiv-ask-user-question` `ask_user_question` tool with the package `questions[]` schema and 2-4 authored options. Do not author sentinel labels such as `Type something.`, `Chat about this`, `Other`, or `Next`; the package supplies escape hatches. Put the recommended option first and append `(Recommended)` when one is clear.\n\nResolve the tracker fields you need. This requires user input for ambiguous cases — never guess.", + ), + ( + "**Origin:** arc issue agentmarke-0qex.04hl1w (https://arc.bactrack.com/browse/agentmarke-0qex.04hl1w)", + "**Origin:** arc issue agentmarke-0qex.04hl1w\n\nInclude an Arc URL only when `arc show` or project configuration provides a canonical base URL; never fabricate a host.", + ), + ( + "### 10. Verify — Non-Negotiable\n\n**Re-read the created issue from the tracker.** Confirm that sprint, labels, and assignee actually landed.", + "### 10. Verify — Non-Negotiable\n\n**Re-read both records.** Fetch the updated Arc issue and confirm its complete prior body plus the new tracker backlink remain present. Then re-read the created external issue and confirm its Arc origin plus sprint, labels, and assignee landed.", + ), + ( + "The full current description was already captured in Step 2 (`arc show --json`). To safely backlink, re-supply that **complete existing body** with the tracker link appended, using `--stdin` to avoid shell-escaping or clobbering:\n\n```bash\narc update --stdin <<'EOF'\n\n\n---\n**Tracker:** [BT-3014](https://bactrack.atlassian.net/browse/BT-3014)\nEOF\n```", + "Preserve the current Arc description mechanically: write it to a temporary file, append only the backlink, then pipe the file back through `--stdin`. Never retype the existing body through the model:\n\n```bash\nTMP=$(mktemp)\narc show --json | jq -j .description > \"$TMP\"\ncat >> \"$TMP\" <<'EOF'\n\n---\n**Tracker:** [BT-3014](https://bactrack.atlassian.net/browse/BT-3014)\nEOF\narc update --stdin < \"$TMP\"\nrm -f \"$TMP\"\n```", + ), +]) + # Copy agents as bundled prompts for arc_agent. for f in sorted((SRC / "agents").glob("*.md")): text = transform_text(f.read_text()) @@ -608,6 +660,8 @@ def replace_section(rel: str, start_marker: str, end_marker: str, replacement: s text = re.sub(r"(?m)^model:\s*haiku\s*$", "model: small", text) text = re.sub(r"(?m)^model:\s*sonnet\s*$", "model: standard", text) text = re.sub(r"(?m)^model:\s*opus\s*$", "model: large", text) + if f.name in {"code-reviewer.md", "devops-builder.md", "evaluator.md", "spec-reviewer.md"}: + text = re.sub(r"(?m)^model:\s*standard\s*$", "model: large", text) if f.name == "issue-manager.md": text = re.sub(r"(?m)^model:\s*small\s*$", "model: nano", text) if "## Timing / Progress Instrumentation" not in text: @@ -646,8 +700,8 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st patch_file("skills/arc/SKILL.md", [ ( - "- **Parallel Arc build**: For independent task batches, `implement` can use worktree-isolated `pi-subagents` runs when that companion package and Arc agent definitions are available. This is not Claude-style team deployment; the orchestrator still owns verification, patch application, issue closure, and handoff.", - "- **Parallel Arc build**: For independent task batches, `implement` can use worktree-isolated `pi-subagents` runs when an external `pi-subagents` extension/tool is installed and Arc specialist definitions are available. Custom Arc specialists remain the preferred `pi-subagents` targets, and generic `worker`/`reviewer` agents should not be substituted for Arc gates. This is not Claude-style team deployment; the orchestrator still owns verification, patch application, issue closure, and handoff.", + "- **Parallel Arc build**: For independent task batches, `build` can use worktree-isolated `pi-subagents` runs when that companion package and Arc agent definitions are available. This is not Claude-style team deployment; the orchestrator still owns verification, patch application, issue closure, and handoff.", + "- **Parallel Arc build**: For independent task batches, `build` can use worktree-isolated `pi-subagents` runs when an external `pi-subagents` extension/tool is installed and Arc specialist definitions are available. Custom Arc specialists remain the preferred `pi-subagents` targets, and generic `worker`/`reviewer` agents should not be substituted for Arc gates. This is not Claude-style team deployment; the orchestrator still owns verification, patch application, issue closure, and handoff.", ), ]) @@ -661,8 +715,8 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st "**Example `ask_user_question` usage:**\n```json\n{\n \"questions\": [\n {\n \"header\": \"Session\",\n \"question\": \"How should we handle session persistence?\",\n \"options\": [\n {\n \"label\": \"SQLite (Recommended)\",\n \"description\": \"Persistent, single-node, matches existing storage, and avoids new infrastructure.\"\n },\n {\n \"label\": \"In-memory only\",\n \"description\": \"Simplest option, but sessions are lost on restart.\"\n },\n {\n \"label\": \"Redis\",\n \"description\": \"Supports distributed deployments, but adds an infrastructure dependency.\"\n }\n ]\n }\n ]\n}\n```", ), ( - "**Example `ask_user_question` usage:**\n```\nQuestion: \"Which approach should we go with?\"\nOptions:\n - \"Approach A: ...\" (recommended — trade-offs...)\n - \"Approach B: ...\" (trade-offs...)\n - \"Approach C: ...\" (trade-offs...)\n```", - "**Example `ask_user_question` usage:**\n```json\n{\n \"questions\": [\n {\n \"header\": \"Approach\",\n \"question\": \"Which approach should we go with?\",\n \"options\": [\n {\n \"label\": \"Approach A (Recommended)\",\n \"description\": \"Best balance of scope, risk, and implementation speed for the current constraints.\"\n },\n {\n \"label\": \"Approach B\",\n \"description\": \"Lower short-term code churn, but leaves more long-term maintenance risk.\"\n },\n {\n \"label\": \"Approach C\",\n \"description\": \"Most flexible, but likely needs larger-model implementation and more review cycles.\"\n }\n ]\n }\n ]\n}\n```", + "**Example — full approach write-ups as text, then:**\n```\nQuestion: \"Which approach should we go with?\"\nOptions:\n - \"A: \" (recommended — )\n - \"B: \" ()\n - \"C: \" ()\n```", + "**Example — after presenting the full approach write-ups as text:**\n```json\n{\n \"questions\": [\n {\n \"header\": \"Approach\",\n \"question\": \"Which approach should we go with?\",\n \"options\": [\n {\n \"label\": \"Approach A (Recommended)\",\n \"description\": \"Recommended for the reasons analyzed above.\"\n },\n {\n \"label\": \"Approach B\",\n \"description\": \"Choose the second approach analyzed above.\"\n },\n {\n \"label\": \"Approach C\",\n \"description\": \"Choose the third approach analyzed above.\"\n }\n ]\n }\n ]\n}\n```", ), ( "If the design will produce multiple implementation tasks that could run in parallel, explicitly identify the **shared contracts** — types, interfaces, config keys, constants, and function signatures that multiple tasks will reference.\n\nContracts fall into two tiers:", @@ -673,29 +727,33 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st "```json\n{\n \"questions\": [\n {\n \"header\": \"Grill\",\n \"question\": \"Stress-test the design before publishing?\",\n \"options\": [\n {\n \"label\": \"Yes, grill me (Recommended)\",\n \"description\": \"Interrogate decisions one at a time until the design converges; recommended for medium/large work or when clarifying questions were skipped.\"\n },\n {\n \"label\": \"No, proceed\",\n \"description\": \"Skip the stress-test and register the saved design for review now.\"\n }\n ]\n }\n ]\n}\n```", ), ( - "```\nQuestion: \"How would you like to review this design?\"\nOptions:\n - \"Legacy planner (solo, plain HTTP, simplest)\" —\n `arc plan` surface at /planner/. No encryption, no accept-resolve;\n just a comment thread on a markdown render. Best when you want quick\n review notes without setting up the share UI.\n - \"Encrypted local share (solo, but want annotations/accept-resolve)\" —\n `arc share` on this machine. Plan content + comments are encrypted at\n rest in ~/.arc/data.db. Reviewer URL only works from this machine.\n - \"Encrypted remote share (multiple reviewers)\" —\n `arc share` on the configured remote server (default arcplanner.sentiolabs.io).\n Reviewers on other machines can open the link.\n - \"Save for later\" — keep the saved file (from step 5.5) and stop. No\n server registration; resume in a new session. **Terminates the\n skill — skip steps 7 and 8.**\n```", - "```json\n{\n \"questions\": [\n {\n \"header\": \"Review\",\n \"question\": \"How would you like to review this design?\",\n \"options\": [\n {\n \"label\": \"Legacy planner\",\n \"description\": \"Solo plain-HTTP review at /planner/; simplest, with no encryption or accept/resolve UI.\"\n },\n {\n \"label\": \"Encrypted local\",\n \"description\": \"Solo encrypted review with annotations and accept/resolve UI on this machine only.\"\n },\n {\n \"label\": \"Encrypted remote\",\n \"description\": \"Multiple reviewers can open the remote encrypted share; the author URL must stay private.\"\n },\n {\n \"label\": \"Save for later\",\n \"description\": \"Keep the saved design file and stop without server registration; resume in a new session.\"\n }\n ]\n }\n ]\n}\n```", + "```\nQuestion: \"Register this design on the planner for review?\"\nOptions:\n - \"Register on the planner\" — comment thread at /planner/\n - \"Save for later\" — keep the local file (from step 5.5) and stop\n```", + "```json\n{\n \"questions\": [\n {\n \"header\": \"Review\",\n \"question\": \"Register this design on the planner for review?\",\n \"options\": [\n {\n \"label\": \"Register (Recommended)\",\n \"description\": \"Create a local planner comment thread at /planner/.\"\n },\n {\n \"label\": \"Save for later\",\n \"description\": \"Keep the local design file and stop without registering it.\"\n }\n ]\n }\n ]\n}\n```", ), ( - "```\nQuestion: \"Design ready for review at — how would you like to proceed?\"\nOptions:\n - \"Approve\" — mark the design approved and proceed to step 8\n routing analysis\n - \"I've finished review (pull comments now)\" — fetch reviewer feedback,\n apply edits, re-share if needed, repeat\n - \"Pause review\" — design is saved; resume in a new session\n```", - "```json\n{\n \"questions\": [\n {\n \"header\": \"Review\",\n \"question\": \"Design ready for review at — how would you like to proceed?\",\n \"options\": [\n {\n \"label\": \"Approve\",\n \"description\": \"Mark the design approved and continue to routing analysis.\"\n },\n {\n \"label\": \"I've finished review (pull comments now)\",\n \"description\": \"Fetch accepted reviewer feedback, apply edits, update the review surface if needed, and repeat review.\"\n },\n {\n \"label\": \"Pause review\",\n \"description\": \"Leave the design saved in docs/plans and resume in a future session.\"\n }\n ]\n }\n ]\n}\n```", + "```\nQuestion: \"Design ready for review at — how would you like to proceed?\"\nOptions:\n - \"Approve\" — proceed to step 8 routing analysis\n - \"Pull review comments\" — fetch feedback, apply edits, repeat\n - \"Pause review\" — design is saved; resume in a new session\n```", + "```json\n{\n \"questions\": [\n {\n \"header\": \"Review\",\n \"question\": \"Design ready for review at — how would you like to proceed?\",\n \"options\": [\n {\n \"label\": \"Approve\",\n \"description\": \"Approve the design and continue to routing analysis.\"\n },\n {\n \"label\": \"Pull comments\",\n \"description\": \"Read planner feedback, apply edits, re-register if needed, and repeat review.\"\n },\n {\n \"label\": \"Pause review\",\n \"description\": \"Leave the design saved in docs/plans and resume later.\"\n }\n ]\n }\n ]\n}\n```", ), ( "```\nQuestion: \"Design approved! What's next?\"\nOptions:\n - \"Break into tasks with /arc-plan\" (recommended — )\n - \"Implement directly with /arc-build\" (for small, single-task work)\n - \"Done for now\" (design is saved — continue in a new session)\n```", "```json\n{\n \"questions\": [\n {\n \"header\": \"Next\",\n \"question\": \"Design approved! What's next?\",\n \"options\": [\n {\n \"label\": \"Break into tasks (Recommended)\",\n \"description\": \"Recommended when the design has multiple work items, shared contracts, multiple layers, migrations, breaking changes, or medium/large scale.\"\n },\n {\n \"label\": \"Implement directly\",\n \"description\": \"Use only for small designs with one work item, one layer, no shared contracts, and no risk areas.\"\n },\n {\n \"label\": \"Done for now\",\n \"description\": \"The design is approved and saved; continue with /arc-plan in a future session.\"\n }\n ]\n }\n ]\n}\n```", ), + ( + "First create a single self-contained task capturing the approved design by piping the design doc directly: `arc create \"\" -t task --stdin < docs/plans/<file>.md` (file redirection keeps the description byte-exact — never retype or summarize it, and don't route long content through a subagent prompt).", + "First canonicalize the approved design to match Arc's outer-whitespace normalization, then create one self-contained task without passing the body through the model: `TMP=$(mktemp); python3 -c 'from pathlib import Path; import sys; Path(sys.argv[2]).write_text(Path(sys.argv[1]).read_text().strip())' docs/plans/<file>.md \"$TMP\"; arc create \"<title>\" -t task --stdin < \"$TMP\"; rm -f \"$TMP\"`.", + ), ]) replace_section("skills/arc-build/SKILL.md", "## Model Selection\n\n", "\n## Dispatch Modes", """## Model Selection -Every Arc subagent dispatch can override the subagent's frontmatter model via the `model:` parameter. `modelProfiles` from `${XDG_CONFIG_HOME:-~/.config}/pi-arc/models.json` are the preferred way to choose role-specific models, and `arc.modelTiers` is a legacy fallback for older setups. Before dispatching, assess the task size/risk and choose the smallest model tier that is likely to succeed. The default floor per agent is set in frontmatter — use overrides to downgrade trivial tasks or escalate complex/high-risk tasks. +Every Arc subagent dispatch can override the subagent's frontmatter model via the `model:` parameter. `modelProfiles` from `${XDG_CONFIG_HOME:-~/.config}/pi-arc/models.json` are the preferred way to choose role-specific models, and `arc.modelTiers` is a legacy fallback for older setups. GPT-5.6 maps naturally onto Arc's roles: Luna for fast/affordable work, Terra for balanced implementation, and Sol for high-risk reasoning. The dedicated `devopsBuilder` profile uses Sol because live-system changes require blast-radius, staging, and rollback judgment. Before dispatching, assess the task size/risk and choose the smallest model tier that is likely to succeed. The default floor per agent is set in frontmatter — use overrides to downgrade trivial tasks or escalate complex/high-risk tasks. | Tier | Default concrete model | Use for | |---|---|---| -| `nano` | `openai-codex/gpt-5.4-mini` | Bulk CLI issue creation and other low-reasoning issue-manager work | -| `small` | `openai-codex/gpt-5.4-mini` | Mechanical edits and docs | -| `standard` | `openai-codex/gpt-5.3-codex` | Normal contained implementation/review | -| `large` | `openai-codex/gpt-5.5` | Cross-cutting, architectural, security-sensitive, or adversarial review | +| `nano` | `openai-codex/gpt-5.6-luna` | Bulk CLI issue creation and other low-reasoning issue-manager work | +| `small` | `openai-codex/gpt-5.6-luna` | Mechanical edits and docs | +| `standard` | `openai-codex/gpt-5.6-terra` | Normal contained implementation/review | +| `large` | `openai-codex/gpt-5.6-sol` | Cross-cutting, architectural, security-sensitive, or adversarial review | ```markdown Arc model selection resolves in this order: @@ -714,10 +772,10 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st { "arc": { "modelTiers": { - "nano": "openai-codex/gpt-5.4-mini", - "small": "openai-codex/gpt-5.4-mini", - "standard": "openai-codex/gpt-5.3-codex", - "large": "openai-codex/gpt-5.5" + "nano": "openai-codex/gpt-5.6-luna", + "small": "openai-codex/gpt-5.6-luna", + "standard": "openai-codex/gpt-5.6-terra", + "large": "openai-codex/gpt-5.6-sol" } } } @@ -747,9 +805,9 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st arc_agent(agent="builder", model="large", task="...") # complex # Preferred when pi-subagents Arc agents are installed: -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.4-mini", context: "fresh", async: true, clarify: false }) -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.3-codex", context: "fresh", async: true, clarify: false }) -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.5", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-luna", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-terra", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-sol", context: "fresh", async: true, clarify: false }) ``` **When unsure, omit `model:`** — the agent's frontmatter floor is calibrated for the typical case. @@ -785,7 +843,7 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st `pi-subagents` worktree mode returns per-task patch files and cleans up temporary worktrees. It does **not** automatically merge changes into the main working tree. The orchestrator must inspect, apply, verify, commit, and close each patch/task explicitly. -**When NOT to use parallel**: missing `subagent` tool, missing Arc agent definitions, overlapping files, task dependencies, uncertainty about scope, or fewer than 3 implementation tasks. Default to sequential — the cost of serial execution is time; the cost of a bad parallel patch merge is data loss. +**When NOT to use parallel**: missing `subagent` tool, missing Arc agent definitions, `devops` tasks that touch live systems, overlapping files, task dependencies, uncertainty about scope, or fewer than 3 implementation tasks. Default to sequential — the cost of serial execution is time; the cost of a bad parallel patch merge is data loss. ## Orchestration Loop @@ -800,10 +858,12 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st - `completed` when the task is closed in arc ```bash -# Get the list of tasks to implement -arc list --parent=<epic-id> --status=open --json +# Get every unfinished child, including resumed/blocked/deferred work +arc list --parent=<epic-id> --json | jq '.[] | select(.status != "closed")' ``` +If you were handed an epic ID, use its children. If you were handed one standalone task ID from the brainstorm-direct path, use `arc ready` / `arc show <task-id>` and run the loop once. If no Arc task exists, stop and route the user to `/arc-plan`; build dispatches existing tasks and does not invent them. + Create a `todo` checklist entry for each, then work through this loop: """) @@ -815,33 +875,39 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st PRE_TASK_SHA=$(git rev-parse HEAD) ``` -Check whether the task has a `docs-only` label: +Fetch the design excerpt once for the implementer, evaluator, and code reviewer: ```bash -arc show <task-id> --json | jq -e '.labels[] | select(. == "docs-only")' > /dev/null 2>&1 +PARENT=$(arc show <task-id> --json | jq -r '.parent_id // empty') +[ -n "$PARENT" ] && arc show "$PARENT" ``` -**If `docs-only`** (exit code 0) — spawn a `doc-writer` subagent: +Extract the sections relevant to this task into `{DESIGN_EXCERPT}`. If the task has no parent epic, use `none`. -Use the template at `./doc-writer-prompt.md`. Fill placeholder `{TASK_ID}`. For docs-only work, the agent default (`small`) is correct — omit `model:` unless the docs task is unusually complex. +Check task labels with precedence `docs-only` → `devops` → `builder`: -Dispatch preference: -- If `subagent` is available and `arc-doc-writer` is installed: `subagent({ agent: "arc-doc-writer", task: "<filled prompt>", context: "fresh", async: true, clarify: false })` -- If `subagent` is available but Arc specialists are missing: Arc specialists should already be auto-materialized. First run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })`. -- Otherwise: `arc_agent(agent="doc-writer", task="<filled prompt>")` +```bash +arc show <task-id> --json | jq -e '.labels[] | select(. == "docs-only")' > /dev/null 2>&1 +arc show <task-id> --json | jq -e '.labels[] | select(. == "devops")' > /dev/null 2>&1 +``` -For async `pi-subagents` dispatches, immediately capture the returned run ID, poll with `subagent({ action: "status", id: "<run-id>" })` or watch `/subagents-status` until terminal, then read the final output before evaluating the report or moving to validation. +**If `docs-only`** — use `./doc-writer-prompt.md` and dispatch: +- Preferred: `subagent({ agent: "arc-doc-writer", task: "<filled prompt>", context: "fresh", async: true, clarify: false })` +- Fallback: `arc_agent(agent="doc-writer", task="<filled prompt>")` -**Otherwise** — spawn a `builder` subagent: +**Else if `devops`** — use `./devops-builder-prompt.md`, filling `{TASK_ID}`, `{PRE_TASK_SHA}`, `{DESIGN_EXCERPT}`, and `{MODEL_TIER_NOTE}`. The `devopsBuilder` model profile is recommended at the `large` tier because operations work has live blast radius and partial-failure modes. Dispatch: +- Preferred: `subagent({ agent: "arc-devops-builder", task: "<filled prompt>", context: "fresh", async: true, clarify: false })` +- Fallback: `arc_agent(agent="devops-builder", task="<filled prompt>")` (the configured `devopsBuilder` profile is authoritative; `large` frontmatter is the fallback) -Use the template at `./builder-prompt.md`. Fill placeholders (`{TASK_ID}`, `{PRE_TASK_SHA}`, `{DESIGN_EXCERPT}`) and apply Model Selection guidance (see `## Model Selection` above) for the dispatch `model:`. +The devops builder follows PLAN → SAFEGUARD → APPLY → VERIFY → GATE. Never route `devops` tasks through the normal TDD builder, and never include live-system operations tasks in a parallel patch batch. -Dispatch preference: -- If `subagent` is available and `arc-builder` is installed: `subagent({ agent: "arc-builder", task: "<filled prompt>", model: "<concrete-model-if-needed>", context: "fresh", async: true, clarify: false })` -- If `subagent` is available but Arc specialists are missing: Arc specialists should already be auto-materialized. First run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })`. -- Otherwise: `arc_agent(agent="builder", task="<filled prompt>", model="<tier-if-needed>")` +**Otherwise** — use `./builder-prompt.md`, filling `{TASK_ID}`, `{PRE_TASK_SHA}`, and `{DESIGN_EXCERPT}`. Dispatch: +- Preferred: `subagent({ agent: "arc-builder", task: "<filled prompt>", model: "<concrete-model-if-needed>", context: "fresh", async: true, clarify: false })` +- Fallback: `arc_agent(agent="builder", task="<filled prompt>", model="<tier-if-needed>")` -For async `pi-subagents` dispatches, immediately capture the returned run ID, poll with `subagent({ action: "status", id: "<run-id>" })` or watch `/subagents-status` until terminal, then read the final output before evaluating the report or moving to validation. +Arc specialists should already be auto-materialized. If a required specialist is missing, first run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })`. + +For async `pi-subagents` dispatches, capture the returned run ID, poll with `subagent({ action: "status", id: "<run-id>" })` or watch `/subagents-status` until terminal, and read the final output before validation. """) replace_section("skills/arc-build/SKILL.md", "Dispatch `spec-reviewer`:\n\n", "\nHandle results:", """Dispatch `spec-reviewer`: @@ -849,7 +915,7 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st Use the template at `./spec-reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}`, `{HEAD_SHA}`). Spec review is a focused comparison task — the Arc `standard` tier is appropriate unless the spec is unusually large or ambiguous. Dispatch preference: -- If `subagent` is available and `arc-spec-reviewer` is installed: `subagent({ agent: "arc-spec-reviewer", task: "<filled prompt>", model: "openai-codex/gpt-5.3-codex", context: "fresh", async: true, clarify: false })` +- If `subagent` is available and `arc-spec-reviewer` is installed: `subagent({ agent: "arc-spec-reviewer", task: "<filled prompt>", context: "fresh", async: true, clarify: false })` - If `subagent` is available but Arc specialists are missing: Arc specialists should already be auto-materialized. First run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })`. - Otherwise: `arc_agent(agent="spec-reviewer", task="<filled prompt>")` @@ -858,34 +924,130 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st Do **not** substitute the generic `worker` or `reviewer` agent for spec compliance gates. Generic `pi-subagents` agents are not Arc specialists, and manually passing an Anthropic model bypasses Arc's Pi-native model tier policy. If Arc `pi-subagents` definitions are unavailable, use the bundled sequential `arc_agent` fallback. """) +patch_file("skills/arc-build/SKILL.md", [ + ( + "Every `builder` and `doc-writer` dispatch returns one of four terminal statuses. Handle each explicitly:", + "Every `builder`, `devops-builder`, and `doc-writer` dispatch returns one of four terminal statuses. Handle each explicitly:", + ), + ( + "Spec review is a focused comparison task — the Arc `standard` tier is appropriate unless the spec is unusually large or ambiguous.", + "The configured `specReviewer` profile is authoritative; the agent's `large` frontmatter is the fallback.", + ), + ( + "# or for a specific epic:\narc list --parent=<epic-id> --status=open", + "# or for a specific epic, include resumed/blocked/deferred children:\narc list --parent=<epic-id> --json | jq '.[] | select(.status != \"closed\")'", + ), + ( + "### 10. Epic Completion Gate\n\nClosing the last task is not the same as the epic being done.", + "### 10. Completion Gate\n\nFor a standalone task, verify its task-specific command (or live `## Verification` for DevOps), confirm it is closed, skip all epic-only commands, and hand off to `finish`.\n\nFor an epic, closing the last selected task is not the same as the epic being done.", + ), + ( + "1. **All tasks closed:** `arc list --parent=<epic-id> --status=open` returns nothing.\n2. **Full suite green:** run the project's full test command (not a per-task subset) and confirm exit 0.", + "1. **All tasks closed:** `arc list --parent=<epic-id> --json | jq '[.[] | select(.status != \"closed\")] | length'` returns `0`. Any `open`, `in_progress`, `blocked`, or `deferred` child keeps the epic open.\n2. **Epic-wide verification:** for code/docs epics, run the project's full test command and confirm exit 0. For DevOps-only epics, re-run each task's live `## Verification` and confirm rollback evidence; for mixed epics, run both.", + ), +]) + patch_file("skills/arc-plan/SKILL.md", [ ( "**Model tier:** `issue-manager` defaults to `nano` — the right tier for low-reasoning CLI formatting and bulk issue creation. For this dispatch, omit `model:`. See the Model Selection table in `../arc-build/SKILL.md` for the full guidance.", - "**Model tier:** `issue-manager` defaults to `nano` — the right tier for low-reasoning CLI formatting and bulk issue creation. Model profile: issue creation uses the issueManager profile when configured via `/arc-models`; otherwise it falls back to the legacy tier/frontmatter behavior. This work is mostly CLI formatting, so the recommended profile uses gpt-5.4-mini with thinking off. For this dispatch, omit `model:`. See the Model Selection table in `../arc-build/SKILL.md` for the full guidance.", + "**Model tier:** `issue-manager` defaults to `nano` — the right tier for low-reasoning CLI formatting and bulk issue creation. Model profile: issue creation uses the issueManager profile when configured via `/arc-models`; otherwise it falls back to the legacy tier/frontmatter behavior. This work is mostly CLI formatting, so the recommended profile uses gpt-5.6-luna with thinking off. For this dispatch, omit `model:`. See the Model Selection table in `../arc-build/SKILL.md` for the full guidance.", + ), + ( + "Then dispatch the manifest — titles, metadata, and file paths only, no description bodies:\n\n```\nUse the arc_agent tool with agent=\"issue-manager\":\n\nCreate the following epic and tasks using the arc CLI.", + "Before persistence, self-review the canonical description files against the approved design:\n\n1. **Spec coverage:** Every design requirement maps to a task.\n2. **Success-criteria coverage:** Every `## Success Criteria` item maps to at least one task's `## Expected Outcome`.\n3. **T0 contract coverage:** Shared contract blocks match the T0 definitions exactly.\n4. **Type consistency:** Names and signatures agree across tasks.\n5. **Placeholder scan:** No TBD/TODO/vague implementation placeholders remain.\n6. **Step completeness:** Every code or command step includes concrete content.\n\nFix the canonical files now, then repeat this review. Do not create any Arc issue until it passes.\n\nIssue creation must be phased:\n\n1. Create the epic first and capture the epic ID.\n2. Create all child tasks with the epic as parent before applying dependencies.\n3. Capture the complete task-name-to-ID table.\n4. Apply dependencies only after all child IDs exist.\n5. Apply labels after dependencies with `arc update <id> --label-add=<label>`.\n6. Verify descriptions and return the final ID table, dependency summary, and a `## Timing` section with phase-level `elapsed_ms` values.\n\nThen dispatch the manifest — titles, metadata, and file paths only, no description bodies. Prefer true `pi-subagents` so long issue-creation runs are visible in `/subagents-status`:\n\nDispatch preference:\n- Primary: `subagent({ agent: \"arc-issue-manager\", task: \"<manifest below>\", context: \"fresh\", async: true, clarify: false })`\n- Wait for terminal status by polling `subagent({ action: \"status\", id: \"<run-id>\" })` until `completed` or `failed`\n- Users can monitor progress via `/subagents-status`\n- If `subagent({ action: \"list\" })` shows `arc-issue-manager`, do **not** use the slower `arc_agent(agent=\"issue-manager\")` fallback\n- If it is missing, run `subagent({ action: \"doctor\" })` and inspect Arc's materialization warning; use `/arc-subagents-sync` only as a deprecated repair command\n- Fallback only when `pi-subagents` is unavailable after repair: `arc_agent(agent=\"issue-manager\", task=\"<manifest below>\")`\n\nUse this task payload for whichever dispatcher you choose:\n\n```markdown\nCreate the following epic and tasks using the arc CLI.", ), ( - "The share keyring entries have `{id, kind, url, key_b64url, plan_file, created_at}` — edit tokens are intentionally redacted. Then dispatch the manifest. Prefer true `pi-subagents` so long issue-creation runs are visible in `/subagents-status`:", - "The share keyring entries have `{id, kind, url, key_b64url, plan_file, created_at}` — edit tokens are intentionally redacted.\n\nIssue creation must be phased:\n\n1. Create the epic first and capture the epic ID.\n2. Create all child tasks with the epic as parent before applying dependencies.\n3. Capture the complete task-name-to-ID table.\n4. Apply dependencies only after all child IDs exist.\n5. Apply labels after dependencies, or in the same post-creation phase.\n6. Return the final ID table, dependency summary, and a `## Timing` section with phase-level `elapsed_ms` values when available.\n\nThen dispatch the manifest. Prefer true `pi-subagents` so long issue-creation runs are visible in `/subagents-status`:", + "- Create every issue with its description piped from the listed file:\n arc create \"<title>\" --type=<type> [--parent=<id>] [--label=<label>] --stdin < \"<description file>\"", + "- Create the epic first, then create every child with its description piped from the listed file:\n arc create \"<title>\" --type=<type> [--parent=<id>] --stdin < \"<description file>\"\n- Create all children and capture every ID before applying dependencies.\n- Apply dependencies only after all child IDs exist.\n- Apply manifest labels only after dependencies with `arc update <id> --label-add=<label>`.", ), ( - "- If `subagent` unavailable or `arc-issue-manager` missing: run `/arc-subagents-sync`, then `subagent({ action: \"list\" })` to verify, then retry primary\n- Fallback only if `pi-subagents` is not installed or cannot load after sync: `arc_agent(agent=\"issue-manager\", task=\"<manifest below>\")`", - "- Arc issue-manager should be auto-materialized; if it is missing, first run `subagent({ action: \"doctor\" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: \"list\" })`\n- Fallback only if `pi-subagents` is not installed or cannot load after deprecated repair: `arc_agent(agent=\"issue-manager\", task=\"<manifest below>\")`", + "| Epic | ... | ... | ... | ... |\n| T1 | ... | ... | ... | ... |\n```", + "| Epic | ... | ... | ... | ... |\n| T1 | ... | ... | ... | ... |\n\n## Timing\n| Phase | elapsed_ms |\n|-------|------------|\n| epic | ... |\n| child_tasks | ... |\n| dependencies | ... |\n| labels | ... |\n| verification | ... |\n```\n\nThe `## Timing` section is required for bulk issue creation; use `unknown` only when a phase timestamp could not be captured.", ), ( - "Return a summary table mapping task names to arc IDs.", - "Return a summary table mapping task names to arc IDs, plus a `## Timing` section with phase-level `elapsed_ms` values when available.", + "Labels are applied at creation time via the repeatable `--label` flag — never as a separate follow-up pass that can be skipped.", + "Keep `Labels:` in the manifest, but apply labels only after dependencies with `arc update <id> --label-add=<label>` so the phased creation contract remains observable and recoverable.", ), ( - "| Epic | ... | ... |\n| T1 | ... | ... |\n```\n\n**IMPORTANT**: The epic description MUST contain the complete approved design.", - "| Epic | ... | ... |\n| T1 | ... | ... |\n\n## Timing\n| Phase | elapsed_ms |\n|-------|------------|\n| epic | ... |\n| child_tasks | ... |\n| dependencies | ... |\n| labels | ... |\n```\n\nThe `## Timing` section is required for bulk issue creation; use `unknown` for a phase only if the issue-manager could not capture a timestamp.\n\n**IMPORTANT**: The epic description MUST contain the complete approved design.", + "Counts must match (±1 for a trailing newline). For tasks with code blocks (T0 especially), also compare code-fence counts — ``grep -c '^```' <file>`` vs the same grep over `arc show <id> --json | jq -r .description`. A summarized description is a plan failure — detail dropped here is detail the implementer never sees.", + "First compare byte hashes: `sha256sum < \"<description file>\"` must equal `arc show <id> --json | jq -j .description | sha256sum`. Line counts and, for T0, code-fence counts are diagnostics only. Any hash mismatch is a plan failure — repair with file redirection and re-check before continuing.", + ), + ( + "**Never put description content in the agent prompt — descriptions travel as files.** Any content that passes through the subagent's prompt or output gets re-emitted token-by-token, and smaller models compress long content when re-emitting it, *even when explicitly told not to*. The defense is mechanical, not instructional: write each description to a file, and the agent pipes it into arc with shell redirection (`--stdin < file`) so the bytes never flow through the model.", + "**Never put description content in the agent prompt — descriptions travel as canonical files.** Arc normalizes leading/trailing whitespace from `--stdin`, so canonicalize each file with outer whitespace removed before dispatch. The issue-manager then transfers those canonical bytes with shell redirection; description bodies never pass through the model.", + ), + ( + "1. Create a manifest directory: `mkdir -p /tmp/arc-manifest-<epic-slug>`\n2. Write each task's full self-contained description to its own file with the `write` tool: `/tmp/arc-manifest-<epic-slug>/T0.md`, `T1.md`, … You authored these descriptions, so writing them yourself is verbatim by construction.\n3. The **epic's** description file is the plan file itself. You typically already have its path from the brainstorm hand-off; if you only have the ID, `arc plan show` prints it in its metadata header:\n\n```bash\narc plan show <id> | grep -oE '^File: \\S+' | awk '{print $2}'\n```", + "1. Create a manifest directory: `mkdir -p /tmp/arc-manifest-<epic-slug>`.\n2. Write every task's full self-contained draft to `/tmp/arc-manifest-<epic-slug>/T0.md`, `T1.md`, and so on with the `write` tool.\n3. Copy the approved plan to `/tmp/arc-manifest-<epic-slug>/epic.md`. If only the plan ID is known, recover the source path with `arc plan show <id> | grep -oE '^File: \\S+' | awk '{print $2}'`.\n4. Canonicalize only outer whitespace so the files match Arc's `--stdin` normalization while preserving every internal byte:\n ```bash\n python3 - /tmp/arc-manifest-<epic-slug> <<'PY'\n from pathlib import Path\n import sys\n for path in Path(sys.argv[1]).glob('*.md'):\n path.write_text(path.read_text().strip())\n PY\n ```\n5. From this point onward, hash, dispatch, repair, and verify only these canonical files.", + ), + ( + "Description file: <absolute path to the plan markdown file>", + "Description file: /tmp/arc-manifest-<epic-slug>/epic.md", + ), + ( + "- After each create, verify the description landed verbatim:\n arc show <id> --json | jq -r .description | wc -l\n must match `wc -l < \"<description file>\"` (±1 for a trailing newline).\n Report any mismatch in your summary — do not silently continue.", + "- After each create, verify the stored description equals the canonical file:\n `sha256sum < \"<description file>\"` must equal\n `arc show <id> --json | jq -j .description | sha256sum`.\n Treat any mismatch as a failed verification phase; repair from the canonical file and re-check.", + ), + ( + "| Task | Arc ID | Title | File lines | Arc lines |\n|------|--------|-------|------------|-----------|", + "| Task | Arc ID | Title | File SHA-256 | Arc SHA-256 |\n|------|--------|-------|-------------|------------|", ), ( "**Use the `ask_user_question` tool** to let the user choose:\n\n```\nQuestion: \"Epic and tasks created. How should we proceed with implementation?\"\nOptions:\n - \"Start implementing now\" (invoke /arc-build in this session — subagents handle TDD per task)\n - \"Implement in a new session\" (provides the exact prompt to use)\n - \"Done for now\" (tasks are tracked in arc — implement manually or later)\n```", - "**Use the `ask_user_question` tool** with the package's `questions[]` schema to let the user choose:\n\n```json\n{\n \"questions\": [\n {\n \"header\": \"Next\",\n \"question\": \"Epic and tasks created. How should we proceed with implementation?\",\n \"options\": [\n {\n \"label\": \"Start now (Recommended)\",\n \"description\": \"Recommended when you want this session to continue directly into /arc-build with subagents handling TDD per task.\"\n },\n {\n \"label\": \"New session\",\n \"description\": \"Prints the exact /arc-build <epic-id> command to run in a fresh Pi session.\"\n },\n {\n \"label\": \"Done for now\",\n \"description\": \"Leaves the tasks tracked in arc for manual or future implementation.\"\n }\n ]\n }\n ]\n}\n```", + "**Use the bundled `@juicesharp/rpiv-ask-user-question` `ask_user_question` tool** with the package `questions[]` schema to let the user choose. Do not manually author package sentinel labels (`Type something.`, `Chat about this`, `Other`, `Next`):\n\n```json\n{\n \"questions\": [\n {\n \"header\": \"Next\",\n \"question\": \"Epic and tasks created. How should we proceed with implementation?\",\n \"options\": [\n {\n \"label\": \"Start now (Recommended)\",\n \"description\": \"Continue directly into /arc-build in this session.\"\n },\n {\n \"label\": \"New session\",\n \"description\": \"Print the exact /arc-build <epic-id> command for a fresh Pi session.\"\n },\n {\n \"label\": \"Done for now\",\n \"description\": \"Leave the tasks tracked in arc for future implementation.\"\n }\n ]\n }\n ]\n}\n```", ), ]) -insert_before_if_missing("skills/arc-plan/SKILL.md", "## Task Description Format", """## Parallel Readiness +replace_section( + "skills/arc-plan/SKILL.md", + "### 6.5. Self-Review\n\n", + "\n### 7. Choose Execution Path", + "", +) + +replace_section( + "skills/arc-plan/SKILL.md", + "Example skeleton:\n\n", + "\n## Rules", + """Example skeleton: + +```markdown +## Summary +Upgrade the staging `payments` Helm release with a staged, reversible rollout. + +## Target +Cluster context: `arn:aws:eks:us-east-1:123456789012:cluster/staging-eks`; namespace: `payments`; release: `payments`. + +## Files +- Modify: `deploy/values-staging.yaml` + +## Safeguards +- `kubectl config current-context` must equal the target context above. +- `PREV_REV=$(helm history payments -n payments -o json | jq -r 'map(.revision) | max')` +- `helm get values payments -n payments -o yaml > /tmp/payments-values-before.yaml` + +## Steps +1. Preview: `helm diff upgrade payments ./deploy/payments -n payments -f deploy/values-staging.yaml` and confirm only the intended image/config changes appear. +2. Apply with rollback-on-failure: `helm upgrade payments ./deploy/payments -n payments -f deploy/values-staging.yaml --atomic --timeout 10m`. +3. Observe: `kubectl rollout status deployment/payments -n payments --timeout=10m`. + +## Verification +- `helm diff upgrade payments ./deploy/payments -n payments -f deploy/values-staging.yaml` → empty diff. +- `kubectl get deployment payments -n payments -o jsonpath='{.status.readyReplicas}/{.status.replicas}'` → equal counts. +- `kubectl get pods -n payments` → no `CrashLoopBackOff` or `ImagePullBackOff`. + +## Rollback +`helm rollback payments "$PREV_REV" -n payments --wait --timeout 10m`; verify rollout and pod health again. + +## Expected Outcome +The staging release converges to the intended chart values, all replicas are Ready, the post-apply diff is empty, and the recorded prior revision remains available for rollback. +``` + +The task must contain concrete target values and executable commands like this example. If the provider has no preview/change-set mechanism or the recovery path cannot be verified, stop for explicit authorization rather than weakening the dry-run/rollback law. +""", +) + +insert_before_if_missing("skills/arc-plan/SKILL.md", "\n## Task Description Format\n", """## Parallel Readiness When a design can split into parallel implementation batches, document the readiness proof before handing off tasks. @@ -902,7 +1064,7 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st ### Parallel Batch Manifest -Group only disjoint tasks into parallel batches after file ownership is settled. +Group only disjoint tasks into parallel batches after file ownership is settled. Never place a `devops` task or other live-system mutation in a parallel batch; those tasks must remain sequential. | Batch | Prerequisites | Tasks | Independence proof | Validation | |---|---|---|---|---| @@ -927,7 +1089,7 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st - Arc code-reviewer should be auto-materialized; if it is missing, first run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })` - Fallback only if `pi-subagents` is not installed or cannot load after deprecated repair: `arc_agent(agent="code-reviewer", task="<filled prompt>")` -**Model tier:** Follow the Model Selection table in `../arc-build/SKILL.md`. Model profile: reviews use the `codeReviewer` profile when configured via `/arc-models`; otherwise they fall back to existing tier/frontmatter behavior. Escalate only for large, cross-layer, or security-sensitive diffs. For most reviews, omit `model:` (use the agent's `standard` default). For `pi-subagents`, pass the configured concrete large model only when escalating. +**Model tier:** Follow the Model Selection table in `../arc-build/SKILL.md`. Reviews use the `codeReviewer` profile when configured via `/arc-models`; otherwise the agent's `large` frontmatter is the fallback. Omit `model:` so the configured profile remains authoritative. Use an explicit override only for deliberate escalation beyond the configured profile. """) insert_before_if_missing( @@ -947,9 +1109,64 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st ), ]) +patch_file("skills/arc-build/references/devops-patterns.md", [ + ( + "| GATE (idempotency) | re-run `--dry-run` → expect no changes |", + "| GATE (idempotency) | run `helm diff upgrade <release> <chart> -f values.yaml` and expect an empty diff; if the plugin is unavailable, compare `helm template` output with `helm get manifest` |", + ), + ( + "For raw CLI mutations with no dry-run, describe the current state first so you have a before/after.", + "If a raw CLI mutation has no provider-supported preview or change set, STOP and require explicit authorization plus a verified recovery procedure; describing current state alone is not a safe preview.", + ), +]) + +patch_file("skills/arc-build/devops-builder-prompt.md", [ + ( + "For tool-specific dry-run / verify / rollback command idioms, Read:", + "For tool-specific dry-run / verify / rollback command idioms, use `read` on:", + ), +]) + +patch_file("agents/devops-builder.md", [ + ( + "if its path was provided in your dispatch prompt, e.g. `Read` `skills/arc-build/references/devops-patterns.md`.", + "if its path was provided in your dispatch prompt, using `read` on `skills/arc-build/references/devops-patterns.md`.", + ), +]) + +replace_section("agents/evaluator.md", "## Sandbox Model\n\n", "\n## Information Asymmetry", """## Sandbox Model + +The preferred `pi-subagents` dispatch runs in a disposable git worktree. In that mode you may write acceptance tests, add temporary test dependencies, and modify build configuration; do not commit. + +The bundled `arc_agent` fallback runs in the main checkout. In fallback mode: + +1. Record `git status --short` before touching files. If it is not clean, report `BLOCKED` instead of risking unrelated work. +2. Track every file you create or modify. +3. Run the evaluation. +4. Restore modified tracked files and remove only the temporary files you created. +5. Verify `git status --short` exactly matches the clean baseline before returning. + +Never claim cleanup is unnecessary unless runtime instructions explicitly confirm a disposable worktree. Never commit evaluation artifacts. +""") + +patch_file("agents/evaluator.md", [ + ( + "Report your findings to the dispatching agent. Do NOT commit or clean up — the worktree is discarded automatically.", + "Report your findings to the dispatching agent. Do not commit. In a disposable worktree, runtime cleanup handles artifacts; in the `arc_agent` fallback, complete the tracked-file restoration and temporary-file cleanup from the Sandbox Model before reporting.", + ), +]) + +patch_file("agents/code-reviewer.md", [ + ( + "Read the project's CLAUDE.md if it exists.", + "Read the project's AGENTS.md (or legacy CLAUDE.md) if it exists.", + ), +]) + SUPERVISOR_SECTIONS = { "agents/builder.md": ("## When Tests Can't Run", "implementation plan"), "agents/code-reviewer.md": ("## Rules", "review plan"), + "agents/devops-builder.md": ("## When Verification Can't Run", "operations plan"), "agents/doc-writer.md": ("## Quality Checklist", "documentation plan"), "agents/evaluator.md": ("## Rationalizations You Must Reject", "evaluation plan"), "agents/issue-manager.md": ("## Output Format", "issue plan"), @@ -957,7 +1174,7 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st } for rel, (marker, plan_phrase) in SUPERVISOR_SECTIONS.items(): extra = "Preserve adversarial/read-only expectations and" if rel == "agents/evaluator.md" else "Preserve read-only behavior and" if rel in {"agents/code-reviewer.md", "agents/spec-reviewer.md"} else "" - if rel in {"agents/builder.md", "agents/doc-writer.md", "agents/issue-manager.md"}: + if rel in {"agents/builder.md", "agents/devops-builder.md", "agents/doc-writer.md", "agents/issue-manager.md"}: routine = "Do not send routine completion handoffs through intercom; return your final task result normally." else: routine = f"{extra} do not send routine completion handoffs through intercom; return your final {'evaluation result' if rel == 'agents/evaluator.md' else 'review result'} normally." @@ -984,42 +1201,55 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st replace_section("agents/issue-manager.md", "## Processing Task Manifests\n\n", "\n## Bulk Operations", """## Processing Task Manifests -When receiving a structured manifest from the `plan` or `brainstorm` skills, parse the `## Epic` and `## Tasks` sections to assemble the manifest, then process it in phases: +When receiving a manifest from the `plan` or `brainstorm` skills, parse titles, metadata, labels, dependencies, and canonical description-file paths. Arc normalizes outer whitespace from `--stdin`; the planner has already canonicalized these files to match. Never summarize, trim, paraphrase, or retype their content. Transfer canonical bytes only with shell redirection. + +Process every manifest in these phases: 1. **Create the epic first** and capture the epic ID. -2. **Create all child tasks** with the epic as parent before applying dependencies. ```bash - arc create "Task title" --type=task --parent=<epic-id> --stdin <<'EOF' - Full multi-line description here. - EOF + arc create "Epic title" --type=epic --stdin < "/path/to/plan.md" + ``` +2. **Create all child tasks** with the epic as parent before applying dependencies. Create them in manifest order; do not claim concurrent Arc writes are safe. + ```bash + arc create "Task title" --type=task --parent=<epic-id> --stdin < "/path/to/T1.md" ``` -3. **Capture the complete task-name-to-ID table**. +3. **Capture the complete task-name-to-ID table** before any dependency command. 4. **Apply dependencies only after all child IDs exist**. ```bash arc dep add <real-later-id> <real-earlier-id> --type=blocks ``` -5. **Apply labels after dependencies**, or in the same post-creation phase. +5. **Apply labels after dependencies** using the CLI's repeatable update flag. ```bash - # Labels are managed via the REST API (no CLI command exists) - # Use arc update to add label context in the description, or - # note the labels in the summary for the dispatcher to handle + arc update <id> --label-add=docs-only + arc update <id> --label-add=devops ``` -6. **Return the final ID table, dependency summary, and `## Timing` summary**. - -Print `[arc-issue-manager] phase=<name> status=start|done elapsed_ms=<n>` progress lines around each phase (`epic`, `child_tasks`, `dependencies`, `labels`, and optional `verification`) so long-running issue creation is observable. +6. **Verify every stored description equals its canonical file** and return the final ID table, dependency summary, label summary, and `## Timing` section. + ```bash + wc -l < "/path/to/T1.md" + arc show <id> --json | jq -r .description | wc -l + ``` + Compare `sha256sum < "/path/to/T1.md"` with `arc show <id> --json | jq -j .description | sha256sum`; the hashes must match. Line counts are diagnostic only. On mismatch, repair mechanically with `arc update <id> --stdin < "/path/to/T1.md"` and re-check. -**Concurrency note:** Concurrent child-task creation is future work pending Arc CLI/server concurrency verification. Do not claim true parallel CLI issue creation is safe today. +Print `[arc-issue-manager] phase=<name> status=start|done elapsed_ms=<n>` around `epic`, `child_tasks`, `dependencies`, `labels`, and `verification`. Include all phase values in `## Timing`; use `unknown` only when a timestamp cannot be captured. **Handling partial failures**: If a task creation fails mid-manifest: - Continue creating the remaining tasks in order — do not abort the manifest - Report partial results clearly: "Created 4/5 tasks. T3 failed: `<error message>`" -- Include the ID mapping for all successfully created tasks so the dispatcher can act on what exists -- Do not attempt to clean up already-created tasks — the dispatcher will decide +- Include the ID mapping for all successfully created tasks +- Do not clean up already-created tasks; the dispatcher decides recovery This is the primary interface used by the `plan` and `brainstorm` skills for bulk issue creation. """) patch_file("agents/issue-manager.md", [ + ( + "# With description from a file (preferred for long content — byte-exact):", + "# With a canonical description file (preferred for long content and lossless internal content):", + ), + ( + "# Replace description from a file (preferred for long content — byte-exact):", + "# Replace description from a canonical file (preserves all internal content):", + ), ( "- Summarize any errors encountered\n- Provide next steps if applicable", "- Summarize any errors encountered\n- Include a `## Timing` section with phase-level elapsed times for bulk operations when available\n- Provide next steps if applicable", @@ -1027,8 +1257,38 @@ def insert_before_if_missing(rel: str, marker: str, insertion: str, sentinel: st ]) +def install_generated_resources() -> None: + backup_root = Path(tempfile.mkdtemp(prefix=".pi-arc-backup-", dir=REPO_ROOT.parent)) + moved_old: list[str] = [] + installed: list[str] = [] + try: + for name in ("prompts", "skills", "agents"): + target = REPO_ROOT / name + backup = backup_root / name + staged = ARC_ROOT / name + if target.exists(): + target.rename(backup) + moved_old.append(name) + staged.rename(target) + installed.append(name) + except Exception: + for name in reversed(installed): + target = REPO_ROOT / name + if target.exists(): + shutil.rmtree(target) + for name in reversed(moved_old): + backup = backup_root / name + if backup.exists(): + backup.rename(REPO_ROOT / name) + raise + finally: + shutil.rmtree(backup_root, ignore_errors=True) + + +install_generated_resources() + print(f"Migrated arc plugin resources from {SRC}") -print(f"Package root: {ARC_ROOT}") -print(f"Prompts: {len(list((ARC_ROOT / 'prompts').glob('*.md')))}") -print(f"Skills: {len(list((ARC_ROOT / 'skills').glob('*/SKILL.md')))}") -print(f"Agents: {len(list((ARC_ROOT / 'agents').glob('*.md')))}") +print(f"Package root: {REPO_ROOT}") +print(f"Prompts: {len(list((REPO_ROOT / 'prompts').glob('*.md')))}") +print(f"Skills: {len(list((REPO_ROOT / 'skills').glob('*/SKILL.md')))}") +print(f"Agents: {len(list((REPO_ROOT / 'agents').glob('*.md')))}") diff --git a/packages/pi-arc/skills/arc-brainstorm/SKILL.md b/packages/pi-arc/skills/arc-brainstorm/SKILL.md index 68fe804..c46b8e5 100644 --- a/packages/pi-arc/skills/arc-brainstorm/SKILL.md +++ b/packages/pi-arc/skills/arc-brainstorm/SKILL.md @@ -1,6 +1,6 @@ --- name: arc-brainstorm -description: You MUST use this skill for any design exploration, architecture decision, or trade-off analysis before implementation begins — especially when the user says "brainstorm", "explore the design", "think through", "what approach should we take", or describes a feature with multiple valid strategies. This is the arc-native brainstorming skill that writes designs to docs/plans/ and registers them on one of three review surfaces (legacy `arc plan`, encrypted local `arc share`, or encrypted remote `arc share --remote`), depending on who's reviewing and whether encryption is needed. Always prefer this over generic brainstorming when the project uses arc issue tracking. +description: You MUST use this skill for any design exploration, architecture decision, or trade-off analysis before implementation begins — especially when the user says "brainstorm", "explore the design", "think through", "what approach should we take", or describes a feature with multiple valid strategies. This is the arc-native brainstorming skill that writes designs to docs/plans/ and registers them on the arc planner review surface (`arc plan`). Always prefer this over generic brainstorming when the project uses arc issue tracking. --- # Brainstorm — Design Discovery @@ -17,6 +17,17 @@ Before starting the design dialogue, perform the protected-branch check per `ski Brainstorm itself doesn't commit code, but the design doc, the planned tasks, the eventual implementation, and the final commits will all land on whatever branch you start from. Catching trunk *now* avoids "we built three hours of work and it's all on main" at finish time. If the user picks "switch to a feature branch", suggest a name based on the brief they just gave you (e.g. `feat/<topic>`). +## Presenting Choices: Text First, Then `ask_user_question` + +The `ask_user_question` UI renders only compact option chips — short labels (1-5 words) and one-line descriptions. Anything longer is cut off and the user never sees it. + +So wherever a step below says "use the `ask_user_question` tool": + +1. **Write the substance in the conversation first** — approach summaries, trade-offs, recommendations, analysis — as a normal markdown message immediately before the tool call. +2. **Then call `ask_user_question` to capture the decision only.** Keep labels short and descriptions to one line; they may reference the content above ("Approach A", "as analyzed above") instead of restating it. + +Never put information into an option label or description that appears nowhere else. If the user reads only your text message plus the option labels, they must have everything they need to decide. + ## Workflow Create a task for each step below using the bundled `todo` checklist (via `todo` tool / `/todos`). Mark each as `in_progress` when starting and `completed` when done. This creates a visible progress list in the CLI that carries forward into the plan skill. Step 5.5 gets its own task whether or not the user opts into grilling — "No, proceed" still counts as completing the step. @@ -27,7 +38,7 @@ Create a task for each step below using the bundled `todo` checklist (via `todo` - Review existing arc issues (`arc list`) - Understand what already exists and what constraints are in play -**Scope check before proceeding:** Before asking detailed clarifying questions, assess whether the request describes multiple independent subsystems (e.g., "build a platform with chat, storage, billing, and analytics"). If so, help the user decompose into sub-projects first — each sub-project gets its own brainstorm → plan → implement cycle. Don't spend questions refining details of a project that needs to be split. A decomposition sketch (what are the independent pieces, how do they relate, what order should they be built) is more valuable than a half-specified monolith. +**Scope check before proceeding:** Before asking detailed clarifying questions, assess whether the request describes multiple independent subsystems (e.g., "build a platform with chat, storage, billing, and analytics"). If so, help the user decompose into sub-projects first — each sub-project gets its own brainstorm → plan → build cycle. Don't spend questions refining details of a project that needs to be split. A decomposition sketch (what are the independent pieces, how do they relate, what order should they be built) is more valuable than a half-specified monolith. ### 2. Ask Clarifying Questions @@ -72,10 +83,11 @@ Create a task for each step below using the bundled `todo` checklist (via `todo` - Each approach: summary, trade-offs, estimated complexity - Include a recommendation with reasoning -- **Use the `ask_user_question` tool** to present approaches as structured choices +- **Present every approach in full as normal text first** — summary, trade-offs, complexity, and your recommendation all go in the conversation message, NOT inside the tool call +- **Then use the `ask_user_question` tool** to capture the choice with short labels - Apply YAGNI — remove features from all designs that aren't explicitly required -**Example `ask_user_question` usage:** +**Example — after presenting the full approach write-ups as text:** ```json { "questions": [ @@ -85,15 +97,15 @@ Create a task for each step below using the bundled `todo` checklist (via `todo` "options": [ { "label": "Approach A (Recommended)", - "description": "Best balance of scope, risk, and implementation speed for the current constraints." + "description": "Recommended for the reasons analyzed above." }, { "label": "Approach B", - "description": "Lower short-term code churn, but leaves more long-term maintenance risk." + "description": "Choose the second approach analyzed above." }, { "label": "Approach C", - "description": "Most flexible, but likely needs larger-model implementation and more review cycles." + "description": "Choose the third approach analyzed above." } ] } @@ -113,6 +125,10 @@ Create a task for each step below using the bundled `todo` checklist (via `todo` **In existing codebases:** Follow existing patterns. Where existing code has problems that affect the work (e.g., a file that's grown too large, unclear boundaries, tangled responsibilities), include targeted improvements as part of the design — the way a good developer improves code they're working in. Don't propose unrelated refactoring. Stay focused on what serves the current goal. +**Required: a `## Success Criteria` section.** Every design doc must include a `## Success Criteria` section listing observable, testable conditions that define "this feature is done and correct" — not implementation steps, but outcomes (e.g., "a request with an expired token returns 401", "the migration is reversible", "cold-start under 200ms"). This section is the single source of truth that the rest of the pipeline maps to: the planner derives each task's `## Expected Outcome` from it, the evaluator derives acceptance tests from it, and `build`'s Epic Completion Gate checks the closed work against it. Keep criteria concrete enough that a reader can mechanically decide pass/fail. If the user gave success criteria during step 2, crystallize them here; if not, propose them and get agreement. + +**For infrastructure/operations designs** (cluster upgrades, IaC/provisioning changes, pipeline work), the same rule applies but success criteria are **observable live-system states**, not unit-test assertions — e.g. "all nodes report v1.29 and `Ready`", "zero pods in `CrashLoopBackOff` for 10 min post-change", "`terraform plan` shows no drift", "p99 latency unchanged after rollout". Additionally, an ops design should state up front: the **blast radius / staging strategy** (canary, one-at-a-time, maintenance window), the **rollback strategy** (and honestly flag any irreversible steps), and the **target environments** in scope. These flow downstream: `plan` turns them into `devops`-labeled runbook tasks (with `## Verification`, `## Safeguards`, `## Rollback`), and `build` routes those to the `devops-builder` agent. Reach for this framing whenever the work is primarily operating live systems rather than writing application code. + ### 5. Identify Shared Contracts (Parallel Readiness) If the design can produce independent implementation tasks, the brainstorm output must include a `## Parallel Readiness` section **before** `/arc-plan` creates Arc issues. Use these exact subsection headings: @@ -226,8 +242,8 @@ If "Yes", run the loop: **Loop rules:** - Walk the design's decision tree **depth-first, ordered by dependency**. Resolve decisions that constrain later answers first (e.g., "what storage layer?" before "how do we serialize sessions?"). When a resolution opens new branches, recurse into them before backtracking. -- **One question per turn** via ``ask_user_question``. Mark the recommended option. When the choice is genuinely contested, offer 2-3 options; when one option is objectively dominant, a single recommendation is fine — but never rubber-stamp open questions just because you have an opinion. -- **Codebase-first rule.** Before each question, name the symbol, file, or pattern that would answer it. If you can name one, search first (Grep / Read / symbol search) and only ask when the codebase doesn't — or can't — answer. This is the single biggest difference from step 2's clarifying questions, where you don't yet have a draft to ground against. +- **One question per turn** via ``ask_user_question``. State the decision being probed and any relevant codebase findings as text before the tool call — the options carry only short labels. Mark the recommended option. When the choice is genuinely contested, offer 2-3 options; when one option is objectively dominant, a single recommendation is fine — but never rubber-stamp open questions just because you have an opinion. +- **Codebase-first rule.** Before each question, name the symbol, file, or pattern that would answer it. If you can name one, search first (`grep` / `read` / symbol search) and only ask when the codebase doesn't — or can't — answer. This is the single biggest difference from step 2's clarifying questions, where you don't yet have a draft to ground against. - **Capture resolutions in-place.** Each resolved decision is an edit to `docs/plans/<file>.md` — update the relevant section, don't maintain a separate Q&A log. The design doc is the artifact. **Stop when ANY of:** @@ -240,34 +256,26 @@ Then proceed to step 6. ### 6. Register for Review -The design doc already exists on disk from step 5.5. This step registers it for review on the surface the user picks. +The design doc already exists on disk from step 5.5. This step registers it on the arc planner for review. -Arc supports three review surfaces. They differ along two axes — *who reviews* (just you vs. teammates on other machines) and *do you want encryption + the new annotation/accept-resolve UI* (legacy planner is plain HTTP and simpler; `arc share` is encrypted and richer). Pick based on how the design will actually be reviewed, not which command you happen to remember. +The planner is a plain-HTTP comment surface served at `http://localhost:7432/planner/<id>` — a markdown render of the design with a flat comment thread. Register the design there unless the user wants to keep it as a local file and resume later. -**Use the bundled `@juicesharp/rpiv-ask-user-question` `ask_user_question` tool with the package `questions[]` schema:** +Explain the two paths in text first, **then use the `ask_user_question` tool:** ```json { "questions": [ { "header": "Review", - "question": "How would you like to review this design?", + "question": "Register this design on the planner for review?", "options": [ { - "label": "Legacy planner", - "description": "Solo plain-HTTP review at /planner/<id>; simplest, with no encryption or accept/resolve UI." - }, - { - "label": "Encrypted local", - "description": "Solo encrypted review with annotations and accept/resolve UI on this machine only." - }, - { - "label": "Encrypted remote", - "description": "Multiple reviewers can open the remote encrypted share; the author URL must stay private." + "label": "Register (Recommended)", + "description": "Create a local planner comment thread at /planner/<id>." }, { "label": "Save for later", - "description": "Keep the saved design file and stop without server registration; resume in a new session." + "description": "Keep the local design file and stop without registering it." } ] } @@ -275,49 +283,36 @@ Arc supports three review surfaces. They differ along two axes — *who reviews* } ``` -Route on the answer: +If the user picks "Save for later", stop here — no server registration; the user resumes in a new session. **That choice terminates the skill: skip steps 7 and 8.** Otherwise register the design: -| Choice | CLI to run | Marker `kind=` | URL printed | -|---|---|---|---| -| Legacy planner | `arc plan create docs/plans/<file>.md` | `legacy` | `Review at: http://localhost:7432/planner/<id>` | -| Encrypted local | `arc share create docs/plans/<file>.md` | `share-local` | `Preview URL (local-only — not reachable by others):` | -| Encrypted remote | `arc share create docs/plans/<file>.md --remote` | `share-remote` | `Author URL (keep private — open it, then use the in-page Share link button to copy a reviewer URL):` | -| Save for later | (no command) | (no marker) | n/a | +| Choice | CLI to run | URL printed | +|---|---|---| +| Register on the planner | `arc plan create --no-frontmatter docs/plans/<file>.md` | `Review at: http://localhost:7432/planner/<id>` | +| Save for later | (no command) | n/a | -**Capture the ID and write the review marker.** After the create call succeeds, prepend a single HTML-comment line to the design doc so `/arc-plan` (and any future skill that queries review state) knows which CLI to call. Today only `/arc-plan` reads it — `/arc-build` and the dispatched implementer/reviewer agents read design content from the parent epic's description, not from the share/plan CLIs — but the marker is the canonical record of which surface this doc lives on. Without it, downstream falls back to `arc share list --json | jq` which doesn't cover legacy plans. +**Capture the ID and write the review marker.** After the create call succeeds, prepend a single HTML-comment line to the design doc so `/arc-plan` (and any future skill that queries review state) knows the plan ID. Today only `/arc-plan` reads it — `/arc-build` and the dispatched implementer/reviewer agents read design content from the parent epic's description, not from the plan CLI — but the marker is the canonical record of the plan this doc is registered as. ```bash -# Run the chosen CLI and capture stdout. -OUT=$(arc share create docs/plans/2026-05-01-foo.md --remote) +# Run the create CLI and capture stdout. +OUT=$(arc plan create --no-frontmatter docs/plans/2026-05-01-foo.md) echo "$OUT" # ALWAYS print verbatim — the user needs to see the URL -# Extract the ID: -# - share-local / share-remote: the URL fragment contains /share/<id>#... -# - legacy: the first line is "Plan created: <id> (file: ..., status: ...)" -ID=$(echo "$OUT" | grep -oE '/share/[^#]+' | head -1 | sed 's|/share/||') -# For legacy, instead: ID=$(echo "$OUT" | grep -oE 'Plan created: \S+' | awk '{print $3}') - -KIND="share-remote" # legacy | share-local | share-remote (matches the chosen branch) +# Extract the ID: the first line is "Plan created: <id> (file: ..., status: ...)" +ID=$(echo "$OUT" | grep -oE 'Plan created: \S+' | awk '{print $3}') # Prepend the marker idempotently. If line 1 already starts with "<!-- arc-review:", # replace it; otherwise prepend a new line. FILE="docs/plans/2026-05-01-foo.md" if head -1 "$FILE" | grep -q '^<!-- arc-review:'; then - sed -i.bak "1s|.*|<!-- arc-review: kind=$KIND id=$ID -->|" "$FILE" && rm "$FILE.bak" + sed -i.bak "1s|.*|<!-- arc-review: id=$ID -->|" "$FILE" && rm "$FILE.bak" else - { echo "<!-- arc-review: kind=$KIND id=$ID -->"; cat "$FILE"; } > "$FILE.tmp" && mv "$FILE.tmp" "$FILE" + { echo "<!-- arc-review: id=$ID -->"; cat "$FILE"; } > "$FILE.tmp" && mv "$FILE.tmp" "$FILE" fi ``` -The marker format is fixed: `<!-- arc-review: kind=<legacy|share-local|share-remote> id=<id> -->`. Always line 1, always exactly one space between fields. - -**URL handling rules — print exactly what the CLI printed, then add a kind-specific instruction:** +The marker format is fixed: `<!-- arc-review: id=<id> -->`. Always line 1, always exactly one space between fields. -- **Legacy** — print the `Review at:` line. Tell the user this URL is local-only (their browser must reach `http://localhost:7432`). -- **Encrypted local** — print the Preview URL line. Tell the user it's not reachable from other machines; if they need a reviewer on a different machine, re-create the share with `--remote` instead. -- **Encrypted remote** — print the Author URL line. Then tell the user: *"Open this URL yourself; that's the author view. To send a reviewer link, click the **Share link** button in the page header — it strips `&t=` and copies a reviewer URL to your clipboard. Don't paste the Author URL into chat or tickets — the `&t=` token gives the recipient your edit privileges."* - -The encrypted-share CLI persists the edit_token + key into the local arc keyring (a `shares` table in `~/.arc/data.db`, served by the local arc-server — never written to disk as JSON). If a share Author URL is lost, regenerate it with `arc share show <id> --author-url`. Legacy plans don't have this — the URL is just `<base>/planner/<id>` and there are no edit tokens. +**URL handling:** Print the `Review at:` line exactly as the CLI printed it, then tell the user this URL is local-only — their browser must reach `http://localhost:7432`. The planner URL is just `<base>/planner/<id>`; there are no edit tokens or keys to manage. ### 7. Review Loop @@ -334,15 +329,15 @@ Otherwise, print the URL from step 6 again as a reminder. **Use the bundled `@ju "options": [ { "label": "Approve", - "description": "Mark the design approved and continue to routing analysis." + "description": "Approve the design and continue to routing analysis." }, { - "label": "I've finished review (pull comments now)", - "description": "Fetch accepted reviewer feedback, apply edits, update the review surface if needed, and repeat review." + "label": "Pull comments", + "description": "Read planner feedback, apply edits, re-register if needed, and repeat review." }, { "label": "Pause review", - "description": "Leave the design saved in docs/plans and resume in a future session." + "description": "Leave the design saved in docs/plans and resume later." } ] } @@ -350,26 +345,16 @@ Otherwise, print the URL from step 6 again as a reminder. **Use the bundled `@ju } ``` -Branch the CLI by the marker's `kind`: - -| kind | Approve | Pull comments | -|---|---|---| -| `legacy` | `arc plan approve <id>` | `arc plan comments <id>` (no accepted-only filter — review the thread inline) | -| `share-local` | `arc share approve <id>` | `arc share pull <id>` (accepted-only by default) | -| `share-remote` | `arc share approve <id>` | `arc share pull <id>` (accepted-only by default) | - -**Why the legacy path lacks `pull`:** legacy plan comments don't have an Accept/Resolve/Reject state — they're a flat thread. The trade-off was made when picking legacy in step 6; if the volume of comments grows, suggest re-creating the design as `share-local` so the user gets the accepted-only filter. - -**For `share-local` / `share-remote`** — only `accepted` comments flow into refinement when pulled. The author is the only one who can mark comments as `accepted` (verified by the plan's `author_name`). For `share-remote`, reviewers comment via the reviewer URL (the in-page Share link button; *not* the Author URL). +Run the matching CLI: -After a refinement pass, if the design changed materially, update the review surface to match the new content. The CLI and marker handling differ by `kind`: +| Action | CLI | +|---|---| +| Approve | `arc plan approve <id>` | +| Pull comments | `arc plan comments <id>` (flat thread — review the comments inline; there's no accepted-only filter) | -| kind | Update CLI | ID stable? | Marker action | -|---|---|---|---| -| `share-local` / `share-remote` | `arc share update <id> <plan-file>` | yes | leave marker as-is | -| `legacy` | `arc plan create <plan-file>` (no in-place update — re-creates with a new ID) | **no — new ID** | rewrite line 1 with the new ID | +Planner comments are a flat thread with no Accept/Resolve/Reject state, so read the whole thread and decide which comments to act on yourself. -For legacy, after re-creating, replace the `id=<old>` portion of line 1 with the new ID — the idempotent `sed` snippet from step 6 works as-is: set `KIND=legacy` and `ID=<new>` and the "marker already present" branch overwrites line 1. Then loop back to step 7. +After a refinement pass, if the design changed materially, re-register the updated content. The planner has no in-place update — `arc plan create --no-frontmatter <plan-file>` re-creates the plan with a **new ID**, so rewrite line 1 with the new ID. The idempotent `sed` snippet from step 6 works as-is: set `ID=<new>` and the "marker already present" branch overwrites line 1. Then loop back to step 7. ### 8. Routing Analysis & Transition @@ -405,9 +390,9 @@ Scale: Small / Medium / Large ``` **Routing rules** (use these to drive the recommendation): -- **→ arc:plan** when ANY of: 2+ work items, shared contracts exist, multiple layers crossed, migrations or breaking changes present, medium/large scale -- **→ arc:build** when ALL of: single work item, no shared contracts, single layer, no risk areas, small scale -- When borderline, recommend `arc:plan` — the overhead of planning is low, but the cost of a disorganized multi-task implementation is high +- **→ /arc-plan** when ANY of: 2+ work items, shared contracts exist, multiple layers crossed, migrations or breaking changes present, medium/large scale +- **→ /arc-build** when ALL of: single work item, no shared contracts, single layer, no risk areas, small scale +- When borderline, recommend `/arc-plan` — the overhead of planning is low, but the cost of a disorganized multi-task implementation is high After the analysis, use the **`ask_user_question` tool** — mark the recommended option: ```json @@ -437,8 +422,8 @@ After the analysis, use the **`ask_user_question` tool** — mark the recommende If `/arc-build` is recommended instead, swap which option gets the "(recommended)" tag. -- **Break into tasks**: invoke the `plan` skill, passing the review ID from the line-1 marker (the `id=…` value; whether it's a legacy plan ID or a share ID depends on `kind=…`) -- **Implement directly**: invoke the `implement` skill +- **Break into tasks**: invoke the `plan` skill, passing the review ID from the line-1 marker (the `id=…` value) +- **Implement directly**: this path skips `plan`, so no arc tasks exist yet — `build` dispatches existing arc issues and won't invent one. First canonicalize the approved design to match Arc's outer-whitespace normalization, then create one self-contained task without passing the body through the model: `TMP=$(mktemp); python3 -c 'from pathlib import Path; import sys; Path(sys.argv[2]).write_text(Path(sys.argv[1]).read_text().strip())' docs/plans/<file>.md "$TMP"; arc create "<title>" -t task --stdin < "$TMP"; rm -f "$TMP"`. Then invoke the `build` skill with that task ID. - **Done for now**: tell the user the design is approved and they can run `/arc-plan` in a new session ## Scale Detection @@ -451,9 +436,10 @@ If `/arc-build` is recommended instead, swap which option gets the "(recommended ## Rules -- The ONLY next skill after brainstorm is `plan` (or `implement` for small work) +- The ONLY next skill after brainstorm is `plan` (or `build` for small work, after creating a single task) - Never invoke implementation skills from brainstorm -- Design documents go in `docs/plans/` and are registered via one of three review surfaces (`arc plan create` for legacy, `arc share create` for encrypted local, `arc share create … --remote` for encrypted remote). The skill writes a `<!-- arc-review: kind=… id=… -->` marker as line 1 of the doc so downstream skills can route their CLI calls. +- `ask_user_question` captures decisions only — all substance goes in a normal text message first (see "Presenting Choices: Text First, Then `ask_user_question`") +- Design documents go in `docs/plans/` and are registered on the planner via `arc plan create --no-frontmatter`. The skill writes a `<!-- arc-review: id=… -->` marker as line 1 of the doc so downstream skills can find the plan ID. - Arc issues track persistent work; the bundled `todo` checklist tracks in-session workflow progress in the CLI - YAGNI: if the user didn't ask for it, don't design it - Format all arc content (descriptions, plans, comments) per `skills/arc/_formatting.md` diff --git a/packages/pi-arc/skills/arc-build/SKILL.md b/packages/pi-arc/skills/arc-build/SKILL.md index df513fd..f8cd25d 100644 --- a/packages/pi-arc/skills/arc-build/SKILL.md +++ b/packages/pi-arc/skills/arc-build/SKILL.md @@ -19,14 +19,14 @@ This catches the case where build was invoked without going through `brainstorm` ## Model Selection -Every Arc subagent dispatch can override the subagent's frontmatter model via the `model:` parameter. `modelProfiles` from `${XDG_CONFIG_HOME:-~/.config}/pi-arc/models.json` are the preferred way to choose role-specific models, and `arc.modelTiers` is a legacy fallback for older setups. Before dispatching, assess the task size/risk and choose the smallest model tier that is likely to succeed. The default floor per agent is set in frontmatter — use overrides to downgrade trivial tasks or escalate complex/high-risk tasks. +Every Arc subagent dispatch can override the subagent's frontmatter model via the `model:` parameter. `modelProfiles` from `${XDG_CONFIG_HOME:-~/.config}/pi-arc/models.json` are the preferred way to choose role-specific models, and `arc.modelTiers` is a legacy fallback for older setups. GPT-5.6 maps naturally onto Arc's roles: Luna for fast/affordable work, Terra for balanced implementation, and Sol for high-risk reasoning. The dedicated `devopsBuilder` profile uses Sol because live-system changes require blast-radius, staging, and rollback judgment. Before dispatching, assess the task size/risk and choose the smallest model tier that is likely to succeed. The default floor per agent is set in frontmatter — use overrides to downgrade trivial tasks or escalate complex/high-risk tasks. | Tier | Default concrete model | Use for | |---|---|---| -| `nano` | `openai-codex/gpt-5.4-mini` | Bulk CLI issue creation and other low-reasoning issue-manager work | -| `small` | `openai-codex/gpt-5.4-mini` | Mechanical edits and docs | -| `standard` | `openai-codex/gpt-5.3-codex` | Normal contained implementation/review | -| `large` | `openai-codex/gpt-5.5` | Cross-cutting, architectural, security-sensitive, or adversarial review | +| `nano` | `openai-codex/gpt-5.6-luna` | Bulk CLI issue creation and other low-reasoning issue-manager work | +| `small` | `openai-codex/gpt-5.6-luna` | Mechanical edits and docs | +| `standard` | `openai-codex/gpt-5.6-terra` | Normal contained implementation/review | +| `large` | `openai-codex/gpt-5.6-sol` | Cross-cutting, architectural, security-sensitive, or adversarial review | ```markdown Arc model selection resolves in this order: @@ -45,10 +45,10 @@ Legacy fallback settings can still override the tier map in `~/.pi/agent/setting { "arc": { "modelTiers": { - "nano": "openai-codex/gpt-5.4-mini", - "small": "openai-codex/gpt-5.4-mini", - "standard": "openai-codex/gpt-5.3-codex", - "large": "openai-codex/gpt-5.5" + "nano": "openai-codex/gpt-5.6-luna", + "small": "openai-codex/gpt-5.6-luna", + "standard": "openai-codex/gpt-5.6-terra", + "large": "openai-codex/gpt-5.6-sol" } } } @@ -78,9 +78,9 @@ arc_agent(agent="builder", task="...") # standard default arc_agent(agent="builder", model="large", task="...") # complex # Preferred when pi-subagents Arc agents are installed: -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.4-mini", context: "fresh", async: true, clarify: false }) -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.3-codex", context: "fresh", async: true, clarify: false }) -subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.5", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-luna", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-terra", context: "fresh", async: true, clarify: false }) +subagent({ agent: "arc-builder", task: "...", model: "openai-codex/gpt-5.6-sol", context: "fresh", async: true, clarify: false }) ``` **When unsure, omit `model:`** — the agent's frontmatter floor is calibrated for the typical case. @@ -115,7 +115,7 @@ Parallel worktree dispatch is available **only** through an installed `pi-subage `pi-subagents` worktree mode returns per-task patch files and cleans up temporary worktrees. It does **not** automatically merge changes into the main working tree. The orchestrator must inspect, apply, verify, commit, and close each patch/task explicitly. -**When NOT to use parallel**: missing `subagent` tool, missing Arc agent definitions, overlapping files, task dependencies, uncertainty about scope, or fewer than 3 implementation tasks. Default to sequential — the cost of serial execution is time; the cost of a bad parallel patch merge is data loss. +**When NOT to use parallel**: missing `subagent` tool, missing Arc agent definitions, `devops` tasks that touch live systems, overlapping files, task dependencies, uncertainty about scope, or fewer than 3 implementation tasks. Default to sequential — the cost of serial execution is time; the cost of a bad parallel patch merge is data loss. ## Orchestration Loop @@ -130,18 +130,20 @@ Inspect the plan's `Parallel Batch Manifest` first. If it yields a ready batch a - `completed` when the task is closed in arc ```bash -# Get the list of tasks to implement -arc list --parent=<epic-id> --status=open --json +# Get every unfinished child, including resumed/blocked/deferred work +arc list --parent=<epic-id> --json | jq '.[] | select(.status != "closed")' ``` +If you were handed an epic ID, use its children. If you were handed one standalone task ID from the brainstorm-direct path, use `arc ready` / `arc show <task-id>` and run the loop once. If no Arc task exists, stop and route the user to `/arc-plan`; build dispatches existing tasks and does not invent them. + Create a `todo` checklist entry for each, then work through this loop: ### 1. Find Next Task ```bash arc ready -# or for a specific epic: -arc list --parent=<epic-id> --status=open +# or for a specific epic, include resumed/blocked/deferred children: +arc list --parent=<epic-id> --json | jq '.[] | select(.status != "closed")' ``` ### 2. Claim Task @@ -158,38 +160,46 @@ Record the current HEAD before dispatching — needed for review if escalated: PRE_TASK_SHA=$(git rev-parse HEAD) ``` -Check whether the task has a `docs-only` label: +Fetch the design excerpt once for the implementer, evaluator, and code reviewer: ```bash -arc show <task-id> --json | jq -e '.labels[] | select(. == "docs-only")' > /dev/null 2>&1 +PARENT=$(arc show <task-id> --json | jq -r '.parent_id // empty') +[ -n "$PARENT" ] && arc show "$PARENT" ``` -**If `docs-only`** (exit code 0) — spawn a `doc-writer` subagent: +Extract the sections relevant to this task into `{DESIGN_EXCERPT}`. If the task has no parent epic, use `none`. -Use the template at `./doc-writer-prompt.md`. Fill placeholder `{TASK_ID}`. For docs-only work, the agent default (`small`) is correct — omit `model:` unless the docs task is unusually complex. +Check task labels with precedence `docs-only` → `devops` → `builder`: -Dispatch preference: -- If `subagent` is available and `arc-doc-writer` is installed: `subagent({ agent: "arc-doc-writer", task: "<filled prompt>", context: "fresh", async: true, clarify: false })` -- If `subagent` is available but Arc specialists are missing: Arc specialists should already be auto-materialized. First run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })`. -- Otherwise: `arc_agent(agent="doc-writer", task="<filled prompt>")` +```bash +arc show <task-id> --json | jq -e '.labels[] | select(. == "docs-only")' > /dev/null 2>&1 +arc show <task-id> --json | jq -e '.labels[] | select(. == "devops")' > /dev/null 2>&1 +``` -For async `pi-subagents` dispatches, immediately capture the returned run ID, poll with `subagent({ action: "status", id: "<run-id>" })` or watch `/subagents-status` until terminal, then read the final output before evaluating the report or moving to validation. +**If `docs-only`** — use `./doc-writer-prompt.md` and dispatch: +- Preferred: `subagent({ agent: "arc-doc-writer", task: "<filled prompt>", context: "fresh", async: true, clarify: false })` +- Fallback: `arc_agent(agent="doc-writer", task="<filled prompt>")` -**Otherwise** — spawn a `builder` subagent: +**Else if `devops`** — use `./devops-builder-prompt.md`, filling `{TASK_ID}`, `{PRE_TASK_SHA}`, `{DESIGN_EXCERPT}`, and `{MODEL_TIER_NOTE}`. The `devopsBuilder` model profile is recommended at the `large` tier because operations work has live blast radius and partial-failure modes. Dispatch: +- Preferred: `subagent({ agent: "arc-devops-builder", task: "<filled prompt>", context: "fresh", async: true, clarify: false })` +- Fallback: `arc_agent(agent="devops-builder", task="<filled prompt>")` (the configured `devopsBuilder` profile is authoritative; `large` frontmatter is the fallback) -Use the template at `./builder-prompt.md`. Fill placeholders (`{TASK_ID}`, `{PRE_TASK_SHA}`, `{DESIGN_EXCERPT}`) and apply Model Selection guidance (see `## Model Selection` above) for the dispatch `model:`. +The devops builder follows PLAN → SAFEGUARD → APPLY → VERIFY → GATE. Never route `devops` tasks through the normal TDD builder, and never include live-system operations tasks in a parallel patch batch. -Dispatch preference: -- If `subagent` is available and `arc-builder` is installed: `subagent({ agent: "arc-builder", task: "<filled prompt>", model: "<concrete-model-if-needed>", context: "fresh", async: true, clarify: false })` -- If `subagent` is available but Arc specialists are missing: Arc specialists should already be auto-materialized. First run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })`. -- Otherwise: `arc_agent(agent="builder", task="<filled prompt>", model="<tier-if-needed>")` +**Otherwise** — use `./builder-prompt.md`, filling `{TASK_ID}`, `{PRE_TASK_SHA}`, and `{DESIGN_EXCERPT}`. Dispatch: +- Preferred: `subagent({ agent: "arc-builder", task: "<filled prompt>", model: "<concrete-model-if-needed>", context: "fresh", async: true, clarify: false })` +- Fallback: `arc_agent(agent="builder", task="<filled prompt>", model="<tier-if-needed>")` + +Arc specialists should already be auto-materialized. If a required specialist is missing, first run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })`. -For async `pi-subagents` dispatches, immediately capture the returned run ID, poll with `subagent({ action: "status", id: "<run-id>" })` or watch `/subagents-status` until terminal, then read the final output before evaluating the report or moving to validation. +For async `pi-subagents` dispatches, capture the returned run ID, poll with `subagent({ action: "status", id: "<run-id>" })` or watch `/subagents-status` until terminal, and read the final output before validation. ### 4. Evaluate Result When the subagent reports back, check its **Status** (one of `DONE | DONE_WITH_CONCERNS | BLOCKED | NEEDS_CONTEXT`) and **Gate Results**. Follow the `## Handle Implementer Status` table below for the status-specific action. In all cases, run the project test command fresh yourself — do NOT trust the subagent's report alone. +> **DevOps tasks** (from `devops-builder`): there may be no project test command — the artifact is the live system's state. Verify by re-running the task's `## Verification` commands yourself and confirming each asserts the desired state, then confirm the report's **Rollback path** names a backup/revision that actually exists. Treat a missing or unverifiable rollback path the same as a failing test: do not proceed to review until it's resolved. + **On `DONE`:** - Run the project tests. If they pass → proceed to step 5 (Spec Compliance Review). - If tests fail despite a `DONE` report, treat as `BLOCKED`: re-dispatch with the failure output. @@ -228,10 +238,10 @@ BASE_SHA=$PRE_TASK_SHA Dispatch `spec-reviewer`: -Use the template at `./spec-reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}`, `{HEAD_SHA}`). Spec review is a focused comparison task — the Arc `standard` tier is appropriate unless the spec is unusually large or ambiguous. +Use the template at `./spec-reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}`, `{HEAD_SHA}`). The configured `specReviewer` profile is authoritative; the agent's `large` frontmatter is the fallback. Dispatch preference: -- If `subagent` is available and `arc-spec-reviewer` is installed: `subagent({ agent: "arc-spec-reviewer", task: "<filled prompt>", model: "openai-codex/gpt-5.3-codex", context: "fresh", async: true, clarify: false })` +- If `subagent` is available and `arc-spec-reviewer` is installed: `subagent({ agent: "arc-spec-reviewer", task: "<filled prompt>", context: "fresh", async: true, clarify: false })` - If `subagent` is available but Arc specialists are missing: Arc specialists should already be auto-materialized. First run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })`. - Otherwise: `arc_agent(agent="spec-reviewer", task="<filled prompt>")` @@ -248,6 +258,8 @@ Handle results: > **Docs-only tasks**: Skip this step. The spec-reviewer is designed around code verification (file lists, function signatures, test coverage) and doesn't apply to documentation. For docs-only tasks, the orchestrator verifies formatting/completeness directly: check that all files in `## Files` were created/modified, links resolve, heading hierarchy is correct, code blocks have language tags. +> **DevOps tasks**: If the task committed IaC/config/manifests (non-empty `$PRE_TASK_SHA..HEAD` diff), dispatch the `spec-reviewer` as normal — it compares the committed diff to the spec. If the change was **imperative-only** (empty diff), there's nothing for a diff-based reviewer to read; instead the orchestrator verifies spec compliance directly: for each step in `## Steps` and each assertion in `## Verification`, confirm the report's verification evidence shows it was done against the live system. Flag any spec step with no corresponding evidence as a gap and re-dispatch. + ### 6. Code Quality Review Only dispatched after spec compliance passes. Use the `review` skill or dispatch `code-reviewer` directly: @@ -256,7 +268,7 @@ Only dispatched after spec compliance passes. Use the `review` skill or dispatch HEAD_SHA=$(git rev-parse HEAD) ``` -Use the template at `../arc-review/reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}` = PRE_TASK_SHA recorded earlier, `{HEAD_SHA}` = current HEAD, `{DESIGN_EXCERPT}` from parent epic or "none", `{EVALUATOR_STATUS}` = "active" if evaluator was dispatched, else "not dispatched"). Follow Model Selection above for the dispatch `model:` — `standard` default is appropriate for most reviews. +Use the template at `../arc-review/code-reviewer-prompt.md`. Fill placeholders (`{TASK_ID}`, `{BASE_SHA}` = PRE_TASK_SHA recorded earlier, `{HEAD_SHA}` = current HEAD, `{DESIGN_EXCERPT}` from parent epic or "none" — fetch it per step 3's design-context block, `{EVALUATOR_STATUS}` = "active" if evaluator was dispatched, else "not dispatched"). Follow Model Selection above for the dispatch `model:` — the configured `codeReviewer` profile is authoritative and `large` frontmatter is the fallback. **On `{EVALUATOR_STATUS}`:** Decide whether to dispatch the evaluator (step 6.5) BEFORE filling this placeholder. If you plan to run step 6.5 in parallel with step 6, set `{EVALUATOR_STATUS}="active"`. Otherwise set `"not dispatched"`. Step 6.5 has the decision criteria for when to dispatch the evaluator. @@ -273,6 +285,8 @@ Circuit breaker: 3 review/fix cycles on the same finding → escalate to user. > **Docs-only tasks**: Skip code quality review. For substantial documentation changes (developer-facing API docs, architecture docs), optionally dispatch `code-reviewer` for a quality check. +> **DevOps tasks**: When the task committed IaC/config/manifests, dispatch `code-reviewer` — review it through an ops lens: no hardcoded secrets or credentials, least-privilege (IAM/RBAC), pinned versions/digests, idempotency, and resource limits where applicable. When the change was imperative-only (no diff), skip `code-reviewer` and instead confirm the `devops-builder`'s gate evidence directly: idempotency (a clean second dry-run), no leftover debris (uncordoned nodes, no debug pods/port-forwards), and a reachable rollback path. + ### 6.5. High-Risk Evaluation (Optional) The evaluator is **not dispatched by default**. Dispatch only when: @@ -284,7 +298,7 @@ When `pi-subagents` is available, dispatch the evaluator through a one-task work ```ts subagent({ tasks: [ - { agent: "arc-evaluator", task: "<filled evaluator prompt>", model: "openai-codex/gpt-5.5" } + { agent: "arc-evaluator", task: "<filled evaluator prompt>" } ], worktree: true, concurrency: 1, @@ -294,13 +308,13 @@ subagent({ }) ``` -If `pi-subagents` or `arc-evaluator` is not available, fall back to sequential `arc_agent(agent="evaluator", model="large", task="<filled evaluator prompt>")` and ensure the evaluator does not leave uncommitted artifacts in the main worktree. +If `pi-subagents` or `arc-evaluator` is not available, fall back to sequential `arc_agent(agent="evaluator", task="<filled evaluator prompt>")`. The configured `evaluator` profile remains authoritative and the agent's `large` frontmatter is the fallback. Because this runs in the main checkout, require the evaluator to remove every temporary test, dependency, and build-file edit and verify `git status --short` matches its pre-evaluation baseline before returning. ```bash PARENT=$(arc show <task-id> --json | jq -r '.parent_id // empty') ``` -Use the template at `./evaluator-prompt.md`. Fill placeholder `{TASK_ID}`. Because evaluation is adversarial verification on high-risk tasks, escalate one tier from the agent default (typically to `large`) — set `model: "large"` on `arc_agent` dispatches unless the task is narrow. For `pi-subagents`, pass the concrete configured large model. +Use the template at `./evaluator-prompt.md`. Fill `{TASK_ID}` and `{DESIGN_EXCERPT}` from the parent epic fetched above; use `none` only when there is no parent design. Because evaluation is adversarial verification on high-risk tasks, use the `evaluator` model profile when configured or the `large` tier fallback. When you plan to run the evaluator, set the code quality reviewer's `## Evaluator Status` to `active`; otherwise set it to `not dispatched`. @@ -324,10 +338,10 @@ arc close <task-id> -r "Implemented: <summary>" ### 8. Integration Checkpoint -After closing 2-3 related tasks, or before switching to a new epic phase, run the full integration test suite: +After closing 2-3 related tasks, or before switching to a new epic phase, run the full integration test suite. Use the project's integration test command — check the design's `## Test Command`, the project `CLAUDE.md`/`AGENTS.md`, or the `Makefile`/`package.json` for the real target (e.g., `make test-integration`, `npm run test:integration`, `go test -tags=integration ./...`): ```bash -make test-integration +make test-integration # example — substitute the project's actual command ``` This catches cross-task regressions that individual implementer gate checks won't — each implementer only validates its own task's scope. Do not wait until all tasks are complete to discover integration failures. @@ -341,9 +355,21 @@ If integration tests fail: Go to step 1 for the next task. Continue until all tasks in the epic are closed. +### 10. Completion Gate + +For a standalone task, verify its task-specific command (or live `## Verification` for DevOps), confirm it is closed, skip all epic-only commands, and hand off to `finish`. + +For an epic, closing the last selected task is not the same as the epic being done. Before declaring the build complete, verify the epic as a whole — per-task gates only validate each task's own scope: + +1. **All tasks closed:** `arc list --parent=<epic-id> --json | jq '[.[] | select(.status != "closed")] | length'` returns `0`. Any `open`, `in_progress`, `blocked`, or `deferred` child keeps the epic open. +2. **Epic-wide verification:** for code/docs epics, run the project's full test command and confirm exit 0. For DevOps-only epics, re-run each task's live `## Verification` and confirm rollback evidence; for mixed epics, run both. +3. **Success criteria met:** re-read the epic description's `## Success Criteria` section (carried from the design). Confirm each criterion is satisfied by the closed tasks. If a criterion has no implementing task, that's a planning gap — surface it to the user rather than closing the epic. +4. **Close the epic** with a summary: `arc close <epic-id> -r "Implemented: <one-line summary of what shipped>"`. +5. **Hand off to `finish`:** the build skill does not commit/push the final state or run the session-close protocol. Invoke the `finish` skill to capture remaining work, run quality gates, and push. Work is not done until `git push` succeeds. + ## Handle Implementer Status -Every `builder` and `doc-writer` dispatch returns one of four terminal statuses. Handle each explicitly: +Every `builder`, `devops-builder`, and `doc-writer` dispatch returns one of four terminal statuses. Handle each explicitly: | Status | Orchestrator action | |---|---| @@ -388,6 +414,7 @@ arc show <task-id> ``` Confirm: +- No task has a `devops` label or any live-system mutation scope; those tasks are always sequential - No `blocks`/`blockedBy` relationships between tasks in this batch - No overlapping file paths in task descriptions - Each task has a clearly scoped, non-ambiguous specification @@ -402,9 +429,9 @@ Dispatch all parallel tasks in one `subagent` tool call so they branch from the ```ts subagent({ tasks: [ - { agent: "arc-builder", task: "<filled builder prompt for task 1>", model: "openai-codex/gpt-5.3-codex" }, - { agent: "arc-builder", task: "<filled builder prompt for task 2>", model: "openai-codex/gpt-5.3-codex" }, - { agent: "arc-doc-writer", task: "<filled doc-writer prompt for task 3>", model: "openai-codex/gpt-5.4-mini" } + { agent: "arc-builder", task: "<filled builder prompt for task 1>" }, + { agent: "arc-builder", task: "<filled builder prompt for task 2>" }, + { agent: "arc-doc-writer", task: "<filled doc-writer prompt for task 3>" } ], worktree: true, concurrency: 3, diff --git a/packages/pi-arc/skills/arc-build/builder-prompt.md b/packages/pi-arc/skills/arc-build/builder-prompt.md index 982285c..bc8d99c 100644 --- a/packages/pi-arc/skills/arc-build/builder-prompt.md +++ b/packages/pi-arc/skills/arc-build/builder-prompt.md @@ -21,6 +21,10 @@ You are implementing arc task {TASK_ID}. ## Pre-Task SHA {PRE_TASK_SHA} +## Definition of Done + +You are done when **every item in the task's `## Expected Outcome` works** and the task's `## Test Command` exits 0 with zero failures. That is the target — the steps below are how you reach it, and the gate checks in your agent instructions are how you confirm it before reporting `DONE`. + ## Your Job 1. Read the task spec end-to-end before writing code diff --git a/packages/pi-arc/skills/arc-build/devops-builder-prompt.md b/packages/pi-arc/skills/arc-build/devops-builder-prompt.md new file mode 100644 index 0000000..45085e0 --- /dev/null +++ b/packages/pi-arc/skills/arc-build/devops-builder-prompt.md @@ -0,0 +1,70 @@ +# DevOps Builder Prompt Template + +Use this template when dispatching `devops-builder` for a task labeled `devops`. + +**Placeholders:** +- `{TASK_ID}` — arc issue ID (e.g., `task.abc123`) +- `{PRE_TASK_SHA}` — git SHA before this task starts (recorded by orchestrator; relevant only if the task commits IaC) +- `{DESIGN_EXCERPT}` — relevant design section from parent epic, or "none" +- `{MODEL_TIER_NOTE}` — optional hint about expected blast radius / complexity + +````text +You are executing arc devops task {TASK_ID}. + +## Task Spec +<paste output of: arc show {TASK_ID}> + +## Design Context +{DESIGN_EXCERPT} +(Omit this section if no parent epic design applies.) + +## Pre-Task SHA +{PRE_TASK_SHA} + +## Patterns Reference +For tool-specific dry-run / verify / rollback command idioms, use `read` on: +`skills/arc-build/references/devops-patterns.md` +(Adapt to the task's actual tooling — it's a cheatsheet, not a mandate.) + +## Definition of Done + +You are done when **every assertion in the task's `## Verification` works against the live +system** and the system is in the desired state described by `## Expected Outcome`, with a +confirmed-reachable rollback path. That is the target — the PLAN → SAFEGUARD → APPLY → VERIFY → +GATE loop in your agent instructions is how you reach it safely. + +## Your Job + +1. Read the task spec end-to-end before touching anything. +2. Confirm you are pointed at the authorized target (context/workspace/account). If the task is + ambiguous about which environment, STOP and report `NEEDS_CONTEXT`. +3. PLAN: dry-run / diff the change and confirm it matches intent. If the preview shows anything + you didn't expect, report `NEEDS_CONTEXT` with the diff — do not apply what you don't understand. +4. SAFEGUARD: back up current state and write the exact rollback procedure before mutating. + If the task lacks a `## Rollback` path and you can't establish one, report `BLOCKED`. +5. APPLY: execute staged (canary / one-at-a-time) where possible — never big-bang production. +6. VERIFY: assert the live desired state with pasted command output. +7. GATE: run all 5 gate checks (spec, idempotency, rollback readiness, no debris, verify re-run). +8. Commit any IaC/config/manifest changes with a conventional commit message. Imperative-only + changes have nothing to commit — say so. + +## Honor the Iron Law + +NO MUTATION WITHOUT A DRY-RUN PREVIEW AND A ROLLBACK PATH. If either is missing and you cannot +establish it, STOP and report `NEEDS_CONTEXT` or `BLOCKED` — never "just run it and see." + +## Report Format + +Report back with one of: `DONE` | `DONE_WITH_CONCERNS` | `BLOCKED` | `NEEDS_CONTEXT`. + +Include: +1. Status +2. Summary (one paragraph) and resulting system state +3. Target environment acted on +4. Plan preview (the dry-run/diff, summarized) and that it matched intent +5. Files changed (committed IaC) or "none — imperative change" +6. Verification evidence (commands run + actual output, pass/fail per assertion) +7. Rollback path (exact command(s) + confirmation the backup/revision exists) +8. Gate Results (per-check PASS/FAIL/NOT RUN) +9. Concerns / Blockers / Missing context (non-DONE only) +```` diff --git a/packages/pi-arc/skills/arc-build/evaluator-prompt.md b/packages/pi-arc/skills/arc-build/evaluator-prompt.md index 6b4cd3f..d8d0cb2 100644 --- a/packages/pi-arc/skills/arc-build/evaluator-prompt.md +++ b/packages/pi-arc/skills/arc-build/evaluator-prompt.md @@ -4,6 +4,7 @@ Use this template when dispatching `evaluator` for adversarial verification of a **Placeholders:** - `{TASK_ID}` — arc issue ID +- `{DESIGN_EXCERPT}` — relevant design section from the parent epic, or "none" if no parent design applies ````text You are the adversarial evaluator for arc task {TASK_ID}. @@ -11,6 +12,10 @@ You are the adversarial evaluator for arc task {TASK_ID}. ## Task Spec <paste output of: arc show {TASK_ID}> +## Design Context +{DESIGN_EXCERPT} +(The approved design — your source of spec *intent*. If "none", omit this section and derive intent from the task spec alone.) + ## Your Job You have NOT seen the diff or the implementer's tests. Your job is to: diff --git a/packages/pi-arc/skills/arc-build/references/devops-patterns.md b/packages/pi-arc/skills/arc-build/references/devops-patterns.md new file mode 100644 index 0000000..4cb8e10 --- /dev/null +++ b/packages/pi-arc/skills/arc-build/references/devops-patterns.md @@ -0,0 +1,115 @@ +# DevOps Patterns — Dry-Run, Verify, Rollback Idioms + +A cheatsheet for the `devops-builder` agent's PLAN → SAFEGUARD → APPLY → VERIFY → GATE loop. +Map each phase to the task's actual tooling. These are idioms, not a mandate — adapt to the +project's conventions, versions, and wrappers (Makefiles, `task`, `just`, CI jobs). + +The columns deliberately line up with the agent's loop: + +| Phase | What you're proving | +|-------|---------------------| +| PLAN | "Here is exactly what will change" (no surprises) | +| SAFEGUARD | "Here is how I undo it" (rollback exists) | +| APPLY | "Change it, but staged" (bounded blast radius) | +| VERIFY | "It reached the desired state" (live assertion) | +| GATE | "A second preview is clean" (idempotent + reversible) | + +## Kubernetes (kubectl) + +| Phase | Command idiom | +|-------|---------------| +| Confirm target | `kubectl config current-context`, `kubectl config view --minify -o jsonpath='{..namespace}'` | +| PLAN | `kubectl diff -f manifest.yaml` (shows server-side diff before apply) | +| SAFEGUARD | `kubectl get <kind> <name> -n <ns> -o yaml > backup-<name>.yaml`; record image/replicas | +| APPLY (staged) | drain one node at a time: `kubectl cordon <node>` → `kubectl drain <node> --ignore-daemonsets --delete-emptydir-data` → upgrade → `kubectl uncordon <node>`. For Deployments, rely on `RollingUpdate` `maxUnavailable`/`maxSurge` and watch. | +| VERIFY | `kubectl get nodes -o wide` (version + `Ready`); `kubectl rollout status deploy/<name> -n <ns>`; `kubectl get pods -n <ns>` (no `CrashLoopBackOff`/`ImagePullBackOff`); `kubectl get events --sort-by=.lastTimestamp` | +| GATE (idempotency) | `kubectl diff -f manifest.yaml` again → expect empty | +| ROLLBACK | `kubectl rollout undo deploy/<name> -n <ns>`; or `kubectl apply -f backup-<name>.yaml`; re-`uncordon` any nodes left cordoned | + +**Cluster/control-plane upgrades** (kubeadm/EKS/GKE/AKS): upgrade control plane first, then node pools one at a time; never upgrade the data plane ahead of the control plane; respect the version-skew policy (kubelet may trail the API server by N minor versions but never lead it). Verify with `kubectl version` and node `KubeletVersion` before declaring done. + +## Helm + +| Phase | Command idiom | +|-------|---------------| +| PLAN | `helm upgrade <release> <chart> --dry-run --debug -f values.yaml` (and `helm diff upgrade …` if the diff plugin is installed) | +| SAFEGUARD | `helm history <release>` — record the current revision number to roll back to; `helm get values <release> > backup-values.yaml` | +| APPLY | `helm upgrade <release> <chart> -f values.yaml --atomic --timeout 5m` (`--atomic` auto-rolls-back a failed upgrade) | +| VERIFY | `helm status <release>`; `helm test <release>` if tests are defined; then the kubectl health checks above | +| GATE (idempotency) | run `helm diff upgrade <release> <chart> -f values.yaml` and expect an empty diff; if the plugin is unavailable, compare `helm template` output with `helm get manifest` | +| ROLLBACK | `helm rollback <release> <previous-revision>` | + +## Terraform / OpenTofu + +| Phase | Command idiom | +|-------|---------------| +| Confirm target | `terraform workspace show`; confirm the backend/account in the active credentials | +| PLAN | `terraform plan -out=tfplan` — **read it**; confirm the create/update/**destroy** counts match intent. An unexpected `destroy` is a STOP condition. | +| SAFEGUARD | back up state: `terraform state pull > backup.tfstate`; for remote state, confirm versioning is on | +| APPLY | `terraform apply tfplan` (apply the saved plan, not a fresh one — guarantees you apply exactly what you reviewed) | +| VERIFY | `terraform plan` → expect "No changes"; check the real resources via the cloud CLI/provider | +| GATE (idempotency) | the post-apply `terraform plan` showing **no drift** IS the idempotency check | +| ROLLBACK | re-apply the prior config/state (`git revert` the IaC change then `apply`), or `terraform state push backup.tfstate` for state-level recovery. Note: not all resource changes are cleanly reversible — flag destroys in SAFEGUARD. | + +## Ansible + +| Phase | Command idiom | +|-------|---------------| +| PLAN | `ansible-playbook play.yml --check --diff` (check mode = dry run; `--diff` shows file/template changes) | +| SAFEGUARD | back up changed files/configs on targets; note current package versions / service states | +| APPLY (staged) | `--limit <canary-host>` first, verify, then widen; use `serial:` in the play for batched rollout | +| VERIFY | re-run with `--check --diff` → expect no changes (idempotent); plus task-specific health checks | +| GATE (idempotency) | a second real run reports `changed=0` — Ansible's built-in convergence signal | +| ROLLBACK | a reverse playbook, or restore the backed-up configs and restart services | + +## Cloud CLIs (AWS / GCP / Azure) + +- **Confirm target first:** `aws sts get-caller-identity`, `gcloud config list`, `az account show`. Wrong-account is the highest-cost mistake. +- **PLAN:** prefer the provider's preview — CloudFormation `change-sets`, `gcloud … --dry-run` where supported, `az deployment … what-if`. If a raw CLI mutation has no provider-supported preview or change set, STOP and require explicit authorization plus a verified recovery procedure; describing current state alone is not a safe preview. +- **SAFEGUARD:** snapshot before destructive changes (EBS/RDS snapshots, disk images); record current values of anything you're changing. +- **VERIFY:** `describe`/`get` the resource and assert the new state; check it's not just created but healthy/available. +- **ROLLBACK:** restore from snapshot, or re-apply prior config. Many cloud deletes are irreversible — those are STOP-and-confirm conditions, not autonomous actions. + +## Database migrations + +The other big "can't unit-test, must verify live, must be reversible" class. The cardinal rule: **never couple a schema change to the app deploy that needs it.** Use **expand/contract** (a.k.a. parallel change) so the schema is compatible with both the old and new app versions at every step — that's what makes the rollout and the rollback safe. + +Expand/contract in three deploys: + +1. **Expand** — add the new structure, backward-compatible only. Add nullable columns / new tables / new indexes. Do NOT drop or rename anything yet. Old code keeps working untouched. +2. **Migrate + dual-write** — deploy app code that writes **both** old and new shapes and reads the new one (falling back to old). Backfill historical rows in batches. The system is correct whether or not the backfill has finished. +3. **Contract** — once the new shape is fully populated and nothing reads the old one, drop the old columns/tables. This is the only destructive step, and it's now safe because nothing depends on what's being dropped. + +| Phase | What to do | +|-------|-----------| +| Confirm target | Confirm the DB host/name in the active connection string; confirm you're not pointed at prod by accident (`SELECT current_database()`, check the host). | +| PLAN | Generate and **read** the migration SQL/DDL before running it (`migrate ... --dry-run` / framework's "show SQL" / `EXPLAIN` the backfill). Confirm it's expand-only at this step — no drops/renames sneaking in. Check lock impact (see below). | +| SAFEGUARD | Take a backup/snapshot (`pg_dump`, RDS snapshot) before any schema change. Confirm the migration tool records a **down/rollback** migration, and that it actually reverses the up. For the contract step, confirm the dropped objects are captured in the backup. | +| APPLY (staged) | Run the migration, then deploy app code — never in one irreversible step. Backfill in **batches** (`LIMIT`/keyset pagination), not one giant `UPDATE`, to avoid long locks and replication lag. Watch lag between batches. | +| VERIFY | Row counts/parity between old and new shape; new constraints hold; `EXPLAIN` shows the new index is used; app health green on the new code path; replication lag returned to baseline. | +| GATE (idempotency) | Re-running the migration is a no-op (tool reports "already applied" / version table current). The backfill, re-run, changes 0 rows. | +| ROLLBACK | Before contract: redeploy the previous app version (schema is still backward-compatible — that's the whole point) and/or run the down migration. After contract: the drop is destructive, so rollback is restore-from-backup or a forward-fix migration — flag this explicitly in `## Rollback`, like any irreversible step. | + +Lock-safety reminders (Postgres-flavored, adapt per engine): + +- **Build indexes concurrently** (`CREATE INDEX CONCURRENTLY`) so you don't hold a write lock on a hot table. +- **Adding a `NOT NULL` column with a default** can rewrite the whole table on older engines — prefer add-nullable → backfill → add constraint `NOT VALID` → `VALIDATE CONSTRAINT`. +- **Renames and type changes are not backward-compatible** — model them as add-new + dual-write + drop-old, never an in-place rename. +- A migration that takes a lock and then waits behind a long query can **queue every subsequent query behind it** — set a `lock_timeout` so it fails fast instead of stalling the app. + +NoSQL / schemaless stores have no DDL but the same shape applies: the schema lives in the application, so expand/contract means *deploy code that tolerates both the old and new document shape*, backfill, then remove the old-shape handling. + +## CI/CD pipelines + +- **PLAN:** lint/validate the pipeline config (`gh workflow view`, `circleci config validate`, `gitlab-ci-lint`); for changes to a running pipeline, dry-run on a branch first. +- **SAFEGUARD:** the prior pipeline config is in git — the rollback is `git revert`. +- **APPLY/VERIFY:** trigger a run on a non-protected branch, watch it go green, *then* merge. Don't validate a pipeline change only by reading the YAML. +- **VERIFY:** the run completes successfully end-to-end; artifacts/deploys land where expected. + +## Universal safety reminders + +- A tool reporting "success" means the API accepted the request — **not** that the system converged. Always assert observed live state. +- "Exists" ≠ "Ready" ≠ "serving". Give rollouts time to settle and re-check. +- Stage everything that can be staged. The blast radius of a staged change is one unit; of a big-bang, the whole fleet. +- If a second dry-run still wants to change things, the apply didn't converge — that's a finding, not noise. +- Never log secrets. Redact tokens/kubeconfigs/credentials in evidence and reports. diff --git a/packages/pi-arc/skills/arc-debug/SKILL.md b/packages/pi-arc/skills/arc-debug/SKILL.md index 9379360..d7f690f 100644 --- a/packages/pi-arc/skills/arc-debug/SKILL.md +++ b/packages/pi-arc/skills/arc-debug/SKILL.md @@ -87,5 +87,5 @@ You're doing it wrong if you: - Always write a bug-demonstrating test before the fix - Always run the full test suite after fixing - Revert failed fix attempts cleanly — don't leave debris -- After debugging, return to the calling skill — typically `implement` step 4 to re-verify the subagent's result, or `verify` to re-run the gate sequence +- After debugging, return to the calling skill — typically `build` step 4 to re-verify the subagent's result, or `verify` to re-run the gate sequence - Format all arc content (descriptions, plans, comments) per `skills/arc/_formatting.md` diff --git a/packages/pi-arc/skills/arc-plan/SKILL.md b/packages/pi-arc/skills/arc-plan/SKILL.md index 1bda42b..5cbf4c3 100644 --- a/packages/pi-arc/skills/arc-plan/SKILL.md +++ b/packages/pi-arc/skills/arc-plan/SKILL.md @@ -9,38 +9,28 @@ Break an approved design into bite-sized, self-contained tasks with exact file p ## Review Commands -Design docs live in `docs/plans/<file>.md`. The brainstorm skill registers each doc on one of three review surfaces and writes a routing marker as line 1 of the doc itself: +Design docs live in `docs/plans/<file>.md`. The brainstorm skill registers each doc on the planner and writes a marker as line 1 of the doc itself recording the plan ID: ``` -<!-- arc-review: kind=<legacy|share-local|share-remote> id=<id> --> +<!-- arc-review: id=<id> --> ``` -**Always read the marker before invoking any review CLI.** The plan skill's CLI calls branch on `kind`: +The planner CLI verbs are: -| kind | Show content | List comments | Pull accepted | Approve | Update content | -|---|---|---|---|---|---| -| `legacy` | `arc plan show <id>` | `arc plan comments <id>` | n/a — review thread inline | `arc plan approve <id>` | re-create the plan (no in-place update) | -| `share-local` | `arc share show <id>` | `arc share comments <id>` | `arc share pull <id>` | `arc share approve <id>` | `arc share update <id> <path>` | -| `share-remote` | `arc share show <id>` | `arc share comments <id>` | `arc share pull <id>` | `arc share approve <id>` | `arc share update <id> <path>` | +| Show content | List comments | Approve | Update content | +|---|---|---|---| +| `arc plan show <id>` | `arc plan comments <id>` | `arc plan approve <id>` | re-create the plan (no in-place update) | -Read the marker with one shell call: +Read the plan ID from the marker with one shell call: ```bash MARKER=$(head -1 docs/plans/<file>.md) -KIND=$(echo "$MARKER" | grep -oE 'kind=[a-z-]+' | cut -d= -f2) ID=$(echo "$MARKER" | grep -oE 'id=\S+' | sed 's/id=//' | tr -d '>' | xargs) -# Now branch on $KIND for every review CLI call. ``` -**Encrypted-share keyring.** For `share-local` and `share-remote`, the author's edit tokens live in the arc-server's local keyring (a `shares` table in `~/.arc/data.db`) — not in any JSON file. `arc share show <id> --author-url` reprints the Author URL if it's lost. Legacy plans don't have edit tokens; the URL is just `<base>/planner/<id>`. - -**Fallback for unmarked design docs.** Older design docs created before the marker contract may not have line 1 set. If the marker is missing, fall back to: - -```bash -arc share list --json | jq -r '.[] | select(.plan_file=="docs/plans/<file>.md") | .id' -``` +The planner is plain HTTP with no edit tokens or keys to manage; the URL is just `<base>/planner/<id>`. -This only covers `share-*` plans (legacy plans aren't in the share keyring). If the fallback returns no result, ask the user which review surface the plan was registered on. +**Fallback for unmarked design docs.** Older design docs created before the marker contract may not have line 1 set. If the marker is missing, read the file directly and ask the user for the plan ID (or re-register the doc via brainstorm step 6). ## Granularity Rule @@ -65,21 +55,20 @@ Add tasks for each step below using the bundled `todo` checklist (via `todo` too ### 1. Read the Design -You're handed a plan-file path (typically `docs/plans/<file>.md`) by the brainstorm skill. Read line 1 to learn which review surface the plan lives on, then call the matching show command: +You're handed a plan-file path (typically `docs/plans/<file>.md`) by the brainstorm skill. Read line 1 to get the plan ID, then show its content: ```bash MARKER=$(head -1 docs/plans/<file>.md) -KIND=$(echo "$MARKER" | grep -oE 'kind=[a-z-]+' | cut -d= -f2) ID=$(echo "$MARKER" | grep -oE 'id=\S+' | sed 's/id=//' | tr -d '>' | xargs) -case "$KIND" in - legacy) arc plan show "$ID" ;; - share-local|share-remote) arc share show "$ID" ;; - *) echo "No review marker; reading file directly"; cat docs/plans/<file>.md ;; -esac +if [ -n "$ID" ]; then + arc plan show "$ID" +else + echo "No review marker; reading file directly"; cat docs/plans/<file>.md +fi ``` -The full content is what you'll break down in the next steps. If the file has no marker (an older design doc), reading the file directly is fine — but warn the user the review-state CLI calls (approve, pull) won't work without a registered review surface, and offer to register it via brainstorm step 6. +The full content is what you'll break down in the next steps. If the file has no marker (an older design doc), reading the file directly is fine — but warn the user the review-state CLI calls (approve) won't work without a registered plan, and offer to register it via brainstorm step 6. ### 2. Identify Shared Contracts (Foundation Task) @@ -174,27 +163,38 @@ When identifying tasks, assign **file ownership** — each file should be owned ### 4. Create Epic and Tasks via issue-manager -**Model tier:** `issue-manager` defaults to `nano` — the right tier for low-reasoning CLI formatting and bulk issue creation. Model profile: issue creation uses the issueManager profile when configured via `/arc-models`; otherwise it falls back to the legacy tier/frontmatter behavior. This work is mostly CLI formatting, so the recommended profile uses gpt-5.4-mini with thinking off. For this dispatch, omit `model:`. See the Model Selection table in `../arc-build/SKILL.md` for the full guidance. +**Model tier:** `issue-manager` defaults to `nano` — the right tier for low-reasoning CLI formatting and bulk issue creation. Model profile: issue creation uses the issueManager profile when configured via `/arc-models`; otherwise it falls back to the legacy tier/frontmatter behavior. This work is mostly CLI formatting, so the recommended profile uses gpt-5.6-luna with thinking off. For this dispatch, omit `model:`. See the Model Selection table in `../arc-build/SKILL.md` for the full guidance. **Never run `arc create` directly** — always delegate to the `issue-manager` agent. This keeps bulk CLI output in a disposable subagent context. -Read the full plan content first using the kind-aware case from step 1 (`arc plan show "$ID"` for legacy, `arc share show "$ID"` for share-local / share-remote). Then build a task manifest that includes: -1. **The epic** — its description will be populated by the agent from the plan file (see below) -2. **All child tasks** with self-contained descriptions - -**Critical**: Do NOT paste or summarize the plan content into the agent prompt. Instead, pass the plan file path and let the agent read it directly. This prevents content loss from summarization. +**Never put description content in the agent prompt — descriptions travel as canonical files.** Arc normalizes leading/trailing whitespace from `--stdin`, so canonicalize each file with outer whitespace removed before dispatch. The issue-manager then transfers those canonical bytes with shell redirection; description bodies never pass through the model. + +Before dispatching: + +1. Create a manifest directory: `mkdir -p /tmp/arc-manifest-<epic-slug>`. +2. Write every task's full self-contained draft to `/tmp/arc-manifest-<epic-slug>/T0.md`, `T1.md`, and so on with the `write` tool. +3. Copy the approved plan to `/tmp/arc-manifest-<epic-slug>/epic.md`. If only the plan ID is known, recover the source path with `arc plan show <id> | grep -oE '^File: \S+' | awk '{print $2}'`. +4. Canonicalize only outer whitespace so the files match Arc's `--stdin` normalization while preserving every internal byte: + ```bash + python3 - /tmp/arc-manifest-<epic-slug> <<'PY' + from pathlib import Path + import sys + for path in Path(sys.argv[1]).glob('*.md'): + path.write_text(path.read_text().strip()) + PY + ``` +5. From this point onward, hash, dispatch, repair, and verify only these canonical files. -You typically already have the plan file path from the brainstorm hand-off. If you only have the ID and need to find the file path, the lookup depends on `kind`: +Before persistence, self-review the canonical description files against the approved design: -```bash -# share-local / share-remote: keyring includes the plan_file mapping -arc share list --json | jq -r '.[] | select(.id=="<id>") | .plan_file' - -# legacy: arc plan show prints "File: <path>" in its metadata header -arc plan show <id> | grep -oE '^File: \S+' | awk '{print $2}' -``` +1. **Spec coverage:** Every design requirement maps to a task. +2. **Success-criteria coverage:** Every `## Success Criteria` item maps to at least one task's `## Expected Outcome`. +3. **T0 contract coverage:** Shared contract blocks match the T0 definitions exactly. +4. **Type consistency:** Names and signatures agree across tasks. +5. **Placeholder scan:** No TBD/TODO/vague implementation placeholders remain. +6. **Step completeness:** Every code or command step includes concrete content. -The share keyring entries have `{id, kind, url, key_b64url, plan_file, created_at}` — edit tokens are intentionally redacted. +Fix the canonical files now, then repeat this review. Do not create any Arc issue until it passes. Issue creation must be phased: @@ -202,62 +202,67 @@ Issue creation must be phased: 2. Create all child tasks with the epic as parent before applying dependencies. 3. Capture the complete task-name-to-ID table. 4. Apply dependencies only after all child IDs exist. -5. Apply labels after dependencies, or in the same post-creation phase. -6. Return the final ID table, dependency summary, and a `## Timing` section with phase-level `elapsed_ms` values when available. +5. Apply labels after dependencies with `arc update <id> --label-add=<label>`. +6. Verify descriptions and return the final ID table, dependency summary, and a `## Timing` section with phase-level `elapsed_ms` values. -Then dispatch the manifest. Prefer true `pi-subagents` so long issue-creation runs are visible in `/subagents-status`: +Then dispatch the manifest — titles, metadata, and file paths only, no description bodies. Prefer true `pi-subagents` so long issue-creation runs are visible in `/subagents-status`: -Dispatch preference (use **async** so long-running issue creation appears in `/subagents-status`): +Dispatch preference: - Primary: `subagent({ agent: "arc-issue-manager", task: "<manifest below>", context: "fresh", async: true, clarify: false })` -- After launching async, **wait for terminal status** by polling `subagent({ action: "status", id: "<run-id>" })` until status is `completed` or `failed` -- Users can monitor progress via `/subagents-status` during the async run -- If `subagent({ action: "list" })` shows `arc-issue-manager`, do **not** use the slower `arc_agent(agent="issue-manager")` fallback for bulk issue creation -- Arc issue-manager should be auto-materialized; if it is missing, first run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })` -- Fallback only if `pi-subagents` is not installed or cannot load after deprecated repair: `arc_agent(agent="issue-manager", task="<manifest below>")` +- Wait for terminal status by polling `subagent({ action: "status", id: "<run-id>" })` until `completed` or `failed` +- Users can monitor progress via `/subagents-status` +- If `subagent({ action: "list" })` shows `arc-issue-manager`, do **not** use the slower `arc_agent(agent="issue-manager")` fallback +- If it is missing, run `subagent({ action: "doctor" })` and inspect Arc's materialization warning; use `/arc-subagents-sync` only as a deprecated repair command +- Fallback only when `pi-subagents` is unavailable after repair: `arc_agent(agent="issue-manager", task="<manifest below>")` Use this task payload for whichever dispatcher you choose: ```markdown -Create the following epic and tasks. -After creation, set dependencies and labels as listed. -Return a summary table mapping task names to arc IDs, plus a `## Timing` section with phase-level `elapsed_ms` values when available. +Create the following epic and tasks using the arc CLI. + +RULES (mechanical, not stylistic): +- Create the epic first, then create every child with its description piped from the listed file: + arc create "<title>" --type=<type> [--parent=<id>] --stdin < "<description file>" +- Create all children and capture every ID before applying dependencies. +- Apply dependencies only after all child IDs exist. +- Apply manifest labels only after dependencies with `arc update <id> --label-add=<label>`. +- NEVER read a description file and retype its contents into a heredoc or a + --description flag. Shell redirection only. You may not summarize, trim, + reformat, or "clean up" description content under any circumstances. +- After each create, verify the stored description equals the canonical file: + `sha256sum < "<description file>"` must equal + `arc show <id> --json | jq -j .description | sha256sum`. + Treat any mismatch as a failed verification phase; repair from the canonical file and re-check. ## Epic ### <epic title> Type: epic -Plan file: <absolute path to the plan markdown file> - -IMPORTANT: Read the plan file at the path above using the Read tool. Use the COMPLETE -file contents as the epic description. Do NOT summarize, truncate, or paraphrase — -copy the full file content verbatim as the description. +Description file: /tmp/arc-manifest-<epic-slug>/epic.md ## Tasks ### T1: <title> Type: task Parent: <epic-id from above> -Description: -<full multi-line self-contained description> +Labels: none +Description file: /tmp/arc-manifest-<epic-slug>/T1.md ### T2: <title> Type: task Parent: <epic-id from above> -Description: -<full multi-line self-contained description> +Labels: docs-only +Description file: /tmp/arc-manifest-<epic-slug>/T2.md ## Dependencies - T2 blocked by T1 - T4 blocked by T3 -## Labels -- T3: docs-only - ## Required Output -| Task | Arc ID | Title | -|------|--------|-------| -| Epic | ... | ... | -| T1 | ... | ... | +| Task | Arc ID | Title | File SHA-256 | Arc SHA-256 | +|------|--------|-------|-------------|------------| +| Epic | ... | ... | ... | ... | +| T1 | ... | ... | ... | ... | ## Timing | Phase | elapsed_ms | @@ -266,29 +271,41 @@ Description: | child_tasks | ... | | dependencies | ... | | labels | ... | +| verification | ... | ``` -The `## Timing` section is required for bulk issue creation; use `unknown` for a phase only if the issue-manager could not capture a timestamp. +The `## Timing` section is required for bulk issue creation; use `unknown` only when a phase timestamp could not be captured. + +**IMPORTANT**: The epic description MUST contain the complete approved design. The plan file is ephemeral; the epic description is the permanent record. Piping the file into `--stdin` guarantees no summarization or content loss. -**IMPORTANT**: The epic description MUST contain the complete approved design. The agent reads the plan file directly to avoid any summarization or content loss. The plan file is ephemeral; the epic description is the permanent record. +For each task, check whether **all** files in its `## Files` section are documentation (`.md`, `.txt`, `README`, `CHANGELOG`, or anything under `docs/`). If so, set `Labels: docs-only` on that task in the manifest. Doc-only tasks skip TDD — the `build` skill routes them to `doc-writer` instead of `builder`. Keep `Labels:` in the manifest, but apply labels only after dependencies with `arc update <id> --label-add=<label>` so the phased creation contract remains observable and recoverable. -For each task, check whether **all** files in its `## Files` section are documentation (`.md`, `.txt`, `README`, `CHANGELOG`, or anything under `docs/`). If so, include it in the `## Labels` section with `docs-only`. Doc-only tasks skip TDD — the `implement` skill routes them to `doc-writer` instead of `builder`. +For each task whose work is **infrastructure/operations** rather than application code — cluster upgrades, Terraform/Helm/Ansible changes, cloud provisioning, CI/CD pipeline edits, anything where success means a *live system reaching a desired state* rather than a passing unit test — set `Labels: devops` on that task in the manifest. DevOps tasks skip TDD — the `build` skill routes them to `devops-builder` (PLAN → SAFEGUARD → APPLY → VERIFY → GATE) instead of `builder`. Use the **DevOps task format** (see `## Task Description Format` below) for these: it replaces `## Test Command` with `## Verification` and adds `## Safeguards` and `## Rollback`. A task is either `devops` or a normal code task — don't apply both labels. ### 5. Validate Returned Results -Before proceeding, verify the agent's output: +Before proceeding, verify the agent's output. Do not trust the agent's self-reported line counts — re-check independently: 1. **Count check**: The number of returned IDs must match the number of tasks in your manifest -2. **Spot-check**: Run `arc show <id>` on one returned task to confirm it exists and has the correct parent -3. **If mismatch**: Re-dispatch the agent for missing tasks only, or create them manually +2. **Verbatim check**: For the epic and every task, compare line counts between the description file and what arc stores: + ```bash + wc -l < "<description file>" + arc show <id> --json | jq -r .description | wc -l + ``` + First compare byte hashes: `sha256sum < "<description file>"` must equal `arc show <id> --json | jq -j .description | sha256sum`. Line counts and, for T0, code-fence counts are diagnostics only. Any hash mismatch is a plan failure — repair with file redirection and re-check before continuing. +3. **Label check**: `arc show <id> --json | jq .labels` for each task with labels in the manifest (`docs-only`, `devops`) — missing labels misroute tasks at build time +4. **Parent/dependency spot-check**: Run `arc show <id>` on one task to confirm parentage and one dependency edge +5. **If any description fails the verbatim check**: Do NOT re-dispatch the agent — repair mechanically yourself: `arc update <id> --stdin < "<description file>"`, then re-verify. For missing tasks, re-dispatch the agent for those tasks only +6. **Cleanup**: After all checks pass, remove the manifest directory: `rm -rf /tmp/arc-manifest-<epic-slug>` ### 6. Append Task Breakdown to Epic Description -The epic was created in step 4 with the full design content. Now append the task breakdown table (with actual arc IDs from step 5) to the epic's description: +The epic was created in step 4 with the full design content. Now append the task breakdown table (with actual arc IDs from step 5) to the epic's description. Build the new description by *concatenating*, never by retyping the existing content: ```bash -arc update <epic-id> --stdin <<'EOF' -<existing epic description — the full design content from step 4> +{ + arc show <epic-id> --json | jq -r .description + cat <<'EOF' --- @@ -296,24 +313,15 @@ arc update <epic-id> --stdin <<'EOF' <task breakdown table with arc IDs, titles, statuses, and dependency info> EOF +} | arc update <epic-id> --stdin ``` -**IMPORTANT**: Preserve the full design content already in the description — do not replace it with a summary. The epic description is the permanent record of the design. Only append the task breakdown table at the end. +**IMPORTANT**: Preserve the full design content already in the description — do not replace it with a summary. The epic description is the permanent record of the design. Only append the task breakdown table at the end. The concatenation pattern above guarantees this: only the new table passes through your output; the existing design content round-trips through the shell. -### 6.5. Self-Review - -After writing all tasks, review the plan against the design before proceeding: - -1. **Spec coverage:** Skim each section/requirement in the design. Can you point to a task that implements it? If a gap exists, add the task. -2. **Placeholder scan:** Search all task descriptions for red flags from the No Placeholders list. Fix them. -3. **Type consistency:** Do the types, method signatures, and property names used in later tasks match what was defined in earlier tasks? A function called `clearLayers()` in T1 but `clearFullLayers()` in T3 is a bug. -4. **Step completeness:** Every code step has a code block. Every command step has the exact command and expected output. No exceptions. - -Fix issues inline. No need to re-review — just fix and move on. ### 7. Choose Execution Path -**Use the `ask_user_question` tool** with the package's `questions[]` schema to let the user choose: +**Use the bundled `@juicesharp/rpiv-ask-user-question` `ask_user_question` tool** with the package `questions[]` schema to let the user choose. Do not manually author package sentinel labels (`Type something.`, `Chat about this`, `Other`, `Next`): ```json { @@ -324,15 +332,15 @@ Fix issues inline. No need to re-review — just fix and move on. "options": [ { "label": "Start now (Recommended)", - "description": "Recommended when you want this session to continue directly into /arc-build with subagents handling TDD per task." + "description": "Continue directly into /arc-build in this session." }, { "label": "New session", - "description": "Prints the exact /arc-build <epic-id> command to run in a fresh Pi session." + "description": "Print the exact /arc-build <epic-id> command for a fresh Pi session." }, { "label": "Done for now", - "description": "Leaves the tasks tracked in arc for manual or future implementation." + "description": "Leave the tasks tracked in arc for future implementation." } ] } @@ -342,7 +350,7 @@ Fix issues inline. No need to re-review — just fix and move on. After the user chooses: -**Start implementing now**: Invoke the `implement` skill immediately with the epic ID. +**Start implementing now**: Invoke the `build` skill immediately with the epic ID. **Implement in a new session**: Output the exact command for the user to copy-paste: ``` @@ -354,7 +362,6 @@ Run this in a new Pi session: Replace `<epic-id>` with the actual epic ID. **Done for now**: Confirm the epic and tasks are saved in arc. The user can run `/arc-build <epic-id>` whenever they're ready. - ## Parallel Readiness When a design can split into parallel implementation batches, document the readiness proof before handing off tasks. @@ -372,7 +379,7 @@ Do not mark any task parallelizable until this matrix is complete and every file ### Parallel Batch Manifest -Group only disjoint tasks into parallel batches after file ownership is settled. +Group only disjoint tasks into parallel batches after file ownership is settled. Never place a `devops` task or other live-system mutation in a parallel batch; those tasks must remain sequential. | Batch | Prerequisites | Tasks | Independence proof | Validation | |---|---|---|---|---| @@ -384,6 +391,7 @@ List the validation command(s) for each batch and the result that proves the bat | Check | Scope | Command | Expected result | |---|---|---|---| + ## Task Description Format Each task's `--description` must be **self-contained** (~3-5k tokens). The task description IS the implementation context — the implementer loads `arc show <task-id>` and nothing else. @@ -455,14 +463,62 @@ For `docs-only` tasks, omit `## Test Command` and use `## Verification` instead: - Code blocks have language tags ``` +### DevOps task format + +For `devops` tasks, the implementer executes a change against a live system, so the description is a **runbook**, not a code spec. Keep `## Summary` and `## Steps`, but adapt the rest: + +- **`## Files`** lists committed IaC/config/manifests the task changes (Terraform, Helm values, k8s YAML, pipeline config). If the change is purely imperative (e.g. `kubectl drain`/`cordon` with no committed artifact), write `n/a (imperative change)`. +- **`## Target`** names the exact environment the task is authorized to touch — cluster/context, namespace, Terraform workspace, cloud account/project. This is mandatory: the `devops-builder` reports `NEEDS_CONTEXT` if the target is ambiguous, so resolve it at plan time. +- **`## Steps`** are ordered runbook actions, each with the **exact command** (dry-run before apply) — e.g. `terraform plan -out=tfplan`, then `terraform apply tfplan`. Stage multi-unit changes (one node/replica/canary at a time). Same anti-placeholder rule as code tasks: show the real commands, not "apply the change." +- **`## Safeguards`** — what to back up and pre-flight before mutating (e.g. `kubectl get <res> -o yaml > backup.yaml`, snapshot the state, record the current Helm revision). This is the SAFEGUARD phase made concrete. +- **`## Verification`** (replaces `## Test Command`) — commands that assert the **live desired state**, each with the expected result: e.g. `kubectl get nodes -o wide` → all nodes report v1.29 + `Ready`; `terraform plan` → "No changes"; endpoint returns 200. +- **`## Rollback`** — the exact command(s) to undo the change (e.g. `helm rollback <release> <prev-revision>`, `kubectl apply -f backup.yaml`), referencing the backup/revision captured in `## Safeguards`. A `devops` task with no rollback path is a plan failure — the implementer is required to have one. + +Example skeleton: + +```markdown +## Summary +Upgrade the staging `payments` Helm release with a staged, reversible rollout. + +## Target +Cluster context: `arn:aws:eks:us-east-1:123456789012:cluster/staging-eks`; namespace: `payments`; release: `payments`. + +## Files +- Modify: `deploy/values-staging.yaml` + +## Safeguards +- `kubectl config current-context` must equal the target context above. +- `PREV_REV=$(helm history payments -n payments -o json | jq -r 'map(.revision) | max')` +- `helm get values payments -n payments -o yaml > /tmp/payments-values-before.yaml` + +## Steps +1. Preview: `helm diff upgrade payments ./deploy/payments -n payments -f deploy/values-staging.yaml` and confirm only the intended image/config changes appear. +2. Apply with rollback-on-failure: `helm upgrade payments ./deploy/payments -n payments -f deploy/values-staging.yaml --atomic --timeout 10m`. +3. Observe: `kubectl rollout status deployment/payments -n payments --timeout=10m`. + +## Verification +- `helm diff upgrade payments ./deploy/payments -n payments -f deploy/values-staging.yaml` → empty diff. +- `kubectl get deployment payments -n payments -o jsonpath='{.status.readyReplicas}/{.status.replicas}'` → equal counts. +- `kubectl get pods -n payments` → no `CrashLoopBackOff` or `ImagePullBackOff`. + +## Rollback +`helm rollback payments "$PREV_REV" -n payments --wait --timeout 10m`; verify rollout and pod health again. + +## Expected Outcome +The staging release converges to the intended chart values, all replicas are Ready, the post-apply diff is empty, and the recorded prior revision remains available for rollback. +``` + +The task must contain concrete target values and executable commands like this example. If the provider has no preview/change-set mechanism or the recovery path cannot be verified, stop for explicit authorization rather than weakening the dry-run/rollback law. + ## Rules - Never reference external docs or the full plan in task descriptions — everything needed is in the description -- Design documents live in `docs/plans/` and are registered via one of `arc plan create` (legacy), `arc share create` (encrypted local default), or `arc share create … --remote` (encrypted remote). The brainstorm skill writes a `<!-- arc-review: kind=… id=… -->` marker as line 1 of the doc — always read the marker before invoking review CLIs to route correctly +- Design documents live in `docs/plans/` and are registered on the planner via `arc plan create --no-frontmatter`. The brainstorm skill writes a `<!-- arc-review: id=… -->` marker as line 1 of the doc — read it to get the plan ID before invoking review CLIs - Task descriptions must include actual code guidance, not vague instructions - `teammate:*` labels may be used as planning metadata, but Pi does not support Claude-style team deployment. Use `/arc-build` for orchestrated sequential work or independent `pi-subagents` parallel batches when available. - The plan skill creates tasks; it does not implement them - The plan skill never runs `arc create` directly — always delegate to `issue-manager` +- Description content travels by file + `--stdin < file` redirection — never inline in a subagent prompt, never retyped by a subagent. A model re-emitting long content will compress it regardless of instructions; only shell redirection is verbatim - Every task must include a `## Scope Boundary` section — no file modifications outside the `## Files` list - No two parallelizable tasks may own the same file — resolve overlaps via foundation task, merging, or serialization - Format all arc content (descriptions, plans, comments) per `skills/arc/_formatting.md` diff --git a/packages/pi-arc/skills/arc-review/SKILL.md b/packages/pi-arc/skills/arc-review/SKILL.md index 04e93c8..e8b9546 100644 --- a/packages/pi-arc/skills/arc-review/SKILL.md +++ b/packages/pi-arc/skills/arc-review/SKILL.md @@ -13,7 +13,7 @@ Create a checklist using the bundled `todo` tool (or `/todos`) with these steps: ### 1. Get Git SHAs -Use the `PRE_TASK_SHA` recorded by the implement skill before dispatching the implementer: +Use the `PRE_TASK_SHA` recorded by the build skill before dispatching the implementer: ```bash BASE_SHA=$PRE_TASK_SHA @@ -32,7 +32,7 @@ HEAD_SHA=$(git rev-parse HEAD) ### 2. Get Design Context -If the review was invoked from the implement skill, a design excerpt should be available. Retrieve it: +If the review was invoked from the build skill, a design excerpt should be available. Retrieve it: ```bash # Get the parent epic of this task @@ -54,7 +54,7 @@ Dispatch preference (use **async** so longer reviews appear in `/subagents-statu - Arc code-reviewer should be auto-materialized; if it is missing, first run `subagent({ action: "doctor" })` and inspect Arc's materialization warning. Use `/arc-subagents-sync` only as a deprecated repair command, then re-check with `subagent({ action: "list" })` - Fallback only if `pi-subagents` is not installed or cannot load after deprecated repair: `arc_agent(agent="code-reviewer", task="<filled prompt>")` -**Model tier:** Follow the Model Selection table in `../arc-build/SKILL.md`. Model profile: reviews use the `codeReviewer` profile when configured via `/arc-models`; otherwise they fall back to existing tier/frontmatter behavior. Escalate only for large, cross-layer, or security-sensitive diffs. For most reviews, omit `model:` (use the agent's `standard` default). For `pi-subagents`, pass the configured concrete large model only when escalating. +**Model tier:** Follow the Model Selection table in `../arc-build/SKILL.md`. Reviews use the `codeReviewer` profile when configured via `/arc-models`; otherwise the agent's `large` frontmatter is the fallback. Omit `model:` so the configured profile remains authoritative. Use an explicit override only for deliberate escalation beyond the configured profile. ### 4. Triage Feedback @@ -80,7 +80,7 @@ If fixes are needed: ### 6. Proceed - If all tasks are done → invoke `finish` -- If more tasks remain → return to `implement` for the next task +- If more tasks remain → return to `build` for the next task ## Response Discipline diff --git a/packages/pi-arc/skills/arc-summarize/SKILL.md b/packages/pi-arc/skills/arc-summarize/SKILL.md new file mode 100644 index 0000000..fe056d9 --- /dev/null +++ b/packages/pi-arc/skills/arc-summarize/SKILL.md @@ -0,0 +1,416 @@ +--- +name: arc-summarize +description: You MUST use this skill to summarize an arc issue into an external tracker (Jira, Linear, GitHub, or any tracker with a connected integration) when the user says "summarize <arc-id> for <tracker>", "create a <tracker> issue from arc <id>", "push/mirror this arc issue to <tracker>", or indicates they want to mirror an arc issue to an external system. The skill produces a lossy, directional summary (not a verbatim paste) that points back to arc as the source of truth, detects which connected tool (MCP server or CLI) can fulfill the request, creates the ticket, backlinks both directions, and verifies the result. One-way snapshot only in v1 — no two-way status sync. +--- + +# Summarize — Mirror an Arc Issue to an External Tracker + +Summarize an arc issue into an external tracker with intelligent field mapping, capability detection, and verification. + +## Iron Law + +**The external ticket is a lossy, directional summary that points back to arc.** Arc remains the source of truth. Never paste the full arc issue verbatim. Condense, preserve intent and scope, drop internal tactics. + +## Workflow + +Follow these 10 steps in sequence: + +### 1. Parse the Request + +Extract from the user's input: +- **Arc ID** — the issue to summarize +- **Target tracker** — explicitly named (jira, linear, github, etc.) or implied from context +- **Inline specifics** — board/project, sprint/cycle/milestone, assignee, labels, issue type — anything the user stated. Honor these; do not re-ask. + +Example: +```text +User: "summarize agentmarke-0qex.04hl1w for jira in the BT board" +Parsed: id=agentmarke-0qex.04hl1w, tracker=jira, board=BT +``` + +### 2. Extract Arc Issue Data + +Fetch the arc issue in JSON: + +```bash +arc show <id> --json +``` + +Extract and clean: +- Title +- Body (markdown, full) +- Type (feature / bug / task / chore) +- Priority (0–4 scale) +- Labels (array) +- Status +- Origin PR/branch (if present) + +Store these for downstream use. + +### 3. Detect Capability — By Reasoning, Not a Lookup Table + +**Determine which connected capability can write to the named tracker.** Do not hardcode an MCP namespace. Use Pi's `mcp` gateway to inspect server status and search available tools by tracker/action, then call the selected tool through the same gateway. + +- **Jira / Atlassian** → Search connected MCP tools for Atlassian/Jira issue creation. +- **Linear** → Search connected MCP tools for Linear issue creation. +- **GitHub / GitHub Issues** → Prefer an authenticated GitHub MCP tool when present; otherwise verify `gh auth status` and use `gh`. +- **Other trackers** → Use a connected MCP write tool or authenticated CLI wrapper; otherwise stop. + +A server that is installed but unauthenticated is not usable. Prefer an authenticated provider when several exist. If authentication is required, use Pi's MCP authentication flow or tell the user to open `/mcp`; never guess a raw `mcp__...` tool namespace. + +**If no connected tool can satisfy the request:** +- **STOP. Do not create anything.** +- Name the missing capability: *"Jira write requires the Atlassian MCP server."* +- Tell the user how to enable it: *"Run `/mcp` to authenticate an Atlassian connector, or `gh auth login` for GitHub."* +- Create no ticket; let the user take the action and re-invoke. + +### 4. Load Cache + +Read `~/.arc/tracker-map.yaml`. This file accumulates discovered constants across all projects on this machine: + +```yaml +# ~/.arc/tracker-map.yaml +trackers: + jira:bactrack.atlassian.net: + instance: + cloud_id: <uuid> + sprint_field: customfield_10020 + issue_type_map: + feature: { name: Story, id: "10001" } + bug: { name: Bug, id: "10004" } + task: { name: Task, id: "10002" } + chore: { name: Task, id: "10002" } + priority_map: { 0: Highest, 1: High, 2: Medium, 3: Low, 4: Lowest } + assignee_aliases: { "ben firestone": <accountId> } + prefs: + last_project: BT +``` + +**Tolerance:** +- File does not exist → create it on first use (step 8). +- Entry for this tracker:instance does not exist → discover-then-cache (step 7). +- Keys missing within an entry → fall back to discover (step 7). +- `prefs` → optional; used only to pre-select prompts, never silently applied. + +### 5. Summarize — The Heart of the Skill + +Condense the verbose arc body into **tracker-idiomatic copy**. This is lossy by design. + +**Keep:** +- Intent (why this work matters) +- Scope (what problem is solved, what is in/out of scope) +- Acceptance criteria (what done looks like) +- Origin (link to original arc issue) +- PR / branch link (if present) + +**Drop:** +- Arc-internal IDs (bacst-xxxx, agentmarke-yyyy) +- Implementation tactics (technical approach, tools, frameworks) +- Dev-only notes (debugging steps, internal debugging context) +- Commit SHAs, worktree details +- Internal tracker links + +**Example:** + +Arc issue: +``` +# proj-1a2b.3c4d — CLI docs for OpenCode installer + +## Description +The OpenCode installer is live but undocumented. Users don't know the CLI flags, how to set auth, or what to do when installation fails. + +## Acceptance Criteria +- [ ] CLI help text for `opencode-installer install` +- [ ] Auth troubleshooting guide +- [ ] Example: install w/ GITHUB_TOKEN + +## Implementation Notes +Used bactrack/sentinel for auth detection. See arc bacst-05ca.09y0bs for the shared pattern. +``` + +Summarized for Jira: +``` +## OpenCode CLI: Installation Guide + +Update the OpenCode installer CLI documentation to cover setup, authentication, and troubleshooting. + +### Acceptance Criteria +- CLI help text for `opencode-installer install` command +- Authentication troubleshooting guide +- Example: install with GITHUB_TOKEN + +**Origin:** arc issue proj-1a2b.3c4d +``` + +### 6. Map Fields + +For ambiguous structured decisions, use the bundled `@juicesharp/rpiv-ask-user-question` `ask_user_question` tool with the package `questions[]` schema and 2-4 authored options. Do not author sentinel labels such as `Type something.`, `Chat about this`, `Other`, or `Next`; the package supplies escape hatches. Put the recommended option first and append `(Recommended)` when one is clear. + +Resolve the tracker fields you need. This requires user input for ambiguous cases — never guess. + +#### **Issue Type** + +1. **User input wins.** If the user said "create a bug", use that. +2. Else, **translate arc type through the cache's `issue_type_map`:** + - Arc `bug` → Jira `Bug` (id from cache) + - Arc `feature` → Jira `Story` (id from cache) + - Arc `task` → Jira `Task` (id from cache) + - Arc `chore` → Jira `Task` (id from cache) +3. If the map does not cover it or the cache is empty → **ask the user:** + ```json + { + "questions": [ + { + "header": "Issue type", + "question": "What issue type should be created?", + "options": [ + { "label": "Story (Recommended)", "description": "Use the tracker's feature-oriented issue type." }, + { "label": "Bug", "description": "Use the tracker's defect issue type." }, + { "label": "Task", "description": "Use the tracker's general work-item type." } + ] + } + ] + } + ``` + +#### **Board / Project** + +1. **User stated it in the request** → use it (e.g., "in the BT board"). +2. Else, **prompt with real discovered projects:** + ```json + { + "questions": [ + { + "header": "Project", + "question": "Which discovered project or board should receive the issue?", + "options": [ + { "label": "BT (Recommended)", "description": "Use the previously selected Bactrack project." }, + { "label": "ARCH", "description": "Use the discovered Arc project." } + ] + } + ] + } + ``` + Use the tool's live discovery (Jira JQL, Linear API, `gh` CLI) to list real projects; pre-select `prefs.last_project` if set. +3. After the user picks → **cache the choice to `prefs.last_project`** for next run. + +#### **Sprint** + +- "Current / active sprint" is **resolved live** against the board each run — **never cached** (active sprints roll over every few weeks; a cached id goes stale). +- Since active sprint is almost always the intent, it rarely needs asking. +- When ambiguous (e.g., "which of the 3 open sprints?"), ask from **live sprint data only:** + ```json + { + "questions": [ + { + "header": "Sprint", + "question": "Which live sprint should receive the issue?", + "options": [ + { "label": "Sprint 47 (Recommended)", "description": "Use the current active sprint discovered from the tracker." }, + { "label": "Sprint 48", "description": "Use the next open sprint discovered from the tracker." } + ] + } + ] + } + ``` + +#### **Assignee** + +- **Never guess.** Always ask. +- Populate from discovered team members + the tool's "Unassigned" default. +- Translate user input through `assignee_aliases` if cached; otherwise probe live. + +#### **Labels / Tags** + +- Pre-populate from the arc issue's labels if the cache has a mapped equivalent. +- Ask the user if labels are ambiguous or the map is empty. + +### 7. Discover Only What Is Missing, Then Cache It + +For any field that is not in the prompt, cache, or arc issue, probe **live** from the tracker's API. + +**Examples:** + +- **Jira sprint custom field ID:** Use JQL probe to find `customfield_10020`. +- **Jira issue type IDs:** Call `createmeta` to resolve type names to instance-specific IDs. +- **Linear team members:** Fetch live team roster to populate assignee list. +- **GitHub assignees:** `gh api repos/<owner>/<repo>/collaborators` to list potential assignees. + +After each probe, **write newly discovered constants to `~/.arc/tracker-map.yaml`** under the `instance` key. The next run will skip the probe. + +```bash +# Example: cache a discovered Jira sprint field +# in ~/.arc/tracker-map.yaml under trackers[jira:<host>][instance] +sprint_field: customfield_10020 +``` + +### 8. Create + +Call the tracker's API via the connected tool (MCP server or CLI) to create the issue. + +**Required fields:** +- Title (from summary in step 5) +- Body (from summary in step 5) +- Issue type (from step 6) +- Project / Board (from step 6) + +**Optional fields (set if resolved):** +- Sprint (from step 6) +- Assignee (from step 6) +- Labels (from step 6) +- Priority (from arc priority, via cache map) + +**Example — Jira via Pi's MCP gateway:** +```text +mcp({ search: "Jira create issue" }) +mcp({ + tool: "<discovered-create-tool>", + args: '{"project":"BT","type":"Story","summary":"OpenCode CLI: Installation Guide","description":"<summarized markdown>","sprint":"<resolved-sprint-id>","assignee":"<resolved-account-id>","labels":["doc","cli"]}' +}) +``` + +Use the exact schema returned by `mcp({ describe: "<discovered-create-tool>" })`; the fields above are illustrative, not a raw function call. + +**Example — GitHub via gh CLI:** +```bash +gh issue create \ + --repo owner/repo \ + --title "OpenCode CLI: Installation Guide" \ + --body "<summarized markdown>" \ + --label "doc,cli" \ + --assignee ben-firestone +# Returns: tracker URL +``` + +Record the created **tracker key** (e.g., `BT-3014`) and **tracker URL** for the next step. + +### 9. Backlink Both Directions + +**Arc → Tracker:** Stamp the new tracker key onto the arc issue. + +Preserve the current Arc description mechanically: write it to a temporary file, append only the backlink, then pipe the file back through `--stdin`. Never retype the existing body through the model: + +```bash +TMP=$(mktemp) +arc show <arc-id> --json | jq -j .description > "$TMP" +cat >> "$TMP" <<'EOF' + +--- +**Tracker:** [BT-3014](https://bactrack.atlassian.net/browse/BT-3014) +EOF +arc update <arc-id> --stdin < "$TMP" +rm -f "$TMP" +``` + +**Caution:** `arc update --description`/`--stdin` REPLACES the whole description — never pass only the link, or you erase the issue body. + +Or use an arc field if one exists for tracker links. + +**Tracker → Arc:** Stamp the arc ID and link into the created tracker issue. + +``` +**Origin:** arc issue agentmarke-0qex.04hl1w + +Include an Arc URL only when `arc show` or project configuration provides a canonical base URL; never fabricate a host. +``` + +Or use the tracker's "Linked Issues" / "Related" feature if available. + +### 10. Verify — Non-Negotiable + +**Re-read both records.** Fetch the updated Arc issue and confirm its complete prior body plus the new tracker backlink remain present. Then re-read the created external issue and confirm its Arc origin plus sprint, labels, and assignee landed. + +**Why this is non-negotiable — the create *response* is not proof.** The fields most likely to matter (sprint, labels) are routinely **absent from the create call's response payload even when they were applied correctly**. Trusting the response would falsely report a failure — and a naive "fix" would double-apply the field. Only a fresh read of the issue is ground truth. + +1. **Fetch the created issue, requesting the flaky fields by name.** Default field sets often omit custom fields (e.g. the sprint custom field), so ask for them explicitly: + ```bash + # Jira: re-read with an explicit field list, e.g. fields=[summary, issuetype, + # assignee, labels, customfield_XXXXX] (the sprint custom field id) + # Linear: query the issue's cycle / labels / assignee fields explicitly + # GitHub: gh issue view <number> --json labels,assignees,milestone + ``` + +2. **For each known-flaky field (sprint, labels, assignee), compare requested vs. landed:** + ``` + Requested sprint: "Sprint 47" + Landed sprint: null ← DISCREPANCY + ``` + +3. **Report every discrepancy:** + ``` + Warning: Sprint did not land on create. Attempted fix and re-verify. + ``` + +4. **Attempt one auto-fix-and-re-verify:** + - If sprint is missing, call update to set it, then re-read. + - If labels are missing, call update to set them, then re-read. + - If assignee is missing, call update to set it, then re-read. + +5. **If a discrepancy survives the single retry, surface it clearly to the user:** + ``` + Warning: BT-3014 — Sprint "Sprint 48" failed to land even after retry. + Please set it by hand in Jira, or check your project's sprint configuration. + ``` + Include the tracker key so the user can fix it manually. Do not loop; move on. + +## Cache Schema — Explicit Contract + +The cache file `~/.arc/tracker-map.yaml` is the durable interface between runs. It must tolerate missing files, missing entries, and missing keys (all gracefully fall back to discover-then-cache). + +```yaml +# ~/.arc/tracker-map.yaml +trackers: + jira:bactrack.atlassian.net: + instance: # facts — expensive to rediscover, applied automatically + cloud_id: <uuid> + sprint_field: customfield_10020 # found via JQL probe + issue_type_map: # createmeta probe to resolve; ids are instance-specific + feature: { name: Story, id: "10001" } + bug: { name: Bug, id: "10004" } + task: { name: Task, id: "10002" } + chore: { name: Task, id: "10002" } + priority_map: { 0: Highest, 1: High, 2: Medium, 3: Low, 4: Lowest } + assignee_aliases: { "ben firestone": <accountId> } + prefs: # remembered choices — only pre-select a confirmable prompt + last_project: BT + # no sprint here — "current sprint" is resolved live each run + linear:linear.app: + instance: + team_id: <uuid> + issue_type_map: + feature: { name: Feature, id: "..." } + bug: { name: Bug, id: "..." } + priority_map: { 0: Urgent, 1: High, 2: Medium, 3: Low, 4: Backlog } + assignee_aliases: { "ben firestone": <user-id> } + prefs: + last_project: Arc + github:github.com: + instance: + owner: bfirestone + repo: agent-marketplace + prefs: {} +``` + +**Rules:** +- Create the file on first run if it does not exist. +- If an entry for `<tracker>:<instance>` does not exist, discover and create it. +- If keys are missing within an entry, probe live and fill in. +- `instance` keys are applied automatically; `prefs` keys are used only to pre-select prompts. + +## Scope (v1) + +- **In scope:** One-way summarized snapshot, field mapping, backlinks, verification. +- **Out of scope:** Two-way status/comment sync, bulk export, scheduled mirroring. + +Two-way sync is deferred to a future iteration. + +## Rules + +- Never paste the full arc issue verbatim. Summarize lossy. +- Always detect capability by reasoning over connected tools, not a hardcoded lookup. +- Never create a ticket if the required tool is not connected. Stop and guide the user. +- Always ask for ambiguous fields (issue type, assignee, project). Never guess. +- Resolve "current sprint" live every run; never use a cached sprint ID. +- Always verify the created issue. Report and attempt one auto-fix for flaky fields (sprint, labels, assignee). +- Cache discovered constants to `~/.arc/tracker-map.yaml` to skip probes on the next run. +- Format all arc content (descriptions, comments) per `skills/arc/_formatting.md`. diff --git a/packages/pi-arc/skills/arc-verify/SKILL.md b/packages/pi-arc/skills/arc-verify/SKILL.md index d51176e..6718367 100644 --- a/packages/pi-arc/skills/arc-verify/SKILL.md +++ b/packages/pi-arc/skills/arc-verify/SKILL.md @@ -71,7 +71,7 @@ arc close <id> -r "Verified: <evidence summary>" ``` If verification **fails**, do NOT close the issue. Instead: -- Return to `implement` to fix the failure +- Return to `build` to fix the failure - Or invoke `debug` if the failure is unexpected ## Rules @@ -79,5 +79,5 @@ If verification **fails**, do NOT close the issue. Instead: - Never close an arc issue without fresh verification evidence - Never claim completion without running the proof command - Never trust cached or remembered results — run it fresh -- After verification, proceed to `finish` (session end) or back to `implement` (next task) +- After verification, proceed to `finish` (session end) or back to `build` (next task) - Format all arc content (descriptions, plans, comments) per `skills/arc/_formatting.md` diff --git a/packages/pi-arc/skills/arc/SKILL.md b/packages/pi-arc/skills/arc/SKILL.md index 943eb7b..60e55a3 100644 --- a/packages/pi-arc/skills/arc/SKILL.md +++ b/packages/pi-arc/skills/arc/SKILL.md @@ -22,7 +22,7 @@ arc init # Initialize project ``` The plugin is the single source of truth for Pi integration. It provides: -- **SessionStart/PreCompact hooks** - runs `arc prime` automatically +- **Pi extension session-start and before-compaction handlers** - runs `arc prime` automatically - **Prompt configuration** - reminds Pi to run `arc onboard` - **Skills and resources** - detailed guides and reference - **Agents** - for bulk operations @@ -48,7 +48,7 @@ Arc includes workflow skills that guide you through the development lifecycle wi |-------|---------|-------------| | `brainstorm` | Design discovery through Socratic dialogue | Starting new features or significant work | | `plan` | Break design into implementation tasks | After brainstorm approves a design | -| `implement` | TDD execution via fresh subagents per task | Ready to implement planned tasks | +| `build` | TDD execution via fresh subagents per task | Ready to implement planned tasks | | `debug` | 4-phase root cause investigation | Encountering bugs or test failures | | `verify` | Evidence-based completion gates | Before claiming any work is done | | `review` | Code review dispatch and triage | After implementing a task | @@ -57,7 +57,7 @@ Arc includes workflow skills that guide you through the development lifecycle wi ### Pipeline ``` -brainstorm → plan → implement (per task) → review → finish +brainstorm → plan → build (per task) → review → finish ↕ ↕ debug verify ``` @@ -65,8 +65,8 @@ brainstorm → plan → implement (per task) → review → finish ### Execution Paths After `plan`, choose: -- **Single-agent + subagents**: Invoke `implement`. Main agent orchestrates, subagents do TDD. Best for sequential tasks. -- **Parallel Arc build**: For independent task batches, `implement` can use worktree-isolated `pi-subagents` runs when an external `pi-subagents` extension/tool is installed and Arc specialist definitions are available. Custom Arc specialists remain the preferred `pi-subagents` targets, and generic `worker`/`reviewer` agents should not be substituted for Arc gates. This is not Claude-style team deployment; the orchestrator still owns verification, patch application, issue closure, and handoff. +- **Single-agent + subagents**: Invoke `build`. Main agent orchestrates, subagents do TDD. Best for sequential tasks. +- **Parallel Arc build**: For independent task batches, `build` can use worktree-isolated `pi-subagents` runs when an external `pi-subagents` extension/tool is installed and Arc specialist definitions are available. Custom Arc specialists remain the preferred `pi-subagents` targets, and generic `worker`/`reviewer` agents should not be substituted for Arc gates. This is not Claude-style team deployment; the orchestrator still owns verification, patch application, issue closure, and handoff. - **Stacked PRs (arc + git-spice)**: When the epic is 3+ tasks with linear dependencies and each task is independently reviewable, ship as a stack of PRs instead of one. See [`STACKING.md`](../../STACKING.md) for the integration playbook (concept mapping, per-task loop, review iteration). ## Quick Start @@ -86,7 +86,7 @@ Run `arc prime` for full workflow context, or `arc <command> --help` for specifi - `arc close` - Complete work - `arc show` - View details - `arc dep` - Manage dependencies -- `arc share` - Manage encrypted plan shares (create, show, approve, comments, pull, list, update, delete) +- `arc plan` - Manage design plan reviews on the planner (create, show, approve, comments) - `arc which` - Show active project and resolution source - `arc paths` - Manage workspace path registrations - `arc project` - Manage projects (list, create, delete, rename, merge) @@ -125,7 +125,7 @@ Run `arc docs` without a topic to see an overview. ## Agent Mode -For bulk operations (creating epics with tasks, batch updates), use the **issue-manager** agent via the Task tool. This runs arc commands without consuming main conversation context. +For bulk operations (creating epics with tasks, batch updates), use the **issue-manager** agent through the auto-materialized `arc-issue-manager` pi-subagent when available, or the bundled `arc_agent` fallback. This runs arc commands without consuming main conversation context. ## Dependency Types @@ -142,47 +142,30 @@ Arc supports four dependency types: ## Design Reviews -Design docs live in `docs/plans/` as filesystem markdown. Arc registers them on one of three review surfaces, chosen at create time by the `/arc-brainstorm` skill based on who's reviewing and whether encryption is needed. Each surface has its own CLI verb set; `/arc-plan` and any other consumer reads line 1 of the doc — `<!-- arc-review: kind=<legacy|share-local|share-remote> id=<id> -->` — to know which CLI to call. +Design docs live in `docs/plans/` as filesystem markdown. Arc registers them on the planner — a plain-HTTP review surface — at create time via the `/arc-brainstorm` skill. `/arc-plan` and any other consumer reads line 1 of the doc — `<!-- arc-review: id=<id> -->` — to get the plan ID. -**Surfaces:** +**Surface:** -| `kind` | Create command | URL pattern | Encrypted? | Best for | -|---|---|---|---|---| -| `legacy` | `arc plan create <file>` | `http://localhost:7432/planner/<id>` | no | Solo, plain HTTP, simplest comment thread | -| `share-local` | `arc share create <file>` | `http://localhost:7432/share/<id>#k=<key>` | yes | Solo, but want annotations + accept-resolve UI | -| `share-remote` | `arc share create <file> --remote` | `<remote>/share/<id>#k=<key>` (default `https://arcplanner.sentiolabs.io`) | yes | Reviewers on other machines | +| Create command | URL pattern | Best for | +|---|---|---| +| `arc plan create --no-frontmatter <file>` | `http://localhost:7432/planner/<id>` | Design review via a markdown render with a comment thread | -`arc share create --server <url>` overrides `--remote` to target an explicit server. +The planner is plain HTTP with no encryption, edit tokens, or keys to manage; the URL is just the planner path. -For the encrypted surfaces, the author's edit tokens live in the arc-server's local keyring (a `shares` table in `~/.arc/data.db`) — multi-client accessible via `/api/v1/shares`, never written to disk as JSON. Legacy plans don't have edit tokens; the URL is just the planner path. - -### `arc share` commands (share-local, share-remote) - -| Command | Purpose | -|---------|---------| -| `arc share create <file-path> [--remote]` | Encrypt a plan and create a share, returns share ID. Default is local; `--remote` targets the configured share server. Output prints a single URL: `Preview URL` (local) or `Author URL` (shared) — the reviewer URL is obtained from the in-page **Share link** button on the share page header, not the CLI. | -| `arc share show <id>` | Decrypt and print plan content (use `--author-url` to reprint the Author URL) | -| `arc share approve <id>` | Mark the share as approved | -| `arc share comments <id>` | All review comments + statuses | -| `arc share pull <id>` | Accepted-only comments (the agent-input form) | -| `arc share list` | List shares known to this machine (incl. `plan_file` mapping). Add `--json` for `[{id, kind, url, key_b64url, plan_file, created_at}]` — pipe to `jq` to look up a share's local file path | -| `arc share update <id> <plan-file>` | Replace the encrypted plan content (in-place; ID stays stable) | -| `arc share delete <id>` | Delete a share (`--force` cleans up local keyring entries when the server is already gone) | - -### `arc plan` commands (legacy) +### `arc plan` commands | Command | Purpose | |---------|---------| -| `arc plan create <file-path>` | Register a plan on the legacy `/planner/<id>` surface (plain HTTP, no encryption). There's no in-place update — re-running `create` produces a new ID. | +| `arc plan create --no-frontmatter <file-path>` | Register a plan on the `/planner/<id>` surface (plain HTTP). There's no in-place update — re-running `create` produces a new ID. | | `arc plan show <id>` | Print plan metadata + content (the metadata header includes `File: <path>`, useful for plan-file lookups) | | `arc plan approve <id>` | Mark the plan as approved | | `arc plan comments <id>` | List comments on the plan (flat thread; no Accept/Resolve/Reject states) | ### Review cycle -create → reviewers leave annotations → author Accepts/Resolves/Rejects (encrypted surfaces) or replies inline (legacy) → `arc share pull` surfaces accepted comments to the implementation flow (legacy reads the comments thread inline since it has no accepted-only filter). Approved design content is written into the epic's description field when creating implementation tasks. Run `arc docs plans` for full details. +create → reviewers leave comments → author reads the thread inline and decides which to act on → approved design content is written into the epic's description field when creating implementation tasks. Run `arc docs plans` for full details. -The `<!-- arc-review: kind=… id=… -->` marker on line 1 of every registered design doc tells downstream skills which CLI table above to use. See `skills/arc-brainstorm/SKILL.md` step 6 for the marker-write contract and `skills/arc-plan/SKILL.md` step 1 for the read pattern. +The `<!-- arc-review: id=… -->` marker on line 1 of every registered design doc gives downstream skills the plan ID for the CLI calls above. See `skills/arc-brainstorm/SKILL.md` step 6 for the marker-write contract and `skills/arc-plan/SKILL.md` step 1 for the read pattern. ## Labels diff --git a/packages/pi-arc/tests/arc-model-profiles-contract.test.mjs b/packages/pi-arc/tests/arc-model-profiles-contract.test.mjs index 7420f69..364e859 100644 --- a/packages/pi-arc/tests/arc-model-profiles-contract.test.mjs +++ b/packages/pi-arc/tests/arc-model-profiles-contract.test.mjs @@ -8,7 +8,7 @@ function read(path) { test('arc model profile contract defines stable profile keys and config shape', () => { const source = read('extensions/arc/model-profiles.ts'); - for (const key of ['brainstorm', 'plan', 'issueManager', 'builder', 'codeReviewer', 'docWriter', 'specReviewer', 'evaluator']) { + for (const key of ['brainstorm', 'plan', 'issueManager', 'builder', 'devopsBuilder', 'codeReviewer', 'docWriter', 'specReviewer', 'evaluator']) { assert.match(source, new RegExp(`"${key}"`)); } assert.match(source, /export interface ArcModelsConfig/); diff --git a/packages/pi-arc/tests/arc-model-profiles-integration.test.mjs b/packages/pi-arc/tests/arc-model-profiles-integration.test.mjs index 10d477e..464abc8 100644 --- a/packages/pi-arc/tests/arc-model-profiles-integration.test.mjs +++ b/packages/pi-arc/tests/arc-model-profiles-integration.test.mjs @@ -23,17 +23,18 @@ function extractFunctionBlock(source, functionName, stopToken) { } const EXPECTED_RECOMMENDATIONS = [ - ['brainstorm', 'gpt-5.5', 'high', 'design exploration and architecture judgment'], - ['plan', 'gpt-5.5', 'high', 'task breakdown and sequencing'], - ['issueManager', 'gpt-5.4-mini', 'off', 'Arc CLI formatting and issue updates'], - ['builder', 'gpt-5.3-codex', 'medium', 'implementation and code navigation'], - ['codeReviewer', 'gpt-5.5', 'high', 'review judgment and risk detection'], - ['docWriter', 'gpt-5.4-mini', 'low', 'documentation prose and light reasoning'], - ['specReviewer', 'gpt-5.5', 'high', 'spec compliance and ambiguity detection'], - ['evaluator', 'gpt-5.5', 'high', 'adversarial validation'], + ['brainstorm', 'gpt-5.6-sol', 'high', 'design exploration and architecture judgment'], + ['plan', 'gpt-5.6-sol', 'high', 'task breakdown and sequencing'], + ['issueManager', 'gpt-5.6-luna', 'off', 'Arc CLI formatting and issue updates'], + ['builder', 'gpt-5.6-terra', 'medium', 'implementation and code navigation'], + ['devopsBuilder', 'gpt-5.6-sol', 'high', 'live-system operations and blast-radius judgment'], + ['codeReviewer', 'gpt-5.6-sol', 'high', 'review judgment and risk detection'], + ['docWriter', 'gpt-5.6-luna', 'low', 'documentation prose and light reasoning'], + ['specReviewer', 'gpt-5.6-sol', 'high', 'spec compliance and ambiguity detection'], + ['evaluator', 'gpt-5.6-sol', 'high', 'adversarial validation'], ]; -const ALLOWED_RECOMMENDED_MODEL_IDS = new Set(['gpt-5.5', 'gpt-5.4-mini', 'gpt-5.3-codex']); +const ALLOWED_RECOMMENDED_MODEL_IDS = new Set(['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna']); test('arc extension wires model profiles into commands and agent dispatch', () => { const source = read('extensions/arc.ts'); @@ -46,6 +47,7 @@ test('arc extension wires model profiles into commands and agent dispatch', () = 'resolveArcModelProfile', 'ARC_AGENT_PROFILE_KEYS', 'builder', + 'devopsBuilder', 'codeReviewer', 'docWriter', 'evaluator', @@ -132,20 +134,20 @@ test('README modelProfiles example stays within the recommended model set', () = const end = source.indexOf('## Sync Arc specialists', start); assert.notEqual(end, -1, 'missing next README section'); const section = source.slice(start, end); - assert.doesNotMatch(section, /gpt-5\.4-(?!mini\b)[a-z0-9-]+|gpt-5\.1|claude|haiku|opus|sonnet/i); - assert.match(section, /openai-codex\/gpt-5\.5/); - assert.match(section, /openai-codex\/gpt-5\.4-mini/); - assert.match(section, /openai-codex\/gpt-5\.3-codex/); + assert.doesNotMatch(section, /gpt-5\.[1-5](?:\b|[-.])|claude|haiku|opus|sonnet/i); + assert.match(section, /openai-codex\/gpt-5\.6-sol/); + assert.match(section, /openai-codex\/gpt-5\.6-terra/); + assert.match(section, /openai-codex\/gpt-5\.6-luna/); }); -test('issue-manager docs recommend gpt-5.4-mini while preserving legacy fallback guidance', () => { +test('issue-manager docs recommend gpt-5.6-luna while preserving legacy fallback guidance', () => { const readme = read('README.md'); - assert.match(readme, /issueManager profile \(recommended gpt-5\.4-mini with thinking off\)/); + assert.match(readme, /issueManager profile \(recommended gpt-5\.6-luna with thinking off\)/); assert.doesNotMatch(readme, /issueManager profile \(nano tier, thinking off\)/); const source = read('skills/arc-plan/SKILL.md'); assert.match(source, /issueManager profile/); - assert.match(source, /gpt-5\.4-mini with thinking off/); + assert.match(source, /gpt-5\.6-luna with thinking off/); assert.match(source, /defaults to `nano`/); assert.match(source, /legacy tier\/frontmatter behavior/); assert.doesNotMatch(source, /recommended profile uses a nano-tier model with thinking off/); diff --git a/packages/pi-arc/tests/arc-model-profiles-ui.test.mjs b/packages/pi-arc/tests/arc-model-profiles-ui.test.mjs index 433c32e..363e3b1 100644 --- a/packages/pi-arc/tests/arc-model-profiles-ui.test.mjs +++ b/packages/pi-arc/tests/arc-model-profiles-ui.test.mjs @@ -23,17 +23,18 @@ function extractFunctionBlock(source, functionName, stopToken) { } const EXPECTED_RECOMMENDATIONS = [ - ['brainstorm', 'gpt-5.5', 'high', 'design exploration and architecture judgment'], - ['plan', 'gpt-5.5', 'high', 'task breakdown and sequencing'], - ['issueManager', 'gpt-5.4-mini', 'off', 'Arc CLI formatting and issue updates'], - ['builder', 'gpt-5.3-codex', 'medium', 'implementation and code navigation'], - ['codeReviewer', 'gpt-5.5', 'high', 'review judgment and risk detection'], - ['docWriter', 'gpt-5.4-mini', 'low', 'documentation prose and light reasoning'], - ['specReviewer', 'gpt-5.5', 'high', 'spec compliance and ambiguity detection'], - ['evaluator', 'gpt-5.5', 'high', 'adversarial validation'], + ['brainstorm', 'gpt-5.6-sol', 'high', 'design exploration and architecture judgment'], + ['plan', 'gpt-5.6-sol', 'high', 'task breakdown and sequencing'], + ['issueManager', 'gpt-5.6-luna', 'off', 'Arc CLI formatting and issue updates'], + ['builder', 'gpt-5.6-terra', 'medium', 'implementation and code navigation'], + ['devopsBuilder', 'gpt-5.6-sol', 'high', 'live-system operations and blast-radius judgment'], + ['codeReviewer', 'gpt-5.6-sol', 'high', 'review judgment and risk detection'], + ['docWriter', 'gpt-5.6-luna', 'low', 'documentation prose and light reasoning'], + ['specReviewer', 'gpt-5.6-sol', 'high', 'spec compliance and ambiguity detection'], + ['evaluator', 'gpt-5.6-sol', 'high', 'adversarial validation'], ]; -const ALLOWED_RECOMMENDED_MODEL_IDS = new Set(['gpt-5.5', 'gpt-5.4-mini', 'gpt-5.3-codex']); +const ALLOWED_RECOMMENDED_MODEL_IDS = new Set(['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna']); test('arc model profiles UI exports the editor entrypoint and section-style labels', () => { const source = read('extensions/arc/model-profiles-ui.ts'); diff --git a/packages/pi-arc/tests/arc-source-sync.test.mjs b/packages/pi-arc/tests/arc-source-sync.test.mjs index 3781851..412b4dc 100644 --- a/packages/pi-arc/tests/arc-source-sync.test.mjs +++ b/packages/pi-arc/tests/arc-source-sync.test.mjs @@ -1,5 +1,7 @@ import { execFileSync } from 'node:child_process'; -import { existsSync, readFileSync } from 'node:fs'; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; import { test } from 'node:test'; import assert from 'node:assert/strict'; @@ -18,6 +20,12 @@ test('migration script validates source before rewriting resources', () => { const source = read('scripts/migrate-arc-plugin.py'); assert.match(source, /import argparse/); assert.match(source, /expanduser\(\)\.resolve\(\)/); + assert.match(source, /DEFAULT_SOURCE_CANDIDATES/); + assert.match(source, /bfirestone\/agent-marketplace\/claude-marketplace\/plugins\/arc/); + assert.match(source, /tempfile\.mkdtemp/); + assert.match(source, /install_generated_resources/); + assert.match(source, /REPO_ROOT\.parents\[1\]\.parent \/ "agent-nexus\/claude-marketplace\/plugins\/arc"/); + assert.match(source, /Path\.home\(\) \/ "devspace\/personal\/sentiolabs\/agent-nexus\/claude-marketplace\/plugins\/arc"/); assert.match(source, /def validate_source/); assert.match(source, /"commands"/); assert.match(source, /"skills"/); @@ -51,6 +59,23 @@ test('arc-source-sync codifies reproducible Pi adaptation loop', () => { assert.match(source, /Do not tell the user "ready to push"/); }); +test('failed regeneration leaves installed resources untouched', () => { + const fixture = mkdtempSync(path.join(tmpdir(), 'pi-arc-invalid-source-')); + const protectedPath = 'skills/arc/SKILL.md'; + const before = read(protectedPath); + try { + for (const directory of ['commands', 'skills', 'agents', '.claude-plugin']) { + mkdirSync(path.join(fixture, directory), { recursive: true }); + } + writeFileSync(path.join(fixture, '.claude-plugin', 'plugin.json'), '{}\n'); + + assert.throws(() => execFileSync('python3', ['scripts/migrate-arc-plugin.py', fixture], { encoding: 'utf8', stdio: 'pipe' })); + assert.equal(read(protectedPath), before); + } finally { + rmSync(fixture, { recursive: true, force: true }); + } +}); + test('migration script excludes upstream eval fixtures without preserving package-local maintainer skills', () => { const source = read('scripts/migrate-arc-plugin.py'); assert.match(source, /PI_LOCAL_SKILL_DIRS = set\(\)/); @@ -87,9 +112,10 @@ test('README documents repo-local maintainer source sync', () => { assert.match(source, /python3 scripts\/migrate-arc-plugin\.py --source ~\/foo\/bar\/arc/); }); -test('README documents arc share review surfaces', () => { +test('README documents the planner-only review surface', () => { const source = read('README.md'); - assert.match(source, /Plan review surfaces/); - assert.match(source, /arc share create <file> --remote/); - assert.match(source, /arc-review: kind=share-remote id=<id>/); + assert.match(source, /Plan review surface/); + assert.match(source, /arc plan create --no-frontmatter <file>/); + assert.match(source, /arc-review: id=<id>/); + assert.doesNotMatch(source, /arc share create|share-local|share-remote|kind=legacy/); }); diff --git a/packages/pi-arc/tests/arc-subagents-auto-materialization.test.mjs b/packages/pi-arc/tests/arc-subagents-auto-materialization.test.mjs index f7d8431..acf2d46 100644 --- a/packages/pi-arc/tests/arc-subagents-auto-materialization.test.mjs +++ b/packages/pi-arc/tests/arc-subagents-auto-materialization.test.mjs @@ -15,7 +15,7 @@ function renderTestAgent(mod, source, target, modelsConfigHash = 'models-hash') sourceName: source, sourceMarkdown: `# ${source}`, parsedSource: { prompt: `# ${source}` }, - resolvedModel: 'openai-codex/gpt-5.4-mini', + resolvedModel: 'openai-codex/gpt-5.6-terra', modelProfileKey: 'builder', modelResolutionSource: 'test', modelsConfigHash, @@ -210,6 +210,7 @@ test('Arc source agents document optional supervisor escalation without bundling for (const file of [ 'agents/builder.md', 'agents/code-reviewer.md', + 'agents/devops-builder.md', 'agents/doc-writer.md', 'agents/evaluator.md', 'agents/issue-manager.md', @@ -237,7 +238,7 @@ test('Arc subagent markdown render runtime output matches expected structure', a description: 'Use this agent when creating issues. This includes: epics, tasks, labels.', tools: ['bash', 'read', 'grep'], }, - resolvedModel: 'openai-codex/gpt-5.4-mini', + resolvedModel: 'openai-codex/gpt-5.6-luna', modelProfileKey: 'issueManager', modelResolutionSource: 'profile', modelsConfigHash: 'abc123', @@ -246,7 +247,7 @@ test('Arc subagent markdown render runtime output matches expected structure', a assert.ok(output.startsWith('---\nname: arc-issue-manager')); assert.ok(output.includes('description: "Use this agent when creating issues. This includes: epics, tasks, labels."')); - assert.ok(output.includes('model: openai-codex/gpt-5.4-mini')); + assert.ok(output.includes('model: openai-codex/gpt-5.6-luna')); assert.ok(output.includes('tools: bash, read, grep')); assert.ok(output.includes('systemPromptMode: replace')); assert.ok(output.includes('inheritProjectContext: true')); diff --git a/packages/pi-arc/tests/arc-subagents-sync.test.mjs b/packages/pi-arc/tests/arc-subagents-sync.test.mjs index 714ff04..72199c5 100644 --- a/packages/pi-arc/tests/arc-subagents-sync.test.mjs +++ b/packages/pi-arc/tests/arc-subagents-sync.test.mjs @@ -28,6 +28,7 @@ test('arc extension sync map includes all Arc specialists', () => { const source = read('extensions/arc.ts'); for (const name of [ 'arc-builder', + 'arc-devops-builder', 'arc-doc-writer', 'arc-spec-reviewer', 'arc-code-reviewer', @@ -42,7 +43,7 @@ test('arc extension sync map includes all Arc specialists', () => { test('arc extension model tiers include nano', () => { const source = read('extensions/arc.ts'); assert.match(source, /type ArcModelTier = "nano" \| "small" \| "standard" \| "large"/); - assert.match(source, /nano: "openai-codex\/gpt-5\.4-mini"/); + assert.match(source, /nano: "openai-codex\/gpt-5\.6-luna"/); assert.match(source, /nano for bulk CLI issue creation/); }); @@ -59,6 +60,8 @@ test('arc-build skill references arc-subagents-sync and async pi-subagents worke const source = read('skills/arc-build/SKILL.md'); assert.match(source, /\/arc-subagents-sync/); assert.match(source, /arc-builder/); + assert.match(source, /arc-devops-builder/); + assert.match(source, /devopsBuilder/); assert.match(source, /async: true/); assert.match(source, /clarify: false/); assert.match(source, /subagent\(\{ action: "status", id: "<run-id>" \}\)/); diff --git a/packages/pi-arc/tests/ask-user-question-package.test.mjs b/packages/pi-arc/tests/ask-user-question-package.test.mjs index 5d7e364..6ea5557 100644 --- a/packages/pi-arc/tests/ask-user-question-package.test.mjs +++ b/packages/pi-arc/tests/ask-user-question-package.test.mjs @@ -42,10 +42,11 @@ test('docs teach rpiv ask_user_question schema and escape hatches', () => { const readme = read('README.md'); const brainstorm = read('skills/arc-brainstorm/SKILL.md'); const plan = read('skills/arc-plan/SKILL.md'); + const summarize = read('skills/arc-summarize/SKILL.md'); const sourceSync = read('../../.pi/skills/arc-source-sync/SKILL.md'); const migrate = read('scripts/migrate-arc-plugin.py'); - for (const source of [readme, brainstorm, plan, sourceSync, migrate]) { + for (const source of [readme, brainstorm, plan, summarize, sourceSync, migrate]) { assert.match(source, /questions\[\]/); } @@ -56,8 +57,12 @@ test('docs teach rpiv ask_user_question schema and escape hatches', () => { assert.match(source, /\(Recommended\)/); } - for (const source of [brainstorm, plan]) { + for (const source of [brainstorm, plan, summarize]) { assert.doesNotMatch(source, /^Question:\s*"/m); assert.doesNotMatch(source, /^Options:/m); } + + assert.match(summarize, /@juicesharp\/rpiv-ask-user-question/); + assert.doesNotMatch(summarize, /`ask_user_question`:\s*$/m); + assert.doesNotMatch(summarize, /label:\s*"(?:Other|Next|Type something\.|Chat about this)"/); }); diff --git a/packages/pi-arc/tests/upstream-workflow-contracts.test.mjs b/packages/pi-arc/tests/upstream-workflow-contracts.test.mjs new file mode 100644 index 0000000..3fc7620 --- /dev/null +++ b/packages/pi-arc/tests/upstream-workflow-contracts.test.mjs @@ -0,0 +1,171 @@ +import { existsSync, readFileSync } from 'node:fs'; +import { test } from 'node:test'; +import assert from 'node:assert/strict'; + +function read(path) { + return readFileSync(path, 'utf8'); +} + +test('planner-only review contract is consistent across Arc workflow skills', () => { + for (const path of [ + 'skills/arc/SKILL.md', + 'skills/arc-brainstorm/SKILL.md', + 'skills/arc-plan/SKILL.md', + ]) { + const source = read(path); + assert.match(source, /arc plan/); + assert.doesNotMatch(source, /arc share|share-local|share-remote|kind=legacy/); + } + + const brainstorm = read('skills/arc-brainstorm/SKILL.md'); + const plan = read('skills/arc-plan/SKILL.md'); + assert.match(brainstorm, /<!-- arc-review: id=<id> -->/); + assert.match(plan, /<!-- arc-review: id=<id> -->/); + assert.match(brainstorm, /arc plan create --no-frontmatter/); + assert.doesNotMatch(brainstorm, /arc plan create (?!--no-frontmatter)/); + const combined = [read('skills/arc/SKILL.md'), brainstorm, plan].join('\n'); + assert.doesNotMatch(combined, /arc:(?:plan|build)|arc plan create (?!--no-frontmatter)/); +}); + +test('GPT-5.6 variants map onto Arc model tiers and role profiles', () => { + const extension = read('extensions/arc.ts'); + const readme = read('README.md'); + + assert.match(extension, /nano: "openai-codex\/gpt-5\.6-luna"/); + assert.match(extension, /small: "openai-codex\/gpt-5\.6-luna"/); + assert.match(extension, /standard: "openai-codex\/gpt-5\.6-terra"/); + assert.match(extension, /large: "openai-codex\/gpt-5\.6-sol"/); + assert.match(readme, /Luna for fast\/affordable `nano` and `small` work/); + assert.match(readme, /Terra for balanced `standard` implementation/); + assert.match(readme, /Sol for `large` design, operations, review, and adversarial evaluation/); +}); + +test('general Arc reference uses Pi-native lifecycle and dispatch wording', () => { + const source = read('skills/arc/SKILL.md'); + assert.match(source, /Pi extension session-start and before-compaction handlers/); + assert.match(source, /auto-materialized `arc-issue-manager` pi-subagent/); + assert.match(source, /bundled `arc_agent` fallback/); + assert.doesNotMatch(source, /SessionStart\/PreCompact hooks|via the Task tool/); +}); + +test('success criteria flow from brainstorm through epic completion', () => { + const brainstorm = read('skills/arc-brainstorm/SKILL.md'); + const plan = read('skills/arc-plan/SKILL.md'); + const build = read('skills/arc-build/SKILL.md'); + + assert.match(brainstorm, /## Success Criteria/); + assert.match(plan, /Success-criteria coverage/); + assert.match(plan, /## Expected Outcome/); + assert.match(build, /Completion Gate/); + assert.match(build, /select\(\.status != "closed"\)/); + assert.match(build, /Any `open`, `in_progress`, `blocked`, or `deferred` child keeps the epic open/); + assert.match(build, /For a standalone task/); + assert.match(build, /For DevOps-only epics/); + assert.match(build, /Success Criteria/); +}); + +test('DevOps tasks use dedicated Pi-native specialist and safety resources', () => { + for (const path of [ + 'agents/devops-builder.md', + 'skills/arc-build/devops-builder-prompt.md', + 'skills/arc-build/references/devops-patterns.md', + ]) { + assert.equal(existsSync(path), true, `missing ${path}`); + } + + const agent = read('agents/devops-builder.md'); + const prompt = read('skills/arc-build/devops-builder-prompt.md'); + const plan = read('skills/arc-plan/SKILL.md'); + const build = read('skills/arc-build/SKILL.md'); + const extension = read('extensions/arc.ts'); + const subagents = read('extensions/arc/subagents.ts'); + + assert.match(agent, /^model:\s*large$/m); + assert.match(agent, /NO MUTATION WITHOUT A DRY-RUN PREVIEW AND A ROLLBACK PATH/); + assert.match(agent, /## Supervisor Escalation/); + assert.doesNotMatch(agent, /skills\/build\//); + assert.doesNotMatch(prompt, /skills\/build\//); + assert.match(prompt, /skills\/arc-build\/references\/devops-patterns\.md/); + const patterns = read('skills/arc-build/references/devops-patterns.md'); + assert.match(patterns, /helm diff upgrade/); + assert.match(patterns, /STOP and require explicit authorization plus a verified recovery procedure/); + assert.doesNotMatch(plan, /Upgrade the staging cluster control plane from 1\.28 to 1\.29/); + assert.match(plan, /helm rollback payments/); + + for (const heading of ['## Target', '## Safeguards', '## Verification', '## Rollback']) { + assert.match(plan, new RegExp(heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); + } + assert.match(plan, /Labels: devops/); + assert.match(build, /arc-devops-builder/); + assert.match(build, /arc_agent\(agent="devops-builder", task="<filled prompt>"\)/); + assert.doesNotMatch(build, /arc_agent\(agent="devops-builder"[^\n]*model=/); + assert.match(build, /devopsBuilder/); + assert.match(build, /never include live-system operations tasks in a parallel patch batch/i); + assert.match(build, /No task has a `devops` label or any live-system mutation scope/); + assert.match(plan, /Never place a `devops` task or other live-system mutation in a parallel batch/); + assert.match(extension, /"devops-builder"/); + assert.match(subagents, /target: "arc-devops-builder", profileKey: "devopsBuilder"/); +}); + +test('issue-manager preserves canonical descriptions and phased ordering', () => { + const source = read('agents/issue-manager.md'); + const plan = read('skills/arc-plan/SKILL.md'); + assert.match(source, /Never summarize, trim, or paraphrase/i); + assert.match(source, /--stdin < "\/path\/to\/T1\.md"/); + assert.match(source, /Create the epic first/); + assert.match(source, /Create all child tasks/); + assert.match(source, /Apply dependencies only after all child IDs exist/); + assert.match(source, /Apply labels after dependencies/); + assert.match(source, /arc update <id> --label-add=devops/); + assert.match(source, /canonical description-file paths/); + assert.match(source, /Arc normalizes outer whitespace/); + assert.match(source, /jq -j \.description \| sha256sum/); + assert.match(source, /wc -l/); + assert.match(plan, /path\.write_text\(path\.read_text\(\)\.strip\(\)\)/); + assert.match(plan, /File SHA-256/); + assert.doesNotMatch(plan, /File lines \| Arc lines/); + assert.ok(plan.indexOf('Before persistence, self-review') < plan.indexOf('Then dispatch the manifest')); + assert.equal((plan.match(/### 6\.5\. Self-Review/g) ?? []).length, 0); + assert.match(source, /## Timing/); +}); + +test('arc-summarize uses Pi MCP gateway and bundled question schema', () => { + const skill = read('skills/arc-summarize/SKILL.md'); + const extension = read('extensions/arc.ts'); + + assert.match(skill, /^name:\s*arc-summarize$/m); + assert.match(skill, /Pi's `mcp` gateway/); + assert.match(skill, /questions\[\]/); + assert.match(skill, /@juicesharp\/rpiv-ask-user-question/); + assert.doesNotMatch(skill, /mcp__claude|mcp__plugin/); + assert.doesNotMatch(skill, /jira_create\(/); + assert.match(skill, /mcp\(\{ describe: "<discovered-create-tool>" \}\)/); + assert.doesNotMatch(skill, /`ask_user_question`:\s*$/m); + assert.match(skill, /TMP=\$\(mktemp\)/); + assert.match(skill, /jq -j \.description > "\$TMP"/); + assert.match(skill, /arc update <arc-id> --stdin < "\$TMP"/); + assert.match(skill, /Re-read both records/); + assert.match(skill, /never fabricate a host/); + assert.doesNotMatch(skill, /https:\/\/arc\.bactrack\.com/); + assert.doesNotMatch(skill, /<full existing description, unchanged from arc show>/); + assert.match(extension, /command: "arc-summarize"/); + assert.match(extension, /skill: "arc-summarize"/); +}); + +test('review and evaluator profiles remain authoritative with large fallbacks', () => { + for (const path of ['agents/code-reviewer.md', 'agents/spec-reviewer.md', 'agents/evaluator.md']) { + assert.match(read(path), /^model:\s*large$/m, path); + } + + const build = read('skills/arc-build/SKILL.md'); + assert.match(build, /subagent\(\{ agent: "arc-spec-reviewer", task: "<filled prompt>", context: "fresh"/); + assert.doesNotMatch(build, /agent: "arc-spec-reviewer"[^\n]*model:/); + assert.doesNotMatch(build, /agent: "arc-evaluator"[^\n]*model:/); + assert.doesNotMatch(build, /arc_agent\(agent="evaluator"[^\n]*model=/); + assert.match(build, /git status --short.*pre-evaluation baseline/s); + + const evaluator = read('agents/evaluator.md'); + assert.match(evaluator, /If it is not clean, report `BLOCKED`/); + assert.match(evaluator, /restore modified tracked files and remove only the temporary files/i); + assert.doesNotMatch(evaluator, /Do NOT worry about cleanup/); +});