Extend the inbound-PR reviewer beyond external/fork PRs to internal org-repo
PRs that bypassed the agent task-flow (a human-pushed branch). The org's own
in-flight integration PRs are skipped — a live task owns their branch and they
already pass QA + PM review — so the reviewer only flags off-process PRs.
- config: internal_pr_enabled (default OFF, like external_pr_enabled)
- PR_REVIEW_SOURCES = (external_pr, internal_pr); generalize dispatch, dedup,
the decision queue, the git-gate exemption, and supersede to both sources
- TaskService.active_task_owns_branch (skip lifecycle PRs) + ingest source param
with source-aware wording
- poll loop runs when EITHER flag is on; _ingest_pr_if_reviewable picks the
source per PR (external: flag+author-allow; internal: flag+not-task-owned)
- 11 unit tests (decision logic + branch-ownership)
The PR-review queue only listed COMPLETED reviews and hid when empty, so while
a review was in_progress the panel showed nothing — no sign a review was
happening or where its findings go (the reviewer posts its change-request on
the PR itself). Add TaskService.list_external_pr_reviews (active reviews +
awaiting-decision, minus cancelled/decided/dismissed); the route uses it. The
panel card now shows active reviews with a 'Reviewing' badge and a link to the
PR where the change-request lands, and the Supersede/Dismiss actions only once
the review completes.
A notification can't be the gate: the reviewer is read-only and the CEO
decides what happens next. Backend for a real PR-review decision queue:
- post_pr_review now notifies the CEO (send_external_pr_reviewed_notification,
APPROVAL/HIGH, related_task_id) the moment a review lands — server-side
best-effort (the reviewer has no notify verb).
- TaskService.list_external_pr_reviews_awaiting_decision(): completed
external_pr reviews the CEO has neither superseded (confirmed_by_human) nor
dismissed (quick_context dismissed=1 marker).
- TaskService.dismiss_external_pr_review(): CEO declines → marker → leaves queue.
- GET /api/tasks/external-pr-reviews (PM+/CEO) and CEO-only
POST /api/tasks/{id}/dismiss-external-pr. Supersede already exists.
Panel queue wiring follows in the next commit.
The reviewer was one-shot: external_review_task_exists deduped on
(project, pr_number) only, so an external PR was reviewed exactly once ever —
a contributor pushing a fix never triggered a re-review (the review went
stale). Drive re-review off the PR's head commit instead:
- list_open_prs now returns head_sha (the change signal).
- ingest records the reviewed SHA as an external_pr_head=<sha> marker in the
review task's quick_context.
- external_review_task_exists is head-SHA aware: same SHA -> skip (unchanged);
new SHA -> open a fresh review (changed); no task yet -> first review;
legacy/markerless task or unknown SHA -> skip (never re-review on a guess,
so existing reviews don't re-fire after deploy).
No migration — reuses quick_context, like the supersede markers.