python -m guard.review --base … --head … --defs --memory --gate --agent run

coverage (deterministic): ok, 32881 of 32881 changed lines and 85 of 85 files are in a bubble

Review the change, not the diff.

Molly shows a GitLab merge request file by file, splits it into changes, proves the links between them in the code and shows every changed line. A GitLab Duo agent explains each change; code checks what it says.

Open source under the MIT licence. Built on GitLab Duo Agent Platform, with GitLab Orbit for memory.

  • Code checks that every changed line and file is in a change, before and after the agent writes; the job fails if one is missing. On !18, 32,881 of 32,881 changed lines shown. coverage: ok

  • Hover a proven name in the diff and a peek shows its definition and the link code proved. README

  • Click a proven name and the code it points to unfolds in place, inside the diff, so you never leave the change you are reading. README

  • Plan proposes a stack of smaller merge requests from the proven links, bottom layer first. Stack dims the files of the other merge requests in a GitLab stack, and Mine shows a reviewer only their slice. README

  • The GitLab Duo agent groups, explains and ranks the changes (30 explained on !18). CI refuses a note that names a change code did not build. note on !18

  • GitLab Orbit gives the callers outside the diff and each file’s past merge requests, reverts and incidents. A file changed by a merge request that was later reverted with an incident marks the new one review-lens::history-risk. memory.py

  • Semgrep SAST and secret-detection findings are placed on the change they touch, so a finding sits next to the code it is about. semgrep on !18

  • On dbt merge requests CI builds the data at base and head and diffs it. On !13 Board Revenue for one month fell, and the guard said so first. guard note on !13

  • When a dashboard number moves, the merge request waits for a data owner who is not its author. On !13 the author’s approval was refused and another owner’s was recorded. the recorded approval

  • The release gate holds main until every data change has a safe floor or a recorded decision; it logged “release: yes” and shipped data-release-61, and a deploy job ships the dashboard to Cloud Run. data-release-61

  • The data watch reconciles production on main. After !2 merged it found 74 orders missing (25 October run), failed, and nothing was packaged. prod_build pipeline

  • The incident flow opened Incident #3 with what the guard had predicted and prepared the revert; a person merged it eleven minutes after the merge. Incident #3

Molly, feature tour

Press a feature to see it; the tour plays when none is open.

$ numbers from the merge request pipeline of !18

What the review page shows.

Molly’s review page, rendered from the review model CI built for a merge request. Code finds the changes, proves the links and checks that every line is there; the GitLab Duo agent only adds text.

Every changed line, with its proof.

Reviewing a big merge request today means going back and forth through a long diff. Molly keeps the diff file by file and adds what it lacks: code finds the changes, proves the links between them and checks that every line is there, and the agent explains.

Every changed line shown

Pipeline 2991927333 on !18
review_lensjob 17486070469
python -m guard.review --base … --head … --defs --memory --gate --agent run
review lens ff44e08e..5164d377 (!18 Add the review lens: a connected map of a merge request)
85 files, 1888 hunks, 1033 bubbles, +32863 -18 lines
links: 400 (file 5, method 395)
memory: ok, 11 calls in 6.2 s
coverage (deterministic): ok, 32881 of 32881 changed lines and 85 of 85 files are in a bubble
history risk: none (every changed file's history was read), review-lens::history-risk not added
started flow session 10092479 (status created)
merged the review-lens note: 0 problem(s)
coverage (after-agent): ok, 32881 of 32881 changed lines and 85 of 85 files are in a bubble
Lines from the job log of review_lens on !18, pipeline 2991927333, 26 October 2026 (the long command is shortened to its module).

Duo explains, code checks it

The review-lens note on !18
The review-lens note on !18, written by the review-lens service account: the summary, a link to the interactive view, where to start in reading order, and groups by purpose.
Captured signed out on 2026-10-27, headless Chromium at 1440 wide, 2x from the GitLab page, cropped to the top of the note.Captured signed out on 2026-10-27, headless Chromium at 390 wide, 2x from the GitLab page, cropped to the top of the note.

It remembers what the diff can’t show.

The review lens asks GitLab Orbit, GitLab’s knowledge graph, what the diff cannot show: who calls the changed code, each file’s past merge requests, reverts and incidents, and who has reviewed it. Suggested reviewers come from that evidence, never the author or a service account.

Pipeline 2992386531 on Draft !22
review_lensjob 17489142969
review lens 32e66498..4c39a1f0 (!22 Draft: Demo: history risk on fct_orders.sql (do not merge))
memory: ok, 7 calls in 2.1 s
history risk: analytics/models/marts/fct_orders.sql (b1): !2 reverted by !3 (Incident #2); !8 reverted by !9 (Incident #3)
coverage (deterministic): ok, 1 of 1 changed lines and 1 of 1 files are in a bubble
wrote review_lens.env: REVIEW_LENS_HISTORY=risk
history risk on 1 change(s): labelled review-lens::history-risk
owner_decisionjob 17489142968
python -m guard.owner_decision --diff guard_out/data_diff.json
!22: history risk: review-lens::history-risk: waiting until a project member who is not the author replies "@ai-review-lens-gitlab-ai-hackathon approve" or removes review-lens::history-risk with a note saying why
ERROR: Job failed: exit code 1
Lines from the job log of review_lens on Draft !22 (a demo that changes one comment in fct_orders.sql), pipeline 2992386531, 27 October 2026.
  • Callers outside the diff.

    On !18, guard.release_gate.run has 14 unchanged callers that no diff would show you. memory: ok

  • History risk.

    A file changed before by a merge request that was later reverted with an incident marks the new one review-lens::history-risk. history_risk.py

  • Held until a person clears it.

    On Draft !22, owner_decision waited for a project member who is not the author. job 17489142968

  • Not yet seen live.

    The gate moving a merge request to Draft and a person clearing it are covered by unit tests only. docs/stages.md

A person decides when a number moves.

On dbt merge requests CI builds the data at base and head and diffs it. When a dashboard number moves, the merge request waits for a data owner who is not its author, and the release gate holds main until one decides.

!13Exclude gift card payments from order revenue
analytics/models/marts/fct_orders.sql+1−0
@@ -3,6 +3,7 @@33 with payments as (44     select order_id, sum(amount) as amount_paid55     from {{ ref('stg_payments') }}6+    where payment_method != 'gift_card'  -- gift cards are booked as revenue when sold67     group by order_id78 )
The data diff of !13
DashboardMonthBaseHeadChange
Board Revenue2026-09141,799.15116,581.82−17.78%
The whole diff of !13 from the GitLab API, and the guard’s data diff of base and head (job 17465300021). Shop data is synthetic.
Replies on !13, 26 October 2026 (UTC)
  1. @codebreaker23 author replied approve

    The guard's reply on !13: codebreaker23 cannot record this decision, because an author cannot approve their own change.
  2. @bhawnanautiyal data owner replied approve

    The guard's reply on !13: data owner bhawnanautiyal approved the data change, which lowers Board Revenue for 2026-09 by 17.78%.
Captured signed out on 2026-10-27, headless Chromium at 1440 wide, 2x from the GitLab page, cropped to the guard’s two replies.Captured signed out on 2026-10-27, headless Chromium at 390 wide, 2x from the GitLab page, cropped to the guard’s two replies.

A broken number opens an incident.

!8 changed one join. The guard warned that orders with no payment would vanish, and it was merged anyway. The production watch found 73 orders missing, the incident flow opened Incident #3 and prepared the revert, and a person merged it eleven minutes after the merge.

!8Only count orders that have a payment
analytics/models/marts/fct_orders.sql+1−1
@@ -14,4 +14,4 @@ select1414     coalesce(p.amount_paid, 0) as amount_paid,1515     case when o.status = 'completed' then coalesce(p.amount_paid, 0) else 0 end as revenue1616 from {{ ref('stg_orders') }} as o17−left join payments as p on p.order_id = o.order_id17+inner join payments as p on p.order_id = o.order_id
The guard's impact note on !8: no dashboard number changes in the sample data, but the inner join drops orders with no payment; verdict needs_human.
The whole diff of !8 from the GitLab API, then the guard’s note on it. Captured signed out on 2026-10-27, headless Chromium at 1440 wide, 2x from the GitLab page, cropped to one note.Captured signed out on 2026-10-27, headless Chromium at 390 wide, 2x from the GitLab page, cropped to the note down to its verdict.
Incident #3, opened by the incident flow
Incident #3, Was this predicted? Yes: the guard's impact note on !8 warned that fresh orders with no payment would disappear.Incident #3, closed: the impact on Board Revenue and Daily Operations and the reconciliation table, 73 orders missing over six days.
Captured signed out on 2026-10-27, headless Chromium at 1440 wide, 2x from the GitLab page, cropped to Was this predicted?, the title, Impact and Evidence.Captured signed out on 2026-10-27, headless Chromium at 390 wide, 2x from the GitLab page, cropped to Was this predicted?, the title, Impact and Evidence.

Nine stages, each with a real run.

Every stage of the DevSecOps loop is touched by a job in this project, and every row links to the run. The unit tests run on every pipeline: on !18, 538 passed, 1 skipped.

The nine DevSecOps stages and their runs
StageWhat runs itEvidence
PlanThe incident flow, started from CI, opens an Incident with the impact, the evidence and “Was this predicted?”The incident flow, started from CI, opens an Incident with the impact, the evidence and “Was this predicted?”Incident #3, session 10036890
CreateThe review-lens flow writes one note per merge request; the incident flow prepares the revertThe review-lens flow writes one note per merge request; the incident flow prepares the revertnote on !18, revert !9
VerifyThe review lens fails the job if a changed line is in no change; dbt builds base and head and diffs the dataThe review lens fails the job if a changed line is in no change; dbt builds base and head and diffs the datajob 17486070469, 538 passed
PackageThe dashboard image goes to the container registry after the release gate, then container scanningThe dashboard image goes to the container registry after the release gate, then container scanningpackage_image, container_scanning
SecureSAST and secret detection; the review lens places their findings on the change they touchSAST and secret detection; the review lens places their findings on the change they touchsemgrep-sast on !18, findings on !18
Releaserelease_gate holds main until every data change is safe or approved; GitLab Pages and Cloud Run deploy after itrelease_gate holds main until every data change is safe or approved; GitLab Pages and Cloud Run deploy after itheld, data-release-61
ConfigureThree flows as code in .gitlab/duo/flows, linted on every pipeline and published to the AI CatalogThree flows as code in .gitlab/duo/flows, linted on every pipeline and published to the AI Catalogflows_lint on !18, flows_config
MonitorA production data watch on every main pipeline and daily at 06:00 UTC; a failed check starts the incident flowA production data watch on every main pipeline and daily at 06:00 UTC; a failed check starts the incident flow73 missing, passing
GovernHistory-risk gate from GitLab Orbit's memory; an owner who is not the author decides; a floor the agent cannot lowerHistory-risk gate from GitLab Orbit's memory; an owner who is not the author decides; a floor the agent cannot lowerheld on !22, refusal on !13
Pipeline 2991927333 on !18

11 of 11 jobs passed

Jobs, stages and durations of the merge request pipeline of !18, from the GitLab API.
Release written by the release gate

data-release-612026-10-26 13:15 UTC

Every data change from ccb93ec1 to e54672ef has a safe floor or a recorded decision.

Merge requestFloorDecision
!13 Exclude gift card payments from order revenueneeds_humanrelease
approved by a data owner who is not its author

Dashboard image dashboard:e54672ef

release_gatejob 17469676481
python -m guard.release_gate
counting from ccb93ec1 (last release) to e54672ef
!13   release  approved by a data owner who is not its author  (Exclude gift card payments from order revenue)
release: yes
The release notes of data-release-61 (from gitlab_release) and the gate’s log.

Run it on your laptop.

The tests and the dbt build run on your machine against the synthetic shop data. The setup guide covers the GitLab side: the three flows, the service accounts, the Mention trigger and the daily schedule.

Terminal
git clone https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/22678466/showcase.git
cd showcase && uv venv && . .venv/bin/activate && uv pip install -r requirements.txt
pytest && cd analytics && DUCKDB_PATH=../warehouse/dev.duckdb dbt build --profiles-dir .
538 passed, 1 skipped  (on !18’s pipeline)

Read one connected change.