---
title: "MGS3014: gate superseded"
description: Fires when an earlier ci gate is stopped because a later gate started on the same workspace root and wants its project locks, so the machine spends its time on the verdict that describes the tree as it stands.
tags: [MGS3014, ci, gate, supersede, locks, workspace-lock, worktrees, exit 75, concurrency]
---

# MGS3014: gate superseded

A ci gate stopped before it finished. A **later** gate started on the **same tree** and
wanted the project locks this one held, so this run yielded them and exited:

```text
[MGS3014] this gate was superseded by a later gate on the same tree (pid 41221, started
  2026-09-10T18:04:11Z, magus affected ci --no-default-charms); its verdict would have
  described a tree that has since changed; nothing here was wrong.
  yielded: project ., along with every other project this run had locked
  the later gate is running now, so there is nothing to rerun here
```

Nothing failed. Nothing in the workspace is wrong. The tree this run was judging moved
on while it was judging it, which makes the answer it was about to give an answer to a
question nobody is still asking.

The other side of it prints one line and keeps going:

```text
magus: superseded the earlier gate on project . (held by pid 40118 (magus affected ci .),
running 4m12s, in /Users/me/src/acme); its verdict would have described a tree that has
since changed.
```

## Why the later run wins

Before this, the newer gate queued on the [workspace lock](../../../concepts/concurrency.md)
behind the older one and started only once that finished. That is the right order for two
runs doing different work, and the wrong one for two gates on one checkout, where the
first gate's compute is spent producing a verdict about files that have already changed.
The queue cost the newer run the older one's whole duration, and bought a stale answer.

Ordering is decided by the **tree**, never by the caller. There is no priority flag and
no configuration key, because "which gate matters" is not a question the person typing
the command is better placed to answer than the workspace is.

## What qualifies

All four hold, or the contention is ordinary (refused immediately, exit 75 -
magus never waits on another magus invocation):

- **One root.** The same _resolved_ workspace root, not merely the same project name. A
  sibling worktree serves a project called `.` too, and its gate judges different files,
  so it is refused rather than superseded.
- **Both gates.** The whole `ci` target on both sides. A `run build` behind a `run test`
  is refused, a sharded gate is not a gate, and a dry run locks nothing.
- **Later start wins.** The holder began first. A holder with no start time on record is
  refused rather than superseded, because a supersede that cannot prove it is the newer
  run is a coin toss.
- **Two independent runs.** A lock held by one of this run's own ancestors is refused as
  [MGS3007](MGS3007.md) instead, which is the more specific diagnosis: superseding a
  parent would kill the run that is blocked waiting for this one to exit.

A run that has finished its targets and is in the settle tail counts as running: it holds
the locks, so it is superseded like any other gate.

Superseding is the one contention that is not immediately fail-fast: it asks the holder
to stop and waits, briefly (thirty seconds), for it to unwind before falling back to an
ordinary refusal. See [How the handover works](#how-the-handover-works).

## How the handover works

The later gate leaves a request beside the lock file naming itself: its pid, its command
and when it started. The holder reads it on its own heartbeat, cancels through the same
path the [stall watchdog](MGS3012.md) uses, and releases. Cancelling alone would not be
enough, because a run's locks come back only when it returns, so the abort releases them
explicitly and the successor takes the lock within seconds rather than after a timeout.

A marker rather than a signal, for what the abort has to **say**. A signal carries no
identity, so the stopped run could not name what replaced it, and nothing could tell this
from the `Ctrl-C` and the supervisor `SIGTERM` magus already handles as an interrupt.
`SIGTERM` is not deliverable on Windows at all, and under the server the holder and the
waiter can be threads of one process, where signaling the pid means signaling yourself.

A holder that never answers, because it is stopped, wedged in a syscall, or an older magus
that has never heard of a yield request, is not waited on further: the later gate retracts
the request after thirty seconds, says so, and refuses like any other contention (exit 75).

## Exit status

**75** (`EX_TEMPFAIL`), the same status the contended lock and [MGS3010](MGS3010.md) use.
Nothing here is broken and the same command is valid the moment the later gate is done, so
a harness can tell a yielded gate from a gate that found a bug. Exit 1 would collapse the
two.

## Resolution

Nothing. This is not a failure and there is no repair.

1. **Read what superseded it.** The message names the pid, the command and the start time
   of the run that took over. That run is judging the tree as it stands now, and its
   verdict is the one to wait for.

2. **Run it again if you actually wanted that verdict.** If the superseded run was
   deliberate, and the tree has stopped changing, `magus affected ci` again. Nothing is
   cached against a superseded run, so a rerun starts clean.

3. **Stop starting gates on top of each other.** The usual cause is a habit rather than a
   race: a gate per edit in one checkout. `magus affected ci` once, when the change is
   complete, is the cadence the check is built around.

4. **Use a worktree if two gates really are independent.** A sibling worktree is a
   different tree, so its gate is never superseded by this one and never supersedes it.

## See also

- [Concurrency](../../../concepts/concurrency.md): the workspace lock this decides the order of.
- [MGS3007](MGS3007.md): a lock held by this run's own ancestor, refused rather than superseded.
- [MGS3010](MGS3010.md): a gate deferred before it starts, rather than stopped after it did.
- [MGS3012](MGS3012.md): the stall watchdog, whose abort path this shares.
