---
title: Merge-tooling approval card
---

# Merge-tooling approval card

## What it is

**What it is:** an approval card in your Inbox (and the Approvals hub) that asks you to
approve a change to the **merge tooling**: the small helper programs the app runs while it
merges your finished branches.

Those programs are part of your project, not part of the app's own code. This card is the
one place a newer version of them gets approved.

## Why it exists

Merging is not a single step. To combine two versions of a file the way your project wants,
the app runs small programs from the project itself. They used to be taken from whatever was
on disk at that moment, so an edit to one of them became code the app executed on the very
next merge, with no person in between.

That is the same risk as installing a package from a changed lockfile: code that runs
unattended. So the app now runs that tooling **only from a version a person approved**, and
this card is how a newer version becomes approved.

### How that version is reached

The app keeps its own copy of the approved version, prepared from the exact commit you
approve, and checks that copy is intact before every merge that uses it. The helper programs
are then pointed at that copy **for that one merge**, which means nothing else on your
computer changes: your own merges, another project's merges, and anything else running at
the same time behave exactly as before.

If that copy cannot be prepared, or fails the check that it is still intact, the app does **not** quietly go back
to running whatever is on disk — the files those helpers would have handled are merged with
the app's own built-in merge instead, and if it keeps happening you get a card telling you.

### Turning it off

There is one switch that puts the old behaviour back, and it is on by default. It is kept
deliberately out of Settings: the thing being turned off is a safety guarantee, so the
control that removes it should not be one that anything but a person at the keyboard can
reach.

### What you will not see

Merges that do **not** change the tooling raise no card at all, which is the great majority
of them. The card appears only when the tooling genuinely moved since the version you last
approved.

## What the card shows

| Row | What it tells you |
|---|---|
| Project | Which project the tooling belongs to, by its name, never a folder path |
| Files changed | How many tooling files changed, and their names |
| Commits | How many commits changed them, and the newest few descriptions |

The card never prints a long commit id. The exact version you are approving is recorded for
you: if the tooling changes again after the card went up, that card is replaced by a fresh
one describing the new version, so what you see is always what you are approving.

Other merges that land while the card is waiting do not get in the way. As long as they left
the tooling itself untouched, your click is approved the first time and the card is gone for
good. A click is only turned down when the tooling really did change after the card went up,
and then a fresh card shows you the newer version.

## What each choice does

- **Approve**: merges switch to the newer version of the tooling. The next merge uses it.
- **Decline**: merges keep using the version you last approved, and that exact version is
  not put in front of you again. Nothing is lost: you can approve a later version whenever
  you want.

While a card is waiting, merges **keep using the last version you approved**. A waiting card
never runs anything new, and it never blocks your work.

## Who can answer it

Only a person. This card has no CLI or API route and no settings toggle, because the whole point
is that a person looks at this one.

It does now offer an **"Always allow"** option (owner decision, 2026-09-26), as do the auto-lander's
other sign-off cards: click the small arrow on the right edge of **Approve**, pick **Everywhere**,
and confirm. Later cards then approve themselves; **Settings → CLI Control → Always-allowed
actions** undoes it. That lever stops the repeating ask; it does not let anyone but you pull it —
the grant is written only from the approval card itself, so an agent can raise this card and can
never answer or grant it. The reason it was withheld before, and what changed, is recorded on the
kind in `src/shared/cli-approval-families.ts`.

## For agents

- The approval kind is `auto_lander.merge_tooling_update`; it is always-gated
  (`NON_TOGGLEABLE_CLI_ACTION_KINDS`) and deliberately routeless.
- The record of what is approved lives in `auto_lander_approved_merge_tooling`, one row per
  repo, written **only** by the approval handler.
- After each land the app compares the approved commit with the integration branch tip. If
  nothing under the tooling's own tree moved, the approved commit advances with no card; if
  anything moved, a card is raised.
- An approval is bound to the TOOLING the card showed, not to the branch tip. When the tip
  has moved since the card's commit, the click diffs the two with the same predicate and
  filters that raised the card (the closure plus `scripts/` and `.gitattributes`): unchanged
  tooling records the current tip; tooling that moved is refused (`repo-moved`) and a fresh
  card comes back; an unreadable comparison is refused (`request-failed`), never approved.
- The after-land review skips a repo that has no approval record and no
  `scripts/merge-tooling-closure.mjs` (`no-tooling`), before any git read or process start. A
  repo that already has a record is always reviewed.
- The merge step runs from the approved copy, not the checkout: the per-file drivers are
  re-pointed at it for that one invocation through a per-run config block (the clone-shared
  `.git/config` is never touched), and the batch pass and the resident host are launched
  from it. The copy borrows the main checkout's installed dependencies rather than
  installing its own.
- The switch is `AMC_DISABLE_LANDER_APPROVED_MERGE_TOOLING=1`, registered in the lander
  switch registry and read before any other work, so a disabled feature costs nothing. It
  carries no Settings field on purpose — see "Turning it off" above.
- What the merge step executes under `scripts/` and `.gitattributes` is hashed when an
  approved commit is first used, and re-checked against those bytes on a short bounded
  interval rather than on every call — this module is reached from every drivers-on walk and
  the lander runs five probes per branch per tick, so a full re-hash each time would slow the
  very path it protects. The copy's own clean-tree check still runs on every call, and a
  mismatch from either runs the built-in merge instead.

## Related

- [Session land status strip](session-land-status-strip.md): the strip showing where a branch
  is in its journey to master, including when it is held for a sign-off.
- [Auto-lander dashboard](auto-lander-dashboard.md): watching and pausing the lander itself.
- [MD merge driver](md-merge-driver.md): one of the merge tools this card gates.
