Skip to content

fix: make CI review advisory by default (#23)#24

Merged
fatherlinux merged 3 commits into
masterfrom
fix/23-advisory-by-default
Jul 6, 2026
Merged

fix: make CI review advisory by default (#23)#24
fatherlinux merged 3 commits into
masterfrom
fix/23-advisory-by-default

Conversation

@fatherlinux

Copy link
Copy Markdown
Member

Closes #23.

Problem

The reusable review workflow ran gatehouse --stdin --comment, which exits 1 on critical/high findings and submits a REQUEST_CHANGES review. When a consumer wired it as a required status check, a non-deterministic LLM finding could block an otherwise-green PR. That's exactly what happened on crunchtools/mcp-trentina#56 — three runs produced three different sets of blocking HIGH findings, every one a verifiable false positive, forcing an admin-merge.

The crunchtools constitution (§XII) already declares Gatehouse an advisory gate. The implementation just didn't honor it. This makes it so.

Changes

  • review.yml: new blocking input (default false) → the workflow passes --advisory unless a caller explicitly opts in with blocking: true. The check now exits 0 by construction.
  • review.py / github.py: in advisory mode, post a plain COMMENT review instead of REQUEST_CHANGES (which otherwise lingers as an unresolved review even when the check is green).
  • examples/gatehouse.yml: corrected the guidance that told adopters to mark both jobs required. Only the workflow-protection guard job is a blocking gate; the review job must not be required.
  • Local CLI behavior is unchangedgatehouse still exits 1 on blocking findings for local/pre-commit gating. The fix is scoped to CI, where the incident occurred.
  • Docs: README "GitHub Actions" section, CHANGELOG, gatehouse-local constitution note.

Scope notes

  • Issue option feat: detect secret exfiltration in GitHub Actions workflow files #4 (deterministic head-scoped diffing) needs no code change. The workflow already feeds gh pr diff (net merge-base vs head); BASE_REF/GATEHOUSE_CONTEXT_REF only scopes styleguide/constitution loading, not the reviewed diff.
  • Options Add Documentation agent #2 (corroboration gate) and Add Constitution agent #3 (dismissible findings) are deliberately left out — advisory-by-default resolves the incident; those are larger follow-ups if blocking is ever re-enabled.
  • Constitution §XII strengthening ships as a separate PR in crunchtools/constitution.

Test

ruffmypypytest ✓ (126 passed, incl. a new test asserting advisory mode posts COMMENT, not REQUEST_CHANGES).

🤖 Generated with Claude Code

The reusable review workflow ran `gatehouse --stdin --comment`, which
exits 1 on critical/high findings and posts a REQUEST_CHANGES review.
Wired as a required status check, a non-deterministic LLM finding could
block an otherwise-green PR — which is exactly what happened on
mcp-trentina#56 (3 runs, 3 different all-false HIGH sets).

The crunchtools constitution already declares Gatehouse an advisory gate;
this makes the implementation honor that.

- review.yml: add a `blocking` input (default false) → pass --advisory
  unless the caller opts in. The check now passes by construction.
- review.py/github.py: in advisory mode post a COMMENT review, never
  REQUEST_CHANGES (which lingers as an unresolved review).
- examples/gatehouse.yml: stop telling adopters to mark the review job a
  required check; only the workflow-protection guard job is blocking.
- Local CLI behavior unchanged (still exits 1 on blocking findings).

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

@github-actions github-actions 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.

Gatehouse found 1 issues (1 high)

Comment thread src/gatehouse/review.py

has_blocking = _has_blocking_findings(all_results)
exit_code = 1 if has_blocking and not advisory else 0
request_changes = has_blocking and not advisory

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH (Documentation): The run_review function's behavior regarding GitHub PR review posting and exit codes has changed with the introduction of the advisory parameter's interaction with has_blocking. Specifically, in advisory mode, the function now always posts a COMMENT review and exits with code 0, even if blocking findings are present. The docstring for run_review does not appear to have been updated to reflect this significant change in its public interface behavior.

Suggestion: Update the docstring for the run_review function to clearly explain how the advisory parameter affects both the function's exit code and the type of GitHub PR review (COMMENT vs REQUEST_CHANGES) that is posted.

Evidence:

    has_blocking = _has_blocking_findings(all_results)
    request_changes = has_blocking and not advisory
    exit_code = 1 if request_changes else 0
    print_summary(all_results, exit_code)

    if comment:
        # In advisory mode, post a plain COMMENT review — never REQUEST_CHANGES,
        # which would still register as an unresolved review on the PR.
        await post_pr_review(all_results, request_changes)

@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 updates the reusable GitHub Actions review workflow to be advisory by default, meaning it posts plain comments and exits with code 0 unless the new blocking: true input is explicitly configured. It also updates the documentation, CLI constitution, and tests to align with this change. The review feedback correctly identifies two issues: an indentation error in the commented-out YAML example in examples/gatehouse.yml that would cause syntax errors if uncommented, and an outdated version reference (@v0.2.0) in the README.md example that does not support the newly introduced blocking parameter.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread examples/gatehouse.yml
Comment on lines +78 to +79
# with:
# blocking: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The commented-out indentation for with: and blocking: true is incorrect. If a user uncomments these lines by removing the comment prefix # , they will get:

      with:
        blocking: true

Since uses: is indented at 4 spaces, with: must also be indented at 4 spaces to be a valid sibling key. The current commented-out lines have 6 spaces of indentation for with:, which will cause a YAML syntax/validation error in GitHub Actions.

Please adjust the commented-out indentation so that removing # results in correct alignment.

    # with:
    #   blocking: true

Comment thread README.md
The review is **advisory by default**: findings post as PR comments and the check always passes, so a non-deterministic LLM finding can never block a merge. Do not mark it a required status check. To let critical/high findings fail the check (still not recommended as a required gate), opt in:

```yaml
uses: crunchtools/gatehouse/.github/workflows/review.yml@v0.2.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The example snippet references @v0.2.0 of the reusable workflow. However, the blocking input is a new feature being introduced in this PR (which is post-v0.3.0). If a user copies this snippet and runs it with @v0.2.0, GitHub Actions will fail with a validation error because the blocking input does not exist in that version.

Please update the reference to @main or the upcoming version tag (e.g., @v0.4.0) so that the documented feature is actually available.

Suggested change
uses: crunchtools/gatehouse/.github/workflows/review.yml@v0.2.0
uses: crunchtools/gatehouse/.github/workflows/review.yml@main

The repo's verbose_comments rule allows no standalone `#` comment blocks
(docstrings only). The rationale already lives in post_pr_review's
docstring and the test docstring.

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

@github-actions github-actions 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.

Gatehouse found 2 issues (2 low)

Comment thread tests/test_review.py
comment=True,
)
mock_post.assert_awaited_once()
assert mock_post.call_args.args[1] is True

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LOW (Consistency Check): An explanatory comment for the test assertion was removed, reducing the clarity and maintainability of the test. The comment clarified the condition under which a change review is requested.

Suggestion: Restore the comment # Blocking findings with advisory=False → request a change review. above the assertion to clarify its intent.

Evidence:

    639    |     mock_post.assert_awaited_once()
    640    |     assert mock_post.call_args.args[1] is True

Comment thread tests/test_review.py
)
assert exit_code == 0
mock_post.assert_awaited_once()
assert mock_post.call_args.args[1] is False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LOW (Consistency Check): An explanatory comment for the test assertion was removed, reducing the clarity and maintainability of the test. The comment clarified that advisory mode suppresses change requests.

Suggestion: Restore the comment # Advisory suppresses REQUEST_CHANGES → request_changes must be False. above the assertion to clarify its intent.

Evidence:

    672    |     mock_post.assert_awaited_once()
    673    |     assert mock_post.call_args.args[1] is False

Bump version, pin the reusable workflow's default image to :0.4.0, and
finalize the CHANGELOG for the advisory-by-default change (#23).

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

@github-actions github-actions 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.

Gatehouse found 1 issues (1 high)

Comment thread src/gatehouse/review.py
@@ -264,10 +264,11 @@ async def run_review(
format_results(all_results)

has_blocking = _has_blocking_findings(all_results)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HIGH (Documentation): The run_review function's behavior regarding its exit code and the type of GitHub PR review posted has changed significantly with the introduction of the 'advisory by default' logic. The advisory parameter now directly controls whether blocking findings result in an exit code of 1 and a 'REQUEST_CHANGES' review, or an exit code of 0 and a 'COMMENT' review. The function's docstring (not shown in the diff, but assumed to exist for a public function) should be updated to clearly explain this new behavior.

Suggestion: Update the docstring for the run_review function to explicitly describe how the advisory parameter influences the function's return value (exit code) and the type of GitHub PR review (COMMENT vs. REQUEST_CHANGES) that is posted. For example, clarify that if advisory is True, the function will always return 0 and post a COMMENT review, regardless of blocking findings.

Evidence:

  266    |     has_blocking = _has_blocking_findings(all_results)
  267 [+]|     request_changes = has_blocking and not advisory
  268 [+]|     exit_code = 1 if request_changes else 0
  269    |     print_summary(all_results, exit_code)
  270    | 
  271    |     if comment:
  272 [+]|         await post_pr_review(all_results, request_changes)
  273    | 
  274    |     return exit_code

@fatherlinux fatherlinux merged commit ecafc9f into master Jul 6, 2026
11 of 12 checks passed
@fatherlinux fatherlinux deleted the fix/23-advisory-by-default branch July 6, 2026 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI reviewer blocks merges on non-deterministic false-positive HIGH findings — make advisory / calibrate severity

1 participant