# A Fix Is Not a Verdict: How Apex Verifies Security Patches > In a frozen three-month production cohort, Fix Review surfaced 97 issues across 62 of 193 fix pull requests. Here is what those patches missed and how Apex verifies them now. Author: Isaac Chin Published: August 29, 2026 Topics: Vulnerability Research, Fix Review, AI remediation, Security patches Canonical URL: https://www.cantina.security/blog/end-to-end-fix-review-workflows A closed security ticket does not prove the exploit is gone. Fix Review tests the submitted code against the exploit path, safety properties, and supported behavior established during the original investigation. Submitted fix pull requests failed in two ways: the original vulnerability remained reachable, or the patch introduced another security problem. The figures carry the exact counts and denominators. [Figure: Production review cohort and code targets](https://www.cantina.security/blog/end-to-end-fix-review-workflows#fix-review-scale) ## Scope and definitions The published dataset contains disclosure-safe aggregates only. It excludes names and identifiers for tenants, workspaces, repositories, pull requests, findings, users, and commits. A **review** is one initiated attempt against a linked pull request or the repository's current branch. A **substantive review** passes the no-change preflight and ends with **Fix confirmed** or **Fix Incorrect**. The pass chart includes only findings that eventually reached confirmation. These results are historical; the workflow figure documents current behavior. ## Select the code under review Target identity is part of the verdict. Pull-request mode checks the exact linked checkout. Branch mode reads the current default branch and is intended for a merged fix. Combining those targets would obscure which code the result covers. Start from a finding, a filtered findings list, a signed pull-request comment, or a scheduled recheck. [The workflow guide](https://apex.cantina.security/docs/fix-review) documents permissions and target-selection rules. If the finding's original location has not changed, the run stops before analysis. This preflight returned **Fix Pending** for 154 attempts within 2.2 seconds; those runs were skipped, not fast confirmations. ## What failed inside submitted patches The observed failures were semantic, not syntactic. A patch could compile, pass its local tests, and still leave the security property false. [Figure: Issues found in submitted security patches](https://www.cantina.security/blog/end-to-end-fix-review-workflows#fix-review-patch-issues) [Figure: Semantic patch failures and required proof](https://www.cantina.security/blog/end-to-end-fix-review-workflows#fix-review-failure-modes) The key question is not whether a guard appears in the diff. It is whether the required property holds across equivalent inputs, sibling callers, side-effect ordering, supported flows, and the new dataflow created by the patch. ## Evidence-gated verification The diff is the target, not the source of truth. The reviewer receives the raw finding, validation records, and synthesized remediation. That packet defines the exploit path, attacker preconditions, safety requirements, supported flows, prohibited patch shapes, negative cases, adjacent state transitions, deployment assumptions, and supported code anchors. Unknowns remain explicit. [Figure: Evidence-gated verification workflow](https://www.cantina.security/blog/end-to-end-fix-review-workflows#fix-review-pipeline) **Fix confirmed** requires evidence for every first-class safety requirement. Contradictory evidence produces **Fix Incorrect**; missing evidence produces insufficient context. For the 1,003 substantive runs, median time to verdict was 37 minutes. The middle half took 27–51 minutes. ## Iteration cost [Figure: Review passes before fix confirmation](https://www.cantina.security/blog/end-to-end-fix-review-workflows#fix-review-passes) A new commit invalidates the prior verdict. ## Output you can act on **Fix Incorrect** separates *Original finding still needs attention* from *Additional issue found*. After cross-category deduplication, 93 issue writeups remain: every one has analysis, 70 include a specific `file.ext:line`, and 66 include a supported next fix. Stored analysis is capped at 6,000 characters. An unmerged pull request can earn **Fix confirmed**, but it also shows **Pending Merge** and the finding stays in Todo. After merge, the status changes to **Merged** and the finding moves to Fixed. A webhook records the merge; a ten-minute sweep covers missed delivery. ## Limits The verdict answers one narrow question: did the reviewed code close this finding? A patch that moves a trust boundary or changes authentication still warrants broader review. Undocumented business rules cannot be inferred reliably. Amend the pull request and the prior result is stale. Run the review again against the new commit. **[Talk to us about evidence-backed fix verification →](/get-a-demo?utm_source=blog&utm_medium=post&utm_campaign=end-to-end-fix-review-workflows&utm_content=cta)** [Download the aggregate study data](/blog-data/end-to-end-fix-review-workflows.json)