What starts a review
Draft pull requests are not reviewed by default. A mention overrides the settings whose job is to keep the reviewer quiet, such as a draft or a policy of reviewing only when asked. It does not override scope filters: an excluded branch stays excluded, and the reply says which setting declined.
The stages a review moves through
While a review is in flight the app shows one line of progress. These are the words it uses, in order: Queued, Reading the change, Summarising each file,Reviewing file X of Y counting up through the files, Verifying findings, Posting the review, and finally Done. A review that gave up says Could not review, and a stage the app does not recognise shows the single word Working rather than nothing at all.
In product terms, this is what sits behind those words.
1
The event arrives
The App receives the pull-request event. A delivery that is not signed is refused.
2
The change is split up
One piece per hunk or declaration. This is the point at which the check run appears, along with a reaction on the pull request saying the review has started.
3
Each piece is summarised
A short description of every piece, so the reviewer of one file knows what the others do.
4
The repository is indexed
Cortex reads the pull request’s head tree, not just the diff, so it can answer “who calls this from a file the change did not touch”. That is what makes a finding like “this function stopped accepting an empty value, and its caller in another file has always relied on it” expressible at all. The nearest files to the change are indexed first, up to a budget. The index is heuristic and line-oriented: a missed link costs context, and a spurious one costs an irrelevant caller being shown.
5
The change is reviewed
A file at a time, then bug theories are tested against it, then the deterministic checks run. See the layers below.
6
Every finding is verified
A separate second pass, described below, decides which candidates reach GitHub.
7
The change may be run
Where the deployment has an isolated runtime, Cortex Agent can run the change to gather evidence. It is skipped when there is nothing to run. The setting for it is on by default, but the runtime it needs is absent on most deployments, so in practice the run is recorded as unavailable, with
This deployment has no isolated runtime for Cortex Agent to use, so this change was not run. A run that could not happen is never a pass. See Cortex Agent runs.8
The review is published
Inline comments, the summary comment, the review event and the check run’s conclusion.
What looks at your change
Five things can produce a finding. All of them produce the same kind of finding, all of them go through the same second pass, and duplicates between them are collapsed into one comment. The pull-request page records which layer each finding came from.
The rules and the advisories are the two layers with nothing to switch on: they cost nothing to run, so they always run. A raised severity floor cannot silence either of them when the finding is filed under security, on the reasoning that somebody who raised their floor asked for a quieter reviewer rather than to stop being told about injection. A security finding the reviewer itself wrote is filtered normally, which keeps the exemption predictable. When a rule and the reviewer catch the same weakness on the same line, you get the reviewer’s prose with the rule’s identity and severity, so the comment that names the variable is the one that cannot be silenced.
Rule findings are titled plainly, and the title is what you read at the top of the comment: SQL query built by string concatenation, Certificate verification switched off, A live credential written into the source. Eleven such rules ship, they are Cortex’s own, and each body names the fix rather than only the problem. Because they match on shape, with exclusions so that the fix is not reported as the bug, a rule can miss and never needs a model.
Advisory findings are the ones no reviewer could make by reading the diff: the line is correct, the code using it is correct, and the defect is in the published package. They come from a built-in table of published advisories for npm, PyPI, Cargo and Go packages, so nothing is fetched while a review runs and two runs of the same review agree. Each advisory in that table carries its published identifier, a
CVE-… or a RUSTSEC-… id, which is what lets you go and read the original advisory rather than take the finding’s word for it.
A finding is made only when the change touches the manifest, and only when every version your declaration permits is affected. A range that already allows the fixed version is not a finding, which is the rule you can check against your own lockfile and agree with. Where the comment lands follows what you did to the manifest:
At most twenty advisory findings are made per review, reading at most 512 KB from any one manifest.
What is never reviewed
Generated and vendored content is recorded as touched and never reviewed: lockfiles, bundles, vendored trees. If every changed file falls in that set, the review ends as failed, with the reason Every changed file is generated or vendored, so there was nothing to review. It does not end as an approval, because nobody looked.The second pass
Every candidate finding is checked again by a separate pass, which is asked a closed question about it: is this specific claim supported by this specific text. That is a narrower question than “what is wrong with this file”, and two of its four rejections need no model at all.
Rejections are counted rather than quietly dropped. Open a review in the app and you get a disclosure reading N findings the verifier rejected, each carrying a
Not posted — {reason}. line, so a second pass that rejects everything is visible instead of merely quiet. When nothing survives at all, the review says so: Nothing survived verification. Every candidate finding either pointed at a line this pull request did not change or could not be supported by the change itself.
Posted, discarded, filtered out
A finding ends in one of three states, and the app keeps them apart on purpose.
The filtered ones appear under N findings your review policy filtered out, each with a reason: the finding was below the severity floor, below the confidence floor, filed under a criterion that is switched off, or silenced because this repository has dismissed that class of finding before.
The first three are settings on the policy form. The fourth is not a setting: after the same class of finding has been dismissed three times, this repository stops being sent it, and there is no screen listing what has been silenced. Anything blocking or major, anything carrying a CWE, and anything filed under security is never silenced this way, however often it is dismissed. See Review policy.
The verdict is arithmetic
What the review concludes is derived from what survived verification, so it can never disagree with the findings printed beside it. A model is never asked for the verdict.
“Complete” means every assigned file came back readable and the second pass ran. Without that condition, a review whose file stage died halfway would have no findings, and “no findings” would read as “approved”.
The first row is what a repository does before anybody changes a setting, and it is the one row a setting moves. On a blocking finding bends its last two columns together rather than just the check, so read them as a pair.
Two policy settings bend that pair of columns. On a blocking finding decides whether a blocking finding only comments, requests changes, or requests changes and fails the check, and each of the three moves both columns: on the first, the review event stays a plain comment and the check never reports Failure at all; on the second, changes are requested but the check still reports Success; only the third, the default, gives you the row above. Fail the check run at decides which severity is serious enough to fail it, separately from which findings get reported, so you can comment on everything and still only block on the serious. Whether a failing check blocks a merge is GitHub branch protection rather than a Cortex setting: a reviewer that could block a merge on its own authority would be one nobody could switch off in a hurry.
A failed review stays failed
When a review could not run, the row in the inbox reads Not reviewed and carries one sentence explaining which of seven things happened. Those sentences are the whole explanation. They are not platform error codes, and there is no error-code page to look them up in.
On the pull request itself the check run concludes Neutral, and the app’s verdict card says No verdict was reached for this commit, so the check is not an approval.
Skipped is not failed
A pull request your policy declined also reads Not reviewed in the inbox. The pill is the same as a failed review’s, and the difference is in the reason line on the row, so do not read the pill alone. Each reason names the setting you would go and change: that it is a draft and this repository does not review drafts, that reviews are switched off or happen only when asked, that a later push does not start a new review here, that the base branch or the author is not reviewed, that a required label is absent or a label suppresses review, or that the change is larger than the file limit. Anything else falls back to This repository’s review policy excludes it. Security troubleshooting works through them symptom by symptom.A pull request cannot talk the reviewer into anything
A change can contain a comment shaped like an instruction to the reviewer, written by somebody who knows a bot reads it. Every piece of the change crosses a trust boundary before it reaches the reviewer, the second pass or the summariser, with Cortex’s own delimiters neutralised inside it. It is handled the way a web page is handled: as text to be read, not as instructions to be followed. The same principle governs the settings a repository carries in its own files. A repository can keep instructions and rules on the branch under review, and they can make a review stricter or better informed. They cannot exclude a path, raise a threshold or switch the review off, because the author of a pull request would then have a say over the scrutiny it receives. A refused key is reported back in the review’s summary, so a setting quietly dropped and a setting never read do not look the same.Related
- Checks and comments - the check run, the summary comment, and what an inline finding looks like.
- Review policy - every setting the stages above consult.
- Cortex Agent runs - the layer that runs the change instead of reading it.
- Repository scans - the other question: what is wrong in this tree, rather than what this change introduced.
- Security troubleshooting - symptom by symptom, when a review does not do what you expected.