-
-
Release v2026-08: 21 shared-vintage pairs, 2,762,944 tCO2e on the smaller side. A shared vintage is a lead for review, not proof.
-
Public dashboard for v2026-08: 478 current valid verdicts (374 Duo, 104 Gemma); this release needed 145 accepted Duo pair-judgments.
-
MR !15, the last v2026-08 batch. GitLab lists the triggering user as author; the steward service account made the commits and merge.
-
Main pipeline 2921334462 after the last v2026-08 batch: build, Firebase deploy, live check, package, release and SCI all passed.
-
Closed issue #2 holds the steward's note for each v2026-08 batch. MR !13 shows closed: its branch was deleted early; 44395e8 is on main.
-
Estimated software carbon intensity by release. Model energy comes from published per-query estimates, not from the steward model.
Inspiration
CarbonTwin gives a cross-registry finding to the people who can act on it. An auditor reviews the matched records and links the two listings in the contract. Registry operators record issuances against those listings. Once the link exists, the contract rejects a second registry's issuance for a vintage already recorded on the first listing.
That handoff matters each time the source database changes. The live report for Berkeley release v2026-08 identifies 63 same-asset pairs; 21 share at least one vintage, covering 2,762,944 tCO2e on the smaller side. One new shared-vintage signal since v2026-06 is ACR0931 and CAR2112: v2026-08 shows 147,508 tCO2e issued for 2025 in each registry, an overlap the v2026-06 data could not show. A shared vintage is a lead for registry review, not proof of double counting. A project can move between registries and legitimately split a year, and the summary database has no serial numbers, so CarbonTwin reports the smaller issuance for each shared vintage and shows the evidence for investigation.
CarbonTwin began as an MIT-licensed project for the IEEE ClimateChain hackathon. It screens Berkeley's Voluntary Registry Offsets Database for the same project listed in two registries. Its published run on release v2026-06 found 18 shared-vintage pairs, covering 2,486,944 tCO2e on the smaller side.
Berkeley republishes the database about every two months. A release can rename IDs, add descriptions, and change listings that were already judged, so a one-off audit becomes stale. We built a GitLab release steward to update the audit without a person between batches and without sending every covered pair back to a model.
What it does
A daily scheduled pipeline watches the database page. A second schedule, every 15 minutes, creates a pipeline only while pairs are waiting for a verdict. When a pipeline passes, a pipeline-event trigger starts a custom GitLab Duo Agent Platform flow, the release steward, which takes one step per session:
- Rescreen and carry. Scripts rescreen only the countries whose records changed, with the same result as a full run, and move cached verdicts to renamed IDs. A verdict stays valid while both listings read the same to the adjudicator: name, developer, location, type, methodology, notes, and description. Issuance numbers and registry status do not count, because code computes the overlap and a status change does not change the asset.
- Adjudicate one batch. The agent judges up to 30 pairs whose verdicts are missing or stale, largest overlapping volume first, and writes one JSON verdict per pair with the shared facts it relied on.
- Check the agent. Code validates every line against the schema and looks for each cited fact in both listings. Unsupported facts are dropped; a pair is confirmed only with two grounded facts. The report is rebuilt and its Merkle root anchored.
- Merge only what the pipeline verified. The agent opens a merge request. Its pipeline recomputes the merge on a copy and fails on any difference, and it runs the tests, SAST, secret detection, and dependency scanning. The next scheduled session reads that pipeline. If it passed, the steward fast-forwards
mainto exactly the tested commit. Ifmainmoved, it rebases and lets the pipeline run again. - Ship. After a merge, the dashboard deploys to Firebase Hosting on Google Cloud, a live check reads the public site back and confirms the new Merkle root, the verified data goes to the package registry, and a release links to it. GitLab Pages receives the same build but is visible only to project members in this hackathon group.
- Report. The agent notes every batch on a database-release issue and closes it when nothing is waiting. CI jobs are tracked with CodeCarbon, flow steps record their run time, and each pipeline reports an estimated software carbon intensity.
Results on two real database releases
The repository starts from release v2026-04 so that the steward can replay the real v2026-04 to v2026-06 transition. Between those releases, 920 project IDs appeared and 795 disappeared; 784 of them were renames (ACR102 became ACR0102), and 721 of the renamed ACR listings gained a description they did not have before.
- Judge every covered pair again: 450 pair-judgments
- Reuse a verdict only when both full records are unchanged: 330 pair-judgments
- Reuse a verdict while both identity texts are unchanged (CarbonTwin): 237 pair-judgments
On GitLab the steward worked through v2026-06 in eight batches on 6 October 2026, between 15:36 and 20:04 UTC. Each batch took two scheduled sessions, one to adjudicate and one to merge, and its merge request stayed open for 6 to 26 minutes. The agent opened seven of the eight merge requests and the steward merged all eight; we opened the first by hand while fixing how the flow creates merge requests. From the second merge onward, each merge deployed the dashboard and published a package and a release. See the pipelines, a merge request, the release issue, the package registry, and a release.
The agent judged 237 pairs for the accepted v2026-06 batches and agreed with the published verdict on 192 (81%). The release sent 23 published findings back to it because a listing had been renamed or redescribed; it confirmed 22 again and marked one as unclear (same name and developer, different project types). It also confirmed three pairs that the published run's second pass had not, one of them with a 2020 vintage issued in both registries (718 tCO2e). Of the 45 disagreements, 37 were pairs the published run had judged the same asset but could not ground in both listings, so it had already left them for review. The replay report lists 63 same-asset pairs, 19 with a shared vintage, covering 2,487,662 tCO2e. The published v2026-06 report had 61, 18, and 2,486,944 tCO2e respectively.
Berkeley then published v2026-08 late on 6 October UTC. A session started by an unrelated README push picked it up at 00:08 UTC on 7 October; the daily watch would otherwise have found it at 06:00. The steward reused 318 of the 450 covered verdicts, sent the other 132 to the agent, and then rejudged 13 flagged pairs outside the coverage whose listings had changed: 145 accepted pair-judgments in six batches, MRs !10–!15. The first batch waited for the daily watch to merge it because the 15-minute clock starts only after a merge records on main that pairs are waiting. The final v2026-08 report contains 63 same-asset pairs, 21 with a shared vintage, covering 2,762,944 tCO2e. Issue #2 records the batches and is closed.
The report footer is a stock count, not a release-work count: its 478 current valid verdicts comprise 374 whose latest source is the Duo flow and 104 from the original Gemma run. Across the two stewarded releases, Duo performed 382 accepted pair-judgments (237 + 145); eight pairs were judged in both releases, which is why the current unique Duo provenance is 374.
A local check, ci/replay_check.sh, replays all eight v2026-06 batches with the published verdicts standing in for the agent. Every batch passes the merge-request checks, and the end state contains all 61 findings of the published report with the same facts.
Sustainability
Most of the estimated footprint is model work, so the design avoids repeat judgments. The v2026-06 transition needed 237 accepted pair-judgments instead of 450, 47% fewer. The v2026-08 release needed 145; 318 covered verdicts were reused. Scheduled pipelines run only the watch job because nothing in the repository changed. Once the backlog is empty, the 15-minute clock creates no pipeline and starts no session. The daily watch still creates one pipeline and one short skip session each day so a new release can be found.
Each pipeline reports estimated SCI = ((E × I) + M) / R, with R equal to one pipeline run. CodeCarbon estimates CI-job energy. Model energy is not measured: the calculation uses 0.95 Wh per pair-judgment from a published estimate for a short Claude Sonnet query, with a 0.24 to 2.989 Wh range. It applies a US grid intensity of 384 gCO2/kWh and an embodied share of 0.00033 kg per runner hour. Sources and limitations are in ci/sci_assumptions.json.
A 30-pair merge-request pipeline is estimated at about 11 gCO2e, with a 2.8 to 34 g range; scheduled and main pipelines stay under 0.01 g each. The v2026-06 release total is estimated at 97.5 gCO2e across the 45 pipelines that reported SCI, with a 24.7 to 306.6 g range. That total counts 267 pair-judgments: the 237 in accepted batches plus 30 in duplicate MR !3. A separate overlapping session, 9097657, judged another 30 pairs but produced no SCI report, so those judgments are not in the 97.5 g total. Reusing 213 verdicts avoided an estimated 77.7 g, with a 19.6 to 244.5 g range. For v2026-08, 145 pair-judgments produced an estimated total of about 53 g over 25 pipelines, with a 13 to 167 g range; reusing 318 verdicts avoided about 116 g, with a 29 to 365 g range.
How we built it
Python (pandas, scikit-learn) handles screening and validation, Node.js handles Merkle anchoring and the dashboard, and Solidity implements the registry contract. GitLab CI/CD runs the pipeline, package registry, and releases. A custom GitLab Duo Agent Platform flow runs in the default sandboxed image with network access limited to the database host. The dashboard is public on Firebase Hosting. The pipeline deploys it without a stored key by exchanging GitLab's ID token through Workload Identity Federation. The Google Cloud project has no billing account and remains on the free Spark plan.
Challenges
- The v2026-06 release renamed 784 IDs and filled in descriptions for the same listings. Keying the cache on full records sent 93 more pairs to the model; keying it on what the adjudicator reads fixed that.
- Pipeline-event triggers fire for every pipeline, including the steward's own merge requests, but GitLab does not start a flow from a pipeline that the flow's own service account created. A second trigger meant to merge the steward's merge requests therefore never fired. The steward now takes one step per scheduled session: merge the open batch if its pipeline passed, or start the next one.
- Sessions can overlap when a scheduled run fires while another session is still judging. A failed branch listing once read as "no batch open," and the same batch was opened twice. The prepare script now stops when it cannot list branches, names the batch branch after a hash of its pairs, and takes a claim branch atomically before any work, so an overlapping session skips instead of judging the same pairs again.
- After the first merges, GitLab refused to create the scheduled pipeline on a batch commit: a release job chosen by the commit message needed the live check, which scheduled pipelines leave out. No pipeline meant no session, and the steward stalled without an error where it was looking. Those jobs now run only in push pipelines, and a test evaluates every job's rules and needs for each pipeline source.
- Release v2026-08 added a 2025 vintage to 34 more pairs, which pushed lower-ranked pairs out of the 450-pair coverage while it changed their listings. Thirteen had previously been judged the same asset, and their findings dropped out of the report without an error. A pair outside the coverage now goes back to the agent when its verdict had flagged it and its listings change; the agent confirmed all 13 again.
- MR !13 showed as closed although its batch was on
main: the merge script deleted the branch immediately after pushing, before GitLab had registered the merge. The next session now removes merged branches. - The flow's
create_file_with_contentstool refuses to overwrite a file. A leftover answer file made the agent fall back toedit_fileand a shell heredoc and write its 30 verdicts three times; removing the file before each batch brought a session down to eight tool calls. - The two releases differ in column count, and the loader read vintage columns by position. It now finds them by label and stops on a layout change; the agent escalates that to an issue for a person.
What was added during the submission period
This is a Path B entry. The original project (MIT, https://github.com/HyunsikParker/carbontwin) supplied the screening pipeline, model adjudication, contract, and dashboard. Everything about the release lifecycle was added from 5 October 2026: incremental rescreening, identity-keyed verdict caching and rename carry, the batch queue, verdict validation and merge, the Duo flow and its configuration, CI/CD, release-issue reporting, packaging, public deployment and live checking, carbon estimation, and replay tooling.
What's next
Keep running on each Berkeley release; merge a release's first batch sooner (today it waits for the daily watch because the 15-minute clock starts only after a merge records that pairs are waiting); and add a second adjudicator for pairs where the agent and the published run disagree.
Built With
- codecarbon
- firebase
- gitlab
- gitlab-ci
- gitlab-duo
- gitlab-pages
- google-cloud
- node.js
- pandas
- python
- scikit-learn
- solidity
- vite
Log in or sign up for Devpost to join the conversation.