Skip to content

Add pre-run file creation for unresolved, resolved, and outdated threads#503

Merged
strawgate merged 3 commits intomainfrom
copilot/add-prerequisites-file-creation
Mar 1, 2026
Merged

Add pre-run file creation for unresolved, resolved, and outdated threads#503
strawgate merged 3 commits intomainfrom
copilot/add-prerequisites-file-creation

Conversation

Copy link
Contributor

Copilot AI commented Feb 28, 2026

  • Add filtered review thread files to pr-context.md fragment:
    • unresolved_threads.json — threads where isResolved == false
    • resolved_threads.json — threads where isResolved == true
    • outdated_threads.json — threads where isOutdated == true
  • Update the README manifest in pr-context.md to document new files
  • Update prompt references in workflows that tell agents to parse review_comments.json manually (pr-review-addresser, mention-in-pr, mention-in-pr-no-sandbox)
  • Recompile lock files with make compile using gh-aw v0.51.0
  • Run tests to validate changes (30/30 pass)

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI changed the title [WIP] Add pre-run file creation for unresolved threads Add pre-run file creation for unresolved, resolved, and outdated threads Feb 28, 2026
@strawgate strawgate marked this pull request as ready for review February 28, 2026 23:43
@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fda2b2d and 63dcfdc.

📒 Files selected for processing (9)
  • .github/workflows/gh-aw-fragments/pr-context.md
  • .github/workflows/gh-aw-mention-in-pr-by-id.lock.yml
  • .github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml
  • .github/workflows/gh-aw-mention-in-pr-no-sandbox.md
  • .github/workflows/gh-aw-mention-in-pr.lock.yml
  • .github/workflows/gh-aw-mention-in-pr.md
  • .github/workflows/gh-aw-pr-review-addresser.lock.yml
  • .github/workflows/gh-aw-pr-review-addresser.md
  • .github/workflows/gh-aw-pr-review.lock.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/gh-aw-pr-review.lock.yml

📝 Walkthrough

Walkthrough

Adds precomputed, filtered review-thread JSON views—unresolved_threads.json, resolved_threads.json, and outdated_threads.json—generated from review_comments.json and recorded in /tmp/pr-context. Workflow guidance and workflow manifests were updated to read unresolved_threads.json (and consult outdated_threads.json post-push) when locating and re-checking threads to address. PR-context README/manifest entries were extended to document the new outputs. An optional repository-specific setup step was also added to the activation workflow.

Possibly related PRs

  • Add address-pr-feedback workflow #339: Introduces an address-pr-feedback workflow that consumes unresolved review threads, directly related to the addition of precomputed unresolved/outdated thread JSON views.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed PR implements all core objectives from #497: creates pre-filtered JSON files (unresolved_threads.json, resolved_threads.json, outdated_threads.json) during context setup and updates agent prompts to use them, eliminating repeated jq/Python parsing.
Out of Scope Changes check ✅ Passed All changes align with #497 objectives: generating filtered thread files and updating workflows to consume them. One incidental addition (repo-specific setup step in gh-aw-pr-review.lock.yml) appears unrelated to thread filtering but is minor and doesn't distract from core purpose.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch copilot/add-prerequisites-file-creation

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml:
- Line 759: The lock YAML fails to parse because the source Markdown contains
invalid escaped single quotes (e.g., occurrences of don\'t and jq \'...\'), so
open the Markdown that generated the lock, search for patterns like don\'t and
jq \' and remove the backslashes (convert don\'t -> don't and jq \'...\' -> jq
'...'), save the source, then run the same compile command (gh aw compile) to
regenerate the .lock.yml and confirm the resulting YAML lints cleanly.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2897d27 and af7fe86.

📒 Files selected for processing (9)
  • .github/workflows/gh-aw-fragments/pr-context.md
  • .github/workflows/gh-aw-mention-in-pr-by-id.lock.yml
  • .github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml
  • .github/workflows/gh-aw-mention-in-pr-no-sandbox.md
  • .github/workflows/gh-aw-mention-in-pr.lock.yml
  • .github/workflows/gh-aw-mention-in-pr.md
  • .github/workflows/gh-aw-pr-review-addresser.lock.yml
  • .github/workflows/gh-aw-pr-review-addresser.md
  • .github/workflows/gh-aw-pr-review.lock.yml

PR_NUMBER: ${{ github.event.pull_request.number || inputs.target-pr-number || github.event.issue.number }}
name: Fetch PR context to disk
run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,headRefOid,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, head commit SHA (`headRefOid`), URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/<path>.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id` (node ID for resolving), `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with `id`, `databaseId` (numeric REST ID for replies), body/author |\n| `threads/<path>.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/"
run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,headRefOid,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Filtered review thread views (pre-computed so agents don\'t need to parse review_comments.json)\njq \'[.[] | select(.isResolved == false)]\' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq \'[.[] | select(.isResolved == true)]\' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq \'[.[] | select(.isOutdated == true)]\' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, head commit SHA (`headRefOid`), URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/<path>.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id` (node ID for resolving), `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with `id`, `databaseId` (numeric REST ID for replies), body/author |\n| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\n| `threads/<path>.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml"

echo "=== Checking file existence ==="
if [ ! -f "$FILE" ]; then
    echo "File not found: $FILE"
    exit 1
fi

echo "=== Looking for \' patterns in the file ==="
rg -n "\\\\'" "$FILE" | head -20 || echo "No matches found"

echo ""
echo "=== Looking for the source .md file ==="
SOURCE_MD="${FILE%.lock.yml}.md"
if [ -f "$SOURCE_MD" ]; then
    echo "Found source: $SOURCE_MD"
    echo "First 50 lines of source:"
    head -50 "$SOURCE_MD"
else
    echo "Source .md file not found: $SOURCE_MD"
fi

echo ""
echo "=== Attempting YAML parsing ==="
python3 << 'PYEND'
from pathlib import Path
import sys

try:
    import yaml
    p = Path(".github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml")
    if not p.exists():
        print("File does not exist")
        sys.exit(1)
    
    text = p.read_text()
    yaml.safe_load(text)
    print("✓ YAML parsing successful - file is valid YAML")
    sys.exit(0)
except yaml.YAMLError as e:
    print("✗ YAML parsing failed:")
    print(str(e)[:500])
    sys.exit(1)
except Exception as e:
    print(f"Error: {e}")
    sys.exit(1)
PYEND

Repository: elastic/ai-github-actions

Length of output: 8856


Fix invalid YAML escaping in the source .md file, then recompile.

The .lock.yml file fails YAML parsing due to \' escape sequences (not valid in YAML double-quoted strings). Line 759 contains multiple instances: don\'t, jq \'...\', etc.

Edit .github/workflows/gh-aw-mention-in-pr-no-sandbox.md to remove the backslashes before single quotes (e.g., don't instead of don\'t), then run gh aw compile to regenerate the .lock.yml.

Fix pattern
-# Filtered review thread views (pre-computed so agents don\'t need to parse review_comments.json)
-jq \'[.[] | select(.isResolved == false)]\' /tmp/pr-context/review_comments.json \
+# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)
+jq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \
   > /tmp/pr-context/unresolved_threads.json
-jq \'[.[] | select(.isResolved == true)]\' /tmp/pr-context/review_comments.json \
+jq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \
   > /tmp/pr-context/resolved_threads.json
-jq \'[.[] | select(.isOutdated == true)]\' /tmp/pr-context/review_comments.json \
+jq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \
   > /tmp/pr-context/outdated_threads.json
🧰 Tools
🪛 YAMLlint (1.38.0)

[error] 759-759: syntax error: found unknown escape character "'"

(syntax)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml at line 759, The
lock YAML fails to parse because the source Markdown contains invalid escaped
single quotes (e.g., occurrences of don\'t and jq \'...\'), so open the Markdown
that generated the lock, search for patterns like don\'t and jq \' and remove
the backslashes (convert don\'t -> don't and jq \'...\' -> jq '...'), save the
source, then run the same compile command (gh aw compile) to regenerate the
.lock.yml and confirm the resulting YAML lints cleanly.

@strawgate
Copy link
Collaborator

@copilot please address and recompile

Copy link
Contributor Author

Copilot AI commented Mar 1, 2026

@copilot please address and recompile

Recompiled all lock files using make compile with gh-aw v0.51.0 in c39f2db. The compiler regenerated the 5 lock files that inline the pr-context fragment with proper escaping and updated metadata hashes.

@strawgate strawgate force-pushed the copilot/add-prerequisites-file-creation branch from c39f2db to fda2b2d Compare March 1, 2026 00:10
Copilot AI and others added 3 commits February 28, 2026 18:13
Generate filtered review thread files during PR context setup:
- unresolved_threads.json: threads where isResolved is false
- resolved_threads.json: threads where isResolved is true
- outdated_threads.json: threads where isOutdated is true

Update workflow prompts to reference pre-filtered files instead of
telling agents to manually parse review_comments.json with jq.

Co-authored-by: strawgate <6384545+strawgate@users.noreply.github.com>
Co-authored-by: strawgate <6384545+strawgate@users.noreply.github.com>
@strawgate strawgate force-pushed the copilot/add-prerequisites-file-creation branch from fda2b2d to 63dcfdc Compare March 1, 2026 00:14
@strawgate strawgate merged commit 0ce77a8 into main Mar 1, 2026
18 checks passed
@strawgate strawgate deleted the copilot/add-prerequisites-file-creation branch March 1, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants