---
title: Let a league deploy pin the commit it ships
type: task
status: draft
description: >-
  yarn deploy does a bare git pull on both hosts, so a deploy ships whatever origin/master points at
  when the ssh lands -- clearance is granted per commit but spent per branch tip, and commits
  landing in the gap ride along unreviewed.
base_uri: user:task/league/pin-the-commit-a-deploy-ships.md
created_at: '2026-09-20T17:34:26.556Z'
entity_id: 1d5e1dec-25a4-4d06-ad29-6db2d3f51d4b
owner_identity_uri: █████████████████████████
public_read: true
relations:
  - relates [[█████████████████████-████████████████-███████-█████-██████-████████]]
tags:
  - user:tag/league-xo-football.md
  - user:tag/software-task.md
updated_at: '2026-09-20T17:34:54.232Z'
visibility_analyzed_at: '2026-11-06T00:17:58.368Z'
---

# Let a league deploy pin the commit it ships

## The property

`yarn deploy` runs `load:main` and `load:worker1`, and each is an `ssh … 'cd <checkout> && git pull && … pm2 reload'` (`package.json:46-47`). A bare `git pull` takes whatever `origin/master` points at when the ssh lands. Nothing in the deploy names a commit, so a deploy cannot ship a subset of master and cannot be asked to.

## Why it matters

Deploy clearance is granted per commit but SPENT per branch tip. Whoever deploys ships every commit that reached `origin/master` before their ssh ran, including commits that landed in the gap between the clearance and the launch, from lanes that were never part of the decision.

Observed 2026-09-20 and this is what prompted the task, not a hypothetical: the operator cleared a range of 6; by the time the deploy launched the range was 9. The three additions were two percentile-shading commits and one novig commit from a third lane. One of them, `1ec639006`, went to production with its CI run still QUEUED. It happened to be clean — its run finished later at 10978 passing / 4 failing, the same four pre-existing failures as master — but that was luck rather than control, and nothing in the deploy path could have told anyone either way at launch.

The deploying session also cannot hold: waiting for a clean moment makes the range LARGER, because master keeps moving. So the rational move under the current shape is always to go immediately, which is the opposite of what a gate is for.

## What a fix has to provide

- A way to name the commit: `git fetch && git checkout --detach <sha>` in place of `git pull`, with the sha passed in rather than discovered.
- A default that stays ergonomic for the ordinary case (ship the tip) so this does not become a step people route around.
- An answer for the two hosts drifting: `load:main` and `load:worker1` are separate ssh calls and can already land on different tips today. The same sha must go to both.
- An answer for `private/`, which is a submodule updated by `git submodule update --init private` — pinning the superproject without pinning the submodule pointer only half-solves it.
- A preflight that REPORTS the range about to ship, by sha and subject, before anything reloads. That alone would have surfaced the 6-to-9 drift at launch, and it is worth having even if pinning is never built.

## Not in scope

Whether CI should gate the deploy at all. Today it does not, deliberately — a red master is a property of the fleet rather than of the commits in front of you, and the guard that used to block on it also blocked the fix. That rule stays; this task is about knowing and choosing WHAT ships, not about adding a gate.
