papa-hq reads 01 research -> 03 decision -> 02 design. The order is a scar, not a choice: 02-DESIGN existed from its initial commit, and when adr/ was finally promoted on 2026-07-13 it took the next free number rather than its place in the sequence. By then design was too settled to renumber. hal-hq was three commits old, so it is not. adr/ becomes 02-DECISIONS and 02-DESIGN becomes 03-DESIGN, and following the folder numbers now walks the process in the order it happens: research produces a decision, the decision authorises a design. 00-GENESIS becomes 00-META, matching papa's rename from the same restructure. Every path reference rewritten across documents, frontmatter, playbooks and skills. All links resolve; all 58 frontmatter blocks parse and their path fields still point at files that exist.
101 lines
4.9 KiB
Markdown
101 lines
4.9 KiB
Markdown
---
|
|
layer: as-is
|
|
status: implemented
|
|
code: [hal]
|
|
updated: 2026-08-23
|
|
decisions:
|
|
- 02-DECISIONS/0014-build-publish-and-deploy-are-three-silos.md
|
|
- 02-DECISIONS/0013-an-artifact-is-build-output.md
|
|
- 02-DECISIONS/0008-a-failed-step-fails-the-job.md
|
|
---
|
|
|
|
# Delivery — from a push to a running node
|
|
|
|
One trigger, three silos, and a fan-out point that is the most consequential boundary in the
|
|
mesh.
|
|
|
|
## The trigger
|
|
|
|
A push to the forge. The forge calls a webhook; the receiving module verifies its signature
|
|
and emits an event. The coordinator resolves which modules the pushed commits affect, orders
|
|
them into dependency levels, and creates a pipeline per level.
|
|
|
|
There is no second path. A manual trigger exists for a module the push detection missed, and
|
|
using it routinely is a sign the detection is wrong rather than a workflow.
|
|
|
|
Detection is the pipeline's most fragile input. It has failed for reasons that have nothing to
|
|
do with the change: a webhook truncating its commit list on a large merge, a forge address
|
|
whose port broke the module-path matching. The characteristic outcome is the bad one — **a
|
|
merge that created no pipeline, and nothing said so**.
|
|
|
|
## Three silos
|
|
|
|
Cardinality is the whole point, and the three differ
|
|
([ADR 0014](../../02-DECISIONS/0014-build-publish-and-deploy-are-three-silos.md)):
|
|
|
|
| Silo | Runs | Where | Does |
|
|
|---|---|---|---|
|
|
| **build** | once per module feature | the build node | compile and bundle into a self-contained output |
|
|
| **publish** | once per module feature | the build node | package that output and upload it; a package-registry feature publishes here |
|
|
| **deploy** | once per module feature **per node** | every assigned node | install, configure, start, verify |
|
|
|
|
Commands and events are addressed per feature, not per module.
|
|
|
|
Build hands over a **staged tree**, not a package. Packaging belongs to publish, so a failed
|
|
upload retries by re-packaging rather than by re-sending something stale, and build never needs
|
|
to know how each module composes its artifact. The handover is a tree in a known location,
|
|
because the build's own working directory is reference-counted and may be gone by the time a
|
|
later stage runs.
|
|
|
|
## The artifact
|
|
|
|
The artifact is **build output** — compiled and bundled with its dependency graph inlined —
|
|
never a filtered copy of source ([ADR 0013](../../02-DECISIONS/0013-an-artifact-is-build-output.md)).
|
|
A deploy is extract-and-run and touches no network.
|
|
|
|
The consequence is the whole cost of the decision: **anything not in the build output does not
|
|
ship.** Every file kind had to be brought into that rule separately, and each was discovered by
|
|
something silently not happening after deploy — migrations reading a source layout, provisioning
|
|
scripts reading a source layout, selection files never packaged at all.
|
|
|
|
## The fan-out, and the build node
|
|
|
|
After publish, work fans out to every assigned node. The build node is the only node that has
|
|
already passed through two silos when this happens, and that asymmetry has its own defect
|
|
class: anything advancing a node's stage must account for **both** pre-fan-out stages. Code
|
|
that knew only about the first parked the build node forever while every other node deployed
|
|
cleanly — and the recovery sweeper, which knew the same subset, reported nothing to recover.
|
|
|
|
A recovery mechanism that knows less than the thing it guards is worse than none, because it
|
|
reports success over a stall it cannot see.
|
|
|
|
## Levels
|
|
|
|
A level completes before the next begins, so a module builds against its dependencies as they
|
|
were just published. The shared library is at level zero, which means anything that breaks it
|
|
breaks the first module of every cascade.
|
|
|
|
## What green proves
|
|
|
|
**A green pipeline proves transport, not effect.** The stages report that a message was
|
|
dispatched and accepted, which is not the same as the thing being running, correct, or present.
|
|
|
|
This is the mesh's most consistent failure shape and it is not incidental to the design — it is
|
|
what the stage reporting currently measures. Documented instances include a service reported
|
|
started when the container command merely returned, an image pull failure that did not fail the
|
|
deploy, a package install that 404ed from every mirror while the job went green (see
|
|
[`04-ISSUES/001`](../../04-ISSUES/001-failed-package-install-reports-success/00-report.md)),
|
|
and a node left on old code after a failed artifact download with a version marker that had
|
|
already advanced.
|
|
|
|
A verify stage exists to close this gap. It has been built and, for a period, was never
|
|
scheduled, because the coordinator's stage list did not include it.
|
|
|
|
## What is not covered
|
|
|
|
The end-to-end harness for this pipeline has not built since the workspace was removed, and
|
|
nothing reports that nothing runs it
|
|
([`04-ISSUES/005`](../../04-ISSUES/005-pipeline-test-harness-unbuildable/00-report.md)). The
|
|
pipeline's coverage is currently assumed rather than checked — which is the same class of claim
|
|
this repository exists to make people stop making.
|