Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# CodeRabbit configuration — https://docs.coderabbit.ai/guides/configure-coderabbit
# CodeRabbit reviews every non-draft PR automatically (free for this public repo).
# its verdict is advisory: it gates nothing and closes nothing. the merge path is
# ci + trust-gate + CODEOWNERS, with the owner's auto-merge label as the go signal.
# ci + CODEOWNERS, with the owner's auto-merge label as the go signal.
# request_changes_workflow stays on so its stance is legible at a glance, but a
# request-changes review no longer blocks or reaps a pr.
language: "en-US"
Expand Down
45 changes: 0 additions & 45 deletions .github/workflows/trust-gate.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/zizmor.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# zizmor configuration — https://docs.zizmor.sh/configuration/
#
# the pr-bot workflows (auto-merge, trust-gate, ci-label, ui-screenshot-gate,
# the pr-bot workflows (auto-merge, ci-label, ui-screenshot-gate,
# workflow-lint) pin their actions and annotate their triggers. these three
# audits are disabled repo-wide so the new workflow-lint gate does not force a
# full pin / least-permissions migration of the older workflows in a single
Expand Down
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,18 @@ All notable changes to vouch are documented here. Format follows
removes the machinery that outlived it rather than lowering a live bar.
CodeRabbit still reviews every non-draft pr and still files formal
approve / request-changes reviews — they are advisory now. the merge
path is ci + trust-gate + CODEOWNERS, with the owner's auto-merge label
as the go signal.
path is ci + CODEOWNERS, with the owner's auto-merge label as the go
signal.
- **the `trust-gate` workflow is removed.** it failed a pr when an author
outside the OWNER association touched a core path — a bar that the
rewritten `arm-auto-merge.yml` already enforces from the other side:
nothing arms without the owner's own label, green `diff coverage`, and a
closing reference to an owner-opened issue, and CODEOWNERS still holds
the review requirement on core paths. the `trust` pr_bot command and its
`is_trusted` helper go with it. core-path classification stays — it is
what `arm-auto-merge.yml` reads. **remove `trust-gate` from the `test`
ruleset's required checks**, or every pr will block on a check that no
longer reports.
- **auto approval is the default** (`review.approver_role: trusted-agent`
in the starter config): a fresh KB approves the capturing agent's
proposals with no human step. nothing bypasses the gate — every write
Expand Down
18 changes: 3 additions & 15 deletions src/vouch/pr_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Pure stdlib — no model dependency, no vouch-runtime imports. The CI workflows
call ``python -m vouch.pr_bot <subcommand>`` for every decision that must be
trustworthy: an author's trust tier, whether a PR touches core/ui paths, whether
a UI PR carries before/after screenshots, and whether a labeled PR may arm
native auto-merge. CodeRabbit runs as a GitHub App and still comments on PRs,
trustworthy: whether a PR touches core/ui paths, whether a UI PR carries
before/after screenshots, and whether a labeled PR may arm native auto-merge.
CodeRabbit runs as a GitHub App and still comments on PRs,
but its verdict no longer gates anything — nothing here reads it.
"""
from __future__ import annotations
Expand Down Expand Up @@ -43,8 +43,6 @@
"webapp/**",
)

_OWNER_ASSOCIATION = "OWNER"
_BOT_ACTORS = frozenset({"dependabot[bot]"})


def _match(path: str, glob: str) -> bool:
Expand Down Expand Up @@ -72,10 +70,6 @@ def klass(changed: Sequence[str]) -> str:
return "core" if c["is_core"] else "ui" if c["is_ui"] else "code"


def is_trusted(author_association: str, actor: str) -> bool:
return author_association == _OWNER_ASSOCIATION or actor in _BOT_ACTORS


_GH_IMAGE = re.compile(
r"""(?:!\[[^\]]*\]\(\s*|<img\b[^>]*\bsrc\s*=\s*["']?)"""
r"""(?:https?://(?:user-images\.githubusercontent\.com/"""
Expand Down Expand Up @@ -222,10 +216,6 @@ def main(argv: Sequence[str] | None = None) -> int:
sp = sub.add_parser(name)
sp.add_argument("--files-file", required=True)

t = sub.add_parser("trust")
t.add_argument("--author-association", required=True)
t.add_argument("--actor", required=True)

s = sub.add_parser("has-screenshots")
s.add_argument("--body-file", required=True)

Expand Down Expand Up @@ -253,8 +243,6 @@ def main(argv: Sequence[str] | None = None) -> int:
return 0 if classify(_read_lines(ns.files_file))["is_core"] else 1
if ns.cmd == "ui-touched":
return 0 if _touches(_read_lines(ns.files_file), UI_GLOBS) else 1
if ns.cmd == "trust":
return 0 if is_trusted(ns.author_association, ns.actor) else 1
if ns.cmd == "has-screenshots":
with open(ns.body_file, encoding="utf-8") as fh:
return 0 if has_before_after_screenshots(fh.read()) else 1
Expand Down
18 changes: 2 additions & 16 deletions tests/test_pr_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,6 @@ def test_core_paths_all_flagged():
assert pr_bot.classify([f])["is_core"] is True, f


def test_trust():
assert pr_bot.is_trusted("OWNER", "plind-junior") is True
assert pr_bot.is_trusted("CONTRIBUTOR", "rando") is False
assert pr_bot.is_trusted("NONE", "dependabot[bot]") is True


def test_screenshots_two_gh_images():
body = (
"before\n![a](https://user-images.githubusercontent.com/1/a.png)\n"
Expand Down Expand Up @@ -89,14 +83,6 @@ def test_cli_classify_print_klass(tmp_path):
assert out.stdout == "ui"


def test_cli_trust_exit_codes():
ok = subprocess.run([sys.executable, "-m", "vouch.pr_bot", "trust",
"--author-association", "OWNER", "--actor", "plind-junior"])
bad = subprocess.run([sys.executable, "-m", "vouch.pr_bot", "trust",
"--author-association", "NONE", "--actor", "rando"])
assert ok.returncode == 0 and bad.returncode == 1


def test_extract_changed_paths_plain_file():
files_json = '[{"filename": "src/vouch/context.py"}]'
assert pr_bot.extract_changed_paths(files_json) == ["src/vouch/context.py"]
Expand All @@ -113,8 +99,8 @@ def test_extract_changed_paths_includes_previous_filename_on_rename():


def test_rename_of_core_path_still_classifies_core():
# a rename that lands a core path under a new name must not slip past
# trust-gate — the pre-rename path has to stay in the classified list.
# a rename that lands a core path under a new name must not slip past the
# core gate — the pre-rename path has to stay in the classified list.
files_json = (
'[{"filename": "src/vouch/web_server.py", "status": "renamed", '
'"previous_filename": "src/vouch/http_server.py"}]'
Expand Down
Loading