Deterministic governance for AI coding agents across the post-code DevSecOps lifecycle, built on GitLab and GitLab Duo Agent Platform.
AFAW Engine is the entry of the AFAW (Anti-Fragile Agentic Workflow) method to the GitLab Transcend Hackathon "Life After Code" (Devpost). Agents write and repair code, deterministic tools decide whether a change is acceptable, and the human approves the merge policy instead of every merge.
Built with: Python 3.12 · FastAPI · GitLab CI/CD (9-stage pipeline) · GitLab Duo Agent Platform (Developer Flow, Fix CI/CD Flow, custom agent) · Terraform · Google Cloud Run · Workload Identity Federation · Docker · Playwright (end-to-end) · pytest · mypy --strict · ruff · mutmut (AST mutation testing) · Semgrep SAST · secret detection · Mermaid.
What it does: a failing check opens an issue, a Duo agent fixes it, a deterministic guard merges when every gate is green, Google Cloud Run redeploys, and a live browser test verifies the result. Around that loop: red-first TDD check, mutation testing, SAST, chaos experiments and a carbon (SCI) measurement of every flow.
Measured so far: 9 of 9 DevSecOps stages have a merge request written by a Duo agent. One run from the orchestrator command to a verified live app took 25 min 19 s and 6.86 GitLab Credits (details); three dependent tasks filed one after another by the orchestrator took 1 h 18 min 23 s, each verified live (details); a planted bug was caught by a live test, fixed by Duo and redeployed in 23 min 44 s (details). Limits are stated in Why use it.
- Author: Agustin Diaz-Cano (www.agustindiazcano.com)
- Hackathon: GitLab Transcend, "Life After Code" (Devpost)
- Path: B, Bring Your Own (existing MIT project, imported to GitLab)
- Autonomy track: Best Hands-off Agent
- Also targeted: Most Stages Covered, Google Cloud bonus (Cloud Run)
- Opted in: Most Environmentally Impactful (one measured optimization, 0.038 to 0.382 gCO2eq saved per mutation run; see Sustainability)
- Demo video: https://youtu.be/42C2ZQdyujw
- Live dashboard (Google Cloud Run): https://afaw-handsoff-ljm5hefnsq-uc.a.run.app
- Demo app that the Duo agents change (Google Cloud Run): https://afaw-demo-app-ljm5hefnsq-uc.a.run.app
- Source of truth: https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/43344247/showcase (a mirror is kept at https://github.com/agustindiazcano/afaw-engine-gitlab)
- Original project (MIT, upstream): https://github.com/agustindiazcano/afaw-anti-fragile-agentic-workflow
- License: MIT
- White paper of this project:
docs/whitepaper/afaw-engine.md(hands-off CI/CD with measured results) - White paper of the original method:
docs/paper/afaw.pdf, DOI 10.5281/zenodo.23050310
Status legend used in this document: Done (merged, evidence given), Merged, not deployed (code in
main, needs a human step), In progress, Planned. Nothing marked Planned or In progress is claimed to work.
| Section | What is there |
|---|---|
| Problem and Why use it | What the project is for |
| The loop and Hands-off status | Each link of the loop and whether it ran |
| Hackathon requirements | Devpost requirements and their state |
| The nine stages and Stages and Duo | The jobs of each stage and where a Duo flow ran |
| Demo app and End to end tests | The deployed app the agents change, and the browser tests that watch it |
| Hands-off method | Diagrams of the loop, self-repair, merge decision, control layers and observability |
| Stack | Technologies; Google Cloud, Terraform and CI/CD in docs/stack.md |
| Dashboard | Live, experiments and environment pages |
| Guardrails and Sustainability | Limits and the carbon estimate |
| Experiments and evidence | Measured results, link by link |
| How to run it and Getting started | Setup |
Several coding agents on one repository fail in predictable ways: they collide on branches and shared context files, claim results they never measured, and write tests that pass whatever the code does. Once code generation is cheap, the open problem is everything after the code: review, security, tests, compliance, release and monitoring.
AFAW Engine runs that part with GitLab Duo flows, while deterministic tools (CI jobs, scripts, security scanners) decide what is acceptable. Principle: the AI decides, the engine measures. Every number comes from a deterministic tool, and a value an agent could have made up is rejected where it would be declared.
Several AI assistants can now write code in the same project at the same time. Left alone, they get in each other's way, they say a job is finished when it is not, and nobody can tell how the project is doing without reading everything.
AFAW is a small set of rules and checking programs that fixes this:
- One job, one branch. Each assistant gets one task and works on its own copy, so two of them do not edit the same thing.
- One shared notebook. Every task, decision and result is written in the repository in the same format. The status board is built by a program from GitLab, not typed by anyone, so a person or an assistant can read how the project is going without guessing.
- Nobody marks their own homework. Ordinary programs check what the assistant says. A new test must fail on the old code. The list of files it says it changed must match the real change. A number it reports must come from a measurement, not from the assistant.
AFAW Engine, this repository, goes one step further for the hackathon: when every check is green a program merges the change, with no person clicking; when something breaks, an issue is opened for a GitLab Duo assistant to fix; the app is redeployed and a browser test checks the live site.
What each benefit rests on today. "Measured" means we read the number from GitLab on the day shown and the link is in the evidence or the experiments.
| Without AFAW | With AFAW | Do we know it works? |
|---|---|---|
| Assistants overwrite each other | One task, one branch; the pipeline rejects a task number that is already taken | Partly. On 2026-10-25 the task-file check failed on clashing task numbers and copied task files four times (issues #48, #49, #51, #52). It caught them; it did not prevent them, because two assistants picked the same number at the same moment |
| An assistant says "done" and it is not | Programs check the claim: red-first test, mutation check, files changed, live check of the deployed app | Yes, measured. Six defects planted on purpose were repaired and none was missed by the checks (experiments); a browser test also caught a bug that every earlier test missed |
| Nobody can tell how the project is going | A board built from the GitLab API with a light for each task | Yes, with a gap. It exists and runs; the live dashboard still shows older numbers than the repository |
| A broken build waits for a person | Failing check, then an issue, then a Duo fix, then an automatic merge and deploy | Yes, measured. For the planted bug of 2026-10-26: issue opened at 01:00:37 UTC, fix merged at 01:13:29 UTC, 12 min 52 s |
| A new assistant must read everything | It starts from one index (LASTCONTEXT.md) and a hand-over file |
Not measured. We expect it saves effort; we have not compared it with reading the code |
| Running more tests than needed wastes energy | The assistant runs only its own test; the pipeline runs the rest once | Not measured as a saving. We measure the cost of a change: see Sustainability |
What it does not prove: the undraft and the merge are done with a person's access token, so GitLab shows that person's name. The summary comment on the issue and the review by the Code Review Flow each ran, started by a person (a mention and an assignment as reviewer). No event starts either of them by itself. A repair that needed more than one attempt has not happened yet.
| AFAW (upstream) | AFAW Engine (this repository) | Status | |
|---|---|---|---|
| Merge | A human approves every pull request | The human approves the policy; the deterministic engine approves each merge (ADR 0007) | Policy documented; the automatic merge rule is merged (task 012, merge_guard) and has run on real merge requests: 30 merge requests made only by agents were merged with a recorded automatic merge (read on 2026-10-26, docs/experiments.md). The undraft and the merge use a person's token; merge requests written by people are merged by people |
| Measuring | The AI decides, the engine measures | Unchanged | Done |
| Forge | GitHub | GitLab (REST API v4); the GitHub path is kept | Done for facts: they are read from the GitLab API (docs/facts.md, task 010) |
| Agents | Coding agents that open pull requests | GitLab Duo flows, configured to run under a group service account instead of a personal token (docs/duo.md) |
Configuration merged; the Developer Flow ran four times and opened !16, !17 and !18; the Fix CI/CD Pipeline Flow ran once by hand (evidence) |
| Guardrails | Human reviews each diff | Human-only files (a weak control, see limits) and a red-first check that a new test fails on the old code | Done |
| Cap of 3 automatic fix attempts per merge request, merge only when the full pipeline is green | Code and tests merged (task 012); it ran on real merge requests (see Hands-off status) and no merge request has needed a second automatic fix attempt |
Diagrams of the loop, the self-repair, the merge decision, the control layers and the observability: docs/method-handsoff.md. The loop is the target design; the status of each link is in Hands-off status:
flowchart LR
I[Issue<br/>or failed pipeline] --> D[Duo Developer Flow<br/>service account]
D --> MR[Merge request<br/>Draft]
MR --> G{{Gates<br/>tests, red-first, mutation,<br/>SAST, secrets, human-only files}}
G -- red --> RF[request_fix<br/>note or issue for Duo]
RF --> D
G -- green --> MG[merge_guard<br/>identity + rules]
MG -- merge: yes --> M[Merge to main]
M --> R[Release, image,<br/>Cloud Run deploy]
R --> E[e2e_live<br/>Playwright on the live apps]
E -- red --> RF
E -- green --> O[Observe<br/>dashboard, experiments,<br/>environment]
Not in the diagram: the review by the Code Review Flow, which ran once on a merge request that a person assigned to it, and a summary comment on the issue, which ran once on a merge request that a person mentioned it on.
| Requirement | Status | Evidence |
|---|---|---|
| Uses GitLab Duo Agent Platform (agents, flows, MCP clients) | Done for the Developer Flow, the Code Review Flow and the loop reporter; in progress for the rest | The Developer Flow, started by assigning an issue through the API, opened merge requests that merge_guard merged; the Fix CI/CD Pipeline Flow ran once by hand and once from its trigger on failed pipelines (it repaired a planted failure in 1 min 43 s, in parallel with the Developer Flow, so the trigger is kept off); the Code Review Flow ran on !118 in three rounds and approved; the custom flow AFAW loop reporter ran once; the custom agents AFAW auditor and AFAW planner exist and have not run (evidence) |
| Public GitLab project with all source, assets and instructions | Done | https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/43344247/showcase (visibility public) |
| MIT License visible in the repository | Done | LICENSE; the project API reports the license key mit |
| Visible CI/CD pipeline history showing the automation | Done | Pipelines of the project, including failed ones, and the repairs that followed them; the failures on main are explained in limits |
| Text description | In progress | Derived from this README; see docs/submission/devpost.md and docs/submission/rules-and-coverage.md |
| Demo video under 3 minutes, YouTube, no third-party trademarks or copyrighted music | Planned | https://youtu.be/42C2ZQdyujw |
| Path B: what was added or changed since Oct 5, 2026 | Done | Added since Oct 5 |
| Path B: deployed project URL, kept live until about Nov 16 | Done | https://afaw-handsoff-ljm5hefnsq-uc.a.run.app (/health answered ok on Oct 24, 2026) |
| Path B: URL of the original MIT project | Done | Link at the top |
| Google Cloud bonus: deployment code in the repository and public URL | Done | deploy/terraform (cloud_run.tf), docs/google-cloud.md |
| Most Environmentally Impactful (opt-in, SCI) | Measured, partial; opted in with one measured optimization | Sustainability |
.gitlab-ci.yml defines 36 jobs, plus the jobs of the SAST and secret-detection templates. test is a technical stage: the GitLab SAST and secret-detection templates require it to exist, and none of our jobs runs in it. The nine DevSecOps stages are the other nine.
| Stage | Jobs (script) | GitLab-native feature | Status | Tasks |
|---|---|---|---|---|
| plan | task_files (check_task_files), docs_check (check_docs), duo_config (check_duo_config) |
Merge request pipelines (workflow:rules) |
Done | 009, 011 |
| create | task_delta (check_task_delta), derived_views and web_data (build_state) |
Merge requests opened by the Developer Flow, review rules in .gitlab/duo/ |
Done for the Developer Flow started by a person (!16 to !18); not started by a trigger or by a template yet | 009, 011 |
| verify | lint (ruff), typecheck (mypy --strict), tests (pytest), red_check, mutation_gate (mutation_ci) |
rules:changes path filters, pipeline cache |
Done | 009 |
| secure | semgrep-sast, secret_detection, security_gate (blocks on any secret or a Critical or High SAST finding) |
SAST template (Semgrep) and pipeline secret detection | Done | 009 |
| govern | paper_pdf, equivalent_mutants_report, human_only_files (a weak backup control: the same merge request can edit it away) |
Strong control deferred: pipeline execution policy or CODEOWNERS with required approval, depending on the tier | Done (weak); strong control Planned | 009, 012 |
| package | package_docker (service Dockerfile); package_buildah as the fallback, selected with PACKAGE_BUILDER=buildah |
GitLab Container Registry | Done for package_docker; the Buildah path has not run |
009, 013 |
| configure | check_configuration (check_ci_config: config schema and CI variables, by name only), terraform_validate |
Protected CI/CD variables | Done: check_configuration passes on main since the GCP_* variables were set by a human (pipeline) |
009, 013 |
| release | release (glab), publish_image and deploy_cloud_run (gcp_access_token.sh, gcloud run deploy) |
release: keyword (a GitLab Release per pipeline on main); id_tokens with Workload Identity Federation |
Done: release, publish_image and deploy_cloud_run passed on main with Workload Identity Federation |
009, 013 |
| monitor | monitor (collect_facts, build_state), monitor_live (check_live: fails if live /health or /api/state does not answer) |
Job artifacts | Done: monitor runs collect_facts on the GitLab API and monitor_live checks the live service; both passed on main |
009, 010, 013 |
Verified in docs.gitlab.com (Oct 24, 2026): the SAST and secret-detection templates run in the test stage and only in branch pipelines by default, so .gitlab-ci.yml moves them to secure and also runs them in merge request pipelines. Basic Semgrep SAST and the secret-detection JSON report are in every tier; findings in the merge request and Advanced SAST need Ultimate. Kaniko is marked as no longer maintained, so Buildah is the fallback.
Counts are read from the evidence file on the day written; a Duo flow counts only if a session ran and left a merge request, a commit, an issue or a note. Touching a stage is enough for the Most Stages Covered prize (rules).
| Stage | Job that runs | A Duo flow ran here | Evidence |
|---|---|---|---|
| plan | task_files, docs_check, duo_config |
Duo-authored change to check_task_files (a count of files checked), merged by merge_guard |
#37 → !79 |
| create | task_delta, derived_views, web_data |
Yes: the Developer Flow opened merge requests from issues | evidence |
| verify | lint, typecheck, tests, red_check, mutation_gate |
Yes: Fix CI/CD once; repairs from request_fix issues |
evidence, experiments |
| secure | semgrep-sast, secret_detection, security_gate |
Duo-authored change to security_gate (JSON summary), merged by merge_guard |
#38 → !77 |
| package | package_docker, package_demo_app |
Duo-authored change to the demo app image (HEALTHCHECK), merged by merge_guard |
#39 → !75 |
| release | release, publish_image, deploy_cloud_run |
Duo-authored change to release_notes, merged by merge_guard |
#40 → !80 |
| configure | check_configuration, terraform_validate |
Duo-authored change to check_ci_config (summary line), merged by merge_guard |
#41 → !76 |
| monitor | monitor, monitor_live, merge_guard, request_fix |
Duo-authored change to check_live (JSON result file), merged by merge_guard; later Duo repaired check_live twice (!85, !87) |
#42 → !81 |
| govern | paper_pdf, human_only_files, equivalent_mutants_report |
Duo-authored change to human_only_files (--list), merged by merge_guard |
#43 → !78 |
All nine stages have a job that runs, and each has a Duo-authored merge request that went through the
pipeline (9 of 9). "A Duo flow ran here" means a change to a script or file of that stage written by the
Developer Flow, not that Duo ran inside the stage's job. Times and the burst that produced the seven new
rows are in docs/experiments.md; the undraft
and the merge are done by merge_guard with a person's token, so GitLab shows that person's name.
What is configured in the GitLab project, read on 2026-10-26. A person creates agents, flows and triggers in the interface (AI > Agents, AI > Flows, AI > Triggers); the sources are in duo/custom-flow.yaml and the other files of that folder, and the rules in docs/duo.md.
| Name | Kind | Runs as | Started by | What ran, and when |
|---|---|---|---|---|
| Developer Flow | Foundational flow | duo-developer-gitlab-ai-hackathon (id 33073386) |
A person or a script assigning an issue to the account | Dozens of sessions. Example: issues #37 to #43 became !75 to !81 in 95 to 132 s |
| Fix CI/CD Pipeline Flow | Foundational flow | duo-fix-ci-cd-pipeline-gitlab-ai-hackathon (id 33073387) |
Trigger "AFAW fix on failed pipeline" (event Pipeline events, Failed), created 2026-10-26 | Ran once by hand on 2026-10-24 (pipeline 2985662569) and once from the trigger on 2026-10-26: it opened a repair 1 min 43 s after a planted lint failure, in parallel with the Developer Flow started by request_fix; both made the same fix, so the trigger is kept off outside a demonstration |
| Code Review Flow | Foundational flow | @GitLabDuo |
A person assigning @GitLabDuo as reviewer |
Once: session 10028468 on !118, 2026-10-26, 1 min 51 s, three comments |
| AFAW loop reporter | Custom flow (duo/custom-flow.yaml) |
ai-afaw-loop-reporter-gitlab-ai-hackathon (id 43732366) |
Trigger on Mention: a person mentions the account in a merge request or issue | Once: !110, sessions 10028680 and 10028682, 2026-10-26, 1 min 23 s. Two sessions started for one mention because the trigger existed twice; one was removed |
| AFAW auditor | Custom agent (duo/custom-agent.md) |
Chat in the Duo sidebar | A person, from a merge request | Created 2026-10-26; not run yet |
| AFAW planner | Custom agent (duo/custom-planner.md) |
Chat in the Duo sidebar | A person, from an issue | Created 2026-10-26; not run yet |
Code, not agents, does the rest of the loop: scripts/orchestrator.py picks the next task and files the issue, request_fix opens an issue when a pipeline fails, repair_check compares the jobs an issue lists with the jobs of its repair, merge_guard decides and performs the merge, telemetry_report writes the event report and handoff writes the hand-over file for the next session.
| Link of the loop | Status |
|---|---|
| Nine-stage pipeline with a real job in each stage | Done |
| Security gate that blocks (reads both scanner reports) | Done |
GitLab Release created on main |
Done (v0.9, v0.15 and v0.20) |
| Custom agents and custom flow | Created in the GitLab UI by a person on 2026-10-26 from the sources in duo/: the agents AFAW auditor and AFAW planner (not run yet) and the flow AFAW loop reporter (ran once). The agent AFAW merge agent exists in the AI Catalog (id 1019706, version 1.1.0, seven read-and-note tools, no merge tool because Duo has none); the triage flow (duo/triage-flow.yaml) is not created. Table: Duo agents, flows and triggers |
| Developer Flow opens a merge request from an issue | Ran: !16, !17, !18 and later ones (evidence). Assigning an issue through the API starts it with no click (issues #6 and #12). Its merge requests are Draft |
| A failed pipeline is repaired | Ran: the request_fix job opens an issue for the Developer Flow account; Duo repaired planted defects and one real defect, merged by the guard (experiments.md). The Fix CI/CD Pipeline Flow itself ran once, by hand, and only commented; a trigger on failed pipelines ran once on a planted failure and duplicated that path, so it is kept off |
| Code Review Flow comments on a merge request | Done once: assigning @GitLabDuo as reviewer of !118 started a session in 4 seconds and it left three comments in 1 min 51 s; after two fixing rounds a third session approved it at 03:07:45 UTC (review session 10028468); it does not run on Draft merge requests |
| Cap of 3 automatic fix attempts per merge request | Merged (task 012, merge_guard); held in the chaos case C3; no real repair needed a second attempt |
| Draft cleared and merge requested when every gate is green | Ran on 30 merge requests made only by agents (read on 2026-10-26) by merge_guard with AFAW_AUTO_MERGE=true, using the token of a person (AFAW_MERGE_TOKEN), so GitLab shows that person's name on the notes. It merges nothing unless a person turns it on (docs/autohealing.md) |
| Release, deploy to Cloud Run and live monitor | Done, including after an automatic merge (pipeline 2986982832); monitor_live proves the commit |
| Summary comment on the original issue | Ran once: a person mentioned the AFAW loop reporter on !110 and it posted a note on the merge request and one on issue #55 in 1 min 23 s (sessions 10028680 and 10028682; the trigger was duplicated and one copy was removed). No event after a merge starts it by itself, so a person mentions it (docs/duo.md) |
| Orchestrator picks the next task and files its issue | Ran: scripts/orchestrator.py --create (ADR 0010) filed issue #61 in the end to end rehearsal (25 min 19 s to a verified app) and issues #64 to #66 in the long chain |
| Repair check and telemetry report | Merged (!110, !112, written by Duo; wired in the pipeline by !119). A defect in the wiring made repair_check fail on merge requests with no issue number; it caused two extra Duo sessions and was fixed in !130 with a test that runs the line under pipefail |
src/notes_app is a small notes app deployed on its own Cloud Run service. The dashboard shows the
project; this app is the product the agents change through issues, so a reader can open it and see
what an agent did. Details, limits and pipeline jobs: docs/demo-app.md. Status:
live on Cloud Run; Duo changed it through issues (the UI redesign #44, the summary strip) and the browser tests watch it.
- Verification gates: red-first check (a new test must fail on the old code), mutation gate on the diff, lint, strict typing, tests, in parallel.
- Security gate: GitLab SAST and secret detection, with a script that turns their reports into a blocking decision.
- Governance: human-only files (
.gitlab-ci.yml,AGENTS.md,state/equivalent_mutants.json,deploy/, and the mutation and gate settings instate/config.json), merge requests judged by the identity of their author and of the pusher, a task file and a delta per task, decision records (ADRs). - Derived state: task board, metrics, traffic lights,
LASTCONTEXT.mdand a dashboard with a live GitLab panel, rebuilt from git and the forge API and never committed. Reading the GitLab API is Done (task 010,docs/facts.md). - Read-only service (
src/afaw_web):/,/live/,/health,/api/state(derived metrics verbatim), and/api/ask, a deterministic file search that cites paths and copies measured numbers verbatim. No language model call, GET only. - Deploy: Terraform for Cloud Run (public, minimum 0 and maximum 2 instances), Artifact Registry, Workload Identity Federation from GitLab OIDC, no JSON keys. Done: applied by a person on Oct 24, 2026.
Infrastructure as code, identity, CI/CD variables and the full table: docs/stack.md.
Python, pytest, mypy --strict, ruff, FastAPI, Docker, Terraform, GitLab CI/CD, GitLab Duo Agent Platform, GitLab Container Registry, Google Cloud Run, Artifact Registry, Workload Identity Federation.
flowchart TB
subgraph GitLab
MR[Merge request] --> PIPE[Nine-stage pipeline]
DUO[Duo flows<br/>service account] --> MR
PIPE --> REG[Container Registry]
PIPE --> REL[GitLab Release]
end
PIPE -- OIDC id_token --> WIF[Workload Identity Federation]
WIF --> AR[Artifact Registry]
AR --> RUN[Cloud Run service<br/>read-only, public]
PIPE -- check_live --> RUN
human_only_filesis a weak control: it lives in.gitlab-ci.yml, so the same merge request can remove it. The strong control (a pipeline execution policy in a separate project, or CODEOWNERS with required approval) is planned and depends on the role and tier available.mainwas red on purpose until a human ranterraform applyand set the protectedGCP_*CI/CD variables (check_configurationandpublish_imagefail without them); the pipelines ofmainbefore that stay red in the history. The service is public with no rate limiting; its cost is bounded by a maximum of two instances. Steps:docs/google-cloud.md.- Security scanners do not block on lower severities; those are counted and printed.
- The account has 500 GitLab Credits per month, which bounds how many flow runs fit (ADR 0007).
collect_factsreads the GitLab REST API v4. Tasks 001 to 006 and 999 have no facts (their branches were never merge requests in this project), tasks 007 and 008 have no CI facts (no pipeline existed before the pipeline of task 009), andstarted_atof task 007 comes from the imported history, so its lead time is not a real working time.monitoris skipped onmainwhilemainis red.- Branch protection, approvals and group settings of the GitLab project are set by its maintainers; this repository does not verify them.
/api/askis a deterministic search; it does not generate answers.- The paper PDF is compiled by the
paper_buildjob but not committed by CI: after a change toAGENTS.md,docs/paper/afaw.texordocs/img/, download the job's artifact and commitdocs/paper/afaw.pdfin the same merge request;paper_pdffails otherwise. - No claim about speed, cost, defect rate or carbon is made anywhere in this repository without a measurement.
Design choice: the agent runs only the test it is writing, and the full check suite runs once in CI, instead of an agent running the whole suite locally and retrying with its whole context. The Cloud Run service is configured with a minimum of 0 instances and a maximum of 2.
Intended metric: Software Carbon Intensity, SCI = (O + M) per R with O = E x I, as the Green Software Foundation specification defines it (E energy in kWh, I carbon intensity in gCO2eq per kWh, M embodied emissions, R the functional unit). Here R is one merged merge request. Inputs task 010 captures per merge request: CI job seconds and minutes, pipelines and retries (Done). Inputs we must assume and declare: runner power draw, grid carbon intensity, embodied emissions. The ISO number of the specification is not stated here because it was not confirmed.
Status (2026-10-25): first measurement taken, partial. scripts/sci_report.py reads the job seconds of every merged merge request (its own pipelines plus the main pipeline of its merge commit) from the public GitLab API and gives a range of gCO2eq, never a point. On the 30 most recent flows (2026-10-25): 89,312 job seconds, 39 to 233 Wh and 13.3 to 134.6 gCO2eq in total, about 0.44 to 4.49 gCO2eq per flow. The range is wide because power per vCPU (0.71 to 4.26 W, Cloud Carbon Footprint) and grid intensity (343 to 579 gCO2eq/kWh, Google Cloud 2025, the two hosted-runner regions) are assumptions. It is SCI without M: embodied emissions are not estimated, and the energy of the model behind GitLab Duo is not measured. The same measurement runs in the web_data job on every pipeline and appears as the indicator at the top of the dashboard; the mutation tool change (mutmut 56 s against cosmic-ray 309 s, one run) is the one measured optimization. A comparison of AFAW against reading the code is not measured: it stays a hypothesis. The Most Environmentally Impactful prize is opt-in; the entry opts in with the one measured optimization below, from docs/experiments.md (EXP-09).
Measured optimization (2026-10-25, docs/submission/mutation-tools-measurement.md): the mutation gate runs mutmut instead of cosmic-ray. On the same GitLab.com shared runner (saas-linux-small-amd64, 2 vCPUs) against src/afaw_web, the whole job took 56.08 s with mutmut and 309.34 s with cosmic-ray, so mutmut saves 253.26 s per run. With the assumptions above (E = seconds / 3600 x 2 vCPUs x 0.71 to 4.26 W x PUE 1.1; I = 343 to 579 gCO2eq/kWh) that is 0.110 to 0.659 Wh and 0.038 to 0.382 gCO2eq per run, SCI without M. The two tools did not do equal work (mutmut: 104 mutants, score 71.2 percent; cosmic-ray: 240 mutants, 77.1 percent) and each ran once or twice, so this is a measured difference and not a statistical claim. The effect of path filters, of running only the new or modified files through the mutation gate, and of scaling Cloud Run to zero is not quantified.
The base method was committed on Sep 29 and 30, 2026. Since Oct 5, from git log and the merge requests of the project:
| Date | Change | Evidence | Status |
|---|---|---|---|
| Oct 23 | English-only rule and first GitLab Duo rules in AGENTS.md |
Commit 8f29bb3 |
Done |
| Oct 23 | Test suite passes on Windows (task 007) | Commit 2f696fe, MR !1 |
Done |
| Oct 24 | Repository imported into the GitLab project | MR !2 | Done |
| Oct 24 | Merge policy ADR 0007, README and roadmap (task 008) | MR !3 | Done |
| Oct 24 | Nine-stage pipeline and security gate (task 009) | MR !4, release v0.9 | Done |
| Oct 24 | Read-only service and Terraform for Cloud Run (task 013) | MR !5, release v0.15; deployed and checked in this pipeline | Done |
| Oct 24 | Duo configuration, custom agent and flow sources (task 011) | MR !6, release v0.20 | Merged; the flows must be enabled by a person |
| Oct 24 | GitLab fact collection (task 010) | MR !7, release v0.23 | Done |
| Oct 24 | Merge guardrails and automatic merge rule (task 012) | MR !10 | Merged; ran on real merge requests since 2026-10-25 (see Hands-off status) |
| Oct 24 | Demo script and Devpost text (task 014) | MR !8 | Merged; the video is not recorded |
| Oct 25 to 26 | SCI range per merged merge request, indicator on the dashboard (task 119) | MR !102 | Done, partial: without M and without the energy of the Duo model; the comparison against reading the code is not measured |
| Criterion | Where to look |
|---|---|
| Technological implementation | .gitlab-ci.yml (nine stages, 36 jobs), scripts/security_gate.py, scripts/human_only_files.py, deploy/terraform, Workload Identity Federation without keys |
| Design (end-to-end workflow) | The loop and Hands-off status, stated link by link |
| Potential impact | Governance of agent-written code for teams that already generate code and need review, security and release to keep up |
| Innovation | Merge approval moved from each merge to the policy, with deterministic gates and identity checks as compensating controls (ADR 0007) |
| Presentation | Demo video https://youtu.be/42C2ZQdyujw |
Both pages carry the project name and logo in the top bar, links to the views and the repository, and the author in the footer (a link to www.agustindiazcano.com, checked on 2026-10-25: it answers 200 and its title is "Agustin Diaz-Cano | Software Engineer"). The logo is a simple mark drawn for the page, not an official asset.
The live service serves it at its root (/; /live/ is the old address of the same page), and the demo of invented data at /demo/. The board, the metrics and the history are derived at build time from main, so they change when a new image is deployed. The Live from GitLab panel is different: the browser reads the public GitLab REST API every 30 seconds (no token) and shows merge requests merged and open, Duo flow sessions, pipelines passed and failed, and the latest merge requests and pipelines. Draft merge requests show as draft.
Two more pages of the live service, built when the image is built from docs/data/experiments.json, a file in which every figure names its source. Each chart has a table view, a legend when it has two series, and no script.
/experiments/: coverage before and after the tests Duo wrote, mutation score measured in CI, minutes from the request to the result, the merge requests merged by merge_guard, and the tools built from a specification and judged by tests Duo never saw.
/environment/: the estimated Software Carbon Intensity of one mutation run with each tool, computed by scripts/sci_estimate.py (written by the Duo Developer Flow) from CI job seconds, with the assumptions printed. It claims one measured comparison and nothing more; the limits are on the page.
A dashboard built by scripts/build_state.py from mock data: a fictional project whose every task and number is invented (examples/mock_dashboard.py). It shows each traffic-light rule firing: CI failing, a blocker not done, no activity for three days, nothing measured.
Build it locally: python -m examples.mock_dashboard mock-output and open mock-output/index.html.
Each command below was run on Oct 24, 2026 (Python 3.12).
python -m pip install --group dev
ruff check scripts tests examples .claude/hooks
mypy --strict scripts src
python -m pytest tests
python -m scripts.check_docs
python -m scripts.build_state --ref HEAD --out build/state
python -m examples.mock_dashboard mock-output # demo dashboard from mock dataThe read-only service reads the views that the web_data job assembles. To run it locally, from the repository root:
python -m scripts.build_state --ref HEAD --out build/web-data/live
python -m examples.mock_dashboard build/web-data/demo
mkdir -p build/web-data/docs && cp -r docs/adr docs/gotchas build/web-data/docs/
PYTHONPATH=src AFAW_WEB_DATA_DIR=build/web-data python -m uvicorn afaw_web.main:app --port 8080Then open http://localhost:8080/ and http://localhost:8080/health. Pipeline, deploy and one-time human steps: docs/google-cloud.md and docs/duo.md.
GitLab does not copy branch protection, CI/CD variables or Duo settings when a project is imported, so each step below is done once by a person with the role shown. The menu paths come from docs.gitlab.com (checked Oct 24, 2026) and from docs/google-cloud.md and docs/duo.md.
- Import: Create new > New project/repository > Import project > Repository by URL, with the URL of this repository. A username and an access token are needed only for a private source.
- Configure
state/config.json: code folders,mutation_critical_paths,mutation_min_scoreand thedashboardsettings. - Protect
main: Settings > Repository > Branch rules >main> Protect branch. Set who may merge and who may push; nobody should push directly. What the project actually has set is not verified by this repository (see limits). - CI/CD variables and Cloud Run: a human runs
terraform applyand adds the protectedGCP_*variables under Settings > CI/CD > Variables, as indocs/google-cloud.md. Until thenmainis red. - Duo flows: enable the foundational flows, create the custom agent and flow, and add the triggers, as in
docs/duo.md. Some of these need the Owner of the top-level group and a Premium or Ultimate tier. - Verify: open a test merge request and check that the pipeline runs.
- Start: open a terminal per agent and answer the
ROLE:question.
.github/ and scripts/setup_protection.sh belong to the original GitHub project and do nothing on GitLab.
- One terminal per agent; answer its
ROLE:. The session-start hook injects the generatedLASTCONTEXT.mdandPENDING.md. - The agent creates its branch, works with TDD running only its own test, and pushes; the pipeline verifies the rest.
- At the end it writes
summaryandnext, fillsfiles_touchedwithpython -m scripts.check_task_delta --base origin/main --write task_NNN, records any decision as an ADR, and asks before opening the merge request. Use the name of your GitLab remote in place oforiginif it differs. - A person opens and merges the merge request. The automatic merge rule exists (task 012) but is off until
AFAW_AUTO_MERGE=true, with the switch on, the guard merges the merge requests made by the Duo flow; a person merges the rest. - Ask for status at any time, or open the dashboard.
Generated from git ls-files on Oct 24, 2026.
.
├── AGENTS.md # The rules: single source for every agent
├── CLAUDE.md # Imports AGENTS.md
├── PENDING.md # The human's roadmap
├── CODEOWNERS # Code owners (used once required approval is enabled)
├── LICENSE # MIT
├── Dockerfile # Image of the read-only service
├── pyproject.toml # Dev dependency group and tool settings
├── .gitlab-ci.yml # Nine-stage pipeline (human-only file)
├── .gitlab/duo/ # Duo review rules and sandbox configuration
├── .agents/agents/ # Workspace subagent for Duo (afaw-reviewer)
├── duo/ # Custom agent and custom flow sources
├── .claude/ # Hooks (session start, safety guard, lint), settings, skills
├── .github/ # Upstream GitHub workflows, kept for the original project
├── src/afaw_web/ # Read-only FastAPI service
├── src/back/, src/front/ # Empty placeholders of the template (.gitkeep only)
├── deploy/ # gcp_access_token.sh and Terraform for Cloud Run (human-only)
├── state/ # config.json, schemas/, tasks/task_NNN.json
├── context/tasks/ # One delta per task
├── docs/
│ ├── adr/ # Decision records (0001 to 0008)
│ ├── gotchas/ # Operational traps
│ ├── diagrams/ # Graphviz sources of the diagrams
│ ├── img/, screenshots/ # Rendered diagrams and dashboard screenshots
│ ├── templates/ # ADR and gotcha templates
│ ├── duo.md # Duo setup and what needs a person
│ ├── google-cloud.md # Cloud Run setup and one-time human steps
│ └── paper/ # White paper (afaw.pdf, afaw.tex)
├── scripts/ # build_state, collect_facts, check_*, red_check,
│ # mutation_ci, security_gate, human_only_files,
│ # check_live, afaw_state/ (derived-state package)
├── examples/mock_dashboard.py # Demo dashboard from mock data
└── tests/ # tooling/ (scripts and hooks), web/ (service), back/, front/
build/ is generated and ignored by git; it does not appear in the tree. Folders that do not exist yet: none; src/back/ and src/front/ exist but hold only a .gitkeep.
The sections below are the original method, unchanged in substance. In this variant, "a human approves every PR" is replaced by the policy described above.
The tables and diagrams from here to the end describe upstream AFAW, where a human approves every pull request. The hands-off method of AFAW Engine, with its own diagrams, is in
docs/method-handsoff.md.
The method does not require these exact tools. Some parts are what make it work: remove them and the problem they solve comes back.
| Core: remove it and the problem returns | Replaceable: same idea, another tool | Optional |
|---|---|---|
| Each task writes its own state files; shared views are derived | The CI system, the test framework | The dashboard and its charts |
| Measured values, never written by the agent | The mutation engine | Mutation testing outside critical code |
| Heavy checks in the cloud, not on the agent's machine | Hooks, for another agent or tool | Timings and CI cost |
One branch per task, no push to main |
The format of decision records | Per-role context files |
| The red step checked by a tool, not reported by the agent | The language of the rules file | An assistant that answers from the records |
| Failure | Mechanism | What remains |
|---|---|---|
| Wrong-branch work | Step 0 (git status, git branch), one branch per task; a hook denies a push to main |
Agents sharing one working tree can overwrite uncommitted files |
| Shared-state conflicts | Each task writes only its own files; shared views are derived and never committed | Agents do not see unmerged work on other branches |
| Unmeasured claims | Measured values rejected in task files; files_touched must equal the diff; red step verified in CI |
A person must still follow the CI link |
| Vacuous tests | Red-first check; mutation testing on the diff; equivalent mutants accepted only by the human | Mutation score is not correctness |
| Heavy local runs | Two tiers: single tests locally, everything else in CI, in parallel | CI queue latency and minutes |
| Unreviewed integration | Hooks, CI checks, branch protection, merge policy | Hooks are best-effort; branch protection is a setting of the forge |
- Task files and derived state. An agent writes only
state/tasks/task_NNN.jsonandcontext/tasks/task_NNN_context.json. Everything shared is derived bypython -m scripts.build_state, never committed. Timestamps and CI results are measured from git and the forge API. Traffic light: red when a dependency is not done or CI fails on the latest commit of the branch; yellow when in progress with no commit or CI run for more thanstale_days; green otherwise; "no data" when nothing was measured. - Project knowledge. State says what, prose says why: ADRs in
docs/adr/, gotchas indocs/gotchas/, indexed in the generatedLASTCONTEXT.md. - Strict TDD in two tiers. Locally the agent runs only the test it is writing; CI runs the rest, including the red-first check (
python -m scripts.red_check). - Mutation testing on the diff. Only new or modified files are mutated; the gate is K / (N - E_human), where only the human records an equivalent mutant.
- Human authority over the policy. Rules in
AGENTS.mdare advice, so they are also enforced by hooks, CI and protected branches. - Dashboard. A static page with progress, board, timing and CI cost, built from derived state.
Diagrams: docs/diagrams.md, rendered in docs/img/. Rebuild: for f in docs/diagrams/*.dot; do n=$(basename "$f" .dot); dot -Tsvg "$f" -o "docs/img/$n.svg"; done. A merge request that changes afaw.tex, AGENTS.md or docs/img/ must also commit the rebuilt docs/paper/afaw.pdf.
- The reference rules assume a Python backend (async, pytest,
mypy --strict, Postgres, Terraform). - The method has not been validated in a controlled study and makes no claim about speed, cost or defect rates.
- Mutation score measures how sensitive the tests are to changes, not whether the code meets its requirements.
- The red-first check shows that a new test fails on the old code, not that it fails for the right reason.
- Checks verify the ADRs that exist; a decision that was never recorded is left to the reviewer.
- Documentation that stops being true misleads more than none; nothing detects a stale decision record that nobody replaced.
DOI 10.5281/zenodo.23050310. Author: Agustin Diaz-Cano, MS Candidate, Information Systems Engineering (UTN). ORCID 0009-0001-4336-490X.
MIT
Built for the GitLab Transcend Hackathon, Life After Code. Author: Agustin Diaz-Cano.





