# DD — Pinned Run inputs (#767) [Common design](dd.md) · [Tasks and completion status](adr.md) · [Repositories through Source Control](../project-provisioning/source-control-repositories.md) A Run records exactly which code and action definition it uses. A branch can move after the Run is accepted without changing what the Run fetches, a retry fetches the same commit, and a Pipeline step uses the exact commit the step before it pushed. ## Inventory of Run and Pipeline inputs | Input | Owner | Version | Pinned by | | --- | --- | --- | --- | | The action's repository (`main_repo`) | Source Control | Git commit | this change: the commit `actions.yaml` was read at | | Repositories listed under `repos:` (datasets, code) | Source Control | Git commit; LFS objects are named in the commit | this change: the head of their default branch at acceptance | | The action definition (the Workload) | Workload Portfolio | Workload ID and revision number | already: the Run saves both | | An earlier Pipeline step's output | pushed to a repository, so Git | the commit that step pushed | this change: the next step is pinned when it starts | Every input in use today is a Git repository. There is no data, artefact or object input kind yet (`InputMaterialisation` is a placeholder with no owner); one would need an owner and a version or checksum contract before it is enabled, recorded as follow-up work with that owner. ## Decisions 1. **The Run is the record.** Each `source-repository` input of a planned Run carries `commit` next to `repositoryId` and `branch`. The Workload revision stays branch-based: its identity is Project, repository, branch and action, so a new commit never makes a new revision. 2. **A direct action launch is pinned at submission.** Workload Portfolio reads `actions.yaml` and reports the commit it read at (`ActionsRead`); that commit pins the main repository, so the definition and the code come from the same commit. The other repositories are pinned to their default-branch head through the `SourceVersions` port. The accepted launch carries the pins (`payload.sources`). 3. **Planning completes the pins.** Planning keeps the commits the launch carried (a pin for another branch or an unknown repository is `source_mismatch`) and resolves any repository left open. A Pipeline step's launch carries no pins, so the step is pinned when the coordinator starts it, which is after the previous step pushed. Nothing is resolved for later steps at Pipeline submission. 4. **The branch stays beside the commit.** The Site fetches the exact commit and creates the local branch at it, tracking the selected branch, so the action's pushes still go to the branch. A pinned commit that is no longer reachable from the branch is still fetched; GitLab allows fetching reachable commits by object name, and the test fixture allows the same (`uploadpack.allowAnySHA1InWant`). 5. **A retry reuses the pins.** A new Attempt works on the same Run inputs, so it fetches the same commit. When the Site answers that the pinned inputs cannot be prepared by any retry — `repository-commit-missing`, `repository-branch-missing`, `repository-too-large`, `unsupported-repository-file`, `unsupported-repository-configuration` — the supervisor records the Attempt as `rejected` with the Site's code and completes the Run with its completion event, instead of sending the command again until its deadline. A passing failure (`native-unavailable` and the like) is retried as before. 6. **Comparisons use the selection.** Wherever a Run's sources are checked against the Workload's `intent.repository` (batch and service starts, the Site protocol, the execution validation), the selection is compared without its pin. 7. **Source Control answers the pins.** `SourceVersions.commit_of(member, project, repository, branch)` is the port; production refuses to start until Source Control is connected (`external_owners.source_versions`), like every other owner. #759 answers it with `commit_of(member, project, repository, ref)`, which applies the member's current access. 8. **Pinning outlives the initiator only with Project authority.** Accepted Runs continue when their initiator leaves the Pharos Project (#755). A direct launch and its retries keep the pins saved at submission, so they need no new lookup. A Pipeline's later steps are pinned when they start, and both planning's execution check and `commit_of` use the initiator's own access, so a step that starts after the initiator left is refused today. The decision on what authority continues the work belongs to #766 (execution) and must reach this lookup too; until then the refusal is the safe behaviour. ## Flow 1. The member runs an action on a branch. Workload Portfolio reads `actions.yaml` at the branch, prepares the Workload revision and returns the repositories with the read commit. Execution Core pins the rest and accepts the launch with its pins. 2. The launch worker plans the Run: the saved Workload revision's inputs become the Run's inputs, each with its commit. 3. Supervision builds start and prepare messages from the saved Run; the Site's `sources` rows carry `commit`. The Site fetches the commit, verifies it and checks out the branch at it. 4. A retry works on the same inputs. A definite Site rejection ends the Run as `rejected`. 5. A Pipeline step is launched when the previous step finished; its planning pins it then, so it reads what the previous step pushed. ## Contracts - `contracts/http/site-execution.v1.yaml`: `Source`, `BatchSource` and `NamedSource` gain an optional `commit` (a full SHA-1 or SHA-256 object name). - Execution Core's launch request payload gains optional `sources`: the pins, one per repository, each with its commit. - The Run's `requiredInputs[kind=source-repository]` gain `commit`; an Attempt gains `rejection` (`code`, `message`) when it ends as `rejected`. - Workload Portfolio's `RepositoryActions.actions` returns `ActionsRead(document, commit)`; `PreparedWorkload` and Execution Core's `PreparedAction` carry `sources`. ## Follow-ups - Connect `SourceVersions` (over #759's `commit_of`) and `RepositoryActions` to Source Control once #724 and #739 share a base. - Pin a Pipeline's later steps under the Pharos Project's execution authority once #766 defines it, so a step that starts after its initiator left can continue. - Use the pins when issuing repository grants (#762) and in the Slurm Runtime's checkout, which is not in this branch. - A non-Git input kind needs an owner before it is enabled. ## Verification Completed on 24 October 2026: - **35 new unit cases**: pinning rules (kept, carried, resolved, mismatched, missing, outage, invalid owner answer), launch payload validation, launch acceptance keeping its pins, planning keeping pins after the branch moved and pinning open repositories at planning, planning rejections and outages, a retry fetching the same commit, a definite Site rejection completing the Run and a passing failure still retrying, the rejected Attempt's restore and limits, the pin travelling to the Site and abbreviated or upper-case commits refused at the boundary, Workload Portfolio's read commit on every prepared action, the Site model's commit checks, and the checkout helper's refusal of a non-object name. - **2 real Git/LFS helper cases**: a pinned commit behind the branch tip is checked out on the branch with its upstream and without the later file; a commit the remote does not have is `repository-commit-missing`. - `pnpm verify` passed: ruff, strict mypy, **2,469 Metallo tests**, the installed wheel imports and the regenerated Site contract models. - `pnpm test:integration` passed: **342 Metallo cases** on real PostgreSQL and RabbitMQ, including launch acceptance, planning, launch processing, cancellation and Pipeline execution with the pinning port in place. Not run to completion: the Kubernetes pipeline lane (the real Site checkout with a moved branch). With the lane adapted to the dev box's chart GitLab (the DX fix on `fix/pipeline-test-chart-gitlab`), three runs on the dev box (10.0.1.23) created the GitLab repositories and tokens, seeded the branch and started the Pipeline. One run showed that the execution worker needs read access to GitLab to pin a step, which the lane now gives it with a read-only token. No run reached the first native Job: the dev box's Kubernetes datastore stalls for minutes on its slow cloud storage (one write took 317 seconds), longer than the lane's `kubectl` time limit. Also not run: the Slurm Runtime, and Source Control connected for real.