From 4d1b3fab32249b63de3142b021420fe18cb7cfd7 Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Mon, 9 Mar 2026 17:11:17 +0800 Subject: [PATCH 01/11] feat: add epic-cleanup-yakshaver agentic workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a GitHub Agentic Workflow to reorganise three large YakShaver epics: - ✨ YakShaver Agent 2.0 (SSW.YakShaver#2811) - ✨ YakShaver Desktop App (SSW.YakShaver.Desktop#677) - ♻️ Auth Migration (SSW.YakShaver#3494) The workflow clusters open PBIs into smaller, focused sub-epics, all created in SSWConsulting/SSW.YakShaver.Desktop for centralised tracking: - πŸ”Œ MCP Integration / πŸ—οΈ Core Infrastructure (Agent 2.0) - πŸ› Bug Fixes / ✨ Features & UX / πŸ—οΈ Platform & Infrastructure (Desktop) - ♻️ Scope 1: Enterprise Auth / 🌐 Scope 2: Extended Providers (Auth) The workflow uses the GitHub MCP to read issues cross-repo and posts reorganisation summaries on each original epic with CLI commands for completing the sub-issue re-linking. Requires: GH_AW_CROSS_REPO_PAT secret with read/write access to both SSWConsulting/SSW.YakShaver and SSWConsulting/SSW.YakShaver.Desktop. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .gitattributes | 2 + .../workflows/epic-cleanup-yakshaver.lock.yml | 1182 +++++++++++++++++ .github/workflows/epic-cleanup-yakshaver.md | 178 +++ 3 files changed, 1362 insertions(+) create mode 100644 .github/workflows/epic-cleanup-yakshaver.lock.yml create mode 100644 .github/workflows/epic-cleanup-yakshaver.md diff --git a/.gitattributes b/.gitattributes index 7322570c..f4243f1a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,5 @@ *.ps1 text eol=crlf *.bat text eol=crlf *.md text eol=lf + +.github/workflows/*.lock.yml linguist-generated=true merge=ours diff --git a/.github/workflows/epic-cleanup-yakshaver.lock.yml b/.github/workflows/epic-cleanup-yakshaver.lock.yml new file mode 100644 index 00000000..7d32596d --- /dev/null +++ b/.github/workflows/epic-cleanup-yakshaver.lock.yml @@ -0,0 +1,1182 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.56.2). DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# Analyses the three large YakShaver epics in the Product Backlog, clusters their +# open PBIs thematically, and creates focused smaller sub-epics β€” all in +# SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning. +# +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e9b459c06c620d814b6fe25fb28d8fd0364ff40dbea5b17d791cf536eb0ba79a","compiler_version":"v0.56.2","strict":true} + +name: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" +"on": + workflow_dispatch: + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + +jobs: + activation: + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + comment_id: "" + comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@f1073c5498ee46fec1530555a7c953445417c69b # v0.56.2 + with: + destination: /opt/gh-aw/actions + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "latest" + GH_AW_INFO_CLI_VERSION: "v0.56.2" + GH_AW_INFO_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '[]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.23.0" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + GH_AW_COMPILED_STRICT: "true" + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 + persist-credentials: false + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_WORKFLOW_FILE: "epic-cleanup-yakshaver.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" + cat << 'GH_AW_PROMPT_EOF' + + Tools: add_comment, create_issue, missing_tool, missing_data, noop + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + {{#runtime-import .github/workflows/epic-cleanup-yakshaver.md}} + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + contents: read + issues: read + pull-requests: read + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_WORKFLOW_ID_SANITIZED: epiccleanupyakshaver + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }} + model: ${{ needs.activation.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@f1073c5498ee46fec1530555a7c953445417c69b # v0.56.2 + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + with: + github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh latest + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + CUSTOM_GITHUB_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.8 node:lts-alpine + - name: Write Safe Outputs Config + run: | + mkdir -p /opt/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"add_comment":{"max":6,"target":"*"},"create_issue":{"max":15},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + GH_AW_SAFE_OUTPUTS_CONFIG_EOF + cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' + [ + { + "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 15 issue(s) can be created.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.", + "type": "string" + }, + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "labels": { + "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.", + "items": { + "type": "string" + }, + "type": "array" + }, + "parent": { + "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.", + "type": [ + "number", + "string" + ] + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + }, + "temporary_id": { + "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.", + "pattern": "^aw_[A-Za-z0-9]{3,12}$", + "type": "string" + }, + "title": { + "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.", + "type": "string" + } + }, + "required": [ + "title", + "body" + ], + "type": "object" + }, + "name": "create_issue" + }, + { + "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 6 comment(s) can be added. Target: *.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", + "type": "string" + }, + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "item_number": { + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers β€” it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.", + "type": [ + "number", + "string" + ] + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + }, + "temporary_id": { + "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.", + "pattern": "^aw_[A-Za-z0-9]{3,12}$", + "type": "string" + } + }, + "required": [ + "body" + ], + "type": "object" + }, + "name": "add_comment" + }, + { + "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "reason": { + "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", + "type": "string" + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + }, + "tool": { + "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + }, + "name": "missing_tool" + }, + { + "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "message": { + "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", + "type": "string" + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "noop" + }, + { + "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "context": { + "description": "Additional context about the missing data or where it should come from (max 256 characters).", + "type": "string" + }, + "data_type": { + "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", + "type": "string" + }, + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "reason": { + "description": "Explanation of why this data is needed to complete the task (max 256 characters).", + "type": "string" + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "missing_data" + } + ] + GH_AW_SAFE_OUTPUTS_TOOLS_EOF + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "create_issue": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "labels": { + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 + }, + "parent": { + "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "temporary_id": { + "type": "string" + }, + "title": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + } + } + GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash /opt/gh-aw/actions/start_safe_outputs_server.sh + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + export GITHUB_PERSONAL_ACCESS_TOKEN="$GITHUB_MCP_SERVER_TOKEN" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_PERSONAL_ACCESS_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.8' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "http", + "url": "https://api.githubcopilot.com/mcp/", + "headers": { + "Authorization": "Bearer \${GITHUB_PERSONAL_ACCESS_TOKEN}", + "X-MCP-Lockdown": "$([ "$GITHUB_MCP_LOCKDOWN" = "1" ] && echo true || echo false)", + "X-MCP-Readonly": "true", + "X-MCP-Toolsets": "context,repos,issues,pull_requests" + }, + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}" + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_EOF + - name: Download activation artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: activation + path: /tmp/gh-aw + - name: Clean git credentials + run: bash /opt/gh-aw/actions/clean_git_credentials.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Detect inference access error + id: detect-inference-error + if: always() + continue-on-error: true + run: bash /opt/gh-aw/actions/detect_inference_access_error.sh + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_CROSS_REPO_PAT,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_CROSS_REPO_PAT: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + - name: Append agent step summary + if: always() + run: bash /opt/gh-aw/actions/append_agent_step_summary.sh + - name: Upload Safe Outputs + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output + path: ${{ env.GH_AW_SAFE_OUTPUTS }} + if-no-files-found: warn + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Upload sanitized agent output + if: always() && env.GH_AW_AGENT_OUTPUT + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-output + path: ${{ env.GH_AW_AGENT_OUTPUT }} + if-no-files-found: warn + - name: Upload engine output files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent_outputs + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + if-no-files-found: ignore + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-artifacts + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + if-no-files-found: ignore + # --- Threat Detection (inline) --- + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + WORKFLOW_DESCRIPTION: "Analyses the three large YakShaver epics in the Product Backlog, clusters their\nopen PBIs thematically, and creates focused smaller sub-epics β€” all in\nSSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning." + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_detection_results + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: threat-detection.log + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Set detection conclusion + id: detection_conclusion + if: always() + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} + run: | + if [[ "$RUN_DETECTION" != "true" ]]; then + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection was not needed, marking as skipped" + elif [[ "$DETECTION_SUCCESS" == "true" ]]; then + echo "conclusion=success" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection passed successfully" + else + echo "conclusion=failure" >> "$GITHUB_OUTPUT" + echo "success=false" >> "$GITHUB_OUTPUT" + echo "Detection found issues" + fi + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + concurrency: + group: "gh-aw-conclusion-epic-cleanup-yakshaver" + cancel-in-progress: false + outputs: + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@f1073c5498ee46fec1530555a7c953445417c69b # v0.56.2 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + with: + github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing Tool + id: missing_tool + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + with: + github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent Failure + id: handle_agent_failure + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_GROUP_REPORTS: "false" + GH_AW_TIMEOUT_MINUTES: "20" + with: + github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op Message + id: handle_noop_message + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + safe_outputs: + needs: agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/epic-cleanup-yakshaver" + GH_AW_ENGINE_ID: "copilot" + GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }} + created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@f1073c5498ee46fec1530555a7c953445417c69b # v0.56.2 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":6,\"target\":\"*\"},\"create_issue\":{\"max\":15},\"missing_data\":{},\"missing_tool\":{}}" + with: + github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload safe output items manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn + diff --git a/.github/workflows/epic-cleanup-yakshaver.md b/.github/workflows/epic-cleanup-yakshaver.md new file mode 100644 index 00000000..d5e485c1 --- /dev/null +++ b/.github/workflows/epic-cleanup-yakshaver.md @@ -0,0 +1,178 @@ +--- +name: Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics +description: | + Analyses the three large YakShaver epics in the Product Backlog, clusters their + open PBIs thematically, and creates focused smaller sub-epics β€” all in + SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning. + +on: + workflow_dispatch: + +permissions: + contents: read + issues: read + pull-requests: read + +tools: + github: + mode: remote + toolsets: [default] + github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + +network: {} + +safe-outputs: + github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + create-issue: + max: 15 + add-comment: + max: 6 + target: "*" +--- + +# Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics + +You are an expert product backlog manager. Your task is to reorganise three large, unwieldy epics in the YakShaver Product Backlog by clustering their open PBIs into smaller, focused sub-epics. + +**All new sub-epic issues must be created in `SSWConsulting/SSW.YakShaver.Desktop`**, regardless of which repo the original epic lives in. This centralises the Desktop product's backlog in one place. + +## Epics to Reorganise + +| Epic | Source Repo | Issue | +|------|-------------|-------| +| ✨ YakShaver Agent 2.0 | SSWConsulting/SSW.YakShaver | #2811 | +| ✨ YakShaver Desktop App | SSWConsulting/SSW.YakShaver.Desktop | #677 | +| ♻️ Auth Migration | SSWConsulting/SSW.YakShaver | #3494 | + +## Step 1 β€” Read the Epics + +For each epic listed above, use the GitHub MCP tools to: + +1. Get the epic issue details (title, body, labels) β€” use the `github-token` configured for cross-repo access to read from both `SSWConsulting/SSW.YakShaver` and `SSWConsulting/SSW.YakShaver.Desktop` +2. Get all sub-issues of the epic +3. For each **open** sub-issue, read its title, labels, and body to understand its theme and functional area + +Skip any sub-issues that are already closed/completed. + +## Step 2 β€” Cluster Open PBIs Thematically + +Analyse all **open** sub-issues for each epic and group them into 2–4 thematic clusters. + +**Clustering guidelines:** +- Group by functional area (e.g., authentication, MCP integration, UI/UX, infrastructure) +- Group by issue type only if that is the dominant distinction (e.g., a large batch of bugs) +- Aim for 3–8 PBIs per cluster β€” not too fine-grained, not too broad +- A PBI that clearly spans two clusters goes in the most relevant one + +**Suggested starting clusters (validate against actual PBI content and adjust):** + +### YakShaver Agent 2.0 (SSWConsulting/SSW.YakShaver #2811) +- `πŸ”Œ MCP Integration` β€” MCP service configuration, default services, fallback/error handling for MCP +- `πŸ—οΈ Core Infrastructure` β€” database redesign, configurable settings, architecture/roadmap items + +### YakShaver Desktop App (SSWConsulting/SSW.YakShaver.Desktop #677) +- `πŸ› Bug Fixes` β€” all defects (camera/audio, auth errors, recording issues, UI glitches, sign-in failures) +- `✨ Features & UX` β€” new capabilities (stop button, project selection, transcript optimisation, system prompts) +- `πŸ—οΈ Platform & Infrastructure` β€” code signing, centralised logging, onboarding wizard refactor, LLM provider support, Intel macOS installer + +### Auth Migration (SSWConsulting/SSW.YakShaver #3494) +- `♻️ Scope 1: Enterprise Auth Simplification` β€” Make Entra permission optional, ADR revision, Better-Auth spike follow-up, implementation +- `🌐 Scope 2: Extended Login Providers` β€” Non-Entra tenant policy, invitation system for external users + +## Step 3 β€” Create Smaller Sub-Epic Issues + +For each cluster, create a new sub-epic issue using `create-issue`. + +⚠️ **All new sub-epics must be created in `SSWConsulting/SSW.YakShaver.Desktop`** β€” always set `target-repo: SSWConsulting/SSW.YakShaver.Desktop` on every `create-issue` call. + +**Required fields for each new sub-epic:** + +- **title**: Format β€” `[Emoji] [Cluster Name] β€” [Parent Epic Short Name]` + - Example: `πŸ”Œ MCP Integration β€” YakShaver Agent 2.0` +- **issue_type**: `Epic` +- **labels**: `Type: Feature` for feature/agent clusters; `Type: Bug` for bug clusters; `Type: Refactor` for refactor clusters +- **target-repo**: `SSWConsulting/SSW.YakShaver.Desktop` _(always)_ +- **body**: Use the template below + +**Body template:** + +``` +Part of [Parent Epic Title] [SourceOrg/SourceRepo]#[ParentIssueNumber] + +### Scope +[1–2 sentences describing what this sub-epic covers and why it is grouped together] + +### PBIs + +- [SourceOrg/SourceRepo]#[number] β€” [title] +- [SourceOrg/SourceRepo]#[number] β€” [title] +[... list all open PBIs in this cluster with their source repo prefix ...] + +### Acceptance Criteria +This sub-epic is complete when all listed PBIs above are done. +``` + +## Step 4 β€” Comment on Each Original Epic + +After creating all new sub-epics, use `add-comment` to post a reorganisation summary on each of the three **original** epics. + +Use `target-repo` to direct comments to the correct repository: +- `target-repo: SSWConsulting/SSW.YakShaver` for Agent 2.0 (#2811) and Auth Migration (#3494) +- `target-repo: SSWConsulting/SSW.YakShaver.Desktop` for Desktop App (#677) + +**Comment template:** + +```markdown +## πŸ—‚οΈ Epic Reorganised into Smaller Sub-Epics + +This epic was too large for effective sprint planning. It has been split into smaller, +focused sub-epics in [SSWConsulting/SSW.YakShaver.Desktop](https://github.com/SSWConsulting/SSW.YakShaver.Desktop). +Each sub-epic groups related PBIs by functional area. + +### New Sub-Epics Created + +| Sub-Epic | Scope | +|----------|-------| +| SSWConsulting/SSW.YakShaver.Desktop#[number] β€” [name] | [brief scope, ~8 words] | +| SSWConsulting/SSW.YakShaver.Desktop#[number] β€” [name] | [brief scope, ~8 words] | + +### PBI Assignments + +| PBI | Moved to Sub-Epic | +|-----|------------------| +| #[number] [title] | SSWConsulting/SSW.YakShaver.Desktop#[sub_epic_number] | +[... list all open PBIs and their assigned sub-epic ...] + +### πŸ“Œ Action Required β€” Complete the Sub-Issue Linking + +The new sub-epics have been created. To finish the reorganisation: + +1. **Open each new sub-epic in SSW.YakShaver.Desktop and set this issue as its parent** +2. **Move each PBI to be a sub-issue of its new sub-epic** (remove from this epic β†’ add to sub-epic) + +Or complete in bulk via GitHub CLI: + +```bash +# For each new sub-epic β€” reference it as a sub-issue of this epic (cross-repo) +gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC_NUMBER]/sub_issues \ + --method POST -f sub_issue_id=[NEW_DESKTOP_SUB_EPIC_NUMBER] + +# For each PBI β€” remove from this epic, link to new sub-epic in Desktop repo +gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC_NUMBER]/sub_issues/[PBI_NUMBER] \ + --method DELETE +``` + +> ℹ️ Replacing the sub-issue links is a manual step because the PBIs remain in their original +> repos β€” only the sub-epic tracking issues have moved to SSW.YakShaver.Desktop. +``` + +## Important Notes + +- Only process **open** PBIs β€” do not touch closed/completed sub-issues +- The three original epics must remain open and unmodified +- Every new sub-epic goes in `SSWConsulting/SSW.YakShaver.Desktop` β€” no exceptions +- The `GH_AW_CROSS_REPO_PAT` secret is required for: + - Reading issues in `SSWConsulting/SSW.YakShaver` (Agent 2.0, Auth Migration) + - Creating all new sub-epics in `SSWConsulting/SSW.YakShaver.Desktop` + - Posting comments on `SSWConsulting/SSW.YakShaver` issues +- If the secret is missing, warn clearly in a comment on each epic that the PAT must be configured at **Settings β†’ Secrets β†’ Actions β†’ `GH_AW_CROSS_REPO_PAT`** in `SSWConsulting/SSW.YakShaver.Desktop` From 61bddf21deaf9f809d14aa2212651915226451e9 Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Mon, 9 Mar 2026 17:38:41 +0800 Subject: [PATCH 02/11] =?UTF-8?q?refactor:=20revise=20epic-cleanup=20workf?= =?UTF-8?q?low=20=E2=80=94=20holistic=20cross-epic=20clustering,=20all=20P?= =?UTF-8?q?BIs,=20progress=20tracking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Key changes from initial draft: - Read ALL sub-issues (open + closed), not just open ones - Cluster PBIs holistically across all three epics; Desktop App + Agent 2.0 PBIs sharing the same theme go into the same new smaller epic - Target 10-20 PBIs per cluster (5-8 total) β€” coarser, fewer epics - All-closed clusters are acceptable; group by theme not by state - New epic body has a full βœ…/πŸ”² progress table per PBI with source repo - Comment on each original epic shows only relevant new epics + all PBI assignments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/epic-cleanup-yakshaver.lock.yml | 34 +-- .github/workflows/epic-cleanup-yakshaver.md | 200 ++++++++++-------- 2 files changed, 128 insertions(+), 106 deletions(-) diff --git a/.github/workflows/epic-cleanup-yakshaver.lock.yml b/.github/workflows/epic-cleanup-yakshaver.lock.yml index 7d32596d..fe0d74cd 100644 --- a/.github/workflows/epic-cleanup-yakshaver.lock.yml +++ b/.github/workflows/epic-cleanup-yakshaver.lock.yml @@ -21,13 +21,13 @@ # # For more information: https://github.github.com/gh-aw/introduction/overview/ # -# Analyses the three large YakShaver epics in the Product Backlog, clusters their -# open PBIs thematically, and creates focused smaller sub-epics β€” all in -# SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning. +# Analyses the three large YakShaver epics in the Product Backlog, clusters ALL their +# PBIs (open and closed) thematically into focused smaller epics β€” all created in +# SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e9b459c06c620d814b6fe25fb28d8fd0364ff40dbea5b17d791cf536eb0ba79a","compiler_version":"v0.56.2","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"b53ec64627f7f6cead85b7120d13411405930041ae1712f9a01b2f28b2a970a1","compiler_version":"v0.56.2","strict":true} -name: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" +name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" "on": workflow_dispatch: @@ -36,7 +36,7 @@ permissions: {} concurrency: group: "gh-aw-${{ github.workflow }}" -run-name: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" +run-name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" jobs: activation: @@ -62,7 +62,7 @@ jobs: GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" GH_AW_INFO_CLI_VERSION: "v0.56.2" - GH_AW_INFO_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_INFO_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" @@ -310,12 +310,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":6,"target":"*"},"create_issue":{"max":15},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":6,"target":"*"},"create_issue":{"max":12},"missing_data":{},"missing_tool":{},"noop":{"max":1}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ { - "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 15 issue(s) can be created.", + "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 12 issue(s) can be created.", "inputSchema": { "additionalProperties": false, "properties": { @@ -923,8 +923,8 @@ jobs: if: always() && steps.detection_guard.outputs.run_detection == 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: - WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" - WORKFLOW_DESCRIPTION: "Analyses the three large YakShaver epics in the Product Backlog, clusters their\nopen PBIs thematically, and creates focused smaller sub-epics β€” all in\nSSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning." + WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" + WORKFLOW_DESCRIPTION: "Analyses the three large YakShaver epics in the Product Backlog, clusters ALL their\nPBIs (open and closed) thematically into focused smaller epics β€” all created in\nSSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking." HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} with: script: | @@ -1052,7 +1052,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_NOOP_MAX: "1" - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | @@ -1065,7 +1065,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | @@ -1078,7 +1078,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" @@ -1099,7 +1099,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} @@ -1126,7 +1126,7 @@ jobs: GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/epic-cleanup-yakshaver" GH_AW_ENGINE_ID: "copilot" GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" outputs: code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} @@ -1164,7 +1164,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":6,\"target\":\"*\"},\"create_issue\":{\"max\":15},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":6,\"target\":\"*\"},\"create_issue\":{\"max\":12},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | diff --git a/.github/workflows/epic-cleanup-yakshaver.md b/.github/workflows/epic-cleanup-yakshaver.md index d5e485c1..527ef027 100644 --- a/.github/workflows/epic-cleanup-yakshaver.md +++ b/.github/workflows/epic-cleanup-yakshaver.md @@ -1,9 +1,9 @@ --- -name: Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics +name: Epic Cleanup β€” Cluster PBIs into Smaller Epics description: | - Analyses the three large YakShaver epics in the Product Backlog, clusters their - open PBIs thematically, and creates focused smaller sub-epics β€” all in - SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning. + Analyses the three large YakShaver epics in the Product Backlog, clusters ALL their + PBIs (open and closed) thematically into focused smaller epics β€” all created in + SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking. on: workflow_dispatch: @@ -24,17 +24,17 @@ network: {} safe-outputs: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} create-issue: - max: 15 + max: 12 add-comment: max: 6 target: "*" --- -# Epic Cleanup β€” Cluster PBIs into Smaller Sub-Epics +# Epic Cleanup β€” Cluster PBIs into Smaller Epics -You are an expert product backlog manager. Your task is to reorganise three large, unwieldy epics in the YakShaver Product Backlog by clustering their open PBIs into smaller, focused sub-epics. +You are an expert product backlog manager. Your task is to reorganise three large, unwieldy epics in the YakShaver Product Backlog. For each epic, you will read **all** of its PBIs (open and closed), cluster them into a small number of focused themes, and create one new, smaller epic per theme. -**All new sub-epic issues must be created in `SSWConsulting/SSW.YakShaver.Desktop`**, regardless of which repo the original epic lives in. This centralises the Desktop product's backlog in one place. +**All new epic issues must be created in `SSWConsulting/SSW.YakShaver.Desktop`**, regardless of which repo the original epic lives in. This centralises the Desktop product's backlog. ## Epics to Reorganise @@ -44,135 +44,157 @@ You are an expert product backlog manager. Your task is to reorganise three larg | ✨ YakShaver Desktop App | SSWConsulting/SSW.YakShaver.Desktop | #677 | | ♻️ Auth Migration | SSWConsulting/SSW.YakShaver | #3494 | -## Step 1 β€” Read the Epics +--- + +## Step 1 β€” Read Every PBI in Each Epic -For each epic listed above, use the GitHub MCP tools to: +For each of the three epics: -1. Get the epic issue details (title, body, labels) β€” use the `github-token` configured for cross-repo access to read from both `SSWConsulting/SSW.YakShaver` and `SSWConsulting/SSW.YakShaver.Desktop` -2. Get all sub-issues of the epic -3. For each **open** sub-issue, read its title, labels, and body to understand its theme and functional area +1. Read the epic issue body and labels +2. Fetch **all sub-issues** β€” including both open and closed ones +3. For every sub-issue, record: + - Issue number and title + - State: `open` or `closed` + - Labels (e.g., Type: Bug, Type: Feature, Type: Refactor) + - A one-line summary of what it does (from title or first sentence of body) -Skip any sub-issues that are already closed/completed. +Do **not** skip closed PBIs β€” they are needed to show full progress context in the new epics. + +--- -## Step 2 β€” Cluster Open PBIs Thematically +## Step 2 β€” Cluster All PBIs Holistically Across the Three Epics -Analyse all **open** sub-issues for each epic and group them into 2–4 thematic clusters. +After reading all PBIs from all three epics, treat them as **one unified pool** and cluster them by theme. PBIs from different source epics may end up in the same new smaller epic if they are closely related. -**Clustering guidelines:** -- Group by functional area (e.g., authentication, MCP integration, UI/UX, infrastructure) -- Group by issue type only if that is the dominant distinction (e.g., a large batch of bugs) -- Aim for 3–8 PBIs per cluster β€” not too fine-grained, not too broad -- A PBI that clearly spans two clusters goes in the most relevant one +**Clustering rules:** -**Suggested starting clusters (validate against actual PBI content and adjust):** +- Each cluster should represent a coherent functional area or delivery theme +- **Cross-epic clustering is encouraged** β€” Desktop App PBIs and Agent 2.0 PBIs that share the same theme (e.g., MCP integration, video pipeline) should be grouped together +- Target **10–20 PBIs per cluster** β€” coarse-grained, not fine-grained +- Aim for **5–8 clusters total** across all three epics combined β€” let the actual PBI themes drive the count, not the source epic structure +- It is fine for a cluster to consist entirely of closed PBIs β€” group by theme, not by state +- Auth Migration PBIs are self-contained (auth/identity scope) and should remain in their own cluster(s) +- A PBI that spans two themes goes in whichever is more dominant -### YakShaver Agent 2.0 (SSWConsulting/SSW.YakShaver #2811) -- `πŸ”Œ MCP Integration` β€” MCP service configuration, default services, fallback/error handling for MCP -- `πŸ—οΈ Core Infrastructure` β€” database redesign, configurable settings, architecture/roadmap items +**Suggested starting clusters β€” validate against actual PBI content and adjust:** -### YakShaver Desktop App (SSWConsulting/SSW.YakShaver.Desktop #677) -- `πŸ› Bug Fixes` β€” all defects (camera/audio, auth errors, recording issues, UI glitches, sign-in failures) -- `✨ Features & UX` β€” new capabilities (stop button, project selection, transcript optimisation, system prompts) -- `πŸ—οΈ Platform & Infrastructure` β€” code signing, centralised logging, onboarding wizard refactor, LLM provider support, Intel macOS installer +- `πŸ”Œ MCP & AI Pipeline` β€” MCP host/client, MCP service config, fallback/error handling, OpenAI integration, transcript pipeline (from Agent 2.0 and Desktop App) +- `🎬 Video Recording & Processing` β€” screen capture, local video processing, FFmpeg, upload pipeline, recording confirmation, video hosting (from Agent 2.0 and Desktop App) +- `✨ Features & UX` β€” stop button, project selection, transcript optimisation, fallback/system prompt, additional LLM providers, customisable prompt (from Desktop App) +- `πŸ› Bug Fixes & Quality` β€” all defects across Desktop App: camera/audio, auth errors, sign-in failures, UI glitches, download confusion, Azure DevOps MCP bugs +- `πŸ—οΈ Platform & Infrastructure` β€” Tauri app init, database redesign, configurable settings, code signing, centralised logging, onboarding wizard refactor, Intel macOS installer, architecture/roadmap (from Agent 2.0 and Desktop App) +- `♻️ Auth Scope 1 β€” Enterprise Auth` β€” Make Entra permission optional, Better-Auth spike, ADR revision, implementation (from Auth Migration) +- `🌐 Auth Scope 2 β€” Extended Login Providers` β€” non-Entra tenant policy, invitation system (from Auth Migration) -### Auth Migration (SSWConsulting/SSW.YakShaver #3494) -- `♻️ Scope 1: Enterprise Auth Simplification` β€” Make Entra permission optional, ADR revision, Better-Auth spike follow-up, implementation -- `🌐 Scope 2: Extended Login Providers` β€” Non-Entra tenant policy, invitation system for external users +--- -## Step 3 β€” Create Smaller Sub-Epic Issues +## Step 3 β€” Create One New Epic per Cluster -For each cluster, create a new sub-epic issue using `create-issue`. +For each cluster identified in Step 2, create a new epic issue using `create-issue`. -⚠️ **All new sub-epics must be created in `SSWConsulting/SSW.YakShaver.Desktop`** β€” always set `target-repo: SSWConsulting/SSW.YakShaver.Desktop` on every `create-issue` call. +⚠️ **Set `target-repo: SSWConsulting/SSW.YakShaver.Desktop` on every `create-issue` call.** -**Required fields for each new sub-epic:** +**Issue fields:** -- **title**: Format β€” `[Emoji] [Cluster Name] β€” [Parent Epic Short Name]` - - Example: `πŸ”Œ MCP Integration β€” YakShaver Agent 2.0` +- **title**: `[Emoji] [Cluster Name]` + - Example: `πŸ”Œ MCP & AI Pipeline` - **issue_type**: `Epic` -- **labels**: `Type: Feature` for feature/agent clusters; `Type: Bug` for bug clusters; `Type: Refactor` for refactor clusters -- **target-repo**: `SSWConsulting/SSW.YakShaver.Desktop` _(always)_ -- **body**: Use the template below +- **labels**: `Type: Feature` for feature/AI clusters; `Type: Bug` for bug clusters; `Type: Refactor` for refactor clusters +- **target-repo**: `SSWConsulting/SSW.YakShaver.Desktop` +- **body**: Use the template below exactly -**Body template:** +**Body template for each new epic:** -``` -Part of [Parent Epic Title] [SourceOrg/SourceRepo]#[ParentIssueNumber] +```markdown +Consolidates PBIs from: +- [source epic 1 title] β€” [SourceOrg/SourceRepo]#[number] +- [source epic 2 title] β€” [SourceOrg/SourceRepo]#[number] (if this cluster spans multiple source epics) ### Scope -[1–2 sentences describing what this sub-epic covers and why it is grouped together] +[2–3 sentences describing what this epic covers, which source epics it draws from, and the value it delivers] + +### Progress -### PBIs - -- [SourceOrg/SourceRepo]#[number] β€” [title] -- [SourceOrg/SourceRepo]#[number] β€” [title] -[... list all open PBIs in this cluster with their source repo prefix ...] +> βœ… = done Β· πŸ”² = open / in progress + +| # | Repo | Title | Status | +|---|------|-------|--------| +| [number] | SSW.YakShaver | [title] | βœ… | +| [number] | SSW.YakShaver.Desktop | [title] | πŸ”² | +[... one row per PBI in this cluster, all states, all source repos ...] + +**[X] / [total] done** ### Acceptance Criteria -This sub-epic is complete when all listed PBIs above are done. +This epic is complete when all πŸ”² open PBIs in the progress table above are closed. ``` +--- + ## Step 4 β€” Comment on Each Original Epic -After creating all new sub-epics, use `add-comment` to post a reorganisation summary on each of the three **original** epics. +After all new epics are created, post one summary comment on each of the three **original** epics using `add-comment`. -Use `target-repo` to direct comments to the correct repository: +Target repos for comments: - `target-repo: SSWConsulting/SSW.YakShaver` for Agent 2.0 (#2811) and Auth Migration (#3494) - `target-repo: SSWConsulting/SSW.YakShaver.Desktop` for Desktop App (#677) -**Comment template:** +**Comment body:** ```markdown -## πŸ—‚οΈ Epic Reorganised into Smaller Sub-Epics +## πŸ—‚οΈ Epic Reorganised β€” Smaller Epics Created -This epic was too large for effective sprint planning. It has been split into smaller, -focused sub-epics in [SSWConsulting/SSW.YakShaver.Desktop](https://github.com/SSWConsulting/SSW.YakShaver.Desktop). -Each sub-epic groups related PBIs by functional area. +This epic's PBIs have been clustered thematically (along with related PBIs from sibling epics) +into focused smaller epics in +[SSWConsulting/SSW.YakShaver.Desktop](https://github.com/SSWConsulting/SSW.YakShaver.Desktop). -### New Sub-Epics Created +### New Epics (containing PBIs from this epic) -| Sub-Epic | Scope | -|----------|-------| -| SSWConsulting/SSW.YakShaver.Desktop#[number] β€” [name] | [brief scope, ~8 words] | -| SSWConsulting/SSW.YakShaver.Desktop#[number] β€” [name] | [brief scope, ~8 words] | +| Epic | Open | Done | Scope | +|------|------|------|-------| +| [SSW.YakShaver.Desktop#number](link) β€” [name] | [X] open | [Y] done | [~10 word description] | +[... only list new epics that contain at least one PBI from *this* original epic ...] -### PBI Assignments +### PBIs from This Epic β†’ New Epic Assignment -| PBI | Moved to Sub-Epic | -|-----|------------------| -| #[number] [title] | SSWConsulting/SSW.YakShaver.Desktop#[sub_epic_number] | -[... list all open PBIs and their assigned sub-epic ...] +| PBI | State | New Epic | +|-----|-------|---------| +| SSWConsulting/[repo]#[number] [title] | βœ…/πŸ”² | [SSW.YakShaver.Desktop#number](link) | +[... all PBIs from this original epic, open and closed ...] -### πŸ“Œ Action Required β€” Complete the Sub-Issue Linking +### πŸ“Œ Action Required -The new sub-epics have been created. To finish the reorganisation: +1. **Link each new epic above as a sub-issue of this epic** so the GitHub progress bar updates +2. **Re-assign each PBI as a sub-issue of its new epic** (remove from this epic β†’ add to the smaller one) -1. **Open each new sub-epic in SSW.YakShaver.Desktop and set this issue as its parent** -2. **Move each PBI to be a sub-issue of its new sub-epic** (remove from this epic β†’ add to sub-epic) - -Or complete in bulk via GitHub CLI: +GitHub CLI commands: ```bash -# For each new sub-epic β€” reference it as a sub-issue of this epic (cross-repo) -gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC_NUMBER]/sub_issues \ - --method POST -f sub_issue_id=[NEW_DESKTOP_SUB_EPIC_NUMBER] +# Add a new epic as a sub-issue of this epic +gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC]/sub_issues \ + --method POST -f sub_issue_id=[NEW_EPIC_NUMBER] -# For each PBI β€” remove from this epic, link to new sub-epic in Desktop repo -gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC_NUMBER]/sub_issues/[PBI_NUMBER] \ +# Move a PBI: remove from this epic +gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC]/sub_issues/[PBI_NUMBER] \ --method DELETE +# Move a PBI: add to new epic in Desktop repo +gh api repos/SSWConsulting/SSW.YakShaver.Desktop/issues/[NEW_EPIC_NUMBER]/sub_issues \ + --method POST -f sub_issue_id=[PBI_NUMBER] ``` -> ℹ️ Replacing the sub-issue links is a manual step because the PBIs remain in their original -> repos β€” only the sub-epic tracking issues have moved to SSW.YakShaver.Desktop. +> ℹ️ PBIs remain in their original repos. Only the tracking epics move to SSW.YakShaver.Desktop. ``` +--- + ## Important Notes -- Only process **open** PBIs β€” do not touch closed/completed sub-issues -- The three original epics must remain open and unmodified -- Every new sub-epic goes in `SSWConsulting/SSW.YakShaver.Desktop` β€” no exceptions -- The `GH_AW_CROSS_REPO_PAT` secret is required for: - - Reading issues in `SSWConsulting/SSW.YakShaver` (Agent 2.0, Auth Migration) - - Creating all new sub-epics in `SSWConsulting/SSW.YakShaver.Desktop` - - Posting comments on `SSWConsulting/SSW.YakShaver` issues -- If the secret is missing, warn clearly in a comment on each epic that the PAT must be configured at **Settings β†’ Secrets β†’ Actions β†’ `GH_AW_CROSS_REPO_PAT`** in `SSWConsulting/SSW.YakShaver.Desktop` +- Include **all** PBIs β€” open and closed β€” so new epics show real progress +- Cluster **holistically across all three source epics** β€” PBIs from Desktop App and Agent 2.0 may share the same new smaller epic +- All-closed clusters are acceptable β€” group by theme, not state +- Each new epic body **must** include the full βœ…/πŸ”² progress table with every PBI and its source repo +- The three original epics must stay open and untouched β€” do not modify or close them +- The `GH_AW_CROSS_REPO_PAT` secret must have: + - **Read** access to `SSWConsulting/SSW.YakShaver` + - **Read + Write** access to `SSWConsulting/SSW.YakShaver.Desktop` +- If the secret is missing or has insufficient scope, stop and post a warning comment on the Desktop App epic (#677) instructing the team to add `GH_AW_CROSS_REPO_PAT` at **Settings β†’ Secrets β†’ Actions** in `SSWConsulting/SSW.YakShaver.Desktop` From b987449c8a609c13b0a3fe19db5481768b7ec53a Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Mon, 9 Mar 2026 17:58:23 +0800 Subject: [PATCH 03/11] fix: correct 5 technical issues in epic-cleanup-yakshaver workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Tool schema alignment β€” use create_issue (not create-issue); remove unsupported fields issue_type and target-repo from agent instructions; agent only provides title, body, labels 2. 50-link comment limit β€” replace Markdown links with plain SSWConsulting/org.repo#number cross-references in add_comment template; cross-repo commenting on SSW.YakShaver issues documented as manual step 3. Remove progress table β€” dropped βœ…/πŸ”² PBI table from new epic body; GitHub sub-issue progress bar covers this natively 4. GH_AW_CROSS_REPO_PAT validation β€” added pre-agent steps: block that checks for the secret early; if missing, posts a warning comment on Desktop epic #677 via GITHUB_TOKEN then exits 1 (fail fast) 5. ubuntu-slim runner β€” patch activation and safe_outputs jobs from ubuntu-slim to ubuntu-latest in lock file; add runs-on: ubuntu-latest to frontmatter for agent job Also: add title-prefix [YakShaver 2.0] and target-repo to safe-outputs frontmatter so create_issue calls always land in SSW.YakShaver.Desktop with the correct prefix automatically Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/epic-cleanup-yakshaver.lock.yml | 22 ++- .github/workflows/epic-cleanup-yakshaver.md | 142 ++++++++++++------ 2 files changed, 106 insertions(+), 58 deletions(-) diff --git a/.github/workflows/epic-cleanup-yakshaver.lock.yml b/.github/workflows/epic-cleanup-yakshaver.lock.yml index fe0d74cd..9b2deef4 100644 --- a/.github/workflows/epic-cleanup-yakshaver.lock.yml +++ b/.github/workflows/epic-cleanup-yakshaver.lock.yml @@ -25,7 +25,7 @@ # PBIs (open and closed) thematically into focused smaller epics β€” all created in # SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"b53ec64627f7f6cead85b7120d13411405930041ae1712f9a01b2f28b2a970a1","compiler_version":"v0.56.2","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"06b67ed2b5d5f818817a5d2067296713aae48d0a1a0171cdae138f55601eb867","compiler_version":"v0.56.2","strict":true} name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" "on": @@ -40,7 +40,7 @@ run-name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" jobs: activation: - runs-on: ubuntu-slim + runs-on: ubuntu-latest permissions: contents: read outputs: @@ -261,6 +261,12 @@ jobs: persist-credentials: false - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - env: + GH_AW_PAT: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + GH_TOKEN: ${{ github.token }} + name: Validate GH_AW_CROSS_REPO_PAT + run: "if [ -z \"$GH_AW_PAT\" ]; then\n echo \"::error::GH_AW_CROSS_REPO_PAT secret is not configured.\"\n BODY=\"## ⚠️ Epic Cleanup workflow failed to start\\n\\nThe \\`GH_AW_CROSS_REPO_PAT\\` secret is missing. This workflow needs a PAT to read issues from \\`SSW.YakShaver\\` and create epics in \\`SSW.YakShaver.Desktop\\`.\\n\\nGo to **Settings β†’ Secrets β†’ Actions β†’ New repository secret**, add \\`GH_AW_CROSS_REPO_PAT\\` with a fine-grained token that has Issues Read+Write on both \\`SSWConsulting/SSW.YakShaver\\` and \\`SSWConsulting/SSW.YakShaver.Desktop\\`, then re-run this workflow.\"\n printf '%b' \"$BODY\" | gh issue comment 677 --repo SSWConsulting/SSW.YakShaver.Desktop --body-file -\n exit 1\nfi\n" + - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -310,12 +316,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":6,"target":"*"},"create_issue":{"max":12},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":3,"target":"*","target-repo":"SSWConsulting/SSW.YakShaver.Desktop"},"create_issue":{"max":12},"missing_data":{},"missing_tool":{},"noop":{"max":1}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ { - "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 12 issue(s) can be created.", + "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 12 issue(s) can be created. Title will be prefixed with \"[YakShaver 2.0] \". Issues will be created in repository \"SSWConsulting/SSW.YakShaver.Desktop\".", "inputSchema": { "additionalProperties": false, "properties": { @@ -364,7 +370,7 @@ jobs: "name": "create_issue" }, { - "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 6 comment(s) can be added. Target: *.", + "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 3 comment(s) can be added. Target: *. Comments will be added in repository \"SSWConsulting/SSW.YakShaver.Desktop\".", "inputSchema": { "additionalProperties": false, "properties": { @@ -1015,7 +1021,7 @@ jobs: - agent - safe_outputs if: (always()) && (needs.agent.result != 'skipped') - runs-on: ubuntu-slim + runs-on: ubuntu-latest permissions: contents: read discussions: write @@ -1115,7 +1121,7 @@ jobs: safe_outputs: needs: agent if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') - runs-on: ubuntu-slim + runs-on: ubuntu-latest permissions: contents: read discussions: write @@ -1164,7 +1170,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":6,\"target\":\"*\"},\"create_issue\":{\"max\":12},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\",\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\"},\"create_issue\":{\"max\":12,\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\",\"title_prefix\":\"[YakShaver 2.0] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | diff --git a/.github/workflows/epic-cleanup-yakshaver.md b/.github/workflows/epic-cleanup-yakshaver.md index 527ef027..e7435b1c 100644 --- a/.github/workflows/epic-cleanup-yakshaver.md +++ b/.github/workflows/epic-cleanup-yakshaver.md @@ -8,6 +8,8 @@ description: | on: workflow_dispatch: +runs-on: ubuntu-latest + permissions: contents: read issues: read @@ -21,13 +23,29 @@ tools: network: {} +steps: + - name: Validate GH_AW_CROSS_REPO_PAT + run: | + if [ -z "$GH_AW_PAT" ]; then + echo "::error::GH_AW_CROSS_REPO_PAT secret is not configured." + BODY="## ⚠️ Epic Cleanup workflow failed to start\n\nThe \`GH_AW_CROSS_REPO_PAT\` secret is missing. This workflow needs a PAT to read issues from \`SSW.YakShaver\` and create epics in \`SSW.YakShaver.Desktop\`.\n\nGo to **Settings β†’ Secrets β†’ Actions β†’ New repository secret**, add \`GH_AW_CROSS_REPO_PAT\` with a fine-grained token that has Issues Read+Write on both \`SSWConsulting/SSW.YakShaver\` and \`SSWConsulting/SSW.YakShaver.Desktop\`, then re-run this workflow." + printf '%b' "$BODY" | gh issue comment 677 --repo SSWConsulting/SSW.YakShaver.Desktop --body-file - + exit 1 + fi + env: + GH_AW_PAT: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + GH_TOKEN: ${{ github.token }} + safe-outputs: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} create-issue: max: 12 + title-prefix: "[YakShaver 2.0] " + target-repo: SSWConsulting/SSW.YakShaver.Desktop add-comment: - max: 6 + max: 3 target: "*" + target-repo: SSWConsulting/SSW.YakShaver.Desktop --- # Epic Cleanup β€” Cluster PBIs into Smaller Epics @@ -90,94 +108,116 @@ After reading all PBIs from all three epics, treat them as **one unified pool** ## Step 3 β€” Create One New Epic per Cluster -For each cluster identified in Step 2, create a new epic issue using `create-issue`. +For each cluster identified in Step 2, call the `create_issue` tool once. + +The tool automatically prefixes every title with `[YakShaver 2.0]` and creates the issue in `SSWConsulting/SSW.YakShaver.Desktop`. Do **not** include `issue_type`, `repo`, or `target-repo` β€” those fields are not supported and will cause validation errors. -⚠️ **Set `target-repo: SSWConsulting/SSW.YakShaver.Desktop` on every `create-issue` call.** +**Fields to provide:** -**Issue fields:** +| Field | Value | +|-------|-------| +| `title` | `[Emoji] [Cluster Name]` β€” e.g. `πŸ”Œ MCP & AI Pipeline` | +| `labels` | `Type: Feature` / `Type: Bug` / `Type: Refactor` as appropriate | +| `body` | Use the template below | -- **title**: `[Emoji] [Cluster Name]` - - Example: `πŸ”Œ MCP & AI Pipeline` -- **issue_type**: `Epic` -- **labels**: `Type: Feature` for feature/AI clusters; `Type: Bug` for bug clusters; `Type: Refactor` for refactor clusters -- **target-repo**: `SSWConsulting/SSW.YakShaver.Desktop` -- **body**: Use the template below exactly +Do NOT include `issue_type` or any field not listed above. **Body template for each new epic:** ```markdown Consolidates PBIs from: -- [source epic 1 title] β€” [SourceOrg/SourceRepo]#[number] -- [source epic 2 title] β€” [SourceOrg/SourceRepo]#[number] (if this cluster spans multiple source epics) +- [source epic 1 title] β€” SSWConsulting/[SourceRepo]#[number] +- [source epic 2 title] β€” SSWConsulting/[SourceRepo]#[number] (omit if single source) ### Scope -[2–3 sentences describing what this epic covers, which source epics it draws from, and the value it delivers] - -### Progress - -> βœ… = done Β· πŸ”² = open / in progress +[2–3 sentences describing what this epic covers and the value it delivers] -| # | Repo | Title | Status | -|---|------|-------|--------| -| [number] | SSW.YakShaver | [title] | βœ… | -| [number] | SSW.YakShaver.Desktop | [title] | πŸ”² | -[... one row per PBI in this cluster, all states, all source repos ...] +### PBIs -**[X] / [total] done** + +- SSWConsulting/[SourceRepo]#[number] β€” [title] +- SSWConsulting/[SourceRepo]#[number] β€” [title] +[... one line per PBI in this cluster, all states (open and closed) ...] ### Acceptance Criteria -This epic is complete when all πŸ”² open PBIs in the progress table above are closed. +This epic is complete when all open PBIs listed above are closed. ``` +Use plain `SSWConsulting/RepoName#number` references (not Markdown links) β€” GitHub auto-links these without counting toward the 50-link comment limit. + --- -## Step 4 β€” Comment on Each Original Epic +## Step 4 β€” Post a Summary Comment on Desktop App Epic #677 + +After all new epics are created, call `add_comment` **once** to post a comprehensive summary on the Desktop App epic. + +**Important constraints:** +- Always provide `item_number: 677` β€” this workflow is triggered by `workflow_dispatch` so auto-targeting does NOT work; the tool will fail without an explicit `item_number` +- All `add_comment` calls go to `SSWConsulting/SSW.YakShaver.Desktop` (configured in frontmatter); cross-repo commenting on `SSW.YakShaver` issues #2811 and #3494 is **not supported** by the tool and must be done manually +- Use plain `SSWConsulting/RepoName#number` cross-references (not Markdown links `[text](url)`) β€” the tool enforces a hard limit of 50 HTTP/HTTPS links per comment; with ~60 total PBIs across the three epics, explicit links will breach this limit -After all new epics are created, post one summary comment on each of the three **original** epics using `add-comment`. +**Call:** -Target repos for comments: -- `target-repo: SSWConsulting/SSW.YakShaver` for Agent 2.0 (#2811) and Auth Migration (#3494) -- `target-repo: SSWConsulting/SSW.YakShaver.Desktop` for Desktop App (#677) +``` +add_comment( + item_number: 677, + body: +) +``` -**Comment body:** +**Comment body template:** ```markdown -## πŸ—‚οΈ Epic Reorganised β€” Smaller Epics Created +## πŸ—‚οΈ Epic Reorganised β€” [N] Smaller Epics Created -This epic's PBIs have been clustered thematically (along with related PBIs from sibling epics) -into focused smaller epics in -[SSWConsulting/SSW.YakShaver.Desktop](https://github.com/SSWConsulting/SSW.YakShaver.Desktop). +The three large YakShaver epics have been clustered thematically into smaller, focused epics +(all in SSWConsulting/SSW.YakShaver.Desktop). PBIs from Desktop App and Agent 2.0 that share +the same theme have been grouped together. -### New Epics (containing PBIs from this epic) +### New Epics Created -| Epic | Open | Done | Scope | -|------|------|------|-------| -| [SSW.YakShaver.Desktop#number](link) β€” [name] | [X] open | [Y] done | [~10 word description] | -[... only list new epics that contain at least one PBI from *this* original epic ...] +| Epic | Scope | +|------|-------| +| SSWConsulting/SSW.YakShaver.Desktop#[number] | [~10 word description] | +[... one row per new epic; use plain org/repo#number, NOT markdown links ...] -### PBIs from This Epic β†’ New Epic Assignment +### PBI Assignment β€” Agent 2.0 (SSWConsulting/SSW.YakShaver#2811) | PBI | State | New Epic | |-----|-------|---------| -| SSWConsulting/[repo]#[number] [title] | βœ…/πŸ”² | [SSW.YakShaver.Desktop#number](link) | -[... all PBIs from this original epic, open and closed ...] +| SSWConsulting/SSW.YakShaver#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | +[... all PBIs from Agent 2.0 epic ...] + +### PBI Assignment β€” Desktop App (SSWConsulting/SSW.YakShaver.Desktop#677) + +| PBI | State | New Epic | +|-----|-------|---------| +| SSWConsulting/SSW.YakShaver.Desktop#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | +[... all PBIs from Desktop App epic ...] + +### PBI Assignment β€” Auth Migration (SSWConsulting/SSW.YakShaver#3494) + +| PBI | State | New Epic | +|-----|-------|---------| +| SSWConsulting/SSW.YakShaver#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | +[... all PBIs from Auth Migration epic ...] ### πŸ“Œ Action Required -1. **Link each new epic above as a sub-issue of this epic** so the GitHub progress bar updates -2. **Re-assign each PBI as a sub-issue of its new epic** (remove from this epic β†’ add to the smaller one) +1. **Link each new epic as a sub-issue of its source epic** (progress bar will update automatically) +2. **Re-assign each PBI as a sub-issue of its new smaller epic** (remove from old epic β†’ add to new) +3. **Manually post the same summary** on SSWConsulting/SSW.YakShaver#2811 and SSWConsulting/SSW.YakShaver#3494 (cross-repo commenting is not supported by this workflow) -GitHub CLI commands: +GitHub CLI commands for bulk re-assignment: ```bash -# Add a new epic as a sub-issue of this epic -gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC]/sub_issues \ +# Add a new epic as a sub-issue of the original epic +gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[SOURCE_EPIC]/sub_issues \ --method POST -f sub_issue_id=[NEW_EPIC_NUMBER] -# Move a PBI: remove from this epic -gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC]/sub_issues/[PBI_NUMBER] \ +# Move a PBI: remove from old epic, add to new epic +gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[SOURCE_EPIC]/sub_issues/[PBI_NUMBER] \ --method DELETE -# Move a PBI: add to new epic in Desktop repo gh api repos/SSWConsulting/SSW.YakShaver.Desktop/issues/[NEW_EPIC_NUMBER]/sub_issues \ --method POST -f sub_issue_id=[PBI_NUMBER] ``` @@ -185,6 +225,8 @@ gh api repos/SSWConsulting/SSW.YakShaver.Desktop/issues/[NEW_EPIC_NUMBER]/sub_is > ℹ️ PBIs remain in their original repos. Only the tracking epics move to SSW.YakShaver.Desktop. ``` +If the comment body would exceed ~60 PBI rows, split into two `add_comment` calls (the workflow allows max 3 comments): first call covers Agent 2.0 PBIs, second covers Desktop App + Auth Migration PBIs. Always include `item_number: 677` on every call. + --- ## Important Notes From 008a88787af7d0e9764d017be88d7ce0d4b7119b Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Mon, 9 Mar 2026 18:20:08 +0800 Subject: [PATCH 04/11] feat: add cross-repo commenting via bash tool + post-steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add bash: true tool so agent can write per-epic comment files to /tmp/gh-aw/comments/ during its run - Step 4 now has agent write comment bodies for SSW.YakShaver epics #2811 and #3494 to files AND call add_comment for #677 - Add post-steps: job that reads the files and posts comments to SSWConsulting/SSW.YakShaver using GH_AW_CROSS_REPO_PAT - Add pre-step to mkdir -p /tmp/gh-aw/comments - post-steps skips gracefully if agent did not write a file - Remove 'manually post summary' note β€” now fully automated - Patch ubuntu-slim to ubuntu-latest in lock file Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/epic-cleanup-yakshaver.lock.yml | 9 +- .github/workflows/epic-cleanup-yakshaver.md | 110 ++++++++++-------- 2 files changed, 71 insertions(+), 48 deletions(-) diff --git a/.github/workflows/epic-cleanup-yakshaver.lock.yml b/.github/workflows/epic-cleanup-yakshaver.lock.yml index 9b2deef4..2985464c 100644 --- a/.github/workflows/epic-cleanup-yakshaver.lock.yml +++ b/.github/workflows/epic-cleanup-yakshaver.lock.yml @@ -25,7 +25,7 @@ # PBIs (open and closed) thematically into focused smaller epics β€” all created in # SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"06b67ed2b5d5f818817a5d2067296713aae48d0a1a0171cdae138f55601eb867","compiler_version":"v0.56.2","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"5ababe00126282abab56ec2b6aebee20d13c5bfbf7642b4851e86579a574c737","compiler_version":"v0.56.2","strict":true} name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" "on": @@ -266,6 +266,8 @@ jobs: GH_TOKEN: ${{ github.token }} name: Validate GH_AW_CROSS_REPO_PAT run: "if [ -z \"$GH_AW_PAT\" ]; then\n echo \"::error::GH_AW_CROSS_REPO_PAT secret is not configured.\"\n BODY=\"## ⚠️ Epic Cleanup workflow failed to start\\n\\nThe \\`GH_AW_CROSS_REPO_PAT\\` secret is missing. This workflow needs a PAT to read issues from \\`SSW.YakShaver\\` and create epics in \\`SSW.YakShaver.Desktop\\`.\\n\\nGo to **Settings β†’ Secrets β†’ Actions β†’ New repository secret**, add \\`GH_AW_CROSS_REPO_PAT\\` with a fine-grained token that has Issues Read+Write on both \\`SSWConsulting/SSW.YakShaver\\` and \\`SSWConsulting/SSW.YakShaver.Desktop\\`, then re-run this workflow.\"\n printf '%b' \"$BODY\" | gh issue comment 677 --repo SSWConsulting/SSW.YakShaver.Desktop --body-file -\n exit 1\nfi\n" + - name: Prepare comment output directory + run: mkdir -p /tmp/gh-aw/comments - name: Configure Git credentials env: @@ -880,6 +882,11 @@ jobs: else echo 'AWF binary not installed, skipping firewall log summary' fi + - env: + GH_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + name: Post summary to SSW.YakShaver epics + run: "for ISSUE in 2811 3494; do\n FILE=\"/tmp/gh-aw/comments/comment-yakshaver-${ISSUE}.md\"\n if [ -f \"$FILE\" ]; then\n echo \"Posting comment to SSWConsulting/SSW.YakShaver#${ISSUE}...\"\n gh issue comment \"$ISSUE\" \\\n --repo SSWConsulting/SSW.YakShaver \\\n --body-file \"$FILE\"\n else\n echo \"No comment file found for SSW.YakShaver#${ISSUE} β€” skipping.\"\n fi\ndone\n" + - name: Upload agent artifacts if: always() continue-on-error: true diff --git a/.github/workflows/epic-cleanup-yakshaver.md b/.github/workflows/epic-cleanup-yakshaver.md index e7435b1c..74b39f14 100644 --- a/.github/workflows/epic-cleanup-yakshaver.md +++ b/.github/workflows/epic-cleanup-yakshaver.md @@ -20,6 +20,7 @@ tools: mode: remote toolsets: [default] github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} + bash: true network: {} @@ -35,6 +36,25 @@ steps: env: GH_AW_PAT: ${{ secrets.GH_AW_CROSS_REPO_PAT }} GH_TOKEN: ${{ github.token }} + - name: Prepare comment output directory + run: mkdir -p /tmp/gh-aw/comments + +post-steps: + - name: Post summary to SSW.YakShaver epics + run: | + for ISSUE in 2811 3494; do + FILE="/tmp/gh-aw/comments/comment-yakshaver-${ISSUE}.md" + if [ -f "$FILE" ]; then + echo "Posting comment to SSWConsulting/SSW.YakShaver#${ISSUE}..." + gh issue comment "$ISSUE" \ + --repo SSWConsulting/SSW.YakShaver \ + --body-file "$FILE" + else + echo "No comment file found for SSW.YakShaver#${ISSUE} β€” skipping." + fi + done + env: + GH_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} safe-outputs: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} @@ -147,86 +167,83 @@ Use plain `SSWConsulting/RepoName#number` references (not Markdown links) β€” Gi --- -## Step 4 β€” Post a Summary Comment on Desktop App Epic #677 +## Step 4 β€” Post Summary Comments on All Three Original Epics -After all new epics are created, call `add_comment` **once** to post a comprehensive summary on the Desktop App epic. +After all new epics are created, post a reorganisation summary on each of the three original epics. -**Important constraints:** -- Always provide `item_number: 677` β€” this workflow is triggered by `workflow_dispatch` so auto-targeting does NOT work; the tool will fail without an explicit `item_number` -- All `add_comment` calls go to `SSWConsulting/SSW.YakShaver.Desktop` (configured in frontmatter); cross-repo commenting on `SSW.YakShaver` issues #2811 and #3494 is **not supported** by the tool and must be done manually -- Use plain `SSWConsulting/RepoName#number` cross-references (not Markdown links `[text](url)`) β€” the tool enforces a hard limit of 50 HTTP/HTTPS links per comment; with ~60 total PBIs across the three epics, explicit links will breach this limit +### 4a β€” Write per-epic comment files (for SSW.YakShaver epics) -**Call:** +The `add_comment` tool can only post to `SSWConsulting/SSW.YakShaver.Desktop`. For the two epics in `SSWConsulting/SSW.YakShaver` (#2811 and #3494), use the `bash` tool to write their comment bodies to files β€” a `post-steps:` job will pick them up and post via `gh issue comment`. +Use `bash` to write each file: + +```bash +cat > /tmp/gh-aw/comments/comment-yakshaver-2811.md << 'EOF' + +EOF + +cat > /tmp/gh-aw/comments/comment-yakshaver-3494.md << 'EOF' + +EOF ``` -add_comment( - item_number: 677, - body: -) -``` -**Comment body template:** +### 4b β€” Post comment on Desktop App epic #677 + +Call `add_comment` with `item_number: 677` for the Desktop App epic (same repo, no cross-repo needed). + +**Always provide `item_number: 677` explicitly** β€” `workflow_dispatch` triggers do not support auto-targeting. + +Use plain `SSWConsulting/RepoName#number` references (not Markdown links) β€” the tool has a hard limit of 50 HTTP/HTTPS links per comment. With ~60 PBIs total, plain references avoid breaching this limit. + +If the comment body would exceed ~60 PBI rows, split into two `add_comment` calls (max 3 total comments allowed). Always include `item_number: 677` on every call. + +--- + +### Comment template (use for all three epics, tailoring PBI section to each epic's own PBIs) ```markdown ## πŸ—‚οΈ Epic Reorganised β€” [N] Smaller Epics Created The three large YakShaver epics have been clustered thematically into smaller, focused epics -(all in SSWConsulting/SSW.YakShaver.Desktop). PBIs from Desktop App and Agent 2.0 that share +in SSWConsulting/SSW.YakShaver.Desktop. PBIs from Desktop App and Agent 2.0 that share the same theme have been grouped together. -### New Epics Created +### New Epics (containing PBIs from this epic) | Epic | Scope | |------|-------| | SSWConsulting/SSW.YakShaver.Desktop#[number] | [~10 word description] | -[... one row per new epic; use plain org/repo#number, NOT markdown links ...] - -### PBI Assignment β€” Agent 2.0 (SSWConsulting/SSW.YakShaver#2811) - -| PBI | State | New Epic | -|-----|-------|---------| -| SSWConsulting/SSW.YakShaver#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | -[... all PBIs from Agent 2.0 epic ...] +[... only rows for new epics that contain at least one PBI from THIS epic; plain org/repo#number only ...] -### PBI Assignment β€” Desktop App (SSWConsulting/SSW.YakShaver.Desktop#677) +### PBI Assignment | PBI | State | New Epic | |-----|-------|---------| -| SSWConsulting/SSW.YakShaver.Desktop#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | -[... all PBIs from Desktop App epic ...] - -### PBI Assignment β€” Auth Migration (SSWConsulting/SSW.YakShaver#3494) - -| PBI | State | New Epic | -|-----|-------|---------| -| SSWConsulting/SSW.YakShaver#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | -[... all PBIs from Auth Migration epic ...] +| SSWConsulting/[SourceRepo]#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | +[... all PBIs that belong to THIS original epic, open and closed ...] ### πŸ“Œ Action Required -1. **Link each new epic as a sub-issue of its source epic** (progress bar will update automatically) -2. **Re-assign each PBI as a sub-issue of its new smaller epic** (remove from old epic β†’ add to new) -3. **Manually post the same summary** on SSWConsulting/SSW.YakShaver#2811 and SSWConsulting/SSW.YakShaver#3494 (cross-repo commenting is not supported by this workflow) +1. **Link each new epic above as a sub-issue of this epic** so the GitHub progress bar updates +2. **Re-assign each PBI as a sub-issue of its new smaller epic** (remove from this epic β†’ add to the new one) -GitHub CLI commands for bulk re-assignment: +GitHub CLI commands: ```bash -# Add a new epic as a sub-issue of the original epic -gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[SOURCE_EPIC]/sub_issues \ +# Add a new epic as a sub-issue of this epic +gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC]/sub_issues \ --method POST -f sub_issue_id=[NEW_EPIC_NUMBER] -# Move a PBI: remove from old epic, add to new epic -gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[SOURCE_EPIC]/sub_issues/[PBI_NUMBER] \ +# Move a PBI: remove from this epic, add to new epic +gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC]/sub_issues/[PBI_NUMBER] \ --method DELETE gh api repos/SSWConsulting/SSW.YakShaver.Desktop/issues/[NEW_EPIC_NUMBER]/sub_issues \ --method POST -f sub_issue_id=[PBI_NUMBER] ``` -> ℹ️ PBIs remain in their original repos. Only the tracking epics move to SSW.YakShaver.Desktop. +> ℹ️ PBIs stay in their original repos. Only the tracking epics move to SSW.YakShaver.Desktop. ``` -If the comment body would exceed ~60 PBI rows, split into two `add_comment` calls (the workflow allows max 3 comments): first call covers Agent 2.0 PBIs, second covers Desktop App + Auth Migration PBIs. Always include `item_number: 677` on every call. - --- ## Important Notes @@ -234,9 +251,8 @@ If the comment body would exceed ~60 PBI rows, split into two `add_comment` call - Include **all** PBIs β€” open and closed β€” so new epics show real progress - Cluster **holistically across all three source epics** β€” PBIs from Desktop App and Agent 2.0 may share the same new smaller epic - All-closed clusters are acceptable β€” group by theme, not state -- Each new epic body **must** include the full βœ…/πŸ”² progress table with every PBI and its source repo - The three original epics must stay open and untouched β€” do not modify or close them +- Use `bash` to write comment files for #2811 and #3494 before calling `add_comment` for #677 β€” the `post-steps:` job will post them automatically - The `GH_AW_CROSS_REPO_PAT` secret must have: - **Read** access to `SSWConsulting/SSW.YakShaver` - - **Read + Write** access to `SSWConsulting/SSW.YakShaver.Desktop` -- If the secret is missing or has insufficient scope, stop and post a warning comment on the Desktop App epic (#677) instructing the team to add `GH_AW_CROSS_REPO_PAT` at **Settings β†’ Secrets β†’ Actions** in `SSWConsulting/SSW.YakShaver.Desktop` + - **Read + Write (Issues)** access to `SSWConsulting/SSW.YakShaver.Desktop` From 01b8bf89d8ac3d6ce2550e7c1a1c8abadae9a10e Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Mon, 9 Mar 2026 18:21:12 +0800 Subject: [PATCH 05/11] chore: add gh-aw actions-lock.json for supply chain security Pins GitHub Actions used by gh-aw compiled workflows to exact commit SHAs to prevent tag-mutable supply chain attacks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/aw/actions-lock.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/aw/actions-lock.json diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json new file mode 100644 index 00000000..e0234428 --- /dev/null +++ b/.github/aw/actions-lock.json @@ -0,0 +1,14 @@ +{ + "entries": { + "actions/github-script@v8": { + "repo": "actions/github-script", + "version": "v8", + "sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd" + }, + "github/gh-aw/actions/setup@v0.56.2": { + "repo": "github/gh-aw/actions/setup", + "version": "v0.56.2", + "sha": "f1073c5498ee46fec1530555a7c953445417c69b" + } + } +} From 933ef2a28fdd48d7e01506de5e15a57991b1e6f0 Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Fri, 13 Mar 2026 10:36:06 +0800 Subject: [PATCH 06/11] refactor: split Features/UX cluster and merge Auth into single cluster MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Split '✨ Features & UX' into '✨ Features' and '🎨 UX Improvements' - Merge '♻️ Auth Scope 1' and '🌐 Auth Scope 2' into single '♻️ Auth Migration' cluster Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/epic-cleanup-yakshaver.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/epic-cleanup-yakshaver.md b/.github/workflows/epic-cleanup-yakshaver.md index 74b39f14..49735a9a 100644 --- a/.github/workflows/epic-cleanup-yakshaver.md +++ b/.github/workflows/epic-cleanup-yakshaver.md @@ -118,11 +118,11 @@ After reading all PBIs from all three epics, treat them as **one unified pool** - `πŸ”Œ MCP & AI Pipeline` β€” MCP host/client, MCP service config, fallback/error handling, OpenAI integration, transcript pipeline (from Agent 2.0 and Desktop App) - `🎬 Video Recording & Processing` β€” screen capture, local video processing, FFmpeg, upload pipeline, recording confirmation, video hosting (from Agent 2.0 and Desktop App) -- `✨ Features & UX` β€” stop button, project selection, transcript optimisation, fallback/system prompt, additional LLM providers, customisable prompt (from Desktop App) +- `✨ Features` β€” stop button, project selection, transcript optimisation, fallback/system prompt, additional LLM providers, customisable prompt (from Desktop App) +- `🎨 UX Improvements` β€” UI polish, onboarding experience, settings panels, visual consistency, accessibility improvements (from Desktop App) - `πŸ› Bug Fixes & Quality` β€” all defects across Desktop App: camera/audio, auth errors, sign-in failures, UI glitches, download confusion, Azure DevOps MCP bugs - `πŸ—οΈ Platform & Infrastructure` β€” Tauri app init, database redesign, configurable settings, code signing, centralised logging, onboarding wizard refactor, Intel macOS installer, architecture/roadmap (from Agent 2.0 and Desktop App) -- `♻️ Auth Scope 1 β€” Enterprise Auth` β€” Make Entra permission optional, Better-Auth spike, ADR revision, implementation (from Auth Migration) -- `🌐 Auth Scope 2 β€” Extended Login Providers` β€” non-Entra tenant policy, invitation system (from Auth Migration) +- `♻️ Auth Migration` β€” Make Entra permission optional, Better-Auth spike, ADR revision, non-Entra tenant policy, invitation system (from Auth Migration) --- From 34cbac781a74b10d8d92780cd9f200fae8e0f0ec Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Fri, 20 Mar 2026 17:58:46 +0800 Subject: [PATCH 07/11] add staged mode --- .github/aw/actions-lock.json | 5 + .../workflows/epic-cleanup-yakshaver.lock.yml | 173 +++++++++++------- .github/workflows/epic-cleanup-yakshaver.md | 51 +++--- 3 files changed, 143 insertions(+), 86 deletions(-) diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index e0234428..d643f7d5 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -9,6 +9,11 @@ "repo": "github/gh-aw/actions/setup", "version": "v0.56.2", "sha": "f1073c5498ee46fec1530555a7c953445417c69b" + }, + "github/gh-aw/actions/setup@v0.58.0": { + "repo": "github/gh-aw/actions/setup", + "version": "v0.58.0", + "sha": "cb7966564184443e601bd6135d5fbb534300070e" } } } diff --git a/.github/workflows/epic-cleanup-yakshaver.lock.yml b/.github/workflows/epic-cleanup-yakshaver.lock.yml index 2985464c..3d9f27ce 100644 --- a/.github/workflows/epic-cleanup-yakshaver.lock.yml +++ b/.github/workflows/epic-cleanup-yakshaver.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.56.2). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.0). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -25,7 +25,7 @@ # PBIs (open and closed) thematically into focused smaller epics β€” all created in # SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"5ababe00126282abab56ec2b6aebee20d13c5bfbf7642b4851e86579a574c737","compiler_version":"v0.56.2","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"efc7abb947f247fb6e8623d12fac1d693e15c50c318115b382c3e911304e367e","compiler_version":"v0.58.0","strict":true} name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" "on": @@ -40,7 +40,7 @@ run-name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" jobs: activation: - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read outputs: @@ -50,7 +50,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@f1073c5498ee46fec1530555a7c953445417c69b # v0.56.2 + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -61,11 +61,11 @@ jobs: GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.56.2" + GH_AW_INFO_CLI_VERSION: "v0.58.0" GH_AW_INFO_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" - GH_AW_INFO_STAGED: "false" + GH_AW_INFO_STAGED: "true" GH_AW_INFO_ALLOWED_DOMAINS: '[]' GH_AW_INFO_FIREWALL_ENABLED: "true" GH_AW_INFO_AWF_VERSION: "v0.23.0" @@ -85,12 +85,12 @@ jobs: - name: Checkout .github and .agents folders uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: + persist-credentials: false sparse-checkout: | .github .agents sparse-checkout-cone-mode: true fetch-depth: 1 - persist-credentials: false - name: Check workflow file timestamps uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: @@ -125,7 +125,7 @@ jobs: cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" cat << 'GH_AW_PROMPT_EOF' - Tools: add_comment, create_issue, missing_tool, missing_data, noop + Tools: add_comment, create_issue, link_sub_issue, missing_tool, missing_data, noop The following GitHub context information is available for this workflow: @@ -216,7 +216,7 @@ jobs: run: bash /opt/gh-aw/actions/print_prompt_summary.sh - name: Upload activation artifact if: success() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: activation path: | @@ -252,7 +252,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@f1073c5498ee46fec1530555a7c953445417c69b # v0.56.2 + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -297,7 +297,7 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest - - name: Install awf binary + - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown @@ -311,15 +311,17 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.8 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.14 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":3,"target":"*","target-repo":"SSWConsulting/SSW.YakShaver.Desktop"},"create_issue":{"max":12},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":3,"target":"*","target-repo":"SSWConsulting/SSW.YakShaver.Desktop"},"create_issue":{"max":12},"link_sub_issue":{"max":80},"missing_data":{},"missing_tool":{},"noop":{"max":1}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF + - name: Write Safe Outputs Tools + run: | cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ { @@ -466,6 +468,42 @@ jobs: }, "name": "noop" }, + { + "description": "Link an issue as a sub-issue of a parent issue. Use this to establish parent-child relationships between issues for better organization and tracking of related work items. CONSTRAINTS: Maximum 80 sub-issue link(s) can be created.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "parent_issue_number": { + "description": "The parent issue number to link the sub-issue to. This is the numeric ID from the GitHub URL (e.g., 100 in github.com/owner/repo/issues/100).", + "type": [ + "number", + "string" + ] + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + }, + "sub_issue_number": { + "description": "The issue number to link as a sub-issue of the parent. This is the numeric ID from the GitHub URL (e.g., 101 in github.com/owner/repo/issues/101).", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "parent_issue_number", + "sub_issue_number" + ], + "type": "object" + }, + "name": "link_sub_issue" + }, { "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", "inputSchema": { @@ -556,6 +594,24 @@ jobs: } } }, + "link_sub_issue": { + "defaultMax": 5, + "fields": { + "parent_issue_number": { + "required": true, + "issueNumberOrTemporaryId": true + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "sub_issue_number": { + "required": true, + "issueNumberOrTemporaryId": true + } + }, + "customValidation": "parentAndSubDifferent" + }, "missing_data": { "defaultMax": 20, "fields": { @@ -678,7 +734,7 @@ jobs: export GH_AW_ENGINE="copilot" export GITHUB_PERSONAL_ACCESS_TOKEN="$GITHUB_MCP_SERVER_TOKEN" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_PERSONAL_ACCESS_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.8' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_PERSONAL_ACCESS_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -714,7 +770,7 @@ jobs: } GH_AW_MCP_CONFIG_EOF - name: Download activation artifact - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: activation path: /tmp/gh-aw @@ -735,9 +791,13 @@ jobs: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_STAGED: true + GH_AW_VERSION: v0.58.0 GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} GITHUB_REF_NAME: ${{ github.ref_name }} @@ -810,13 +870,11 @@ jobs: - name: Append agent step summary if: always() run: bash /opt/gh-aw/actions/append_agent_step_summary.sh - - name: Upload Safe Outputs + - name: Copy Safe Outputs if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output - path: ${{ env.GH_AW_SAFE_OUTPUTS }} - if-no-files-found: warn + run: | + mkdir -p /tmp/gh-aw + cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true - name: Ingest agent output id: collect_output if: always() @@ -832,21 +890,6 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); await main(); - - name: Upload sanitized agent output - if: always() && env.GH_AW_AGENT_OUTPUT - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-output - path: ${{ env.GH_AW_AGENT_OUTPUT }} - if-no-files-found: warn - - name: Upload engine output files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent_outputs - path: | - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - if-no-files-found: ignore - name: Parse agent logs for step summary if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -890,15 +933,19 @@ jobs: - name: Upload agent artifacts if: always() continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: agent-artifacts + name: agent path: | /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log /tmp/gh-aw/mcp-logs/ /tmp/gh-aw/sandbox/firewall/logs/ /tmp/gh-aw/agent-stdio.log /tmp/gh-aw/agent/ + /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent_output.json if-no-files-found: ignore # --- Threat Detection (inline) --- - name: Check if detection needed @@ -972,8 +1019,11 @@ jobs: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_VERSION: v0.58.0 GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} GITHUB_REF_NAME: ${{ github.ref_name }} GITHUB_SERVER_URL: ${{ github.server_url }} @@ -996,9 +1046,9 @@ jobs: await main(); - name: Upload threat detection log if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: threat-detection.log + name: detection path: /tmp/gh-aw/threat-detection/detection.log if-no-files-found: ignore - name: Set detection conclusion @@ -1028,7 +1078,7 @@ jobs: - agent - safe_outputs if: (always()) && (needs.agent.result != 'skipped') - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read discussions: write @@ -1043,22 +1093,22 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@f1073c5498ee46fec1530555a7c953445417c69b # v0.56.2 + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 with: destination: /opt/gh-aw/actions - name: Download agent output artifact id: download-agent-output continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ + name: agent + path: /tmp/gh-aw/ - name: Setup agent output environment variable if: steps.download-agent-output.outcome == 'success' run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" - name: Process No-Op Messages id: noop uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1099,6 +1149,7 @@ jobs: GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} GH_AW_GROUP_REPORTS: "false" + GH_AW_FAILURE_REPORT_AS_ISSUE: "true" GH_AW_TIMEOUT_MINUTES: "20" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} @@ -1128,7 +1179,7 @@ jobs: safe_outputs: needs: agent if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read discussions: write @@ -1138,6 +1189,7 @@ jobs: env: GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/epic-cleanup-yakshaver" GH_AW_ENGINE_ID: "copilot" + GH_AW_SAFE_OUTPUTS_STAGED: "true" GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" outputs: @@ -1153,22 +1205,22 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@f1073c5498ee46fec1530555a7c953445417c69b # v0.56.2 + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 with: destination: /opt/gh-aw/actions - name: Download agent output artifact id: download-agent-output continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ + name: agent + path: /tmp/gh-aw/ - name: Setup agent output environment variable if: steps.download-agent-output.outcome == 'success' run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" - name: Process Safe Outputs id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1177,7 +1229,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\",\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\"},\"create_issue\":{\"max\":12,\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\",\"title_prefix\":\"[YakShaver 2.0] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\",\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\"},\"create_issue\":{\"max\":12,\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\",\"title_prefix\":\"[YakShaver 2.0] \"},\"link_sub_issue\":{\"max\":80,\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | @@ -1185,11 +1237,4 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - - name: Upload safe output items manifest - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output-items - path: /tmp/safe-output-items.jsonl - if-no-files-found: warn diff --git a/.github/workflows/epic-cleanup-yakshaver.md b/.github/workflows/epic-cleanup-yakshaver.md index 49735a9a..e26f1616 100644 --- a/.github/workflows/epic-cleanup-yakshaver.md +++ b/.github/workflows/epic-cleanup-yakshaver.md @@ -57,11 +57,15 @@ post-steps: GH_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} safe-outputs: + staged: true github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} create-issue: max: 12 title-prefix: "[YakShaver 2.0] " target-repo: SSWConsulting/SSW.YakShaver.Desktop + link-sub-issue: + max: 80 + target-repo: SSWConsulting/SSW.YakShaver.Desktop add-comment: max: 3 target: "*" @@ -139,6 +143,7 @@ The tool automatically prefixes every title with `[YakShaver 2.0]` and creates t | `title` | `[Emoji] [Cluster Name]` β€” e.g. `πŸ”Œ MCP & AI Pipeline` | | `labels` | `Type: Feature` / `Type: Bug` / `Type: Refactor` as appropriate | | `body` | Use the template below | +| `temporary_id` | A unique ID like `aw_mcp`, `aw_video`, `aw_auth`, etc. β€” used in Step 4 to reference this epic before it has a real issue number | Do NOT include `issue_type` or any field not listed above. @@ -154,7 +159,6 @@ Consolidates PBIs from: ### PBIs - - SSWConsulting/[SourceRepo]#[number] β€” [title] - SSWConsulting/[SourceRepo]#[number] β€” [title] [... one line per PBI in this cluster, all states (open and closed) ...] @@ -167,11 +171,29 @@ Use plain `SSWConsulting/RepoName#number` references (not Markdown links) β€” Gi --- -## Step 4 β€” Post Summary Comments on All Three Original Epics +## Step 4 β€” Link PBIs as Sub-Issues of New Epics + +After all new epics are created in Step 3, link every PBI as a sub-issue of its corresponding new epic using the `link_sub_issue` tool. + +For each PBI in each cluster, call `link_sub_issue` once: + +| Field | Value | +|-------|-------| +| `parent_issue_number` | The `temporary_id` of the new epic created in Step 3 (e.g. `aw_mcp`) | +| `sub_issue_number` | The PBI's issue number (e.g. `123`) | + +**Notes:** +- The `parent_issue_number` field accepts `temporary_id` values β€” use the same IDs assigned in Step 3 +- Link **all** PBIs β€” both open and closed β€” so the new epic's progress bar reflects real status +- PBIs from `SSWConsulting/SSW.YakShaver` and `SSWConsulting/SSW.YakShaver.Desktop` should both be linked + +--- + +## Step 5 β€” Post Summary Comments on All Three Original Epics After all new epics are created, post a reorganisation summary on each of the three original epics. -### 4a β€” Write per-epic comment files (for SSW.YakShaver epics) +### 5a β€” Write per-epic comment files (for SSW.YakShaver epics) The `add_comment` tool can only post to `SSWConsulting/SSW.YakShaver.Desktop`. For the two epics in `SSWConsulting/SSW.YakShaver` (#2811 and #3494), use the `bash` tool to write their comment bodies to files β€” a `post-steps:` job will pick them up and post via `gh issue comment`. @@ -187,7 +209,7 @@ cat > /tmp/gh-aw/comments/comment-yakshaver-3494.md << 'EOF' EOF ``` -### 4b β€” Post comment on Desktop App epic #677 +### 5b β€” Post comment on Desktop App epic #677 Call `add_comment` with `item_number: 677` for the Desktop App epic (same repo, no cross-repo needed). @@ -222,26 +244,11 @@ the same theme have been grouped together. | SSWConsulting/[SourceRepo]#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | [... all PBIs that belong to THIS original epic, open and closed ...] -### πŸ“Œ Action Required - -1. **Link each new epic above as a sub-issue of this epic** so the GitHub progress bar updates -2. **Re-assign each PBI as a sub-issue of its new smaller epic** (remove from this epic β†’ add to the new one) +### βœ… Automated Linking -GitHub CLI commands: - -```bash -# Add a new epic as a sub-issue of this epic -gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC]/sub_issues \ - --method POST -f sub_issue_id=[NEW_EPIC_NUMBER] - -# Move a PBI: remove from this epic, add to new epic -gh api repos/SSWConsulting/[SOURCE_REPO]/issues/[THIS_EPIC]/sub_issues/[PBI_NUMBER] \ - --method DELETE -gh api repos/SSWConsulting/SSW.YakShaver.Desktop/issues/[NEW_EPIC_NUMBER]/sub_issues \ - --method POST -f sub_issue_id=[PBI_NUMBER] -``` +All PBIs listed above have been automatically linked as sub-issues of their new smaller epics. -> ℹ️ PBIs stay in their original repos. Only the tracking epics move to SSW.YakShaver.Desktop. +> ℹ️ PBIs stay in their original repos. Only the tracking epics are in SSW.YakShaver.Desktop. ``` --- From 4bbcf0674d450956b9be06e065fbb38aa93c10b7 Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Thu, 23 Apr 2026 14:17:40 +0800 Subject: [PATCH 08/11] update workflow to dry run --- .github/aw/actions-lock.json | 5 + .../workflows/epic-cleanup-yakshaver.lock.yml | 536 ++++++------------ .github/workflows/epic-cleanup-yakshaver.md | 306 +++++----- 3 files changed, 317 insertions(+), 530 deletions(-) diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index d643f7d5..5bfb38ee 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -5,6 +5,11 @@ "version": "v8", "sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd" }, + "github/gh-aw-actions/setup@v0.62.3": { + "repo": "github/gh-aw-actions/setup", + "version": "v0.62.3", + "sha": "4880241327b52a412519b3d987076538cdf8d745" + }, "github/gh-aw/actions/setup@v0.56.2": { "repo": "github/gh-aw/actions/setup", "version": "v0.56.2", diff --git a/.github/workflows/epic-cleanup-yakshaver.lock.yml b/.github/workflows/epic-cleanup-yakshaver.lock.yml index 3d9f27ce..9bce5d40 100644 --- a/.github/workflows/epic-cleanup-yakshaver.lock.yml +++ b/.github/workflows/epic-cleanup-yakshaver.lock.yml @@ -1,4 +1,3 @@ -# # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -13,7 +12,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.0). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.62.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -21,22 +20,35 @@ # # For more information: https://github.github.com/gh-aw/introduction/overview/ # -# Analyses the three large YakShaver epics in the Product Backlog, clusters ALL their -# PBIs (open and closed) thematically into focused smaller epics β€” all created in -# SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking. +# Scans the YakShaver repos (Teams, Desktop, 360) for open epics whose sub-issue +# progress is below 100%, analyses their PBIs across states, and produces a single +# "Dry-Run Plan" report issue in SSWConsulting/SSW.YakShaver.Desktop. The report +# proposes smaller theme epics (tagged by product), flags open issues that look +# like duplicates of other work (open or closed) across the whole backlog, and +# suggests source epics that could themselves be closed or archived. No epics are +# created, linked, or modified β€” execution is deferred until a human confirms the plan. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"efc7abb947f247fb6e8623d12fac1d693e15c50c318115b382c3e911304e367e","compiler_version":"v0.58.0","strict":true} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"ca0161b5f619ba3f61af5ee208441c8bcef39e1924738a049885dbd8b954e7c6","compiler_version":"v0.62.3","strict":true,"agent_id":"copilot"} -name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" +name: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" "on": workflow_dispatch: + inputs: + source_repos: + default: SSWConsulting/SSW.YakShaver,SSWConsulting/SSW.YakShaver.Desktop,SSWConsulting/SSW.YakShaver360 + description: Comma-separated list of owner/repo to scan for open epics + required: false + target_repo: + default: SSWConsulting/SSW.YakShaver.Desktop + description: Repo where the Dry-Run Plan report issue is created + required: false permissions: {} concurrency: group: "gh-aw-${{ github.workflow }}" -run-name: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" +run-name: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" jobs: activation: @@ -46,13 +58,14 @@ jobs: outputs: comment_id: "" comment_repo: "" + lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} model: ${{ steps.generate_aw_info.outputs.model }} secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + uses: github/gh-aw-actions/setup@4880241327b52a412519b3d987076538cdf8d745 # v0.62.3 with: - destination: /opt/gh-aw/actions + destination: ${{ runner.temp }}/gh-aw/actions - name: Generate agentic run info id: generate_aw_info env: @@ -61,25 +74,27 @@ jobs: GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.0" - GH_AW_INFO_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" + GH_AW_INFO_CLI_VERSION: "v0.62.3" + GH_AW_INFO_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "true" GH_AW_INFO_ALLOWED_DOMAINS: '[]' GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.23.0" + GH_AW_INFO_AWF_VERSION: "v0.24.5" GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "squid" GH_AW_COMPILED_STRICT: "true" uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: | - const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); await main(core, context); - name: Validate COPILOT_GITHUB_TOKEN secret id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + run: ${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - name: Checkout .github and .agents folders @@ -97,9 +112,9 @@ jobs: GH_AW_WORKFLOW_FILE: "epic-cleanup-yakshaver.lock.yml" with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); await main(); - name: Create prompt with built-in context env: @@ -113,19 +128,20 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_SOURCE_REPOS: ${{ inputs.source_repos }} run: | - bash /opt/gh-aw/actions/create_prompt_first.sh + bash ${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh { cat << 'GH_AW_PROMPT_EOF' GH_AW_PROMPT_EOF - cat "/opt/gh-aw/prompts/xpia.md" - cat "/opt/gh-aw/prompts/temp_folder_prompt.md" - cat "/opt/gh-aw/prompts/markdown.md" - cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" + cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" cat << 'GH_AW_PROMPT_EOF' - Tools: add_comment, create_issue, link_sub_issue, missing_tool, missing_data, noop + Tools: create_issue, missing_tool, missing_data, noop The following GitHub context information is available for this workflow: @@ -156,6 +172,7 @@ jobs: GH_AW_PROMPT_EOF + cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" cat << 'GH_AW_PROMPT_EOF' GH_AW_PROMPT_EOF @@ -167,11 +184,12 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_INPUTS_SOURCE_REPOS: ${{ inputs.source_repos }} with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); await main(); - name: Substitute placeholders uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -185,12 +203,13 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_SOURCE_REPOS: ${{ inputs.source_repos }} with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); // Call the substitution function return await substitutePlaceholders({ @@ -203,17 +222,18 @@ jobs: GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_INPUTS_SOURCE_REPOS: process.env.GH_AW_INPUTS_SOURCE_REPOS } }); - name: Validate prompt placeholders env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + run: bash ${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh - name: Print prompt env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/print_prompt_summary.sh + run: bash ${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh - name: Upload activation artifact if: success() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 @@ -237,9 +257,6 @@ jobs: GH_AW_ASSETS_BRANCH: "" GH_AW_ASSETS_MAX_SIZE_KB: 0 GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json GH_AW_WORKFLOW_ID_SANITIZED: epiccleanupyakshaver outputs: checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} @@ -252,22 +269,28 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + uses: github/gh-aw-actions/setup@4880241327b52a412519b3d987076538cdf8d745 # v0.62.3 with: - destination: /opt/gh-aw/actions + destination: ${{ runner.temp }}/gh-aw/actions + - name: Set runtime paths + run: | + echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV" + echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" >> "$GITHUB_ENV" + echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" >> "$GITHUB_ENV" - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Create gh-aw temp directory - run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + run: bash ${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure gh CLI for GitHub Enterprise + run: bash ${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh + env: + GH_TOKEN: ${{ github.token }} - env: GH_AW_PAT: ${{ secrets.GH_AW_CROSS_REPO_PAT }} - GH_TOKEN: ${{ github.token }} name: Validate GH_AW_CROSS_REPO_PAT - run: "if [ -z \"$GH_AW_PAT\" ]; then\n echo \"::error::GH_AW_CROSS_REPO_PAT secret is not configured.\"\n BODY=\"## ⚠️ Epic Cleanup workflow failed to start\\n\\nThe \\`GH_AW_CROSS_REPO_PAT\\` secret is missing. This workflow needs a PAT to read issues from \\`SSW.YakShaver\\` and create epics in \\`SSW.YakShaver.Desktop\\`.\\n\\nGo to **Settings β†’ Secrets β†’ Actions β†’ New repository secret**, add \\`GH_AW_CROSS_REPO_PAT\\` with a fine-grained token that has Issues Read+Write on both \\`SSWConsulting/SSW.YakShaver\\` and \\`SSWConsulting/SSW.YakShaver.Desktop\\`, then re-run this workflow.\"\n printf '%b' \"$BODY\" | gh issue comment 677 --repo SSWConsulting/SSW.YakShaver.Desktop --body-file -\n exit 1\nfi\n" - - name: Prepare comment output directory - run: mkdir -p /tmp/gh-aw/comments + run: "if [ -z \"$GH_AW_PAT\" ]; then\n echo \"::error::GH_AW_CROSS_REPO_PAT secret is not configured.\"\n echo \"The workflow needs a fine-grained PAT with Issues: Read on all source repos and Issues: Read+Write on the target repo.\"\n exit 1\nfi\n" - name: Configure Git credentials env: @@ -291,276 +314,49 @@ jobs: with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh latest + run: ${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary - run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 + run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.24.5 - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - CUSTOM_GITHUB_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} with: script: | - const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.14 node:lts-alpine + run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.5 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.5 ghcr.io/github/gh-aw-firewall/squid:0.24.5 ghcr.io/github/gh-aw-mcpg:v0.1.19 node:lts-alpine - name: Write Safe Outputs Config run: | - mkdir -p /opt/gh-aw/safeoutputs + mkdir -p ${RUNNER_TEMP}/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":3,"target":"*","target-repo":"SSWConsulting/SSW.YakShaver.Desktop"},"create_issue":{"max":12},"link_sub_issue":{"max":80},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"create_issue":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF - name: Write Safe Outputs Tools run: | - cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' - [ - { - "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 12 issue(s) can be created. Title will be prefixed with \"[YakShaver 2.0] \". Issues will be created in repository \"SSWConsulting/SSW.YakShaver.Desktop\".", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.", - "type": "string" - }, - "integrity": { - "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", - "type": "string" - }, - "labels": { - "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.", - "items": { - "type": "string" - }, - "type": "array" - }, - "parent": { - "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.", - "type": [ - "number", - "string" - ] - }, - "secrecy": { - "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", - "type": "string" - }, - "temporary_id": { - "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.", - "pattern": "^aw_[A-Za-z0-9]{3,12}$", - "type": "string" - }, - "title": { - "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.", - "type": "string" - } - }, - "required": [ - "title", - "body" - ], - "type": "object" - }, - "name": "create_issue" - }, - { - "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 3 comment(s) can be added. Target: *. Comments will be added in repository \"SSWConsulting/SSW.YakShaver.Desktop\".", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", - "type": "string" - }, - "integrity": { - "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", - "type": "string" - }, - "item_number": { - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers β€” it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.", - "type": [ - "number", - "string" - ] - }, - "secrecy": { - "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", - "type": "string" - }, - "temporary_id": { - "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.", - "pattern": "^aw_[A-Za-z0-9]{3,12}$", - "type": "string" - } - }, - "required": [ - "body" - ], - "type": "object" - }, - "name": "add_comment" - }, - { - "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "integrity": { - "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", - "type": "string" - }, - "reason": { - "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", - "type": "string" - }, - "secrecy": { - "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", - "type": "string" - }, - "tool": { - "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", - "type": "string" - } - }, - "required": [ - "reason" - ], - "type": "object" - }, - "name": "missing_tool" - }, - { - "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "integrity": { - "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", - "type": "string" - }, - "message": { - "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", - "type": "string" - }, - "secrecy": { - "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "name": "noop" - }, - { - "description": "Link an issue as a sub-issue of a parent issue. Use this to establish parent-child relationships between issues for better organization and tracking of related work items. CONSTRAINTS: Maximum 80 sub-issue link(s) can be created.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "integrity": { - "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", - "type": "string" - }, - "parent_issue_number": { - "description": "The parent issue number to link the sub-issue to. This is the numeric ID from the GitHub URL (e.g., 100 in github.com/owner/repo/issues/100).", - "type": [ - "number", - "string" - ] - }, - "secrecy": { - "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", - "type": "string" - }, - "sub_issue_number": { - "description": "The issue number to link as a sub-issue of the parent. This is the numeric ID from the GitHub URL (e.g., 101 in github.com/owner/repo/issues/101).", - "type": [ - "number", - "string" - ] - } - }, - "required": [ - "parent_issue_number", - "sub_issue_number" - ], - "type": "object" - }, - "name": "link_sub_issue" - }, - { - "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "context": { - "description": "Additional context about the missing data or where it should come from (max 256 characters).", - "type": "string" - }, - "data_type": { - "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", - "type": "string" - }, - "integrity": { - "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", - "type": "string" - }, - "reason": { - "description": "Explanation of why this data is needed to complete the task (max 256 characters).", - "type": "string" - }, - "secrecy": { - "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", - "type": "string" - } - }, - "required": [], - "type": "object" - }, - "name": "missing_data" - } - ] - GH_AW_SAFE_OUTPUTS_TOOLS_EOF - cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF' { - "add_comment": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "item_number": { - "issueOrPRNumber": true - }, - "repo": { - "type": "string", - "maxLength": 256 - } - } + "description_suffixes": { + "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Issues will be created in repository \"${{ inputs.target_repo }}\"." }, + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { "create_issue": { "defaultMax": 1, "fields": { @@ -594,24 +390,6 @@ jobs: } } }, - "link_sub_issue": { - "defaultMax": 5, - "fields": { - "parent_issue_number": { - "required": true, - "issueNumberOrTemporaryId": true - }, - "repo": { - "type": "string", - "maxLength": 256 - }, - "sub_issue_number": { - "required": true, - "issueNumberOrTemporaryId": true - } - }, - "customValidation": "parentAndSubDifferent" - }, "missing_data": { "defaultMax": 20, "fields": { @@ -671,6 +449,7 @@ jobs: } } GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + node ${RUNNER_TEMP}/gh-aw/actions/generate_safe_outputs_tools.cjs - name: Generate Safe Outputs MCP Server Config id: safe-outputs-config run: | @@ -695,8 +474,8 @@ jobs: DEBUG: '*' GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs run: | # Environment variables are set above to prevent template injection @@ -707,7 +486,7 @@ jobs: export GH_AW_SAFE_OUTPUTS_CONFIG_PATH export GH_AW_MCP_LOG_DIR - bash /opt/gh-aw/actions/start_safe_outputs_server.sh + bash ${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh - name: Start MCP Gateway id: start-mcp-gateway @@ -715,7 +494,8 @@ jobs: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} - GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }} + GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} run: | set -eo pipefail @@ -734,10 +514,10 @@ jobs: export GH_AW_ENGINE="copilot" export GITHUB_PERSONAL_ACCESS_TOKEN="$GITHUB_MCP_SERVER_TOKEN" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_PERSONAL_ACCESS_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_PERSONAL_ACCESS_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.19' mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + cat << GH_AW_MCP_CONFIG_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { "github": { @@ -745,12 +525,18 @@ jobs: "url": "https://api.githubcopilot.com/mcp/", "headers": { "Authorization": "Bearer \${GITHUB_PERSONAL_ACCESS_TOKEN}", - "X-MCP-Lockdown": "$([ "$GITHUB_MCP_LOCKDOWN" = "1" ] && echo true || echo false)", "X-MCP-Readonly": "true", "X-MCP-Toolsets": "context,repos,issues,pull_requests" }, "env": { - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}" + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_HOST": "\${GITHUB_SERVER_URL}" + }, + "guard-policies": { + "allow-only": { + "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY", + "repos": "$GITHUB_MCP_GUARD_REPOS" + } } }, "safeoutputs": { @@ -758,6 +544,13 @@ jobs: "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", "headers": { "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } } } }, @@ -775,7 +568,8 @@ jobs: name: activation path: /tmp/gh-aw - name: Clean git credentials - run: bash /opt/gh-aw/actions/clean_git_credentials.sh + continue-on-error: true + run: bash ${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): @@ -784,7 +578,7 @@ jobs: set -o pipefail touch /tmp/gh-aw/agent-step-summary.md # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.5 --skip-pull --enable-api-proxy \ -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE @@ -795,7 +589,7 @@ jobs: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_STAGED: true - GH_AW_VERSION: v0.58.0 + GH_AW_VERSION: v0.62.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -813,7 +607,7 @@ jobs: id: detect-inference-error if: always() continue-on-error: true - run: bash /opt/gh-aw/actions/detect_inference_access_error.sh + run: bash ${RUNNER_TEMP}/gh-aw/actions/detect_inference_access_error.sh - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -851,15 +645,15 @@ jobs: MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} run: | - bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + bash ${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" - name: Redact secrets in logs if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); await main(); env: GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_CROSS_REPO_PAT,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN' @@ -869,7 +663,7 @@ jobs: SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - name: Append agent step summary if: always() - run: bash /opt/gh-aw/actions/append_agent_step_summary.sh + run: bash ${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh - name: Copy Safe Outputs if: always() run: | @@ -886,9 +680,9 @@ jobs: GITHUB_API_URL: ${{ github.api_url }} with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); await main(); - name: Parse agent logs for step summary if: always() @@ -897,18 +691,18 @@ jobs: GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); await main(); - name: Parse MCP Gateway logs for step summary if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); await main(); - name: Print firewall logs if: always() @@ -925,11 +719,6 @@ jobs: else echo 'AWF binary not installed, skipping firewall log summary' fi - - env: - GH_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} - name: Post summary to SSW.YakShaver epics - run: "for ISSUE in 2811 3494; do\n FILE=\"/tmp/gh-aw/comments/comment-yakshaver-${ISSUE}.md\"\n if [ -f \"$FILE\" ]; then\n echo \"Posting comment to SSWConsulting/SSW.YakShaver#${ISSUE}...\"\n gh issue comment \"$ISSUE\" \\\n --repo SSWConsulting/SSW.YakShaver \\\n --body-file \"$FILE\"\n else\n echo \"No comment file found for SSW.YakShaver#${ISSUE} β€” skipping.\"\n fi\ndone\n" - - name: Upload agent artifacts if: always() continue-on-error: true @@ -983,14 +772,14 @@ jobs: if: always() && steps.detection_guard.outputs.run_detection == 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: - WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" - WORKFLOW_DESCRIPTION: "Analyses the three large YakShaver epics in the Product Backlog, clusters ALL their\nPBIs (open and closed) thematically into focused smaller epics β€” all created in\nSSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking." + WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" + WORKFLOW_DESCRIPTION: "Scans the YakShaver repos (Teams, Desktop, 360) for open epics whose sub-issue\nprogress is below 100%, analyses their PBIs across states, and produces a single\n\"Dry-Run Plan\" report issue in SSWConsulting/SSW.YakShaver.Desktop. The report\nproposes smaller theme epics (tagged by product), flags open issues that look\nlike duplicates of other work (open or closed) across the whole backlog, and\nsuggests source epics that could themselves be closed or archived. No epics are\ncreated, linked, or modified β€” execution is deferred until a human confirms the plan." HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs'); await main(); - name: Ensure threat-detection directory and log if: always() && steps.detection_guard.outputs.run_detection == 'true' @@ -1013,7 +802,7 @@ jobs: set -o pipefail touch /tmp/gh-aw/agent-step-summary.md # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.5 --skip-pull --enable-api-proxy \ -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE @@ -1021,7 +810,7 @@ jobs: COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.0 + GH_AW_VERSION: v0.62.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1040,9 +829,9 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); await main(); - name: Upload threat detection log if: always() && steps.detection_guard.outputs.run_detection == 'true' @@ -1077,13 +866,9 @@ jobs: - activation - agent - safe_outputs - if: (always()) && (needs.agent.result != 'skipped') + if: (always()) && ((needs.agent.result != 'skipped') || (needs.activation.outputs.lockdown_check_failed == 'true')) runs-on: ubuntu-slim - permissions: - contents: read - discussions: write - issues: write - pull-requests: write + permissions: {} concurrency: group: "gh-aw-conclusion-epic-cleanup-yakshaver" cancel-in-progress: false @@ -1093,9 +878,9 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + uses: github/gh-aw-actions/setup@4880241327b52a412519b3d987076538cdf8d745 # v0.62.3 with: - destination: /opt/gh-aw/actions + destination: ${{ runner.temp }}/gh-aw/actions - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1115,55 +900,57 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_NOOP_MAX: "1" - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/noop.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/noop.cjs'); await main(); - name: Record Missing Tool id: missing_tool uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); await main(); - name: Handle Agent Failure id: handle_agent_failure + if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} GH_AW_GROUP_REPORTS: "false" GH_AW_FAILURE_REPORT_AS_ISSUE: "true" GH_AW_TIMEOUT_MINUTES: "20" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); - name: Handle No-Op Message id: handle_noop_message uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} @@ -1171,32 +958,26 @@ jobs: with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); await main(); safe_outputs: needs: agent if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') runs-on: ubuntu-slim - permissions: - contents: read - discussions: write - issues: write - pull-requests: write + permissions: {} timeout-minutes: 15 env: GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/epic-cleanup-yakshaver" GH_AW_ENGINE_ID: "copilot" GH_AW_SAFE_OUTPUTS_STAGED: "true" GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Cluster PBIs into Smaller Epics" + GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" outputs: code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} - comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} - comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }} @@ -1205,9 +986,9 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + uses: github/gh-aw-actions/setup@4880241327b52a412519b3d987076538cdf8d745 # v0.62.3 with: - destination: /opt/gh-aw/actions + destination: ${{ runner.temp }}/gh-aw/actions - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1221,6 +1002,14 @@ jobs: mkdir -p /tmp/gh-aw/ find "/tmp/gh-aw/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" + - name: Configure GH_HOST for enterprise compatibility + shell: bash + run: | + # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct + # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. + GH_HOST="${GITHUB_SERVER_URL#https://}" + GH_HOST="${GH_HOST#http://}" + echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" - name: Process Safe Outputs id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1229,12 +1018,13 @@ jobs: GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\",\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\"},\"create_issue\":{\"max\":12,\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\",\"title_prefix\":\"[YakShaver 2.0] \"},\"link_sub_issue\":{\"max\":80,\"target-repo\":\"SSWConsulting/SSW.YakShaver.Desktop\"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1,\"target-repo\":\"${{ inputs.target_repo }}\"},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"}}" + GH_AW_SAFE_OUTPUTS_STAGED: "true" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/epic-cleanup-yakshaver.md b/.github/workflows/epic-cleanup-yakshaver.md index e26f1616..ecb9708b 100644 --- a/.github/workflows/epic-cleanup-yakshaver.md +++ b/.github/workflows/epic-cleanup-yakshaver.md @@ -1,12 +1,25 @@ --- -name: Epic Cleanup β€” Cluster PBIs into Smaller Epics +name: Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog description: | - Analyses the three large YakShaver epics in the Product Backlog, clusters ALL their - PBIs (open and closed) thematically into focused smaller epics β€” all created in - SSWConsulting/SSW.YakShaver.Desktop β€” for better sprint planning and progress tracking. + Scans the YakShaver repos (Teams, Desktop, 360) for open epics whose sub-issue + progress is below 100%, analyses their PBIs across states, and produces a single + "Dry-Run Plan" report issue in SSWConsulting/SSW.YakShaver.Desktop. The report + proposes smaller theme epics (tagged by product), flags open issues that look + like duplicates of other work (open or closed) across the whole backlog, and + suggests source epics that could themselves be closed or archived. No epics are + created, linked, or modified β€” execution is deferred until a human confirms the plan. on: workflow_dispatch: + inputs: + source_repos: + description: "Comma-separated list of owner/repo to scan for open epics" + required: false + default: "SSWConsulting/SSW.YakShaver,SSWConsulting/SSW.YakShaver.Desktop,SSWConsulting/SSW.YakShaver360" + target_repo: + description: "Repo where the Dry-Run Plan report issue is created" + required: false + default: "SSWConsulting/SSW.YakShaver.Desktop" runs-on: ubuntu-latest @@ -29,237 +42,216 @@ steps: run: | if [ -z "$GH_AW_PAT" ]; then echo "::error::GH_AW_CROSS_REPO_PAT secret is not configured." - BODY="## ⚠️ Epic Cleanup workflow failed to start\n\nThe \`GH_AW_CROSS_REPO_PAT\` secret is missing. This workflow needs a PAT to read issues from \`SSW.YakShaver\` and create epics in \`SSW.YakShaver.Desktop\`.\n\nGo to **Settings β†’ Secrets β†’ Actions β†’ New repository secret**, add \`GH_AW_CROSS_REPO_PAT\` with a fine-grained token that has Issues Read+Write on both \`SSWConsulting/SSW.YakShaver\` and \`SSWConsulting/SSW.YakShaver.Desktop\`, then re-run this workflow." - printf '%b' "$BODY" | gh issue comment 677 --repo SSWConsulting/SSW.YakShaver.Desktop --body-file - + echo "The workflow needs a fine-grained PAT with Issues: Read on all source repos and Issues: Read+Write on the target repo." exit 1 fi env: GH_AW_PAT: ${{ secrets.GH_AW_CROSS_REPO_PAT }} - GH_TOKEN: ${{ github.token }} - - name: Prepare comment output directory - run: mkdir -p /tmp/gh-aw/comments - -post-steps: - - name: Post summary to SSW.YakShaver epics - run: | - for ISSUE in 2811 3494; do - FILE="/tmp/gh-aw/comments/comment-yakshaver-${ISSUE}.md" - if [ -f "$FILE" ]; then - echo "Posting comment to SSWConsulting/SSW.YakShaver#${ISSUE}..." - gh issue comment "$ISSUE" \ - --repo SSWConsulting/SSW.YakShaver \ - --body-file "$FILE" - else - echo "No comment file found for SSW.YakShaver#${ISSUE} β€” skipping." - fi - done - env: - GH_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }} safe-outputs: staged: true github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} create-issue: - max: 12 - title-prefix: "[YakShaver 2.0] " - target-repo: SSWConsulting/SSW.YakShaver.Desktop - link-sub-issue: - max: 80 - target-repo: SSWConsulting/SSW.YakShaver.Desktop - add-comment: - max: 3 - target: "*" - target-repo: SSWConsulting/SSW.YakShaver.Desktop + max: 1 + title-prefix: "" + target-repo: ${{ inputs.target_repo }} --- -# Epic Cleanup β€” Cluster PBIs into Smaller Epics +# Epic Cleanup β€” Dry-Run Analysis + +You are an expert product backlog manager. Your job is to **analyse** β€” not execute β€” a cleanup of the YakShaver product backlog across multiple repos and produce a single report issue that a human can review, tick off, and then separately execute. + +## Inputs + +- `source_repos` β€” comma-separated list of `owner/repo` to scan. Default: `SSWConsulting/SSW.YakShaver`, `SSWConsulting/SSW.YakShaver.Desktop`, `SSWConsulting/SSW.YakShaver360`. +- `target_repo` β€” where the single Dry-Run Plan report issue is created. Default: `SSWConsulting/SSW.YakShaver.Desktop`. -You are an expert product backlog manager. Your task is to reorganise three large, unwieldy epics in the YakShaver Product Backlog. For each epic, you will read **all** of its PBIs (open and closed), cluster them into a small number of focused themes, and create one new, smaller epic per theme. +Parse `${{ inputs.source_repos }}` on commas and trim whitespace. -**All new epic issues must be created in `SSWConsulting/SSW.YakShaver.Desktop`**, regardless of which repo the original epic lives in. This centralises the Desktop product's backlog. +## Product mapping -## Epics to Reorganise +Each source repo maps to a **product tag** used when labelling proposed new epics in the report: -| Epic | Source Repo | Issue | -|------|-------------|-------| -| ✨ YakShaver Agent 2.0 | SSWConsulting/SSW.YakShaver | #2811 | -| ✨ YakShaver Desktop App | SSWConsulting/SSW.YakShaver.Desktop | #677 | -| ♻️ Auth Migration | SSWConsulting/SSW.YakShaver | #3494 | +| Repo | Product tag | +|------|-------------| +| `SSWConsulting/SSW.YakShaver` | `teams` (also hosts shared backend / portal β€” tag as `shared` when a PBI is clearly backend/portal work rather than Teams UX) | +| `SSWConsulting/SSW.YakShaver.Desktop` | `desktop` | +| `SSWConsulting/SSW.YakShaver360` | `360` | + +When a cluster spans multiple products, pick the dominant one as the primary tag and list the others in the epic body. --- -## Step 1 β€” Read Every PBI in Each Epic +## Step 1 β€” Discover in-scope epics -For each of the three epics: +For each repo in `source_repos`: -1. Read the epic issue body and labels -2. Fetch **all sub-issues** β€” including both open and closed ones -3. For every sub-issue, record: - - Issue number and title - - State: `open` or `closed` - - Labels (e.g., Type: Bug, Type: Feature, Type: Refactor) - - A one-line summary of what it does (from title or first sentence of body) +1. Query issues where **issue type = "Epic"** (GitHub's native issue types, not a label) AND **state = OPEN**. +2. For each epic, read its **sub-issue progress** (`completed` / `total`). +3. **Skip epics where `completed == total`** β€” those are already done. Record them in the "Skipped epics" section of the report. +4. For the remaining qualifying epics, read: + - Epic title, number, repo, labels, and body (first few paragraphs is enough) + - **All sub-issues (both open and closed)** β€” for each, read title, number, state, labels, last-updated date, **and the full issue body**. The full body is required so you have enough context to cluster accurately in Step 2 and to judge duplicate similarity in Step 3. -Do **not** skip closed PBIs β€” they are needed to show full progress context in the new epics. +**Closed sub-issues are in scope for clustering.** Treat them the same as open ones in Step 2 β€” a closed PBI still belongs in whichever new smaller epic matches its theme, so the future epic's progress bar reflects historical work. Do not drop closed sub-issues just because they are done. --- -## Step 2 β€” Cluster All PBIs Holistically Across the Three Epics +## Step 2 β€” Cluster PBIs into proposed smaller epics -After reading all PBIs from all three epics, treat them as **one unified pool** and cluster them by theme. PBIs from different source epics may end up in the same new smaller epic if they are closely related. +Treat the PBIs from all in-scope epics as one unified pool and cluster them by theme. **Clustering rules:** +- Each cluster should represent a coherent functional area (e.g. `πŸ”Œ MCP & AI Pipeline`, `🎬 Video Recording`, `♻️ Auth Migration`, `πŸ› Desktop Bug Fixes`, `πŸ—οΈ Platform & Infrastructure`, `🎨 UX Improvements`). +- **Cross-repo / cross-product clustering is encouraged** β€” a Teams PBI and a Desktop PBI on the same topic go in the same cluster. +- Target **10–20 PBIs per cluster** when the pool allows; let the actual themes drive the count. +- The total number of proposed new epics is **flexible** β€” do not force a fixed count. +- A PBI spanning two themes goes in whichever is dominant. +- All-closed clusters are acceptable β€” they show historical progress the future epic will inherit. + +Assign each cluster a product tag based on the dominant source: +- `teams` β€” mostly PBIs from `SSW.YakShaver` that are clearly Teams UX +- `desktop` β€” mostly PBIs from `SSW.YakShaver.Desktop` +- `360` β€” mostly PBIs from `SSW.YakShaver360` +- `shared` β€” backend / portal / infrastructure work that serves multiple products -- Each cluster should represent a coherent functional area or delivery theme -- **Cross-epic clustering is encouraged** β€” Desktop App PBIs and Agent 2.0 PBIs that share the same theme (e.g., MCP integration, video pipeline) should be grouped together -- Target **10–20 PBIs per cluster** β€” coarse-grained, not fine-grained -- Aim for **5–8 clusters total** across all three epics combined β€” let the actual PBI themes drive the count, not the source epic structure -- It is fine for a cluster to consist entirely of closed PBIs β€” group by theme, not by state -- Auth Migration PBIs are self-contained (auth/identity scope) and should remain in their own cluster(s) -- A PBI that spans two themes goes in whichever is more dominant +--- -**Suggested starting clusters β€” validate against actual PBI content and adjust:** +## Step 3 β€” Detect duplicate candidates (whole backlog) -- `πŸ”Œ MCP & AI Pipeline` β€” MCP host/client, MCP service config, fallback/error handling, OpenAI integration, transcript pipeline (from Agent 2.0 and Desktop App) -- `🎬 Video Recording & Processing` β€” screen capture, local video processing, FFmpeg, upload pipeline, recording confirmation, video hosting (from Agent 2.0 and Desktop App) -- `✨ Features` β€” stop button, project selection, transcript optimisation, fallback/system prompt, additional LLM providers, customisable prompt (from Desktop App) -- `🎨 UX Improvements` β€” UI polish, onboarding experience, settings panels, visual consistency, accessibility improvements (from Desktop App) -- `πŸ› Bug Fixes & Quality` β€” all defects across Desktop App: camera/audio, auth errors, sign-in failures, UI glitches, download confusion, Azure DevOps MCP bugs -- `πŸ—οΈ Platform & Infrastructure` β€” Tauri app init, database redesign, configurable settings, code signing, centralised logging, onboarding wizard refactor, Intel macOS installer, architecture/roadmap (from Agent 2.0 and Desktop App) -- `♻️ Auth Migration` β€” Make Entra permission optional, Better-Auth spike, ADR revision, non-Entra tenant policy, invitation system (from Auth Migration) +Duplicate detection is **not** limited to PBIs under the in-scope epics. Scan the full backlog of each repo in `source_repos`. ---- +**Build two pools:** +- **Left pool (candidates to flag):** every **open** issue in every `source_repos` repo β€” including issues that have no parent epic at all. +- **Right pool (match targets):** every issue (open **or** closed) in every `source_repos` repo. -## Step 3 β€” Create One New Epic per Cluster +For each open issue in the left pool, check whether it appears to duplicate an issue in the right pool: -For each cluster identified in Step 2, call the `create_issue` tool once. +- Compare titles and first paragraphs of bodies for semantic similarity. +- Look for explicit wording like "duplicate of", "supersedes", "same as" in either issue's body or comments. +- Flag only where the signal is strong β€” precision matters more than recall here, because each flag costs a human triage step. -The tool automatically prefixes every title with `[YakShaver 2.0]` and creates the issue in `SSWConsulting/SSW.YakShaver.Desktop`. Do **not** include `issue_type`, `repo`, or `target-repo` β€” those fields are not supported and will cause validation errors. +**Do not flag** closed-vs-closed duplicates β€” they are historical noise and don't need action. -**Fields to provide:** +**Do flag:** +- Open ↔ closed β€” the open one may already be done, close it. +- Open ↔ open β€” the two open issues overlap, merge or close one. -| Field | Value | -|-------|-------| -| `title` | `[Emoji] [Cluster Name]` β€” e.g. `πŸ”Œ MCP & AI Pipeline` | -| `labels` | `Type: Feature` / `Type: Bug` / `Type: Refactor` as appropriate | -| `body` | Use the template below | -| `temporary_id` | A unique ID like `aw_mcp`, `aw_video`, `aw_auth`, etc. β€” used in Step 4 to reference this epic before it has a real issue number | +For each flagged pair, record: +- The open issue reference and title (always on the left) +- The matched issue reference, its state (open / closed-on-date), and title +- A one-line reason (e.g. "title near-match", "explicit 'duplicate of' in #X's comment", "body describes the same acceptance criteria") -Do NOT include `issue_type` or any field not listed above. +--- -**Body template for each new epic:** +## Step 4 β€” Suggest source epics to close or archive -```markdown -Consolidates PBIs from: -- [source epic 1 title] β€” SSWConsulting/[SourceRepo]#[number] -- [source epic 2 title] β€” SSWConsulting/[SourceRepo]#[number] (omit if single source) +Review each in-scope source epic (the ones with progress < 100%) against your clustering. Suggest it for close/archive when: -### Scope -[2–3 sentences describing what this epic covers and the value it delivers] +- All of its remaining open PBIs are duplicates flagged in Step 3. +- All of its remaining open PBIs have been absorbed into proposed new smaller epics and the source epic adds no extra scope of its own. +- Its body describes work that has been superseded by a decision or another initiative. -### PBIs +Also list the 100%-done epics from Step 1 here β€” they are straightforward close candidates. -- SSWConsulting/[SourceRepo]#[number] β€” [title] -- SSWConsulting/[SourceRepo]#[number] β€” [title] -[... one line per PBI in this cluster, all states (open and closed) ...] +--- -### Acceptance Criteria -This epic is complete when all open PBIs listed above are closed. -``` +## Step 5 β€” Produce the Dry-Run Plan report issue -Use plain `SSWConsulting/RepoName#number` references (not Markdown links) β€” GitHub auto-links these without counting toward the 50-link comment limit. +Call `create_issue` **exactly once** with: ---- +- `title`: `🧹 Dry-Run Plan β€” YakShaver Backlog Cleanup (YYYY-MM-DD)` (substitute today's date) +- `labels`: `Type: Refactor`, `cleanup` +- `body`: use the template below +- `temporary_id`: `aw_cleanup_report` -## Step 4 β€” Link PBIs as Sub-Issues of New Epics +Do not include `issue_type`, `repo`, or `target-repo` β€” the safe-output config handles the target repo. -After all new epics are created in Step 3, link every PBI as a sub-issue of its corresponding new epic using the `link_sub_issue` tool. +### Body template -For each PBI in each cluster, call `link_sub_issue` once: +```markdown +# 🧹 Dry-Run Plan β€” YakShaver Backlog Cleanup -| Field | Value | -|-------|-------| -| `parent_issue_number` | The `temporary_id` of the new epic created in Step 3 (e.g. `aw_mcp`) | -| `sub_issue_number` | The PBI's issue number (e.g. `123`) | +**Run date:** YYYY-MM-DD +**Scanned repos:** SSWConsulting/SSW.YakShaver, SSWConsulting/SSW.YakShaver.Desktop, SSWConsulting/SSW.YakShaver360 +**In-scope epics:** N (open, sub-issue progress < 100%) +**Total PBIs reviewed:** M (X open, Y closed) -**Notes:** -- The `parent_issue_number` field accepts `temporary_id` values β€” use the same IDs assigned in Step 3 -- Link **all** PBIs β€” both open and closed β€” so the new epic's progress bar reflects real status -- PBIs from `SSWConsulting/SSW.YakShaver` and `SSWConsulting/SSW.YakShaver.Desktop` should both be linked +> This is an **analysis only** β€” no epics have been created, no sub-issues have been linked, and no existing issues have been modified. Tick the confirmation checklist at the bottom once you're satisfied with the plan, then trigger the execute workflow. --- -## Step 5 β€” Post Summary Comments on All Three Original Epics +## 1. Proposed smaller epics -After all new epics are created, post a reorganisation summary on each of the three original epics. +### πŸ”Œ Example Cluster Name β€” `product: desktop` -### 5a β€” Write per-epic comment files (for SSW.YakShaver epics) +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver.Desktop#677 β€” ✨ YakShaver Desktop App -The `add_comment` tool can only post to `SSWConsulting/SSW.YakShaver.Desktop`. For the two epics in `SSWConsulting/SSW.YakShaver` (#2811 and #3494), use the `bash` tool to write their comment bodies to files β€” a `post-steps:` job will pick them up and post via `gh issue comment`. +**Scope:** 2–3 sentences describing what this epic covers and the value it delivers. -Use `bash` to write each file: +**PBIs:** +- SSWConsulting/SSW.YakShaver.Desktop#123 β€” [title] (open) +- SSWConsulting/SSW.YakShaver.Desktop#124 β€” [title] (closed) +- SSWConsulting/SSW.YakShaver#456 β€” [title] (open) -```bash -cat > /tmp/gh-aw/comments/comment-yakshaver-2811.md << 'EOF' - -EOF +[... one block per cluster ...] -cat > /tmp/gh-aw/comments/comment-yakshaver-3494.md << 'EOF' - -EOF -``` +--- -### 5b β€” Post comment on Desktop App epic #677 +## 2. Duplicate candidates (whole backlog) -Call `add_comment` with `item_number: 677` for the Desktop App epic (same repo, no cross-repo needed). +Open issues anywhere in the scanned repos (not limited to epic sub-issues) that look like they duplicate other work. Both open↔closed and open↔open pairs are included. Closed↔closed duplicates are omitted β€” they don't need action. -**Always provide `item_number: 677` explicitly** β€” `workflow_dispatch` triggers do not support auto-targeting. +| Open issue | Matched issue (state) | Reason | +|------------|------------------------|--------| +| SSWConsulting/SSW.YakShaver.Desktop#200 β€” [title] | SSWConsulting/SSW.YakShaver.Desktop#120 β€” [title] (closed 2025-11-02) | near-identical title and scope | +| SSWConsulting/SSW.YakShaver#305 β€” [title] | SSWConsulting/SSW.YakShaver.Desktop#210 β€” [title] (open) | same acceptance criteria, cross-repo overlap | -Use plain `SSWConsulting/RepoName#number` references (not Markdown links) β€” the tool has a hard limit of 50 HTTP/HTTPS links per comment. With ~60 PBIs total, plain references avoid breaching this limit. +--- -If the comment body would exceed ~60 PBI rows, split into two `add_comment` calls (max 3 total comments allowed). Always include `item_number: 677` on every call. +## 3. Source epics suggested for close or archive ---- +Existing epics that can likely be closed once this plan is executed. -### Comment template (use for all three epics, tailoring PBI section to each epic's own PBIs) +| Epic | Reason | +|------|--------| +| SSWConsulting/SSW.YakShaver#2811 β€” ✨ YakShaver Agent 2.0 | All remaining open PBIs absorbed into proposed epics 1, 3, and 5 | -```markdown -## πŸ—‚οΈ Epic Reorganised β€” [N] Smaller Epics Created +--- -The three large YakShaver epics have been clustered thematically into smaller, focused epics -in SSWConsulting/SSW.YakShaver.Desktop. PBIs from Desktop App and Agent 2.0 that share -the same theme have been grouped together. +## 4. Epics skipped (already 100% complete) -### New Epics (containing PBIs from this epic) +These were excluded from clustering because their sub-issue progress was 100%. -| Epic | Scope | -|------|-------| -| SSWConsulting/SSW.YakShaver.Desktop#[number] | [~10 word description] | -[... only rows for new epics that contain at least one PBI from THIS epic; plain org/repo#number only ...] +- SSWConsulting/SSW.YakShaver#NNN β€” [title] -### PBI Assignment +--- -| PBI | State | New Epic | -|-----|-------|---------| -| SSWConsulting/[SourceRepo]#[number] [title] | open/closed | SSWConsulting/SSW.YakShaver.Desktop#[number] | -[... all PBIs that belong to THIS original epic, open and closed ...] +## 5. βœ… Confirmation checklist -### βœ… Automated Linking +Tick each box once you've reviewed and agree. Add comments on this issue for anything you want changed before execution. -All PBIs listed above have been automatically linked as sub-issues of their new smaller epics. +- [ ] Proposed smaller epics (Section 1) look right +- [ ] Duplicate candidates (Section 2) are genuine duplicates +- [ ] Source epics in Section 3 should be closed/archived after execution +- [ ] Ready to trigger the execute workflow -> ℹ️ PBIs stay in their original repos. Only the tracking epics are in SSW.YakShaver.Desktop. +> After all boxes are ticked, run the execute workflow (future) β€” it will read this issue and perform the actual epic creation, sub-issue linking, and original-epic updates. ``` +Use plain `owner/repo#number` references everywhere (not Markdown links). GitHub auto-links them and this keeps the comment under GitHub's HTTP-link cap. + --- -## Important Notes +## Important notes -- Include **all** PBIs β€” open and closed β€” so new epics show real progress -- Cluster **holistically across all three source epics** β€” PBIs from Desktop App and Agent 2.0 may share the same new smaller epic -- All-closed clusters are acceptable β€” group by theme, not state -- The three original epics must stay open and untouched β€” do not modify or close them -- Use `bash` to write comment files for #2811 and #3494 before calling `add_comment` for #677 β€” the `post-steps:` job will post them automatically +- **No `create-issue` beyond the single report.** Do not attempt to create theme epics or a cleanup-report-per-product β€” one report issue only. +- **No `link-sub-issue` calls.** Linking is deferred to the execute workflow. +- **No `add-comment` calls.** Source-epic comments are not produced in this workflow. +- The three scanned repos stay untouched β€” do not modify, close, or relabel any existing issue. +- Include **all** PBIs from qualifying epics in the clusters β€” open and closed β€” so the proposed-epic scope reflects full context. +- If a source repo is unreachable (e.g. PAT lacks access), note it in the report's Scope section and continue with what you can read. - The `GH_AW_CROSS_REPO_PAT` secret must have: - - **Read** access to `SSWConsulting/SSW.YakShaver` - - **Read + Write (Issues)** access to `SSWConsulting/SSW.YakShaver.Desktop` + - **Read** access to every repo in `source_repos` + - **Read + Write (Issues)** access to `target_repo` From fc4de7fd7e16a76be179530b198d2a6e098fda93 Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Thu, 23 Apr 2026 14:20:35 +0800 Subject: [PATCH 09/11] rename file name --- ...lock.yml => backlog-cleanup-yakshaver.lock.yml} | 14 +++++++------- ...p-yakshaver.md => backlog-cleanup-yakshaver.md} | 0 2 files changed, 7 insertions(+), 7 deletions(-) rename .github/workflows/{epic-cleanup-yakshaver.lock.yml => backlog-cleanup-yakshaver.lock.yml} (99%) rename .github/workflows/{epic-cleanup-yakshaver.md => backlog-cleanup-yakshaver.md} (100%) diff --git a/.github/workflows/epic-cleanup-yakshaver.lock.yml b/.github/workflows/backlog-cleanup-yakshaver.lock.yml similarity index 99% rename from .github/workflows/epic-cleanup-yakshaver.lock.yml rename to .github/workflows/backlog-cleanup-yakshaver.lock.yml index 9bce5d40..9f00dd8b 100644 --- a/.github/workflows/epic-cleanup-yakshaver.lock.yml +++ b/.github/workflows/backlog-cleanup-yakshaver.lock.yml @@ -109,7 +109,7 @@ jobs: - name: Check workflow file timestamps uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: - GH_AW_WORKFLOW_FILE: "epic-cleanup-yakshaver.lock.yml" + GH_AW_WORKFLOW_FILE: "backlog-cleanup-yakshaver.lock.yml" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -177,7 +177,7 @@ jobs: GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - {{#runtime-import .github/workflows/epic-cleanup-yakshaver.md}} + {{#runtime-import .github/workflows/backlog-cleanup-yakshaver.md}} GH_AW_PROMPT_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates @@ -257,7 +257,7 @@ jobs: GH_AW_ASSETS_BRANCH: "" GH_AW_ASSETS_MAX_SIZE_KB: 0 GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - GH_AW_WORKFLOW_ID_SANITIZED: epiccleanupyakshaver + GH_AW_WORKFLOW_ID_SANITIZED: backlogcleanupyakshaver outputs: checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} @@ -870,7 +870,7 @@ jobs: runs-on: ubuntu-slim permissions: {} concurrency: - group: "gh-aw-conclusion-epic-cleanup-yakshaver" + group: "gh-aw-conclusion-backlog-cleanup-yakshaver" cancel-in-progress: false outputs: noop_message: ${{ steps.noop.outputs.noop_message }} @@ -930,7 +930,7 @@ jobs: GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" + GH_AW_WORKFLOW_ID: "backlog-cleanup-yakshaver" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} @@ -970,10 +970,10 @@ jobs: permissions: {} timeout-minutes: 15 env: - GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/epic-cleanup-yakshaver" + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/backlog-cleanup-yakshaver" GH_AW_ENGINE_ID: "copilot" GH_AW_SAFE_OUTPUTS_STAGED: "true" - GH_AW_WORKFLOW_ID: "epic-cleanup-yakshaver" + GH_AW_WORKFLOW_ID: "backlog-cleanup-yakshaver" GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" outputs: code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} diff --git a/.github/workflows/epic-cleanup-yakshaver.md b/.github/workflows/backlog-cleanup-yakshaver.md similarity index 100% rename from .github/workflows/epic-cleanup-yakshaver.md rename to .github/workflows/backlog-cleanup-yakshaver.md From cc0e863d33f3a561176cd57eecec0da5ed1f164e Mon Sep 17 00:00:00 2001 From: Willow Lyu Date: Thu, 23 Apr 2026 14:28:25 +0800 Subject: [PATCH 10/11] update name --- .../backlog-cleanup-yakshaver.lock.yml | 20 +++++++++---------- .../workflows/backlog-cleanup-yakshaver.md | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/backlog-cleanup-yakshaver.lock.yml b/.github/workflows/backlog-cleanup-yakshaver.lock.yml index 9f00dd8b..70f8459b 100644 --- a/.github/workflows/backlog-cleanup-yakshaver.lock.yml +++ b/.github/workflows/backlog-cleanup-yakshaver.lock.yml @@ -28,9 +28,9 @@ # suggests source epics that could themselves be closed or archived. No epics are # created, linked, or modified β€” execution is deferred until a human confirms the plan. # -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"ca0161b5f619ba3f61af5ee208441c8bcef39e1924738a049885dbd8b954e7c6","compiler_version":"v0.62.3","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"eeb0895223290cf0e244797f43fe84a62ef9eaeaac33c8b8d142d10a4c142294","compiler_version":"v0.62.3","strict":true,"agent_id":"copilot"} -name: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" +name: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" "on": workflow_dispatch: inputs: @@ -48,7 +48,7 @@ permissions: {} concurrency: group: "gh-aw-${{ github.workflow }}" -run-name: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" +run-name: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" jobs: activation: @@ -75,7 +75,7 @@ jobs: GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" GH_AW_INFO_CLI_VERSION: "v0.62.3" - GH_AW_INFO_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" + GH_AW_INFO_WORKFLOW_NAME: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "true" @@ -772,7 +772,7 @@ jobs: if: always() && steps.detection_guard.outputs.run_detection == 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: - WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" + WORKFLOW_NAME: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" WORKFLOW_DESCRIPTION: "Scans the YakShaver repos (Teams, Desktop, 360) for open epics whose sub-issue\nprogress is below 100%, analyses their PBIs across states, and produces a single\n\"Dry-Run Plan\" report issue in SSWConsulting/SSW.YakShaver.Desktop. The report\nproposes smaller theme epics (tagged by product), flags open issues that look\nlike duplicates of other work (open or closed) across the whole backlog, and\nsuggests source epics that could themselves be closed or archived. No epics are\ncreated, linked, or modified β€” execution is deferred until a human confirms the plan." HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} with: @@ -900,7 +900,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_NOOP_MAX: "1" - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" + GH_AW_WORKFLOW_NAME: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | @@ -913,7 +913,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" + GH_AW_WORKFLOW_NAME: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" with: github-token: ${{ secrets.GH_AW_CROSS_REPO_PAT }} script: | @@ -927,7 +927,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" + GH_AW_WORKFLOW_NAME: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "backlog-cleanup-yakshaver" @@ -950,7 +950,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" + GH_AW_WORKFLOW_NAME: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} @@ -974,7 +974,7 @@ jobs: GH_AW_ENGINE_ID: "copilot" GH_AW_SAFE_OUTPUTS_STAGED: "true" GH_AW_WORKFLOW_ID: "backlog-cleanup-yakshaver" - GH_AW_WORKFLOW_NAME: "Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog" + GH_AW_WORKFLOW_NAME: "Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog" outputs: code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} diff --git a/.github/workflows/backlog-cleanup-yakshaver.md b/.github/workflows/backlog-cleanup-yakshaver.md index ecb9708b..072675c6 100644 --- a/.github/workflows/backlog-cleanup-yakshaver.md +++ b/.github/workflows/backlog-cleanup-yakshaver.md @@ -1,5 +1,5 @@ --- -name: Epic Cleanup β€” Dry-Run Analysis of YakShaver Backlog +name: Backlog Cleanup β€” Dry-Run Analysis of YakShaver Backlog description: | Scans the YakShaver repos (Teams, Desktop, 360) for open epics whose sub-issue progress is below 100%, analyses their PBIs across states, and produces a single @@ -57,7 +57,7 @@ safe-outputs: target-repo: ${{ inputs.target_repo }} --- -# Epic Cleanup β€” Dry-Run Analysis +# Backlog Cleanup β€” Dry-Run Analysis You are an expert product backlog manager. Your job is to **analyse** β€” not execute β€” a cleanup of the YakShaver product backlog across multiple repos and produce a single report issue that a human can review, tick off, and then separately execute. From f79c480f959306c3f65dfa9fde490f6557ce6fab Mon Sep 17 00:00:00 2001 From: Tino Date: Mon, 27 Apr 2026 17:53:41 +0800 Subject: [PATCH 11/11] Add YakShaver backlog dry-run audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a read-only dry-run backlog audit for YakShaver (Run ID 2026-04-27-165630). The new markdown documents scan metadata and findings across SSW.YakShaver, SSW.YakShaver.Desktop and SSW.YakShaver360: 421 PBIs reviewed (120 open / 301 closed), 20 in-scope epics, 40 duplicate candidates flagged, and recommendations to consolidate PBIs into smaller epics (clusters 1–17) plus 6 source epics suggested for closure (+2 thin candidates). No GitHub issues were modified; this file is for human review. --- .../audits/backlog-audit-2026-04-27-165630.md | 735 ++++++++++++++++++ 1 file changed, 735 insertions(+) create mode 100644 docs/audits/backlog-audit-2026-04-27-165630.md diff --git a/docs/audits/backlog-audit-2026-04-27-165630.md b/docs/audits/backlog-audit-2026-04-27-165630.md new file mode 100644 index 00000000..122f3b33 --- /dev/null +++ b/docs/audits/backlog-audit-2026-04-27-165630.md @@ -0,0 +1,735 @@ +# 🧹 Dry-Run Audit β€” YakShaver Backlog + +**Run date:** 2026-04-27 16:56 +**Run ID:** 2026-04-27-165630 +**Scanned repos:** SSWConsulting/SSW.YakShaver, SSWConsulting/SSW.YakShaver.Desktop, SSWConsulting/SSW.YakShaver360 +**In-scope epics:** 20 (open, sub-issue progress < 100% β€” includes one empty-stub epic with 0 sub-issues) +**Total PBIs reviewed:** 421 (120 open, 301 closed) +**Duplicate candidates flagged:** 40 +**Source epics suggested for close:** 6 (all 100%-complete) + 2 thin/redundant candidates + +> Read-only audit. No GitHub issues were created, modified, closed, or commented on. This file is for human review. + +--- + +## 0. Methodology & data summary + +### Queries used +- **Epic discovery (paginated):** GraphQL `repository.issues(states:OPEN)` with `issueType{ name }`, filtered to nodes where `issueType.name == "Epic"`. +- **Sub-issue progress per epic:** GraphQL `repository.issue(number).subIssuesSummary{ total completed percentCompleted }`. +- **Sub-issue lists:** REST `repos///issues//sub_issues --paginate`, capturing `number, title, state, labels, body, updated_at`. +- **Whole-backlog dedup pool:** `gh issue list -R --state all --limit --json number,title,state,updatedAt`. + +All calls were read-only (GraphQL `query { ... }` blocks; REST `GET`). No `-X POST/PATCH/PUT/DELETE`, no mutations, no `gh issue close/edit/create/comment/lock/transfer/reopen`, no `gh pr` mutating commands. + +### Repos accessed +- βœ… `SSWConsulting/SSW.YakShaver` β€” 2569 issues total +- βœ… `SSWConsulting/SSW.YakShaver.Desktop` β€” 476 issues total +- βœ… `SSWConsulting/SSW.YakShaver360` β€” 44 issues total +- No access failures. + +### Per-repo epic and sub-issue totals + +| Repo | Open Epics found | 100% Skipped | In-scope | Sub-issues fetched (open / closed) | +|------|-----------------:|-------------:|---------:|-----------------------------------:| +| `SSWConsulting/SSW.YakShaver` | 24 | 6 | 18 | 376 (111 / 265) | +| `SSWConsulting/SSW.YakShaver.Desktop` | 2 | 0 | 2 | 40 (9 / 31) | +| `SSWConsulting/SSW.YakShaver360` | 0 | 0 | 0 | β€” | +| **Totals** | **26** | **6** | **20** | **421 (120 / 301)** | + +Notes: +- One in-scope epic (`SSW.YakShaver#3583 β€” ✨ 🏍️ Performance Optimization`) has **0 sub-issues** but is still open, so it is included in the in-scope count and listed under "Source epics suggested for close" (Section 3). +- Sub-issue numbers in the table above (e.g. 111/265) are the totals after de-duplicating sub-issues that appear under multiple parents β€” the project's clustering pool is **421**. + +### Whole-backlog dedup pool sizes + +| Repo | OPEN | CLOSED | Total | +|------|-----:|-------:|------:| +| `SSWConsulting/SSW.YakShaver` | 740 | 1829 | 2569 | +| `SSWConsulting/SSW.YakShaver.Desktop` | 106 | 370 | 476 | +| `SSWConsulting/SSW.YakShaver360` | 29 | 15 | 44 | +| **Totals** | **875** | **2214** | **3089** | + +Dedup left pool = every OPEN issue (875). Dedup right pool = every issue, OPEN+CLOSED (3089). Closed↔closed matches were excluded by construction. + +### Detection method +Title-similarity scan over the 875Γ—3089 pair space using normalized titles (ASCII-only, lowercased, stop-words and ticket-prefix tokens removed) plus Jaccard token similarity β‰₯ 0.55 OR exact-normalized-title equality. Templated/series patterns ("Sprint TBD video to YouTube channel", per-person "Canary Testing - ") were filtered out as false positives. Final shortlist was hand-curated for genuine duplicates. + +--- + +## 1. Proposed smaller epics + +> Each cluster lists the source epics it consolidates, a short scope statement, and the PBIs it should pull in (open + closed). All 421 in-scope sub-issues are accounted for across clusters 1–17. + +### πŸ€– 1. Agent 2.0 β€” Recording, Transcript, LLM & Prompt Pipeline β€” `product: desktop` (also `shared`) + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#1150 β€” πŸš€ Agent - replace current workflow with full agentic capability +- SSWConsulting/SSW.YakShaver#2811 β€” ✨ YakShaver Agent 2.0 (recording/transcription/prompt portions) +- SSWConsulting/SSW.YakShaver#584 β€” small overlap (prompt name-recognition) + +**Scope:** The end-to-end YakShaver 2.0 engine: video recording β†’ audio extraction β†’ transcription β†’ LLM call (prompt + structured output) β†’ issue creation. Owns the Tauri/Electron recording subsystem, FFmpeg pipeline, OpenAI/Whisper integration, prompt design, model selection, and core agent architecture. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#1000 β€” ✨ Refactor Separation of Concerns Between Function App and Container App (open) +- SSWConsulting/SSW.YakShaver#1001 β€” ✨ Refactor YakShaver Agent to Define Required Kernel Functions (Tools) (open) +- SSWConsulting/SSW.YakShaver#1002 β€” ✨ Implement Database Design for Storing Chat History (open) +- SSWConsulting/SSW.YakShaver#1003 β€” ✨ Add System Prompt Configuration for YakShaver Agent (open) +- SSWConsulting/SSW.YakShaver#1004 β€” ✨ Define and Document the Boundary of a Vertical Slice (open) +- SSWConsulting/SSW.YakShaver#1005 β€” ✨ Add Unit and Integration Tests for YakShaver Agent (open) +- SSWConsulting/SSW.YakShaver#1016 β€” ✨ Implement Feature Flag for New Agent in YakShaver (open) +- SSWConsulting/SSW.YakShaver#1334 β€” ✨ Doc - Draft High Level Approach How to Implement AI Agent (open) +- SSWConsulting/SSW.YakShaver#587 β€” ♻️ OpenAI Prompt - Improve recognizing uncommon names (open) +- SSWConsulting/SSW.YakShaver#76 β€” ✨ System Portal - Ability to switch AI models (open) +- SSWConsulting/SSW.YakShaver#77 β€” ✨ System Portal - Ability to switch speech-to-text models (open) +- SSWConsulting/SSW.YakShaver#80 β€” ✨ System Portal - Modify prompts (open) +- SSWConsulting/SSW.YakShaver#2890 β€” ✨ MCP Issue Creation - Implement Fallback for YakShaver 2.0 (open) +- SSWConsulting/SSW.YakShaver#2894 β€” ✨ Database Redesign (open) +- SSWConsulting/SSW.YakShaver#2909 β€” ✨ Roadmap - Define Standalone App Requirements (open) +- SSWConsulting/SSW.YakShaver#3012 β€” ✨ YakShaver 2.0 - Configurable Settings (open) +- SSWConsulting/SSW.YakShaver#2786 β€” πŸ•΅οΈ Spike: YakShaver Agent 2.0 (closed) +- SSWConsulting/SSW.YakShaver#2813 β€” πŸ•΅οΈ Spike: Investigate frameworks to build Standalone App (closed) +- SSWConsulting/SSW.YakShaver#2814 β€” ✨ Investigate MCP Hosts/Clients (closed) +- SSWConsulting/SSW.YakShaver#2815 β€” ✨ Event Storming 2.0 (closed) +- SSWConsulting/SSW.YakShaver#2877 β€” ✨ Initialise YakShaver 2.0 Tauri App (closed) +- SSWConsulting/SSW.YakShaver#2893 β€” ✨ Local Processing - Implement Local Video Processing (closed) +- SSWConsulting/SSW.YakShaver#2895 β€” ✨ Screen Recording - Add screen capture and video review functionality (closed) +- SSWConsulting/SSW.YakShaver#2953 β€” ✨ YakShaver 2.0 MVP - FFmpeg (closed) +- SSWConsulting/SSW.YakShaver#2954 β€” ✨ YakShaver 2.0 MVP - Video Recording (closed) +- SSWConsulting/SSW.YakShaver#2955 β€” ✨ YakShaver 2.0 MVP - Video Hosting (closed) +- SSWConsulting/SSW.YakShaver#2956 β€” ✨ YakShaver 2.0 MVP - OpenAI - Process Transcript (closed) +- SSWConsulting/SSW.YakShaver#2958 β€” ✨ YakShaver 2.0 MVP - OpenAI - Extract Transcript (closed) +- SSWConsulting/SSW.YakShaver#2987 β€” ✨ YakShaver 2.0 MVP - Connect Recording to Transcription pipeline (closed) +- SSWConsulting/SSW.YakShaver#2989 β€” ✨ YakShaver 2.0 MVP - Connect Recording to Upload Video (closed) +- SSWConsulting/SSW.YakShaver#3009 β€” ✨ YakShaver 2.0 - Refine Prompt (closed) +- SSWConsulting/SSW.YakShaver#3011 β€” ✨ Make LLM client generic for different models/services (closed) +- SSWConsulting/SSW.YakShaver#175 β€” ✨ Generalize Transcription Model (closed) +- SSWConsulting/SSW.YakShaver#287 β€” Add 3-2-1 countdown before recording starts (closed) +- SSWConsulting/SSW.YakShaver#3005 β€” ✨ YakShaver 2.0: Add a control bar in recording (closed) +- SSWConsulting/SSW.YakShaver#18 β€” ✨ Recording Toolbar - Minimize App Window During Recording (closed) +- SSWConsulting/SSW.YakShaver#19 β€” ✨ Task Execution Details Enhancement (closed) +- SSWConsulting/SSW.YakShaver#22 β€” ✨ YouTube Link Integration - Add YouTube URL to LLM Input (closed) +- SSWConsulting/SSW.YakShaver#21 β€” ✨ YouTube Connection Update (closed) +- SSWConsulting/SSW.YakShaver#118 β€” πŸ› Issue with YouTube Link in YakShaver Desktop (closed) +- SSWConsulting/SSW.YakShaver#3385 β€” πŸ› Bug - Remote participant audio is not captured when recording system audio (closed) +- SSWConsulting/SSW.YakShaver#354 β€” Add "No screen / Camera only" option (closed) +- SSWConsulting/SSW.YakShaver#3158 β€” ✨ YakShaver Desktop - AI Assisted Development Workflow (closed) +- SSWConsulting/SSW.YakShaver#436 β€” ♻️ Refactor custom prompt to properly embed screenshot (closed) +- SSWConsulting/SSW.YakShaver#302 β€” ✨ Add screenshot upload tool to Yak video tools (closed) +- SSWConsulting/SSW.YakShaver#425 β€” Save recorded video files and durations (closed) +- SSWConsulting/SSW.YakShaver#443 β€” Workflow stops at 'Stop' but doesn't update YouTube metadata (closed) + +### 🎬 2. YakShaver Desktop App β€” Onboarding, OAuth, Distribution & Reliability β€” `product: desktop` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver.Desktop#677 β€” ✨ YakShaver Desktop App +- SSWConsulting/SSW.YakShaver#2811 β€” ✨ YakShaver Agent 2.0 (distribution/onboarding/login portions) + +**Scope:** Everything around shipping the desktop app: code signing & notarization (Win/macOS), installers, auto-update, onboarding wizard, login flow, error reporting, in-app UX polish, settings, factory reset, telemetry, and bug fixes against the running desktop app. + +**PBIs:** +- SSWConsulting/SSW.YakShaver.Desktop#350 β€” Code signing & notarization - Windows (Authenticode) (open) +- SSWConsulting/SSW.YakShaver.Desktop#3544 β€” Prompt access requires login but not communicated (open) +- SSWConsulting/SSW.YakShaver.Desktop#471 β€” ✨ Stop Button - Add functionality to stop AI workflow (open) +- SSWConsulting/SSW.YakShaver.Desktop#542 β€” ✨ Feature - Add additional LLM providers to the Desktop setup wizard (open) +- SSWConsulting/SSW.YakShaver.Desktop#604 β€” πŸ› Bug - Failure to download YouTube video when using Process Link box (open) +- SSWConsulting/SSW.YakShaver.Desktop#693 β€” ✨ Optimize Transcript - Fix spellings and optimize transcript (open) +- SSWConsulting/SSW.YakShaver#3295 β€” πŸ“ Publish blogs about YakShaver Desktop (open) +- SSWConsulting/SSW.YakShaver#3321 β€” πŸ§ͺ Canary Testing - Feedback from someone outside YakShaver team (open) +- SSWConsulting/SSW.YakShaver.Desktop#3422 β€” πŸ“ Download page is confusing (closed) +- SSWConsulting/SSW.YakShaver.Desktop#3439 β€” πŸ› Microsoft Auth shows "Unhandled protocol route: /auth" (closed) +- SSWConsulting/SSW.YakShaver.Desktop#3440 β€” ✨ AI - Add Agents.md file (closed) +- SSWConsulting/SSW.YakShaver.Desktop#3445 β€” ✨ Project Selection - Add project selection and metadata (closed) +- SSWConsulting/SSW.YakShaver.Desktop#3451 β€” πŸ› Video processed successfully despite no audio (closed) +- SSWConsulting/SSW.YakShaver.Desktop#3497 β€” ✨ Authentication-migration PBIs breakdown (closed) +- SSWConsulting/SSW.YakShaver.Desktop#3498 β€” ✨ Jira Integration and Done Video (closed) +- SSWConsulting/SSW.YakShaver.Desktop#3515 β€” πŸ› Update YakShaver.Desktop template (closed) +- SSWConsulting/SSW.YakShaver.Desktop#3543 β€” Errors are hard to notice in results log (closed) +- SSWConsulting/SSW.YakShaver.Desktop#526 β€” Add centralized error logging to App Insights (closed) +- SSWConsulting/SSW.YakShaver.Desktop#593 β€” ✨ Recording Confirmation - Prevent recording from starting until user confirms (closed) +- SSWConsulting/SSW.YakShaver.Desktop#642 β€” πŸ› Camera and audio not working despite permissions (closed) +- SSWConsulting/SSW.YakShaver.Desktop#646 β€” πŸ› Multiple 'Update ready' popups shown repeatedly (closed) +- SSWConsulting/SSW.YakShaver.Desktop#649 β€” πŸ•΅οΈβ€β™‚οΈ Spike - LLM API key not recognized (closed) +- SSWConsulting/SSW.YakShaver.Desktop#662 β€” πŸ› GitHub authentication fails when creating issue (closed) +- SSWConsulting/SSW.YakShaver.Desktop#665 β€” πŸ› Clicking taskbar/shortcut does not restore window on Windows (closed) +- SSWConsulting/SSW.YakShaver.Desktop#671 β€” πŸ› Undo shows as Completed in portal instead of Cancelled (closed) +- SSWConsulting/SSW.YakShaver.Desktop#718 β€” Add fallback/system prompt in desktop app when project prompt missing (closed) +- SSWConsulting/SSW.YakShaver.Desktop#734 β€” πŸ› PBI link can't be extracted (closed) +- SSWConsulting/SSW.YakShaver.Desktop#737 β€” ♻️ Refactor - Split onboarding wizard into per-tab components (closed) +- SSWConsulting/SSW.YakShaver.Desktop#746 β€” Provide Intel (x86_64) macOS installer (closed) +- SSWConsulting/SSW.YakShaver.Desktop#749 β€” πŸ› Unable to sign in using SSW Account (closed) +- SSWConsulting/SSW.YakShaver.Desktop#751 β€” πŸ› Azure DevOps MCP service name with spaces breaks approve flow (closed) +- SSWConsulting/SSW.YakShaver#91 β€” ✨ Desktop App - Code Signing and Notarization (closed) +- SSWConsulting/SSW.YakShaver#351 β€” ✨ Code signing & notarization - macOS (closed) +- SSWConsulting/SSW.YakShaver#3024 β€” ✨ YakShaver 2.0 - Build and Release pipeline (closed) +- SSWConsulting/SSW.YakShaver#3025 β€” ✨ YakShaver 2.0 - Auto update feature (closed) +- SSWConsulting/SSW.YakShaver#332 β€” ✨ Add onboarding wizard (3-step) (closed) +- SSWConsulting/SSW.YakShaver#444 β€” Wizard UX & onboarding improvements (closed) +- SSWConsulting/SSW.YakShaver#442 β€” UX: Add preset MCP servers + onboarding checklist (closed) +- SSWConsulting/SSW.YakShaver#3022 β€” ✨ YakShaver 2.0 - Canary Testing Document (closed) +- SSWConsulting/SSW.YakShaver#470 β€” ✨ Factory Reset Button (closed) +- SSWConsulting/SSW.YakShaver#3050 β€” ✨ Make UX of Desktop more user-friendly (closed) +- SSWConsulting/SSW.YakShaver#3118 β€” ✨ YakShaver Desktop App Logo (closed) +- SSWConsulting/SSW.YakShaver#3129 β€” Custom Prompt Settings with Tabs and Presets (closed) +- SSWConsulting/SSW.YakShaver#3131 β€” ✨ YakShaver Desktop - Allow more flexible custom instructions (closed) +- SSWConsulting/SSW.YakShaver#3047 β€” ✨ Environment Variables Accessibility (closed) +- SSWConsulting/SSW.YakShaver#3095 β€” ✨ Secret Storage (closed) +- SSWConsulting/SSW.YakShaver#3130 β€” Display Thought Process in Executing Tasks Area (closed) +- SSWConsulting/SSW.YakShaver#3132 β€” Add YouTube URL and Timestamp to Issues (closed) +- SSWConsulting/SSW.YakShaver#310 β€” Electron Backend - Add desktop app testing framework (closed) +- SSWConsulting/SSW.YakShaver#311 β€” Portal Integration: send shave entries to API (closed) +- SSWConsulting/SSW.YakShaver#312 β€” 🧠 Spike: Storing Desktop Shave Metadata (closed) +- SSWConsulting/SSW.YakShaver#352 β€” Install ORM (Prisma/Drizzle) and prototype 'My Shave' (closed) +- SSWConsulting/SSW.YakShaver#417 β€” πŸ•΅οΈβ€β™‚οΈ Spike - UI Refactor - Workflow progress panel (closed) +- SSWConsulting/SSW.YakShaver#420 β€” πŸ› Thought process no longer shown (closed) +- SSWConsulting/SSW.YakShaver#422 β€” ✨ Improve Login Flow for YakShaver Desktop (closed) +- SSWConsulting/SSW.YakShaver#457 β€” Prompts, deduplication, title and emoji formatting differ (closed) +- SSWConsulting/SSW.YakShaver#49 β€” Feature Requests for YakShaver Desktop (closed) +- SSWConsulting/SSW.YakShaver#493 β€” Display play emoji and linked "Watch the video" on PBIs (closed) +- SSWConsulting/SSW.YakShaver#51 β€” ✨ Version Display - Add Version Number to UI (closed) +- SSWConsulting/SSW.YakShaver#515 β€” Detect macOS screen-record permission and instruct user (closed) +- SSWConsulting/SSW.YakShaver#524 β€” UI: Use arrow next to AI reasoning/plan (closed) +- SSWConsulting/SSW.YakShaver#64 β€” ♻️ Refactor Electron IPC Logic (closed) +- SSWConsulting/SSW.YakShaver#119 β€” πŸ› Issue with Opening Links in Default Browser (closed) +- SSWConsulting/SSW.YakShaver#152 β€” πŸ› Flashing 'GitHub token required' message (closed) +- SSWConsulting/SSW.YakShaver#24 β€” ✨ Tray Icon Behavior - Modify app close to minimize to tray (closed) +- SSWConsulting/SSW.YakShaver#307 β€” πŸ› default prompt not using template verbatim (closed) +- SSWConsulting/SSW.YakShaver#3309 β€” πŸ› MCP Whitelist - Error on adding within execution loop (closed) + +### πŸ”Œ 3. MCP Server Registry, Gateway & Tool Integration β€” `product: desktop` (also `shared`) + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver.Desktop#720 β€” Dynamic MCP +- SSWConsulting/SSW.YakShaver#2811 β€” ✨ YakShaver Agent 2.0 (MCP-tool portions) +- SSWConsulting/SSW.YakShaver.Desktop#677 β€” ✨ YakShaver Desktop App (MCP server preset portions) + +**Scope:** The MCP plumbing surface β€” portal-side server registry/catalog, desktop-side dynamic fetching of tools, MCP gateway, OAuth for MCP servers, whitelist/approval UX, default/preset MCP servers (Jira, GitHub, Azure DevOps), and MCP-related bug fixes. + +**PBIs:** +- SSWConsulting/SSW.YakShaver.Desktop#721 β€” ✨ [PORTAL] - MCP Server Registry / Catalog (open) +- SSWConsulting/SSW.YakShaver.Desktop#722 β€” ✨ [DESKTOP] - Fetch all available MCP tools from portal and persist (open) +- SSWConsulting/SSW.YakShaver.Desktop#723 β€” ✨ [DESKTOP] - Create MCP Gateway (open) +- SSWConsulting/SSW.YakShaver#2898 β€” ✨ Configure Default MCP Services for YakShaver Desktop (open) +- SSWConsulting/SSW.YakShaver.Desktop#639 β€” ♻️ Move MCP common interfaces/types to shared folder (closed) +- SSWConsulting/SSW.YakShaver.Desktop#767 β€” ✨ Jira preset MCP server - Provide guided domain input (closed) +- SSWConsulting/SSW.YakShaver.Desktop#768 β€” ✨ Custom prompts - Default built-in MCP servers to selected (closed) +- SSWConsulting/SSW.YakShaver.Desktop#774 β€” ✨ Prompt Manager - Clarify default/template prompt (closed) +- SSWConsulting/SSW.YakShaver.Desktop#776 β€” ✨ Auto approve option for Project Prompt and MCP approvals (closed) +- SSWConsulting/SSW.YakShaver.Desktop#777 β€” πŸ€–βœ¨ Project prompt selection - Improve confirmation wording (closed) +- SSWConsulting/SSW.YakShaver.Desktop#778 β€” πŸ€–βœ¨ MCP tool confirmation - Show service icons (closed) +- SSWConsulting/SSW.YakShaver.Desktop#779 β€” πŸ€–βœ¨ Final result - Add spacing for camelCase titles (closed) +- SSWConsulting/SSW.YakShaver#117 β€” πŸ•΅πŸ» SPIKE: Enhance YakShaver with Optional User Confirmation for MCP Actions (closed) +- SSWConsulting/SSW.YakShaver#225 β€” ✨ Whitelist MCP Tool - Enhance YakShaver Desktop App (closed) +- SSWConsulting/SSW.YakShaver#229 β€” ✨ Allow MCP Servers to connect with OAuth integration (closed) +- SSWConsulting/SSW.YakShaver#2899 β€” ✨ Add MCP Support to YakShaver Desktop (closed) +- SSWConsulting/SSW.YakShaver#2900 β€” ✨ Improve MCP Service Support and Error Messaging (closed) +- SSWConsulting/SSW.YakShaver#2901 β€” ✨ Add Customizable Prompt Feature (closed) +- SSWConsulting/SSW.YakShaver#2896 β€” ✨ Load MCP Server Configuration on Startup (closed) +- SSWConsulting/SSW.YakShaver#2957 β€” ✨ YakShaver 2.0 MVP - MCP Host (closed) +- SSWConsulting/SSW.YakShaver#3008 β€” ✨ YakShaver 2.0 - Local MCP Support - Implement STDIO (closed) +- SSWConsulting/SSW.YakShaver#3010 β€” ✨ YakShaver 2.0 - Improve MCP Response Handling (closed) +- SSWConsulting/SSW.YakShaver#637 β€” πŸ€–βœ¨ Add fill_template MCP tool for LLM-powered template filling (closed) +- SSWConsulting/SSW.YakShaver#20 β€” ✨ MCP and API Key Validation - Add Visual Confirmation (closed) + +### πŸ’„ 4. Portal UI/UX Polish & Visual Bug Fixes β€” `product: shared` (`teams` portal) + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#3063 β€” πŸ’„ YakShaver Portal UI/UX improvements + +**Scope:** Visual fixes, alignment/padding, navigation indicators, button states, breadcrumbs, dashboards, project page tweaks, and small UX polish across the YakShaver client portal. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#1809 β€” ✨ Portal - UI Improvements (open) +- SSWConsulting/SSW.YakShaver#2413 β€” ✨ Client Portal - Improve YouTube Integration UX (open) +- SSWConsulting/SSW.YakShaver#2628 β€” ✨ Portal - Active State Indication for Nav Links (open) +- SSWConsulting/SSW.YakShaver#2779 β€” πŸ’„ Portal - Improvements to "New Project" form (open) +- SSWConsulting/SSW.YakShaver#2993 β€” ✨ Admin Visibility Enhancement (open) +- SSWConsulting/SSW.YakShaver#2997 β€” πŸ’„ MyShaves UX - Improve status colour variations (open) +- SSWConsulting/SSW.YakShaver#3014 β€” πŸ› Bug - YakShaver favicon is squished (open) +- SSWConsulting/SSW.YakShaver#3015 β€” ✨ UI Filter Indicator - Active filters (open) +- SSWConsulting/SSW.YakShaver#3016 β€” πŸ› Bug - Inconsistent Titles in Portal (open) +- SSWConsulting/SSW.YakShaver#3017 β€” πŸ› Bug - Last Updated Column Cut Off (open) +- SSWConsulting/SSW.YakShaver#3018 β€” πŸ› Bug - Padding/Alignment in Integrations (open) +- SSWConsulting/SSW.YakShaver#3019 β€” ✨ Video Thumbnail Overlay (open) +- SSWConsulting/SSW.YakShaver#3026 β€” ✨ UI Improvement - Add Hover State for Clickable Tools (open) β€” *flagged in Section 2* +- SSWConsulting/SSW.YakShaver#3033 β€” ✨ Shorten Backlog URL - Click Functionality (open) +- SSWConsulting/SSW.YakShaver#3038 β€” πŸ› Bug - Duplicate Navigation to Dashboard (open) +- SSWConsulting/SSW.YakShaver#3039 β€” ✨ Subscription Page Button Colors (open) +- SSWConsulting/SSW.YakShaver#3040 β€” πŸ› Bug - Sorting in My Shaves Table (open) +- SSWConsulting/SSW.YakShaver#3045 β€” ♻️ UI/UX - Dashboard view (open) +- SSWConsulting/SSW.YakShaver#3051 β€” ✨ Menu Reorganization - Settings Layout (open) +- SSWConsulting/SSW.YakShaver#3057 β€” ✨ Project Interface (open) +- SSWConsulting/SSW.YakShaver#3058 β€” ✨ Dashboard and Menu Improvements (open) +- SSWConsulting/SSW.YakShaver#3151 β€” ✨ UI Alignment - Field and Label Layout (open) +- SSWConsulting/SSW.YakShaver#3152 β€” ✨ Dashboard - Date Range and Metrics Display (open) +- SSWConsulting/SSW.YakShaver#3320 β€” πŸ› Bug - Breadcrumb and Route Inconsistencies (open) +- SSWConsulting/SSW.YakShaver#2627 β€” ✨ Improve Project Deletion Process (closed) +- SSWConsulting/SSW.YakShaver#2991 β€” βœ‰οΈ portal 1/2 wide fields (closed) +- SSWConsulting/SSW.YakShaver#3034 β€” πŸ’„ Move "Delete Project" Button (closed) +- SSWConsulting/SSW.YakShaver#3069 β€” ✨ Project Page Updates (closed) +- SSWConsulting/SSW.YakShaver#3232 β€” ✨ Leaderboard - Most shaved Projects (closed) +- SSWConsulting/SSW.YakShaver#3481 β€” πŸ› OneDrive subscription dips (closed) +- SSWConsulting/SSW.YakShaver#3495 β€” πŸ€– System Info button style inconsistency (closed) +- SSWConsulting/SSW.YakShaver#3496 β€” πŸ› System Info visible without access (closed) + +### 🏒 5. Client Tenant Portal β€” Multi-tenancy, Admin & Project Management β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#693 β€” πŸš€ Client Tenant Setup (including admin portal) + +**Scope:** The backend and admin-facing portal capabilities: multi-tenancy infrastructure, project sync from DevOps/GitHub, user roles (promote/demote/revoke), tenant invitations, admin notices, dashboard endpoints (Recent Shaves, Adoption Rate, Leaderboard backend), system-portal config (AI/STT model toggles, prompt editing, user prefs). + +**PBIs:** +- SSWConsulting/SSW.YakShaver#1187 β€” ✨ Administrator Permission Notice on GitHub Integration (open) +- SSWConsulting/SSW.YakShaver#1265 β€” ✨ Work Item Dashboard (open) +- SSWConsulting/SSW.YakShaver#1411 β€” πŸ•΅πŸ» Spike: Deleting a project in DevOps/GitHub (open) +- SSWConsulting/SSW.YakShaver#1412 β€” ✨ Implement Logging (open) +- SSWConsulting/SSW.YakShaver#1497 β€” ✨ Sync Projects - exclude projects through portal (open) +- SSWConsulting/SSW.YakShaver#1716 β€” ✨ New Tenant: Invite Users (open) +- SSWConsulting/SSW.YakShaver#1737 β€” πŸ’„ Frontend - Recent Shaves Module (open) +- SSWConsulting/SSW.YakShaver#1738 β€” ✨ Backend - distinguish user by Role (open) +- SSWConsulting/SSW.YakShaver#1747 β€” ✨ Backend - User Roles - Promotion and Demotion (open) +- SSWConsulting/SSW.YakShaver#1748 β€” πŸ’„ Frontend - Hide content based on roles (open) +- SSWConsulting/SSW.YakShaver#1749 β€” πŸ’„ Frontend - User Roles - Revoke/Demote (open) +- SSWConsulting/SSW.YakShaver#1755 β€” ✨ Backend - Endpoint for Recent Shaves Module (open) +- SSWConsulting/SSW.YakShaver#1838 β€” ✨ Backend - Adoption Rate Formula (open) +- SSWConsulting/SSW.YakShaver#81 β€” ✨ Client Portal - User preference (notification opt-in) (open) +- SSWConsulting/SSW.YakShaver#911 β€” ✨ Support: OneDrive, GoogleDrive, ICloud, Dropbox storage (open) +- SSWConsulting/SSW.YakShaver#1007 β€” ✨ Multi Tenancy - Master Database Design (closed) +- SSWConsulting/SSW.YakShaver#1008 β€” ✨ Multi Tenancy - REST API for Tenants (closed) +- SSWConsulting/SSW.YakShaver#1009 β€” ✨ Multi Tenancy - DevOps CD Pipeline Provision DB (closed) +- SSWConsulting/SSW.YakShaver#1010 β€” ✨ Multi Tenancy - Auto-Apply EF Migrations (closed) +- SSWConsulting/SSW.YakShaver#1011 β€” ✨ Multi Tenancy - Dynamic Database Connection String (closed) +- SSWConsulting/SSW.YakShaver#1037 β€” ✨ Provision Master Database CI/CD (closed) +- SSWConsulting/SSW.YakShaver#1076 β€” ✨ Configure Azure DevOps Service Integrations (closed) +- SSWConsulting/SSW.YakShaver#1109 β€” ✨ Register domain or subdomain for Portal/Api (closed) +- SSWConsulting/SSW.YakShaver#1110 β€” ✨ Create Staging YakShaver App on GitHub (closed) +- SSWConsulting/SSW.YakShaver#1129 β€” ✨ Client Portal - Entra ID Integration (closed) +- SSWConsulting/SSW.YakShaver#1153 β€” ✨ Set up client portal for production (closed) +- SSWConsulting/SSW.YakShaver#1182 β€” ✨ Add a product tour wizard to the portal (closed) +- SSWConsulting/SSW.YakShaver#1186 β€” ✨ Improve UI/UX Design (closed) +- SSWConsulting/SSW.YakShaver#1366 β€” ✨ Implement Video Tracking and Retry Feature (closed) +- SSWConsulting/SSW.YakShaver#1518 β€” ✨ My Shaves list (closed) +- SSWConsulting/SSW.YakShaver#1734 β€” πŸ’„ Frontend - Dashboard - Leaderboard (closed) +- SSWConsulting/SSW.YakShaver#1743 β€” ✨ Backend - Endpoints for Leaderboard (closed) +- SSWConsulting/SSW.YakShaver#1745 β€” ✨ Backend - Update table to include Roles (closed) +- SSWConsulting/SSW.YakShaver#270 β€” ✨ API Security Enhancement for PROD (closed) +- SSWConsulting/SSW.YakShaver#2931 β€” πŸ› Reprocessing of Completed YakShaver Tasks (closed) +- SSWConsulting/SSW.YakShaver#3072 β€” ✨ My Tenant - See other users (closed) +- SSWConsulting/SSW.YakShaver#3279 β€” ✨ Org Member Visibility - Add members from other orgs (closed) +- SSWConsulting/SSW.YakShaver#3519 β€” πŸ› Subscription credits not resetting on renewal (closed) +- SSWConsulting/SSW.YakShaver#412 β€” πŸ“ Check function Authorization level (closed) +- SSWConsulting/SSW.YakShaver#434 β€” ♻️ Replace Container App with Function App (closed) +- SSWConsulting/SSW.YakShaver#621 β€” βš—οΈ Observability - Set up SEQ (closed) +- SSWConsulting/SSW.YakShaver#711 β€” πŸ‘· Stable AI API Outputs for CI/CD Tests (closed) +- SSWConsulting/SSW.YakShaver#712 β€” πŸ› Missing Telemetry Data (closed) +- SSWConsulting/SSW.YakShaver#73 β€” ✨ Video to YouTube (already on MS Stream) (closed) +- SSWConsulting/SSW.YakShaver#759 β€” πŸ” Spike - Set up Client Instance (closed) +- SSWConsulting/SSW.YakShaver#770 β€” ✨ System Portal - CI/CD Pipeline (closed) +- SSWConsulting/SSW.YakShaver#78 β€” ✨ Configure GitHub Service Integrations (closed) +- SSWConsulting/SSW.YakShaver#79 β€” ✨ Client Portal - CI/CD Pipeline (closed) +- SSWConsulting/SSW.YakShaver#798 β€” πŸ“ ADR - Tech Stack for Portals (closed) +- SSWConsulting/SSW.YakShaver#816 β€” πŸ” Spike - cost effective global toggle (closed) +- SSWConsulting/SSW.YakShaver#819 β€” πŸ”Ž Spike - Multi-Tenancy Solution (closed) +- SSWConsulting/SSW.YakShaver#913 β€” ✨ Frontend - Initial Project setup (closed) +- SSWConsulting/SSW.YakShaver#914 β€” ✨ Frontend - Tech stack Decision + Rule (closed) +- SSWConsulting/SSW.YakShaver#933 β€” ✨ Automated YakShaver access script (closed) +- SSWConsulting/SSW.YakShaver#997 β€” ✨ Backend Authentication (closed) + +### ♻️ 6. Auth Migration β€” IdentityServer, OAuth & Invitations β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#3494 β€” ♻️ Auth Migration +- SSWConsulting/SSW.YakShaver#2811 β€” OAuth-related portion + +**Scope:** SSW IdentityServer migration, Better-Auth spike & ADR, Entra optionality wizard, OAuth2 + PKCE flow, refresh-token plumbing across desktop/portal/backend, dynamic client registration, invitation system for non-Entra users. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#3509 β€” ✨ Invitation system for non-Entra users (open) +- SSWConsulting/SSW.YakShaver#578 β€” Backend token exchange (auth code β†’ access + refresh tokens) (open) +- SSWConsulting/SSW.YakShaver#2889 β€” ✨ External Login Integration - Microsoft or GitHub accounts (open) +- SSWConsulting/SSW.YakShaver#3503 β€” ✨ Make Entra permission optional on wizard (closed) +- SSWConsulting/SSW.YakShaver#3504 β€” ✨ Spike: Break down 'Make Entra permission optional' (closed) +- SSWConsulting/SSW.YakShaver#3505 β€” ✨ Spike: Investigate Better-Auth integration (closed) +- SSWConsulting/SSW.YakShaver#3506 β€” ✨ Revisit ADR after Better-Auth Spike (closed) +- SSWConsulting/SSW.YakShaver#3507 β€” Breakdown authentication implementation approach (closed) +- SSWConsulting/SSW.YakShaver#3508 β€” ✨ Policy for assigning tenant IDs to non-Entra users (closed) +- SSWConsulting/SSW.YakShaver#3597 β€” ✨ Desktop app - Authentication using SSW IdentityServer (closed) +- SSWConsulting/SSW.YakShaver#3598 β€” ✨ Portal - Authentication using SSW IdentityServer (closed) +- SSWConsulting/SSW.YakShaver#3599 β€” ✨ Backend API - Validate access tokens using IdentityServer (closed) +- SSWConsulting/SSW.YakShaver#297 β€” Replace in-memory OAuth provider with persistent OAuth (closed) +- SSWConsulting/SSW.YakShaver#305 β€” Feature: Add sign-in flow to YakShaver Desktop (deep links) (closed) +- SSWConsulting/SSW.YakShaver#308 β€” 🧠 Spike: Secure OAuth2 Flow with PKCE or BFF Token Broker (closed) +- SSWConsulting/SSW.YakShaver#577 β€” Implement simple 302 OAuth authorize redirect (closed) +- SSWConsulting/SSW.YakShaver#579 β€” Store access and refresh tokens in Electron safe storage (closed) +- SSWConsulting/SSW.YakShaver#580 β€” Implement refresh access token flow (closed) +- SSWConsulting/SSW.YakShaver#581 β€” Persist dynamic client registration per server name (closed) +- SSWConsulting/SSW.YakShaver#624 β€” ✨ OAuth Flow - Desktop initiates OAuth with Backend (closed) +- SSWConsulting/SSW.YakShaver#630 β€” πŸ› OAuth callback returns empty page instead of deep-link (closed) +- SSWConsulting/SSW.YakShaver#636 β€” ✨ YouTube Integration - Authorization + PKCE (closed) +- SSWConsulting/SSW.YakShaver#3378 β€” Backend: Move discovery lookup from Desktop to .NET API (closed) +- SSWConsulting/SSW.YakShaver#300 β€” UI: Reorder Tool Approval Mode (closed) + +### πŸ’³ 7. Subscriptions, Pricing & Billing Emails β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#693 (subscription email portion) +- SSWConsulting/SSW.YakShaver#694 (pricing/licensing portion) + +**Scope:** Customer-facing subscription lifecycle: tier confirmation emails (starter/team/growth/enterprise), addon purchase emails, cancellation emails, renewal reminders, trial-expiring nudges, plus the website pricing/licensing pages they reference. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#1750 β€” ✨ Email confirming subscription tier (open) +- SSWConsulting/SSW.YakShaver#1751 β€” ✨ Email confirming addon purchase (open) +- SSWConsulting/SSW.YakShaver#1752 β€” ✨ Email confirming cancellation (open) +- SSWConsulting/SSW.YakShaver#1756 β€” ✨ Email subscription renewal reminder (open) +- SSWConsulting/SSW.YakShaver#1757 β€” ✨ Email Trial expiring β†’ user will be charged (open) +- SSWConsulting/SSW.YakShaver#317 β€” ✨ Website - Add a licencing model (closed) +- SSWConsulting/SSW.YakShaver#716 β€” ✨ Add pricing model to website (closed) +- SSWConsulting/SSW.YakShaver#787 β€” ✨ Website - Create Pricing Page Component (closed) + +### 🌐 8. Public/Free YakShaver & Browser Extension β€” `product: shared` (extension + portal) + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#2308 β€” πŸš€ Free YakShaver for Public Use - MVP + +**Scope:** The free/public tier: GitHub-login authentication for public users, public-mode behavior (no name recognition), watermark (static + dynamic), public YouTube hosting, browser extension with GitHub login, retry/upload UX, public/private repo status display. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#2313 β€” ✨ Public - Users not supported, ignore names in video (open) +- SSWConsulting/SSW.YakShaver#2314 β€” ✨ Public - Notifications - 1-way email (open) +- SSWConsulting/SSW.YakShaver#2315 β€” ✨ Public - Watermark - Dynamic Video branding (open) +- SSWConsulting/SSW.YakShaver#2324 β€” ✨ Public - Open Source GitHub Repo + YouTube free (open) +- SSWConsulting/SSW.YakShaver#2440 β€” ✨ Public - Enhance Browser Extension Auth and UX (open) +- SSWConsulting/SSW.YakShaver#2702 β€” ♻️ Extension - Integrate "ExecutePromptCommand" (open) +- SSWConsulting/SSW.YakShaver#2708 β€” πŸ› Retry Feature Fails for Videos from Chrome Extension (open) +- SSWConsulting/SSW.YakShaver#2744 β€” ✨ Display Public/Private Status of Repositories (open) +- SSWConsulting/SSW.YakShaver#1971 β€” ✨ Public - Watermark - Static Video branding (closed) +- SSWConsulting/SSW.YakShaver#2310 β€” ✨ Public - Authentication - FREE, GitHub login (closed) +- SSWConsulting/SSW.YakShaver#2311 β€” ✨ Public - Projects - supply list of public repos (closed) +- SSWConsulting/SSW.YakShaver#2312 β€” ✨ Public - Video Host - YouTube Channel (closed) +- SSWConsulting/SSW.YakShaver#2322 β€” ✨ Public - Spike Browser Extension GitHub Auth (closed) +- SSWConsulting/SSW.YakShaver#2542 β€” ✨ Update Wizard for GitHub Users (closed) +- SSWConsulting/SSW.YakShaver#2543 β€” ✨ Support GitHub Users in Video Endpoint (closed) +- SSWConsulting/SSW.YakShaver#2544 β€” ✨ Chrome Extension - Add GitHub Login UI (closed) +- SSWConsulting/SSW.YakShaver#2563 β€” ✨ Watermark - Push message to service bus (closed) +- SSWConsulting/SSW.YakShaver#2668 β€” πŸ› Browser ext - Fix uploads for GitHub auth (closed) +- SSWConsulting/SSW.YakShaver#2684 β€” ✨ Watermark - Container App processing logic (closed) +- SSWConsulting/SSW.YakShaver#2703 β€” πŸ’„ Extension - Improve UI (closed) + +### 🌎 9. Chinafy / i18n β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#2550 β€” 🌎 YakShaver Chinafy + +**Scope:** China-specific support: Chinese-language portal, Chinese chrome extension (Tencent/Baidu/Alibaba browsers), BiliBili video host provider, Chinese-language PBI generation. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#2606 β€” 🌎 ✨ Chinese browser extension (Tencent/Baidu/Alibaba) (open) +- SSWConsulting/SSW.YakShaver#2619 β€” 🌎 ✨ Chinese Language Support to Portal (open) +- SSWConsulting/SSW.YakShaver#2631 β€” 🌎 ✨ BiliBili Part 2 - test BiliBiliVideoHostProvider (open) +- SSWConsulting/SSW.YakShaver#2548 β€” 🌎 Draft v2 PowerPoint Presentation (closed) +- SSWConsulting/SSW.YakShaver#2592 β€” 🌎 ✨ Chinese Language Support for PBI Generation (closed) +- SSWConsulting/SSW.YakShaver#2593 β€” 🌎 πŸ› Chrome Extension Not Working (closed) +- SSWConsulting/SSW.YakShaver#2594 β€” 🌎 ✨ Integrate GitHub Login (Staging only) (closed) +- SSWConsulting/SSW.YakShaver#2595 β€” 🌎 ✨ BiliBili Part 1 - Implement provider (closed) +- SSWConsulting/SSW.YakShaver#2596 β€” 🌎 ✨ Alternative Chrome Extension Download instructions for China (closed) +- SSWConsulting/SSW.YakShaver#2597 β€” 🌎 ✨ Chinese Language to Chrome Extension (closed) +- SSWConsulting/SSW.YakShaver#2598 β€” 🌎 ✨ Chinese Language to YakShaver.ai (closed) + +### πŸ”— 10. External Work Item Systems Integration β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#633 β€” πŸš€ Work item systems other than GitHub and AzDO + +**Scope:** Adding support for Linear, Jira, Zendesk, and other work-item repositories beyond GitHub/Azure DevOps. Includes shared API endpoint plumbing. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#1767 β€” ✨ Integrate YakShaver with Linear (open) +- SSWConsulting/SSW.YakShaver#510 β€” ✨ Integrate Zendesk Ticket Creation (open) +- SSWConsulting/SSW.YakShaver#562 β€” ✨ Integrate YakShaver with Jira (open) β€” *flagged in Section 2* +- SSWConsulting/SSW.YakShaver#593 β€” πŸš€ Gold Plating | API Endpoints (open) +- SSWConsulting/SSW.YakShaver#549 β€” ✨ Duplicates - Scan existing backlog before creating PBI (closed) + +### 🌐 11. Marketing Website & SEO β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#694 β€” πŸš€ CX - Website, pricing, etc. +- SSWConsulting/SSW.YakShaver#2441 β€” ✨ Website - Improve SEO using Semrush + +**Scope:** YakShaver.ai marketing site β€” pages, components (hero/CTAs/FAQ/footer/blog), tagline, logo, mobile responsiveness, plus SEO improvements (Semrush-driven schema, keywords, backlinks, readability). + +**PBIs:** +- SSWConsulting/SSW.YakShaver#519 β€” ✨ Website - Onboarding experience (open) +- SSWConsulting/SSW.YakShaver#616 β€” ✨ YakShaver.ai - SEO Review (open) +- SSWConsulting/SSW.YakShaver#635 β€” ✨ YouTube Channel - Upload Previous Videos (open) +- SSWConsulting/SSW.YakShaver#714 β€” ✨ Improve UX for YakShaver Plugin Activation (open) +- SSWConsulting/SSW.YakShaver#793 β€” ✨ Bring Your Own Infrastructure CX (open) +- SSWConsulting/SSW.YakShaver#2443 β€” ✨ Website - List google result as Software Application (open) +- SSWConsulting/SSW.YakShaver#2444 β€” ✨ Website - Google video embed initial render (open) +- SSWConsulting/SSW.YakShaver#2445 β€” ✨ Website - Flesch-Kincaid readability (open) +- SSWConsulting/SSW.YakShaver#2446 β€” ✨ Website - Include related keywords (open) +- SSWConsulting/SSW.YakShaver#2447 β€” ✨ Website - Obtain high quality backlinks (open) +- SSWConsulting/SSW.YakShaver#2448 β€” ✨ Website - Include more written content (open) +- SSWConsulting/SSW.YakShaver#1041 β€” πŸ“ Website - blog .mdx format (closed) +- SSWConsulting/SSW.YakShaver#1259 β€” ✨ Website - everyone can edit (closed) +- SSWConsulting/SSW.YakShaver#1276 β€” ✨ Update Default Video to Short (closed) +- SSWConsulting/SSW.YakShaver#500 β€” πŸ“ Docs - How to use YakShaver (closed) +- SSWConsulting/SSW.YakShaver#547 β€” πŸ“ SugarLearning Onboard SSW Users (closed) +- SSWConsulting/SSW.YakShaver#548 β€” ✨ Teams Bot - Improve Welcome Message (closed) +- SSWConsulting/SSW.YakShaver#672 β€” ♻️ Product Page - Move to TinaCMS (closed) +- SSWConsulting/SSW.YakShaver#702 β€” πŸ’„ Marketing Page - UX Research (closed) +- SSWConsulting/SSW.YakShaver#703 β€” πŸ’„ Marketing Page - Design Mockups (closed) +- SSWConsulting/SSW.YakShaver#751 β€” ✨ Update Logo Icon (closed) +- SSWConsulting/SSW.YakShaver#778 β€” ✨ Reusable Hero Section Component (closed) +- SSWConsulting/SSW.YakShaver#779 β€” ✨ Implement CTAs (closed) +- SSWConsulting/SSW.YakShaver#780 β€” ✨ Tabbed Feature Section (closed) +- SSWConsulting/SSW.YakShaver#781 β€” ✨ Video Support to About Us (closed) +- SSWConsulting/SSW.YakShaver#782 β€” ✨ Mobile Responsive (closed) +- SSWConsulting/SSW.YakShaver#783 β€” ✨ Blog Inner Page (closed) +- SSWConsulting/SSW.YakShaver#784 β€” ✨ FAQs Component (closed) +- SSWConsulting/SSW.YakShaver#785 β€” ✨ Reusable Banner Component (closed) +- SSWConsulting/SSW.YakShaver#786 β€” ✨ Footer with Copyright + Social (closed) +- SSWConsulting/SSW.YakShaver#788 β€” ✨ Blog Index Page (closed) +- SSWConsulting/SSW.YakShaver#789 β€” ✨ Sticky Navbar (closed) +- SSWConsulting/SSW.YakShaver#810 β€” ✨ Update YakShaver Tagline (closed) +- SSWConsulting/SSW.YakShaver#813 β€” πŸ’„ Product Page - Inter webfont (closed) +- SSWConsulting/SSW.YakShaver#2442 β€” ✨ Use keywords in titles (closed) + +### πŸ’¬ 12. Teams Bot β€” UX, Disambiguation & PBI Quality β€” `product: teams` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#584 β€” πŸš€ Improvements asked for by SSW staff (MVP) +- SSWConsulting/SSW.YakShaver#720 β€” πŸš€ Improvements asked for by SSW staff (nice to have) + +**Scope:** The Teams bot experience β€” disambiguation cards, prompt-quality fixes, OpenAI prompt tuning, Teams UI affordances (cancel buttons, mention/CC, leaderboard cards, recording status, disambiguation drop-downs, "Move To" buttons), PBI/email format quality, name/project spelling, and small staff-driven workflow improvements. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#846 β€” ✨ Teams App - Exclude YakShaver User from Search Results (open) +- SSWConsulting/SSW.YakShaver#5 β€” ✨ Azure DevOps - custom templates per client (open) +- SSWConsulting/SSW.YakShaver#623 β€” ✨ Naughty Words Filter (open) β€” *flagged in Section 2* +- SSWConsulting/SSW.YakShaver#647 β€” ✨ What happens when you turn it off mid-shave? (open) +- SSWConsulting/SSW.YakShaver#67 β€” ✨ Ask for a Followupthen (open) +- SSWConsulting/SSW.YakShaver#683 β€” ✨ Add 'Start Yakking' Button in Chat Window (open) +- SSWConsulting/SSW.YakShaver#70 β€” ✨ GitHub - Fetch issue templates (open) +- SSWConsulting/SSW.YakShaver#704 β€” ✨ Teams Bot UI - Remove "Present Now" + improve View Prompt (open) +- SSWConsulting/SSW.YakShaver#84 β€” ✨ Done emails - Sent by YakShaver (open) +- SSWConsulting/SSW.YakShaver#980 β€” ✨ Coaching Feedback Feature for Video Submissions (open) +- SSWConsulting/SSW.YakShaver#1135 β€” ✨ Add Link to Video in Notification (closed) +- SSWConsulting/SSW.YakShaver#357 β€” ✨ Implement Name Search with CRM fuzzy search (closed) +- SSWConsulting/SSW.YakShaver#464 β€” ✨ Set up YakShaver themselves on teams via bot (closed) +- SSWConsulting/SSW.YakShaver#487 β€” ✨ Video title should not be a GUID (closed) +- SSWConsulting/SSW.YakShaver#511 β€” ✨ Email and PBI prompts - implementation (closed) +- SSWConsulting/SSW.YakShaver#523 β€” ✨ Email generation - iterate (closed) +- SSWConsulting/SSW.YakShaver#532 β€” ✨ Ignore Historical Projects (closed) +- SSWConsulting/SSW.YakShaver#534 β€” ✨ Update Email Notification Card (closed) +- SSWConsulting/SSW.YakShaver#553 β€” ✨ Disambiguation - Cancel button (closed) +- SSWConsulting/SSW.YakShaver#563 β€” πŸ› Disambiguation - Blank Drop-Down (closed) +- SSWConsulting/SSW.YakShaver#567 β€” ✨ Email HTML formating (closed) +- SSWConsulting/SSW.YakShaver#577 β€” πŸ› OpenAI Prompt - Invalid JSON in Prod (closed) *(note: 577 is shared with cluster 6 by number, distinct title here)* +- SSWConsulting/SSW.YakShaver#580 β€” ♻️ Disambiguation Drop-Down (closed) +- SSWConsulting/SSW.YakShaver#581 β€” πŸ› OK Button error (closed) +- SSWConsulting/SSW.YakShaver#582 β€” ✨ CC/@mention for Teams call participants (closed) +- SSWConsulting/SSW.YakShaver#585 β€” πŸ§ͺ Canary Deployment - Mark /10 (closed) +- SSWConsulting/SSW.YakShaver#589 β€” ♻️ Improve Initial Decision Prompt (closed) +- SSWConsulting/SSW.YakShaver#591 β€” ♻️ Improve Email Task Style (closed) +- SSWConsulting/SSW.YakShaver#594 β€” πŸ‘· DevOps - Update Purchase Budget (closed) +- SSWConsulting/SSW.YakShaver#599 β€” ✨ Email Card Readability + CC Field (closed) +- SSWConsulting/SSW.YakShaver#608 β€” ✨ Structured JSON Outputs for OpenAI (closed) +- SSWConsulting/SSW.YakShaver#609 β€” ✨ Combine Toggle for Private and Channel Calls (closed) +- SSWConsulting/SSW.YakShaver#624 β€” ✨ User Notification for Recording Processing (closed) +- SSWConsulting/SSW.YakShaver#629 β€” ♻️ Azure DevOps PBI - Formatting (closed) +- SSWConsulting/SSW.YakShaver#638 β€” ♻️ Improve Toggle Messages (closed) +- SSWConsulting/SSW.YakShaver#639 β€” πŸ› Couldn't figure out who "myself" is (closed) +- SSWConsulting/SSW.YakShaver#657 β€” ✨ Explicit Assignment Logic for PBIs (closed) +- SSWConsulting/SSW.YakShaver#664 β€” ✨ Add 'Move To' Button (closed) +- SSWConsulting/SSW.YakShaver#680 β€” πŸ› PBI description after selected project (closed) +- SSWConsulting/SSW.YakShaver#685 β€” ✨ Gamification - Leaderboard card (closed) +- SSWConsulting/SSW.YakShaver#690 β€” πŸ› CodingChallenge - sending email not PBI (closed) +- SSWConsulting/SSW.YakShaver#701 β€” ✨ Disambiguation - "Cancel" clearer (closed) +- SSWConsulting/SSW.YakShaver#705 β€” ✨ Updating welcome message (closed) +- SSWConsulting/SSW.YakShaver#749 β€” πŸ› Mentioning the wrong Users (closed) +- SSWConsulting/SSW.YakShaver#750 β€” ✨ Correct Spelling of Project Names (closed) +- SSWConsulting/SSW.YakShaver#768 β€” πŸ› Recording boot delay β†’ incomplete prompt (closed) +- SSWConsulting/SSW.YakShaver#804 β€” ✨ Notify User When YakShaver is Ready (closed) +- SSWConsulting/SSW.YakShaver#817 β€” πŸ› YakShaver not working on production (closed) +- SSWConsulting/SSW.YakShaver#818 β€” ✨ Inject Project List as Context (closed) +- SSWConsulting/SSW.YakShaver#820 β€” πŸ› Expected an Email but PBI Was Created (closed) +- SSWConsulting/SSW.YakShaver#855 β€” πŸ’„ UX: Improve MoveTo (closed) +- SSWConsulting/SSW.YakShaver#858 β€” ✨ Name Correction and User Notification (closed) +- SSWConsulting/SSW.YakShaver#938 β€” ✨ 'Move Work Item' Button on Email Card (closed) +- SSWConsulting/SSW.YakShaver#1022 β€” ✨ Whisper API File Size Limit (closed) +- SSWConsulting/SSW.YakShaver#1193 β€” ✨ GitHub Integration 🟩 (closed) +- SSWConsulting/SSW.YakShaver#282 β€” ✨ Sprint Review/Forecast emails (closed) +- SSWConsulting/SSW.YakShaver#328 β€” ✨ Screenshot - send to ChatGPT for URL (closed) +- SSWConsulting/SSW.YakShaver#342 β€” ✨ Multiple iterations across orgs (closed) +- SSWConsulting/SSW.YakShaver#346 β€” ✨ Add Task Management to GitHub Issues (closed) +- SSWConsulting/SSW.YakShaver#426 β€” ✨ Correct Transcription for PBI/Email (closed) +- SSWConsulting/SSW.YakShaver#433 β€” ✨ Endpoint to Delete Test PBIs (closed) +- SSWConsulting/SSW.YakShaver#516 β€” ✨ Teams Bot Notification for Recording (closed) +- SSWConsulting/SSW.YakShaver#524 β€” ✨ Brainstorming discussions (closed) *(distinct from Desktop #524)* +- SSWConsulting/SSW.YakShaver#600 β€” πŸ› Video Rename Fails Slash (closed) +- SSWConsulting/SSW.YakShaver#620 β€” ✨ Cancel Command Recognition (closed) +- SSWConsulting/SSW.YakShaver#630 β€” ✨ Teams Bot UI - PBI Number (closed) *(distinct from #630 in cluster 6)* +- SSWConsulting/SSW.YakShaver#634 β€” ✨ YouTube Channel - Set up (closed) +- SSWConsulting/SSW.YakShaver#658 β€” ✨ Only Relevant People in PBIs (closed) +- SSWConsulting/SSW.YakShaver#663 β€” ✨ Remove Description Field (closed) +- SSWConsulting/SSW.YakShaver#671 β€” πŸ› Chinese β†’ email instead of PBI (closed) *(distinct from Desktop#671)* +- SSWConsulting/SSW.YakShaver#678 β€” ✨ Automate Daily Scrum Summary Email (closed) +- SSWConsulting/SSW.YakShaver#684 β€” ✨ Improve Confirmation Message when YakShaver On (closed) +- SSWConsulting/SSW.YakShaver#692 β€” ✨ Transcription Accuracy for Email CC (closed) +- SSWConsulting/SSW.YakShaver#709 β€” ✨ Enhance Screenshot Functionality (closed) +- SSWConsulting/SSW.YakShaver#710 β€” ✨ Enhance Contact Matching Logic (closed) +- SSWConsulting/SSW.YakShaver#742 β€” ✨ Leaderboard - timeframe (closed) +- SSWConsulting/SSW.YakShaver#744 β€” ✨ AI-Driven Approval System (closed) +- SSWConsulting/SSW.YakShaver#756 β€” ✨ Leaderboard - by projects (closed) +- SSWConsulting/SSW.YakShaver#764 β€” πŸ› Correct Spelling of Project Name (closed) +- SSWConsulting/SSW.YakShaver#802 β€” πŸ› Repeated Welcome Message (closed) +- SSWConsulting/SSW.YakShaver#805 β€” ✨ Prompt user for email recipient when unknown (closed) +- SSWConsulting/SSW.YakShaver#814 β€” ✨ Multi-video queue notification (closed) +- SSWConsulting/SSW.YakShaver#854 β€” πŸ•΅πŸ» Spike - UI/UX in Teams (closed) + +### πŸ“Š 13. Reporting & Analytics β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#2353 β€” πŸš€ Reporting and YakShaver Stats + +**Scope:** Usage analytics, project-inference confidence reporting, individual usage trends, log-analytics cost optimization. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#2354 β€” ♻️ Process Step logging - "MOVED TO" actions (open) +- SSWConsulting/SSW.YakShaver#2355 β€” ✨ Project Inference and confidence levels (open) +- SSWConsulting/SSW.YakShaver#2456 β€” ✨ Display trends on individual usage (open) +- SSWConsulting/SSW.YakShaver#2838 β€” ✨ Optimize Log Analytics Costs (closed) + +### πŸš€ 14. YakShaver 360 β€” Initial Build β€” `product: 360` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#3595 β€” πŸš€ YakShaver 360 - Frontend +- SSWConsulting/SSW.YakShaver#3596 β€” πŸš€ YakShaver 360 - Backend + +**Scope:** Bring up the YakShaver 360 product β€” initial Next.js frontend project setup, .NET backend project setup, and supporting infrastructure for both. This cluster supersedes the two parent epics, which are essentially containers for the same four PBIs. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#3604 β€” ✨ YakShaver 360 - Initial .NET Project Set Up (open) +- SSWConsulting/SSW.YakShaver#3605 β€” ✨ YakShaver 360 - Initial Next.js Project Set Up (open) +- SSWConsulting/SSW.YakShaver#3606 β€” ✨ YakShaver 360 - Backend Infrastructure (open) +- SSWConsulting/SSW.YakShaver#3607 β€” ✨ YakShaver 360 - Frontend Infrastructure (open) + +### 🏒 15. Client #1 Onboarding (Consulting Scenario) β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#2012 β€” πŸš€ Client #1 - Onboarding (Consulting Scenario) + +**Scope:** First paying-client onboarding scenario β€” using a consulting-engagement client's own SharePoint folder to host their videos. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#2017 β€” ✨ Client #1 - Video on Client's SharePoint folder (open) + +### ♻️ 16. Architecture Refactor β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#2188 β€” ♻️ SSW.YakShaver + +**Scope:** Cross-cutting backend refactors β€” currently only the marker-interface refactor of AdminDbContext / TenantDbContext. + +**PBIs:** +- SSWConsulting/SSW.YakShaver#2189 β€” ♻️ Refactor AdminDbContext and TenantDbContext to use Marker Interfaces (open) + +### 🏍️ 17. Performance Optimization β€” `product: shared` + +**Consolidates PBIs from:** +- SSWConsulting/SSW.YakShaver#3583 β€” ✨ 🏍️ Performance Optimization + +**Scope:** Empty epic β€” has 0 sub-issues. Either populate it with concrete perf-tuning PBIs (LLM cost, video processing throughput, portal cold-start) or close it as a placeholder. (uncertain β€” listed for transparency, also flagged in Section 3.) + +**PBIs:** _none_ + +--- + +## 2. Duplicate candidates (whole backlog) + +> Curated from a 875Γ—3089 title-similarity scan. Series patterns ("Sprint TBD video to YouTube", per-person "Canary Testing - ") and sequence pairs ("Phase N" vs "Phase N+1") were excluded as known templates, not duplicates. Each open issue here is on the left. + +| Open issue | Matched issue (state) | Reason | +|------------|------------------------|--------| +| SSWConsulting/SSW.YakShaver#3349 β€” ✨ Download Page - Dedicated OS-specific installer page | SSWConsulting/SSW.YakShaver.Desktop#492 β€” same title (open) | exact-title cross-repo dup; pick one home repo | +| SSWConsulting/SSW.YakShaver#3168 β€” ✨ Column Selector - Replace Pascal Case with Full Name | SSWConsulting/SSW.YakShaver#3167 β€” same title (closed) | exact title; appears already done in #3167 | +| SSWConsulting/SSW.YakShaver#3142 β€” πŸ› Bug - Video Duration Mismatch | SSWConsulting/SSW.YakShaver#1919 β€” same bug "in YakShaver" (closed) | same root bug, regression or stale dup of #1919/#2800 | +| SSWConsulting/SSW.YakShaver#3081 β€” ✨ Profile Icon Redundancy β†’ editable user details | SSWConsulting/SSW.YakShaver#3078 β€” same title (open) | exact-title dup of an open issue, merge | +| SSWConsulting/SSW.YakShaver#3080 β€” πŸ› Bug - User Details Update Issue | SSWConsulting/SSW.YakShaver#3079 β€” same title (open) | exact-title dup of an open issue, merge | +| SSWConsulting/SSW.YakShaver#3026 β€” ✨ UI Improvement - Add Hover State for Clickable Tools | SSWConsulting/SSW.YakShaver#3035 β€” same title (closed) | exact title; also matches #3032 closed β€” likely already done | +| SSWConsulting/SSW.YakShaver#2661 β€” ✨ Add clickable links for email work items (dashboard) | SSWConsulting/SSW.YakShaver#2646 β€” same scope (open) | tokens identical; merge | +| SSWConsulting/SSW.YakShaver#2623 β€” ✨ Exclude Non-YakShaver NuGet Packages from Code Coverage | SSWConsulting/SSW.YakShaver#2755 β€” same title (closed) | exact title β€” already shipped | +| SSWConsulting/SSW.YakShaver#2609 β€” ✨ Implement Health Check UI for YakShaver | SSWConsulting/SSW.YakShaver#3185 β€” `zzπŸ› Bug - YakShaver Health Check` (closed) | "zz" prefix typically marks deletion; verify if work was completed elsewhere | +| SSWConsulting/SSW.YakShaver#2364 β€” ✨ Add Project or Email Option for Admin Tasks | SSWConsulting/SSW.YakShaver#2363 β€” same title (closed) | exact title duplicate | +| SSWConsulting/SSW.YakShaver#2281 β€” πŸ“ Doc - Update FAQ or Troubleshoot guide with BOT issues | SSWConsulting/SSW.YakShaver#2258 β€” same title (closed) | exact title duplicate | +| SSWConsulting/SSW.YakShaver#2220 β€” πŸ› Bug - Video Upload Fails for Large Files | SSWConsulting/SSW.YakShaver#2218 β€” same title (closed) | exact title duplicate | +| SSWConsulting/SSW.YakShaver#2219 β€” ✨ Enhance Task Cancellation Detection in YakShaver | SSWConsulting/SSW.YakShaver#2217 β€” same title (closed) | exact title duplicate | +| SSWConsulting/SSW.YakShaver#2136 β€” ✨ Rename Developer Account in Partner Center | SSWConsulting/SSW.YakShaver#2135 β€” same title (closed) | exact title duplicate | +| SSWConsulting/SSW.YakShaver#1847 β€” ✨ Website - Implement Carousel for Recent Articles on Mobile | SSWConsulting/SSW.YakShaver#1846 β€” "Add Carousel..." (closed) | same scope β€” already shipped as #1846 | +| SSWConsulting/SSW.YakShaver#1583 β€” ✨#2 Cancelled video - add ❌ | SSWConsulting/SSW.YakShaver#1582 β€” ✨#1 same title (open) | "#1" and "#2" of the same idea β€” split or merge | +| SSWConsulting/SSW.YakShaver#1355 β€” ✨ Collect Email Addresses for Interested Visitors | SSWConsulting/SSW.YakShaver#1364 β€” same scope (closed) | also matches closed #1363 β€” work shipped already | +| SSWConsulting/SSW.YakShaver#1317 β€” πŸ“Š Trello - Integrate Trello Task Creation | SSWConsulting/SSW.YakShaver#1524 β€” "Integrate YakShaver with Trello" (closed) | also #2740, #2706 closed β€” Trello has been opened/closed multiple times; verify current state | +| SSWConsulting/SSW.YakShaver#1199 β€” ✨ Add ability to re-trigger failed or undetected videos | SSWConsulting/SSW.YakShaver#1201 β€” same title (closed) | exact title duplicate | +| SSWConsulting/SSW.YakShaver#1114 β€” ✨ Add Priority Specification for Work Items | SSWConsulting/SSW.YakShaver#1087 β€” same title (closed) | exact title duplicate | +| SSWConsulting/SSW.YakShaver#623 β€” ✨ Implement Naughty Words Filter for YakShaver | SSWConsulting/SSW.YakShaver#622 β€” same title (closed) | exact title duplicate; #623 is in-scope under epic #720 | +| SSWConsulting/SSW.YakShaver#95 β€” Add config valiation [sic] | SSWConsulting/SSW.YakShaver#96 β€” same typo title (closed) | exact title duplicate | +| SSWConsulting/SSW.YakShaver.Desktop#294 β€” Create dedicated GitHub App for MCP OAuth | SSWConsulting/SSW.YakShaver#3271 β€” same scope (closed) | cross-repo dup, work apparently shipped in #3271 | +| SSWConsulting/SSW.YakShaver#3586 β€” πŸ’„ Move "Delete Project" Button (references #3034) | SSWConsulting/SSW.YakShaver#3034 β€” same title (closed) | title explicitly references the closed #3034; verify and close | +| SSWConsulting/SSW.YakShaver#3524 β€” Decouple Graph-dependent backend services | SSWConsulting/SSW.YakShaver#3534 β€” "Phase 2: same title" (closed) | same scope; #3534 marked Phase-2 and closed | +| SSWConsulting/SSW.YakShaver#3523 β€” Model Entra/Graph capabilities explicitly | SSWConsulting/SSW.YakShaver#3533 β€” "Phase 1: same title" (closed) | same scope; #3533 marked Phase-1 and closed | +| SSWConsulting/SSW.YakShaver#3111 β€” ✨ Handling Duplicate PBIs | SSWConsulting/SSW.YakShaver#3076 β€” Improve Handling of Duplicate PBIs (open) | overlapping scope β€” pick one | +| SSWConsulting/SSW.YakShaver#2584 β€” ✨ Implement New Client Portal Design | SSWConsulting/SSW.YakShaver#1186 β€” Improve UI/UX Design (closed) | both reference the same portal redesign; verify if complete | +| SSWConsulting/SSW.YakShaver.Desktop#794 β€” Update Desktop App to use new YakShaver logo | SSWConsulting/SSW.YakShaver#3118 β€” ✨ YakShaver Desktop App Logo (closed) | cross-repo dup β€” logo work may have been completed in #3118 | +| SSWConsulting/SSW.YakShaver#3607 β€” ✨ YakShaver 360 - Frontend Infrastructure | SSWConsulting/SSW.YakShaver#3595 β€” πŸš€ YakShaver 360 - Frontend (open Epic) | sub-issue title equals parent epic title β€” restructure | +| SSWConsulting/SSW.YakShaver#3606 β€” ✨ YakShaver 360 - Backend Infrastructure | SSWConsulting/SSW.YakShaver#3596 β€” πŸš€ YakShaver 360 - Backend (open Epic) | sub-issue title equals parent epic title β€” restructure | +| SSWConsulting/SSW.YakShaver#3552 β€” ✨ Update toast notifications position to bottom center | SSWConsulting/SSW.YakShaver.Desktop#784 β€” same title (open) | cross-repo open↔open dup; pick one | +| SSWConsulting/SSW.YakShaver#3086 β€” ✨ Switch to GPT 5 Mini for Chat Completion | SSWConsulting/SSW.YakShaver#3286 β€” Switch to GPT 5.2 Chat (closed) | model already updated past 5 Mini | +| SSWConsulting/SSW.YakShaver#2391 β€” ✨ Update Mobile Menu Icon to Hamburger Style | SSWConsulting/SSW.YakShaver#2390 β€” Update Mobile Sidebar Icon to Hamburger Menu (closed) | same icon update β€” already shipped | +| SSWConsulting/SSW.YakShaver360#57 β€” πŸ” Spike - Chinafy YakShaver-360 investigation | SSWConsulting/SSW.YakShaver360#23 β€” ✨ Chinafy YakShaver-360 (open) | spike vs implementation β€” spike likely complete or merged | +| SSWConsulting/SSW.YakShaver#3256 β€” πŸ› Play button on MyShaves prompts sign-in despite Microsoft SSO | SSWConsulting/SSW.YakShaver#3255 β€” same bug, different wording (open) | same bug, two open tickets β€” merge | +| SSWConsulting/SSW.YakShaver.Desktop#499 β€” πŸ› (PROMPT TEST) Open tag green instead of red | SSWConsulting/SSW.YakShaver.Desktop#497 β€” same bug (closed) | likely a PROMPT-TEST artifact dup | +| SSWConsulting/SSW.YakShaver#2611 β€” πŸ› Error sending email + converting to work item | SSWConsulting/SSW.YakShaver#2171 β€” Error converting email to work item (closed) | also matches #1281, #2082 closed β€” verify if regression | +| SSWConsulting/SSW.YakShaver#1672 β€” ✨ Separate Production Deployment for AI Models | SSWConsulting/SSW.YakShaver#249 β€” πŸ‘· Production deployment (closed) | likely stale; verify if still relevant | +| SSWConsulting/SSW.YakShaver#1253 β€” ✨ Teams App - Add 'projects' command | SSWConsulting/SSW.YakShaver#1260 β€” Add 'projects' command (closed) | exact-token dup | +| SSWConsulting/SSW.YakShaver#562 β€” ✨ Integrate YakShaver with Jira | SSWConsulting/SSW.YakShaver#2178 β€” 🧠 Spike: Integrate Jira (closed) | spike done; #562 is the implementation β€” keep #562, dup is just historical | + +--- + +## 3. Source epics suggested for close or archive + +| Epic | Reason | +|------|--------| +| SSWConsulting/SSW.YakShaver#498 β€” πŸš€ People data sources other than Dynamics | 100% complete (1/1) | +| SSWConsulting/SSW.YakShaver#717 β€” πŸš€ Project data sources other than Dynamics | 100% complete (1/1) | +| SSWConsulting/SSW.YakShaver#721 β€” πŸš€ Transcript data source other than Teams or Snagit | 100% complete (6/6) | +| SSWConsulting/SSW.YakShaver#2011 β€” πŸš€ SSW Identity Server Integration | 100% complete (1/1); body is now superseded by #3494 Auth Migration | +| SSWConsulting/SSW.YakShaver#2019 β€” πŸš€ Stripe Integration + Release to Waitlist (MVP) | 100% complete (19/19); MVP shipped | +| SSWConsulting/SSW.YakShaver#2097 β€” πŸš€ Full Stripe Integration | 100% complete (2/2) | +| SSWConsulting/SSW.YakShaver#3583 β€” ✨ 🏍️ Performance Optimization | Empty epic (0/0). Either populate concrete perf PBIs into proposed cluster 17 or close as placeholder | +| SSWConsulting/SSW.YakShaver#3595 β€” πŸš€ YakShaver 360 - Frontend | Thin (0/2 progress, 2 PBIs). Both PBIs (#3605, #3607) absorbed into proposed cluster 14 *YakShaver 360 β€” Initial Build*. Consider merging this epic into 360 | +| SSWConsulting/SSW.YakShaver#3596 β€” πŸš€ YakShaver 360 - Backend | Thin (0/2, same situation as #3595). #3604, #3606 absorbed into proposed cluster 14. Consider merging | + +--- + +## 4. Epics skipped (already 100% complete) + +- SSWConsulting/SSW.YakShaver#498 β€” πŸš€ People data sources other than Dynamics - Excel file, Active Directory, Google Cloud, etc. (1/1) +- SSWConsulting/SSW.YakShaver#717 β€” πŸš€ Project data sources other than Dynamics - Excel file, Salesforce, etc. (1/1) +- SSWConsulting/SSW.YakShaver#721 β€” πŸš€ Transcript data source other than Teams or Snagit - CustomGPT, Loom, OBS, etc. (6/6) +- SSWConsulting/SSW.YakShaver#2011 β€” πŸš€ SSW Identity Server Integration (1/1) +- SSWConsulting/SSW.YakShaver#2019 β€” πŸš€ Stripe Integration + Release to Waitlist (MVP) (19/19) +- SSWConsulting/SSW.YakShaver#2097 β€” πŸš€ Full Stripe Integration (2/2) + +--- + +## 5. βœ… Reviewer checklist (verification only β€” do not act on GitHub yet) + +- [ ] Section 0 methodology counts look plausible (re-run a sample query manually if uncertain β€” try `gh issue list -R SSWConsulting/SSW.YakShaver --state all --limit 1 --json number` and compare to the largest issue number recorded) +- [ ] Section 1 clusters look right β€” pay particular attention to the boundary between **Cluster 1 (Agent 2.0 core pipeline)**, **Cluster 2 (Desktop App distribution/UX)**, and **Cluster 3 (MCP plumbing)**, since #2811 sub-issues span all three +- [ ] Section 2 duplicates are genuine β€” the open↔open pairs (3081/3078, 3080/3079, 3552/Desktop#784, 3349/Desktop#492, 3256/3255, 1582/1583, 2661/2646, 3111/3076, 360#57/360#23) are the highest-leverage to act on +- [ ] Section 3 close candidates are correct β€” verify the 100%-complete epics have no recently re-opened sub-issues that flipped completion back below 100%; #3583 is intentionally empty (0/0) +- [ ] Section 4 skipped epics are genuinely 100% complete + +> Once you've validated the audit across a few runs, run the analogous execute workflow (or future command) to act on this plan.