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.

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 SCI per job, the totals, 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. The entrant chose the project name.

Supervised shape: the approver is the AI agent (Claude Code) that drives the entrant account; no human clicks; flows do not chain. The entrant account is 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, 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-ci-cd
  • gitlab-duo-agent-platform
  • gitlab-package-registry
  • gitlab-pages
  • gitlab-releases
  • gitlab-sast
  • gitlab-secret-detection
  • node.js
  • typescript
  • vite
  • vitest
Share this project:

Updates

Submission history