-
-
Ghost Train: a GitLab release train built and run by AI coding agents, with a manual approval and a carbon receipt per release.
-
Path B, before and after: Seance on GitHub (one workflow) against the same app on one GitLab pipeline with five Duo flows.
-
Pipeline #2970065256 built release v0.47: the highlighted production job is the manual approval; staging, in orange, is an allowed failure.
-
The nine lifecycle stages on eight CI stages, and what ran for each (from the README table).
-
Merge request !3, 2026-10-21 UTC: a seeded type error fails verify; three Duo flows follow, production is played, release v0.47 is built.
-
Merge request !3: the Code Review flow note names the type error at src/format.ts line 29 and says an AI agent wrote it.
-
Merge request !3: the Fix CI/CD Pipeline flow suggests the repair, putting the return type back to string.
-
Merge request !3: the Release Summary flow says the pipeline passed with warnings (staging failed) and lists what to check.
-
Release v0.47: the Approval section names the account that started the manual production job; an AI agent played it via the API.
-
Carbon per release: old sequence 0.269306 gCO2e; tests-only verify 0.024482 (90.9 % lower); a full train 0.362 to 0.398.
-
node ci/receipt.mjs --compare ci/baseline.json ci/after.json: plain Node, no install, no network.
Start here (no login needed)
- The app this train ships (Seance, a Tari testnet demo): https://ghost-train-39ca8f.gitlab.io/
- As of Oct 26 the Tari test network's public indexer no longer holds the older demo transactions; the page then says so and shows its recorded copy, and stays on recorded data until you press the mode button again; with live data, Summon shows a live transaction when the latest ones hold a private transfer.
- Carbon receipt of the latest release (written before the budget limit was set; see README, Carbon): https://ghost-train-39ca8f.gitlab.io/receipt.md
- Release v0.47, whose description is its receipt: https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/39972839/ghost-train/-/releases/v0.47
- The recorded evidence run, MR !3: a seeded type error turned
verifyred; a review, a fix suggestion (applied by hand), a merge and a manual approval followed, to release v0.47: https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/39972839/ghost-train/-/merge_requests/3 - The nine lifecycle stages, each with the job or flow run that covers it: https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/39972839/ghost-train#the-nine-stages
- Reproduce the carbon numbers: clone the repository and run
node ci/receipt.mjs --compare ci/baseline.json ci/after.json(plain Node, no install; README, Run it yourself). - Limits, what was seen only once and what is not measured: https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/39972839/ghost-train#limits
- Video, under 3 minutes: https://youtu.be/fqlFEfpyRvA
Supervised shape with an AI approver: the agent approves the outcome (the manual production job), no human clicks, flows do not chain; a person can take that seat with one click. The receipt would then record that person's login, but its fixed approval sentence names the AI agent and would need changing; nobody has done this.
What it does
Ghost Train puts an existing MIT-licensed web app, Seance, on a GitLab release train: one pipeline, five GitLab Duo flows (GitLab Duo Agent Platform) and a carbon receipt with every release. Together they cover nine lifecycle stages. Seance lets you pick a real transaction on Tari's Esmeralda test network and watch three ghosts try to read it.
The pipeline runs typecheck, unit tests and build (verify); GitLab's SAST and Secret Detection templates and a dependency license check; packages the build in the Generic Package Registry (package); runs three live checks against the Tari test network (staging); writes the carbon receipt and checks it against a budget file (budget); and then stops at a manual production job, the approval step. After it, a GitLab Release carries the receipt as its description and the site is published to GitLab Pages. A scheduled probe requests the live site and expects HTTP 200 and a page marker; its schedule is daily at 05:37 UTC.
Before and after (Path B)
Path B asks for the before (manual, fragmented) and the after (automated, integrated). Both are in the repositories.
Before: Seance on GitHub, at the commits imported here (c22eefe, then e98765a).
- It has one GitHub workflow,
pages.yml:npm ci,npm test,npm run build(the build script runs the type check first), then a deploy to GitHub Pages. - Its other checks (
stranger,gate,smoke,links,scanandlicenses) are npm scripts that no workflow runs. - It has no SAST or Secret Detection job, no package registry copy, no approval step, no release step and no carbon receipt.
After: the same app on one GitLab pipeline, with five GitLab Duo flows around it.
verify: type check, unit tests and build.secure: GitLab's SAST and Secret Detection templates and a dependency licence check.package: a copy of the build in the Generic Package Registry.staging: three live checks (stranger,linksandsmoke) against the Tari test network; this job may end orange and the pipeline goes on.budget: the carbon receipt, checked against the limit inci/budget.json.release: the manualproductionjob (the approval), then a GitLab Release with the receipt as its description;deploypublishes the site to GitLab Pages.monitor: a dailyprobeof the live site; a failed scheduled probe starts the Incident Triage flow, which opens an issue.- The five flows: Code Review, Release Summary, Incident Triage, Developer and Fix CI/CD Pipeline.
The problem and who it is for
When AI agents write and ship code, a reader of the repository needs to see which actor did each step and what the pipeline cost. Ghost Train is for a team that lets AI agents drive a repository and has to answer for what shipped. It keeps the record in public: a table of what ran and who did it, the pipeline history, and a receipt with every release. The receipt carries the measured seconds of the jobs that ran before it was written, the energy and gCO2e per job, the totals, the SCI of the release, the approval record (the login that started production), the stage map, an MIT check and a list of what is not measured. The idea is the combination: the job that writes the receipt also checks the budget, and the approval step needs it.
How GitLab is used
Five project flow triggers start the five flows. Code Review starts when a merge request is created. Release Summary starts when a reviewer is assigned. Incident Triage starts on its own when a pipeline fails; it stops at once unless the failed pipeline is a scheduled probe, in which case it opens an issue. Developer starts when the account is assigned or mentioned. Fix CI/CD Pipeline starts when a pipeline fails; its trigger is off now and was on only while that flow was recorded. Code Review, Release Summary and Incident Triage are custom flows defined in the flows/ folder (their AI Catalog entries are private, so those files are the public definition); Developer and Fix CI/CD Pipeline are GitLab's own flows. The pipeline file uses free-tier GitLab features only, and the project has no secrets: the only credential is the job's own CI_JOB_TOKEN.
The README maps all nine lifecycle stages (plan, create, verify, package, secure, release, configure, monitor, govern) to jobs and flow runs. Each row says what ran, where it is defined and where to see it, without logging in. Plan is the thin stage: work is tracked as issues, issue #2 was opened by the Incident Triage flow after a failed scheduled probe, and no planner agent was used. That probe failure was a drill: a schedule that is switched off was played once by hand against a path that does not exist on the real Pages host, so the scheduled pipeline failed and started Incident Triage. The Developer flow answered a mention on issue #1 with one word and opened no merge request. On the failed pipeline of a branch broken on purpose, Fix CI/CD Pipeline opened Draft MR !2 with a one-line fix after about 22 minutes. On the failed pipeline of a merge request (the recorded run below) it posted a suggestion on the diff and opened nothing. Each was seen once, and the cause of the difference was not examined.
The recorded run
On 2026-10-21 the AI agent ran the failure path once, on purpose, on merge request !3, from a red job to a release. A type error was seeded in src/format.ts and verify failed with error TS2322. The Code Review flow named the defect in its review. Incident Triage stopped at once (not a failed scheduled probe). The Fix CI/CD Pipeline flow worked for about eleven minutes and posted a suggestion on the diff that puts the return type back; it opened no branch, merge request or commit. The AI agent applied the suggestion by hand, not with GitLab's Apply button (commit 5d81ec9), and the pipeline of that commit (pipeline 45) passed, with staging orange. The agent then assigned a reviewer, which started the Release Summary flow; merged the merge request with a local merge commit (GitLab shows it as merged manually); and played production, the approval, on the train that built release v0.47 (pipeline 47). Release v0.47 is the live one: the receipt on the site is its receipt.
The staging job of that train ended orange too: its stranger check wants three recent transactions with private transfers among the latest 40 of the public indexer and found none; links and smoke passed. The receipt lists staging as failed. Orange means that one live check disagreed with the live test network at that moment; the job is allowed to fail and the pipeline goes on.
Carbon
Each receipt computes SCI = ((E x I) + M) / R, the Green Software Foundation formula, with R = one release. E comes from the measured seconds of each job and Cloud Carbon Footprint watts per vCPU for GCP (2 vCPU, 50 % utilisation, PUE 1.1), so time is measured and energy is modelled. I = 575.77 gCO2eq/kWh, the 2024 figure for Google Cloud region us-east1, where GitLab.com runs untagged jobs on its small Linux runner by default. M = 0, labelled "not estimated". The comparison uses five runs of the old way (typecheck, tests, build, licenses and the live checks one after the other, nothing cached) and five of the new way, with no tuning and no extra runs.
Result, medians of five runs each: the verify job of a push that changes only tests costs 0.024482 gCO2e (the five runs took 28 to 31 s) against 0.269306 gCO2e for the old sequence (the five runs took 285 to 325 s), which is 90.9 % lower. That holds only for such a push: the path filters skip the licenses, package, staging and release jobs because their inputs did not change, and the checks themselves do not run cheaper. SAST and secret detection, which the old sequence never ran, add 0.053337 gCO2e to such a push; counting them, a tests-only push costs 0.079568 gCO2e at the median, 70.5 % below the old sequence. A full train costs more than the old sequence: 0.362 to 0.398 gCO2e per release against 0.269.
The file ci/budget.json sets max_sci_g to 0.60 gCO2e per release (1.5 times the largest SCI of any full train seen when it was set, rounded up to the next 0.05 g). The budget job exits with an error above the limit, and production needs budget. In CI the job has been seen passing: pipeline 48 reads "SCI budget: 0.6 gCO2e (ok)" for an SCI of 0.3707 gCO2e per release. Nobody played production on that train, so no release was made from it. Its failing path has not been run in CI. Not counted: the energy of the AI models (agents and Duo flows) and of GitLab's own services, the energy of the pages when visitors load them, and the jobs that run when or after the receipt is written.
What we added or changed (Path B)
Original project: Seance (MIT; its first commit is dated 2026-10-18), https://github.com/Kylne700/seance/tree/c22eefe473bb7cc62eb3a17190418f3dea988f1a. An AI coding agent wrote it for another contest (the Tari October Build Contest) and published it on GitHub under the MIT licence; it was imported into this GitLab project at commit c22eefe. One later Seance commit, e98765a, is merged here: it re-made the demo transactions after the public Tari indexer dropped its history of 14-18 October 2026. New work, all made in this GitLab project (created on 2026-10-20) after the Submission Period began on October 5, 2026: .gitlab-ci.yml, the ci/ folder (for example receipt.mjs, probe.mjs, package.mjs, before.mjs, live.sh, budget.json, baseline.json and after.json), the flows/ folder (the three custom flow definitions), the five project flow triggers, the deployment to GitLab Pages, a .gitignore and the top part of the README. The app files here are those of e98765a, with one exception: one comment line at the end of test/format.test.ts, the marker of the measured tests-only runs. Commit e98765a itself also made small changes to the app (the demo token's resource id, a message for a transaction the indexer no longer holds, the wording of one score line) and to the tests that go with them.
How it was built
Built and run by AI coding agents (Claude Code) for the entrant, Kylne700. The pipeline, the receipt tool, the flow files, the README and this submission text were written by AI coding agents, and so were the video and its synthetic-speech narration. Seance, the app they ship, was also developed with an AI coding agent. Who did what: the entrant chose the project name and owns the accounts the agents used; the agents also ran the evidence run and played production for each of the three releases (v0.2, v0.25 and v0.47).
The approver is the AI agent (Claude Code) that drives the entrant account, the GitLab account Kylne700. The manual production job is the approval step: the agent played it through the GitLab API as that account, and the receipt records the login. GitLab cannot tell an agent from a person on the same account, so this is a statement of the entry, not something the API proves; the README table "What ran, and who did it" lists the actions and who took them. "Flows do not chain" means one flow's output does not start another: Draft MR !2, opened by the Fix CI/CD Pipeline flow (GitLab lists the triggering account as author), did not start the Code Review flow, and on MR !3 neither the review note nor the Fix suggestion started a flow.
Built With
- claude-code
- gitlab
- gitlab-ci-cd
- gitlab-duo-agent-platform
- gitlab-package-registry
- gitlab-pages
- gitlab-releases
- gitlab-sast
- gitlab-secret-detection
- node.js
- typescript
- vite
- vitest
