✨ feat(ci): add opencode-new-pr workflow for sequential task execution - #9
✨ feat(ci): add opencode-new-pr workflow for sequential task execution#9dianlight wants to merge 3 commits into
Conversation
Add a new workflow that creates a PR from an approved issue, extracts task lists from comments, creates CHANGELOG entries, and dispatches tasks to opencode-implement.yaml sequentially. The implement workflow was refactored to accept a pull request number and optional task description instead of an issue trigger.
Warning Review limit reached
Next review available in:58 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds an ChangesOrchestrated issue-to-PR implementation pipeline
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Triage as opencode-triage-issue.yaml
participant Orchestrator as opencode-new-pr.yaml
participant GH as GitHub API
participant Implement as opencode-implement.yaml
Triage->>GH: label issue approved-for-implementation
Triage->>Orchestrator: gh workflow run opencode-new-pr.yaml (issue-number)
Orchestrator->>GH: fetch issue comments and extract task checklist
Orchestrator->>GH: prepend CHANGELOG.md entry
Orchestrator->>GH: create feature branch and open PR with task checklist
loop for each task sequentially
Orchestrator->>Implement: dispatch opencode-implement.yaml (pr-number, task-description)
Implement->>GH: checkout PR branch, implement task, push commit
Orchestrator->>GH: poll run status, stop on failure
end
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Verdict: Changes requested
🚨 3 · 🛠️ 3 · 💡 1
Most critical: opencode-new-pr.yaml:157 passes untrusted task descriptions from issue comments directly as gh workflow run -f arguments — a task containing " breaks argument parsing, potentially injecting unexpected flags or values. Combined with opencode-new-pr.yaml:118 suppressing git push errors and a 60-second poll race condition for dispatched runs, the orchestrator will silently fail under real-world queue delays or non-trivial task descriptions.
All three CRITICALs are in opencode-new-pr.yaml (the new file). The implement and triage refactors look sound.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| ## Key labels (state machine) | ||
| | Label | Meaning | |
There was a problem hiding this comment.
💡 [NITPICK] Inaccurate statement about OpenCode API key for CHANGELOG creation
opencode-new-pr.yaml does not use the anomalyco/opencode/github action at all — CHANGELOG creation is done by a Python script, not by the opencode AI. OPENCODE_API_KEY is never set in that workflow.
| | Label | Meaning | | |
| -`opencode-new-pr.yaml` uses `GITHUB_TOKEN` for all API calls (branch creation, PR creation, commenting). |
Review submitted. 3 CRITICAL, 3 QUALITY, 1 NITPICK — all findings in CRITICALs:
|
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/opencode-new-pr.yaml:
- Around line 76-104: The CHANGELOG generation step currently writes and commits
the file directly via inline Python and git in the opencode-new-pr workflow, but
it needs to be routed through OpenCode using the OpenCode API key. Update the
workflow step that creates or updates CHANGELOG.md so it invokes the OpenCode
action/command with OPENCODE_API_KEY for changelog creation instead of doing the
file generation locally, while keeping GITHUB_TOKEN usage limited to the
existing API call steps. Reference the “Create or update CHANGELOG.md” step in
opencode-new-pr.yaml when making the change.
- Around line 106-127: The Create branch and pull request workflow step is
interpolating github.event.inputs.issue-number and the extract-tasks outputs
directly inside run:, which can allow shell injection before the script
executes. Move ISSUE_NUMBER, ISSUE_TITLE, and TASKS into env: for this step and
update the matching extract-tasks and execute-tasks assignments to read from
environment variables instead of inline expressions.
- Around line 28-31: The checkout step in the workflow is clearing git
credentials, so the later branch push will fail. Update the job around the
Checkout repository step and the push logic that uses git push origin "$BRANCH"
to restore authentication first by configuring git to use GITHUB_TOKEN before
pushing, or otherwise ensure credentials are available when the push runs.
- Around line 141-157: The sequential task runner in the workflow is embedding
untrusted workflow outputs directly inside shell literals, which can break the
script if a task contains quotes. Move the values sourced from
needs.create-pr.outputs.task-list, needs.create-pr.outputs.pr-number, and
github.ref_name into env variables on the step, then read those shell variables
inside the run block instead of assigning them with inline expansion.
In @.github/workflows/opencode-triage-issue.yaml:
- Around line 13-32: The debug job in opencode-triage-issue.yaml is vulnerable
because `github.event.comment.body` and `github.event.issue.labels.*.name` are
interpolated directly inside `run:` steps, allowing shell/template injection
from untrusted commenters. Update the `Debug auth info` and `Test condition
parts` steps to pass these values through `env:` and read them as shell
variables inside the script instead of embedding expressions inline, and keep
the logic in the existing workflow job names/steps. Also add a restrictive `if:`
gate similar to the `triage` job’s actor checks, and define an explicit minimal
`permissions:` block for the job.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9a90c349-e031-4cf5-b67d-25de414bdc87
📒 Files selected for processing (7)
.github/sync.yml.github/workflows/WORKFLOWS.md.github/workflows/opencode-implement.yaml.github/workflows/opencode-new-pr.yaml.github/workflows/opencode-triage-issue.yamlAGENTS.mdconfig/workflow-task-map.yaml
| - name: Checkout repository | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | ||
| with: | ||
| persist-credentials: false |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bashset -euo pipefail
file=".github/workflows/opencode-new-pr.yaml"
wc -l "$file"
sed -n '1,170p'"$file"| cat -nRepository: dianlight/opencode-actions
Length of output: 8130
Re-enable git auth before the branch push.persist-credentials: false removes checkout’s git credentials, so git push origin "$BRANCH" will fail unless git is configured to use GITHUB_TOKEN first.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/opencode-new-pr.yaml around lines 28 - 31, The checkout
step in the workflow is clearing git credentials, so the later branch push will
fail. Update the job around the Checkout repository step and the push logic that
uses git push origin "$BRANCH" to restore authentication first by configuring
git to use GITHUB_TOKEN before pushing, or otherwise ensure credentials are
available when the push runs.
There was a problem hiding this comment.
/oc In @.github/workflows/opencode-new-pr.yaml around lines 28 - 31, The checkout
step in the workflow is clearing git credentials, so the later branch push will
fail. Update the job around the Checkout repository step and the push logic that
uses git push origin "$BRANCH" to restore authentication first by configuring
git to use GITHUB_TOKEN before pushing, or otherwise ensure credentials are
available when the push runs.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bashset -euo pipefail
file=".github/workflows/opencode-new-pr.yaml"
sed -n '1,132p'"$file"| cat -nLength of output: 6368
Now I have the exact context needed to plan the fix.
@dianlight, here's the fix — since persist-credentials: false strips the checkout action's git credentials, we need to explicitly authenticate git before pushing. I'll configure the origin remote to use GH_TOKEN via an x-access-token credential right before the push in the "Create branch and pull request" step.
- Add
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}(already present) and set git remote URL with token-based auth beforegit pushin.github/workflows/opencode-new-pr.yaml.
This sets the origin remote URL to include an x-access-token credential derived from GH_TOKEN immediately before the push, so git push succeeds even though checkout didn't persist credentials. gh CLI calls elsewhere already work fine since they use GH_TOKEN directly and don't depend on git's credential store.
Failed to handle agent chat message. Please try again.
| - name: Create or update CHANGELOG.md | ||
| env: | ||
| ISSUE_NUMBER: ${{ github.event.inputs.issue-number }} | ||
| ISSUE_TITLE: ${{ steps.extract-tasks.outputs.issue-title }} | ||
| TASKS: ${{ steps.extract-tasks.outputs.task-list }} | ||
| run: | | ||
| python3 -c ' | ||
| import json, os | ||
| changelog_path = "CHANGELOG.md" | ||
| issue_number = os.environ["ISSUE_NUMBER"] | ||
| issue_title = os.environ["ISSUE_TITLE"] | ||
| tasks = json.loads(os.environ.get("TASKS", "[]")) | ||
| entry = f"## [#{issue_number}] - {issue_title}\n\n### Added\n" | ||
| for task in tasks: | ||
| entry += f"- {task}\n" | ||
| entry += "\n" | ||
| if os.path.exists(changelog_path): | ||
| with open(changelog_path) as f: | ||
| content = f.read() | ||
| content = entry + content | ||
| else: | ||
| content = f"# Changelog\n\n{entry}" | ||
| with open(changelog_path, "w") as f: | ||
| f.write(content) | ||
| ' | ||
| git config user.name "opencode-agent[bot]" | ||
| git config user.email "opencode-agent[bot]@users.noreply.github.com" | ||
| git add CHANGELOG.md | ||
| git commit -m "docs: add changelog entry for issue #$ISSUE_NUMBER" || echo "No changes to commit" |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
CHANGELOG creation should use the OpenCode API key, per guidelines.
This step builds and commits CHANGELOG.md directly with a Python heredoc and git, without invoking OpenCode. The repository guideline requires opencode-new-pr.yaml to use GITHUB_TOKEN for API calls (satisfied elsewhere) and an OpenCode API key for CHANGELOG creation. Please route the CHANGELOG generation through the OpenCode action with OPENCODE_API_KEY.
As per coding guidelines: "opencode-new-pr.yaml must use GITHUB_TOKEN for API calls and an OpenCode API key for CHANGELOG creation."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/opencode-new-pr.yaml around lines 76 - 104, The CHANGELOG
generation step currently writes and commits the file directly via inline Python
and git in the opencode-new-pr workflow, but it needs to be routed through
OpenCode using the OpenCode API key. Update the workflow step that creates or
updates CHANGELOG.md so it invokes the OpenCode action/command with
OPENCODE_API_KEY for changelog creation instead of doing the file generation
locally, while keeping GITHUB_TOKEN usage limited to the existing API call
steps. Reference the “Create or update CHANGELOG.md” step in
opencode-new-pr.yaml when making the change.
Source: Coding guidelines
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bashset -euo pipefail
fd . .github/workflows -e yaml -e yml
echo"---implement---"
cat -n .github/workflows/opencode-implement.yaml
echo"---triage---"
cat -n .github/workflows/opencode-triage-issue.yaml
echo"---new-pr---"
cat -n .github/workflows/opencode-new-pr.yamlLength of output: 25631
@dianlight, sure — here's the update to route CHANGELOG generation through the OpenCode action with OPENCODE_API_KEY, following the same pattern used in opencode-implement.yaml and opencode-triage-issue.yaml.
- Replace the raw Python/git "Create or update CHANGELOG.md" step in
.github/workflows/opencode-new-pr.yamlwith ananomalyco/opencode/githubaction step that usesOPENCODE_API_KEY, instructing it to create/update the file and commit+push directly to the current branch.
Let me know if you'd like the model value aligned with a different one used elsewhere, or if you'd prefer the entry-dedup check omitted for simplicity.
- 📌 Commit the changes to this branch
| - name: Create branch and pull request | ||
| id: create-pr | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| ISSUE_NUMBER="${{ github.event.inputs.issue-number }}" | ||
| ISSUE_TITLE="${{ steps.extract-tasks.outputs.issue-title }}" | ||
| TASKS='${{ steps.extract-tasks.outputs.task-list }}' | ||
| DEFAULT_BRANCH=$(gh repo view --json defaultBranch -q '.defaultBranch') | ||
| SAFE_TITLE=$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | head -c 30) | ||
| BRANCH="opencode/issue-${ISSUE_NUMBER}-${SAFE_TITLE}" | ||
| git checkout -b "$BRANCH" | ||
| git push origin "$BRANCH" 2>/dev/null || git push -u origin "$BRANCH" | ||
| PR_BODY="Closes #${ISSUE_NUMBER}\n\n## Description\n\n${ISSUE_TITLE}\n\n## Tasks\n" | ||
| while IFS= read -r task; do | ||
| PR_BODY="${PR_BODY}\n- [ ] ${task}" | ||
| done <<< "$(echo "$TASKS" | jq -r '.[]')" | ||
| PR_URL=$(gh pr create \ | ||
| --base "$DEFAULT_BRANCH" \ | ||
| --head "$BRANCH" \ | ||
| --title "feat: implement #${ISSUE_NUMBER} - ${ISSUE_TITLE}" \ | ||
| --body "$(echo -e "$PR_BODY")") |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bashset -euo pipefail
# Locate the workflow and inspect the relevant sections.
git ls-files .github/workflows/opencode-new-pr.yaml
wc -l .github/workflows/opencode-new-pr.yaml
cat -n .github/workflows/opencode-new-pr.yaml | sed -n '1,220p'# Pull just the referenced areas for easier review.printf'\n--- around lines 30-45 ---\n'
cat -n .github/workflows/opencode-new-pr.yaml | sed -n '30,45p'printf'\n--- around lines 100-130 ---\n'
cat -n .github/workflows/opencode-new-pr.yaml | sed -n '100,130p'printf'\n--- around lines 140-155 ---\n'
cat -n .github/workflows/opencode-new-pr.yaml | sed -n '140,155p'Repository: dianlight/opencode-actions
Length of output: 12492
Critical: move workflow inputs/outputs out of run:github.event.inputs.issue-number, steps.extract-tasks.outputs.issue-title, and steps.extract-tasks.outputs.task-list are expanded before the shell runs, so a crafted issue title or task body can break out of the assignment and execute commands on the runner. Pass them through env: in this step and the matching extract-tasks/execute-tasks assignments.
🧰 Tools
🪛 zizmor (1.26.1)
[error] 111-111: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 112-112: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 113-113: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/opencode-new-pr.yaml around lines 106 - 127, The Create
branch and pull request workflow step is interpolating
github.event.inputs.issue-number and the extract-tasks outputs directly inside
run:, which can allow shell injection before the script executes. Move
ISSUE_NUMBER, ISSUE_TITLE, and TASKS into env: for this step and update the
matching extract-tasks and execute-tasks assignments to read from environment
variables instead of inline expressions.
Source: Linters/SAST tools
| - name: Execute each task sequentially | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| PR_NUMBER="${{ needs.create-pr.outputs.pr-number }}" | ||
| TASKS='${{ needs.create-pr.outputs.task-list }}' | ||
| REF="${{ github.ref_name }}" | ||
| echo "$TASKS" | jq -r '.[]' | while IFS= read -r task; do | ||
| echo "" | ||
| echo "==========================================" | ||
| echo "=== Implementing: $task ===" | ||
| echo "==========================================" | ||
| BEFORE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") | ||
| gh workflow run opencode-implement.yaml \ | ||
| --ref "$REF" \ | ||
| -f pull-request-number="$PR_NUMBER" \ | ||
| -f "task-description=$task" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bashset -euo pipefail
git ls-files .github/workflows/opencode-new-pr.yaml
wc -l .github/workflows/opencode-new-pr.yaml
cat -n .github/workflows/opencode-new-pr.yaml | sed -n '120,190p'Repository: dianlight/opencode-actions
Length of output: 3538
🏁 Script executed:
#!/bin/bashset -euo pipefail
cat -n .github/workflows/opencode-new-pr.yaml | sed -n '1,140p'Repository: dianlight/opencode-actions
Length of output: 6573
🏁 Script executed:
python3 - <<'PY'import json, subprocess, textwrap, shlex, os, tempfile, pathlibtasks = json.dumps(["Bob's fix"])script = f"""#!/bin/bashset -eTASKS='{tasks}'echo "$TASKS""""print("SCRIPT:")print(script)p = subprocess.run(["bash", "-lc", script], capture_output=True, text=True)print("RETURN:", p.returncode)print("STDOUT:", p.stdout)print("STDERR:", p.stderr)PYRepository: dianlight/opencode-actions
Length of output: 419
Move the workflow outputs out of the shell literalTASKS comes from issue-comment text, and a task containing ' can break the TASKS='…' assignment and inject shell syntax. Pass needs.create-pr.outputs.* and github.ref_name via env: instead, then read them as shell variables.
🧰 Tools
🪛 zizmor (1.26.1)
[info] 145-145: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 146-146: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 147-147: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/opencode-new-pr.yaml around lines 141 - 157, The
sequential task runner in the workflow is embedding untrusted workflow outputs
directly inside shell literals, which can break the script if a task contains
quotes. Move the values sourced from needs.create-pr.outputs.task-list,
needs.create-pr.outputs.pr-number, and github.ref_name into env variables on the
step, then read those shell variables inside the run block instead of assigning
them with inline expansion.
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Debug auth info | ||
| run: | | ||
| echo "Comment user: ${{ github.event.comment.user.login }}" | ||
| echo "Author association: ${{ github.event.comment.author_association }}" | ||
| echo "Issue state: ${{ github.event.issue.state }}" | ||
| echo "Comment body: ${{ github.event.comment.body }}" | ||
| echo "Body length: ${{ github.event.comment.body.length }}" | ||
| echo "Contains /oc: $(echo '${{ github.event.comment.body }}' | grep -E '(^| )/oc' && echo 'yes' || echo 'no')" | ||
| echo "Has awaiting-response label: $(echo '${{ github.event.issue.labels.*.name }}' | grep -c 'opencode:awaiting-response' || echo '0')" | ||
| - name: Test condition parts | ||
| run: | | ||
| body_contains_oc=false | ||
| if echo "${{ github.event.comment.body }}" | grep -qE "(^/oc|^ /oc)"; then | ||
| body_contains_oc=true | ||
| fi | ||
| echo "Body starts/contains /oc: $body_contains_oc" | ||
| echo "Body length > 10: $([ ${#'${{ github.event.comment.body }}'} -gt 10 ] && echo 'yes' || echo 'no')" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
Script injection via unsanitized github.event.comment.body in debug job.
Both steps interpolate ${{ github.event.comment.body }} (and github.event.issue.labels.*.name) directly into run: blocks. Since this job has no if: gate restricting triggering actors (unlike the triage job below, which checks author_association), any commenter can inject arbitrary shell via the comment body, leading to RCE on the runner / secret exfiltration. Flagged by actionlint and zizmor as template-injection. The job also has no permissions: block, so it inherits default (possibly broad) permissions — flagged by zizmor as excessive-permissions.
🔒️ Proposed fix using env-var indirection
debug:
runs-on: ubuntu-latest
+ permissions: {}
steps:
- name: Debug auth info
+ env:+ COMMENT_BODY: ${{ github.event.comment.body }}+ COMMENT_USER: ${{ github.event.comment.user.login }}+ AUTHOR_ASSOCIATION: ${{ github.event.comment.author_association }}+ ISSUE_STATE: ${{ github.event.issue.state }}+ ISSUE_LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
run: |
- echo "Comment user: ${{ github.event.comment.user.login }}"- echo "Author association: ${{ github.event.comment.author_association }}"- echo "Issue state: ${{ github.event.issue.state }}"- echo "Comment body: ${{ github.event.comment.body }}"- echo "Body length: ${{ github.event.comment.body.length }}"- echo "Contains /oc: $(echo '${{ github.event.comment.body }}' | grep -E '(^| )/oc' && echo 'yes' || echo 'no')"- echo "Has awaiting-response label: $(echo '${{ github.event.issue.labels.*.name }}' | grep -c 'opencode:awaiting-response' || echo '0')"+ echo "Comment user: $COMMENT_USER"+ echo "Author association: $AUTHOR_ASSOCIATION"+ echo "Issue state: $ISSUE_STATE"+ echo "Comment body: $COMMENT_BODY"+ echo "Body length: ${`#COMMENT_BODY`}"+ echo "Contains /oc: $(echo "$COMMENT_BODY" | grep -E '(^| )/oc' && echo 'yes' || echo 'no')"+ echo "Has awaiting-response label: $(echo "$ISSUE_LABELS" | grep -c 'opencode:awaiting-response' || echo '0')"
- name: Test condition parts
+ env:+ COMMENT_BODY: ${{ github.event.comment.body }}
run: |
body_contains_oc=false
- if echo "${{ github.event.comment.body }}" | grep -qE "(^/oc|^ /oc)"; then+ if echo "$COMMENT_BODY" | grep -qE "(^/oc|^ /oc)"; then
body_contains_oc=true
fi
echo "Body starts/contains /oc: $body_contains_oc"
- echo "Body length > 10: $([ ${#'${{ github.event.comment.body }}'} -gt 10 ] && echo 'yes' || echo 'no')"+ echo "Body length > 10: $([ ${`#COMMENT_BODY`} -gt 10 ] && echo 'yes' || echo 'no')"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Debug auth info | |
| run: | | |
| echo "Comment user: ${{ github.event.comment.user.login }}" | |
| echo "Author association: ${{ github.event.comment.author_association }}" | |
| echo "Issue state: ${{ github.event.issue.state }}" | |
| echo "Comment body: ${{ github.event.comment.body }}" | |
| echo "Body length: ${{ github.event.comment.body.length }}" | |
| echo "Contains /oc: $(echo '${{ github.event.comment.body }}' | grep -E '(^| )/oc' && echo 'yes' || echo 'no')" | |
| echo "Has awaiting-response label: $(echo '${{ github.event.issue.labels.*.name }}' | grep -c 'opencode:awaiting-response' || echo '0')" | |
| - name: Test condition parts | |
| run: | | |
| body_contains_oc=false | |
| if echo "${{ github.event.comment.body }}" | grep -qE "(^/oc|^ /oc)"; then | |
| body_contains_oc=true | |
| fi | |
| echo "Body starts/contains /oc: $body_contains_oc" | |
| echo "Body length > 10: $([ ${#'${{ github.event.comment.body }}'} -gt 10 ] && echo 'yes' || echo 'no')" | |
| runs-on: ubuntu-latest | |
| permissions: {} | |
| steps: | |
| - name: Debug auth info | |
| env: | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| COMMENT_USER: ${{ github.event.comment.user.login }} | |
| AUTHOR_ASSOCIATION: ${{ github.event.comment.author_association }} | |
| ISSUE_STATE: ${{ github.event.issue.state }} | |
| ISSUE_LABELS: ${{ toJSON(github.event.issue.labels.*.name) }} | |
| run: | | |
| echo "Comment user: $COMMENT_USER" | |
| echo "Author association: $AUTHOR_ASSOCIATION" | |
| echo "Issue state: $ISSUE_STATE" | |
| echo "Comment body: $COMMENT_BODY" | |
| echo "Body length: ${`#COMMENT_BODY`}" | |
| echo "Contains /oc: $(echo "$COMMENT_BODY" | grep -E '(^| )/oc' && echo 'yes' || echo 'no')" | |
| echo "Has awaiting-response label: $(echo "$ISSUE_LABELS" | grep -c 'opencode:awaiting-response' || echo '0')" | |
| - name: Test condition parts | |
| env: | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| run: | | |
| body_contains_oc=false | |
| if echo "$COMMENT_BODY" | grep -qE "(^/oc|^ /oc)"; then | |
| body_contains_oc=true | |
| fi | |
| echo "Body starts/contains /oc: $body_contains_oc" | |
| echo "Body length > 10: $([ ${`#COMMENT_BODY`} -gt 10 ] && echo 'yes' || echo 'no')" |
🧰 Tools
🪛 actionlint (1.7.12)
[error] 16-16: "github.event.comment.body" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
[error] 26-26: "github.event.comment.body" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
🪛 zizmor (1.26.1)
[warning] 12-32: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[error] 17-17: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 20-20: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 21-21: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 22-22: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 23-23: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 28-28: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 32-32: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/opencode-triage-issue.yaml around lines 13 - 32, The debug
job in opencode-triage-issue.yaml is vulnerable because
`github.event.comment.body` and `github.event.issue.labels.*.name` are
interpolated directly inside `run:` steps, allowing shell/template injection
from untrusted commenters. Update the `Debug auth info` and `Test condition
parts` steps to pass these values through `env:` and read them as shell
variables inside the script instead of embedding expressions inline, and keep
the logic in the existing workflow job names/steps. Also add a restrictive `if:`
gate similar to the `triage` job’s actor checks, and define an explicit minimal
`permissions:` block for the job.
Source: Linters/SAST tools
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Verdict: Changes requested
🚨 1 (new) · 10 open threads from prior reviews remain unaddressed
Most critical: The race-condition fix for the workflow-dispatch polling loop was applied without removing the old loop, leaving a for without a matching done (opencode-new-pr.yaml:160). The script will fail with a shell syntax error at runtime, blocking all sequential task execution. 10 prior threads (shell injection vectors in run: blocks, git push error suppression, commit error masking, PR body construction, branch truncation, AGENTS.md inaccuracy, missing git auth, CHANGELOG routing, triage debug injection) remain open.
| -f "$(echo "$task" | jq -R -s 'sub("\n$";"") | @json' | jq -r '"task-description=" + .') " | ||
| echo "Dispatched. Waiting for the run to appear..." | ||
| RUN_ID="" | ||
| for i in $(seq 1 20); do |
There was a problem hiding this comment.
🚨 [CRITICAL] Missing done — duplicate polling loops create a shell syntax error
The original 20-iteration polling loop (line 160) was replaced with a 100-iteration loop (line 172) to fix a race condition, but the old loop was never removed and its closing done is missing. The result is an unclosed for at line 160 that produces a bash syntax error at runtime, causing the entire execute-tasks job to fail before any task runs.
| for i in $(seq 1 20); do | |
| for i in $(seq 1 100); do | |
| sleep 3 | |
| RUN_ID=$(gh run list \ | |
| --workflow=opencode-implement.yaml \ | |
| --limit 20 \ | |
| --json databaseId,createdAt \ | |
| -q "[.[] | select(.createdAt >= \"$BEFORE\")] | .[0].databaseId // empty" \ | |
| 2>/dev/null) | |
| if [ -n "$RUN_ID" ]; then | |
| echo "Found run: $RUN_ID" | |
| break | |
| fi | |
| done |
(Replace lines 158–184 with just the single 100-iteration loop above.)
Review submitted. Summary of actions:
|
There was a problem hiding this comment.
Verdict: Changes requested
🚨 2 · 🛠️ 1
No threads resolved — 0 of 7 open threads have fixes in the current diff.
Most critical: The old 20-iteration polling loop (line 183) was never removed when the 100-iteration loop was stacked on top, leaving a for without a done — a shell syntax error that kills execute-tasks. Separately, the old \n-based PR body code (lines 126–134) was left alongside the replacement, creating a broken gh pr create with unterminated $(...) that crashes create-pr.
Prior review threads about shell injection vectors in run: blocks, missing git auth, CHANGELOG routing, triage debug injection, and AGENTS.md inaccuracy remain open.
| -f "$(echo "$task" | jq -R -s 'sub("\n$";"") | @json' | jq -r '"task-description=" + .') " | ||
| echo "Dispatched. Waiting for the run to appear..." | ||
| RUN_ID="" | ||
| for i in $(seq 1 20); do |
There was a problem hiding this comment.
🚨 [CRITICAL] Missing done — orphaned for from incomplete race-condition fix
The old 20-iteration polling loop (line 183) was meant to be replaced by the 100-iteration loop (line 195). Instead of being removed, the old for was left without a closing done. The done at line 207 closes only the 100-iteration loop, so the first for is unclosed — a bash syntax error that will crash the entire execute-tasks job at runtime.
| for i in $(seq 1 20); do | |
| RUN_ID="" | |
| for i in $(seq 1 100); do | |
| sleep 3 | |
| RUN_ID=$(gh run list \ | |
| --workflow=opencode-implement.yaml \ | |
| --limit 20 \ | |
| --json databaseId,createdAt \ | |
| -q "[.[] | select(.createdAt >= \"$BEFORE\")] | .[0].databaseId // empty" \ | |
| 2>/dev/null) | |
| if [ -n "$RUN_ID" ]; then | |
| echo "Found run: $RUN_ID" | |
| break | |
| fi | |
| done |
(Replace lines 182–207 with the single loop above.)
| BRANCH="opencode/issue-${ISSUE_NUMBER}-${SAFE_TITLE}" | ||
| git checkout -b "$BRANCH" | ||
| git push -u origin "$BRANCH" | ||
| PR_BODY="Closes #${ISSUE_NUMBER}\n\n## Description\n\n${ISSUE_TITLE}\n\n## Tasks\n" |
There was a problem hiding this comment.
🚨 [CRITICAL] Duplicate/incomplete PR creation code — shell parse error
Lines 126–134 contain leftover code from the \n-based PR body approach (thread #5). The \ continuation at line 133 joins with line 134 (PR_BODY="Closes #...), but the " opens a multi-line string that swallows lines 135–141 while $(...) is never closed with ). The replacement code at lines 134–150 correctly builds and creates the PR. The dead code at lines 126–134 must be removed.
| PR_BODY="Closes #${ISSUE_NUMBER}\n\n## Description\n\n${ISSUE_TITLE}\n\n## Tasks\n" | |
| DEFAULT_BRANCH=$(gh repo view --json defaultBranch -q '.defaultBranch') | |
| SAFE_TITLE=$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | head -c 60) | |
| BRANCH="opencode/issue-${ISSUE_NUMBER}-${SAFE_TITLE}" | |
| git checkout -b "$BRANCH" | |
| git push -u origin "$BRANCH" | |
| PR_BODY="Closes #${ISSUE_NUMBER} |
(Remove the old \n-based block, keeping the actual-newline version.)
| - name: Checkout repository | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | ||
| with: | ||
| persist-credentials: false |
There was a problem hiding this comment.
🛠️ [QUALITY] Missing git auth — push will fail (thread already open)
persist-credentials: false at line 31 disables the checkout action's credential helper. git push -u origin "$BRANCH" at line 125 will fail because no auth mechanism is configured. Add the token-based remote URL before the push, or set persist-credentials: true.
| persist-credentials: false | |
| - name: Checkout repository | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | |
| with: | |
| persist-credentials: false | |
| token: ${{ secrets.GITHUB_TOKEN }} |
Review submitted. Summary: Event: No threads resolved — all 7 open threads remain unaddressed in the current diff. Findings:
|
dianlight
commented
Jul 11, 2026
dead end |



Add a new workflow that creates a PR from an approved issue, extracts task lists
from comments, creates CHANGELOG entries, and dispatches tasks to opencode-implement.yaml
sequentially. The implement workflow was refactored to accept a pull request number
and optional task description instead of an issue trigger.
Summary by CodeRabbit