-
-
Real run: issue #4 went from assignment to production in 6 min 6 s with no human action; autonomy receipt 9/9.
-
charter-check measures the model calls and enforces credits, risk, carbon (SCI) and blast radius before any merge.
-
Real escalation: the planner refused a change to a protected path (.gitlab-ci.yml); nothing merged, a human decides.
Autonomy with a budget. A human writes a versioned contract, CHARTER.yml. GitLab Duo agents take an issue from plan to production while four budgets hold: GitLab Credits, a deterministic risk score, carbon (gCO2e per issue) and blast radius (which paths may change, how many files and lines). When one budget breaks, nothing merges and a human is called in. Every run ends with an autonomy receipt on the issue: the nine lifecycle stages, each linked to the GitLab object that proves it.
- Repository: https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/25059446/showcase
- Live run registry (GitLab Pages, published by the pipeline): https://showcase-76033c.gitlab.io
- Reference run, issue to production in 6 min 6 s with no human after the assignment: issue #4 and its autonomy receipt
Inspiration
Agents can already write the code. What stops a team from letting them ship is not the code, it is the question every manager asks: how far can I let it go on its own, and how will I know it stayed inside the lines? Today the answer is either "a human reviews everything", which removes most of the benefit, or "trust the prompt", which nobody signs off on. We wanted a third answer: a written, reviewable limit that the pipeline enforces with deterministic checks, and a receipt that proves what happened.
What it does
- A human labels an issue
~reflexvifand assigns it to the Reflexvif Builder flow. That is the only human action. - F1 Builder (GitLab Duo custom flow) reads
CHARTER.ymlin a deterministic step (no model call). Itsplannerposts a "Charter plan" note with the files it expects to touch and the predicted risk, and routes on one word: PROCEED, ESCALATE or SKIP. Thebuildercreates the branch, implements the change and its tests, runsnpm testin the flow sandbox until green, and opens a merge request whose description links the plan note and the flow session. - The merge request pipeline runs the unit tests, GitLab SAST, Secret Detection, Dependency Scanning, a CycloneDX SBOM and an OSI licence check. Then
charter-checkdecides. It readsCHARTER.ymland the gate code from the target branch, so a merge request can neither raise its own budget nor rewrite its judge. It scores the diff, counts the scanner findings located in the changed files, and measures the model calls of the F1 session from its checkpoint to price them in GitLab Credits. - Within budget,
charter-gatesets auto-merge, pinned to the commit it checked, and GitLab merges when the pipeline succeeds. Outside budget, the pipeline fails,escalatelabels the issuereflexvif::escalatedand names the limit that was crossed, and nothing merges. Any other failure starts F2 Medic through the Flows API (two attempts at most, never around a charter refusal). - After the merge, the default-branch pipeline:
- builds the container image;
- derives the semver tag from the conventional title and creates the GitLab release;
- validates the OpenTofu definition;
- runs a canary of the exact image as a CI service, against the charter's error-rate and p95 thresholds;
- re-checks the merged diff against the charter;
- posts the receipt on the issue and publishes the run to the registry on GitLab Pages;
- probes the live site.
Then F3 Chronicler writes a plain-language "What changed" note. If the canary fails, production keeps the previous version.
How GitLab automates the life after code
Every link below is from the reference run (issue #4, 19 Oct 2026). No human acted between the assignment and the receipt.
| Stage | What runs | Proof |
|---|---|---|
| Plan | F1 planner: deterministic charter read, "Charter plan" note, PROCEED routing | plan note, flow session 9713359 |
| Create | F1 builder: branch, code and tests, npm test green in the sandbox, merge request |
MR !3 |
| Verify | build (syntax, CI and flow validators), unit-tests with JUnit report |
MR pipeline, test report |
| Secure | SAST, Secret Detection, Dependency Scanning, SBOM, licence check; findings in changed files feed the risk score | security tab |
| Package | container-image to the GitLab Container Registry |
container registry |
| Release | version (semver from the conventional title) and release |
release v0.6.0 |
| Configure | tofu-validate (OpenTofu), GitLab production environment |
OpenTofu job, environments |
| Monitor | canary-image on the built image, production-check on the live site (p95 202 ms, 0 errors), daily heartbeat schedule |
canary job, production check |
| Govern | charter-check (risk 11/40, 5.0/25 credits), charter-gate auto-merge, post-merge re-check, receipt, registry, F3 |
charter decision, receipt, registry |
Main pipeline of the reference run: pipeline 2965010663 (15 jobs, all green).
The other two flows, also run for real
- F2 Medic. MR !4 was a human-written demo change shipped with a correct test and a deliberate bug. Its pipeline failed on
unit-tests.medic-dispatchthen started Medic through the Flows API (session 9713778). Medic fixed the code, left the test unchanged and pushed one commit to the merge request branch. The new pipeline passed, andcharter-gatemerged it. The whole repair took about three minutes. - F3 Chronicler. It was started by the Flows API at the end of each delivered run (session 9713622). It was also started by a Mention on issue #3 (session 9713967).
Agents and flows registered in the AI Catalog and enabled in the project
| Name | Type | Started by |
|---|---|---|
| Reflexvif Builder | custom flow (v1 schema, deterministic step + conditional router) | Assign trigger |
| Reflexvif Medic | custom flow | Flows API from CI (medic-dispatch); Pipeline events trigger filtered to failed merge request pipelines started by a human |
| Reflexvif Chronicler | custom flow (coding_environment: none) |
Flows API from CI (govern); Mention trigger |
| Counsel | custom agent, read-only, for Agentic Chat | a person asking "why was this run escalated?" (registered, not exercised in a recorded run) |
Why CI chains the flows
Duo triggers only fire on human actions: a pipeline started by an agent, or a merge done by CI, cannot start another flow. So one human action starts F1, and from there CI calls the Flows API to start F2 and F3. GitLab also rejects git push options coming from a flow identity. The merge is therefore never requested by the agent: charter-gate sets it after the deterministic checks, pinned to the checked commit.
Autonomy level
Hands-off. After a person assigns the issue, no human acts until production. In the reference run, nobody touched anything between the assignment at 20:55:19 UTC and the receipt at 21:01:24:
- F1 planned and built the change;
charter-gateset auto-merge on the commit it had checked, and GitLab merged when the pipeline passed;- release, canary, deployment, production check, receipt and F3's closing note followed automatically.
Humans are called only by exception: a charter refusal labels the issue reflexvif::escalated and nothing merges. The receipt records how every merge happened ("Merge: automatic (charter-gate)"). The automatic merge uses one credential that the accountable human provides, a masked and hidden CI/CD variable, and the charter states that human's limits.
Sustainability (SCI)
We follow the Green Software Foundation SCI specification and its SCI for AI extension: SCI = ((E × I) + M) per R, with R = one issue delivered to production. The boundary includes the CI jobs, the model calls made by F1, and embodied hardware.
- Model calls are measured, not assumed.
scripts/measure-session.mjscounts the model responses stored in the flow session checkpoint, andcharter-checkenforces the credit budget on that number before merging.
| Run | Model calls (planner + builder) | GitLab Credits (Claude Sonnet 5.5, 0.313 per call) | SCI | |---|---|---|---| | Issue #4 (reference) | 16 (3 + 13) | 5.0 | 21.55 gCO2e | | Issue #3 | 17 (4 + 13) | 5.3 | 22.76 gCO2e |
Our design estimate had been 40 calls and about 50 gCO2e per issue.
- I: Google Cloud 2025 grid data for us-east1, where GitLab.com hosted runners run (579 gCO2e/kWh). The grid that serves the model is an assumption (432 gCO2e/kWh) and is flagged as unverified.
- E: CI energy from vCPU and memory power (Cloud Carbon Footprint coefficients, PUE 1.1). Model energy comes from a per-call estimate, also flagged as unverified.
- What drives the number: model calls are about 90% of the total (19.4 g of 21.55 g on issue #4). The design therefore keeps models away from work where they add nothing:
- reading the charter, scoring risk and building the receipt are deterministic CI steps;
- the Chronicler runs without a repository checkout;
- a hard per-run credit budget is enforced before merge.
- Limits, stated plainly:
- The per-call energy and the model's grid are estimates.
- The F3 and F2 calls are not yet included in R's total.
- We did not measure a separate baseline pipeline, so we do not claim a percentage reduction.
Challenges we ran into
- The flow image had no
git. The Duo executor needs it, andnode:24-alpinedoes not ship it: the first session failed after 20 seconds. Switching to the Debian-basednode:24fixed it. - Push options are rejected for flow identities.
merge_request.createandmerge_request.auto_mergefail with "An unknown error occurred". The merge decision moved entirely into CI. - A real run was escalated on a false positive. The gate counted five SAST findings that were in files the agent never touched. It now scores only findings in the changed files and reports the others as
outside_diff. - On merge request pipelines, the gate ran from the merge request's own branch. It now runs from the target branch.
- The trace endpoint did not contain the model messages. Call counting reads the session checkpoint instead.
What we learned
The useful unit of trust is not "the agent" but a contract plus a receipt. Writing the limits as data made every disagreement concrete. When the gate refused a change, it said exactly which number was too high, and the fix went either into the code or into the contract, reviewed by a human.
What's next
- Run
charter-checkas a project-level pipeline execution policy, so the gate cannot be bypassed by editing the CI file on a branch (this needs the Maintainer role). - Include the F2 and F3 calls in the credits and carbon of each run, and reconcile with the GitLab Credits dashboard.
- Learn the risk weights from past escalations, with the charter still reviewed by a human.
Built with
GitLab Duo Agent Platform (custom flows, custom agent, flow triggers, Flows API, AI Catalog), GitLab CI/CD, GitLab SAST, Secret Detection and Dependency Scanning, GitLab Container Registry, GitLab Releases, GitLab Pages, GitLab environments, OpenTofu, Docker, Node.js 24 (no framework), the yaml package (ISC), CycloneDX SBOM. MIT licence.
Log in or sign up for Devpost to join the conversation.