# Claim-Time Guards (load-bearing)

Two guards that run when build-intake claims a ready item, both from failures observed in the fleet: an item built twice because it had already shipped, and a loop burning cycles re-attempting an item that was never going to succeed.

**One vendor-neutral contract, cited by every build-intake arm** (the `leaf-only-lifecycle` / `repo-scope-split` / `rejection-detection` / `claim-archaeology` precedent: one shared slug, never three divergent implementations).

## When they run

Inside build-intake step `3b`, after `rejection-detection` and `claim-archaeology`, still before the `$READY → $CLAIMED` transition. The `two-failed-attempts` valve runs first — it is cheaper and it is the only pass that can stop the claim outright.

## `already-implemented` → `verify-and-close`

A ready item may already be implemented, because agents ship without transitioning. Before implementing, probe for the item's **own** key:

```bash
git log --all --grep "<key>" --format='%H %aI %s' -n 10
gh pr list --repo <org>/<repo> --state all --search "<key>" --json number,state,mergedAt,url
```

On a hit, do not build it twice — switch `3c` to **verify-and-close**: verify what shipped against this item's acceptance criteria, post evidence naming the shipping PR/commit, then run the ordinary `3d` transition and rollup. If the shipped change only partially satisfies the criteria, implement the remaining gap instead of closing.

Distinct from `claim-archaeology` (finds a **different** ancestor issue) and from `DUPLICATE_ALREADY_FIXED` (a **different** canonical issue). Unreadable history degrades to "no hit"; the guard never blocks the claim.

## `two-failed-attempts` → blocked

Every non-success terminal outcome records a durable marker on the item — a visible line plus `<!-- [lisa-build-attempt] n=<N> outcome=<outcome> -->` (match on the marker, never the title). A cron holds no memory between cycles, so the item carries the count.

With **two or more** markers at claim time: do not claim. Move the item to the configured `blocked` role (resolved per `config-resolution`, never hardcoded), post an operator-readable comment naming both attempts and what a human must decide or supply, and **stop the loop** for this cycle. Recovery is deliberate — a human or a `lisa-repair-intake` cycle with the blocker provably cleared returns it to the queue.

Full contract (probe bounds, verify-and-close steps, partial-hit handling, marker mechanics): [reference/claim-time-guards.md](../reference/claim-time-guards.md).
