CD Delivery Lines

Policy major-lines-run-separately makes a platform major a line: a branch that builds, tests and delivers on its own. main carries one major and main3 the previous one. This page describes how core's delivery pipeline (.github/workflows/main-cd.yml) serves every line from ONE file. The same commit applies to every line's branch unchanged, because the line is derived from the branch and never written into the file.

The declaration

.github/delivery-lines.json declares the lines and the repository's default branch:

field meaning
branch the branch that IS the line
pluginsRef the MeshWeaver.Plugins branch the line's portal host is built from (main → main, main3 → main3); vars.MW_PLUGINS_REF still overrides it per run
satelliteRef the satellite branch satellite-compat compiles against the line's set (main for every line until a satellite cuts its own)
label the line's floating image label: the newest PROMOTED set of the line (main, main3), declared in .github/release-channels.json with "line"
movesLatest whether the line moves the line-less latest labels. Exactly one line does

Deriving the line

The line job runs first. It calls .github/scripts/cd-line.py derive, which maps the event to a line:

event line copy of main-cd.yml that runs
workflow_run (Build-and-Test finished on a push) the triggering branch, which must be the default branch the default branch's
schedule the default branch the default branch's
push the pushed branch, which must be a NON-default line that branch's
workflow_dispatch the dispatched ref the one at that ref

Anything else is refused RED by name: an undeclared branch, a workflow_run for another branch (it would run the default line's file for that line's commit), a push to the default branch (it would race the tests), or a payload whose default branch disagrees with the declaration. There is no default line. The same job reads the line's platform major from PlatformVersion in the line's Directory.Build.props (cd-line.py platform-major).

Why the triggers are shaped this way

workflow_run and schedule always execute the default branch's copy of the file. Once the lines' trees diverge, that copy must never decide anything about another line's commit. So:

What reads the line

where what changed
gate resolves commits/<line> and commits?sha=<line>, the line's Plugins branch, and its satellite branch. The line outputs pass through as needs.gate.outputs.*
promote phase B moves <label> on the set's repositories. latest moves only for the movesLatest line. The never-backwards check ignores the run numbers of the OTHER lines' CD runs, because every line's sets share one run counter
control-promote memex-control:<label>
verify-images check-image-set.sh --line-label <label> --moves-latest <bool>. Both are required with --pointers
the satellite-compat baseline the set mw-plugin-test:<label> names
arm arm-promoted-set.py select --branch <line> --major <major> and armed-state --major <major>. Phase D's never-backwards check compares within the major, and latest moves only for the movesLatest line. The job's concurrency group is main-cd-arm-<major>, because the cursor it protects is per major
the promotion record carries line; its base is the newest armed set of the line's major
notify-platform-update the "still the newest armed set?" re-check reads within the major
delivery-verdict resolves the line's tip, and its hand-over dispatches --ref <line>

Cold start of a major. When a line moves to a new major (main to 4.x), no set of that major has been armed yet, while the fleet is armed on the previous one. armed-state --major 4 then answers a cursor of 0 for major 4 (cold_start: true, nothing to resume), and select arms the newest 4.x set whose platform verdict is green through the ordinary path, without --override. A registry with no armed set of ANY major is still refused: that is a broken read, never "nothing armed".

Who may move latest. In .github/release-channels.json every main-cd.yml binding of latest carries onlyTheLineThatMovesLatest: true, and release-channels.py check refuses one without it. tags_for therefore hands latest only to the line that moves it. Phase B is executed in test-cd-steps.py (step pointers_b) for both lines, with a mutation control: a main3 promote writes main3 on every repository and latest nowhere.

The workflow-level concurrency group is per line: the push lane is github.ref, and the reconcile lane is reconcile-<ref name> (main-cd-reconcile-main, main-cd-reconcile-main3). MainRunsAreNeverCancelledGuard evaluates the expression for both lines.

Still one fleet state

These places still announce or decide ONE fleet-wide state. They receive the line but do not key on it yet. The per-major channels (latest3, latest4) are a separate change:

The guard

cd-line.py check-workflow runs on every pull request. It fails when main-cd.yml spells a main that means "the line" in code: a quoted 'main', commits/main, ?sha=main, --ref main, refs/heads/main, an image tag :main, ref: main, a tag loop over main, or a branches: filter. Comments and message prose are not scanned. The one allowed spelling is on.workflow_run.branches: [main], and the guard holds it equal to the declared default branch. The guard also checks that main-cd's push trigger lists exactly the non-default lines, that Build-and-Test pushes on every line, and that reconcile-lines exists.

See also Release Channels.