Inspiration

Developers add AI calls to their code one merge request at a time. Each change looks small, so nobody prices it. The cost shows up later, on the monthly bill, long after the code shipped. Review checks that code is correct, safe and tested. Nothing checks what it will cost to run.

What it does

Every merge request gets a price tag.

  1. The pipeline prices the change. The llm-cost job counts the AI calls the merge request adds or removes and prices the difference per month.
  2. Plain code decides. The gate compares the price with the project's budget in .slash/policy.yml: go up to +$10 a month, route (a smaller model may do) from +$10 to +$100, block over +$100. Same result every run, no AI involved.
  3. A GitLab Duo flow acts on it. When the pipeline passes, the flow posts the receipt on the merge request, word for word. When a change is routed, it opens a follow-up issue: check whether a smaller model fits. On a block it marks the merge request as Draft and asks a person to approve in the Duo session. Approve removes Draft and records who approved.

See it on two demo merge requests (open on purpose, please don't merge):

  • Block, !15: a support bot adds +$144.80/mo. Receipt, Draft, approval in the session, Draft off, approval on record. It is marked Draft again, with a note that it is kept open on purpose, so it can't be merged.
  • Route, !20: a summary bot adds +$33.73/mo. Receipt and issue #2.
  • Go: most other merge requests in the project carry a GO receipt. (!29, a closed demo take, is a block.)

The whole journey, in 17 steps

17 steps: 13 land on GitLab's DevSecOps stages (nine different stages, some used twice), and 4 are ours.

# Step Stage
1 Plan the work, with a public trail of issues plan
2 A developer adds AI calls create
3 They open a merge request create
4 Tests run verify
5 Security scans run secure
6 The change is priced ours: cost
7 Code decides, against the budget govern
8 A Duo flow posts the receipt ours: Duo acts
9 Route: a follow-up issue is opened plan
10 Block: a person decides govern
11 The merge request is merged create
12 It is packaged package
13 It is deployed to Cloud Run, no stored key configure
14 It is watched, and must be this build monitor
15 It is released release
16 All nine stages are proven ours: proof
17 Submit ours: ship

Step 16 is the Stage-Gate. It checks each stage as a claim with a link to the evidence. If a claim can't be shown, the pipeline goes red. It runs on every main pipeline. See the result.

Step 14 checks the build, not just the page. The live app reports the commit it was deployed from. The Stage-Gate fails unless that is the commit of the pipeline that just ran.

Who it's for

If you merge, this is for you. Anyone who ships AI features through merge requests: a solo developer working with agents, or a whole team.

A model call costs money every time it runs. The merge request that adds it doesn't show the price.

Two merge requests in this project:

  • !15: a support bot with five AI steps adds +$144.80 a month. Over the approval line: blocked until a person approves.
  • !20: a summary bot with two AI steps adds +$33.73 a month. Over the review line: a follow-up issue asks if a smaller model fits.

The budget is a file, .slash/policy.yml. It changes by merge request like any code. GitLab records who approved a block.

Prices are estimates from the code in the merge request, not from production traffic.

Design

It lives in the merge request. The price is a comment. A block is Draft. Approving is one click in the Duo session.

Three outcomes:

  • Go: a receipt.
  • Route: a receipt and a follow-up issue. The merge is not blocked.
  • Block: a receipt, Draft, and a person.

Code decides. The AI reports. The decision is plain code against the policy file. The same input gives the same result on every run. The Duo agent posts the receipt word for word. The split is on purpose: plain code decides the same way every run, and the Duo flow explains the decision and acts on it, so the budget can't be argued with.

How we built it

  • GitLab CI/CD: test, SAST and secret detection, llm-cost, package, deploy, release and Stage-Gate jobs.
  • GitLab Duo custom flow, triggered by Pipeline events → Passed. The AI reports, it doesn't decide: the routed agent answers one word, the decision CI already made, and each side effect (issue, Draft, approval) runs in its own step.
  • GitLab Duo CLI: every change to the flow runs locally first (scripts/test-flow.mjs), on the same event GitLab sends the trigger.
  • Google Cloud Run hosts the live page, deployed from main with no stored key (Workload Identity Federation trusts only this project's main branch), watched by an uptime check.
  • Stage-Gate: every main pipeline ends by checking all nine DevSecOps stages as claims with evidence (plan, create, verify, package, secure, release, configure, monitor, govern). Any claim that can't be shown turns the pipeline red. The result is versioned and shown on the live page.
  • The engine is slash-tokens (MIT), our open-source token counter and pricer.

Challenges we ran into

  • Routing on the AI's exact answer. A flow router matches the final answer exactly, so one extra sentence before "block" sent a merge request down the wrong path. We gave every side effect its own step, so the routed agent has one job: answer one word.
  • Approvals that answer the right step. Our first approval step pointed its response at a step that hadn't run yet, and approving failed. The fix was to answer the step that had already run (draft).
  • Testing a flow before it goes live. The Duo CLI runs a local flow only with the right workflow type; without it, it silently runs the default flow. We found the cause in the source and added our findings to the existing issue.

Accomplishments that we're proud of

  • A full loop inside GitLab: price, decision, receipt, issue or Draft, human approval, record. See !15 and !20.
  • Nine of nine DevSecOps stages proven on every main pipeline. Not claimed: checked, with evidence.
  • The live app is checked against the build. It must report the commit that was just deployed.
  • No stored cloud keys. Deploys use Workload Identity Federation.
  • Every Duo flow change ran locally first with the GitLab Duo CLI.

What we learned

  • Code decides, AI reports. Every receipt is the same on every run.
  • Prove it, don't say it. A job that checks each stage beats a list that names them.
  • Test before live. The Duo CLI ran the flow on the real trigger on our machine.
  • Quote numbers from the artifact. Our README once carried a cost figure for the middle path that didn't match the bot's receipt. We fixed the README to match the bot.
  • Authenticity beats polish. We tested AI narration against the author's own voice. The AI was cleaner; the human was more convincing. We offer both, human first.

Two voices, human first

We evaluated the author's own recorded voice against AI voices from Grok Voice (Leo in UK English, Sal in US English, and Rex), directing each one line by line against a timing table, and we gave the author's recording a light polish (a little less boom, a little more clarity, level-matched). The AI read was cleaner; the author's was more authentic. So we decided to offer both, human first: the main video is the author's voice, and the same film with the simulated English voice, Leo by Grok (AI-generated, clearly labelled), is a second video: https://youtu.be/rxQ6IobdaPY . Both play in order in one playlist: https://www.youtube.com/playlist?list=PLEkUshNhHErc . Which do you like best?

Honest limits

  • The price is an estimate. It assumes 100 calls a site a day, output about twice the input, and each call priced at its SDK's representative model. A team sets the two budget lines in .slash/policy.yml; the receipt prints the calls-a-day assumption on every run.
  • Draft is a firm stop, not a hard lock. Anyone with Developer access can mark a merge request ready. GitLab records who did.
  • Monitor is a health check, an uptime check and a commit match. It is not full observability.
  • SAST reports one medium finding (a regex check on fixed provider host names). We triaged it as a false positive: the patterns have no quantifiers, so they can't backtrack. The substring match is on purpose: any URL that names a provider's API host is priced.
  • Plan and release are each exercised once (the issue from !20, and release v1.0.0).

How it meets the hackathon requirements

  • Trigger and action: the Duo flow fires on Pipeline events → Passed, then posts a receipt, opens an issue, marks Draft, or waits for approval.
  • GitLab Duo Agent Platform: a custom flow with five steps (gate, plan, draft, approval, approved), tested with the Duo CLI before it went live.
  • Google Cloud: the live page runs on Cloud Run, deployed from GitLab CI as the production environment.
  • Visible CI/CD history: every pipeline is public, ending with the Stage-Gate.
  • Updated during the hackathon: slash-tokens went from 1.6.5 (Aug 25, before the hackathon) to 1.8.1 (Oct 7–11); this project imported it at 1.8.0 on Oct 11 and CI runs 1.8.1. This project from Oct 11: pipeline, gate and policy, Duo flow, Cloud Run page, Stage-Gate. Later: the follow-up issue for a route, the demo merge requests, local flow tests with the Duo CLI (!25), the commit match on monitor (!28), and the SAST finding triaged in CI (!31, Oct 20).
  • Before and after (Path B): Before, on GitHub: tests and a freshness check in GitHub Actions, releases published to npm by hand, and nothing that priced a change before it merged. After, on GitLab: test, security scans, llm-cost, package, deploy, release and the Stage-Gate on every pipeline; a price and a decision on every merge request; a Duo flow that posts the receipt and asks a person to approve a block; a live page on Cloud Run.
  • Most DevSecOps Stages: nine of nine, proven by the Stage-Gate (plan and create come before the code; the other seven come after it).

Sustainability: work in progress

The gate already does one sustainable thing: on the route path it asks whether a smaller model from the same provider fits the job, before the change merges.

What we do not claim. The receipt shows dollars only. It prints no energy or CO2e number, because no measured figures exist for the models it prices most (Claude, GPT, Grok, Gemini hosted APIs), and we will not extrapolate.

What we found. Published measurements exist only for open-weight models. For example, Yang and Armour (Oxford, arXiv 2610.14389, Oct 2026) measured GPU energy for NVIDIA's Nemotron 3 family (Nano, Super and Lightning, thinking and non-thinking) on named hardware. Their results report that reasoning mode and batch size can move energy more than model size does.

What it would take to quantify the gain. A measured energy range per model, a grid intensity, and embodied hardware carbon. Even then, a GPU-only figure is a lower bound, and it is not an SCI score until the full boundary is covered.

Next. We have asked the paper's authors for the Nemotron 3 rows. If the data allows, the receipt will show a labelled range for the open models only, with the method published and a test pinning the numbers to the cited inputs. Until then it shows none.

What's next

  • A CI/CD Catalog component, so one include adds the gate to every project.
  • Policy as code at group level, with team overrides.
  • Approval rules and protected branches (Premium), so only budget owners can approve a block.
  • Receipts exported to BigQuery: cost per team, per model, per month.

Built With

Share this project:

Updates

Submission history