diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index f94592e76..ef6728a5d 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -996,6 +996,12 @@ "description": "Security frameworks, accessibility guidelines, performance optimization, and code quality best practices for building secure, maintainable, and high-performance applications.", "version": "1.0.0" }, + { + "name": "shepherd-task", + "source": "plugins/shepherd-task", + "description": "End-to-end task shepherding system that automates the full lifecycle of GitHub issues through Copilot coding agent assignment, CI approval, code review resolution, and merge. Includes orchestration scripts for both Bash and PowerShell.", + "version": "1.0.0" + }, { "name": "site-studio", "source": "extensions/site-studio", diff --git a/docs/README.plugins.md b/docs/README.plugins.md index bff4a4b4d..9714d77c0 100644 --- a/docs/README.plugins.md +++ b/docs/README.plugins.md @@ -87,6 +87,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-plugins) for guidelines on how t | [rust-mcp-development](../plugins/rust-mcp-development/README.md) | Build high-performance Model Context Protocol servers in Rust using the official rmcp SDK with async/await, procedural macros, and type-safe implementations. | 2 items | rust, mcp, model-context-protocol, server-development, sdk, tokio, async, macros, rmcp | | [salesforce-development](../plugins/salesforce-development/README.md) | Complete Salesforce agentic development environment covering Apex & Triggers, Flow automation, Lightning Web Components, Aura components, and Visualforce pages. | 7 items | salesforce, apex, triggers, lwc, aura, flow, visualforce, crm, salesforce-dx | | [security-best-practices](../plugins/security-best-practices/README.md) | Security frameworks, accessibility guidelines, performance optimization, and code quality best practices for building secure, maintainable, and high-performance applications. | 1 items | security, accessibility, performance, code-quality, owasp, a11y, optimization, best-practices | +| [shepherd-task](../plugins/shepherd-task/README.md) | End-to-end task shepherding system that automates the full lifecycle of GitHub issues through Copilot coding agent assignment, CI approval, code review resolution, and merge. Includes orchestration scripts for both Bash and PowerShell. | 3 items | shepherd, task-automation, copilot-coding-agent, ci-cd, code-review, github-actions, workflow-approval, yolo | | [skill-image-gen](../plugins/skill-image-gen/README.md) | Generate images using AI directly from your coding workflow. Supports OpenAI (gpt-image-2) and Google Gemini. BYO API key — the skill guides you through setup on first use. | 1 items | image-generation, openai, gemini, ai, art, sprites, textures, icons | | [software-engineering-team](../plugins/software-engineering-team/README.md) | 7 specialized agents covering the full software development lifecycle from UX design and architecture to security and DevOps. | 7 items | team, enterprise, security, devops, ux, architecture, product, ai-ethics | | [structured-autonomy](../plugins/structured-autonomy/README.md) | Premium planning, thrifty implementation | 3 items | | diff --git a/docs/README.skills.md b/docs/README.skills.md index fb94c86e3..37853f425 100644 --- a/docs/README.skills.md +++ b/docs/README.skills.md @@ -356,6 +356,9 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to | [security-review](../skills/security-review/SKILL.md)
`gh skills install github/awesome-copilot security-review` | AI-powered codebase security scanner that reasons about code like a security researcher — tracing data flows, understanding component interactions, and catching vulnerabilities that pattern-matching tools miss. Use this skill when asked to scan code for security vulnerabilities, find bugs, check for SQL injection, XSS, command injection, exposed API keys, hardcoded secrets, insecure dependencies, access control issues, or any request like "is my code secure?", "review for security issues", "audit this codebase", or "check for vulnerabilities". Covers injection flaws, authentication and access control bugs, secrets exposure, weak cryptography, insecure dependencies, and business logic issues across JavaScript, TypeScript, Python, Java, PHP, Go, Ruby, and Rust. | `references/language-patterns.md`
`references/report-format.md`
`references/secret-patterns.md`
`references/vuln-categories.md`
`references/vulnerable-packages.md` | | [semantic-kernel](../skills/semantic-kernel/SKILL.md)
`gh skills install github/awesome-copilot semantic-kernel` | Create, update, refactor, explain, or review Semantic Kernel solutions using shared guidance plus language-specific references for .NET and Python. | `references/dotnet.md`
`references/python.md` | | [setup-my-iq](../skills/setup-my-iq/SKILL.md)
`gh skills install github/awesome-copilot setup-my-iq` | Create, set up, or update the personal context portfolio: structured markdown files describing
who you are, how you work, your teams, and your tool/ADO configuration. Runs the interview
workflow for first-time setup and targeted edits for updates.

Trigger this skill when the user asks to: set up their context, create or update their context
portfolio, "create my IQ", "set up my IQ", edit their profile, add/remove a stakeholder,
update ADO config, change team info, update pillars, or set up any plugin configuration.
Trigger when another skill fails to find context (missing files or TODO markers) and needs
context populated. Also trigger when the user mentions a context change in passing
(e.g., "my manager changed", "we added someone to the team") to offer a context file update.

Do NOT trigger for read-only questions like "who's on my team?" or "what's my ADO config?".
Those are answered directly from the context files referenced in the loaded custom
instructions; no skill is needed. | `assets/templates` | +| [shepherd-task-approve-workflows-and-wait-for-completion](../skills/shepherd-task-approve-workflows-and-wait-for-completion/SKILL.md)
`gh skills install github/awesome-copilot shepherd-task-approve-workflows-and-wait-for-completion` | Use this skill to approve pending workflow runs on a PR branch and wait for them to complete. | None | +| [shepherd-task-from-assignment-to-ready](../skills/shepherd-task-from-assignment-to-ready/SKILL.md)
`gh skills install github/awesome-copilot shepherd-task-from-assignment-to-ready` | Use this skill to shepherd a child Task issue from 'assigned to Copilot' through CI approval and review-agent feedback resolution, stopping just before marking the PR as **Ready for review**. | None | +| [shepherd-task-from-ready-to-merged-to-base](../skills/shepherd-task-from-ready-to-merged-to-base/SKILL.md)
`gh skills install github/awesome-copilot shepherd-task-from-ready-to-merged-to-base` | Use this skill to shepherd a task PR from 'Ready for review' through Copilot code review, local comment resolution, and merge to the specified base branch. | None | | [shuffle-json-data](../skills/shuffle-json-data/SKILL.md)
`gh skills install github/awesome-copilot shuffle-json-data` | Shuffle repetitive JSON objects safely by validating schema consistency before randomising entries. | None | | [slang-shader-engineer](../skills/slang-shader-engineer/SKILL.md)
`gh skills install github/awesome-copilot slang-shader-engineer` | Use when working with Slang shaders, shader modules, HLSL-compatible GPU code, graphics pipelines, compute shaders, tessellation, ray tracing, parameter blocks, generics, interfaces, capabilities, cross-compilation, shader optimization, shader review, or C++ engine integration for Slang. Trigger on any mention of Slang, .slang files, slangc, SPIR-V from Slang, Slang modules, [shader("compute")], [shader("vertex")], or requests to write/review/refactor shader code with modern language features. Also trigger for Slang-to-HLSL/GLSL/Metal/CUDA cross-compile questions, or when the user says "shader" alongside "generics", "interfaces", "parameter blocks", "autodiff", or "capabilities". | `references/language-reference.md`
`references/rules-and-patterns.md`
`references/slang-documentation-full.md` | | [snowflake-semanticview](../skills/snowflake-semanticview/SKILL.md)
`gh skills install github/awesome-copilot snowflake-semanticview` | Create, alter, and validate Snowflake semantic views using Snowflake CLI (snow). Use when asked to build or troubleshoot semantic views/semantic layer definitions with CREATE/ALTER SEMANTIC VIEW, to validate semantic-view DDL against Snowflake via CLI, or to guide Snowflake CLI installation and connection setup. | None | diff --git a/plugins/shepherd-task/.github/plugin/plugin.json b/plugins/shepherd-task/.github/plugin/plugin.json new file mode 100644 index 000000000..9f6747dfb --- /dev/null +++ b/plugins/shepherd-task/.github/plugin/plugin.json @@ -0,0 +1,25 @@ +{ + "name": "shepherd-task", + "description": "End-to-end task shepherding system that automates the full lifecycle of GitHub issues through Copilot coding agent assignment, CI approval, code review resolution, and merge. Includes orchestration scripts for both Bash and PowerShell.", + "version": "1.0.0", + "author": { + "name": "Ed Burns" + }, + "repository": "https://github.com/github/awesome-copilot", + "license": "MIT", + "keywords": [ + "shepherd", + "task-automation", + "copilot-coding-agent", + "ci-cd", + "code-review", + "github-actions", + "workflow-approval", + "yolo" + ], + "skills": [ + "./skills/shepherd-task-approve-workflows-and-wait-for-completion/", + "./skills/shepherd-task-from-assignment-to-ready/", + "./skills/shepherd-task-from-ready-to-merged-to-base/" + ] +} diff --git a/plugins/shepherd-task/README.md b/plugins/shepherd-task/README.md new file mode 100644 index 000000000..12508f444 --- /dev/null +++ b/plugins/shepherd-task/README.md @@ -0,0 +1,251 @@ +## Summary + +This PR adds a new **shepherd-task** plugin that provides an end-to-end system for implementing a specification described as a well-specified list of GitHub issues using an opinionated choice of the following agents and tools. + +1. Copilot Coding Agent (CCA). +2. Copilot Code Review Agent (CCRA). +3. GitHub CLI `gh`. +4. GitHub Copilot CLI `copilot`. + +It shepherds tasks from assignment through CI approval, code review resolution, and merge — all orchestrated via `copilot --yolo` sessions driven by shell scripts. + +## Enabling assumptions + +### Preconditions + +1. The work will happen within a single GitHub repository. +1. The repository has GitHub Copilot Coding Agent and Code Review Agent installed and properly configured. +1. User has `gh` CLI installed at version 2.45.0 or later and is signed in. +1. USer has permissions on the repository to: + 1. Assign issues to `Copilot`. + 1. Push to the repository. +1. User has `copilot` CLI installed at version `1.0.72-0` or later and is signed in. +1. User has a local development environment suitable for using Copilot CLI and `git worktree` to do development work, including running tests. +1. User accepts that all of the work is to be done on a non-`main` base branch in the repository. +1. User accepts this plugin will invoke `copilot --yolo`. +1. The specification for the job to be done is encoded in an ordered set of GitHub issues in the issue tracker of the GitHub repository. +1. User has `jq` installed and accessible to Copilot CLI. + +### Postconditions + +The user of `shepherd-task` is responsible for creating a PR from the specified non-`main base branch to main. + +## Motivation + +When using the Copilot coding agent to implement tasks from a backlog, there is a significant amount of repetitive supervisory work: assigning the issue, waiting for the PR, approving workflow runs, interpreting CI failures, requesting changes, waiting for fixes, resolving code review comments, and finally merging. This system automates that entire loop, allowing a developer to hand off a list of issue numbers and walk away. + +A successful run of the system will result in the work being merged to the specified non-`main` base branch, so that a further PR can be made from that branch with greater human oversight. + +This plugin was developed and battle-tested in the `github/copilot-sdk` repository, where it has successfully shepherded dozens of tasks through the full lifecycle. It is being contributed here so the broader community can adopt, adapt, and improve it. + +## Architecture + +The system has two layers: + +1. **Skills** (Copilot-invocable instructions) — three SKILL.md files that Copilot reads during `--yolo` sessions to know what to do at each phase: + - `shepherd-task-from-assignment-to-ready`: Assigns an issue to @copilot with a specific base branch, waits for PR creation, iterates through CI approval and review-agent feedback (up to 20 iterations). + - `shepherd-task-from-ready-to-merged-to-base`: Marks the PR as ready, waits for Copilot code review, resolves comments locally in a git worktree, pushes fixes, and merges. + - `shepherd-task-approve-workflows-and-wait-for-completion`: Reusable sub-skill that approves pending `action_required` workflow runs and blocks until completion. + +2. **Orchestration scripts** (user-facing entry points) — shell scripts that launch `copilot --yolo` sessions, verify outcomes with `gh` CLI between phases, and provide idempotent retry semantics. Both Bash and PowerShell variants are included for cross-platform support. + +The plugin manifest (`plugin.json`) ties these together as a discoverable unit in the marketplace. + +### Invocation flow + +1. User invokes `shepherd-task-given-list`. See [figure 01: script shepherd-task-given-list](figure-01-shepherd-task-given-list.md). + +2. For each task, `shepherd-task-given-list` invokes `shepherd-task`. See [figure 02: script shepherd-task](figure-02-shepherd-task.md). + + a. Phase 1: Assign task to Copilot Coding Agent and make it ready for review. See [figure 03: skill: from-assigned-to-ready](figure-03-from-assigned-to-ready.md). + + b. Phase 2: Mark ready for review and resolve all Copilot Code Review Agent comments that have merit. See [figure 04: skill: from ready-to-merged-to-base](figure-04-from-ready-to-merged.md). + +3. Perform post-mortem. See [figure 05: skill: create post-mortem](figure-05-post-mortem.md). + + +## Sample invocation + +Bash + +```bash +. ./plugins/shepherd-task/scripts/shepherd-task-given-list.sh 13,4,5,6,7,8,9,10,11 edburns/2-build-out-demo edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk +``` + +PowerShell + +```powershell +. .\plugins\shepherd-task\scripts\shepherd-task-given-list.ps1 13,4,5,6,7,8,9,10,11 edburns/2-build-out-demo edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk +``` + +This invocation was used to implement the issues in this Epic: https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/2 . + +## Installation + +### Step 1: Install skills (via `gh skill`) + +```bash +gh skill install github/awesome-copilot shepherd-task-from-assignment-to-ready +gh skill install github/awesome-copilot shepherd-task-from-ready-to-merged-to-base +gh skill install github/awesome-copilot shepherd-task-approve-workflows-and-wait-for-completion +``` + +Requires GitHub CLI v2.90.0+. + +### Step 2: Install orchestration scripts + +The scripts are not installable via `gh skill`. Either: +- Clone this repository and run from `plugins/shepherd-task/scripts/`, or +- Use the bundled installer to copy only the scripts into your repo: + + Bash + + ```bash + . ./plugins/shepherd-task/scripts/install-task-shepherd.sh /path/to/your/repo + ``` + + PowerShell + + ```powershell + . .\plugins\shepherd-task\scripts\install-task-shepherd.ps1 \path\to\your\repo + ``` + +The installer copies **only** the orchestration scripts — it does not duplicate the skills (those are managed by `gh skill install` above). + +## Uninstallation + +There is no `gh skill uninstall` command. To remove installed skills, manually delete the skill directories from your agent's skills location. + +- Use the bundled uninstaller to remove the scripts from your repo. + + Bash + + ```bash + . ./plugins/shepherd-task/scripts/uninstall-task-shepherd.sh /path/to/your/repo + ``` + + PowerShell + + ```powershell + . .\plugins\shepherd-task\scripts\uninstall-task-shepherd.ps1 \path\to\your\repo + ``` + + +## How it works + +``` +User runs: shepherd-task-given-list.sh "1841,1842,1843" feature-branch owner/repo + └─ For each issue, calls shepherd-task.sh + ├─ Phase 1: echo prompt | copilot --yolo (invokes shepherd-task-from-assignment-to-ready) + │ └─ skill loops: assign → wait for PR → approve workflows → fix CI → repeat + ├─ Verify: gh pr checks, no unresolved reviews + ├─ Phase 2: echo prompt | copilot --yolo (invokes shepherd-task-from-ready-to-merged-to-base) + │ └─ skill loops: mark ready → resolve review comments locally → push → merge + └─ Verify: PR merged to non-main base branch, issue closed +``` + +## Key design decisions + +- **Scripts verify state independently** — they don't trust Copilot exit codes; they use `gh` CLI to confirm PR existence, CI status, and merge state between phases. +- **Idempotent** — if a PR already exists for the issue, Phase 1 is skipped. If already merged, Phase 2 is skipped. Safe to re-run after failures. +- **Local review resolution** — Phase 2 resolves Copilot code review comments in a local git worktree rather than asking the remote agent to fix itself, giving more reliable results. +- **Cross-platform** — every script has both `.sh` and `.ps1` variants. + +## Per-file manifest + +### Plugin + +| File | Purpose | +|------|---------| +| `plugins/shepherd-task/.github/plugin/plugin.json` | Plugin manifest grouping the three skills; includes metadata, keywords, and version | +| `plugins/shepherd-task/README.md` | User-facing documentation for the plugin | + +### Orchestration scripts + +| File | Purpose | +|------|---------| +| `plugins/shepherd-task/scripts/shepherd-task-given-list.sh` | Bash: iterates a comma-separated list of issue numbers, invoking shepherd-task.sh for each | +| `plugins/shepherd-task/scripts/shepherd-task-given-list.ps1` | PowerShell equivalent | +| `plugins/shepherd-task/scripts/shepherd-task.sh` | Bash: orchestrates Phase 1 + Phase 2 for a single task issue with state verification | +| `plugins/shepherd-task/scripts/shepherd-task.ps1` | PowerShell equivalent | +| `plugins/shepherd-task/scripts/shepherd-task-inspect-json.sh` | Bash: debug utility to inspect copilot JSON session logs | +| `plugins/shepherd-task/scripts/shepherd-task-inspect-json.ps1` | PowerShell equivalent | +| `plugins/shepherd-task/scripts/install-task-shepherd.sh` | Bash: copies the full shepherd system into another repository | +| `plugins/shepherd-task/scripts/install-task-shepherd.ps1` | PowerShell equivalent | + +### Skills + +| File | Purpose | +|------|---------| +| `skills/shepherd-task-from-assignment-to-ready/SKILL.md` | Skill instructions for Phase 1: assign to Copilot → PR created → CI passing → no unresolved reviews | +| `skills/shepherd-task-from-ready-to-merged-to-base/SKILL.md` | Skill instructions for Phase 2: mark ready → resolve code review → merge to base branch | +| `skills/shepherd-task-approve-workflows-and-wait-for-completion/SKILL.md` | Reusable sub-skill: approve `action_required` workflow runs and wait for completion | + +### Generated files (updated by `npm run build`) + +| File | Purpose | +|------|---------| +| `.github/plugin/marketplace.json` | Marketplace registry updated with new shepherd-task plugin entry | +| `docs/README.plugins.md` | Plugin listing updated to include shepherd-task | +| `docs/README.skills.md` | Skills listing updated with the three new shepherd-task skills | + +## Validation + +``` +$ npm run skill:validate +✅ shepherd-task-approve-workflows-and-wait-for-completion is valid +✅ shepherd-task-from-assignment-to-ready is valid +✅ shepherd-task-from-ready-to-merged-to-base is valid + +$ npm run plugin:validate +✅ shepherd-task is valid +``` + +## Testing + +This system has been used in production on `github/copilot-sdk` to shepherd implementation tasks for Java SDK development. It handles the common failure modes: workflow approval gates, CI flakiness, Copilot code review comment iteration, merge conflicts, and base branch drift. + +## Pull Request Checklist + +- [x] I have read and followed the [CONTRIBUTING.md](https://github.com/github/awesome-copilot/blob/main/CONTRIBUTING.md) guidelines. +- [x] I have read and followed the [Guidance for submissions involving paid services](https://github.com/github/awesome-copilot/discussions/968). +- [x] My contribution adds a new instruction, prompt, agent, skill, workflow, or canvas extension file in the correct directory. +- [x] The file follows the required naming convention. +- [x] The content is clearly structured and follows the example format. +- [x] I have tested my instructions, prompt, agent, skill, workflow, or canvas extension with GitHub Copilot. +- [x] I have run `npm start` and verified that `README.md` is up to date. +- [ ] I am targeting the `main` branch for this pull request. + +--- + +## Description + + + +--- + +## Type of Contribution + +- [ ] New instruction file. +- [ ] New prompt file. +- [ ] New agent file. +- [x] New plugin. +- [x] New skill file. +- [ ] New agentic workflow. +- [ ] New canvas extension. +- [ ] Update to existing instruction, prompt, agent, plugin, skill, workflow, or canvas extension. +- [ ] Other (please specify): + +--- + +## Additional Notes + + + +--- + +By submitting this pull request, I confirm that my contribution abides by the [Code of Conduct](../CODE_OF_CONDUCT.md) and will be licensed under the MIT License. + + + + diff --git a/plugins/shepherd-task/figure-01-shepherd-task-given-list.md b/plugins/shepherd-task/figure-01-shepherd-task-given-list.md new file mode 100644 index 000000000..f75bcadc0 --- /dev/null +++ b/plugins/shepherd-task/figure-01-shepherd-task-given-list.md @@ -0,0 +1,55 @@ +# Figure 01: shepherd-task-given-list — Batch Dispatch + +This diagram shows the highest-level orchestration: how `shepherd-task-given-list.ps1` takes a comma-separated list of issue numbers and dispatches them serially. + +## Example Invocation + +```powershell +shepherd-task-given-list.ps1 "51,52,53,54" edburns/dd-3034809-test-01 edburns/Build26-BRK206-your-agent +``` + +## Sequence Diagram + +```mermaid +sequenceDiagram + actor User + participant STGL as shepherd-task-given-list.ps1 + participant ST as shepherd-task.ps1 + participant Copilot as copilot --yolo + participant PostMortem as shepherd-task-create-post-mortem + + User->>STGL: "51,52,53,54"
edburns/dd-3034809-test-01
edburns/Build26-BRK206-your-agent + + Note over STGL: Create timestamped log directory
shepherd-tasks-YYYYMMDD-HHMM/ + + Note over STGL: Parse comma-separated list
into [51, 52, 53, 54] + + rect rgb(220, 240, 255) + Note over STGL,ST: Serial loop — one issue at a time + STGL->>ST: shepherd-task.ps1 -TaskIssue 51
-BaseBranch edburns/dd-3034809-test-01
-Repo edburns/Build26-BRK206-your-agent
-LogDir shepherd-tasks-YYYYMMDD-HHMM/ + ST-->>STGL: exit 0 (success) + + STGL->>ST: shepherd-task.ps1 -TaskIssue 52
-BaseBranch edburns/dd-3034809-test-01
-Repo edburns/Build26-BRK206-your-agent
-LogDir shepherd-tasks-YYYYMMDD-HHMM/ + ST-->>STGL: exit 0 (success) + + STGL->>ST: shepherd-task.ps1 -TaskIssue 53
-BaseBranch edburns/dd-3034809-test-01
-Repo edburns/Build26-BRK206-your-agent
-LogDir shepherd-tasks-YYYYMMDD-HHMM/ + ST-->>STGL: exit 0 (success) + + STGL->>ST: shepherd-task.ps1 -TaskIssue 54
-BaseBranch edburns/dd-3034809-test-01
-Repo edburns/Build26-BRK206-your-agent
-LogDir shepherd-tasks-YYYYMMDD-HHMM/ + ST-->>STGL: exit 0 (success) + end + + rect rgb(255, 245, 220) + Note over STGL,PostMortem: finally block — runs on success OR failure + STGL->>Copilot: echo prompt | copilot --yolo
"Invoke skill shepherd-task-create-post-mortem" + Copilot->>PostMortem: Invoke with SHEPHERD_LOG_DIR,
SCRIPT_EXIT_CODE, TASK_ISSUES,
BASE_BRANCH, REPO + PostMortem-->>Copilot: Write YYYYMMDD-HHMM-post-mortem.md
to log directory + Copilot-->>STGL: Session complete + end + + STGL-->>User: exit 0
"All tasks shepherded successfully" +``` + +## Failure Behavior + +If any `shepherd-task.ps1` invocation fails (non-zero exit), the loop stops immediately at that issue. The `finally` block still runs, invoking the post-mortem skill with the non-zero exit code so that a failure report is always generated. diff --git a/plugins/shepherd-task/figure-02-shepherd-task.md b/plugins/shepherd-task/figure-02-shepherd-task.md new file mode 100644 index 000000000..f56c8f4d4 --- /dev/null +++ b/plugins/shepherd-task/figure-02-shepherd-task.md @@ -0,0 +1,80 @@ +# Figure 02: shepherd-task — Single Issue Orchestration + +This diagram drills down into what happens inside `shepherd-task.ps1` for a single issue. It shows the two phases, the `copilot --yolo` sessions that invoke skills, and the independent state verification between phases. + +## Sequence Diagram + +```mermaid +sequenceDiagram + participant ST as shepherd-task.ps1 + participant GH as gh CLI + participant Copilot as copilot --yolo + participant Skill1 as shepherd-task-from-
assignment-to-ready + participant Skill2 as shepherd-task-from-
ready-to-merged-to-base + + Note over ST: Receive TASK_ISSUE, BASE_BRANCH, REPO, LogDir + + rect rgb(230, 245, 230) + Note over ST,GH: Idempotency check — skip Phase 1 if PR exists + ST->>GH: Find-LinkedPR (timeline, body search, title/branch match) + GH-->>ST: PR exists? → skip to verification
No PR? → proceed to Phase 1 + end + + rect rgb(220, 240, 255) + Note over ST,Skill1: Phase 1: Assignment to Ready for Review + ST->>Copilot: echo phase1Prompt | copilot --yolo
--output-format json --share phase1.md > phase1.json + Note over Copilot: COPILOT_OTEL_FILE_EXPORTER_PATH set
for telemetry capture + Copilot->>Skill1: "Invoke skill
shepherd-task-from-assignment-to-ready
with TASK_ISSUE, BASE_BRANCH, REPO" + Note over Skill1: Assign → wait for PR → approve workflows
→ fix CI → iterate (max 20 rounds)
(details in Figure 03) + Skill1-->>Copilot: Skill complete + Copilot-->>ST: Session exits + end + + rect rgb(255, 240, 230) + Note over ST,GH: Phase 1 Verification (script verifies independently via gh) + ST->>GH: Find-LinkedPR → PR_NUMBER + GH-->>ST: PR #N found + ST->>GH: gh pr view — verify base branch = BASE_BRANCH + GH-->>ST: Base branch OK (or fix it) + ST->>GH: Test-CIPassing — all checks pass? + GH-->>ST: CI passing ✓ + ST->>GH: Test-NoUnresolvedReviews — GraphQL review threads + GH-->>ST: No unresolved comments ✓ + Note over ST: "Phase 1 VERIFIED: PR #N is ready" + end + + rect rgb(230, 245, 230) + Note over ST,GH: Idempotency check — skip Phase 2 if already merged + ST->>GH: gh pr view — state == MERGED? + GH-->>ST: Not merged → proceed to Phase 2 + end + + rect rgb(220, 230, 255) + Note over ST,Skill2: Phase 2: Ready for Review to Merged + ST->>Copilot: echo phase2Prompt | copilot --yolo
--output-format json --share phase2.md > phase2.json + Note over Copilot: COPILOT_OTEL_FILE_EXPORTER_PATH set
for telemetry capture + Copilot->>Skill2: "Invoke skill
shepherd-task-from-ready-to-merged-to-base
with TASK_ISSUE, BASE_BRANCH,
REPO, PR_NUMBER" + Note over Skill2: Mark ready → request review → resolve
comments locally → push → merge
(details in Figure 04) + Skill2-->>Copilot: Skill complete + Copilot-->>ST: Session exits + end + + rect rgb(255, 240, 230) + Note over ST,GH: Phase 2 Verification (script verifies independently via gh) + ST->>GH: gh pr view — state == MERGED? + GH-->>ST: MERGED ✓ + ST->>GH: gh pr view — merged into BASE_BRANCH? + GH-->>ST: Correct base ✓ + ST->>GH: gh issue view — state == CLOSED? + GH-->>ST: CLOSED ✓ (or close it) + end + + Note over ST: "SHEPHERD TASK COMPLETE:
PR #N merged to BASE_BRANCH" + ST-->>ST: exit 0 +``` + +## Key Design Points + +- **Scripts verify state independently** — they don't trust `copilot` exit codes; they use `gh` CLI to confirm PR existence, CI status, and merge state between phases. +- **Idempotent** — if a PR already exists for the issue, Phase 1 is skipped. If already merged, Phase 2 is skipped. Safe to re-run after failures. +- **Each phase runs in a separate `copilot --yolo` session** — the skill instructions are read fresh each time. diff --git a/plugins/shepherd-task/figure-03-from-assigned-to-ready.md b/plugins/shepherd-task/figure-03-from-assigned-to-ready.md new file mode 100644 index 000000000..a47be786f --- /dev/null +++ b/plugins/shepherd-task/figure-03-from-assigned-to-ready.md @@ -0,0 +1,91 @@ +# Figure 03: From Assignment to Ready for Review + +This diagram shows the detail of the `shepherd-task-from-assignment-to-ready` skill, including its nested invocation of `shepherd-task-approve-workflows-and-wait-for-completion`. All of this runs inside a single `copilot --yolo` session (the Local Copilot CLI), which orchestrates interaction with the remote Copilot Coding Agent (CCA) via the GitHub API. + +## Sequence Diagram + +```mermaid +sequenceDiagram + participant CLI as Local Copilot CLI
(copilot --yolo) + participant GitHub as GitHub API / gh CLI + participant CCA as Copilot Coding Agent
(remote) + participant ApproveWF as shepherd-task-approve-
workflows-and-wait + + Note over CLI: Skill inputs: TASK_ISSUE, BASE_BRANCH, REPO + + rect rgb(220, 240, 255) + Note over CLI,CCA: Step 1: Assign issue to @Copilot with base branch + CLI->>GitHub: POST /repos/REPO/issues/TASK_ISSUE/assignees
{ assignees: ["copilot-swe-agent[bot]"],
agent_assignment: { base_branch: BASE_BRANCH } } + GitHub->>CCA: Trigger: issue assigned to Copilot + Note over CCA: CCA creates topic branch from BASE_BRANCH,
opens draft PR, pushes initial commits + end + + rect rgb(230, 245, 230) + Note over CLI,GitHub: Step 2: Poll for PR creation (up to 15 min, every 30s) + loop Strategy A → B → C each iteration + CLI->>GitHub: A: Issue timeline (cross-referenced PRs) + CLI->>GitHub: B: PR body search for #TASK_ISSUE + CLI->>GitHub: C: Title/branch name regex match + end + GitHub-->>CLI: PR #N found + CLI->>GitHub: Verify base branch == BASE_BRANCH + end + + rect rgb(255, 245, 220) + Note over CLI,ApproveWF: Steps 3–5: Approve workflows (nested skill) + CLI->>GitHub: Check for action_required workflow runs + CLI->>ApproveWF: Invoke shepherd-task-approve-
workflows-and-wait-for-completion
with REPO, JTBDTASK_BRANCH, PR_NUMBER + ApproveWF->>GitHub: gh run rerun (for each pending run) + ApproveWF->>GitHub: gh pr checks --watch --fail-fast + GitHub-->>ApproveWF: All runs complete + ApproveWF-->>CLI: Workflows done + end + + rect rgb(240, 230, 255) + Note over CLI,GitHub: Step 6: Evaluate CI results + CLI->>GitHub: gh pr checks PR_NUMBER
--jq (exclude "No remove-before-merge directories") + GitHub-->>CLI: Real failures? or All pass? + end + + alt All CI checks pass and no unresolved reviews + Note over CLI: Skip to Step 8–9 + else CI failures or unresolved review comments + rect rgb(255, 230, 230) + Note over CLI,CCA: Step 7: Iteration loop (max 20 rounds) + loop Up to 20 iterations + CLI->>GitHub: 7.1: Gather failed run logs
gh run view RUN_ID --log-failed + CLI->>GitHub: 7.2: Gather bot review comments
from PR comments and reviews + Note over CLI: 7.3: Compose "Request changes" review
with log excerpts + fix instructions + CLI->>GitHub: gh pr review PR_NUMBER --request-changes
--body "@copilot Please fix..." + GitHub->>CCA: Review notification: changes requested + Note over CCA: CCA reads review, pushes fix commits + CLI->>GitHub: 7.4: Poll for new commits
(compare HEAD SHA, up to 10 min) + GitHub-->>CLI: New SHA detected + CLI->>ApproveWF: 7.5: Re-invoke approve-workflows skill + ApproveWF->>GitHub: Approve & wait for new runs + ApproveWF-->>CLI: Workflows complete + CLI->>GitHub: Re-evaluate CI results + Note over CLI: If all pass → break loop
If failures remain → next iteration + end + end + end + + rect rgb(230, 255, 230) + Note over CLI,GitHub: Step 8: Check for unresolved pre-ready review comments + CLI->>GitHub: Query review threads (CHANGES_REQUESTED) + CLI->>GitHub: Query bot comments on PR + Note over CLI: If unresolved comments exist,
iterate using same Step 7 pattern
(shares the 20-iteration budget) + end + + rect rgb(220, 255, 220) + Note over CLI: Step 9: Final status report + Note over CLI: "SHEPHERD COMPLETE: PR #N for task
#TASK_ISSUE is ready for marking
as Ready for Review." + end +``` + +## Notes + +- The Local Copilot CLI acts as the **orchestrator** — it never modifies code itself in this phase. All code changes are made by the **remote Copilot Coding Agent (CCA)**. +- The `agent_assignment.base_branch` API parameter is the only reliable way to set the base branch. The simpler `gh issue edit --add-assignee` does not support this parameter, causing CCA to default to `main`. +- The "No remove-before-merge directories" CI failure is always ignored — it is expected on feature branches. +- The 20-iteration budget is shared between CI fix iterations (Step 7) and pre-ready review comment iterations (Step 8). diff --git a/plugins/shepherd-task/figure-04-from-ready-to-merged.md b/plugins/shepherd-task/figure-04-from-ready-to-merged.md new file mode 100644 index 000000000..756d975d8 --- /dev/null +++ b/plugins/shepherd-task/figure-04-from-ready-to-merged.md @@ -0,0 +1,125 @@ +# Figure 04: From Ready for Review to Merged + +This diagram shows the detail of the `shepherd-task-from-ready-to-merged-to-base` skill. Unlike Phase 1 (which relies on the remote Copilot Coding Agent), this phase resolves review comments **locally** using a git worktree, with dialog between the Copilot Code Review Agent (CCRA) and the local Copilot CLI session. + +## Sequence Diagram + +```mermaid +sequenceDiagram + participant CLI as Local Copilot CLI
(copilot --yolo) + participant GitHub as GitHub API / gh CLI + participant CCRA as Copilot Code Review
Agent (remote) + participant Worktree as Git Worktree
(../review-copilot-pr-N) + participant ApproveWF as shepherd-task-approve-
workflows-and-wait + + Note over CLI: Skill inputs: TASK_ISSUE, BASE_BRANCH, REPO, PR_NUMBER + + rect rgb(220, 240, 255) + Note over CLI,CCRA: Step 0–1: Find PR, mark ready, request review + CLI->>GitHub: Find PR via timeline/body/title strategies + CLI->>GitHub: gh pr ready PR_NUMBER + CLI->>GitHub: gh pr edit --add-reviewer
copilot-pull-request-reviewer + Note over CCRA: CCRA begins analyzing the PR diff + end + + rect rgb(230, 245, 230) + Note over CLI,CCRA: Step 2–3: Wait for CCRA review and count comments + CLI->>GitHub: Poll for review (4 detection strategies,
every 30s, up to 10 min) + Note over CLI: Strategy A: body matches "Copilot's findings"
Strategy B: body matches "Pull request overview"
Strategy C: user login contains copilot-pull-request-reviewer
Strategy D: line-level comments from user "Copilot" + CCRA-->>GitHub: Post review with N comments + GitHub-->>CLI: Review detected, N comments found + end + + alt N == 0 (no comments) + Note over CLI: Skip to Step 15 (final checks) + else N > 0 (comments to address) + rect rgb(255, 245, 220) + Note over CLI,Worktree: Step 4: Set up local worktree (sibling directory) + CLI->>GitHub: git fetch upstream + CLI->>Worktree: git worktree add
"../review-copilot-pr-N"
"upstream/JTBDTASK_BRANCH" + end + + rect rgb(240, 240, 255) + Note over CLI,ApproveWF: Step 5: Approve workflows before gathering comments + CLI->>ApproveWF: Invoke approve-workflows skill
(REPO, JTBDTASK_BRANCH, PR_NUMBER) + Note over ApproveWF: (See Figure 03 for details) + ApproveWF-->>CLI: Workflows complete + end + + rect rgb(255, 230, 230) + Note over CLI,GitHub: Review resolution loop (max 8 rounds) + loop Up to 8 iterations + CLI->>GitHub: Step 6: Gather all review comments
from copilot-pull-request-reviewer / Copilot + + loop For each review comment + Note over CLI,Worktree: Step 7: Address each comment locally + Note over CLI: 7.1: Evaluate comment merit + alt Comment has merit + CLI->>Worktree: 7.2: Implement fix in worktree
Run targeted tests + CLI->>Worktree: 7.3: git commit (do NOT push yet)
Record commit hash for reply + else Comment has no merit + Note over CLI: Note: will resolve with explanation in Step 9 + end + end + + CLI->>Worktree: Step 8: Push all fixes at once
git push REMOTE HEAD:JTBDTASK_BRANCH + + loop For each addressed comment + Note over CLI,GitHub: Step 9: Reply and resolve threads + CLI->>GitHub: POST .../comments/COMMENT_ID/replies
body: "Fixed in abc1234. [explanation]" + CLI->>GitHub: GraphQL resolveReviewThread
mutation { resolveReviewThread(...) } + end + + rect rgb(255, 245, 220) + Note over CLI,ApproveWF: Steps 10–11: Wait for CI, approve workflows + CLI->>GitHub: gh pr checks --watch + CLI->>ApproveWF: Invoke approve-workflows skill + ApproveWF-->>CLI: Workflows complete + end + + CLI->>GitHub: Step 12: Re-request Copilot review
gh pr edit --add-reviewer
copilot-pull-request-reviewer + Note over CCRA: CCRA reviews the updated diff + CLI->>GitHub: Step 13: Poll for new review findings + CCRA-->>GitHub: Post new review + GitHub-->>CLI: New review detected + + Note over CLI: If 0 new comments → break loop
If comments remain → next iteration + end + end + end + + rect rgb(230, 255, 230) + Note over CLI,ApproveWF: Step 14: Final workflow approval + CLI->>ApproveWF: Invoke approve-workflows skill + ApproveWF-->>CLI: All runs complete + end + + rect rgb(220, 255, 220) + Note over CLI,GitHub: Steps 15–21: Final checks, cleanup, and merge + CLI->>GitHub: Step 15: Verify only expected CI failure remains + CLI->>Worktree: Step 16: git worktree remove
"../review-copilot-pr-N" + CLI->>GitHub: Step 17: Verify base branch ≠ main + CLI->>GitHub: Step 18: Handle merge conflicts
(rebase + force-with-lease if needed) + CLI->>GitHub: Step 19: gh pr merge --merge --delete-branch + CLI->>GitHub: Step 20: gh issue close TASK_ISSUE + Note over CLI: Step 21: "SHEPHERD COMPLETE:
PR #N merged to BASE_BRANCH" + end +``` + +## Key Design Points + +### Local Worktree Resolution + +All review comment fixes happen in a **sibling git worktree** (`../review-copilot-pr-N`), not in the main working tree and not via the remote Copilot Coding Agent. This gives more reliable results because the local Copilot CLI has full context of the codebase and can run tests before committing. + +### "Fixed in \" Pattern + +When replying to each review comment, the reply includes the commit hash that addresses it: `"Fixed in abc1234. [explanation of the fix]"`. This creates a traceable link between the review feedback and the specific commit that resolved it. The thread is then resolved via the GraphQL `resolveReviewThread` mutation. + +### Max Review Rounds + +The review resolution loop runs for a **maximum of 8 iterations**. If the CCRA continues to generate new comments after 8 rounds, the skill reports failure and stops, requiring manual intervention. This prevents infinite loops when the review agent and the local fixer disagree. + +### Workflow Approval + +The `shepherd-task-approve-workflows-and-wait-for-completion` sub-skill is invoked at multiple points: before gathering comments (Step 5), after pushing fixes (Step 11), and before the final merge (Step 14). Each push to the PR branch triggers new workflow runs that require approval. diff --git a/plugins/shepherd-task/figure-05-post-mortem.md b/plugins/shepherd-task/figure-05-post-mortem.md new file mode 100644 index 000000000..372c245b3 --- /dev/null +++ b/plugins/shepherd-task/figure-05-post-mortem.md @@ -0,0 +1,75 @@ +# Figure 05: Post-Mortem Report Generation + +This diagram shows the detail of the `shepherd-task-create-post-mortem` skill. It is invoked from the `finally` block of `shepherd-task-given-list.ps1` (see Figure 01) so that a report is always generated, regardless of whether the run succeeded or failed. + +## Sequence Diagram + +```mermaid +sequenceDiagram + participant STGL as shepherd-task-given-list.ps1
(finally block) + participant Copilot as copilot --yolo + participant Skill as shepherd-task-create-post-mortem + participant LogDir as SHEPHERD_LOG_DIR
(shepherd-tasks-YYYYMMDD-HHMM/) + participant ParentDir as Parent Directory
(campaign context) + + rect rgb(255, 245, 220) + Note over STGL,Copilot: Invocation (runs on success OR failure) + STGL->>Copilot: echo prompt | copilot --yolo
"Invoke skill shepherd-task-create-post-mortem"
with SHEPHERD_LOG_DIR, SCRIPT_EXIT_CODE,
TASK_ISSUES, BASE_BRANCH, REPO + end + + rect rgb(220, 240, 255) + Note over Skill,LogDir: Step 1: Validate & collect run artifacts + Copilot->>Skill: Invoke skill + Skill->>LogDir: Validate directory exists + Skill->>LogDir: Collect phase artifacts:
• phase1-task-*.json (session data)
• phase2-task-*.json (session data)
• phase1-task-*.md (session shares)
• phase2-task-*.md (session shares) + end + + rect rgb(230, 245, 230) + Note over Skill,ParentDir: Step 2: Collect campaign context from parent directory + Skill->>ParentDir: Scan for supplementary files:
• *memory*.md
• *prompts.md
• *job-logs.txt + end + + rect rgb(240, 230, 255) + Note over Skill: Step 3: Extract quantitative metrics from artifacts + Note over Skill: Per-task metrics:
• Issues and PRs touched
• Phase 1 & Phase 2 durations
• Review rounds (Comments generated: N)
• Success/failure and failure signatures
• Idle/timeout markers
• Token usage (inputTokens / outputTokens) + end + + rect rgb(255, 240, 230) + Note over Skill: Step 4: Compose report (8 required sections) + Note over Skill: §1 Executive Summary
— outcome, completion rate, elapsed time + Note over Skill: §2 System Architecture
— CCA, CCRA, Local CLI responsibilities + Note over Skill: §3 Per-Task Metrics
— table: issue, PR, timings, rounds, result + Note over Skill: §4 Aggregate Statistics
— totals, averages, convergence signals + Note over Skill: §5 AI Credits and Token Usage
— measured values or "data unavailable" + Note over Skill: §6 Wall-Clock Timeline
— batch windows and notable events + Note over Skill: §7 Failure Analysis (if any)
— root causes, evidence, fixes + Note over Skill: §8 Observations & Recommendations
— what worked, what failed, improvements + end + + rect rgb(220, 255, 220) + Note over Skill,LogDir: Step 5: Write report + Skill->>LogDir: Write YYYYMMDD-HHMM-post-mortem.md + Note over Skill: Links use full URLs:
[#123](https://github.com/REPO/issues/123)
[#456](https://github.com/REPO/pull/456) + end + + Skill-->>Copilot: Report written + Copilot-->>STGL: Session complete +``` + +## Key Design Points + +### Always Runs + +The post-mortem skill is invoked from a `finally` block (PowerShell) or `trap EXIT` (Bash), so it executes for **all outcomes** — full success, partial failure, or early abort. The `SCRIPT_EXIT_CODE` input distinguishes these cases. + +### Evidence-Based + +The report is built entirely from local run artifacts (JSON session logs, markdown shares, OTEL traces). It does not require GitHub API calls unless local artifacts are insufficient, keeping it reliable even when network access is degraded. + +### Structured for Both Outcomes + +The 8-section structure serves both **successful runs** (throughput, convergence, quality metrics) and **failed runs** (root cause analysis, failure signatures, corrective actions). Sections are populated or marked "N/A" based on available data. + +### Link Formatting + +All issue and PR references are rendered as full Markdown hyperlinks using the `REPO` input — never plain-text `#123` references. Table-of-contents entries use plain text to avoid nested link issues. diff --git a/plugins/shepherd-task/scripts/install-task-shepherd.ps1 b/plugins/shepherd-task/scripts/install-task-shepherd.ps1 new file mode 100644 index 000000000..eaac5a46b --- /dev/null +++ b/plugins/shepherd-task/scripts/install-task-shepherd.ps1 @@ -0,0 +1,64 @@ +<# +.SYNOPSIS + Installs the shepherd-task plugin and skills into the user's Copilot home directory. + +.DESCRIPTION + Installs to: + ~/.copilot/plugins/shepherd-task/ (plugin with orchestration scripts) + ~/.copilot/skills/shepherd-task-* (skills, only if not already present) + +.EXAMPLE + ./install-task-shepherd.ps1 +#> + +$ErrorActionPreference = 'Stop' + +$CopilotHome = if ($env:COPILOT_HOME) { $env:COPILOT_HOME } else { Join-Path $HOME '.copilot' } + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition +$SourceRepo = (Resolve-Path (Join-Path $ScriptDir '..\..\..')).Path +$PluginSrc = (Resolve-Path (Join-Path $ScriptDir '..')).Path + +# Install plugin. +$pluginDest = Join-Path $CopilotHome 'plugins' 'shepherd-task' +if (-not (Test-Path $pluginDest)) { + New-Item -ItemType Directory -Path $pluginDest -Force | Out-Null +} +Copy-Item -Path (Join-Path $PluginSrc '*') -Destination $pluginDest -Recurse -Force +Write-Host "Installed plugin to $pluginDest" + +# Install skills (only if not already present). +$skills = @( + 'shepherd-task-from-assignment-to-ready' + 'shepherd-task-from-ready-to-merged-to-base' + 'shepherd-task-approve-workflows-and-wait-for-completion' +) + +$skillsInstalled = 0 +$skillsSkipped = 0 +foreach ($skill in $skills) { + $skillSrc = Join-Path $SourceRepo 'skills' $skill + $skillDest = Join-Path $CopilotHome 'skills' $skill + + if (-not (Test-Path $skillSrc -PathType Container)) { + Write-Warning "Source skill not found: $skillSrc" + continue + } + + if (Test-Path $skillDest -PathType Container) { + Write-Host "Skipped ~/.copilot/skills/$skill (already exists)" + $skillsSkipped++ + } else { + New-Item -ItemType Directory -Path $skillDest -Force | Out-Null + Copy-Item -Path (Join-Path $skillSrc '*') -Destination $skillDest -Recurse -Force + Write-Host "Installed ~/.copilot/skills/$skill" + $skillsInstalled++ + } +} + +Write-Host "" +Write-Host "Installation complete." +Write-Host " Plugin: $pluginDest" +Write-Host " Skills: $skillsInstalled installed, $skillsSkipped already present" +Write-Host "" +Write-Host "Verify with: copilot skill list" diff --git a/plugins/shepherd-task/scripts/install-task-shepherd.sh b/plugins/shepherd-task/scripts/install-task-shepherd.sh new file mode 100755 index 000000000..8a1757746 --- /dev/null +++ b/plugins/shepherd-task/scripts/install-task-shepherd.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# +# install-task-shepherd.sh — Installs the shepherd-task plugin and skills +# into the user's Copilot home directory. +# +# Installs to: +# ~/.copilot/plugins/shepherd-task/ (plugin with orchestration scripts) +# ~/.copilot/skills/shepherd-task-* (skills, only if not already present) +# +# Usage: ./install-task-shepherd.sh + +set -euo pipefail + +COPILOT_HOME="${COPILOT_HOME:-$HOME/.copilot}" + +# Resolve the source repo root (three levels up from plugins/shepherd-task/scripts/). +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SOURCE_REPO="$(cd "$SCRIPT_DIR/../../.." && pwd)" +PLUGIN_SRC="$SCRIPT_DIR/.." + +# Install plugin. +plugin_dest="$COPILOT_HOME/plugins/shepherd-task" +mkdir -p "$plugin_dest" +cp -R "$PLUGIN_SRC/." "$plugin_dest/" +echo "Installed plugin to $plugin_dest" + +# Install skills (only if not already present). +SKILLS=( + "shepherd-task-from-assignment-to-ready" + "shepherd-task-from-ready-to-merged-to-base" + "shepherd-task-approve-workflows-and-wait-for-completion" +) + +skills_installed=0 +skills_skipped=0 +for skill in "${SKILLS[@]}"; do + skill_src="$SOURCE_REPO/skills/$skill" + skill_dest="$COPILOT_HOME/skills/$skill" + + if [ ! -d "$skill_src" ]; then + echo "WARNING: Source skill not found: $skill_src" >&2 + continue + fi + + if [ -d "$skill_dest" ]; then + echo "Skipped ~/.copilot/skills/$skill (already exists)" + skills_skipped=$((skills_skipped + 1)) + else + mkdir -p "$skill_dest" + cp -R "$skill_src/." "$skill_dest/" + echo "Installed ~/.copilot/skills/$skill" + skills_installed=$((skills_installed + 1)) + fi +done + +echo "" +echo "Installation complete." +echo " Plugin: $plugin_dest" +echo " Skills: $skills_installed installed, $skills_skipped already present" +echo "" +echo "Verify with: copilot skill list" diff --git a/plugins/shepherd-task/scripts/shepherd-task-given-list.ps1 b/plugins/shepherd-task/scripts/shepherd-task-given-list.ps1 new file mode 100644 index 000000000..17b60b0e2 --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task-given-list.ps1 @@ -0,0 +1,107 @@ +<# +.SYNOPSIS + Shepherds a list of child Task issues end-to-end by invoking shepherd-task.ps1 for each. + +.DESCRIPTION + Takes a comma-separated list of issue numbers and invokes shepherd-task.ps1 + sequentially for each one. + +.PARAMETER TaskIssues + Comma-separated list of issue numbers (e.g., "1841,1842,1843"). + +.PARAMETER BaseBranch + The base branch the task PRs should target. This is never main. + +.PARAMETER Repo + Repository in OWNER/REPO format. +#> + +param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$TaskIssues, + + [Parameter(Mandatory = $true, Position = 1)] + [string]$BaseBranch, + + [Parameter(Mandatory = $true, Position = 2)] + [string]$Repo +) + +$ErrorActionPreference = 'Stop' +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$shepherdScript = Join-Path $scriptDir 'shepherd-task.ps1' +$scriptExitCode = 0 +$postMortemInvoked = $false + +$logDir = "shepherd-tasks-$(Get-Date -Format 'yyyyMMdd-HHmm')" +if (-not (Test-Path $logDir)) { + New-Item -ItemType Directory -Path $logDir | Out-Null +} +$logDirFull = (Resolve-Path $logDir).Path +Write-Output "Logging shepherd task files to $logDirFull" + +$issues = $TaskIssues -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } + +function Invoke-PostMortemSkill { + param( + [Parameter(Mandatory = $true)] + [int]$ScriptExitCode + ) + + if ($postMortemInvoked) { return } + $script:postMortemInvoked = $true + + try { + $timestamp = Get-Date -Format 'yyyyMMdd-HHmm' + $postMortemPath = Join-Path $logDirFull "$timestamp-post-mortem.md" + $sessionSharePath = Join-Path $logDirFull "post-mortem-session-$timestamp.md" + $sessionJsonPath = Join-Path $logDirFull "post-mortem-session-$timestamp.json" + + $prompt = @" +Invoke skill ``shepherd-task-create-post-mortem`` with these inputs: + +- SHEPHERD_LOG_DIR: $logDirFull +- SCRIPT_EXIT_CODE: $ScriptExitCode +- TASK_ISSUES: $TaskIssues +- BASE_BRANCH: $BaseBranch +- REPO: $Repo + +Write the report to: +- OUTPUT_FILE: $postMortemPath +"@ + + Write-Output "[shepherd-task] Generating post-mortem report at: $postMortemPath" + $prompt | copilot --yolo --output-format json --share $sessionSharePath > $sessionJsonPath + if ($LASTEXITCODE -ne 0) { + Write-Warning "[shepherd-task] Post-mortem skill invocation exited with code $LASTEXITCODE." + } + } + catch { + Write-Warning "[shepherd-task] Post-mortem skill invocation failed: $($_.Exception.Message)" + } +} + +try { + foreach ($issue in $issues) { + Write-Output "=== Shepherding task issue #$issue ===" + & $shepherdScript -TaskIssue $issue -BaseBranch $BaseBranch -Repo $Repo -LogDir $logDir + $issueExitCode = $LASTEXITCODE + if ($issueExitCode -ne 0) { + $script:scriptExitCode = $issueExitCode + throw "shepherd-task.ps1 failed for issue #$issue (exit code $issueExitCode)" + } + } + + Write-Output "=== All tasks shepherded successfully ===" +} +catch { + if ($script:scriptExitCode -eq 0) { + $script:scriptExitCode = 1 + } + Write-Error $_ +} +finally { + Invoke-PostMortemSkill -ScriptExitCode $scriptExitCode +} + +exit $scriptExitCode diff --git a/plugins/shepherd-task/scripts/shepherd-task-given-list.sh b/plugins/shepherd-task/scripts/shepherd-task-given-list.sh new file mode 100755 index 000000000..3958467eb --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task-given-list.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# +# shepherd-task-given-list.sh — Shepherds a list of child Task issues end-to-end +# by invoking shepherd-task.sh sequentially for each one. +# +# Usage: ./shepherd-task-given-list.sh +# TASK_ISSUES: comma-separated list of issue numbers (e.g., "1841,1842,1843") +# BASE_BRANCH: the base branch the task PRs should target (never main) +# REPO: repository in OWNER/REPO format + +set -euo pipefail + +if [[ $# -lt 3 ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +TASK_ISSUES="$1" +BASE_BRANCH="$2" +REPO="$3" + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +LOG_DIR="shepherd-tasks-$(date +%Y%m%d-%H%M)" +mkdir -p "$LOG_DIR" +LOG_DIR_FULL="$(cd "$LOG_DIR" && pwd)" +echo "Logging shepherd task files to $LOG_DIR_FULL" + +IFS=',' read -ra ISSUES <<< "$TASK_ISSUES" + +invoke_post_mortem_on_exit() { + local script_exit=$? + local timestamp post_mortem_path share_path json_path prompt pm_exit + + # Prevent duplicate invocation if EXIT trap is triggered more than once. + if [[ "${POST_MORTEM_INVOKED:-0}" == "1" ]]; then + return + fi + POST_MORTEM_INVOKED=1 + + # Always attempt post-mortem generation once shepherding log directory exists. + if [[ -z "${LOG_DIR_FULL:-}" ]]; then + return + fi + + timestamp="$(date +%Y%m%d-%H%M)" + post_mortem_path="$LOG_DIR_FULL/${timestamp}-post-mortem.md" + share_path="$LOG_DIR_FULL/post-mortem-session-${timestamp}.md" + json_path="$LOG_DIR_FULL/post-mortem-session-${timestamp}.json" + + prompt="Invoke skill \`shepherd-task-create-post-mortem\` with these inputs: + +- SHEPHERD_LOG_DIR: $LOG_DIR_FULL +- SCRIPT_EXIT_CODE: $script_exit +- TASK_ISSUES: $TASK_ISSUES +- BASE_BRANCH: $BASE_BRANCH +- REPO: $REPO + +Write the report to: +- OUTPUT_FILE: $post_mortem_path" + + echo "[shepherd-task] Generating post-mortem report at: $post_mortem_path" + set +e + printf '%s' "$prompt" | copilot --yolo --output-format json --share "$share_path" > "$json_path" + pm_exit=$? + set -e + + if [[ $pm_exit -ne 0 ]]; then + echo "[shepherd-task] WARNING: post-mortem skill invocation exited with code $pm_exit" >&2 + fi +} + +trap 'invoke_post_mortem_on_exit' EXIT + +for issue in "${ISSUES[@]}"; do + issue="$(echo "$issue" | tr -d '[:space:]')" + [[ -z "$issue" ]] && continue + echo "=== Shepherding task issue #${issue} ===" + "$SCRIPT_DIR/shepherd-task.sh" "$issue" "$BASE_BRANCH" "$REPO" "$LOG_DIR" +done + +echo "=== All tasks shepherded successfully ===" +exit 0 diff --git a/plugins/shepherd-task/scripts/shepherd-task-inspect-json.ps1 b/plugins/shepherd-task/scripts/shepherd-task-inspect-json.ps1 new file mode 100644 index 000000000..5ad36ebb1 --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task-inspect-json.ps1 @@ -0,0 +1,53 @@ +<# +.SYNOPSIS + Inspect a copilot JSON log file, showing the last N meaningful events. + +.PARAMETER JsonFile + Path to the JSON log file (relative or absolute). + +.PARAMETER Count + Number of messages to show (default: 20). +#> + +param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$JsonFile, + + [Parameter(Mandatory = $false, Position = 1)] + [int]$Count = 20 +) + +$events = Get-Content $JsonFile | + Where-Object { $_ -notmatch '"ephemeral":true' -and $_ -notmatch '"tool.execution_partial_result"' } | + ForEach-Object { + try { $_ | ConvertFrom-Json } catch { $null } + } | + Where-Object { $_ -ne $null } | + Select-Object -Last $Count + +$events | ForEach-Object { + $evt = $_ + $ts = if ($evt.timestamp) { ([datetime]$evt.timestamp).ToString("HH:mm:ss") } else { "--------" } + $summary = switch -Wildcard ($evt.type) { + "user.message" { $txt = $evt.data.content; "USER: " + $txt.Substring(0, [Math]::Min(120, $txt.Length)) } + "assistant.message" { + if ($evt.data.content) { + $txt = $evt.data.content; "ASST: " + $txt.Substring(0, [Math]::Min(120, $txt.Length)) + } else { + $names = ($evt.data.toolRequests | ForEach-Object { $_.name }) -join ", " + "ASST: [tool calls: $names]" + } + } + "tool.execution_start" { + $args_summary = ($evt.data.arguments | ConvertTo-Json -Compress -Depth 1) -replace '[\r\n]','' + if ($args_summary.Length -gt 80) { $args_summary = $args_summary.Substring(0,77) + "..." } + "TOOL> " + $evt.data.toolName + " :: " + $args_summary + } + "tool.execution_complete" { "TOOL< " + $(if ($evt.data.success) {"OK"} else {"FAIL"}) } + "assistant.reasoning" { $txt = $evt.data.content; "THINK: " + $txt.Substring(0, [Math]::Min(120, $txt.Length)) } + "assistant.turn_start" { $null } + "assistant.turn_end" { $null } + default { $evt.type } + } + if ($summary) { "$ts | $summary" } + } diff --git a/plugins/shepherd-task/scripts/shepherd-task-inspect-json.sh b/plugins/shepherd-task/scripts/shepherd-task-inspect-json.sh new file mode 100755 index 000000000..ae2ab8665 --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task-inspect-json.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# +# shepherd-task-inspect-json.sh — Show the last N meaningful events from a copilot JSON log. +# +# Usage: ./shepherd-task-inspect-json.sh [count] +# json-file: path to the JSON log file +# count: number of messages to show (default: 20) + +set -euo pipefail + +JSON_FILE="${1:?Usage: $0 [count]}" +COUNT="${2:-20}" + +grep -v '"ephemeral":true' "$JSON_FILE" | tail -n "$COUNT" | while IFS= read -r line; do + ts=$(echo "$line" | jq -r '.timestamp // empty' | xargs -I{} date -d {} +%H:%M:%S 2>/dev/null || echo "$line" | jq -r '.timestamp // "--------" | .[11:19]') + type=$(echo "$line" | jq -r '.type') + + case "$type" in + user.message) + content=$(echo "$line" | jq -r '.data.content[0:80]') + echo "$ts | USER: $content" + ;; + assistant.message) + content=$(echo "$line" | jq -r 'if .data.content != "" then .data.content[0:80] else "[tool calls: " + ([.data.toolRequests[].name] | join(", ")) + "]" end') + echo "$ts | ASST: $content" + ;; + tool.execution_start) + tool=$(echo "$line" | jq -r '.data.toolName') + desc=$(echo "$line" | jq -r '.data.arguments.description // ""') + echo "$ts | TOOL> $tool :: $desc" + ;; + tool.execution_complete) + status=$(echo "$line" | jq -r 'if .data.success then "OK" else "FAIL" end') + echo "$ts | TOOL< $status" + ;; + assistant.reasoning) + content=$(echo "$line" | jq -r '.data.content[0:80]') + echo "$ts | THINK: $content" + ;; + *) + echo "$ts | $type" + ;; + esac +done diff --git a/plugins/shepherd-task/scripts/shepherd-task-inspect-otel-token-summary.ps1 b/plugins/shepherd-task/scripts/shepherd-task-inspect-otel-token-summary.ps1 new file mode 100644 index 000000000..bd46d2419 --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task-inspect-otel-token-summary.ps1 @@ -0,0 +1,96 @@ +<# +.SYNOPSIS + Summarize token usage from OTel JSONL exports produced by shepherd-task. + +.PARAMETER Target + Path to a single OTel JSONL file or a directory containing *.jsonl files. + +.EXAMPLE + ./shepherd-task-inspect-otel-token-summary.ps1 ./shepherd-tasks-20260708-1244 + ./shepherd-task-inspect-otel-token-summary.ps1 ./phase1-otel-20260708-1244-4.jsonl +#> + +param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$Target +) + +$ErrorActionPreference = "Stop" + +function Get-TokenSummary { + param([string]$FilePath) + + $content = Get-Content $FilePath -Raw + $lines = Get-Content $FilePath | Where-Object { $_.Trim() -ne "" } + + $inputTokens = 0 + $outputTokens = 0 + $llmCalls = 0 + + foreach ($line in $lines) { + try { + $obj = $line | ConvertFrom-Json -Depth 20 + } catch { + continue + } + + # Walk resourceSpans -> scopeSpans -> spans + if ($obj.resourceSpans) { + foreach ($rs in $obj.resourceSpans) { + foreach ($ss in $rs.scopeSpans) { + foreach ($span in $ss.spans) { + if ($span.name -match "^chat ") { + $llmCalls++ + } + foreach ($attr in $span.attributes) { + if ($attr.key -eq "gen_ai.usage.input_tokens") { + $val = if ($attr.value.intValue) { $attr.value.intValue } elseif ($attr.value.stringValue) { [int]$attr.value.stringValue } else { 0 } + $inputTokens += $val + } + if ($attr.key -eq "gen_ai.usage.output_tokens") { + $val = if ($attr.value.intValue) { $attr.value.intValue } elseif ($attr.value.stringValue) { [int]$attr.value.stringValue } else { 0 } + $outputTokens += $val + } + } + } + } + } + } + } + + return @{ + InputTokens = $inputTokens + OutputTokens = $outputTokens + LLMCalls = $llmCalls + } +} + +Write-Output "=== OTel Token Usage Summary ===" +Write-Output "" + +$totalInput = 0 +$totalOutput = 0 +$totalCalls = 0 + +if (Test-Path $Target -PathType Container) { + $files = Get-ChildItem -Path $Target -Filter "*.jsonl" + if ($files.Count -eq 0) { + Write-Output "No .jsonl files found in $Target" + exit 1 + } + foreach ($f in $files) { + $summary = Get-TokenSummary -FilePath $f.FullName + $name = $f.Name + Write-Output ("{0,-50} {1,8} input {2,8} output {3,4} calls" -f $name, $summary.InputTokens, $summary.OutputTokens, $summary.LLMCalls) + $totalInput += $summary.InputTokens + $totalOutput += $summary.OutputTokens + $totalCalls += $summary.LLMCalls + } + Write-Output "" + Write-Output "--- TOTALS ---" + Write-Output ("{0,-50} {1,8} input {2,8} output {3,4} calls" -f "ALL FILES", $totalInput, $totalOutput, $totalCalls) +} else { + $summary = Get-TokenSummary -FilePath $Target + $name = Split-Path $Target -Leaf + Write-Output ("{0,-50} {1,8} input {2,8} output {3,4} calls" -f $name, $summary.InputTokens, $summary.OutputTokens, $summary.LLMCalls) +} diff --git a/plugins/shepherd-task/scripts/shepherd-task-inspect-otel-token-summary.sh b/plugins/shepherd-task/scripts/shepherd-task-inspect-otel-token-summary.sh new file mode 100755 index 000000000..97d09f54d --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task-inspect-otel-token-summary.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# +# shepherd-task-inspect-otel-token-summary.sh — Summarize token usage from OTel JSONL exports. +# +# Usage: ./shepherd-task-inspect-otel-token-summary.sh +# otel-jsonl-file: path to a single OTel JSONL file +# directory: path to a log directory; processes all *.jsonl files within + +set -euo pipefail + +TARGET="${1:?Usage: $0 }" + +summarize_file() { + local file="$1" + local basename + basename=$(basename "$file") + + local input_tokens output_tokens llm_calls + input_tokens=$(jq -r ' + [.. | objects | select(.key == "gen_ai.usage.input_tokens") | .value.intValue // .value.stringValue // 0 | tonumber] | add // 0 + ' "$file" 2>/dev/null || echo 0) + + output_tokens=$(jq -r ' + [.. | objects | select(.key == "gen_ai.usage.output_tokens") | .value.intValue // .value.stringValue // 0 | tonumber] | add // 0 + ' "$file" 2>/dev/null || echo 0) + + llm_calls=$(jq -r ' + [.. | objects | select(.name? // "" | test("^chat ")) ] | length + ' "$file" 2>/dev/null || echo 0) + + printf "%-50s %8s input %8s output %4s calls\n" "$basename" "$input_tokens" "$output_tokens" "$llm_calls" +} + +echo "=== OTel Token Usage Summary ===" +echo "" + +total_input=0 +total_output=0 +total_calls=0 + +if [[ -d "$TARGET" ]]; then + files=("$TARGET"/*.jsonl) + if [[ ${#files[@]} -eq 0 || ! -e "${files[0]}" ]]; then + echo "No .jsonl files found in $TARGET" + exit 1 + fi + for f in "${files[@]}"; do + summarize_file "$f" + input=$(jq -r '[.. | objects | select(.key == "gen_ai.usage.input_tokens") | .value.intValue // .value.stringValue // 0 | tonumber] | add // 0' "$f" 2>/dev/null || echo 0) + output=$(jq -r '[.. | objects | select(.key == "gen_ai.usage.output_tokens") | .value.intValue // .value.stringValue // 0 | tonumber] | add // 0' "$f" 2>/dev/null || echo 0) + calls=$(jq -r '[.. | objects | select(.name? // "" | test("^chat ")) ] | length' "$f" 2>/dev/null || echo 0) + total_input=$((total_input + input)) + total_output=$((total_output + output)) + total_calls=$((total_calls + calls)) + done + echo "" + echo "--- TOTALS ---" + printf "%-50s %8s input %8s output %4s calls\n" "ALL FILES" "$total_input" "$total_output" "$total_calls" +else + summarize_file "$TARGET" +fi diff --git a/plugins/shepherd-task/scripts/shepherd-task-monitor.ps1 b/plugins/shepherd-task/scripts/shepherd-task-monitor.ps1 new file mode 100644 index 000000000..ec56cdae9 --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task-monitor.ps1 @@ -0,0 +1,301 @@ +<# +.SYNOPSIS + Monitors an ongoing shepherd-task run by watching its log directory and polling GitHub. + +.DESCRIPTION + Watches a shepherd-task log directory for new files and polls the GitHub API + for real-time PR/review/CI status. Alerts on failures, stalls, and completion. + + Run this in a SEPARATE terminal while shepherd-task-given-list.ps1 is running. + +.PARAMETER LogDir + Path to the shepherd-tasks log directory (e.g., shepherd-tasks-20260718-1648). + +.PARAMETER Repo + Repository in OWNER/REPO format. + +.PARAMETER PollInterval + Seconds between polls (default: 30). + +.EXAMPLE + .\shepherd-task-monitor.ps1 ..\shepherd-tasks-20260718-1827 edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk +#> + +param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$LogDir, + + [Parameter(Mandatory = $true, Position = 1)] + [string]$Repo, + + [Parameter(Mandatory = $false, Position = 2)] + [int]$PollInterval = 30 +) + +$ErrorActionPreference = "Continue" + +if (-not (Test-Path $LogDir)) { + Write-Error "Log directory not found: $LogDir" + exit 1 +} + +$LogDirFull = (Resolve-Path $LogDir).Path +Write-Host "=== Shepherd Task Monitor ===" -ForegroundColor Cyan +Write-Host "Log directory: $LogDirFull" +Write-Host "Repository: $Repo" +Write-Host "Poll interval: ${PollInterval}s" +Write-Host "Press Ctrl+C to stop." +Write-Host "" + +# --- State tracking --- +$knownFiles = @{} +$issueState = @{} # issue# -> { phase1: done/running/none, phase2: done/running/none, pr: #, status: ... } +$lastActivity = Get-Date +$staleMinutes = 20 + +function Get-Timestamp { + return (Get-Date -Format 'HH:mm:ss') +} + +function Write-Monitor { + param([string]$Message, [string]$Color = "White") + Write-Host "[$(Get-Timestamp)] $Message" -ForegroundColor $Color +} + +function Write-Alert { + param([string]$Message) + Write-Host "[$(Get-Timestamp)] ⚠️ $Message" -ForegroundColor Yellow +} + +function Write-Success { + param([string]$Message) + Write-Host "[$(Get-Timestamp)] ✅ $Message" -ForegroundColor Green +} + +function Write-Failure { + param([string]$Message) + Write-Host "[$(Get-Timestamp)] ❌ $Message" -ForegroundColor Red +} + +# Extract issue number from a log filename like "phase1-task-20260718-1650-33.md" +function Get-IssueFromFilename { + param([string]$Name) + if ($Name -match '-(\d+)\.(md|json|jsonl)$') { + return $Matches[1] + } + return $null +} + +# Extract phase from a log filename +function Get-PhaseFromFilename { + param([string]$Name) + if ($Name -match '^(phase[12])') { + return $Matches[1] + } + return $null +} + +# Find PR linked to an issue +function Find-PR { + param([string]$Issue) + + # Strategy A: issue timeline + $pr = gh api "/repos/$Repo/issues/$Issue/timeline" ` + --jq '.[] | select(.event == "cross-referenced") | select(.source.issue.pull_request != null) | select(.source.issue.state == "open") | .source.issue.number' 2>$null | + Select-Object -First 1 + + if ($pr) { return $pr.Trim() } + + # Strategy B: PR body search + $pr = gh pr list -R $Repo --state all --json number,body ` + --jq ".[] | select(.body | test(`"#$Issue`")) | .number" 2>$null | + Select-Object -First 1 + + if ($pr) { return $pr.Trim() } + + return $null +} + +# Get PR status summary +function Get-PRStatus { + param([string]$PRNumber) + + $info = gh pr view $PRNumber -R $Repo --json state,isDraft,baseRefName,headRefName,mergeable,reviews,reviewRequests 2>$null | ConvertFrom-Json + if (-not $info) { return $null } + + $reviewCount = 0 + $copilotReviews = @() + if ($info.reviews) { + $copilotReviews = @($info.reviews | Where-Object { + $_.author.login -match 'copilot|copilot-pull-request-reviewer' + }) + $reviewCount = $copilotReviews.Count + } + + # Check for unresolved review comments + $comments = gh api "/repos/$Repo/pulls/$PRNumber/comments" ` + --jq '.[] | select(.user.login | test("copilot-pull-request-reviewer|Copilot")) | .id' 2>$null + $commentCount = if ($comments) { @($comments).Count } else { 0 } + + # Check CI + $ciFailures = gh pr checks $PRNumber -R $Repo --json name,state,bucket ` + --jq '.[] | select(.bucket == "fail") | select(.name != "No remove-before-merge directories") | .name' 2>$null + $ciStatus = if ([string]::IsNullOrWhiteSpace($ciFailures)) { "passing" } else { "failing" } + + return @{ + State = $info.state + IsDraft = $info.isDraft + Base = $info.baseRefName + Mergeable = $info.mergeable + ReviewRounds = $reviewCount + Comments = $commentCount + CI = $ciStatus + } +} + +# --- Main monitor loop --- +$iteration = 0 +while ($true) { + $iteration++ + $now = Get-Date + $newFiles = @() + + # Scan log directory for new or changed files + $currentFiles = Get-ChildItem $LogDirFull -File -ErrorAction SilentlyContinue + foreach ($file in $currentFiles) { + $key = $file.Name + if (-not $knownFiles.ContainsKey($key)) { + $knownFiles[$key] = $file.Length + $newFiles += $file + $lastActivity = $now + } + elseif ($knownFiles[$key] -ne $file.Length) { + $knownFiles[$key] = $file.Length + $lastActivity = $now + } + } + + # Process new files + foreach ($file in $newFiles) { + $issue = Get-IssueFromFilename $file.Name + $phase = Get-PhaseFromFilename $file.Name + $ext = [System.IO.Path]::GetExtension($file.Name) + + if ($issue -and $phase -and $ext -eq '.md') { + # .md means the session exported — phase complete + if (-not $issueState.ContainsKey($issue)) { + $issueState[$issue] = @{ phase1 = 'none'; phase2 = 'none'; pr = $null; status = 'unknown' } + } + + if ($phase -eq 'phase1') { + $issueState[$issue].phase1 = 'done' + Write-Monitor "Issue #$($issue): Phase 1 session exported ($($file.Name))" + + # Try to find the PR + $pr = Find-PR $issue + if ($pr) { + $issueState[$issue].pr = $pr + Write-Monitor "Issue #$($issue): Linked to PR #$($pr)" + } + } + elseif ($phase -eq 'phase2') { + $issueState[$issue].phase2 = 'done' + Write-Monitor "Issue #$($issue): Phase 2 session exported ($($file.Name))" + } + } + elseif ($issue -and $phase -and $ext -eq '.json') { + # .json appearing means the copilot process exited (stdout captured) + if (-not $issueState.ContainsKey($issue)) { + $issueState[$issue] = @{ phase1 = 'none'; phase2 = 'none'; pr = $null; status = 'unknown' } + } + } + } + + # For issues with known PRs, poll GitHub for status + $activeIssue = $null + foreach ($issue in $issueState.Keys) { + $state = $issueState[$issue] + $pr = $state.pr + + # Detect the currently active issue (has a PR but phase2 not done) + if ($pr -and $state.phase2 -ne 'done') { + $activeIssue = $issue + } + + # If phase2 just completed, check final PR state + if ($pr -and $state.phase2 -eq 'done' -and $state.status -ne 'merged' -and $state.status -ne 'failed') { + $prStatus = Get-PRStatus $pr + if ($prStatus) { + if ($prStatus.State -eq 'MERGED') { + $state.status = 'merged' + Write-Success "Issue #$($issue): PR #$($pr) MERGED ($($prStatus.ReviewRounds) review rounds)" + } + elseif ($prStatus.State -eq 'CLOSED') { + $state.status = 'failed' + Write-Failure "Issue #$($issue): PR #$($pr) CLOSED (not merged)" + } + else { + $state.status = 'open' + Write-Failure "Issue #$($issue): PR #$($pr) still OPEN after Phase 2 exited" + } + } + } + } + + # Poll active PR for real-time status + if ($activeIssue) { + $pr = $issueState[$activeIssue].pr + if ($pr) { + $prStatus = Get-PRStatus $pr + if ($prStatus) { + $statusLine = "Issue #$($activeIssue) PR #$($pr): state=$($prStatus.State) draft=$($prStatus.IsDraft) " + + "reviews=$($prStatus.ReviewRounds) comments=$($prStatus.Comments) CI=$($prStatus.CI)" + + if ($prStatus.State -eq 'MERGED') { + Write-Success $statusLine + $issueState[$activeIssue].status = 'merged' + } + elseif ($prStatus.CI -eq 'failing') { + Write-Alert $statusLine + } + else { + Write-Monitor $statusLine -Color "Gray" + } + } + } + } + # If no active issue detected yet but files exist, try to find one + elseif ($iteration -eq 1 -or $newFiles.Count -gt 0) { + foreach ($issue in $issueState.Keys) { + $state = $issueState[$issue] + if (-not $state.pr) { + $pr = Find-PR $issue + if ($pr) { + $state.pr = $pr + Write-Monitor "Issue #$($issue): Found PR #$($pr)" + } + } + } + } + + # Stale detection + $staleDuration = ($now - $lastActivity).TotalMinutes + if ($staleDuration -gt $staleMinutes) { + Write-Alert "No activity for $([Math]::Round($staleDuration, 0)) minutes — shepherd may be stalled or waiting for CCRA" + } + + # Periodic summary (every 5 iterations) + if ($iteration % 5 -eq 0 -and $issueState.Count -gt 0) { + Write-Host "" + Write-Host "[$(Get-Timestamp)] === Summary ===" -ForegroundColor Cyan + foreach ($issue in $issueState.Keys | Sort-Object) { + $s = $issueState[$issue] + $prLabel = if ($s.pr) { "PR #$($s.pr)" } else { "no PR" } + $statusLabel = $s.status + Write-Host " Issue #$($issue) : P1=$($s.phase1) P2=$($s.phase2) $prLabel status=$statusLabel" + } + Write-Host "" + } + + Start-Sleep -Seconds $PollInterval +} diff --git a/plugins/shepherd-task/scripts/shepherd-task-monitor.sh b/plugins/shepherd-task/scripts/shepherd-task-monitor.sh new file mode 100644 index 000000000..2fedf8649 --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task-monitor.sh @@ -0,0 +1,249 @@ +#!/usr/bin/env bash +# +# shepherd-task-monitor.sh — Monitors an ongoing shepherd-task run. +# +# Watches a shepherd-task log directory for new files and polls GitHub +# for real-time PR/review/CI status. Alerts on failures, stalls, and completion. +# +# Run this in a SEPARATE terminal while shepherd-task-given-list.sh is running. +# +# Usage: ./shepherd-task-monitor.sh [POLL_INTERVAL] + +set -uo pipefail + +LOG_DIR="${1:?Usage: $0 [POLL_INTERVAL]}" +REPO="${2:?Usage: $0 [POLL_INTERVAL]}" +POLL_INTERVAL="${3:-30}" +STALE_MINUTES=20 + +if [[ ! -d "$LOG_DIR" ]]; then + echo "Error: Log directory not found: $LOG_DIR" >&2 + exit 1 +fi + +LOG_DIR_FULL="$(cd "$LOG_DIR" && pwd)" + +echo "=== Shepherd Task Monitor ===" +echo "Log directory: $LOG_DIR_FULL" +echo "Repository: $REPO" +echo "Poll interval: ${POLL_INTERVAL}s" +echo "Press Ctrl+C to stop." +echo "" + +# --- State tracking --- +declare -A KNOWN_FILES # filename -> size +declare -A ISSUE_PHASE1 # issue# -> done|none +declare -A ISSUE_PHASE2 # issue# -> done|none +declare -A ISSUE_PR # issue# -> PR number +declare -A ISSUE_STATUS # issue# -> merged|failed|open|unknown +LAST_ACTIVITY=$(date +%s) + +timestamp() { date +%H:%M:%S; } + +monitor() { echo "[$(timestamp)] $*"; } +alert() { echo "[$(timestamp)] ⚠️ $*"; } +success() { echo "[$(timestamp)] ✅ $*"; } +failure() { echo "[$(timestamp)] ❌ $*"; } + +# Extract issue number from filename like "phase1-task-20260718-1650-33.md" +get_issue() { + local name="$1" + if [[ "$name" =~ -([0-9]+)\.(md|json|jsonl)$ ]]; then + echo "${BASH_REMATCH[1]}" + fi +} + +# Extract phase from filename +get_phase() { + local name="$1" + if [[ "$name" =~ ^(phase[12]) ]]; then + echo "${BASH_REMATCH[1]}" + fi +} + +# Find PR linked to an issue +find_pr() { + local issue="$1" + local pr="" + + # Strategy A: issue timeline + pr=$(gh api "/repos/$REPO/issues/$issue/timeline" \ + --jq '.[] | select(.event == "cross-referenced") | select(.source.issue.pull_request != null) | select(.source.issue.state == "open") | .source.issue.number' 2>/dev/null | head -1) + if [[ -n "$pr" ]]; then echo "$pr"; return 0; fi + + # Strategy B: PR body search + pr=$(gh pr list -R "$REPO" --state all --json number,body \ + --jq ".[] | select(.body | test(\"#$issue\")) | .number" 2>/dev/null | head -1) + if [[ -n "$pr" ]]; then echo "$pr"; return 0; fi + + return 1 +} + +# Get PR status as a one-line summary +get_pr_status() { + local pr_number="$1" + + local state draft base mergeable + local info + info=$(gh pr view "$pr_number" -R "$REPO" --json state,isDraft,baseRefName,mergeable 2>/dev/null) + if [[ -z "$info" ]]; then return 1; fi + + state=$(echo "$info" | jq -r '.state') + draft=$(echo "$info" | jq -r '.isDraft') + mergeable=$(echo "$info" | jq -r '.mergeable') + + # Count Copilot review rounds + local review_count + review_count=$(gh api "/repos/$REPO/pulls/$pr_number/reviews" \ + --jq '[.[] | select(.user.login | test("copilot-pull-request-reviewer|Copilot"))] | length' 2>/dev/null || echo "0") + + # Count open review comments from Copilot + local comment_count + comment_count=$(gh api "/repos/$REPO/pulls/$pr_number/comments" \ + --jq '[.[] | select(.user.login | test("copilot-pull-request-reviewer|Copilot"))] | length' 2>/dev/null || echo "0") + + # Check CI + local ci_failures ci_status + ci_failures=$(gh pr checks "$pr_number" -R "$REPO" --json name,state,bucket \ + --jq '.[] | select(.bucket == "fail") | select(.name != "No remove-before-merge directories") | .name' 2>/dev/null) + if [[ -z "$ci_failures" ]]; then ci_status="passing"; else ci_status="failing"; fi + + echo "state=$state draft=$draft reviews=$review_count comments=$comment_count CI=$ci_status" +} + +# --- Main monitor loop --- +ITERATION=0 + +while true; do + ITERATION=$((ITERATION + 1)) + NOW=$(date +%s) + NEW_FILES=() + + # Scan log directory for new or changed files + while IFS= read -r line; do + filename=$(basename "$line") + size=$(stat -f%z "$line" 2>/dev/null || stat -c%s "$line" 2>/dev/null || echo "0") + + if [[ -z "${KNOWN_FILES[$filename]+x}" ]]; then + KNOWN_FILES[$filename]="$size" + NEW_FILES+=("$filename") + LAST_ACTIVITY=$NOW + elif [[ "${KNOWN_FILES[$filename]}" != "$size" ]]; then + KNOWN_FILES[$filename]="$size" + LAST_ACTIVITY=$NOW + fi + done < <(find "$LOG_DIR_FULL" -maxdepth 1 -type f 2>/dev/null) + + # Process new files + for filename in "${NEW_FILES[@]}"; do + issue=$(get_issue "$filename") + phase=$(get_phase "$filename") + ext="${filename##*.}" + + [[ -z "$issue" || -z "$phase" ]] && continue + + # Initialize issue state if needed + : "${ISSUE_PHASE1[$issue]:=none}" + : "${ISSUE_PHASE2[$issue]:=none}" + : "${ISSUE_STATUS[$issue]:=unknown}" + + if [[ "$ext" == "md" ]]; then + if [[ "$phase" == "phase1" ]]; then + ISSUE_PHASE1[$issue]="done" + monitor "Issue #$issue: Phase 1 session exported ($filename)" + + # Try to find the PR + pr=$(find_pr "$issue") || true + if [[ -n "$pr" ]]; then + ISSUE_PR[$issue]="$pr" + monitor "Issue #$issue: Linked to PR #$pr" + fi + elif [[ "$phase" == "phase2" ]]; then + ISSUE_PHASE2[$issue]="done" + monitor "Issue #$issue: Phase 2 session exported ($filename)" + fi + fi + done + + # For issues with known PRs, check status + ACTIVE_ISSUE="" + for issue in "${!ISSUE_PR[@]}"; do + pr="${ISSUE_PR[$issue]}" + + # Detect active issue (has PR, phase2 not done) + if [[ "${ISSUE_PHASE2[$issue]}" != "done" ]]; then + ACTIVE_ISSUE="$issue" + fi + + # If phase2 just completed, check final state + if [[ "${ISSUE_PHASE2[$issue]}" == "done" && "${ISSUE_STATUS[$issue]}" != "merged" && "${ISSUE_STATUS[$issue]}" != "failed" ]]; then + pr_state=$(gh pr view "$pr" -R "$REPO" --json state --jq '.state' 2>/dev/null) + review_count=$(gh api "/repos/$REPO/pulls/$pr/reviews" \ + --jq '[.[] | select(.user.login | test("copilot-pull-request-reviewer|Copilot"))] | length' 2>/dev/null || echo "?") + if [[ "$pr_state" == "MERGED" ]]; then + ISSUE_STATUS[$issue]="merged" + success "Issue #$issue: PR #$pr MERGED ($review_count review rounds)" + elif [[ "$pr_state" == "CLOSED" ]]; then + ISSUE_STATUS[$issue]="failed" + failure "Issue #$issue: PR #$pr CLOSED (not merged)" + else + ISSUE_STATUS[$issue]="open" + failure "Issue #$issue: PR #$pr still OPEN after Phase 2 exited" + fi + fi + done + + # Poll active PR for real-time status + if [[ -n "$ACTIVE_ISSUE" && -n "${ISSUE_PR[$ACTIVE_ISSUE]+x}" ]]; then + pr="${ISSUE_PR[$ACTIVE_ISSUE]}" + status_line=$(get_pr_status "$pr") + if [[ -n "$status_line" ]]; then + full_line="Issue #$ACTIVE_ISSUE PR #$pr: $status_line" + if [[ "$status_line" == *"state=MERGED"* ]]; then + success "$full_line" + ISSUE_STATUS[$ACTIVE_ISSUE]="merged" + elif [[ "$status_line" == *"CI=failing"* ]]; then + alert "$full_line" + else + monitor "$full_line" + fi + fi + fi + + # Try to find PRs for issues that don't have one yet + if [[ $ITERATION -eq 1 ]] || [[ ${#NEW_FILES[@]} -gt 0 ]]; then + for issue in "${!ISSUE_PHASE1[@]}"; do + if [[ -z "${ISSUE_PR[$issue]+x}" ]]; then + pr=$(find_pr "$issue") || true + if [[ -n "$pr" ]]; then + ISSUE_PR[$issue]="$pr" + monitor "Issue #$issue: Found PR #$pr" + fi + fi + done + fi + + # Stale detection + STALE_SECONDS=$(( NOW - LAST_ACTIVITY )) + STALE_MINS=$(( STALE_SECONDS / 60 )) + if [[ $STALE_MINS -ge $STALE_MINUTES ]]; then + alert "No activity for ${STALE_MINS} minutes — shepherd may be stalled or waiting for CCRA" + fi + + # Periodic summary (every 5 iterations) + if [[ $(( ITERATION % 5 )) -eq 0 ]] && [[ ${#ISSUE_PHASE1[@]} -gt 0 ]]; then + echo "" + echo "[$(timestamp)] === Summary ===" + for issue in $(echo "${!ISSUE_PHASE1[@]}" | tr ' ' '\n' | sort -n); do + p1="${ISSUE_PHASE1[$issue]}" + p2="${ISSUE_PHASE2[$issue]:-none}" + pr="${ISSUE_PR[$issue]:-none}" + status="${ISSUE_STATUS[$issue]:-unknown}" + [[ "$pr" != "none" ]] && pr="PR #$pr" + echo " Issue #$issue : P1=$p1 P2=$p2 $pr status=$status" + done + echo "" + fi + + sleep "$POLL_INTERVAL" +done diff --git a/plugins/shepherd-task/scripts/shepherd-task.ps1 b/plugins/shepherd-task/scripts/shepherd-task.ps1 new file mode 100644 index 000000000..fe9b6c0c0 --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task.ps1 @@ -0,0 +1,224 @@ +<# +.SYNOPSIS + Shepherds a child Task issue end-to-end: from Copilot assignment through merge. + +.DESCRIPTION + Orchestrates two phases by launching separate `copilot --yolo` sessions: + Phase 1: Assignment to Ready for Review + Phase 2: Ready for Review to Merged + + Between phases, the script verifies state using gh CLI (not copilot exit codes). + +.PARAMETER TaskIssue + The issue number (e.g., 1841) or URL of the child task to shepherd. + +.PARAMETER BaseBranch + The base branch the task PR should target. + +.PARAMETER Repo + Repository in OWNER/REPO format. +#> + +param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$TaskIssue, + + [Parameter(Mandatory = $true, Position = 1)] + [string]$BaseBranch, + + [Parameter(Mandatory = $true, Position = 2)] + [string]$Repo, + + [Parameter(Mandatory = $false, Position = 3)] + [string]$LogDir = "shepherd-tasks-$(Get-Date -Format 'yyyyMMdd-HHmm')" +) + +$ErrorActionPreference = "Stop" + +if (-not (Test-Path $LogDir)) { + New-Item -ItemType Directory -Path $LogDir | Out-Null +} + +function Write-Status($msg) { + Write-Output "[shepherd-task] $msg" +} + +function Write-Fail($msg) { + Write-Output "[shepherd-task] FAILED: $msg" +} + +function Write-Ok($msg) { + Write-Output "[shepherd-task] $msg" +} + +# --- Helper: Find the PR linked to the task issue --- +function Find-LinkedPR { + # Strategy A: Issue timeline for cross-referenced PRs + $prNumber = gh api "/repos/$Repo/issues/$TaskIssue/timeline" ` + --jq '.[] | select(.event == "cross-referenced") | select(.source.issue.pull_request != null) | select(.source.issue.state == "open") | .source.issue.number' 2>$null | + Select-Object -First 1 + + if ($prNumber) { return $prNumber.Trim() } + + # Strategy B: Search PR bodies for the issue number + $prNumber = gh pr list -R $Repo --state open --json number,body ` + --jq ".[] | select(.body | test(`"#$TaskIssue`")) | .number" 2>$null | + Select-Object -First 1 + + if ($prNumber) { return $prNumber.Trim() } + + # Strategy C: Title or branch name match + $prNumber = gh pr list -R $Repo --state open --json number,title,headRefName ` + --jq ".[] | select((.title | test(`"$TaskIssue`"; `"i`")) or (.headRefName | test(`"$TaskIssue`"))) | .number" 2>$null | + Select-Object -First 1 + + if ($prNumber) { return $prNumber.Trim() } + + return $null +} + +# --- Helper: Verify all CI checks pass (excluding expected failure) --- +function Test-CIPassing { + param([string]$PRNumber) + + $failures = gh pr checks $PRNumber -R $Repo --json name,state,bucket ` + --jq '.[] | select(.bucket == "fail") | select(.name != "No remove-before-merge directories") | .name' 2>$null + + return [string]::IsNullOrWhiteSpace($failures) +} + +# --- Helper: Check for unresolved bot review comments --- +function Test-NoUnresolvedReviews { + param([string]$PRNumber) + + $repoOwner = ($Repo -split '/')[0] + $repoName = ($Repo -split '/')[1] + + $unresolved = gh api graphql -F owner=$repoOwner -F name=$repoName -F number=$PRNumber -f query=' + query($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name) { + pullRequest(number: $number) { + reviewThreads(first: 100) { + nodes { isResolved comments(first: 1) { nodes { author { login } } } } + } + } + } + }' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments.nodes[0].author.login' 2>$null + + return [string]::IsNullOrWhiteSpace($unresolved) +} + +# ============================================================================= +# PHASE 1: Assignment to Ready for Review +# ============================================================================= + +# Idempotency: skip Phase 1 if a PR already exists for this issue +$prNumber = Find-LinkedPR +if ($prNumber) { + Write-Status "PR #$prNumber already exists for issue #$TaskIssue — skipping Phase 1." +} else { + Write-Status "Phase 1: Launching copilot --yolo for task #$TaskIssue" + + $phase1Prompt = @" +Invoke skill ``shepherd-task-from-assignment-to-ready`` with these inputs: + +- TASK_ISSUE: $TaskIssue +- BASE_BRANCH: $BaseBranch +- REPO: $Repo +"@ + + Write-Status "Phase 1 prompt: $phase1Prompt" + $phase1Share = Join-Path $LogDir "phase1-task-$(Get-Date -Format 'yyyyMMdd-HHmm')-$TaskIssue.md" + $phase1Json = Join-Path $LogDir "phase1-task-$(Get-Date -Format 'yyyyMMdd-HHmm')-$TaskIssue.json" + $phase1Otel = Join-Path (Resolve-Path $LogDir) "phase1-otel-$(Get-Date -Format 'yyyyMMdd-HHmm')-$TaskIssue.jsonl" + $env:COPILOT_OTEL_FILE_EXPORTER_PATH = $phase1Otel + $phase1Prompt | copilot --yolo --output-format json --share $phase1Share > $phase1Json + Remove-Item Env:\COPILOT_OTEL_FILE_EXPORTER_PATH -ErrorAction SilentlyContinue + + Write-Status "Phase 1: copilot exited. Verifying state..." + + # --- Verify Phase 1 outcome --- + $prNumber = Find-LinkedPR + if (-not $prNumber) { + Write-Fail "No open PR found linked to issue #$TaskIssue after Phase 1." + exit 1 + } +} +Write-Status "Found PR #$prNumber" + +# Verify base branch +$actualBase = gh pr view $prNumber -R $Repo --json baseRefName --jq '.baseRefName' +if ($actualBase -ne $BaseBranch) { + Write-Status "PR base is '$actualBase', fixing to '$BaseBranch'..." + gh pr edit $prNumber -R $Repo --base $BaseBranch +} + +# Verify CI passing +if (-not (Test-CIPassing $prNumber)) { + Write-Fail "CI checks not passing on PR #$prNumber after Phase 1." + exit 1 +} + +# Verify no unresolved reviews +if (-not (Test-NoUnresolvedReviews $prNumber)) { + Write-Fail "Unresolved review comments remain on PR #$prNumber after Phase 1." + exit 1 +} + +Write-Ok "Phase 1 VERIFIED: PR #$prNumber is ready. CI passing, no unresolved comments." + +# ============================================================================= +# PHASE 2: Ready for Review to Merged +# ============================================================================= + +# Idempotency: skip Phase 2 if PR is already merged +$prState = gh pr view $prNumber -R $Repo --json state --jq '.state' +if ($prState -eq "MERGED") { + Write-Ok "PR #$prNumber already merged — skipping Phase 2." +} else { + Write-Status "Phase 2: Launching copilot --yolo for PR #$prNumber" + + $phase2Prompt = @" +Invoke skill ``shepherd-task-from-ready-to-merged-to-base`` with these inputs: + +- TASK_ISSUE: $TaskIssue +- BASE_BRANCH: $BaseBranch +- REPO: $Repo +- PR_NUMBER: $prNumber +"@ + + Write-Status "Phase 2 prompt: $phase2Prompt" + $phase2Share = Join-Path $LogDir "phase2-task-$(Get-Date -Format 'yyyyMMdd-HHmm')-$TaskIssue.md" + $phase2Json = Join-Path $LogDir "phase2-task-$(Get-Date -Format 'yyyyMMdd-HHmm')-$TaskIssue.json" + $phase2Otel = Join-Path (Resolve-Path $LogDir) "phase2-otel-$(Get-Date -Format 'yyyyMMdd-HHmm')-$TaskIssue.jsonl" + $env:COPILOT_OTEL_FILE_EXPORTER_PATH = $phase2Otel + $phase2Prompt | copilot --yolo --output-format json --share $phase2Share > $phase2Json + Remove-Item Env:\COPILOT_OTEL_FILE_EXPORTER_PATH -ErrorAction SilentlyContinue + + Write-Status "Phase 2: copilot exited. Verifying state..." + + # --- Verify Phase 2 outcome --- + $prState = gh pr view $prNumber -R $Repo --json state --jq '.state' + if ($prState -ne "MERGED") { + Write-Fail "PR #$prNumber is in state '$prState', expected MERGED." + exit 1 + } +} + +# Verify merged into correct branch +$mergedBase = gh pr view $prNumber -R $Repo --json baseRefName --jq '.baseRefName' +if ($mergedBase -ne $BaseBranch) { + Write-Fail "PR #$prNumber was merged into '$mergedBase', expected '$BaseBranch'." + exit 1 +} + +# Verify issue is closed +$issueState = gh issue view $TaskIssue -R $Repo --json state --jq '.state' +if ($issueState -ne "CLOSED") { + Write-Status "Issue #$TaskIssue still open, closing..." + gh issue close $TaskIssue -R $Repo +} + +Write-Ok "SHEPHERD TASK COMPLETE: Task #$TaskIssue has been fully shepherded." +Write-Ok "PR #$prNumber merged to $BaseBranch." +exit 0 diff --git a/plugins/shepherd-task/scripts/shepherd-task.sh b/plugins/shepherd-task/scripts/shepherd-task.sh new file mode 100755 index 000000000..bc4b9df3e --- /dev/null +++ b/plugins/shepherd-task/scripts/shepherd-task.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash +# +# shepherd-task.sh — Shepherds a child Task issue end-to-end: +# from Copilot assignment through merge. +# +# Orchestrates two phases by launching separate `copilot --yolo` sessions. +# Between phases, verifies state using gh CLI (not copilot exit codes). +# +# Usage: ./shepherd-task.sh + +set -euo pipefail + +TASK_ISSUE="${1:?Usage: $0 [LOG_DIR]}" +BASE_BRANCH="${2:?Usage: $0 [LOG_DIR]}" +REPO="${3:?Usage: $0 [LOG_DIR]}" +LOG_DIR="${4:-shepherd-tasks-$(date +%Y%m%d-%H%M)}" + +mkdir -p "$LOG_DIR" + +# --- Helpers --- + +status() { echo "[shepherd-task] $*"; } +fail() { echo "[shepherd-task] FAILED: $*"; exit 1; } +ok() { echo "[shepherd-task] $*"; } + +# Find the PR linked to the task issue using three strategies. +find_linked_pr() { + local pr_number="" + + # Strategy A: Issue timeline for cross-referenced PRs + pr_number=$(gh api "/repos/$REPO/issues/$TASK_ISSUE/timeline" \ + --jq '.[] | select(.event == "cross-referenced") | select(.source.issue.pull_request != null) | select(.source.issue.state == "open") | .source.issue.number' 2>/dev/null | head -1) + + if [[ -n "$pr_number" ]]; then echo "$pr_number"; return 0; fi + + # Strategy B: Search PR bodies for the issue number + pr_number=$(gh pr list -R "$REPO" --state open --json number,body \ + --jq ".[] | select(.body | test(\"#$TASK_ISSUE\")) | .number" 2>/dev/null | head -1) + + if [[ -n "$pr_number" ]]; then echo "$pr_number"; return 0; fi + + # Strategy C: Title or branch name match + pr_number=$(gh pr list -R "$REPO" --state open --json number,title,headRefName \ + --jq ".[] | select((.title | test(\"$TASK_ISSUE\"; \"i\")) or (.headRefName | test(\"$TASK_ISSUE\"))) | .number" 2>/dev/null | head -1) + + if [[ -n "$pr_number" ]]; then echo "$pr_number"; return 0; fi + + return 1 +} + +# Verify all CI checks pass (excluding expected failure). +ci_passing() { + local pr_number="$1" + local failures + failures=$(gh pr checks "$pr_number" -R "$REPO" --json name,state,bucket \ + --jq '.[] | select(.bucket == "fail") | select(.name != "No remove-before-merge directories") | .name' 2>/dev/null) + + [[ -z "$failures" ]] +} + +# Check for unresolved bot review comments. +no_unresolved_reviews() { + local pr_number="$1" + local repo_owner="${REPO%%/*}" + local repo_name="${REPO##*/}" + local unresolved + unresolved=$(gh api graphql -F owner="$repo_owner" -F name="$repo_name" -F number="$pr_number" -f query=' + query($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name) { + pullRequest(number: $number) { + reviewThreads(first: 100) { + nodes { isResolved comments(first: 1) { nodes { author { login } } } } + } + } + } + }' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments.nodes[0].author.login' 2>/dev/null) + + [[ -z "$unresolved" ]] +} + +# ============================================================================= +# PHASE 1: Assignment to Ready for Review +# ============================================================================= + +# Idempotency: skip Phase 1 if a PR already exists for this issue +PR_NUMBER=$(find_linked_pr) || true +if [[ -n "$PR_NUMBER" ]]; then + status "PR #$PR_NUMBER already exists for issue #$TASK_ISSUE — skipping Phase 1." +else + status "Phase 1: Launching copilot --yolo for task #$TASK_ISSUE" + + PHASE1_PROMPT="Invoke skill \`shepherd-task-from-assignment-to-ready\` with these inputs: + +- TASK_ISSUE: $TASK_ISSUE +- BASE_BRANCH: $BASE_BRANCH +- REPO: $REPO" + + status "Phase 1 prompt:" + echo "$PHASE1_PROMPT" + PHASE1_SHARE="$LOG_DIR/phase1-task-$(date +%Y%m%d-%H%M)-$TASK_ISSUE.md" + PHASE1_JSON="$LOG_DIR/phase1-task-$(date +%Y%m%d-%H%M)-$TASK_ISSUE.json" + PHASE1_OTEL="$(cd "$LOG_DIR" && pwd)/phase1-otel-$(date +%Y%m%d-%H%M)-$TASK_ISSUE.jsonl" + export COPILOT_OTEL_FILE_EXPORTER_PATH="$PHASE1_OTEL" + echo "$PHASE1_PROMPT" | copilot --yolo --output-format json --share "$PHASE1_SHARE" > "$PHASE1_JSON" + unset COPILOT_OTEL_FILE_EXPORTER_PATH + + status "Phase 1: copilot exited. Verifying state..." + + # --- Verify Phase 1 outcome --- + PR_NUMBER=$(find_linked_pr) || fail "No open PR found linked to issue #$TASK_ISSUE after Phase 1." +fi +status "Found PR #$PR_NUMBER" + +# Verify base branch +ACTUAL_BASE=$(gh pr view "$PR_NUMBER" -R "$REPO" --json baseRefName --jq '.baseRefName') +if [[ "$ACTUAL_BASE" != "$BASE_BRANCH" ]]; then + status "PR base is '$ACTUAL_BASE', fixing to '$BASE_BRANCH'..." + gh pr edit "$PR_NUMBER" -R "$REPO" --base "$BASE_BRANCH" +fi + +# Verify CI passing +ci_passing "$PR_NUMBER" || fail "CI checks not passing on PR #$PR_NUMBER after Phase 1." + +# Verify no unresolved reviews +no_unresolved_reviews "$PR_NUMBER" || fail "Unresolved review comments remain on PR #$PR_NUMBER after Phase 1." + +ok "Phase 1 VERIFIED: PR #$PR_NUMBER is ready. CI passing, no unresolved comments." + +# ============================================================================= +# PHASE 2: Ready for Review to Merged +# ============================================================================= + +# Idempotency: skip Phase 2 if PR is already merged +PR_STATE=$(gh pr view "$PR_NUMBER" -R "$REPO" --json state --jq '.state') +if [[ "$PR_STATE" == "MERGED" ]]; then + ok "PR #$PR_NUMBER already merged — skipping Phase 2." +else + status "Phase 2: Launching copilot --yolo for PR #$PR_NUMBER" + + PHASE2_PROMPT="Invoke skill \`shepherd-task-from-ready-to-merged-to-base\` with these inputs: + +- TASK_ISSUE: $TASK_ISSUE +- BASE_BRANCH: $BASE_BRANCH +- REPO: $REPO +- PR_NUMBER: $PR_NUMBER" + + status "Phase 2 prompt:" + echo "$PHASE2_PROMPT" + PHASE2_SHARE="$LOG_DIR/phase2-task-$(date +%Y%m%d-%H%M)-$TASK_ISSUE.md" + PHASE2_JSON="$LOG_DIR/phase2-task-$(date +%Y%m%d-%H%M)-$TASK_ISSUE.json" + PHASE2_OTEL="$(cd "$LOG_DIR" && pwd)/phase2-otel-$(date +%Y%m%d-%H%M)-$TASK_ISSUE.jsonl" + export COPILOT_OTEL_FILE_EXPORTER_PATH="$PHASE2_OTEL" + echo "$PHASE2_PROMPT" | copilot --yolo --output-format json --share "$PHASE2_SHARE" > "$PHASE2_JSON" + unset COPILOT_OTEL_FILE_EXPORTER_PATH + + status "Phase 2: copilot exited. Verifying state..." + + # --- Verify Phase 2 outcome --- + PR_STATE=$(gh pr view "$PR_NUMBER" -R "$REPO" --json state --jq '.state') + if [[ "$PR_STATE" != "MERGED" ]]; then + fail "PR #$PR_NUMBER is in state '$PR_STATE', expected MERGED." + fi +fi + +# Verify merged into correct branch +MERGED_BASE=$(gh pr view "$PR_NUMBER" -R "$REPO" --json baseRefName --jq '.baseRefName') +if [[ "$MERGED_BASE" != "$BASE_BRANCH" ]]; then + fail "PR #$PR_NUMBER was merged into '$MERGED_BASE', expected '$BASE_BRANCH'." +fi + +# Verify issue is closed +ISSUE_STATE=$(gh issue view "$TASK_ISSUE" -R "$REPO" --json state --jq '.state') +if [[ "$ISSUE_STATE" != "CLOSED" ]]; then + status "Issue #$TASK_ISSUE still open, closing..." + gh issue close "$TASK_ISSUE" -R "$REPO" +fi + +ok "SHEPHERD TASK COMPLETE: Task #$TASK_ISSUE has been fully shepherded." +ok "PR #$PR_NUMBER merged to $BASE_BRANCH." +exit 0 diff --git a/plugins/shepherd-task/scripts/uninstall-task-shepherd.ps1 b/plugins/shepherd-task/scripts/uninstall-task-shepherd.ps1 new file mode 100644 index 000000000..76009d517 --- /dev/null +++ b/plugins/shepherd-task/scripts/uninstall-task-shepherd.ps1 @@ -0,0 +1,42 @@ +<# +.SYNOPSIS + Removes the shepherd-task plugin and skills from the user's Copilot home directory. + +.DESCRIPTION + Removes: + ~/.copilot/plugins/shepherd-task/ + ~/.copilot/skills/shepherd-task-* + +.EXAMPLE + ./uninstall-task-shepherd.ps1 +#> + +$ErrorActionPreference = 'Stop' + +$CopilotHome = if ($env:COPILOT_HOME) { $env:COPILOT_HOME } else { Join-Path $HOME '.copilot' } + +# Remove plugin. +$pluginDir = Join-Path $CopilotHome 'plugins' 'shepherd-task' +if (Test-Path $pluginDir -PathType Container) { + Remove-Item -Path $pluginDir -Recurse -Force + Write-Host "Removed $pluginDir" +} else { + Write-Host "Plugin not found: $pluginDir (skipped)" +} + +# Remove skills. +$skills = @( + 'shepherd-task-from-assignment-to-ready' + 'shepherd-task-from-ready-to-merged-to-base' + 'shepherd-task-approve-workflows-and-wait-for-completion' +) +foreach ($skill in $skills) { + $skillDir = Join-Path $CopilotHome 'skills' $skill + if (Test-Path $skillDir -PathType Container) { + Remove-Item -Path $skillDir -Recurse -Force + Write-Host "Removed ~/.copilot/skills/$skill" + } +} + +Write-Host "" +Write-Host "Shepherd-task fully uninstalled." diff --git a/plugins/shepherd-task/scripts/uninstall-task-shepherd.sh b/plugins/shepherd-task/scripts/uninstall-task-shepherd.sh new file mode 100755 index 000000000..af9437118 --- /dev/null +++ b/plugins/shepherd-task/scripts/uninstall-task-shepherd.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# +# uninstall-task-shepherd.sh — Removes the shepherd-task plugin and skills +# from the user's Copilot home directory. +# +# Removes: +# ~/.copilot/plugins/shepherd-task/ +# ~/.copilot/skills/shepherd-task-* +# +# Usage: ./uninstall-task-shepherd.sh + +set -euo pipefail + +COPILOT_HOME="${COPILOT_HOME:-$HOME/.copilot}" + +# Remove plugin. +PLUGIN_DIR="$COPILOT_HOME/plugins/shepherd-task" +if [ -d "$PLUGIN_DIR" ]; then + rm -rf "$PLUGIN_DIR" + echo "Removed $PLUGIN_DIR" +else + echo "Plugin not found: $PLUGIN_DIR (skipped)" +fi + +# Remove skills. +SKILLS=( + "shepherd-task-from-assignment-to-ready" + "shepherd-task-from-ready-to-merged-to-base" + "shepherd-task-approve-workflows-and-wait-for-completion" +) +for skill in "${SKILLS[@]}"; do + skill_dir="$COPILOT_HOME/skills/$skill" + if [ -d "$skill_dir" ]; then + rm -rf "$skill_dir" + echo "Removed ~/.copilot/skills/$skill" + fi +done + +echo "" +echo "Shepherd-task fully uninstalled." diff --git a/skills/shepherd-task-approve-workflows-and-wait-for-completion/SKILL.md b/skills/shepherd-task-approve-workflows-and-wait-for-completion/SKILL.md new file mode 100644 index 000000000..6f309d789 --- /dev/null +++ b/skills/shepherd-task-approve-workflows-and-wait-for-completion/SKILL.md @@ -0,0 +1,81 @@ +--- +name: shepherd-task-approve-workflows-and-wait-for-completion +description: "Use this skill to approve pending workflow runs on a PR branch and wait for them to complete." +--- + +# Skill: Approve Workflows and Wait for Completion + +## Purpose + +Approve all pending workflow runs (`action_required` status) on a PR's topic branch and wait for them to complete. This is a reusable sub-skill invoked by other shepherd skills whenever workflow approval is needed. + +## Inputs + +- `REPO`: Repository in `OWNER/REPO` format (e.g., `github/copilot-sdk`). +- `JTBDTASK_BRANCH`: The topic branch name associated with the PR (used to find workflow runs). +- `PR_NUMBER`: The PR number (used for `gh pr checks --watch`). + +## Prerequisites + +- `gh` CLI authenticated with sufficient permissions (actions, PRs). +- The PR exists and has workflow runs triggered on `JTBDTASK_BRANCH`. + +--- + +## ⚠️ CRITICAL: Never go idle while waiting + +The `copilot --yolo` runtime **terminates the session shortly after the agent goes idle** (i.e., when there are no pending tool calls). If you launch a long-running polling command with a short `initial_wait` and then say "I'll check back when it completes," the runtime will kill the session before the command finishes. + +**Rules for all polling and waiting steps:** + +1. ✅✅✅ **ALWAYS use `initial_wait` ≥ 600 seconds** (10 minutes) on any polling/waiting command. This keeps the agent blocked on the tool call rather than going idle. ✅✅✅ +2. ❌❌❌ **NEVER background a polling command and then end your turn with no tool calls.** If a command exceeds `initial_wait`, immediately issue another tool call (e.g., `read_powershell`) to stay active. ❌❌❌ +3. ❌❌❌ **NEVER say "I'll check back when it completes" or "Waiting for notification."** These phrases mean you are going idle, which KILLS THE SESSION. ❌❌❌ +4. ✅✅✅ **ALWAYS prefer a single blocking poll** over launching a background command and waiting for a notification. ✅✅✅ + +--- + +## Procedure + +### Step 1: Approve pending workflow runs + +For each run in `action_required` status on the PR's branch, re-run it. The correct mechanism is `gh run rerun` (the `POST .../actions/runs/{id}/approve` endpoint is for fork PRs only and will return HTTP 403 here). + +```bash +# Get all action_required runs for the PR branch +PENDING_RUNS=$(gh run list -R $REPO --branch "$JTBDTASK_BRANCH" \ + --json databaseId,conclusion --jq '.[] | select(.conclusion == "action_required") | .databaseId') + +for RUN_ID in $PENDING_RUNS; do + gh run rerun $RUN_ID -R $REPO +done +``` + +### Step 2: Wait for workflow runs to complete + +```bash +# Watch all runs on the branch until they complete +# Use gh pr checks with --watch for convenience +gh pr checks $PR_NUMBER -R $REPO --watch --fail-fast +``` + +Alternatively, poll with: + +```bash +gh run list -R $REPO --branch "$JTBDTASK_BRANCH" \ + --json databaseId,status,conclusion,name \ + --jq '.[] | select(.status != "completed")' +``` + +--- + +## Error handling + +- **No pending runs found**: This is not an error — it means runs were already approved (possibly manually). Proceed directly to waiting for completion. +- **`gh run rerun` fails**: Retry up to 3 times with 10-second backoff, then report and stop. +- **Runs do not complete within a reasonable time**: The `--watch` flag on `gh pr checks` will block until completion or failure. If it times out, report and stop. + +## Notes + +- This skill is extracted from Steps 4 and 5 of `shepherd-task-from-assignment-to-ready` for reuse across multiple shepherd skills. +- The `gh api .../actions/runs/{id}/approve` endpoint does NOT work for same-repo PRs (returns HTTP 403 "This run is not from a fork pull request"). Always use `gh run rerun` instead. diff --git a/skills/shepherd-task-create-post-mortem/SKILL.md b/skills/shepherd-task-create-post-mortem/SKILL.md new file mode 100644 index 000000000..d19d3314e --- /dev/null +++ b/skills/shepherd-task-create-post-mortem/SKILL.md @@ -0,0 +1,134 @@ +--- +name: shepherd-task-create-post-mortem +description: 'Create a structured post-mortem report from shepherd-task run artifacts, including metrics, timeline, failures, and actionable recommendations.' +--- + +# Skill: Create Shepherd Task Post-Mortem + +## Purpose + +Create a comprehensive post-mortem report for a completed (or failed) `shepherd-task` run. + +This skill is designed to be invoked from `shepherd-task-given-list.ps1` / `shepherd-task-given-list.sh` in a `finally` / `trap EXIT` path so it runs for **all outcomes**. + +--- + +## Inputs + +- `SHEPHERD_LOG_DIR` (**required**) + Absolute path to the shepherd run log directory (for example: `C:\Users\edburns\workareas\BRK206-02\28-python-agent-demo-remove-before-merge\shepherd-tasks-20260718-1827`). +- `SCRIPT_EXIT_CODE` (optional but recommended) + Exit code from the caller script, to classify success vs failure. +- `TASK_ISSUES` (optional) +- `BASE_BRANCH` (optional) +- `REPO` (optional) + +--- + +## Required Output + +Write the report to: + +`\YYYYMMDD-HHMM-post-mortem.md` + +Use local time for `YYYYMMDD-HHMM`. + +--- + +## Bundled Examples (style + structure references) + +Use both of these as concrete examples: + +1. `examples/dd-3029269-post-mortem-report.md` + (prior complete Java post-mortem; canonical sectioning and depth) +2. `examples/28-python-agent-demo-post-mortem.md` + (Python shepherd-task run post-mortem) + +Match their structure and tone: concise executive summary, clear sectioning, metrics tables, explicit timeline, and action-oriented recommendations. + +--- + +## Data Collection Procedure + +Given `SHEPHERD_LOG_DIR`: + +1. Validate the directory exists; fail clearly if it does not. +2. Collect all run artifacts from that directory: + - `phase1-task-*.json`, `phase2-task-*.json` + - `phase1-task-*.md`, `phase2-task-*.md` + - any supporting markdown notes +3. Determine the parent campaign directory (`PARENT_DIR = dirname(SHEPHERD_LOG_DIR)`), then collect context files there: + - `*memory*.md` (if present) + - `*prompts.md` (if present) + - `*job-logs.txt` (if present) +4. Extract quantitative metrics from JSON/MD artifacts: + - issues/PRs touched + - per-task phase durations + - review rounds (`Comments generated`) + - success/failure and failure signatures + - idle/timeout markers + - token usage where available (`assistant.message.outputTokens` / `inputTokens`) +5. Build the report so it is useful for both: + - **successful runs** (throughput/convergence/quality) + - **failed runs** (root cause and corrective actions) + +--- + +## Report Structure (required) + +1. **Section 1: Executive Summary** + High-level outcome, completion rate, elapsed time, key totals. +2. **Section 2: System Architecture** + CCA, CCRA, Local Copilot CLI responsibilities. +3. **Section 3: Per-Task Metrics** + Table with issue, PR, phase timings, rounds, comments, result. +4. **Section 4: Aggregate Statistics** + Totals/averages and convergence signals. +5. **Section 5: AI Credits and Token Usage** + Include measured values; state clearly when data is unavailable. +6. **Section 6: Wall-Clock Timeline** + Batch windows and notable events. +7. **Section 7: Failure Analysis (if any)** + Root cause(s), evidence, and fixes. +8. **Section 8: Observations and Recommendations** + What worked, what failed, and specific script/skill improvements. + +Use Markdown tables for metrics and keep assertions tied to observable logs. + +### Link Formatting (required) + +Whenever an issue or PR is referenced in the report body, render it as a Markdown hyperlink using `REPO`: + +- Issue format: `[#123](https://github.com//issues/123)` +- PR format: `[#456](https://github.com//pull/456)` + +Apply this consistently in narrative text, metric tables, legends, and comparisons. +Do not leave plain-text references like `#123` or `PR #456` when `REPO` is known. +Do not nest Markdown links inside another Markdown link. + +For Table of Contents entries, use plain text labels for issue/PR identifiers (for example, `3.1 — Issue #34 / PR #44`) and link only to the local anchor target. + +--- + +## Invocation Pattern + +The caller should invoke this skill with a prompt like: + +```text +Invoke skill `shepherd-task-create-post-mortem` with these inputs: + +- SHEPHERD_LOG_DIR: +- SCRIPT_EXIT_CODE: +- TASK_ISSUES: +- BASE_BRANCH: +- REPO: +``` + +--- + +## Guardrails + +- Always produce a post-mortem file, even when the run fails. +- Do not require GitHub API/network calls unless local artifacts are insufficient. +- Prefer evidence from local run logs over assumptions. +- Keep report content factual and reproducible from the captured artifacts. diff --git a/skills/shepherd-task-create-post-mortem/examples/28-python-agent-demo-post-mortem.md b/skills/shepherd-task-create-post-mortem/examples/28-python-agent-demo-post-mortem.md new file mode 100644 index 000000000..642f50f63 --- /dev/null +++ b/skills/shepherd-task-create-post-mortem/examples/28-python-agent-demo-post-mortem.md @@ -0,0 +1,286 @@ +# Post-Mortem Report: Shepherd-Task Runs for Python Demo Tasks [#34-#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues) + +**Campaign:** Python agent demo shepherding in `28-python-agent-demo-remove-before-merge` +**Report generated:** 2026-07-20 +**Period covered:** 2026-07-17 19:34 ET -> 2026-07-18 22:34 ET +**Primary successful batch:** `shepherd-tasks-20260718-1827` + +## Table of Contents + +- [Section 1: Executive Summary](#section-1-executive-summary) +- [Section 2: System Architecture](#section-2-system-architecture) + - [2.1 Copilot Coding Agent (CCA)](#21-copilot-coding-agent-cca) + - [2.2 Copilot Code Review Agent (CCRA)](#22-copilot-code-review-agent-ccra) + - [2.3 Local Copilot CLI (Shepherd)](#23-local-copilot-cli-shepherd) +- [Section 3: Per-Task Metrics](#section-3-per-task-metrics) + - [Issue Legend](#issue-legend) + - [3.1 — Issue #34 / PR #44](#31--issue-34--pr-44) + - [3.2 — Issue #35 / PR #45](#32--issue-35--pr-45) + - [3.3 — Issue #36 / PR #46](#33--issue-36--pr-46) + - [3.4 — Issue #37 / PR #47](#34--issue-37--pr-47) + - [3.5 — Issue #38 / PR #48](#35--issue-38--pr-48) + - [3.6 — Issue #39 / PR #49](#36--issue-39--pr-49) +- [Section 4: Aggregate Statistics](#section-4-aggregate-statistics) + - [4.1 Final Batch Summary](#41-final-batch-summary) + - [4.2 Cross-Batch Outcomes](#42-cross-batch-outcomes) + - [4.3 Convergence Snapshot](#43-convergence-snapshot) +- [Section 5: AI Credits and Token Usage](#section-5-ai-credits-and-token-usage) + - [5.1 Local Copilot CLI Tokens](#51-local-copilot-cli-tokens) + - [5.2 Credit Visibility Limits](#52-credit-visibility-limits) +- [Section 6: Wall-Clock Timeline](#section-6-wall-clock-timeline) + - [6.1 Batch Timeline](#61-batch-timeline) + - [6.2 Final Batch Timeline](#62-final-batch-timeline) +- [Section 7: Failure Analysis Before Final Success](#section-7-failure-analysis-before-final-success) + - [7.1 Idle-Kill Timeout Pattern](#71-idle-kill-timeout-pattern) + - [7.2 Missing Initial Copilot Review Request](#72-missing-initial-copilot-review-request) + - [7.3 Intermediate Stabilization Run](#73-intermediate-stabilization-run) +- [Section 8: Observations and Recommendations](#section-8-observations-and-recommendations) + - [8.1 What Worked Well](#81-what-worked-well) + - [8.2 What Didn’t Work Well](#82-what-didnt-work-well) + - [8.3 Recommendations](#83-recommendations) + - [8.4 Comparison to Prior Java Run](#84-comparison-to-prior-java-run) + +--- + +## Section 1: Executive Summary + +The shepherding campaign converged to full success after three failed/partial iterations. The final run (`shepherd-tasks-20260718-1827`) merged all target Python tasks ([#34](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/34), [#35](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/35), [#36](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/36), [#37](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/37), [#38](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/38), [#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39)), with terminal output `=== All tasks shepherded successfully ===` in `20260718-1826-job-logs.txt`. + +| Metric | Value | +|--------|-------| +| Target tasks in final run | 6 ([#34](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/34)-[#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39)) | +| Completed and merged | 6/6 (100%) | +| Final run elapsed | ~4h 07m (18:27 -> 22:34 ET) | +| Total CCRA rounds (final run) | 20 | +| Total CCRA comments (final run) | 30 | +| Average task duration (final run) | ~40m 57s | +| Idle-kill failures (final run) | 0 | +| Local CLI output tokens (final run JSON logs) | 136,022 | + +Earlier runs (`20260717-1936`, `20260717-2022`, `20260718-1648`) provided failure evidence and fixes that enabled final success. + +--- + +## Section 2: System Architecture + +### 2.1 Copilot Coding Agent (CCA) + +CCA created/updated task PRs and performed initial implementation on GitHub infrastructure. In these runs, relevant PRs were [#42](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/42)-[#49](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/49). + +### 2.2 Copilot Code Review Agent (CCRA) + +CCRA (`copilot-pull-request-reviewer[bot]`) produced iterative review rounds with `Comments generated` summaries. It was the primary convergence signal for phase 2. + +### 2.3 Local Copilot CLI (Shepherd) + +`copilot --yolo` executed two shepherd skills, orchestrated local fixes, re-requested reviews, and merged PRs to `edburns/28-python-agent-demo` after clean review state. + +--- + +## Section 3: Per-Task Metrics + +### Issue Legend + +| Issue | PR | Notes | +|------:|---:|-------| +| [#34](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/34) | [#44](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/44) | Phase 1 skipped; PR pre-existed from earlier run | +| [#35](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/35) | [#45](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/45) | Transient local path lookup errors recovered | +| [#36](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/36) | [#46](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/46) | Longest phase 1 in final run before [#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39) | +| [#37](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/37) | [#47](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/47) | Fastest end-to-end completion | +| [#38](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/38) | [#48](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/48) | Long phase 2 despite low comment count | +| [#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39) | [#49](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/49) | Deepest review loop in final run | + +### 3.1 — Issue [#34](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/34) / PR [#44](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/44) + +| Metric | Value | +|--------|-------| +| Phase 1 duration | skipped (PR already existed) | +| Phase 2 duration | 24m 17s | +| Total duration | 24m 17s | +| CCRA rounds | 4 | +| CCRA comments | 8 | +| Outcome | merged | + +### 3.2 — Issue [#35](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/35) / PR [#45](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/45) + +| Metric | Value | +|--------|-------| +| Phase 1 duration | 14m 41s | +| Phase 2 duration | 14m 23s | +| Total duration | 29m 04s | +| CCRA rounds | 5 | +| CCRA comments | 5 | +| Outcome | merged | + +Phase 2 logs include four transient `Path does not exist` tool failures during local reads; run still converged and merged. + +### 3.3 — Issue [#36](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/36) / PR [#46](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/46) + +| Metric | Value | +|--------|-------| +| Phase 1 duration | 39m 44s | +| Phase 2 duration | 17m 47s | +| Total duration | 57m 31s | +| CCRA rounds | 3 | +| CCRA comments | 5 | +| Outcome | merged | + +### 3.4 — Issue [#37](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/37) / PR [#47](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/47) + +| Metric | Value | +|--------|-------| +| Phase 1 duration | 14m 23s | +| Phase 2 duration | 1m 26s | +| Total duration | 15m 49s | +| CCRA rounds | 0 | +| CCRA comments | 0 | +| Outcome | merged | + +### 3.5 — Issue [#38](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/38) / PR [#48](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/48) + +| Metric | Value | +|--------|-------| +| Phase 1 duration | 10m 35s | +| Phase 2 duration | 41m 11s | +| Total duration | 51m 46s | +| CCRA rounds | 1 | +| CCRA comments | 2 | +| Outcome | merged | + +### 3.6 — Issue [#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39) / PR [#49](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/49) + +| Metric | Value | +|--------|-------| +| Phase 1 duration | 27m 53s | +| Phase 2 duration | 39m 20s | +| Total duration | 1h 07m 13s | +| CCRA rounds | 7 | +| CCRA comments | 10 | +| Outcome | merged | + +--- + +## Section 4: Aggregate Statistics + +### 4.1 Final Batch Summary + +| Metric | Value | +|--------|-------| +| Tasks | 6 | +| Merged PRs | 6 | +| CCRA rounds | 20 | +| CCRA comments | 30 | +| Avg rounds/task | 3.33 | +| Avg comments/task | 5.00 | +| Avg comments/round | 1.50 | +| Tasks with zero comments | 1 ([#37](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/37)) | +| Longest task | [#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39) (1h 07m 13s) | +| Shortest task | [#37](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/37) (15m 49s) | + +### 4.2 Cross-Batch Outcomes + +| Directory | JSON sessions | Outcome | +|-----------|---------------|---------| +| `shepherd-tasks-20260717-1936` | 2 | failed (PR [#42](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/42) left OPEN) | +| `shepherd-tasks-20260717-2022` | 1 | failed (idle-kill while waiting for review) | +| `shepherd-tasks-20260718-1648` | 5 (+ one empty phase2 JSON) | partial success ([#41](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/41) and [#33](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/33) merged) | +| `shepherd-tasks-20260718-1827` | 11 | full success ([#34](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/34)-[#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39) merged) | + +### 4.3 Convergence Snapshot + +- **Strong convergence:** [#37](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/37) (0 comments), [#36](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/36) (3 rounds, 5 comments). +- **Moderate convergence:** [#34](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/34) and [#35](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/35). +- **Long convergence tail:** [#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39) (7 rounds). +- **Throughput bottleneck:** strictly serialized issue processing; wall clock scales with per-issue sum. + +--- + +## Section 5: AI Credits and Token Usage + +### 5.1 Local Copilot CLI Tokens + +| Scope | Output tokens | +|-------|---------------| +| Final successful batch (`20260718-1827`) | 136,022 | +| All four referenced run directories | 186,132 | + +### 5.2 Credit Visibility Limits + +CCA/CCRA billing-credit totals were not present in local artifacts. This report uses rounds/comments and local token usage as measurable proxies. + +Additional observability limitation: `20260718-1855-copilot-cli-otel-not-working.md` documents OTEL file export not flushing in piped-stdin mode ([copilot-agent-runtime#13047](https://github.com/github/copilot-agent-runtime/issues/13047)). + +--- + +## Section 6: Wall-Clock Timeline + +### 6.1 Batch Timeline + +| Batch | Window (ET) | Summary | +|------|--------------|---------| +| `20260717-1936` | ~19:36-19:59 | First phase 2 failure on [#41](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/41) | +| `20260717-2022` | ~20:23-20:26 | Retry failed despite review arrival | +| `20260718-1648` | ~16:49-18:09 | Stabilization run; [#41](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/41) and [#33](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/33) merged | +| `20260718-1827` | ~18:27-22:34 | Final full success for [#34](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/34)-[#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39) | + +### 6.2 Final Batch Timeline + +- Start marker: `phase2-task-20260718-1827-34.*` (~18:27 ET). +- End marker: `phase2-task-20260718-2155-39.*` duration 39m 20s (ending ~22:34 ET). +- Total elapsed: ~4h 07m. + +--- + +## Section 7: Failure Analysis Before Final Success + +### 7.1 Idle-Kill Timeout Pattern + +Failed phase 2 runs show this pattern: + +1. Poll command exceeds `initial_wait` and backgrounds. +2. Assistant reports "I'll check back..." then goes idle. +3. `assistant.idle` appears in JSON logs. +4. Session terminates before workflow can continue, leaving PR open. + +### 7.2 Missing Initial Copilot Review Request + +Earlier phase-2 flow assumed `gh pr ready` auto-requested Copilot review. It does not. This created wait loops for reviews that were never requested. + +Applied fix: explicit `gh pr edit $PR_NUMBER --add-reviewer "copilot-pull-request-reviewer"` in phase-2 step 1. + +### 7.3 Intermediate Stabilization Run + +`shepherd-tasks-20260718-1648` validated key fixes: + +- [#41](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/41) merged with `Comments generated: 0`. +- [#33](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/33) converged through 8 review rounds then merged. + +That run reduced unknowns and set up the final successful batch. + +--- + +## Section 8: Observations and Recommendations + +### 8.1 What Worked Well + +- Final run achieved full completion (6/6 merged). +- Idempotent skip behavior (existing PRs) supported restartability. +- Review/fix loop handled both low- and high-iteration tasks. + +### 8.2 What Didn’t Work Well + +- Early runs were vulnerable to idle termination behavior. +- Review polling logic was verbose and produced long wait overhead. +- OTEL capture gap reduced telemetry quality. + +### 8.3 Recommendations + +1. Keep explicit initial reviewer request in phase 2 as mandatory. +2. Keep anti-idle directives in all shepherd skills. +3. Add explicit exit-code verification for `gh pr edit --base` in `shepherd-task.ps1`. +4. Keep writing top-level `*job-logs.txt` for every batch (success and failure). +5. Continue reporting rounds/comments/tokens as core performance indicators. + +### 8.4 Comparison to Prior Java Run + +The Java post-mortem (`dd-3029269-post-mortem-report.md`) showed long-lived sessions and deep review cycles (for example, 7 rounds on PR [#43](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/pull/43)). After stabilization, the Python campaign reached comparable convergence behavior (notably [#39](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/39) with 7 rounds) and delivered full merge completion for its target set. diff --git a/skills/shepherd-task-create-post-mortem/examples/dd-3029269-post-mortem-report.md b/skills/shepherd-task-create-post-mortem/examples/dd-3029269-post-mortem-report.md new file mode 100644 index 000000000..69d4dde65 --- /dev/null +++ b/skills/shepherd-task-create-post-mortem/examples/dd-3029269-post-mortem-report.md @@ -0,0 +1,707 @@ +# Post-Mortem Report: Agentic Development of Epic [#2](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/2) + +**Epic:** [Java demo implementation](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/2) +**Report generated:** 2026-07-09 +**Period covered:** 2026-07-08 16:03 UTC → 2026-07-09 13:02 UTC + +## Table of Contents + +- [Section 1: Executive Summary](#section-1-executive-summary) +- [Section 2: System Architecture](#section-2-system-architecture) + - [2.1 Copilot Coding Agent (CCA)](#21-copilot-coding-agent-cca) + - [2.2 Copilot Code Review Agent (CCRA)](#22-copilot-code-review-agent-ccra) + - [2.3 Local Copilot CLI (Shepherd)](#23-local-copilot-cli-shepherd) +- [Section 3: Per-Task Metrics](#section-3-per-task-metrics) + - [Issue Legend](#issue-legend) + - [3.1 — Issue #13 / PR #14: Project Scaffolding](#31--issue-13--pr-14-project-scaffolding) + - [3.2 — Issue #4 / PR #15: Domain Model & Database Seeding](#32--issue-4--pr-15-domain-model--database-seeding) + - [3.3 — Issue #5 / PR #16: Core Agent Infrastructure](#33--issue-5--pr-16-core-agent-infrastructure) + - [3.4 — Issue #6 / PR #17: WebSocket Push Infrastructure](#34--issue-6--pr-17-websocket-push-infrastructure) + - [3.5 — Issue #7 / PR #18: JSF Pipeline View](#35--issue-7--pr-18-jsf-pipeline-view) + - [3.6 — Issue #20 / PR #21: Dynamic UI Updates](#36--issue-20--pr-21-dynamic-ui-updates) + - [3.7 — Issue #9 / PR #22: Agent Detail View](#37--issue-9--pr-22-agent-detail-view) + - [3.8 — Issue #10 / PR #23: End-to-End Integration Testing](#38--issue-10--pr-23-end-to-end-integration-testing) + - [3.9 — Issue #11 / PR #24: Demo Polish and README](#39--issue-11--pr-24-demo-polish-and-readme) +- [Section 4: Aggregate Statistics](#section-4-aggregate-statistics) + - [4.1 Summary Table](#41-summary-table) + - [4.2 Aggregate Metrics](#42-aggregate-metrics) + - [4.3 Convergence Analysis](#43-convergence-analysis) +- [Section 5: AI Credits](#section-5-ai-credits) + - [5.1 Local Copilot CLI Token Usage](#51-local-copilot-cli-token-usage) + - [5.2 CCA and CCRA Credits](#52-cca-and-ccra-credits) +- [Section 6: Wall-Clock Timeline](#section-6-wall-clock-timeline) + - [6.1 Overall](#61-overall) + - [6.2 Batch Timeline](#62-batch-timeline) + - [6.3 Per-Issue Timeline](#63-per-issue-timeline) + - [6.4 Notable Events](#64-notable-events) +- [Section 7: Human-Directed Changes After the Agentic Work Completed](#section-7-human-directed-changes-after-the-agentic-work-completed) + - [7.1 Pipeline Layout Restructure (commit `f6d9ddb`)](#71-pipeline-layout-restructure-commit-f6d9ddb) + - [7.2 Canned Query "+" Button (commit `d7e2b56`)](#72-canned-query--button-commit-d7e2b56) + - [7.3 Dashboard Sidebar (commit `c6168d0`)](#73-dashboard-sidebar-commit-c6168d0) + - [7.4 How to Improve the Issues So That the Human-Directed Changes Would Be Less](#74-how-to-improve-the-issues-so-that-the-human-directed-changes-would-be-less) +- [Section 8: Observations and Recommendations](#section-8-observations-and-recommendations) + - [8.1 What Worked Well](#81-what-worked-well) + - [8.2 What Didn't Work Well](#82-what-didnt-work-well) + - [8.3 Recommendations](#83-recommendations) + - [For the CCA (Copilot Coding Agent)](#for-the-cca-copilot-coding-agent) + - [For the CCRA (Copilot Code Review Agent)](#for-the-ccra-copilot-code-review-agent) + - [For the Local Copilot CLI Shepherd](#for-the-local-copilot-cli-shepherd) + - [For the Shepherd Orchestration Script](#for-the-shepherd-orchestration-script) + - [8.4 Patterns Observed](#84-patterns-observed) + +--- + +## Section 1: Executive Summary + +Epic [#2](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/2) tasked a three-agent pipeline with implementing a complete Java EE 11 + OpenLiberty port of the BRK206 real-estate demo across 9 discrete sub-issues (sections 3.1–3.9 of the implementation plan). Two additional sub-issues were aborted before completion and excluded from this analysis. + +| Metric | Value | +|--------|-------| +| Sub-issues attempted | 11 | +| Sub-issues completed (merged) | 9 | +| Sub-issues aborted | 2 ([#3](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/3), [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8)) | +| Total PRs merged | 9 (PR [#14](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/14)–18, [#21](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/21)–24) | +| Total wall-clock time | ~21 hours (2026-07-08 16:03 – 2026-07-09 13:02 UTC) | +| Total lines added by CCA (across all PRs) | 7,453 | +| Total lines deleted | 124 | +| Total CCRA review rounds | 47 | +| Total inline review comments | 287 | +| Local CLI output tokens | 467,288 | +| Tasks hitting 8-round CCRA cap | 2 (issues [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5), [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6)) | +| Manual interventions | 1 (abort of issue [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) / PR [#19](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/19)) | + +All 9 non-aborted tasks resulted in merged PRs. No task required manual code fixes by the human developer. + +--- + +## Section 2: System Architecture + +The pipeline consisted of three collaborating agents: + +### 2.1 Copilot Coding Agent (CCA) + +The CCA performed the initial implementation of each issue. It ran on GitHub's infrastructure, triggered by assigning the issue to Copilot. For 8 of 9 tasks, the `shepherd-task-to-ready` skill (phase 1) monitored the CCA run, polled for PR creation and CI completion, and approved any pending workflow runs. Issue [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13)'s CCA had already completed before the first shepherd batch started. + +The CCA produced draft PRs targeting the `edburns/2-build-out-demo` base branch. Initial implementations ranged from 1 commit (issue [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11)) to 7 commits (issue [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20)) before any CCRA involvement. + +### 2.2 Copilot Code Review Agent (CCRA) + +The CCRA (`copilot-pull-request-reviewer[bot]`) reviewed each PR once it was marked "Ready for Review." It posted inline comments identifying bugs, missing requirements, style violations, and constraint violations. The CCRA ran on GitHub's infrastructure asynchronously, typically completing a review within 5–15 minutes of being requested. + +### 2.3 Local Copilot CLI (Shepherd) + +The local CLI (`copilot --yolo`) ran the `shepherd-task-from-ready-to-merged-to-base` skill (phase 2). For each CCRA review batch, it: + +1. Fetched and read all open review comments +2. Applied each fix locally (via `edit`, `create`, or `powershell` tool calls in a worktree) +3. Made a single commit per batch and pushed to the head branch +4. Re-requested a CCRA review +5. Repeated until no comments remained or 8 rounds were reached +6. Merged the PR via `gh pr merge` + +The local CLI ran in `--yolo` mode, autonomously approving all tool permission requests. Each phase-2 session was a single long-lived `copilot` process that polled GitHub for CCRA completion between rounds. + +--- + +## Section 3: Per-Task Metrics + +### Issue Legend + +| Issue | Section | Title | PR | +|-------|---------|-------|----| +| [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) | 3.1 | Project scaffolding: Maven, server.xml, empty source dirs | [#14](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/14) | +| [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) | 3.2 | Domain model & database seeding: JPA entities, Jakarta Data, JSON loader | [#15](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/15) | +| [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) | 3.3 | Core agent infrastructure: Phase enum, Agent, AppState, CopilotClientProducer, tools | [#16](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/16) | +| [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) | 3.4 | WebSocket push infrastructure: `f:websocket` for real-time UI | [#17](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/17) | +| [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) | 3.5 | JSF pipeline view: static layout with PrimeFaces | [#18](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/18) | +| [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) | 3.6 | Dynamic UI updates: WebSocket-driven re-render with CSS transitions | [#21](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/21) | +| [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) | 3.7 | Agent detail view: side panel with session events, tool calls, report | [#22](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/22) | +| [#10](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/10) | 3.8 | End-to-end integration testing: full pipeline validation | [#23](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/23) | +| [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11) | 3.9 | Demo polish and README: error handling, auto-removal, docs | [#24](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/24) | + +--- + +### 3.1 — Issue [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) / PR [#14](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/14): Project Scaffolding + +**Phase 1 (CCA):** PR created at 2026-07-08 00:25 UTC — before the first shepherd batch. CCA created the Maven + OpenLiberty skeleton independently. + +**Phase 2 (CCRA + Local CLI):** Shepherd batch `shepherd-tasks-20260708-1203`, session 22m 32s. + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 2 | +| CCRA rounds | 1 | +| Local CLI fix commits | 1 | +| Total PR commits | 3 | +| 8-round cap hit? | No | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 143 | +| Deletions | 0 | +| Changed files | 7 | +| Inline CCRA comments | 2 | +| Merge time | 2026-07-08 16:25 UTC | +| Wall-clock (phase 2 only) | 22 min | + +#### Assessment + +The scaffolding task was the simplest of all sub-issues — a Maven POM, `server.xml`, and empty source directories. The CCA produced correct structure on the first try. The single CCRA round caught 2 minor issues (likely naming or packaging), resolved in 1 commit. The low comment count (2) and single review round indicate strong CCA accuracy for this well-bounded task. No constraint violations observed; the output correctly targeted EE 11 and OpenLiberty. + +--- + +### 3.2 — Issue [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) / PR [#15](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/15): Domain Model & Database Seeding + +**Phase 1:** Shepherd batch `shepherd-tasks-20260708-1233` / `shepherd-tasks-20260708-1244`. A quick 13-second phase-1 run (20260708-1234) was aborted and restarted at 16:44 (20260708-1244), running 47 min. CCA produced PR [#15](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/15) at 16:45 UTC. + +**Phase 2:** Shepherd batch `shepherd-tasks-20260708-1340`, session 57m 46s. + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 2 | +| CCRA rounds | 7 | +| Local CLI fix commits | 7 | +| Total PR commits | 9 | +| 8-round cap hit? | No (converged at round 7) | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 3,485 | +| Deletions | 1 | +| Changed files | 107 | +| Inline CCRA comments | 24 | +| Merge time | 2026-07-08 18:37 UTC | +| Wall-clock (phase 1 + 2) | ~2h 3min | + +#### Assessment + +This was the most code-intensive task (107 files, 3,485 additions) — the CCA seeded a full H2 database with JPA entities, a Jakarta Data repository, and a JSON loader. The 7 CCRA rounds reflect genuine complexity: the CCRA caught issues across multiple rounds without clear convergence until round 7, suggesting the initial implementation had several layered defects. The large file count (107 files — many likely generated JSON seed data) may have overwhelmed the CCRA's attention, contributing to sustained comment volume. The CCA correctly used Jakarta Data `@Repository` as required by constraints, with CCRA flagging correctness issues in the JPA mappings. + +The aborted phase-1 attempt (13-second session, 94 tokens) was a script restart with no code impact. + +--- + +### 3.3 — Issue [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) / PR [#16](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/16): Core Agent Infrastructure + +**Phase 1:** Shepherd batch `shepherd-tasks-20260708-1244`, session 19 min. CCA produced PR [#16](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/16) at 18:38 UTC. + +**Phase 2:** Shepherd batch `shepherd-tasks-20260708-1340`, session 71m 15s. + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 2 | +| CCRA rounds | **8 (cap reached)** | +| Local CLI fix commits | 8 | +| Total PR commits | 10 | +| 8-round cap hit? | **Yes** | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 399 | +| Deletions | 0 | +| Changed files | 6 | +| Inline CCRA comments | 46 | +| Merge time | 2026-07-08 20:08 UTC | +| Wall-clock (phase 1 + 2) | ~1h 30min | + +#### Assessment + +The 8-round cap indicates the CCRA and local CLI did not reach a stable state within the allowed iterations. With 46 inline comments across 8 rounds, the average was ~5.75 comments per round — no meaningful convergence trend. This is the second-highest comment density per round after issues [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) and [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20). + +The core agent infrastructure task required implementing the `@CopilotTool` annotation API (a headline SDK feature) alongside CDI producers and state management. The complexity of interleaving Jakarta EE CDI lifecycle with Copilot SDK session management likely generated recurring CCRA concerns across rounds. Possible oscillation: CCRA may have introduced new comments on code touched in earlier rounds (a common sign of the CCRA re-evaluating context). + +The task did merge at round 8, meaning some CCRA comments were likely unaddressed at merge time. + +--- + +### 3.4 — Issue [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) / PR [#17](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/17): WebSocket Push Infrastructure + +**Phase 1:** Shepherd batch `shepherd-tasks-20260708-1244`, session 18 min. CCA produced PR [#17](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/17) at 20:09 UTC. + +**Phase 2:** Shepherd batch `shepherd-tasks-20260708-1340`, session 77m 42s. + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 2 | +| CCRA rounds | **8 (cap reached)** | +| Local CLI fix commits | 8 | +| Total PR commits | 10 | +| 8-round cap hit? | **Yes** | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 145 | +| Deletions | 37 | +| Changed files | 4 | +| Inline CCRA comments | 32 | +| Merge time | 2026-07-08 21:45 UTC | +| Wall-clock (phase 1 + 2) | ~1h 35min | + +#### Assessment + +Notably, 37 deletions suggest the CCRA directed the local CLI to remove code (more than any other small-file task). Despite only 4 changed files, the CCRA generated 32 comments over 8 rounds — the highest comments-per-file ratio (8.0) of all tasks. WebSocket integration with JSF's `f:websocket` channel involves tight coupling between server-push semantics and CDI scopes, a notoriously finicky area in Jakarta EE 11. The CCRA likely kept catching scope and lifecycle violations that the local CLI fixed incompletely. Cap hit at 8 rounds; some comments likely unresolved at merge. + +--- + +### 3.5 — Issue [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) / PR [#18](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/18): JSF Pipeline View + +**Phase 1:** Shepherd batch `shepherd-tasks-20260708-1244`, session 12 min. CCA produced PR [#18](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/18) at 21:46 UTC. + +**Phase 2:** Shepherd batch `shepherd-tasks-20260708-1340`, session 66m 42s. + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 4 | +| CCRA rounds | 6 | +| Local CLI fix commits | 6 | +| Total PR commits | 10 | +| 8-round cap hit? | No (converged at round 6) | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 634 | +| Deletions | 29 | +| Changed files | 6 | +| Inline CCRA comments | 54 | +| Merge time | 2026-07-08 23:04 UTC | +| Wall-clock (phase 1 + 2) | ~1h 18min | + +#### Assessment + +The highest absolute comment count (54) of all tasks, yet this task converged without hitting the cap. The 4 initial CCA commits (vs. the typical 2) suggest the CCA iterated internally before marking the PR ready. PrimeFaces 15.0 JSF layout involves considerable boilerplate (XHTML, bean bindings, CSS), giving the CCRA many opportunities to comment. The convergence at round 6 (despite 54 comments) suggests the CCRA's concerns were genuinely resolvable — each round produced meaningful reduction, unlike the cap-hit tasks. + +--- + +### 3.6 — Issue [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) / PR [#21](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/21): Dynamic UI Updates + +**Phase 1:** Shepherd batch `shepherd-tasks-20260708-1918`, session 31 min. CCA produced PR [#21](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/21) at 23:19 UTC. + +**Phase 2:** Shepherd batch `shepherd-tasks-20260708-1918`, session 93m 17s. + +> **Note:** This issue replaced the aborted issue [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8). The original CCA run for issue [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) (PR [#19](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/19), 7,546 additions across 130 files) was manually aborted after 5 minutes because the scope far exceeded the task specification. Issue [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) was created as a replacement with a tighter prompt. + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 7 | +| CCRA rounds | 5 | +| Local CLI fix commits | 5 | +| Total PR commits | 12 | +| 8-round cap hit? | No (converged at round 5) | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 1,149 | +| Deletions | 0 | +| Changed files | 11 | +| Inline CCRA comments | 72 | +| Merge time | 2026-07-09 01:23 UTC | +| Wall-clock (phase 1 + 2) | ~2h 4min | + +#### Assessment + +The highest total inline comment count (72) across all tasks, yet the fewest CCRA rounds of the UI tasks (5). This implies each CCRA round generated many comments but the local CLI addressed them effectively in bulk. The 7 initial CCA commits reflect the complexity of coordinating CSS transitions, WebSocket push events, and PrimeFaces re-render directives. No lines deleted suggests the CCA added net-new code only. Convergence in 5 rounds shows this replacement issue (with a tighter scope than [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8)) was better suited to agentic implementation. + +The manual abort of issue [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) / PR [#19](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/19) represents the single manual intervention in the entire epic, triggered by CCA scope creep (130 files vs. the expected ~10). + +--- + +### 3.7 — Issue [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) / PR [#22](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/22): Agent Detail View + +**Phase 1:** Shepherd batch `shepherd-tasks-20260708-1918`, session 10 min. CCA produced PR [#22](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/22) at 01:24 UTC (July 9). + +**Phase 2:** Shepherd batch `shepherd-tasks-20260708-1918`, session **602m 53s** (10 hours 2 minutes). + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 3 | +| CCRA rounds | 7 | +| Local CLI fix commits | 7 | +| Total PR commits | 10 | +| 8-round cap hit? | No (converged at round 7) | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 500 | +| Deletions | 37 | +| Changed files | 8 | +| Inline CCRA comments | 41 | +| Merge time | 2026-07-09 11:35 UTC | +| Wall-clock (phase 1 + 2) | ~10h 12min | + +#### Assessment + +The 602-minute phase-2 session is the single most extreme outlier in the dataset. The code work itself was modest (500 additions, 8 files, 7 rounds), but the session ran from 01:33 to 11:36 UTC — overnight. The elapsed time reflects **wall-clock wait time for asynchronous CCRA reviews**, not active processing. Each CCRA round took 20–60 minutes on GitHub's side, and with 7 rounds the session naturally spanned the night. Active Local CLI processing time was far shorter. + +The session log shows the CLI correctly handling thread resolution, polling loops, and multiple re-review requests without human intervention. The task eventually converged and merged cleanly. This illustrates a systemic issue with the shepherd script: long-running overnight sessions consume a copilot CLI process for extended periods without benefit. + +--- + +### 3.8 — Issue [#10](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/10) / PR [#23](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/23): End-to-End Integration Testing + +**Phase 1:** Shepherd batch `shepherd-tasks-20260708-1918`, session 17 min. CCA produced PR [#23](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/23) at 11:36 UTC. + +**Phase 2:** Shepherd batch `shepherd-tasks-20260708-1918`, session 35m 1s. + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 2 | +| CCRA rounds | 4 | +| Local CLI fix commits | 4 | +| Total PR commits | 6 | +| 8-round cap hit? | No (converged at round 4) | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 691 | +| Deletions | 14 | +| Changed files | 7 | +| Inline CCRA comments | 12 | +| Merge time | 2026-07-09 12:28 UTC | +| Wall-clock (phase 1 + 2) | ~52min | + +#### Assessment + +The lowest comment count (12) among the complex tasks, second only to the scaffolding task ([#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13)). The CCA's test suite for pipeline validation was well-scoped and the CCRA converged quickly. The 4-round convergence with only 3 comments per round on average suggests the CCRA found genuinely discrete, resolvable issues without oscillation. The `FEATURE-VERIFICATION.md` artifact in the PR title suggests the CCA created documentation alongside unit tests. + +--- + +### 3.9 — Issue [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11) / PR [#24](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/24): Demo Polish and README + +**Phase 1:** Shepherd batch `shepherd-tasks-20260708-1918`, session 15 min. CCA produced PR [#24](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/24) at 12:29 UTC. + +**Phase 2:** Shepherd batch `shepherd-tasks-20260708-1918`, session 19m 5s. + +#### Throughput & Convergence + +| Metric | Value | +|--------|-------| +| CCA initial commits | 1 | +| CCRA rounds | 1 | +| Local CLI fix commits | 1 | +| Total PR commits | 2 | +| 8-round cap hit? | No | + +#### PR Stats + +| Metric | Value | +|--------|-------| +| Additions | 307 | +| Deletions | 6 | +| Changed files | 7 | +| Inline CCRA comments | 4 | +| Merge time | 2026-07-09 13:02 UTC | +| Wall-clock (phase 1 + 2) | ~34min | + +#### Assessment + +The fastest-completing complex task (34 minutes total). The polish/README task is inherently documentation-heavy, with the CCRA generating only 4 comments in a single round. This reflects the CCRA's strength on documentation: it caught real issues (likely missing sections or broken links) without over-commenting. The 1-round convergence and minimal deletions indicate the CCA produced near-final quality output for documentation tasks. + +--- + +## Section 4: Aggregate Statistics + +### 4.1 Summary Table + +| Issue | PR | CCA commits | CCRA rounds | CLI commits | Comments | +Lines | −Lines | Cap? | Merged | +|-------|-----|-------------|-------------|-------------|----------|--------|--------|------|--------| +| [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) | [#14](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/14) | 2 | 1 | 1 | 2 | 143 | 0 | — | 2026-07-08 16:25 | +| [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) | [#15](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/15) | 2 | 7 | 7 | 24 | 3,485 | 1 | — | 2026-07-08 18:37 | +| [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) | [#16](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/16) | 2 | **8** | 8 | 46 | 399 | 0 | ✓ | 2026-07-08 20:08 | +| [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) | [#17](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/17) | 2 | **8** | 8 | 32 | 145 | 37 | ✓ | 2026-07-08 21:45 | +| [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) | [#18](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/18) | 4 | 6 | 6 | 54 | 634 | 29 | — | 2026-07-08 23:04 | +| [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) | [#21](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/21) | 7 | 5 | 5 | 72 | 1,149 | 0 | — | 2026-07-09 01:23 | +| [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) | [#22](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/22) | 3 | 7 | 7 | 41 | 500 | 37 | — | 2026-07-09 11:35 | +| [#10](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/10) | [#23](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/23) | 2 | 4 | 4 | 12 | 691 | 14 | — | 2026-07-09 12:28 | +| [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11) | [#24](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/24) | 1 | 1 | 1 | 4 | 307 | 6 | — | 2026-07-09 13:02 | +| **Total** | | **25** | **47** | **47** | **287** | **7,453** | **124** | **2/9** | | + +### 4.2 Aggregate Metrics + +| Metric | Value | +|--------|-------| +| Total commits by CCA (initial) | 25 | +| Total fix commits by Local CLI | 47 | +| Total merged commits | 72 | +| Total CCRA review rounds | 47 | +| Average CCRA rounds per task | 5.2 | +| Median CCRA rounds per task | 6 | +| Tasks hitting 8-round cap | 2 (issues [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5), [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6)) | +| Total CCRA comments generated | 287 | +| Average comments per task | 31.9 | +| Average comments per CCRA round | 6.1 | +| Tasks requiring manual intervention | 1 (issue [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) abort) | +| Total lines added | 7,453 | +| Total lines deleted | 124 | +| Net lines added | 7,329 | + +### 4.3 Convergence Analysis + +Convergence (successive CCRA rounds producing fewer comments) was observed in most non-cap tasks. Tasks hitting the cap ([#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5), [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6)) showed no clear downward trend — the comment count remained roughly flat across rounds, suggesting oscillation (CCRA re-flagging code touched in earlier rounds). + +| Convergence pattern | Tasks | +|--------------------|-------| +| Fast convergence (1–2 rounds) | [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13), [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11) | +| Gradual convergence (3–5 rounds) | [#10](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/10), [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) | +| Slow convergence (6–7 rounds) | [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4), [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7), [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) | +| No convergence (cap hit at 8) | [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5), [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) | + +--- + +## Section 5: AI Credits + +### 5.1 Local Copilot CLI Token Usage + +Token counts were parsed from `data.outputTokens` fields in all JSONL event logs. Input tokens were not recorded in the event stream (field absent = 0 in all files). + +| File | Phase | Issue | Output Tokens | +|------|-------|-------|---------------| +| phase1-task-20260708-1234-4.json | 1 | [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) (aborted restart) | 94 | +| phase1-task-20260708-1244-4.json | 1 | [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) | 19,019 | +| phase1-task-20260708-1438-5.json | 1 | [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) | 10,886 | +| phase1-task-20260708-1609-6.json | 1 | [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) | 10,280 | +| phase1-task-20260708-1745-7.json | 1 | [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) | 5,099 | +| phase1-task-20260708-1904-8.json | 1 | [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) (aborted) | 3,119 | +| phase1-task-20260708-1918-20.json | 1 | [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) | 11,213 | +| phase1-task-20260708-2123-9.json | 1 | [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) | 5,777 | +| phase1-task-20260709-0736-10.json | 1 | [#10](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/10) | 22,667 | +| phase1-task-20260709-0828-11.json | 1 | [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11) | 9,561 | +| **Phase 1 subtotal** | | | **97,715** | +| phase2-task-20260708-1203-13.json | 2 | [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) | 4,644 | +| phase2-task-20260708-1332-4.json | 2 | [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) (aborted) | 0 | +| phase2-task-20260708-1340-4.json | 2 | [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) | 42,390 | +| phase2-task-20260708-1457-5.json | 2 | [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) | 54,463 | +| phase2-task-20260708-1628-6.json | 2 | [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) | 49,706 | +| phase2-task-20260708-1757-7.json | 2 | [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) | 56,398 | +| phase2-task-20260708-1950-20.json | 2 | [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) | 65,900 | +| phase2-task-20260708-2133-9.json | 2 | [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) | 69,517 | +| phase2-task-20260709-0753-10.json | 2 | [#10](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/10) | 18,384 | +| phase2-task-20260709-0843-11.json | 2 | [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11) | 8,171 | +| **Phase 2 subtotal** | | | **369,573** | +| **Grand total** | | | **467,288** | + +> **Note:** Input tokens were not present in the event log (all values were 0). The `outputTokens` field on `assistant.message` events reflects only the local CLI's generation; CCA and CCRA tokens are tracked separately by GitHub billing. + +### 5.2 CCA and CCRA Credits + +CCA and CCRA credits are billed by GitHub based on coding agent runs and review rounds respectively. These cannot be directly queried from the repository API. Estimates: + +- **CCA credits:** 9 implementations × ~1 coding agent run each (some tasks like [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) may have required multiple CCA runs) = **~9–11 coding agent runs** +- **CCRA credits:** 47 review rounds = **47 CCRA invocations** +- **Local CLI (copilot --yolo):** 20 shepherd sessions (10 phase-1 + 10 phase-2) consuming 467,288 output tokens + +--- + +## Section 6: Wall-Clock Timeline + +### 6.1 Overall + +- **Start:** 2026-07-08 16:03:31 UTC (first event in `shepherd-tasks-20260708-1203`) +- **End:** 2026-07-09 13:02:40 UTC (last event in `shepherd-tasks-20260708-1918`) +- **Total elapsed:** 20 hours 59 minutes + +### 6.2 Batch Timeline + +| Batch directory | Start (UTC) | End (UTC) | Issues processed | Notes | +|----------------|-------------|-----------|-----------------|-------| +| `shepherd-tasks-20260708-1203` | 16:03 | 16:26 | [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) (phase 2) | First batch; issue [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) already ready | +| `shepherd-tasks-20260708-1233` | — | — | (empty) | Script directory created but no tasks run | +| `shepherd-tasks-20260708-1244` | 16:34 | 23:09 | [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) (ph1), [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) (ph1), [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) (ph1), [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) (ph1), [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) (ph1, aborted) | Aborted issue [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) ended this batch | +| `shepherd-tasks-20260708-1340` | 17:40 | 23:04 | [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) (ph2), [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) (ph2), [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) (ph2), [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) (ph2) | Longest single batch; 4 sequential phase-2 runs | +| `shepherd-tasks-20260708-1918` | 23:04 | 13:02+1d | [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) (ph1+2), [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) (ph1+2), [#10](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/10) (ph1+2), [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11) (ph1+2) | Ran overnight; issue [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) phase-2 ran 10+ hours | + +### 6.3 Per-Issue Timeline + +``` +2026-07-08 UTC +00:00 12:00 2026-07-09 UTC +| | | +·-- [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) CCA (overnight) --------merge(16:25) + ·-- [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) ph1 ·-- [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) ph2 --------merge(18:37) + ·-- [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) ph1 ·-- [#5](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/5) ph2 ----merge(20:08) + ·-- [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) ph1 ·-- [#6](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/6) ph2 --merge(21:45) + ·-- [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) ph1 ·-- [#7](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/7) ph2 --merge(23:04) + ·-- [ABORT [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8)] + ·-- [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) ph1+ph2 --------merge(01:23+1d) + ·-- [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) ph1+ph2 (overnight!) ------merge(11:35+1d) + ·-- [#10](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/10) ph1+ph2 --merge(12:28+1d) + ·-- [#11](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/11) ph1+ph2 --merge(13:02+1d) +``` + +### 6.4 Notable Events + +| Time (UTC) | Event | +|-----------|-------| +| 2026-07-08 00:25 | PR [#14](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/14) created by CCA (pre-batch) | +| 2026-07-08 16:03 | First shepherd batch starts (issue [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) phase 2) | +| 2026-07-08 16:25 | PR [#14](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/14) merged — issue [#13](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/13) complete | +| 2026-07-08 16:34 | Phase-1 for issue [#4](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/4) aborts after 13 seconds (script restart) | +| 2026-07-08 23:04 | Phase-1 for issue [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) manually aborted at 5 min; PR [#19](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/19) closed | +| 2026-07-08 23:05 | PR [#19](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/19) (7,546 additions, 130 files) created then immediately closed | +| 2026-07-08 23:09 | Issue [#8](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/8) aborted; issue [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) created as replacement | +| 2026-07-08 23:19 | New CCA started on replacement issue [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) | +| 2026-07-09 01:23 | PR [#21](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/21) merged — issue [#20](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/20) complete | +| 2026-07-09 01:33 | Phase-2 for issue [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) starts (10-hour overnight session begins) | +| 2026-07-09 11:35 | PR [#22](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/22) merged — issue [#9](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/9) finally complete after 10h of CCRA wait | +| 2026-07-09 13:02 | PR [#24](https://github.com/edburns/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/issues/24) merged — epic complete | + +--- + +## Section 7: Human-Directed Changes After the Agentic Work Completed + +This section details the human-directed changes to the app to make it acceptable to the customer after the agentic work completed. + +After the nine-issue agentic pipeline completed and all PRs were merged, the human developer tested the app end-to-end. Three categories of UI deficiencies were identified that required human-directed changes (commits `f6d9ddb`–`c6168d0`, tags `20260709-1644-02-column-layout-success` through `20260709-1800-04-dashboard-success`). These changes spanned 5 files, adding 500 lines and removing 96 lines. + +### 7.1 Pipeline Layout Restructure (commit `f6d9ddb`) + +**Problem:** The agent built the pipeline grid as a horizontal auto-fill CSS grid (`repeat(auto-fill, minmax(180px, 1fr))`), rendering all 7 phases as equal-width columns in a single row. The Blazor reference app uses a vertical two-column layout: lifecycle states (Queued → Validating → Searching → Writing Report) in the left column with downward arrows, and end states (Rejected, No Matches, Done) in the right column with rightward arrows connecting them to the corresponding lifecycle phase. + +**Changes made:** +- **index.xhtml:** Replaced the single `ui:repeat` over all phases with an explicit two-column structure — 4 `pipeline-row` divs, each with a `lifecycle-col`, `arrow-col`, and `endstate-col`. Added column headers ("Lifecycle state" / "End state"). Created a reusable `agent-card.xhtml` include fragment to avoid repeating card markup across 7 phase slots. +- **pipeline.css:** Replaced `.pipeline-grid` with `.pipeline-layout` using CSS grid rows (`1fr 60px 1fr`). Added vertical arrow styling (`.arrow-line`) and horizontal arrow styling (`.arrow-right`) with triangle pseudo-elements. +- **PipelineView.java:** Added `String` overloads for `getAgentsAtPhase()` and `getPhaseHeaderClass()` since the new XHTML passes phase names as string literals rather than enum references. + +### 7.2 Canned Query "+" Button (commit `d7e2b56`) + +**Problem:** The agent built a free-text input field with a "Submit" button for entering enquiries. The Blazor reference app uses a "+" button in the top-right corner that toggles a popup list of 10 pre-defined sample queries. The free-text input was not part of the Blazor design and would require the presenter to type queries during the demo. + +**Changes made:** +- **PipelineView.java:** Added a `SAMPLE_ENQUIRIES` array (10 canned queries matching the Blazor demo) and a `submitSampleEnquiry(String)` action method. +- **index.xhtml:** Removed the `h:inputText` and `p:commandButton`. Added a circular "+" button with an absolutely-positioned popup that uses `c:forEach` over the sample enquiries list. (Note: `ui:repeat` was tried first but produced no output for the `List`; `c:forEach` was required.) Added inline `