Skip to content

feat: implement issue #1905 — [qa-lead reach 3/3] persona-runner ignores surface=pull_request — the router-served qa-lead advisory loses its test-surface, budget and already-advised gates - #1909

Open
don-petry wants to merge 18 commits into
mainfrom
dev-lead/issue-1905-20260923-0336
Open

don-petry wants to merge 18 commits into
mainfrom
dev-lead/issue-1905-20260923-0336

Conversation

@don-petry

@don-petry don-petry commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

[qa-lead reach 3/3] persona-runner ignores surface=pull_request — the router-served qa-lead advisory loses its test-surface, budget and already-advised gates

From the issue: Epic #1643 (Phase 2), decision (b) in ADR-0009 (#1869): qa-lead's pull_request advisory moves from the self-contained .github/workflows/qa-lead-pr-advisory.yml to the shared persona-mention router (petry-projects/.github#1165, PR petry-projects/.github#1167). The router dispatches repository_dispatch: persona-mention with client_payload[surface]=pull_request into .github/workflows/persona-runner.yml here.

Risk

Medium — changes GitHub Actions workflow behavior, which is exercised only post-merge; verify via the affected workflow runs.

Test plan

Tests added/updated: tests/test_persona_pr_pregate.bats. Verification: bash scripts/dev-lead-lint.sh (shellcheck --severity=warning) ran pre-commit; the bats suite runs in CI.

Rollback

Revert this PR. No non-revertible side effects (no tags, migrations, or external state).

Monitoring

Watch the affected workflow run(s) in the Actions tab and this PR's Lint check for regressions.

Closes #1905

Review in cubic

…res surface=pull_request — the router-served qa-lead advisory loses its test-surface, budget and already-advised gates
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: b2eb9c25-f162-4b68-ba60-0a7cf313bd90

📥 Commits

Reviewing files that changed from the base of the PR and between 5116d1a and 07036f5.

📒 Files selected for processing (7)
  • .github/workflows/lint.yml
  • .github/workflows/persona-runner-reusable.yml
  • .github/workflows/persona-runner.yml
  • .github/workflows/qa-lead-pr-advisory.yml
  • scripts/persona-pr-pregate.sh
  • scripts/qa-lead-pr-gate.sh
  • tests/test_persona_pr_pregate.bats

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.

❤️ Share

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

Comment thread .github/workflows/qa-lead-pr-advisory.yml
Comment thread scripts/persona-pr-pregate.sh Outdated
Comment thread scripts/qa-lead-pr-gate.sh

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a pre-gate mechanism for persona events to prevent the runner from posting on every trusted pull request. It adds signal gathering and decision-making logic in scripts/persona-pr-pregate.sh and scripts/qa-lead-pr-gate.sh, along with comprehensive unit tests in tests/test_persona_pr_pregate.bats. The feedback highlights a critical issue in scripts/qa-lead-pr-gate.sh where a standalone conditional expression under set -e can cause premature script termination. Additionally, it is recommended to replace generic non-zero exit code assertions in the test suite with specific expected exit codes to prevent masking unexpected test failures.

Comment thread scripts/qa-lead-pr-gate.sh Outdated
Comment thread tests/test_persona_pr_pregate.bats Outdated
Comment thread tests/test_persona_pr_pregate.bats Outdated
Comment thread tests/test_persona_pr_pregate.bats Outdated
Comment thread tests/test_persona_pr_pregate.bats Outdated
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-23T04:55:50Z.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 468fa3767f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

env:
# github.token reads the (public) source repo — the same read scope the
# agent step uses. The pre-gate only READS PR signals; it never posts.
GH_TOKEN: ${{ github.token }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Grant the pre-gate pull-request read permissions

The new pre-gate authenticates every API request with github.token, but both this reusable job and the calling job in .github/workflows/persona-runner.yml grant only contents: read; explicitly specifying that permission leaves pull-requests and issues unavailable. Consequently, a surface=pull_request dispatch for this private repository fails on its first /pulls/{pr}/files request, becomes skip:signal-unavailable, and never runs qa-lead, so the new router surface cannot be verified or used here. Request and forward at least pull-requests: read and issues: read for the caller and reusable job.

Useful? React with 👍 / 👎.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-23T05:02:35Z.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e6cffbf9b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/persona-runner-reusable.yml Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

4 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/qa-lead-pr-gate.sh">

<violation number="1" location="scripts/qa-lead-pr-gate.sh:108">
P2: Any PR commenter can suppress the advisory by quoting `` anywhere in a comment, because this treats a raw substring as proof that qa-lead already advised. Fetch comment authors and accept only a marker-first-line comment from the configured qa-lead posting account.</violation>

<violation number="2" location="scripts/qa-lead-pr-gate.sh:108">
P1: Serialize the local and router paths with the same per-PR concurrency group, or make the idempotency check and post atomic. Otherwise concurrent runs can both pass this marker check and post duplicate advisories.</violation>
</file>

<file name="tests/test_persona_pr_pregate.bats">

<violation number="1" location="tests/test_persona_pr_pregate.bats:59">
P3: The gh stub's fallback paths return empty-success: an unrecognized `--jq` filter falls through to `printf '[]\n'` (exit 0), and an unrecognized raw URL returns `[]` in the `''` branch. If a future change adds a new gather call with a new filter or URL, the harness will silently feed fabricated empty data with a success status, so suppressor tests can go green on signals that never existed — the same silent-degradation the pre-gate fails closed on. Make both fallbacks fail loudly (non-zero exit naming the unhandled filter/URL) so stub/implementation drift breaks the suite instead of passing it.</violation>
</file>

<file name="scripts/persona-pr-pregate.sh">

<violation number="1" location="scripts/persona-pr-pregate.sh:42">
P2: The generic already-advised gate can run after an invalid comment response because `jq '.[].body'` succeeds on `{}` and yields an empty stream. Validate that each paginated response is an array before scanning it, otherwise malformed API data can bypass idempotency and post a duplicate advisory.

(Based on your team's feedback about fail-closed API gates.) .</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

_qa_lead_pr_gate_fail_closed "$repo" "$pr" "existing-advisory scan unavailable"
return 1
fi
if grep -qF "$QA_LEAD_ADVISORY_MARKER" <<< "$comment_bodies"; then

@cubic-dev-ai cubic-dev-ai Bot Sep 23, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Serialize the local and router paths with the same per-PR concurrency group, or make the idempotency check and post atomic. Otherwise concurrent runs can both pass this marker check and post duplicate advisories.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/qa-lead-pr-gate.sh, line 108:

<comment>Serialize the local and router paths with the same per-PR concurrency group, or make the idempotency check and post atomic. Otherwise concurrent runs can both pass this marker check and post duplicate advisories.</comment>

<file context>
@@ -0,0 +1,131 @@
+    _qa_lead_pr_gate_fail_closed "$repo" "$pr" "existing-advisory scan unavailable"
+    return 1
+  fi
+  if grep -qF "$QA_LEAD_ADVISORY_MARKER" <<< "$comment_bodies"; then
+    existing_advisory=1
+  else
</file context>
Fix with cubic

_qa_lead_pr_gate_fail_closed "$repo" "$pr" "existing-advisory scan unavailable"
return 1
fi
if grep -qF "$QA_LEAD_ADVISORY_MARKER" <<< "$comment_bodies"; then

@cubic-dev-ai cubic-dev-ai Bot Sep 23, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Any PR commenter can suppress the advisory by quoting <!-- persona:qa-lead --> anywhere in a comment, because this treats a raw substring as proof that qa-lead already advised. Fetch comment authors and accept only a marker-first-line comment from the configured qa-lead posting account.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/qa-lead-pr-gate.sh, line 108:

<comment>Any PR commenter can suppress the advisory by quoting `<!-- persona:qa-lead -->` anywhere in a comment, because this treats a raw substring as proof that qa-lead already advised. Fetch comment authors and accept only a marker-first-line comment from the configured qa-lead posting account.</comment>

<file context>
@@ -0,0 +1,131 @@
+    _qa_lead_pr_gate_fail_closed "$repo" "$pr" "existing-advisory scan unavailable"
+    return 1
+  fi
+  if grep -qF "$QA_LEAD_ADVISORY_MARKER" <<< "$comment_bodies"; then
+    existing_advisory=1
+  else
</file context>
Fix with cubic

Comment thread .github/workflows/persona-runner-reusable.yml
local persona="$1" repo="$2" item="$3" marker bodies
marker="$(pr_agent_marker "$persona")"
if ! bodies="$(gh api --paginate \
"repos/${repo}/issues/${item}/comments" --jq '.[].body')"; then

@cubic-dev-ai cubic-dev-ai Bot Sep 23, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The generic already-advised gate can run after an invalid comment response because jq '.[].body' succeeds on {} and yields an empty stream. Validate that each paginated response is an array before scanning it, otherwise malformed API data can bypass idempotency and post a duplicate advisory.

(Based on your team's feedback about fail-closed API gates.) .

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/persona-pr-pregate.sh, line 42:

<comment>The generic already-advised gate can run after an invalid comment response because `jq '.[].body'` succeeds on `{}` and yields an empty stream. Validate that each paginated response is an array before scanning it, otherwise malformed API data can bypass idempotency and post a duplicate advisory.

(Based on your team's feedback about fail-closed API gates.) .</comment>

<file context>
@@ -0,0 +1,83 @@
+  local persona="$1" repo="$2" item="$3" marker bodies
+  marker="$(pr_agent_marker "$persona")"
+  if ! bodies="$(gh api --paginate \
+      "repos/${repo}/issues/${item}/comments" --jq '.[].body')"; then
+    echo "::error::persona pull_request pre-gate: existing-advisory scan unavailable for ${repo}#${item} — failing closed (skip)" >&2
+    printf 'skip:signal-unavailable\n'
</file context>
Suggested change
"repos/${repo}/issues/${item}/comments" --jq '.[].body')"; then
"repos/${repo}/issues/${item}/comments" --jq 'if type == "array" then .[].body else error("expected comments array") end')
Fix with cubic

Comment thread scripts/persona-pr-pregate.sh Outdated
exit "${STUB_GATHER_RC:-0}"
;;
esac
printf '[]\n'

@cubic-dev-ai cubic-dev-ai Bot Sep 23, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The gh stub's fallback paths return empty-success: an unrecognized --jq filter falls through to printf '[]\n' (exit 0), and an unrecognized raw URL returns [] in the '' branch. If a future change adds a new gather call with a new filter or URL, the harness will silently feed fabricated empty data with a success status, so suppressor tests can go green on signals that never existed — the same silent-degradation the pre-gate fails closed on. Make both fallbacks fail loudly (non-zero exit naming the unhandled filter/URL) so stub/implementation drift breaks the suite instead of passing it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_persona_pr_pregate.bats, line 59:

<comment>The gh stub's fallback paths return empty-success: an unrecognized `--jq` filter falls through to `printf '[]\n'` (exit 0), and an unrecognized raw URL returns `[]` in the `''` branch. If a future change adds a new gather call with a new filter or URL, the harness will silently feed fabricated empty data with a success status, so suppressor tests can go green on signals that never existed — the same silent-degradation the pre-gate fails closed on. Make both fallbacks fail loudly (non-zero exit naming the unhandled filter/URL) so stub/implementation drift breaks the suite instead of passing it.</comment>

<file context>
@@ -0,0 +1,221 @@
+    exit "${STUB_GATHER_RC:-0}"
+    ;;
+esac
+printf '[]\n'
+STUB
+  chmod +x "$STUB_BIN/gh"
</file context>
Suggested change
printf '[]\n'
echo "::error::gh stub: unhandled --jq filter '$jqf'" >&2; exit 42
Fix with cubic

Comment thread tests/test_persona_pr_pregate.bats Outdated
@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (partial)

A commit was pushed, but not every requested change was applied. Per requested item:

  • .github/workflows/qa-lead-pr-advisory.yml:84 — not applied
  • scripts/persona-pr-pregate.sh:67 — applied
  • scripts/qa-lead-pr-gate.sh:107 — not applied
  • scripts/qa-lead-pr-gate.sh:130 — applied
  • tests/test_persona_pr_pregate.bats:112 — applied
  • tests/test_persona_pr_pregate.bats:154 — applied
  • tests/test_persona_pr_pregate.bats:202 — applied
  • tests/test_persona_pr_pregate.bats:209 — applied

The unaddressed items above still need work.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6739306fe9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +194 to +196
# github.token reads the (public) source repo — the same read scope the
# agent step uses. The pre-gate only READS PR signals; it never posts.
GH_TOKEN: ${{ github.token }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use a token that can read the private source repository

When the router dispatch originates from another private org repository, this github.token belongs only to .github-private; adding pull-requests: read and issues: read expands its permissions within that repository but does not grant access to the SOURCE_REPO supplied in the payload. The first repos/${repo}/pulls/${pr}/files request therefore returns 403/404, and the new pre-gate converts that into run=false, leaving the workflow green without producing the fleet-wide advisory this route is intended to enable. Authenticate the pre-gate with a read-capable cross-repository credential, or gather and verify these signals in the source repository before dispatch.

Useful? React with 👍 / 👎.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-23T05:18:09Z.

@donpetry-bot

donpetry-bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor
Superseded by automated re-review at 6739306fe994dacf24db3e776431fb344afc73e3 — click to expand prior review.

Review — fix requested (cycle 1/3)

The automated review identified the following issues. Please address each one:

Findings to fix

Automated review — NEEDS HUMAN REVIEW

Risk: MEDIUM
Reviewed commit: 6739306fe994dacf24db3e776431fb344afc73e3
Review mode: triage-approved (single reviewer)

Summary

Implements #1905: persona-runner now honors client_payload.surface — a router-served pull_request dispatch runs the persona event pre-gate (shared qa-lead gather+decide in scripts/qa-lead-pr-gate.sh) before the engine, failing closed on unreadable signals. The local qa-lead-pr-advisory.yml gate now calls the same shared script (one implementation), and forwarding surface=pull_request from its advise job puts both paths in the reusable's per-(persona,repo,PR) concurrency group, closing the duplicate-advisory race. Code quality is good and all acceptance criteria are met, but the PR cannot auto-approve: the template-drift check is red on the head SHA, several checks are still pending, and reviewer threads remain unresolved — including a post-head-commit P1 about private cross-repo source reads.

Linked issue analysis

Issue #1905 is substantively addressed:

  • AC1: surface (default mention) and event_action forwarded by persona-runner.yml to the reusable; absent surface -> mention keeps existing dispatches unchanged (tested).
  • AC2: pull_request surface runs the persona pre-gate before the engine; qa-lead uses the existing qa_lead_gate_decision fed by the ONE shared gathering script (scripts/qa-lead-pr-gate.sh) — no copy.
  • AC3: fails closed with ::error naming the failed derivation; unregistered personas get the generic already-advised marker check plus a logged notice; an unrecognized surface also fails closed (added during review).
  • AC4: qa-lead-pr-advisory.yml behaviour preserved, now sourcing the shared script.
  • AC5: tests/test_persona_pr_pregate.bats covers run, every skip reason, fail-closed signals, and the mention path; wired into lint.yml's bats list.

Findings

Blocking (why this escalates):

  1. CI red on head SHA — template-drift FAILURE: repo-template stubs (agent-shield.yml, copilot-setup-steps.yml, dependency-audit.yml drifted; agent-ingress.yml missing) diverge from the standards baseline. None of these files are touched by this PR — the drift is pre-existing/org-level — but the check is red on this PR and needs a re-seed via scripts/seed-repo-template.sh (or a fix on main) before auto-approval.
  2. Unresolved review threads (7), most notably chatgpt-codex-connector's P1 posted at 04:13 (after the last commit, no author response): github.token gains pull-requests/issues:read only for THIS repo — it cannot read a private cross-repo SOURCE_REPO, so every routed pull_request advisory for a private source repo will fail closed and silently skip. This is fail-safe (no wrong posts) and consistent with the design note that personas serve public source repos, but it needs an explicit author disposition (accept as scoped-to-public, or use a cross-repo read credential) and the threads resolved.
  3. Checks still pending at review time (prompt-coverage, pr-auto-review) — cannot confirm an all-green rollup.

Assessed as addressed at head (threads just unmarked): the codex/codeant permissions P1 (pull-requests:read + issues:read now granted in both the reusable job and both callers) and the cubic/codeant duplicate-advisory race P1 (both paths now share the reusable concurrency group persona-runner---- since comment_url is empty on both, with cancel-in-progress:false serializing them and the second run's marker recheck suppressing the dup).

Minor, non-blocking: cubic P2 (marker substring quotable by any commenter — pre-existing behaviour, identical grep in the old inline gate); cubic P2 (validate comment payload is an array before jq '.[].body' — theoretical for a 2xx response); cubic P3 (gh test stub fallbacks return empty-success — test-hygiene nit).

Security review: no HIGH signals — new permissions are read-only and justified, actions remain SHA-pinned, untrusted payload values pass through env vars (not interpolated into run:), PATs stay isolated to the post step, secrets: inherit targets the local first-party reusable. gitleaks, CodeQL, agent-shield, actionlint, SonarCloud all green.

CI status

Red/pending on head 6739306: template-drift FAILURE (repo-template stub drift, unrelated to this PR's files); prompt-coverage and pr-auto-review still queued; stub-structure completed green on re-check. Green: shellcheck, bats, unit-tests, CodeQL (actions+python), gitleaks, agent-shield, actionlint, SonarCloud, Lint, caller-stub-freeze, holdout-guard, and the remaining structural gates. dev-lead dispatch/relay CANCELLED entries are superseded orchestration runs, not test failures.


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

@donpetry-bot

donpetry-bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor
Superseded by automated re-review at 6739306fe994dacf24db3e776431fb344afc73e3 — click to expand prior review.

Review — fix requested (cycle 2/3)

The automated review identified the following issues. Please address each one:

Findings to fix

Automated review — NEEDS HUMAN REVIEW

Risk: MEDIUM
Reviewed commit: 6739306fe994dacf24db3e776431fb344afc73e3
Review mode: triage-approved (single reviewer)

Summary

Re-review at the same head SHA as the prior fix-requested review (cycle 1). Independent re-verification confirms the implementation of #1905 is correct and complete: persona-runner forwards client_payload.surface/event_action, a pull_request dispatch runs the persona event pre-gate (ONE shared gather+decide in scripts/qa-lead-pr-gate.sh) before the engine, unknown surfaces and unreadable signals fail closed with ::error, and the local qa-lead-pr-advisory.yml now sources the same script while forwarding surface=pull_request — putting both paths in the reusable per-(persona,repo,PR) concurrency group and closing the duplicate-advisory race. But no new commits have landed since the prior review, and its blocking findings still stand: template-drift is red on the head rollup and all 7 review threads remain unresolved, including a post-head-commit codex P1 (private cross-repo source reads) with no author disposition. Cannot auto-approve.

Linked issue analysis

Issue #1905 is substantively addressed (independently re-verified against the diff):

  • AC1: surface (default mention) and event_action are new reusable inputs, forwarded by persona-runner.yml from client_payload with || 'mention' / || '' fallbacks — absent surface keeps existing mention dispatches byte-identical (tested).
  • AC2: when surface=pull_request the pregate step runs before identity/preflight/engine/post (all now gated on steps.pregate.outputs.run == 'true'); qa-lead uses the existing qa_lead_gate_decision fed by the ONE shared gathering implementation (scripts/qa-lead-pr-gate.sh) — no copy.
  • AC3: every unreadable signal fails closed with a ::error naming the derivation (changed files, count, labels, comments, budget events); an unrecognized surface also fails closed (skip:unknown-surface); personas with no registered gate get the generic already-advised marker check plus a ::notice.
  • AC4: qa-lead-pr-advisory.yml behaviour preserved, its gate job now sources the shared script; not retired.
  • AC5: tests/test_persona_pr_pregate.bats (17 cases) covers run, each skip reason, fail-closed on each unreadable signal, truncated file list, and the unchanged mention path; wired into lint.yml bats list. bats + shellcheck green.

Findings

Blocking (carried forward from the prior review at this same SHA — no new commits since):

  1. CI red on head rollup — template-drift FAILURE. repo-template stubs (agent-shield.yml, copilot-setup-steps.yml, dependency-audit.yml DRIFTED; agent-ingress.yml MISSING) diverge from the standards/v1-stable baseline. None of these files are touched by this PR — the drift is pre-existing and org-level — but the check is red on this PR and blocks the all-green gate. Fix belongs outside this PR (re-seed via scripts/seed-repo-template.sh or fix on main), then re-run the check.
  2. 7 unresolved review threads. Most notably chatgpt-codex-connector P1 (posted 04:13, after the last commit, no author response): github.token gains pull-requests/issues:read only for THIS repo — it cannot read a private cross-repo SOURCE_REPO, so every routed pull_request advisory for a private source repo will fail closed and silently skip. Fail-safe (no wrong posts) and consistent with the documented public-source-repo design, but it needs an explicit author disposition (accept as scoped-to-public, or use a cross-repo read credential) and the thread resolved. The codex/cubic permissions threads and the cubic P1 duplicate-advisory race are assessed as ADDRESSED at head (read scopes granted in the reusable job + both callers; both paths now share the reusable concurrency group with the marker recheck) — but the threads are still unmarked and must be resolved.
  3. Dev-lead fix pass reported partial (qa-lead-pr-advisory.yml:84 and qa-lead-pr-gate.sh:107 items not applied) and minted no new commit — the re-review trigger fired without the blockers changing.

Resolved since the prior review: prompt-coverage and stub-structure completed green; the only queued entry is this review's own pr-auto-review dispatcher.

Minor, non-blocking: cubic P2 (marker substring quotable by any commenter — pre-existing behaviour, identical grep in the old inline gate); cubic P2 (validate the comments payload is an array before jq .[].body); cubic P3 (gh test-stub fallbacks return empty-success).

Security review (this pass): no HIGH signals. New workflow permissions are read-only and justified in comments; untrusted client_payload values flow through env vars, never interpolated into run: bodies; actions stay SHA-pinned; the persona PAT remains isolated to the post step; secrets: inherit targets the local ./ first-party reusable. The run_secret_scanning MCP tool was not available in this run (noted, not fabricated); the gitleaks check is green.

CI status

Head 6739306: template-drift FAILURE (pre-existing repo-template stub drift, unrelated to this PR's files) — everything else green: shellcheck, ShellCheck, bats, unit-tests, unit, Lint, actionlint, CodeQL (actions+python), Secret scan (gitleaks), agent-shield, Agent Security Scan, SonarCloud, caller-stub-freeze, caller-permissions, toplevel-permissions, reusable-pin-compliance, prompt-coverage, stub-structure, holdout-guard, persona-reach-check, validate-* gates, gh-aw-compile. One QUEUED pr-auto-review/check-and-dispatch is this review's own dispatcher. CANCELLED dev-lead dispatch/relay/review entries are superseded orchestration runs, not test failures. mergeable=MERGEABLE, mergeStateStatus=BLOCKED (review required).


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

donpetry-bot
donpetry-bot previously approved these changes Sep 23, 2026

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: LOW
Reviewed commit: 6739306fe994dacf24db3e776431fb344afc73e3
Cascade: triage → audit (triage: haiku 4.5 [sonnet 5] → deep: opus 4.8 [sonnet 5] + duck: o4-mini → audit: fable 5)

Summary

The PR adds a fail-closed pull_request pre-gate to the persona runner so router-served qa-lead advisories apply the same suppressors as the local surface. The only security-relevant delta — pull-requests:read + issues:read on the secrets:inherit path — was verified minimal, read-only, and correctly mirrored at all three call sites; inputs flow via env into quoted shell (no expression injection) and the PAT-posting isolation is unchanged. The failing template-drift check is NOT in the branch ruleset's required set (verified via GET /rules/branches/main: SonarCloud, CodeQL, agent-shield/AgentShield, dependency-audit/Detect ecosystems, duplicate-decl-gate — all green), so all gates pass.

Findings

  • info: Added pull-requests:read and issues:read are least-privilege and mirrored at caller (persona-runner.yml), reusable (persona-runner-reusable.yml), and local advise job (qa-lead-pr-advisory.yml). Top-level permissions:{} retained; PAT selection/posting logic untouched; pre-gate uses only github.token and never posts.
  • info: All dispatch-derived values (persona, surface, source_repo, item_number, event_action) enter the pre-gate step via env: and are quoted in shell — no ${{ }} interpolation inside run: blocks. gh api paths are built from these env vars but repository_dispatch already requires repo write access (trusted router), and calls are read-only GETs.
  • info: template-drift is FAILURE at head, but it is NOT a required context per the main-branch ruleset (verified via gh api repos/.../rules/branches/main) and AGENTS.md documents it as routinely failing without blocking merge. The failure is pre-existing drift on petry-projects/repo-template stubs untouched by this PR. All five ruleset-required checks are SUCCESS.
  • minor: Confirmed from deep review: serialization of the local vs router-served advisory depends on the external ADR-0009 router emitting source_repo identical to github.repository and empty comment_url for pull_request dispatches, so both land in the same reusable concurrency group. If it diverges, worst case is a duplicate advisory comment (noise), not a security exposure. Author has flagged post-merge verification; the already-advised marker recheck limits blast radius.
  • minor: Confirmed from deep review: for a private SOURCE_REPO the github.token-based signal reads 403 and the gate fails closed, so routed advisories silently skip. Safe direction (no wrong post, no data exposure) and consistent with the documented public-source-repo assumption, but a silent functionality gap worth a follow-up issue for observability.

Reviewed by the PR-review cascade (triage: haiku 4.5 [sonnet 5] → deep: opus 4.8 [sonnet 5] + duck: o4-mini → audit: fable 5). Reply if you need a human review.

@donpetry-bot

Copy link
Copy Markdown
Contributor

pr-review approved on PARTIAL advisory evidence: 5/7 required advisory bots reported before the gate's head-age-timeout fallback proceeded. Recorded for the miss-rate metric (#1596).

@donpetry-bot
donpetry-bot dismissed their stale review September 23, 2026 04:51

Dismissing approval due to a PR issue comment lacking a verified disposition (#1813)

@donpetry-bot donpetry-bot added the needs-human-review Flagged by automated PR review agent label Sep 23, 2026
@donpetry-bot
donpetry-bot requested a review from a team September 23, 2026 04:54
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-23T06:11:55Z.

@don-petry

Copy link
Copy Markdown
Collaborator Author

dev-lead is withholding action on this item.

It is labeled needs-human-review (flagged for human review — this label is applied by automation as well as by people, so an item can become held without anyone noticing), so dev-lead will not pick it up while that label is present. This notice is posted once so the withhold is visible rather than looking like a stalled run.

To re-enable automated pickup: remove the needs-human-review label.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-25T04:29:46Z.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-25T05:26:26Z.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-25T06:56:31Z.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-27T00:37:42Z.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-27T03:26:10Z.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-09-29T19:53:28Z.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human-review Flagged by automated PR review agent

Projects

None yet

2 participants