# DD — Repository action pipelines (8E)
[Common design](dd.md) · [Tasks and completion status](adr.md).
"Run all in order" runs every action of a repository branch's `actions.yaml`, top
to bottom, as one Pipeline. 8E connects the two existing halves:
[2D](workload-creation.md#repository-actions) turns actions into saved Workload
revisions, and [8A–8C](pipeline-records.md) run an ordered list of Workload
revisions.
8E has two parts: the [internal start](#flow), and the [public start, read and stop
requests](#public-requests) a member or the browser uses.
## Flow
1. `StartRepositoryPipelineCommand` names the member, Project, repository, branch,
new Pipeline ID and optional parameter values per action.
2. `RepositoryPipelines.start` checks current Project access.
3. The `RepositoryActionWorkloads` port prepares every action. Workload Portfolio
reads `actions.yaml` once, keeps its declaration order and creates, reuses or
revises each action's Workload. Each action gets a stable command identity
derived from the request.
4. Each prepared action becomes a step: its exact Workload revision, environment
(`slurm-job` → `slurm`, Kubernetes actions → `k8s`), family and service kind.
Batch steps advance on success; services advance once healthy with an endpoint.
5. The existing internal Pipeline start checks the revisions through the real
Workload owner adaptor and saves the Pipeline, receipt and coordinator work.
```mermaid
flowchart TD
C["Run all in order
repository, branch, values"] --> R["RepositoryPipelines.start"]
R --> A["Check current Project access"]
A --> P["Workload Portfolio prepare_all
read actions.yaml once"]
P --> W[("Workload Portfolio PostgreSQL
one Workload per action")]
P --> S["Ordered steps with environments"]
S --> O["Pipeline start
checks exact revisions"]
O --> D[("Pipeline Coordination PostgreSQL
Pipeline, receipt and work")]
D --> K["8B coordinator launches each step"]
```
Workload preparation commits in Workload Portfolio before the Pipeline transaction,
so owner I/O stays outside Pipeline transactions. Every step is idempotent: a
retried command reuses the same Workload revisions and replays the Pipeline
receipt. A retry hands Workload Portfolio the revisions its first acceptance saved,
and each is kept while it still defines the same work, so another start that revised
the shared Workloads in between cannot turn the retry into other work. If
`actions.yaml` changes between a request and its retry, the retry returns
`idempotency_conflict` instead of starting different work.
## Public requests
The API exposes three operations, the path a single action's launch already takes:
an owner-enrolled route in `contracts/pipeline_coordination/http_api`, a typed request
to `metallo-requests`, and the use case in `application/member_pipelines.py`.
| Operation | Route | Identity |
| --- | --- | --- |
| Start | `POST /api/v1/{type}/{namespace}/{repo}/pipeline/{branch}` | `Idempotency-Key` names the new Pipeline; `If-Match: "0"` |
| Read | `GET /api/v1/pipelines/{id}` | None; current `pipelines.view` access |
| Stop | `DELETE /api/v1/pipelines/{id}` | `Idempotency-Key` names the stop; `If-Match` is the version last read |
- A start finds the repository through the directory a launch uses. A hidden or absent
repository is `not_found`, and nothing is prepared.
- The branch is one percent-encoded path segment, so `feature/train` is sent as
`feature%2Ftrain`; the generated web clients encode every path value.
- The start and stop receipts leave out the arrival time, as a Run's stop does, because
a retry repeats the command identity and not the time. A retry replays the saved
decision. A start retried after `actions.yaml` changed is `idempotency_conflict`.
- The reply commits with the decision in Pipeline Coordination's database and the
request host relays it. An owner outage saves no receipt and answers 503 for retry.
- A read returns each step in execution order with its action name, status, and the
Run it started. The Run state it carries is the one the Pipeline last observed, which
stops changing once the Pipeline finishes; the jobs and deployments routes give the
current one.
`metallo-requests` answers all of this owner's requests, including the internal query
for accepted Pipelines, with its own database and request-user credentials. The
coordinator keeps executing steps and keeps this context's Party and Project view,
which these requests read. Browser updates follow with 8F; until then a client reads
the Pipeline on demand.
## Ownership
- **Pipeline Coordination** owns the use case and its
`RepositoryActionWorkloads` port. Steps keep only Workload references.
- **Workload Portfolio** owns reading actions, their conversion and the Workloads.
- [`adaptors/pipeline_actions.py`](../../../adaptors/pipeline_actions.py) connects
the port to Workload Portfolio. It maps owner outages and corrupt state to
`ActionsUnavailable` for retry; business rejections such as an unknown action or
a missing required value pass through unchanged.
Every run of the same branch shares its action Workloads; each run is a new
Pipeline with its own step identities.
## Limits
- `actions.yaml` has no Kubernetes batch type. A branch can mix Slurm batch steps
and Kubernetes services; Slurm execution is part of 6B.
- Single-action execution is unchanged.
- Reading `actions.yaml` uses the test-only repository owner until the GitLab
repository connection is available.
## Verification
Completed on 21 October 2026:
- **13 use-case cases** cover ordered steps, environments, advancement
conditions, stable step identities, retries, conflicting retries, access,
rejections and invalid owner evidence.
- **4 cross-context cases** run Workload Portfolio's real preparation and
Pipeline Coordination's real start together, through the production adaptors and
in-memory storage: owner checks, retries, repeated runs and outages.
- **5 `prepare_all` cases** cover declaration order, one read, stable per-action
commands, per-action values, unknown actions and empty files.
- `pnpm verify` passed with **1,827 Metallo tests** and strict Mypy over **297
source files**. `pnpm test:integration` passed with **284 Metallo PostgreSQL
cases**. The two new cases use both context databases with restricted roles: a
branch starts one Pipeline over saved Workloads and replays its receipt, and four
concurrent runs of one branch share the same Workload revisions.
- `make metallo-identity-test TARGET=dev-NAME SSH=your-dev-box` passed **1,589
cases** on the dev box, including all 8E cases, and removed its namespace.
### Native Kubernetes verification
On 21 October 2026, `make metallo-pipeline-test TARGET=dev-NAME SSH=your-dev-box`
passed every [8D scenario](pipeline-verification.md#scenarios) and the new
repository-action scenario:
- A test-only repository owner read `actions.yaml` at the seeded branch's latest
commit from the installed GitLab over HTTPS Git with the scoped token.
- Its `app` (`k8s-deployment`) and `serve` (`k8s-inferenceservice`) actions became
Workload Portfolio Workloads, and one Pipeline started over them. Repeating the
start command replayed the same outcome.
- The hosted coordinator and execution worker read those Workloads from Workload
Portfolio through its restricted role. Each step had exactly one launch and Run.
- A real application Deployment and KServe InferenceService became healthy with
endpoints, and the Pipeline succeeded. The application and the model answered
private requests; both Attempt volumes held the branch's latest files.
- Cleanup removed the services, KServe resources, temporary GitLab access and
repositories, and the owned namespaces.
Not run: the real GitLab repository owner, real permissions and Slurm actions.