Triggers and Commands
What starts a review automatically, the @anton comment commands, and who is allowed to use them.
This page describes son-of-anton-review (feat/cloudflare-native). Pull request webhooks, manual review comments and operator requeues can request reviews. Configured competitor-review events can also request audits. These paths share review processing, but do not all use the same queue-job or dedupe key.
Automatic triggers
A pull_request webhook can request a review for opened, reopened, ready_for_review or synchronize. The native ingress filters events before enqueueing; configuration and authorization are checked downstream, not all before a Cloudflare Queue slot is used.
edited updates stored PR metadata when delivered directly to ReviewService. The native ingress instead records it as an ignored unsupported_action and does not dispatch it to that service.
The standalone service webhook handler applies src/config.js / shouldAutoReview:
| Config key | Default | Effect |
|---|---|---|
enabled | true | Repo is reviewed at all |
review.auto | true | Webhooks may queue reviews |
branches.include | ["main", "dev"] | Base branch must be in this list |
review.onDraft | false | Draft PRs are skipped |
The native container instead seeds a run directly and applies evaluateRuntimeAdmission. It does not call shouldAutoReview, so the main/dev, review.auto and review.onDraft defaults above are not native admission gates. A configured repository uses its enabled value; without config, admission depends on prior review history or autoEnableNewRepos (policy default false).
The pinned review policy (src/operator-review-policy.js, DEFAULT_REVIEW_POLICY) applies:
| Policy key | Default | Effect |
|---|---|---|
autoReviewNewCommits | true | false skips synchronize; open, reopen and ready-for-review remain eligible |
reviewDrafts | false | Draft PRs return draft_review_disabled |
fileChangeLimit | 500 | PRs changing more files return file_change_limit |
pausedAuthors | [] | Logins whose PRs are never reviewed, manual commands included |
filters | [] | Rule list, see below |
Standalone event filtering
src/pr-update-engine.js rejects unsupported actions, merge-commit events and no-op updates. Merge detection uses supplied metadata: an explicit merge flag, multiple head-commit parents, or a message beginning Merge branch, Merge pull request or Merge remote-tracking branch. It does not fetch the head commit to prove that it is a merge.
For synchronize, equal previous/current SHAs or a head already stored for the PR count as no_op. An explicit noChanges also skips the event. The service passes skipBots: false and skipDraft: false to this helper; draft admission is handled by repo config and review policy instead.
The transition helper defines a latest-waiting key, {owner}/{repo}#{pr}:latest-waiting, and can return replace_waiting when given an earlier waiting job. Do not equate that helper with removal of a message already in Cloudflare Queues.
Dedupe keys
Two layers:
- Native ingress writes
delivery:{X-GitHub-Delivery}intoDEDUPEKV withDEDUPE_TTL_SECONDS(default 604800 seconds, seven days). A repeat answersduplicate_delivery. Recording/enqueue failures attempt to delete the key and return HTTP 500 so a redelivery can be accepted. KV is eventually consistent; downstream leases also matter. This setting is read in the Worker, not the container. - Review key
{owner}/{repo}#{pr}@{headSha}identifies the run. Manual commands append a variant,manual-{commentId}, so a fresh@anton reviewon an unchanged head is a new run rather than askipped_lockedhit.
Comment commands
Post a top-level PR comment. The parser (src/commands.js) accepts:
@anton review
@anton rerun
@anton status
@anton help@gilf is an alias for every verb. Matching is case-insensitive, whitespace is collapsed, and the comment must start with the command:
^(?:@anton|@gilf)\s+(review|rerun|status|help)(?:\s+(.*))?$| Verb | What happens |
|---|---|
review | Queues a run for the stored PR head |
rerun | Same review-request behavior as review |
status | Replies with the latest stored PR run status and stored head abbreviation, no new run; the latest run is not filtered to that head |
help | Replies with the command list, no run |
The standalone service only executes commands on created comments. Native ingress accepts created and edited issue comments and its consumer keeps only parsed review/rerun commands; native seeding does not repeat the standalone created-only check. The native review identity remains scoped to the comment ID, so editing one comment is not the same as posting a new rerun command.
Trailing text is parsed as args, but does not select another PR or head. The standalone service dedupes commands by {owner}/{repo}#{pr}@{commentId} and returns duplicate_command for an existing record. Native dispatch uses the review key and lease instead.
Not available: @anton ignore, @anton resolve, @anton learn. Those parse to no_command and are ignored.
Comment handling can be switched off per repo with manual.enabled: false.
Who can run commands
isAuthorizedCommenter checks the GitHub author_association on the comment against the repo config:
{
"manual": {
"enabled": true,
"allowAssociations": ["OWNER", "MEMBER", "COLLABORATOR"],
"allowUsers": []
}
}A login in allowUsers passes regardless of association. Anyone else is recorded as unauthorized_commenter and gets no reply. pausedAuthors still applies to manual runs.
The service requires a stored PR head even for help and status, otherwise it returns unknown_pr_head. Its reply strings still use the legacy Gilf name and help lists the @gilf aliases. The parser accepts both names.
Manual review requests bypass the policy's automatic draft, file-count and filter checks, but not pausedAuthors or separate repository admission/execution checks. Native seeding also checks manual.enabled and isAuthorizedCommenter against the authenticated webhook comment. Native status/help comments are acknowledged as no_review_command without dispatching a reply container.
Policy filters
filters is a list of rules. Any matching rule satisfies this filter gate; within a rule every condition must match. An empty list imposes no filter restriction, but other admission gates still apply. Maximum 50 rules, 20 conditions each.
| Field | Operators | Values |
|---|---|---|
label | is, is_not | Label names |
author | is, is_not | Logins, lowercased |
repository | is, is_not | owner/repo |
targetBranch | is, is_not | Globs |
sourceBranch | is, is_not | Globs |
path | is, is_not | Globs against changed paths |
title | contains, not_contains | Substrings |
keyword | contains, not_contains | Substrings in title or body |
draft | is, is_not | None |
filesChanged | at_most, more_than | One nonnegative integer string, for example "500" |
{
"filters": [
{ "conditions": [
{ "field": "targetBranch", "operator": "is", "values": ["main", "release/*"] },
{ "field": "path", "operator": "is_not", "values": ["docs/**"] }
] }
]
}Globs are validated: no .., no partial ** segments. A rule that needs metadata not yet available (for example changed paths) defers the decision until preflight fills it.
Execution mode gate
Sandbox validation is gated separately from review admission:
{ "execution": { "mode": "always", "filters": [] } }| Mode | Effect |
|---|---|
always | Allows validation; docs/generated skips and executor availability still apply |
never | Analysis only, summary shows execution_disabled |
filters | Same rule engine as above, unmatched PRs show execution_filters_not_matched |
Operator rerun and requeue
From the host:
node bin/gilf-review.mjs requeue owner/repo#123
node bin/gilf-review.mjs requeue "owner/repo#123@<sha>" --variant manual-retryWithout --variant, the CLI appends operator-{timestamp} to the base review key. --json prints the result object, including reviewKey.
The separate son-of-anton-operator-parity tree (feat/greptile-operator-parity) implements the following durable command endpoint. It is not an endpoint on the native ingress Worker:
POST /operator/api/commands/requeue
{ "repo": "owner/name", "number": 123, "variant": "manual-20260824", "reason": "operator_force_review" }POST requires OPERATOR_COMMAND_TOKEN; GET /operator/api/commands and /operator/api/commands/:commandId accept OPERATOR_READ_TOKEN or OPERATOR_BRIDGE_TOKEN. Sibling commands: retry-publish and replay-webhook.
The CLI needs a stored PR head or exact stored review key and an installation ID. Run it from the engine checkout against the intended store/queue; it is not a stateless remote API client.
Source evidence
son-of-anton-review/src/commands.js:9-39;src/config.js:1-45,86-97.son-of-anton-review/src/pr-update-engine.js:81-122,195-231;src/review-service.js:661-838.son-of-anton-review/src/operator-review-policy.js:26-78,138-180.son-of-anton-review/cloudflare-native/src/consumer.js:344-355;cloudflare-native/src/d1-store-adapter.js:146-170;src/review-policy-runtime.js:39-42.son-of-anton-review/cloudflare-native/src/ingress.js:59-132,295-345.son-of-anton-review/src/operator-cli.js:187-261,629-644.son-of-anton-operator-parity/cloudflare/src/worker.js:69-80;cloudflare/src/review-state-api.js:934-942.
See Context graph for preflight behavior and Publish modes for publication boundaries.