Skip to content

Add Concurrent CI Review Gate - #143

Open
danchild wants to merge 8 commits into
forge-sdlc:mainfrom
danchild:feat/concurrent-ci-review-gate
Open

Add Concurrent CI Review Gate#143
danchild wants to merge 8 commits into
forge-sdlc:mainfrom
danchild:feat/concurrent-ci-review-gate

Conversation

@danchild

@danchild danchild commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Reworks the post-PR lifecycle so CI evaluation and human review run concurrently. Previously, workflows blocked at wait_for_ci_gate until all CI checks passed before proceeding to human review. Now, after PR creation the workflow goes directly to human_review_gate and accepts both CI and review webhooks in parallel — matching GitHub's natural PR workflow where merge is the final gate.

Key changes:

  • Eliminate the wait_for_ci_gate node; CI webhooks are routed to human_review_gate instead
  • Add pending_ci_event state field for buffering CI webhooks at human_review_gate
  • Extract shared post-PR lifecycle wiring into post_pr.py (reused by feature, bug, task_takeover graphs)
  • Add CI failure attribution phase before attempting fixes
  • Fix CI webhook routing to preserve current_node at human_review_gate (prevents premature override to ci_evaluator)

This change is not backward compatible. In-flight workflows checkpointed at wait_for_ci_gate will need to be manually restarted.

Test plan

  • All existing unit tests pass (1919 passed)
  • Fast Tests & Coverage CI gate passes
  • Contract & Workflow Flow Tests pass
  • Integration Tests pass
  • E2E Smoke Tests pass
  • Lint passes
  • Container Build passes
  • CI webhook at human_review_gate correctly sets pending_ci_event and preserves current_node
  • CI webhook at ci_evaluator still routes directly (no pending_ci_event)
  • Multi-repo CI webhook activates correct PR record and sets pending_ci_event
  • Review gate resume event handling fully covered (12 tests re-added for review_response_gate and human_review_gate behaviors)

Closes #137

@danchild
danchild force-pushed the feat/concurrent-ci-review-gate branch from 30c8e82 to 6daf65b Compare July 13, 2026 19:17
@eshulman2 eshulman2 added enhancement New feature or request area:workflow Workflow graphs, orchestration, state, routing, and gates ci Continuous integration, checks, and CI repair flows work in progress labels Jul 16, 2026
@danchild
danchild force-pushed the feat/concurrent-ci-review-gate branch 5 times, most recently from 4fc89db to 5dc629f Compare July 23, 2026 20:21
@danchild
danchild force-pushed the feat/concurrent-ci-review-gate branch 10 times, most recently from 22133a4 to 6761477 Compare July 24, 2026 19:46
@danchild
danchild marked this pull request as ready for review July 24, 2026 19:49
@danchild
danchild force-pushed the feat/concurrent-ci-review-gate branch 4 times, most recently from 70913d3 to 32846a6 Compare July 30, 2026 13:49
danchild and others added 5 commits July 31, 2026 16:27
Add pending_ci_event to WorkflowState for buffering CI webhooks that
arrive while the workflow is at human_review_gate. Remove the orphaned
PR status comments test file.

Note: this change is not backward compatible with in-flight workflows
checkpointed at wait_for_ci_gate. Those workflows will need to be
manually restarted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Dan Childers <dchilder@redhat.com>
…tion

Merge wait_for_ci_gate into human_review_gate so CI checks and human
review run concurrently instead of sequentially. human_review_gate now
handles initial-entry housekeeping (posting status comments, setting
review labels) and routes pending CI events to ci_evaluator.

Add Phase 0 CI attribution check so ci_evaluator can distinguish
build-breaking changes from pre-existing failures before attempting
fixes. Fix double-increment of ci_fix_attempt in attempt_ci_fix.
Remove the now-unused wait_for_ci_gate node and clean up stale
references across implement_review and pr_creation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Dan Childers <dchilder@redhat.com>
Replace duplicated post-PR node/edge wiring across bug, feature, and
task-takeover graphs with a shared post_pr module exposing
add_post_pr_nodes() and add_post_pr_edges(). This eliminates ~300 lines
of triplicated graph construction code while updating all graphs to
use human_review_gate instead of the removed wait_for_ci_gate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Dan Childers <dchilder@redhat.com>
Extend worker CI webhook routing to deliver check-suite and
check-run events to human_review_gate (in addition to ci_evaluator
and attempt_ci_fix). When the workflow is at human_review_gate, the
event is stored as pending_ci_event so the gate can route to
ci_evaluator on its next invocation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Dan Childers <dchilder@redhat.com>
Integration-style tests verifying the end-to-end concurrent gate
behavior: CI events arriving while at human_review_gate are buffered
and processed, review approval works independently of CI state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Dan Childers <dchilder@redhat.com>
Align three test files with renamed/moved symbols:
- _route_ci_evaluation moved from feature.graph to post_pr
- wait_for_ci_gate replaced by human_review_gate
- CI routing: pending now goes to human_review_gate instead of END
- attempt_ci_fix now posts two comments (attribution + fix attempt)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Dan Childers <dchilder@redhat.com>
@danchild
danchild force-pushed the feat/concurrent-ci-review-gate branch from 32846a6 to f35d221 Compare July 31, 2026 20:27
eshulman2 and others added 2 commits August 3, 2026 10:48
The pending_ci_event flag was never set because the test state lacked
pull_requests data, and the early current_node override unconditionally
switched to ci_evaluator before the pending_ci_event branch could run.

- Skip the ci_evaluator override when already at human_review_gate
- Add pull_requests + repository to the test fixture so
  event_targets_pull_request returns True
- Update multi-repo test to expect the new human_review_gate behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The PR deleted TestHandleResumeEventReviewGates (15 tests) when removing
the review_response_gate inline-reply handling. Of those, 12 tested
behavior that still exists in the codebase with no equivalent coverage
elsewhere. Re-add them:

- Login caching (_get_forge_github_login)
- Self-authored review suppression (prevents infinite loops)
- changes_requested/commented/approved at review_response_gate
- PR merge at review_response_gate
- Review ID vs fallback API branching for inline comments
- changes_requested at human_review_gate
- is_paused=False guard (review ignored when not paused)
- _FRESH_INVOKE_NODES invariant
- Handler-to-router integration (routes to implement_review)

The 3 correctly deleted tests covered inline-reply thread resolution
at review_response_gate, which was intentionally removed by this PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@eshulman2 eshulman2 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Automated review: found several correctness regressions in the CI/human-review lifecycle rework that should be addressed before merge. Two additional issues fall outside this diff's changed lines and couldn't be attached inline, noting them here:

src/forge/orchestrator/worker.py (around line 886, in _handle_resume_event) — The dedicated handler that resumed the workflow when a human replied inline to a specific contested-comment thread at review_response_gate (matching in_reply_to_id against contested_comments and clearing only that thread) was deleted with no replacement; only the unrelated PRD/spec-proposal-PR inline-reply path remains (is_proposal_reply check). Failure scenario: a human posts an inline reply on a GitHub review thread that Forge previously contested on an implementation PR. No handling matches this event for review_response_gate anymore, so is_rejected/is_approved are never set and the workflow isn't resumed — the reply is silently ignored. Two tests exercising exactly this behavior (test_inline_reply_resumes_only_its_contested_thread, test_standalone_inline_comment_is_actionable_at_response_gate) were deleted rather than adapted.

src/forge/workflow/pr_state.py (line ~100, save_active_pull_request / _PR_LIFECYCLE_NODES) — This file wasn't touched by the diff but is broken by it: save_active_pull_request still defaults a new PR record's lifecycle_node to the now-deleted "wait_for_ci_gate" node, and _PR_LIFECYCLE_NODES still lists it. Failure scenario: in a multi-repo ticket, create_pull_request calls save_active_pull_request with current_node="teardown_workspace", which isn't in _PR_LIFECYCLE_NODES, so lifecycle_node gets stamped as "wait_for_ci_gate" for every non-final repo. A later /forge skip-gate <name> comment targeting an earlier repo restores that dead node, which is no longer in worker.py's local _CI_STAGES tuple, so the skip-gate command is silently dropped even though the ticket is genuinely at a CI-related stage.

next_node = "review_response_gate"
else:
next_node = "wait_for_ci_gate" if unpushed else "human_review_gate"
next_node = "human_review_gate"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

next_node is hardcoded to "human_review_gate" even when objections exist, contradicting this function's own docstring and the code above that still builds contested_comments and posts objections.

Failure scenario: the analysis container marks a review comment as "contest"; Forge posts the objection to the PR/Jira as designed, but the workflow proceeds straight to human_review_gate instead of pausing at review_response_gate. Combined with the deleted inline-reply handler in worker.py (see review summary), a human replying to the contested thread has no mechanism to resolve it.

await jira.close()
logger.info(f"Pausing {ticket_key} at human_review_gate after PR creation")
else:
logger.info(f"Pausing {ticket_key} at human_review_gate (ci_status={ci_status!r})")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

human_review_gate now unconditionally sets is_paused: True, removing the prior explicit is_paused: False branch for pr_merged; nothing downstream resets it.

Failure scenario: PR merges and routing correctly proceeds to current_node="complete" within the same ainvoke, but is_paused stays True (inherited via {**state} spreads). In worker.py, the completion-recording branch guarded by if not is_paused never fires for a normal completed workflow, and a later stray webhook logs "Workflow is paused at complete" and sets should_resume=True based on stale state.


# GitHub issue_comment events: detect /forge skip-gate and /forge unskip-gate
# commands posted as PR comments.
_CI_STAGES = ("ci_evaluator", "attempt_ci_fix", "human_review_gate")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A new local _CI_STAGES is defined inside _handle_resume_event, shadowing the module-level _CI_STAGES which still references the deleted wait_for_ci_gate node and omits human_review_gate.

Failure scenario: the module-level _CI_STAGES is now dead code diverging from reality. Any future refactor that removes the local shadow (e.g. extracting the skip-gate block into a helper) will silently fall back to the stale tuple, re-breaking /forge skip-gate at human_review_gate.

@eshulman2 eshulman2 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Before merging, please provide one of the following for workflows currently checkpointed in the CI stage:

  1. Backward compatibility: retain a temporary route/alias from to , including migration of any values that still reference ; or
  2. A clear, tested migration path for affected tickets that preserves their existing PR mappings, per-PR CI/review state, and branches.

Simply sending another webhook will resume the existing checkpoint rather than restart it, while clearing the checkpoint can discard PR lifecycle state and cause work to be regenerated. The PR description currently says workflows must be manually restarted, but does not define a safe procedure or provide tooling to identify and migrate affected tickets.

Please document the exact operator steps, expected state transformations, rollback behavior, and tests covering an in-flight workflow at (including a multi-repository ticket).

@eshulman2 eshulman2 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Correction/clarification to my previous review comment:

Before merging, please provide one of the following for workflows currently checkpointed in the CI stage:

  1. Backward compatibility: retain a temporary route or alias from wait_for_ci_gate to human_review_gate, including migration of any per-PR lifecycle_node values that still reference wait_for_ci_gate; or
  2. A clear, tested migration path for affected tickets that preserves their existing PR mappings, per-PR CI/review state, and branches.

Simply sending another webhook resumes the existing checkpoint rather than restarting it, while clearing the checkpoint can discard PR lifecycle state and cause work to be regenerated. The PR description currently says workflows must be manually restarted, but does not define a safe procedure or provide tooling to identify and migrate affected tickets.

Please document the exact operator steps, expected state transformations, rollback behavior, and tests covering an in-flight workflow at wait_for_ci_gate, including a multi-repository ticket.

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

Labels

area:workflow Workflow graphs, orchestration, state, routing, and gates ci Continuous integration, checks, and CI repair flows enhancement New feature or request work in progress

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run CI evaluation and human review concurrently instead of sequentially

2 participants