diff --git a/02-DECISIONS/0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md b/02-DECISIONS/0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md index 3497e7e4..4e92373e 100644 --- a/02-DECISIONS/0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md +++ b/02-DECISIONS/0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md @@ -113,6 +113,18 @@ A second linking mechanism would be a second graph to keep acyclic and to show. records the verdict as the delivery's `checked` and asks only for what the controller cannot know: the group's composed check.** Chosen. +**When a published delivery's builds are registered.** + +1. *At the merge, as before, and the walk held back until its turn.* Rejected. A send carries a machine's + whole declaration, and a gated send carries every registered build that waits on its machine + ([ADR 0236](0236-a-build-is-judged-on-its-first-machine-and-put-back-by-something-other-than-itself-and-so-it-rolls-out-unattended.md) + §4a). A build registered before its turn would reach the machines with the next send of anything else + there. The group's order would be broken by a send nobody made for it, and a catalogue member registered + ahead of its controller is the version skew the order exists to prevent. +2. **When its walk starts.** Chosen. The merge opens the walk and asks nothing. Its first tier is asked when + the delivery's word comes, and a later tier is asked once the earlier one runs, as before. `published` is + the trunk commit accepted with its walk opened and waiting. Its builds are registered in `delivering`. + **Where a delivery is kept.** 1. *A database from the store's provider.* Rejected. It would make mesh-delivery depend on a provider whose @@ -145,6 +157,9 @@ walks the table. Each state carries its bound and what healer H2 may do once the | From | Event | To | Guard | |---|---|---|---| +| (none) | announced | proposed | a pull request's head the forge announced | +| (none) | appeared | held | a trunk commit whose walk waits, with no pull request known: merged before the seat was held, or pushed to the trunk | +| (none) | adopted | delivering | a walk already running: at the switch, or on the controller's own path | | proposed | checked | checked | the verdict names this commit | | checked | accepted | ready | the gate passed or warned; the repository's own check did not fail | | checked | refused | rejected | the gate failed or could not run | @@ -152,25 +167,33 @@ walks the table. Each state carries its bound and what healer H2 may do once the | ready | recheck | proposed | a person asked, or its group changed | | proposed, checked, ready, rejected | new head | superseded | a newer head of the same pull request | | proposed, checked, ready, rejected | closed | stopped | the pull request closed unmerged | -| ready | merged | published | the commit is on the trunk its modules follow; its first tier's builds are registered | -| proposed, checked, rejected | merged | held | merged without a passing check: waits for a person | -| published | go | delivering | the order of its group allows it; nothing holds it | -| published | hold | held | the release backlog, a bus step, a module whose policy records, or a group member before it that did not deliver | +| ready | merged | published | merged on the trunk its modules follow, and its walk opened; or nothing for a walk to move | +| proposed, checked, rejected | merged unchecked | held | merged without a passing check; a verdict known with it is taken first | +| published | done | delivered | nothing for a walk to move | +| published, ready | hold | held | merged, and its group's composed check did not pass for the heads that merged, or no walk was opened for it within ten minutes | +| published | go | delivering | its walk started: let go by this owner in its group's order, by a person, or on the controller's own path | +| held | go | delivering | its walk started on a word that was not this owner's: the controller's own path, or a person's `plans go` | | held | release | delivering | a person's decision, with why | | delivering | done | delivered | every machine of its deploy plan passed or was left as its policy says | -| delivering | gate failed | failed | a first machine's gate failed; what it carried was put back (ADR 0236 §3) | +| delivering | failed | failed | its walk failed: a first machine's gate (what it carried put back, ADR 0236 §3), a build, a machine | +| delivering | stop | stopped | its walk was stopped through this owner | | published, delivering | superseded | superseded | a newer delivery to the same trunk took over its walk (ADR 0218) | -| any state not final | stop | stopped | a person, with why | +| any state not final | stop | stopped | a person, with why, or its group stopped by a member before it | `delivered`, `failed`, `superseded` and `stopped` are final. A delivery never goes back to a state before -`published` once it has left one. Inside `delivering`, each machine has its own row in a second table: -`sent` → `judging` → `passed` | `failed`, `failed` → `rolled-back`. These are read from the walk's own -record (rule 7), never guessed. +`published` once it has left one. A row is either *observed*, taken whenever its guard holds, or an *act*, +taken only when a person or healer H2 asks (recheck, release, stop). The observed rows are taken in the +table's order until none holds. A delivery therefore stands where its facts put it, whatever order the facts +arrived in. Inside `delivering`, each machine has its own row in a second table: `sent` → `judging` → +`passed` | `failed`, `failed` → `rolled-back`. A step may pass through states between two readings of the +walk. A move the table does not reach, such as `passed` → `failed`, is refused. Steps are read from the +walk's own record (rule 7), never guessed. **3. The trunk rule ([ADR 0238](0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md) -decision 1) is the guard on `published`.** A commit off the trunk can reach `ready` and no further. Anything -built to check it is built in a scratch namespace of the artifact store that registration refuses. Only a -commit on the trunk is published, and only a published delivery is delivered. +decision 1) is the guard on `published`.** A commit off the trunk can reach `ready` and no further, and the +walk that publishes it exists only for a merge into a branch a module follows. Only a commit on the trunk is +published, and only a published delivery is delivered. A check builds nothing today. When one does, what it +builds goes under a scratch namespace of the artifact store that registration refuses. **4. A delivery group.** @@ -205,22 +228,33 @@ commit on the trunk is published, and only a published delivery is delivered. all are; `failed` or `stopped` when a member is. Nothing writes a group's state except this derivation. **5. Every transition is said four ways, and the owner of each says it.** mesh-delivery keeps the transition -in its state before anything else. It emits it as the event `mesh-delivery.transition`, with no secret and no -address. It asks the forge's holder to append it to the commit's note under `refs/notes/mesh-plan`: predicted -on the head, executed on the commit it landed as. It also asks the forge's holder to reflect it in the -delivery's view, one comment on the pull request kept current, which every status of the commit links to as -its `target_url`, `mesh/merge-gate` among them. The forge's holder writes notes as the mesh's own forge -account, appending: running twice adds nothing. +in its state before anything else, and does nothing else for it until it is kept. What it owes outside is +kept with the delivery and retried until done: + +- the event `mesh-delivery.transition` (a group's change of state: `mesh-delivery.group`), with no secret and + no address; +- a line on the commit's note under `refs/notes/mesh-plan`, asked of the forge's holder (`gitea_note_append`). + The line goes on the head while the delivery is off the trunk, on the commit it landed as once it is on it, + and at its end with what was executed. The forge's holder appends it in its own repository as the forge's + own account. A line the note already holds is not appended again; +- the delivery's view, one comment on the pull request kept current (`gitea_delivery_view`); +- the status `mesh/delivery` on the head, and `mesh/delivery-group` on each member's head + (`gitea_commit_status`). + +Every status links to the pull request where the view is, `mesh/merge-gate` among them. The forge's holder +also says when a pull request closes unmerged (`pull.closed`), and says the head and its statuses with a +merge. **6. The boundary.** mesh-delivery never sends to a machine, never writes a declaration, a bus object or a grant, and never registers a build. It asks: - **the controller**, through the seat's verbs: `delivery-plan` (the planner's one answer for a diffset), - `delivery-order`, `delivery-check` (compose a group's heads), `deliver` (let one published trunk commit's - walk start), `delivery-stop` (end a walk, with why), `delivery-walks` (the walks and their steps); + `delivery-order`, `delivery-check` (compose a group's heads, or check one head again), `deliver` (let one + published trunk commit's walk start), `delivery-stop` (end a walk, with why), `delivery-walks` (the walks + and their steps); - **the build seat**, through the controller's `delivery-check`. A group's check is a check ask like any other; -- **the forge's holder**, through events it consumes: the notes and the delivery's view. +- **the forge's holder**, through its tools: the notes, the view and the statuses. The controller keeps composition, registration, the trunk rule, sending, the gate, the rollback and the walk of one commit's tiers across machines, and says each step of a walk as the event `plan-moved`. @@ -250,8 +284,10 @@ be repaired: broken mesh-delivery is checked, merged and delivered without it. **9. Durable across restarts.** The state is mesh-delivery's key-value state. A restarted holder reads it back -before it takes an event. It then reconciles from the controller's `delivery-walks` and the forge's open pull -requests, so an event missed while it was down is found by comparison, never lost. A state held past its +before it takes an event. It then reads the controller's `delivery-walks`, and again every half minute, so a +walk's move it missed while it was down, or one a command made that says nothing, is found by comparison and +never lost. A pull request merged while it was down is made a delivery from the forge's word: its head and +the statuses the forge holds on it. A state held past its bound is listed by `stalled`. The controller's self-check reads that list and raises `delivery..stalled`. **Healer H2 works from the table**: it closes a stalled delivery only by a transition the table allows for that state (`superseded` when a newer delivery took over, `delivered` when @@ -274,14 +310,22 @@ waiting for mesh-delivery. Unassigning mesh-delivery returns the mesh to the con - **A merge no longer sends anything until mesh-delivery says so** while it is held, except the core and mesh-delivery itself. A down mesh-delivery means walks wait, said as a condition. A person can still deliver by hand. -- **The controller gains six verbs and one event and loses no primitive.** Its release planner's lifecycle, +- **The controller gains six verbs, one event (`plan-moved`, in the installer's first user list too) and a + `go` for `plans`, and loses no primitive.** Its release planner's lifecycle, supersession and hold decisions remain its code until mesh-delivery is held. After that they run only on the built-in path. Removing them is a later decision, once mesh-delivery has delivered for a while. -- **The forge's holder gains the notes and the view.** It needs git on its machine for notes, declared as a - package of its module. +- **The forge's holder gains the notes, the view and two statuses.** A note is written inside the forge's own + container, in the bare repository, as the forge's user. The forge's API reads notes and writes none, and a + push from anywhere else would need a credential to the forge that nothing else should hold. +- **A module named as its seat says its events as itself.** A consumed `.` names the seat's event + only when the seat emits it. Otherwise it names the event of the module of that name (`mesh-delivery.transition`). + The controller derives subscriptions by that rule. - **What is not built by this record**: porting the walk itself into mesh-delivery (option 1 of *where the walk - runs*); a web view of deliveries beyond the pull request's comment; requiring `mesh/delivery-group` on the - trunks, which is the operator's setting through the forge module's protection tool. + runs*); the controller's self-check reading `stalled` and raising `delivery..stalled`, with H2 + calling `close`, which is to-be 47's Phase B (the verbs exist and are tested, the probe does not); a web view + of deliveries beyond the pull request's comment; requiring `mesh/delivery-group` on the trunks, which is the + operator's setting through the forge module's protection tool; the scratch namespace, until a check builds + something. ## How it is checked @@ -293,8 +337,10 @@ waiting for mesh-delivery. Unassigning mesh-delivery returns the mesh to the con | the trunk rule | mesh-delivery's test that an off-trunk commit never reaches published; the controller's publish-rule test (ADR 0238) | | a group's membership, order, composed check and clean stop | mesh-delivery's group tests: by branch name, declared and inferred order, a cycle rejected, ready only when all are, a failed member stopping those after it and leaving those before it delivered | | a restart resumes | mesh-delivery's test that a holder restarted over the same state resumes each delivery and reconciles a walk it missed | -| the bootstrap rule | the controller's tests: a walk moving a core module or mesh-delivery never waits; another waits only while the seat has a holder on record; `plans go` and `push` deliver with mesh-delivery down | -| the inferred order | the controller's `delivery-order` test over the real dependency relation: toolchain first, controller before a manifest change, node-engine before controller | +| the bootstrap rule | the controller's tests: a walk moving a core module or mesh-delivery never waits; another waits only while the seat has a holder on record and asks no build while it waits, so a push carries nothing of it; `plans go` starts it with mesh-delivery down, with why, recorded as a hand act; unassigned, the mesh is back on the controller's own path | +| the inferred order | the controller's `delivery-order` test: built by, version skew, engine before controller, declared, by name the same on every reading, and a contradiction named as a cycle | +| a group is checked as one future state | the controller's gate test: a catalogue change needing a provision only another repository's change adds fails alone and passes composed with it; the builder's check test, on a container runtime, clones a group's other head and hands it to the gate, and runs no member's own script | +| a walk moved by a verb is said | the controller's test that `delivery go`, run as a command of its own, says `plan-moved` on the bus | | every transition is persisted, said, noted and reflected | mesh-delivery's test that each transition is put before it is emitted; the forge module's note test (append-only, idempotent) and view test (statuses carry its link) | | the seat is in the set and its holder is one | the controller's seat test; the catalogue's manifest test of mesh-delivery | | live | the next merge with mesh-delivery held waits for `deliver`, and `deliveries` shows it from head to delivered; `git log --notes=mesh-plan` shows the note on its merge commit | diff --git a/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md b/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md index 223ee2a4..1e80bf7a 100644 --- a/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md +++ b/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md @@ -25,12 +25,13 @@ commit's journey and in what order the steps are allowed. ## The parts ``` - forge's holder ──pull.updated / pull.merged──► mesh-delivery ◄──plan-moved / checked── controller - (statuses, the view, ▲ (deliveries, groups, (planner, gate, - the notes) │ the table, its state) registration, - ▲ │ │ sending, walk) - └──── transition ────────┴───────────────────────┘── verbs: delivery-plan, -order, -check, - deliver, delivery-stop, delivery-walks + forge's holder ──pull.updated / .merged / .closed──► mesh-delivery ◄──checked / plan-moved── controller + (statuses, the view, (deliveries, groups, (planner, gate, + the notes) the table, its state) registration, + ▲ │ │ sending, walk) + └── its tools: note, view, status ◄───────────────┘ └── its verbs: delivery-plan, -order, + -check, deliver, delivery-stop, + delivery-walks ``` - **The controller** stays the only writer of what it owns: machine declarations, bus objects, grants, @@ -68,13 +69,17 @@ allows. | checked | a verdict arrived | 1 minute | nothing: it is decided at once | | ready | it passed; it waits to be merged | none | — | | rejected | it failed its check | none | — | -| published | merged on the trunk, its first tier registered; it waits for its turn or its word | 30 minutes | `superseded` when a newer delivery to the same trunk took over its walk | +| published | merged on the trunk its modules follow, its walk opened and asking nothing; it waits for its turn or its word | 30 minutes | `superseded` when a newer delivery took over its walk; `go` when its walk started | | held | it waits for a person | 1 day | nothing: it is the operator's | -| delivering | its walk runs | 2 hours | `delivered` when the walk's record says done; `superseded` when a newer walk closed it | +| delivering | its walk runs; its builds are asked and registered tier by tier | 2 hours | `delivered`, `failed` or `superseded`, as the walk's record says | | delivered, failed, superseded, stopped | final | — | — | The transitions are those of ADR 0239 decision 2. A delivery that is merged while not `ready` goes to -`held`. Its check did not pass, and only a person decides that it goes on. +`held`. Its check did not pass, and only a person decides that it goes on. A delivery merged with no walk +opened for it within ten minutes is held too, saying so: nothing it moves follows that branch, or the merge +was not heard. Nothing waits silently. **Nothing of a published delivery is registered before its walk +starts**, because a send carries every registered build that waits on its machine (ADR 0236 §4a), and a +build registered before its turn would be carried by somebody else's send. **The machine steps.** While a delivery is `delivering`, each machine its walk reaches has a row with the module and the build: `sent` (the first machine's send, or the rest's), `judging` (the gate's healthy @@ -103,15 +108,18 @@ them. In this order, each by its owner: 1. **kept**: mesh-delivery puts the delivery in its state (`deliveries`, one key per delivery; `groups`, one - key per group). Nothing is said before it is kept; -2. **said**: the event `mesh-delivery.transition`, with the delivery's id, from, to, event, why and plan - summary, and no secret or address; -3. **noted**: the forge's holder, hearing it, appends a line to the commit's note: the head while the - delivery is off the trunk, and the commit it landed as once it is on it. It appends as the mesh's own forge - account, and a line already there is not appended again; -4. **reflected**: the forge's holder edits the delivery's view, one comment on the pull request with the - state, the plan, the machine steps and the group, and sets every status of the commit with the view as its - `target_url`. + key per group). Nothing is said before it is kept. What it owes outside is kept with it and retried until + done; +2. **said**: the event `mesh-delivery.transition` (and `mesh-delivery.group` when a group's derived state + changes), with the delivery's id, from, to, event, why and plan summary, and no secret or address; +3. **noted**: mesh-delivery asks the forge's holder (`gitea_note_append`) to append a line to the commit's + note: the head while the delivery is off the trunk, the commit it landed as once it is on it, and at its + end a line of what was executed there. The forge's holder writes it in its own repository as the forge's + own account, and a line already there is not appended again; +4. **reflected**: the delivery's view, one comment on the pull request with the state, the plan, the machine + steps and the group, kept current (`gitea_delivery_view`); the status `mesh/delivery` on the head, and + `mesh/delivery-group` on every member's head (`gitea_commit_status`). Every status links to the pull + request, `mesh/merge-gate` among them. ## The verbs @@ -131,8 +139,13 @@ In this order, each by its owner: | `close` | H2's only verb: the transition the table names for a stalled delivery, refused otherwise | **The controller's verbs for it** (on the mesh-controller seat): `delivery-plan`, `delivery-order`, -`delivery-check`, `deliver`, `delivery-stop`, `delivery-walks`. A person has `plans go --why`, which -does what `deliver` does under their name. +`delivery-check` (a group's heads composed, or one head checked again), `deliver`, `delivery-stop`, +`delivery-walks`. A person has `plans go --why`, which does what `deliver` does under their name and +is recorded as a hand act. The controller says every walk it keeps as `plan-moved`, from a verb's command +too. + +**The forge's holder's tools for it**: `gitea_note_append`, `gitea_delivery_view`, `gitea_commit_status`. It +also announces `pull.closed`, and a merge's head and the statuses the forge holds on it. ## The store @@ -165,8 +178,8 @@ up. | Phase | Delivers | Done when | |---|---|---| -| A — the owner | mesh-delivery with its table, state, verbs, events and adoption; the controller's seat, verbs, `plan-moved` and the wait; the forge holder's view and notes; the group's order and composed check | the tests of ADR 0239's *How it is checked* pass; a merge on the live mesh with mesh-delivery held is delivered by it and shown by `deliveries` | -| B — the conditions | the self-check reads `stalled`; `delivery..stalled`; H2 closing by the table | a delivery stalled on purpose is raised and closed by H2 through `close` | +| A — the owner | mesh-delivery with its table, state, verbs, events and adoption; the controller's seat, verbs, `plan-moved` and the wait; the forge holder's view, notes and statuses; the group's order and composed check | the tests of ADR 0239's *How it is checked* pass; a merge on the live mesh with mesh-delivery held is delivered by it and shown by `deliveries` | +| B — the conditions | the self-check reads `stalled`; `delivery..stalled`; H2 calling `close` (the verbs are built in A) | a delivery stalled on purpose is raised and closed by H2 through `close` | | C — the walk itself | the per-tier walk moved into mesh-delivery behind single-send verbs, the controller's planner kept only for the built-in path | decided by its own record once Phase A has delivered for a while | ## What is not decided here