Inspiration

Code generation is solved; shipping still needs people. Small teams and solo maintainers drown in the work after the code is written: review, fixing CI, security triage, releases, deploys, checking it is up. Agents can do that work, but every agent turn burns energy, and nobody sees the bill. I wanted a loop that runs itself and knows what it costs.

What it does

I open one issue. After that, the only human step is one Maintainer sign-off before the merge:

  1. Plan. A planner agent writes acceptance criteria, scores complexity 1–5 and sets a carbon budget (complexity x 5 gCO2e).
  2. Create. It delegates to a small-model developer subagent (a large one only for complexity 4–5), which branches, writes the code and tests, and opens a Draft MR.
  3. Verify. CI runs ruff and pytest, but only the jobs the diff can affect. If the pipeline fails, the ci_doctor agent reads the job log, pushes a fix(ci): commit and waits again (at most twice, then it asks a human).
  4. Secure. SAST, secret detection and dependency scanning always run. The guardian agent triages findings, greps the diff for secrets, posts an internal risk note and asks a Maintainer to review.
  5. Govern. A CI gate job computes the change's Software Carbon Intensity and posts it on the MR. Auto-merge needs a Maintainer sign-off tied to the latest commit: the greenlight::reviewed label counts only if a Maintainer added it after the current head was pushed, so any new push needs a fresh sign-off. If that sign-off is in place, the pipeline is green and the change is within budget, the gate sets the MR to merge when the pipeline succeeds. Over budget, it stops unless a maintainer adds carbon::override.
  6. Package, Release, Configure. The main pipeline builds and pushes a container image, bumps the semantic version from conventional commits, publishes a GitLab Release, and validates per-environment config.
  7. Deploy and Monitor. It deploys to staging on Pages, probes it, promotes to production (and optionally Cloud Run in Montréal), probes again, and keeps probing on a schedule.
  8. Report. It posts a summary on the original issue: 9/9 stages, the version, and the grams of CO2e this change cost.

The agents propose, a Maintainer signs off, CI decides. No agent can merge.

How I built it

  • One flow, four agents. Duo flow triggers fire on human actions, so planner, ci_doctor and guardian run in one session joined by routers, with the developer as a subagent of the planner. greenlight-fixer and greenlight-guardian are standalone flows for changes made by hand (pipeline failed; MR created).
  • Carbon model. carbon/ implements SCI = (E x I + M) per deploy. E comes from GitLab job durations and Cloud Carbon Footprint coefficients, plus agent turns x tokens x Wh per token. I is Google Cloud's regional grid intensity. M is server embodied emissions. Every coefficient sits in one YAML file with its source.
  • Pipeline. Ten CI stages, rules: changes: for selective CI (kept in sync with the Python model by a test), no duplicate branch or tag pipelines, auto_merge through the API, Pages with a staging path prefix, probes, a public ledger and an hourly monitor schedule.
  • Testing without Duo. I couldn't use Duo until onboarding was approved, so I built harness/simulate.py. It mocks the GitLab API, walks the real flow routers with scripted agents (each tool call checked against the component's toolset), and runs the real CI scripts end to end. harness/flowlint.py checks the flow YAML against the custom-flow restrictions. 215 tests in total.
  • Security hardening. An independent GPT review of the whole repo (25 findings, each checked and fixed or documented); a prompt-injection guard (issue, MR and pipeline text treated as untrusted data, run_command behind tool approval and removed from guardian and planner, Maintainer-verified labels); every CI image pinned by digest. Residual risk: the bot's CI token is still reachable by code in an MR pipeline, which needs a GitLab project-setting change to close.

Challenges we ran into

  • Custom flows forbid response schemas, so routing works on one-word final answers with a safe default route.
  • Triggers need a human actor, which shaped the single-session design.
  • Making an agent-written change mergeable only by CI meant the merge decision had to be code: a Maintainer review label tied to the latest commit, green pipeline, and budget.
  • Inference energy for hosted models isn't published, so it's reported separately as a labelled estimate.

Accomplishments that we're proud of

  • A loop covering all nine stages that is hands-off apart from one Maintainer sign-off, with failure recovery (ci_doctor) and a human escape hatch (escalate, override). The full flow, from issue through sign-off to auto-merge, runs end to end in the offline rehearsal; on GitLab.com the agents planned, coded, fixed failed pipelines and reviewed security, and the final auto-merge is still pending (MR !4 is open).
  • Carbon as a first-class merge criterion with a public, per-change ledger.
  • Rehearsal: one change cost 3.84 gCO2e, against 9.93 g for the same work on large models with every CI job: 61% saved. Live (GitLab.com ledger, Oct 25): 3 main-pipeline entries at 5.76 gCO2e each against a 13.13 g baseline, 56% lower; these entries carry measured: false (job durations estimated), so the rehearsal stays the headline figure.

What we learned

Turn for minute, agents are the expensive part: one large-model turn costs about as much as two runner-minutes. The model choice moved 5.87 g of the 6.09 g saved. Choosing the smallest model that can do the job matters more than any CI trick.

What's next for Greenlight

Measured inference energy instead of estimates; using the carbon-aware scheduler to defer non-urgent releases to cleaner hours; per-team budgets across projects.

Environmental impact (Most Environmentally Impactful opt-in)

Method. Green Software Foundation SCI (ISO/IEC 21031:2024): SCI = (E x I + M) per R, where R is one shipped change. E: runner energy from GitLab job durations x Cloud Carbon Footprint coefficients (0.71–4.26 W per vCPU, 0.000392 kWh per GB-hour, PUE 1.1), plus agent inference (turns x 6k tokens x 0.012 Wh small / 0.06 Wh large per 1k tokens, anchored to Google's published 0.24 Wh median Gemini prompt). I: Google Cloud 2025 regional grid intensity (us-east1 579 g/kWh, conservatively assumed for runners and inference). M: embodied server emissions per CCF. Failed pipelines count.

Practices, each measured in the ledger:

  • Small-model default with a complexity-gated ladder: ~80% less inference energy per turn; 5.87 g of the 6.09 g saved per change (rehearsal)
  • Carbon budget as a merge gate: spending is capped per issue; overrides are explicit and audited
  • Selective CI (security scans always run): docs-only changes run 3 of 7 MR jobs
  • No duplicate pipelines, slim images, pip cache: fewer and shorter runner-minutes
  • Cloud Run in northamerica-northeast1, scale to zero: 11 vs 579 gCO2e/kWh, 98% lower grid intensity for serving
  • Public per-change ledger: the cost of agentic automation is visible on every MR and issue

Result (rehearsal): 3.84 gCO2e per shipped change vs a 9.93 g baseline, 61% lower. Live ledger (GitLab.com, as of Oct 25, 2026): 3 changes, total 17.29 g, average 5.76 g, saved 56.1% against a 39.38 g baseline. All three entries are flagged measured: false (estimated job durations) and none carries an issue budget yet. Full method, sources and limits: docs/SUSTAINABILITY.md.

Built With

  • dependency-scanning
  • fastapi
  • gitlab-ci-cd
  • gitlab-container-registry
  • gitlab-duo-agent-platform
  • gitlab-pages
  • gitlab-releases
  • gitlab-sast
  • google-cloud-run
  • python
  • secret-detection
Share this project:

Updates

Submission history