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:
- Plan. A planner agent writes acceptance criteria, scores complexity 1–5 and sets a carbon budget (complexity x 5 gCO2e).
- 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.
- Verify. CI runs ruff and pytest, but only the jobs the diff can affect. If the pipeline fails, the
ci_doctoragent reads the job log, pushes afix(ci):commit and waits again (at most twice, then it asks a human). - Secure. SAST, secret detection and dependency scanning always run. The
guardianagent triages findings, greps the diff for secrets, posts an internal risk note and asks a Maintainer to review. - 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::reviewedlabel 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 addscarbon::override. - 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.
- 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.
- 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-fixerandgreenlight-guardianare 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_mergethrough 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.pychecks 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_commandbehind 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
Log in or sign up for Devpost to join the conversation.