From 06b5b3e7974148ed72ed1c7debbc82975512edb1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:28:12 +0000 Subject: [PATCH 1/2] Initial plan From 35c22075e0949b245df5f363f33ac48b17e99d3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:43:21 +0000 Subject: [PATCH 2/2] feat(community): track external contributor issues as contributions, store in wiki (#22174) - Add stateReason field to gh issue list query in shared/community-attribution.md - Add Tier 0 attribution for issues closed as COMPLETED (direct issue contributions) - Update Tier 1/2 to skip Tier 0 issues, Tier 4 to exclude Tier 0 from ambiguous candidates - Add direct issue output type in shared attribution output sections - Add repo-memory wiki: true to daily-community-attribution.md for wiki persistence - Add Step 2 to manage Community Contributors wiki page - Update Step 3 README table with direct issue column support - Update Step 5 PR creation to trigger on README or wiki changes - Recompile lock file with make recompile Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/f1a0bf6c-eaee-4b75-93cc-1d5ae990ebf2 --- .../daily-community-attribution.lock.yml | 123 +++++++++++++++++- .../workflows/daily-community-attribution.md | 84 +++++++++--- .github/workflows/release.lock.yml | 4 +- .../workflows/shared/community-attribution.md | 50 +++++-- pkg/workflow/schemas/github-workflow.json | 23 +++- 5 files changed, 241 insertions(+), 43 deletions(-) diff --git a/.github/workflows/daily-community-attribution.lock.yml b/.github/workflows/daily-community-attribution.lock.yml index a963681925a..d5a7e70cd94 100644 --- a/.github/workflows/daily-community-attribution.lock.yml +++ b/.github/workflows/daily-community-attribution.lock.yml @@ -20,13 +20,13 @@ # # For more information: https://github.github.com/gh-aw/introduction/overview/ # -# Maintains a live community contributions section in README.md by attributing all community-labeled issues to merged PRs using the four-tier attribution strategy +# Maintains a live community contributions section in README.md and an all-time Community Contributors wiki page by attributing all community-labeled issues using the five-tier attribution strategy # # Resolved workflow manifest: # Imports: # - shared/community-attribution.md # -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"53af59b5c774f5d29c12f82bf589b92b3fa7647df4de1d65c074b870ad31d6ce","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"190cde7100706396b88cf3d533802f63d25abd8d8879f6dd8c7b3e833873d006","strict":true,"agent_id":"copilot"} name: "Daily Community Attribution Updater" "on": @@ -135,6 +135,7 @@ jobs: 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/repo_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" cat << 'GH_AW_PROMPT_EOF' @@ -205,6 +206,12 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_MEMORY_BRANCH_NAME: 'master' + GH_AW_MEMORY_CONSTRAINTS: "\n\n**Constraints:**\n- **Max File Size**: 10240 bytes (0.01 MB) per file\n- **Max File Count**: 100 files per commit\n- **Max Patch Size**: 10240 bytes (10 KB) total per push (max: 100 KB)\n" + GH_AW_MEMORY_DESCRIPTION: ' All-time Community Contributors list' + GH_AW_MEMORY_DIR: '/tmp/gh-aw/repo-memory/default/' + GH_AW_MEMORY_TARGET_REPO: ' of the current repository' + GH_AW_WIKI_NOTE: "\n\n> **GitHub Wiki**: This memory is backed by the GitHub Wiki for this repository. Files use GitHub Wiki Markdown syntax. Follow GitHub Wiki conventions when creating or editing pages (e.g., use standard Markdown headers, use `[[Page Name]]` syntax for internal wiki links, name page files with spaces replaced by hyphens or use the wiki page title as the filename)." with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -223,7 +230,13 @@ 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_MEMORY_BRANCH_NAME: process.env.GH_AW_MEMORY_BRANCH_NAME, + GH_AW_MEMORY_CONSTRAINTS: process.env.GH_AW_MEMORY_CONSTRAINTS, + GH_AW_MEMORY_DESCRIPTION: process.env.GH_AW_MEMORY_DESCRIPTION, + GH_AW_MEMORY_DIR: process.env.GH_AW_MEMORY_DIR, + GH_AW_MEMORY_TARGET_REPO: process.env.GH_AW_MEMORY_TARGET_REPO, + GH_AW_WIKI_NOTE: process.env.GH_AW_WIKI_NOTE } }); - name: Validate prompt placeholders @@ -300,13 +313,23 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} name: Fetch community-labeled issues - run: "mkdir -p /tmp/gh-aw/community-data\n\n# The \"community\" label is the **primary attribution signal**: a maintainer\n# explicitly tagged the issue as community-authored, making it a strong and\n# intentional marker that does not rely on free-text heuristics.\necho \"Fetching issues with 'community' label (primary attribution signal)...\"\nif ! gh issue list \\\n --label \"community\" \\\n --state all \\\n --limit 500 \\\n --json number,title,author,labels,closedAt,createdAt,url \\\n > /tmp/gh-aw/community-data/community_issues.json; then\n echo \"[]\" > /tmp/gh-aw/community-data/community_issues.json\nfi\n\nCOMMUNITY_COUNT=$(jq length \"/tmp/gh-aw/community-data/community_issues.json\")\necho \"✓ Fetched $COMMUNITY_COUNT community-labeled issues\"\necho \" Data: /tmp/gh-aw/community-data/community_issues.json\"" + run: "mkdir -p /tmp/gh-aw/community-data\n\n# The \"community\" label is the **primary attribution signal**: a maintainer\n# explicitly tagged the issue as community-authored, making it a strong and\n# intentional marker that does not rely on free-text heuristics.\necho \"Fetching issues with 'community' label (primary attribution signal)...\"\nif ! gh issue list \\\n --label \"community\" \\\n --state all \\\n --limit 500 \\\n --json number,title,author,labels,closedAt,createdAt,url,stateReason \\\n > /tmp/gh-aw/community-data/community_issues.json; then\n echo \"[]\" > /tmp/gh-aw/community-data/community_issues.json\nfi\n\nCOMMUNITY_COUNT=$(jq length \"/tmp/gh-aw/community-data/community_issues.json\")\necho \"✓ Fetched $COMMUNITY_COUNT community-labeled issues\"\necho \" Data: /tmp/gh-aw/community-data/community_issues.json\"" - env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} name: Fetch PR data for attribution index - run: "mkdir -p /tmp/gh-aw/community-data\n\n# Fetch merged PRs from the last 90 days (wide enough to catch any recently attributed issue)\nSINCE=$(date -d '90 days ago' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \\\n || date -v-90d '+%Y-%m-%dT%H:%M:%SZ')\n\necho \"Fetching PRs merged since $SINCE...\"\ngh pr list \\\n --state merged \\\n --limit 500 \\\n --json number,title,author,mergedAt,url,body,closingIssuesReferences \\\n --jq \"[.[] | select(.mergedAt >= \\\"$SINCE\\\")]\" \\\n > /tmp/gh-aw/community-data/pull_requests.json \\\n || echo \"[]\" > /tmp/gh-aw/community-data/pull_requests.json\n\nPR_COUNT=$(jq length /tmp/gh-aw/community-data/pull_requests.json)\necho \"✓ Fetched $PR_COUNT merged PRs\"\n\n# Build closing references index: {issue_number: [pr_numbers]}\njq '\n reduce .[] as $pr (\n {};\n $pr.closingIssuesReferences[]? as $issue |\n ($issue.number | tostring) as $key |\n .[$key] = (.[$key] // []) + [$pr.number]\n )\n' /tmp/gh-aw/community-data/pull_requests.json \\\n > /tmp/gh-aw/community-data/closing_refs_by_issue.json 2>/dev/null \\\n || echo \"{}\" > /tmp/gh-aw/community-data/closing_refs_by_issue.json\n\nLINK_COUNT=$(jq 'keys | length' /tmp/gh-aw/community-data/closing_refs_by_issue.json)\necho \"✓ Built closing refs index: $LINK_COUNT issues with native GitHub close links\"\n\n# Find community issues closed within the PR lookback window (attribution candidates)\njq --arg since \"$SINCE\" \\\n '[.[] | select(.closedAt != null and .closedAt >= $since)]' \\\n /tmp/gh-aw/community-data/community_issues.json \\\n > /tmp/gh-aw/community-data/community_issues_closed_in_window.json 2>/dev/null \\\n || echo \"[]\" > /tmp/gh-aw/community-data/community_issues_closed_in_window.json\n\nCLOSED_COUNT=$(jq length /tmp/gh-aw/community-data/community_issues_closed_in_window.json)\necho \"✓ Found $CLOSED_COUNT community issues closed in the lookback window\"\n\necho \"\"\necho \"Data available in /tmp/gh-aw/community-data/:\"\necho \" community_issues.json — all community-labeled issues\"\necho \" pull_requests.json — merged PRs (last 90 days)\"\necho \" closing_refs_by_issue.json — native GitHub close links\"\necho \" community_issues_closed_in_window.json — closed during lookback\"" + run: "mkdir -p /tmp/gh-aw/community-data\n\n# Fetch merged PRs from the last 90 days (wide enough to catch any recently attributed issue)\nSINCE=$(date -d '90 days ago' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \\\n || date -v-90d '+%Y-%m-%dT%H:%M:%SZ')\n\necho \"Fetching PRs merged since $SINCE...\"\ngh pr list \\\n --state merged \\\n --limit 500 \\\n --json number,title,author,mergedAt,url,body,closingIssuesReferences \\\n --jq \"[.[] | select(.mergedAt >= \\\"$SINCE\\\")]\" \\\n > /tmp/gh-aw/community-data/pull_requests.json \\\n || echo \"[]\" > /tmp/gh-aw/community-data/pull_requests.json\n\nPR_COUNT=$(jq length /tmp/gh-aw/community-data/pull_requests.json)\necho \"✓ Fetched $PR_COUNT merged PRs\"\n\n# Build closing references index: {issue_number: [pr_numbers]}\njq '\n reduce .[] as $pr (\n {};\n $pr.closingIssuesReferences[]? as $issue |\n ($issue.number | tostring) as $key |\n .[$key] = (.[$key] // []) + [$pr.number]\n )\n' /tmp/gh-aw/community-data/pull_requests.json \\\n > /tmp/gh-aw/community-data/closing_refs_by_issue.json 2>/dev/null \\\n || echo \"{}\" > /tmp/gh-aw/community-data/closing_refs_by_issue.json\n\nLINK_COUNT=$(jq 'keys | length' /tmp/gh-aw/community-data/closing_refs_by_issue.json)\necho \"✓ Built closing refs index: $LINK_COUNT issues with native GitHub close links\"\n\n# Find community issues closed within the PR lookback window (attribution candidates)\njq --arg since \"$SINCE\" \\\n '[.[] | select(.closedAt != null and .closedAt >= $since)]' \\\n /tmp/gh-aw/community-data/community_issues.json \\\n > /tmp/gh-aw/community-data/community_issues_closed_in_window.json 2>/dev/null \\\n || echo \"[]\" > /tmp/gh-aw/community-data/community_issues_closed_in_window.json\n\nCLOSED_COUNT=$(jq length /tmp/gh-aw/community-data/community_issues_closed_in_window.json)\necho \"✓ Found $CLOSED_COUNT community issues closed in the lookback window\"\n\necho \"\"\necho \"Data available in /tmp/gh-aw/community-data/:\"\necho \" community_issues.json — all community-labeled issues (includes stateReason)\"\necho \" pull_requests.json — merged PRs (last 90 days)\"\necho \" closing_refs_by_issue.json — native GitHub close links\"\necho \" community_issues_closed_in_window.json — closed during lookback\"" + # Repo memory git-based storage configuration from frontmatter processed below + - name: Clone wiki-memory branch (default) + env: + GH_TOKEN: ${{ github.token }} + GITHUB_SERVER_URL: ${{ github.server_url }} + BRANCH_NAME: master + TARGET_REPO: ${{ github.repository }}.wiki + MEMORY_DIR: /tmp/gh-aw/repo-memory/default + CREATE_ORPHAN: false + run: bash ${RUNNER_TEMP}/gh-aw/actions/clone_repo_memory_branch.sh - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -357,7 +380,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_pull_request":{"draft":true,"expires":24,"max":1,"title_prefix":"[community] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_pull_request":{"draft":true,"expires":24,"max":1,"title_prefix":"[community] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF - name: Write Safe Outputs Tools run: | @@ -763,6 +786,15 @@ jobs: else echo 'AWF binary not installed, skipping firewall log summary' fi + # Upload repo memory as artifacts for push job + - name: Upload wiki-memory artifact (default) + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: repo-memory-default + path: /tmp/gh-aw/repo-memory/default + retention-days: 1 + if-no-files-found: ignore - name: Upload agent artifacts if: always() continue-on-error: true @@ -818,7 +850,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: WORKFLOW_NAME: "Daily Community Attribution Updater" - WORKFLOW_DESCRIPTION: "Maintains a live community contributions section in README.md by attributing all community-labeled issues to merged PRs using the four-tier attribution strategy" + WORKFLOW_DESCRIPTION: "Maintains a live community contributions section in README.md and an all-time Community Contributors wiki page by attributing all community-labeled issues using the five-tier attribution strategy" HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} with: script: | @@ -910,6 +942,7 @@ jobs: needs: - activation - agent + - push_repo_memory - safe_outputs if: always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true') runs-on: ubuntu-slim @@ -992,6 +1025,10 @@ jobs: GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} + GH_AW_PUSH_REPO_MEMORY_RESULT: ${{ needs.push_repo_memory.result }} + GH_AW_REPO_MEMORY_VALIDATION_FAILED_default: ${{ needs.push_repo_memory.outputs.validation_failed_default }} + GH_AW_REPO_MEMORY_VALIDATION_ERROR_default: ${{ needs.push_repo_memory.outputs.validation_error_default }} + GH_AW_REPO_MEMORY_PATCH_SIZE_EXCEEDED_default: ${{ needs.push_repo_memory.outputs.patch_size_exceeded_default }} GH_AW_GROUP_REPORTS: "false" GH_AW_FAILURE_REPORT_AS_ISSUE: "true" GH_AW_TIMEOUT_MINUTES: "30" @@ -1034,6 +1071,78 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_create_pr_error.cjs'); await main(); + push_repo_memory: + needs: agent + if: always() && needs.agent.outputs.detection_success == 'true' + runs-on: ubuntu-latest + permissions: + contents: write + concurrency: + group: "push-repo-memory-${{ github.repository }}" + cancel-in-progress: false + outputs: + patch_size_exceeded_default: ${{ steps.push_repo_memory_default.outputs.patch_size_exceeded }} + validation_error_default: ${{ steps.push_repo_memory_default.outputs.validation_error }} + validation_failed_default: ${{ steps.push_repo_memory_default.outputs.validation_failed }} + steps: + - name: Checkout actions folder + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: github/gh-aw + sparse-checkout: | + actions + persist-credentials: false + - name: Setup Scripts + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: . + - 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: Download wiki-memory artifact (default) + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + continue-on-error: true + with: + name: repo-memory-default + path: /tmp/gh-aw/repo-memory/default + - name: Push wiki-memory changes (default) + id: push_repo_memory_default + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_TOKEN: ${{ github.token }} + GITHUB_RUN_ID: ${{ github.run_id }} + GITHUB_SERVER_URL: ${{ github.server_url }} + ARTIFACT_DIR: /tmp/gh-aw/repo-memory/default + MEMORY_ID: default + TARGET_REPO: ${{ github.repository }}.wiki + BRANCH_NAME: master + REPO_MEMORY_ALLOWED_REPOS: ${{ github.repository }}.wiki + MAX_FILE_SIZE: 10240 + MAX_FILE_COUNT: 100 + MAX_PATCH_SIZE: 10240 + ALLOWED_EXTENSIONS: '[]' + with: + script: | + 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/push_repo_memory.cjs'); + await main(); + safe_outputs: needs: - activation diff --git a/.github/workflows/daily-community-attribution.md b/.github/workflows/daily-community-attribution.md index 70201dba72b..0dd35f04c2a 100644 --- a/.github/workflows/daily-community-attribution.md +++ b/.github/workflows/daily-community-attribution.md @@ -1,6 +1,6 @@ --- name: Daily Community Attribution Updater -description: Maintains a live community contributions section in README.md by attributing all community-labeled issues to merged PRs using the four-tier attribution strategy +description: Maintains a live community contributions section in README.md and an all-time Community Contributors wiki page by attributing all community-labeled issues using the five-tier attribution strategy on: schedule: - cron: daily @@ -22,6 +22,9 @@ tools: github: mode: "local" toolsets: [issues, pull_requests] + repo-memory: + wiki: true + description: "All-time Community Contributors list" bash: - "gh pr list *" - "gh issue list *" @@ -95,7 +98,7 @@ steps: echo "" echo "Data available in /tmp/gh-aw/community-data/:" - echo " community_issues.json — all community-labeled issues" + echo " community_issues.json — all community-labeled issues (includes stateReason)" echo " pull_requests.json — merged PRs (last 90 days)" echo " closing_refs_by_issue.json — native GitHub close links" echo " community_issues_closed_in_window.json — closed during lookback" @@ -104,33 +107,42 @@ steps: # Daily Community Attribution Updater Maintain an up-to-date **🌍 Community Contributions** section in `README.md` -by attributing all resolved community-labeled issues to merged PRs. +and an all-time **Community Contributors** wiki page by attributing all +resolved community-labeled issues using the five-tier attribution strategy. ## Mission The `community` label is the **primary attribution signal**: every issue tagged with it was explicitly identified by a maintainer as community-authored. -This workflow attributes those issues to PRs, updates `README.md`, and opens -a PR for review. +This workflow attributes those issues (including direct-issue contributions +with `stateReason == "COMPLETED"`), updates `README.md`, maintains the wiki, +and opens a PR for review. ## Pre-fetched Data All data is in `/tmp/gh-aw/community-data/`: ```bash -# View all community-labeled issues +# View all community-labeled issues (with stateReason) cat /tmp/gh-aw/community-data/community_issues.json | \ - jq -r '.[] | "- #\(.number) [\(.state // "?")] \(.title) by @\(.author.login)"' + jq -r '.[] | "- #\(.number) [\(.state // "?")] stateReason=\(.stateReason // "null") \(.title) by @\(.author.login)"' + +# View Tier 0 contributions (COMPLETED, direct issue — no PR needed) +cat /tmp/gh-aw/community-data/community_issues.json | \ + jq -r '.[] | select(.stateReason == "COMPLETED") | "- #\(.number): \(.title) by @\(.author.login) (closed: \(.closedAt))"' # View recently closed community issues (attribution candidates) cat /tmp/gh-aw/community-data/community_issues_closed_in_window.json | \ - jq -r '.[] | "- #\(.number): \(.title) by @\(.author.login) (closed: \(.closedAt))"' + jq -r '.[] | "- #\(.number): \(.title) by @\(.author.login) (closed: \(.closedAt), stateReason: \(.stateReason // "null"))"' # View closing reference index cat /tmp/gh-aw/community-data/closing_refs_by_issue.json | jq # View current README head -80 /tmp/gh-aw/community-data/README_current.md + +# View existing wiki page (if any) +cat /tmp/gh-aw/repo-memory-default/Community-Contributors.md 2>/dev/null || echo "(wiki page does not exist yet)" ``` ## Workflow @@ -142,15 +154,43 @@ to every closed community-labeled issue. Focus on issues in `community_issues_closed_in_window.json` first (recently closed, most likely to be new). Then check older closed issues in -`community_issues.json` that are not already reflected in `README.md`. +`community_issues.json` that are not already reflected in `README.md` or the +wiki page. -For each community issue, work through all four attribution tiers (see shared +For each community issue, work through all five attribution tiers (see shared component) and mark it as **confirmed**, **confirmed (via follow-up)**, or **needs review**. -### 2. Build the Community Contributions Table +### 2. Update the Community Contributors Wiki Page + +Read the existing wiki page at +`/tmp/gh-aw/repo-memory-default/Community-Contributors.md` (empty/missing on +first run). Merge all confirmed attributions — both newly found ones and all +previously recorded ones — without duplicating rows. + +The wiki page format: + +```markdown +# Community Contributors + +| Issue | Title | Author | Closed | Attribution | +|-------|-------|--------|--------|-------------| +| [#N](url) | Issue title | `@author` | YYYY-MM-DD | direct issue | +| [#N](url) | Issue title | `@author` | YYYY-MM-DD | resolved by #PR | +``` + +- **`direct issue`** — Tier 0: closed as `COMPLETED`, no PR linkage +- **`resolved by #PR`** — Tiers 1–3: attributed to a specific merged PR +- Sort by issue number descending (newest first) +- Do not add rows for unresolved or ambiguous candidates (Tier 4) -Produce a concise, sorted table of attributed community contributors: +Write the updated content back to +`/tmp/gh-aw/repo-memory-default/Community-Contributors.md` using the edit tool. + +### 3. Build the Community Contributions Table + +Produce a concise, sorted table of attributed community contributors for +`README.md`: ```markdown ## 🌍 Community Contributions @@ -160,12 +200,15 @@ This list is updated automatically and reflects all attributed contributions. | Issue | Title | Author | Resolved By | Attribution | |-------|-------|--------|-------------|-------------| +| [#N](url) | Issue title | @author | — | direct issue | | [#N](url) | Issue title | @author | [#PR](url) | direct | | [#N](url) | Issue title | @author | [#PR](url) via [#M](url) | follow-up | ``` - Sort by issue number descending (newest first) -- `Attribution` column: `direct` for Tier 1/2, `via follow-up #M` for Tier 3 +- **`direct issue`** (Tier 0): `Resolved By` column shows `—` (no PR) +- **`direct`** (Tier 1/2): PR URL in `Resolved By` +- **`via follow-up #M`** (Tier 3): PR URL plus follow-up chain - Omit issues that cannot be attributed (see Attribution Candidates section below) If there are unattributed candidates (Tier 4), append: @@ -179,7 +222,7 @@ linked to a specific merged PR. Please verify whether they should be credited: - **@author** for [Issue title](#N) — closed DATE ``` -### 3. Update README.md +### 4. Update README.md Replace the existing `## 🌍 Community Contributions` section in `README.md` with the newly generated content, or append it after the `## Contributing` @@ -187,13 +230,10 @@ section if it does not yet exist. Use the edit tool to make the change in-place. -If no changes are needed (all attributions already present and current), -call the `noop` safe-output tool and stop. - -### 4. Open a Pull Request +### 5. Open a Pull Request -If `README.md` was updated, call the `create_pull_request` safe-output tool -to open a PR with the changes. +If `README.md` **or** the wiki page changed, call the `create_pull_request` +safe-output tool to open a PR with the changes. **PR title**: `[community] Update community contributions in README` @@ -201,11 +241,13 @@ to open a PR with the changes. ```markdown ### Community Contributions Update -Automated update to the 🌍 Community Contributions section in `README.md`. +Automated update to the 🌍 Community Contributions section in `README.md` +and the Community Contributors wiki page. #### Changes - N community issues newly attributed - N attribution candidates flagged for review (if any) +- Wiki page updated: Y/N #### Attribution Summary [brief summary of what changed and how each was attributed] diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 08959a6e681..9a3dc22f350 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -26,7 +26,7 @@ # Imports: # - shared/community-attribution.md # -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"77776679e0e3d2842bfd82b1429abeb6ba6d1005274257fe7a5753d84431c8aa","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a72dabb6fd69c2b04cc7b24de28546dd9574e26a8d4c0f02088d672e94e9b20e","strict":true,"agent_id":"copilot"} name: "Release" "on": @@ -314,7 +314,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} name: Fetch community-labeled issues - run: "mkdir -p /tmp/gh-aw/community-data\n\n# The \"community\" label is the **primary attribution signal**: a maintainer\n# explicitly tagged the issue as community-authored, making it a strong and\n# intentional marker that does not rely on free-text heuristics.\necho \"Fetching issues with 'community' label (primary attribution signal)...\"\nif ! gh issue list \\\n --label \"community\" \\\n --state all \\\n --limit 500 \\\n --json number,title,author,labels,closedAt,createdAt,url \\\n > /tmp/gh-aw/community-data/community_issues.json; then\n echo \"[]\" > /tmp/gh-aw/community-data/community_issues.json\nfi\n\nCOMMUNITY_COUNT=$(jq length \"/tmp/gh-aw/community-data/community_issues.json\")\necho \"✓ Fetched $COMMUNITY_COUNT community-labeled issues\"\necho \" Data: /tmp/gh-aw/community-data/community_issues.json\"" + run: "mkdir -p /tmp/gh-aw/community-data\n\n# The \"community\" label is the **primary attribution signal**: a maintainer\n# explicitly tagged the issue as community-authored, making it a strong and\n# intentional marker that does not rely on free-text heuristics.\necho \"Fetching issues with 'community' label (primary attribution signal)...\"\nif ! gh issue list \\\n --label \"community\" \\\n --state all \\\n --limit 500 \\\n --json number,title,author,labels,closedAt,createdAt,url,stateReason \\\n > /tmp/gh-aw/community-data/community_issues.json; then\n echo \"[]\" > /tmp/gh-aw/community-data/community_issues.json\nfi\n\nCOMMUNITY_COUNT=$(jq length \"/tmp/gh-aw/community-data/community_issues.json\")\necho \"✓ Fetched $COMMUNITY_COUNT community-labeled issues\"\necho \" Data: /tmp/gh-aw/community-data/community_issues.json\"" - env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_ID: ${{ needs.release.outputs.release_id }} diff --git a/.github/workflows/shared/community-attribution.md b/.github/workflows/shared/community-attribution.md index cc643b876d3..674be389cfc 100644 --- a/.github/workflows/shared/community-attribution.md +++ b/.github/workflows/shared/community-attribution.md @@ -25,7 +25,7 @@ steps: --label "community" \ --state all \ --limit 500 \ - --json number,title,author,labels,closedAt,createdAt,url \ + --json number,title,author,labels,closedAt,createdAt,url,stateReason \ > /tmp/gh-aw/community-data/community_issues.json; then echo "[]" > /tmp/gh-aw/community-data/community_issues.json fi @@ -49,16 +49,35 @@ cat /tmp/gh-aw/community-data/community_issues.json \ | jq -r '.[] | "- #\(.number): \(.title) by @\(.author.login) (closed: \(.closedAt // "open"))"' ``` -Use the following **four-tier** approach to identify which community-labeled +Use the following **five-tier** approach to identify which community-labeled issues were resolved in a given period. Work through all tiers before concluding, and never silently drop a community issue that was closed during the period under review. +### Tier 0 — Direct issue contributions (confirmed, no PR required) + +Any community-labelled issue that was closed with `stateReason == "COMPLETED"` +is a **confirmed contribution** by the issue author — no PR linkage is needed. +External contributors in this repo file issues rather than PRs; when a +maintainer tags an issue `community` and a coding agent resolves it, the issue +is closed as `COMPLETED`. This is the strongest possible attribution signal. + +```bash +# List all community issues closed as COMPLETED (direct contributions) +cat /tmp/gh-aw/community-data/community_issues.json \ + | jq -r '.[] | select(.stateReason == "COMPLETED") | "- #\(.number): \(.title) by @\(.author.login) (closed: \(.closedAt))"' +``` + +Record every matched issue as a **confirmed** attribution with type +`direct issue`. These issues do **not** need to be checked against PR data +in Tiers 1–3. + ### Tier 1 — GitHub-native closing references (primary) -`closing_refs_by_issue.json` records the issues that GitHub itself marks as -"closed by" each merged PR (the native close-with-keyword feature). This is -the strongest signal because it does not depend on free-text conventions. +For community issues **not already attributed in Tier 0**, `closing_refs_by_issue.json` +records the issues that GitHub itself marks as "closed by" each merged PR (the +native close-with-keyword feature). This is the strongest PR-linkage signal +because it does not depend on free-text conventions. ```bash COMMUNITY_NUMBERS=$(jq '[.[].number]' /tmp/gh-aw/community-data/community_issues.json) @@ -74,7 +93,7 @@ Record every matched issue as **confirmed** attribution. ### Tier 2 — PR body keyword parsing (secondary fallback) -For issues **not yet matched** in Tier 1, scan PR bodies for the standard +For issues **not yet matched** in Tier 0 or Tier 1, scan PR bodies for the standard closing keywords. Both bare (`#123`) and fully-qualified (`org/repo#123`) forms are supported. @@ -102,13 +121,16 @@ issues: ### Tier 4 — Surface ambiguous candidates (fail soft, not silent) -After all three active tiers, any community issue that was closed during the -review period but cannot be linked to a specific merged PR must **not** be -silently dropped. Add it to the **"⚠️ Attribution Candidates Need Review"** -section so a maintainer can make the final call. +After all active tiers, any community issue that was closed during the +review period but cannot be linked to a specific merged PR (and was **not** +already attributed in Tier 0) must **not** be silently dropped. Add it to +the **"⚠️ Attribution Candidates Need Review"** section so a maintainer can +make the final call. ```bash -cat /tmp/gh-aw/community-data/community_issues_closed_in_window.json | jq 'length' +# Issues in the window that are NOT COMPLETED (Tier 0) and not matched by PR tiers +cat /tmp/gh-aw/community-data/community_issues_closed_in_window.json | \ + jq '[.[] | select(.stateReason != "COMPLETED")] | length' ``` ### Output sections @@ -121,10 +143,16 @@ cat /tmp/gh-aw/community-data/community_issues_closed_in_window.json | jq 'lengt A huge thank you to the community members who reported issues that were resolved in this release: +- **@author** for Issue title ([#N](url)) _(direct issue)_ - **@author** for Issue title ([#N](url)) - **@author** for Issue title ([#N](url)) _(via follow-up #M)_ ``` +Attribution type suffixes: +- `_(direct issue)_` — Tier 0: issue closed as `COMPLETED`, no PR linkage needed +- _(no suffix)_ — Tier 1/2: PR closes the issue via native close reference or keyword +- `_(via follow-up #M)_` — Tier 3: indirect chain through a follow-up issue + **Unlinked candidates → Attribution Candidates Need Review** ```markdown diff --git a/pkg/workflow/schemas/github-workflow.json b/pkg/workflow/schemas/github-workflow.json index 9c5ed2c6ea0..afa39b90149 100644 --- a/pkg/workflow/schemas/github-workflow.json +++ b/pkg/workflow/schemas/github-workflow.json @@ -257,6 +257,19 @@ "$comment": "https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#example-using-environment-name-and-url", "description": "A deployment URL", "type": "string" + }, + "deployment": { + "$comment": "https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/control-deployments#using-environments-without-deployments", + "description": "Whether to create a deployment for this job. Setting to false lets the job use environment secrets and variables without creating a deployment record. Wait timers and required reviewers still apply.", + "oneOf": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/expressionSyntax" + } + ], + "default": true } }, "required": ["name"], @@ -1656,16 +1669,22 @@ "description": "You can use the GitHub API to trigger a webhook event called repository_dispatch when you want to trigger a workflow for activity that happens outside of GitHub. For more information, see https://developer.github.com/v3/repos/#create-a-repository-dispatch-event.\nTo trigger the custom repository_dispatch webhook event, you must send a POST request to a GitHub API endpoint and provide an event_type name to describe the activity type. To trigger a workflow run, you must also configure your workflow to use the repository_dispatch event." }, "schedule": { - "$comment": "https://help.github.com/en/github/automating-your-workflow-with-github-actions/events-that-trigger-workflows#scheduled-events-schedule", - "description": "You can schedule a workflow to run at specific UTC times using POSIX cron syntax (https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07). Scheduled workflows run on the latest commit on the default or base branch. The shortest interval you can run scheduled workflows is once every 5 minutes.\nNote: GitHub Actions does not support the non-standard syntax @yearly, @monthly, @weekly, @daily, @hourly, and @reboot.\nYou can use crontab guru (https://crontab.guru/). to help generate your cron syntax and confirm what time it will run. To help you get started, there is also a list of crontab guru examples (https://crontab.guru/examples.html).", + "$comment": "https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule", + "description": "You can schedule a workflow to run at specific UTC times using POSIX cron syntax (https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07). You can optionally specify a timezone using an IANA timezone string (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for timezone-aware scheduling. Scheduled workflows run on the latest commit on the default or base branch. The shortest interval you can run scheduled workflows is once every 5 minutes.\nNote: GitHub Actions does not support the non-standard syntax @yearly, @monthly, @weekly, @daily, @hourly, and @reboot.\nYou can use crontab guru (https://crontab.guru/) to help generate your cron syntax and confirm what time it will run. To help you get started, there is also a list of crontab guru examples (https://crontab.guru/examples.html).", "type": "array", "items": { "type": "object", "properties": { "cron": { + "description": "A cron expression that represents a schedule. A scheduled workflow will run at most once every 5 minutes.", + "type": "string" + }, + "timezone": { + "description": "A string that represents the time zone a scheduled workflow will run relative to in IANA format (e.g. 'America/New_York' or 'Europe/London'). If omitted, the workflow will run relative to midnight UTC.", "type": "string" } }, + "required": ["cron"], "additionalProperties": false }, "minItems": 1