#!/usr/bin/env bash
# SPDX-License-Identifier: Apache-2.0
#
# status — what is in and out of sync across an estate, READ-ONLY: `status
# InkRouter`, or just `status` from inside the estate, or `status --all`.
#
# Installed with the other three by `openRepoTools --install`.
#
# IT CHANGES NOTHING. It finds the estate exactly as `park` and `resume` do,
# and then only READS — git's refs, index and working tree — for the family
# holder or the assembly root, each member's working clone beside the holder,
# and every mounted leg. It fetches nothing unless told to (`--fetch`), so
# every ahead/behind answer is AS OF THE LAST FETCH — the one it just made,
# or the one before — and it says which on every run. `make validate` and
# `make pins` say whether an estate is CONSISTENT with itself (naming,
# manifests, lockstep pins); this is the view neither gives — whether it is
# CURRENT with its remotes, and whether anything here has moved and not
# travelled.
#
# THE FOURTH FILE, under Brett Heap's RULING of 2026-09-10 in this repository
# (its #9), verbatim: "lets go with a fourth file. start with the local status
# layer."
# A flag on `park` would have put a read-only report under a verb whose name
# means commit-and-push; a fourth file costs the shared estate resolver a
# third copy, which the suite holds byte-identical, and the install story one
# more line.
#
# THIS IS THE LOCAL LAYER PLUS `--fetch`: what this machine already knows,
# and — only when asked — what origin knows right now. `--fetch` is the
# second layer, under Brett Heap's RULING of 2026-09-11 (this repository's
# #10), verbatim: "next layer: --fetch". It runs `git fetch --prune origin`
# in every repository
# about to be read, so the ahead/behind answers are current and a remote
# branch deleted under a feature reads as gone; that fetch is THE ONE WRITE
# this command ever makes — remote-tracking refs (`refs/remotes/<remote>/*`:
# origin, and upstream where a fork has one),
# the objects behind them and `FETCH_HEAD`, and nothing else: no branch, no
# tag, no HEAD, no index, no working tree, because the refspec is pinned on
# the command line rather than trusted from config (see `fetch_remote`).
# What it cannot do is answer a prompt: `GIT_TERMINAL_PROMPT=0` makes an
# https credential prompt FAIL and be reported, but an ssh host-key or
# passphrase prompt is ssh's own and blocks exactly as `git fetch` would —
# so run `--fetch` where ssh is already non-interactive (an agent, a
# known_hosts entry). Without the flag it fetches nothing, as before.
#
# THE THIRD LAYER is the FORK against what it was forked from, under Brett
# Heap's RULING of 2026-09-11 (this repository's #11), verbatim: "next layer:
# fork against upstream".
# A remote named `upstream` is read from local refs — `origin/<branch>`
# against `upstream/<branch>`, as of the last fetch — and `--fetch` fetches
# it too, under the same pinned refspec. With no such remote, and only under
# `--fetch`, an origin on github.com is asked about with `gh api`, once: a
# fork is a finding that names the parent and the `git remote add upstream …`
# that makes the drift readable here. See "the fork against its upstream",
# below.
#
# THE FOURTH LAYER is the SHAPE PIN, under Brett Heap's RULING of 2026-09-11
# (this repository's #12), verbatim: "next layer: shape-pin drift". Every
# assembly root and family
# holder carries `contracts/shape-pin.yaml`: the openRepoShape commit its
# copied shape files were cut from, and a sha256 per copy. The copies are
# re-hashed here and one that differs is DRIFT — a copy edited in place, whose
# exit is upstream, never a re-digest; a `shape:` mirror in `project.yaml` /
# `family.yaml` that names another commit, or is missing, is out of step; and
# the pin is read against the source's default branch (`main` for
# openRepoShape, read from the clone's `origin/HEAD` or from GitHub) — from a
# clone of the source under the projects directory when there is one, found
# by its origin's slug the way `--repo` finds an estate, as of its last fetch;
# else under `--fetch` from GitHub, once per pin per run — so a pin that has
# fallen behind, or sits on a commit the branch never had, is named with
# `update-shape.py check` as the exit.
# See "the shape pin", below.
#
# THE FIFTH LAYER, the last of the five named when this file landed, is THE
# PARKED RECORD AGAINST DISK, under Brett Heap's RULING of 2026-09-11 (this
# repository's #13), verbatim: "next layer: parked record against disk".
# `park` records every
# open feature in the person's private workspace repository — the one
# `~/.agents/workspace.yaml` names — and `resume` rebuilds the worktrees from
# it; between the two, record and disk drift apart in exactly the ways
# `resume` later refuses, and this reads them beforehand: a recorded feature
# with no worktree here (parked, not resumed — `resume <Name>` is the exit,
# unless `--no-push` left the commit on the workstation that parked it), a
# worktree whose branch tip is not the parked commit (moved on, or BEHIND a
# newer record by more than the WIP commits `resume` itself un-committed, or
# diverged), a leg parked with `--no-push`, a worktree on a recorded branch
# that is NOT at the path both verbs compute for it, and a worktree on disk
# the record does not know (never parked). See "the parked record against
# disk", below.
# Nothing is left on the list.
#
# READ THE LINES, NOT THE EXIT CODE — the rule `park` and `resume` carry.
# Exit 0: every repository read is in sync and clean, as of the last fetch.
# Exit 1: at least one finding was printed. Exit 2: refused, and nothing was
# read. A finding is a fact to look at, not a fault — an unpushed commit on a
# feature branch is exactly what `park` exists to carry.

set -euo pipefail

SELF_NAME="status"

NAME=""
REPO_SLUG=""
REPO_ARG=""
#: `flag_value`'s three answers, declared with the other globals so that a
#: `die` reaching `$FLAG_HINT` before any arm has asked is an empty hint under
#: `set -u`, never "unbound variable" at exit 1 — the one exit these arms must
#: not produce (the independent review of this branch, 2026-09-11).
FLAG_VALUE=""
FLAG_SHIFT=2
FLAG_HINT=""
#: 1 when EVERY estate under the projects directory is to be read: `--all` /
#: `-a`, or the bare form standing outside every estate — which SWEEPS WITHOUT
#: ASKING, unlike `park`'s, because nothing here writes and a question before
#: a read is only friction. `ALL_ROWS` is what the sweep reads, `<name><TAB>
#: <dir>` per estate.
STATUS_ALL=0
ALL_ROWS=""
#: 1 with `--fetch`: `git fetch --prune origin` in every repository before it
#: is read — the one write this command makes. See `fetch_remote`.
FETCH=0

say() { printf '%s\n' "$*"; }
refuse() { printf '\nREFUSED: %s\n' "$1" >&2; }
die() { refuse "$1"; exit "${2:-2}"; }

usage() {
    cat <<'USAGE'
status [<Name>] [--repo <owner/name>] [--fetch]
status --all | -a [--fetch]
status --help

What is in and out of sync across ONE estate, read-only. For the family
holder or the assembly root, each member's working clone, and every mounted
leg: whether the tracking branch is ahead of or behind its origin; what is
dirty or stashed; which feature branches carry commits origin has not seen,
or point at a remote branch that is gone; whether a feature worktree is
dirty; and whether a leg is checked out away from its pin, or its pin has
fallen behind its origin. It FETCHES NOTHING unless you say `--fetch`, so
every answer is AS OF THE LAST FETCH — the one it just made, or the one
before — and the report says which. A FORK is read against what it was
forked from: with a remote named `upstream`, `origin/<branch>` against
`upstream/<branch>` from local refs, and `--fetch` fetches upstream too; with
none, and only under `--fetch`, an origin on github.com is asked about once
with `gh api`, and a fork is a finding naming the parent and the `git remote
add upstream …` to run. Not a fork: nothing to say. THE SHAPE PIN
(`contracts/shape-pin.yaml`) is read too: a copied shape file whose sha256 is
not the one recorded is drift, a `shape:` mirror naming another commit is out
of step, and the pinned commit is compared with the source's default branch
— from a clone of the source under your projects directory, found by its
origin like `--repo` finds an estate, or under `--fetch` from GitHub — so a
pin that has fallen behind is named with the exit, `update-shape.py check
--root <root>`. THE PARKED RECORD (`~/.agents/workspace.yaml` names the
workspace repository; `workspaces/<org>/<id>.yaml` in it) is read against the
worktrees on disk: a recorded feature with no worktree here (`resume <Name>`
brings it back — unless the record says `--no-push`, or its `pushed:` is
missing or neither true nor false, which `resume` refuses the same way, or it
names no parked commit for that leg, which `resume` refuses as moved-on, or it
gives a leg no role at all, or a role this checkout's shape has no place for —
one it does not mount, or one `resume` maps only onto the other shape, or onto
neither, as it maps `assembly` — or lists no leg for it at all, which `resume`
refuses the whole feature for: then only the workstation that parked it can
park it again — and where the role is really another shape's, only a checkout
of that shape, and where it is neither shape's, no checkout at all; and unless
origin has lost the branch, which `resume` refuses as gone from origin, the
WHOLE feature with it — after a fetch that WORKED in that repository, which is
the only way it is settled, a missing `origin/<branch>` is origin's answer and
the exits are the record's own entry where the feature landed or a push of the
branch from the workstation that parked it, and without one it is named beside
the `resume` that still brings back a branch this clone has merely not fetched;
and where the record's KEY ORDER leaves a `- branch:` or a `- role:` where the
loader reads neither — it shuts the `features:` list at the next key at project
indent and a feature's `legs:` list at the next key at feature indent — the
exits are putting that key back, which is yours and not this command's, and a
re-park only from the workstation that has the feature, since a park anywhere
else rewrites the block without those lines;
and where `git worktree list` still holds a registration that is no longer a
worktree, that is cleared with `worktree prune`, after a `worktree unlock` if
it is locked and with any leftover directory moved aside, before `resume` can
recreate anything; and where ANYTHING ELSE is at the path both verbs compute
for that leg — a directory, a file, a worktree of another branch — `resume`
refuses the WHOLE feature rather than overwrite a path it did not create, and
moving that aside first is yours: a `git worktree move` where git holds it as a
worktree and a plain `mv` where it does not, with the `worktree unlock` a
LOCKED registration wants in front of either and the `worktree prune` a dead
one wants beside the `mv`), a worktree whose tip is not the parked commit or a
record that names no parked commit to compare it with, a recorded branch this
repository has no `origin/<branch>` for, a leg parked with `--no-push` or whose
`pushed:` is missing or neither true nor false, a leg the record gives no role
or a role this shape has no place for, a feature the record lists no leg for, a
worktree on a recorded branch that is not at the path BOTH VERBS compute for
that leg — <worktree_root>/<branch>, with the leg's own mount under it in a
three-leg root, out of $SPECKIT_GIT_WORKTREE_ROOT, else this checkout's
git-config.yml, else the shape's own default, and not out of the record — which
is the only worktree `resume` reads as that feature's and the only one `park`
carries, so the exit is a `git worktree move` of yours — except where no move
works and the line says so: the leg's own checkout, a leg declared `path: "."`,
and a symlink anywhere in that path's own parents, each of which names the
component — anything at that same path that is not that leg's worktree, which
`resume` refuses the whole feature for before it reads origin, and a worktree
the record does not know (never parked).

<Name> is the estate's folder under your projects directory: a FAMILY folder
(<Name>/<Name>/family.yaml) or a standalone project root (<Name>/project.yaml).
With no <Name> the estate around the current directory is read. With no
estate around it either, EVERY estate under your projects directory is read,
without asking, because nothing here writes; `--all` (or `-a`) says the same
thing from anywhere. `--repo <owner/name>` names the estate by the origin of
a clone you ALREADY HAVE, in any spelling git accepts.

  --fetch             `git fetch --prune` of origin — and of upstream, where a
                      fork has one — in every repository first, so the answers
                      are current: THE ONE WRITE this command makes, to
                      remote-tracking refs, the objects behind them and
                      FETCH_HEAD; never a local branch, tag, HEAD, index or
                      working tree, because the refspec is pinned. EVERY
                      repository is fetched before the FIRST ROW is printed,
                      so the report appears after them; a fetch that fails is
                      a finding on its own row, read as of the last fetch that
                      worked. An ssh prompt is ssh's own, still blocks, and
                      blocks with no row in front of it: it belongs to a
                      fetch, not to the row you are looking at.
  --all, -a           read EVERY estate under your projects directory
  --project <Name>    the positional under a flag (also --name)
  --flag=<value>      every flag that takes a value takes it after an `=` too
                      (`--project=<Name>`): the one way to give a value that
                      begins with `-`, which the spaced spelling refuses

  $PROJECTS_DIR       your projects directory (default: ~/projects, then
                      ~/Projects - people spell it both ways)
  $SPECKIT_GIT_WORKTREE_ROOT
                      the extension's own override of `worktree_root`, read
                      here because `park` and `resume` read it first of all
                      three: it moves the path a parked feature's worktree
                      belongs at, and so what this reports about one

EXIT 0: every repository read is in sync and clean, as of the last fetch.
EXIT 1: at least one finding was printed. EXIT 2: refused; nothing was read.
WHAT STATUS NEVER DOES: pull, commit, push, reset, or move anything — and it
fetches only with `--fetch`. It takes no lock while it reads (`git
--no-optional-locks`); `--fetch` takes the ref locks a fetch takes.
USAGE
}

# --- BEGIN shared estate resolver (park, resume, status: byte for byte) -----
#
# THIS BLOCK IS DUPLICATED IN THE OTHER COMMANDS, DELIBERATELY, and
# `tests/test_park_resume_commands.py` asserts the three copies are IDENTICAL.
# Each of these is one file a person has on PATH: a shared `orp-estate.sh`
# would be a fifth file for `--install` to place and a broken command the
# first time somebody copied only one of them. The rules themselves are NOT
# new: `<dir>/<Name>/family.yaml` carrying `kind: family-manifest` and naming
# `<Name>` is `setup-project.py`'s `family_folder_here`, doubled-name layout
# and all (#76, RULING ruling 3). What "naming" accepts is widened by #236,
# at `is_family_folder` below: the display `name:` or the repository's name.

# `$PROJECTS_DIR` names ONE directory when it is set. Otherwise both
# spellings, in that order, because people really do use both - and DEDUPED by
# inode, because on a case-insensitive filesystem (a stock Mac) `~/projects`
# and `~/Projects` are the same directory, and listing every estate twice
# would read as two estates.
PROJECTS_DIRS=()
if [ -n "${PROJECTS_DIR:-}" ]; then
    PROJECTS_DIRS=("$PROJECTS_DIR")
else
    PROJECTS_DIRS=("$HOME/projects")
    if [ -d "$HOME/Projects" ]; then
        if [ -d "$HOME/projects" ] && [ "$HOME/Projects" -ef "$HOME/projects" ]; then
            : # one directory under two spellings; name it once
        else
            PROJECTS_DIRS+=("$HOME/Projects")
        fi
    fi
fi

# Where a NEW clone would go: the first spelling that already exists, else the
# first named.
#
# shellcheck disable=SC2329  # `park` and `status` create nothing and never
# call this. The block is byte-identical in all three commands BY DESIGN, so
# each carries a couple of functions only another one asks.
projects_dir_for_new() {
    local dir
    for dir in "${PROJECTS_DIRS[@]}"; do
        if [ -d "$dir" ]; then
            printf '%s\n' "$dir"
            return 0
        fi
    done
    printf '%s\n' "${PROJECTS_DIRS[0]}"
}

# ONE SCALAR'S TEXT, read the way openRepoShape v1.1.0's `parse_yaml` reads
# it (`scripts/repo_shape.py`: `_strip_comment`, `_quote_end`,
# `_quoted_scalar`), so a file both of them read gets one answer (#243). A
# value that OPENS WITH A QUOTE is the text up to the quote that closes it -
# inside `"..."` a `\` takes the character after it along, and inside `'...'`
# a doubled `''` is one quote, so neither closes it - and whatever follows
# the close, a `# comment` included, is not the value: a ` #` inside the
# quotes is text. Any other value is cut at the first `#` that opens it or
# follows a space or tab, which is a comment, and trimmed; `a#b` is the text
# `a#b`. The escapes are honoured to find the close and never unescaped, as
# this reader never unescaped them: no branch, path, folder or repository
# name it is asked for carries one. A quote nothing closes is not YAML at
# all, and the text is kept whole, as before. What is NULL is not said here:
# `null` and `"null"` are both the text `null`, and `yaml_scalar` says which
# of the two it was.
trim_unquote() {
    local value="$1" quote="" ch="" i=1
    value="${value%$'\r'}"
    value="${value#"${value%%[![:space:]]*}"}"
    case "$value" in
    '"'* | "'"*)
        quote="${value:0:1}"
        while [ "$i" -lt "${#value}" ]; do
            ch="${value:$i:1}"
            if [ "$quote" = '"' ] && [ "$ch" = "\\" ]; then
                i=$((i + 2))
            elif [ "$ch" != "$quote" ]; then
                i=$((i + 1))
            elif [ "$quote" = "'" ] && [ "${value:$((i + 1)):1}" = "'" ]; then
                i=$((i + 2))
            else
                printf '%s' "${value:1:$((i - 1))}"
                return 0
            fi
        done
        ;;
    *)
        value=" $value"
        value="${value%%[[:space:]]#*}"
        value="${value#"${value%%[![:space:]]*}"}"
        ;;
    esac
    printf '%s' "${value%"${value##*[![:space:]]}"}"
}

# ONE FOLDER NAME, and never a path. A `<Name>` off the command line and a
# `root:` out of somebody's workspace manifest both end up joined to the
# projects directory, so both are held to this: no empty string, no `/`, and
# no leading `.` — which is what stops `resume ../../elsewhere/X` from cloning
# outside the projects directory at all, rather than noticing afterwards.
is_safe_folder_name() {
    local name="$1"
    case "$name" in
    "" | .* | */*) return 1 ;;
    esac
    case "$name" in
    *[!A-Za-z0-9_.+@-]*) return 1 ;;
    esac
    return 0
}

# One indent-zero scalar out of a small YAML file, with parameter expansion
# only: no YAML library, no python per line, no forks per line. The line is
# found the way the Speckit extension's `workspace_read_scalar` finds one, at
# indent zero by its key; the VALUE is `trim_unquote`'s, above, which is
# openRepoShape v1.1.0's reading of it and differs from the extension's where
# a `#` sits inside quotes or with no space in front of it, and on a line that
# is not YAML at all (#243). The keys the three commands ask for are
# `family.yaml`'s and `project.yaml`'s, a record's `family:`, the two of
# `workspace.yaml`, and the shape pin's.
# `YAML_QUOTED` is 1 when the value was QUOTED: YAML's null is an unquoted
# `null` or `~`, and a quoted `"null"` is the text, which is a difference
# `trim_unquote`'s answer alone cannot carry (`named_for_repository`).
YAML_VALUE=""
YAML_QUOTED=0
yaml_scalar() {
    local file="$1" key="$2" line value
    YAML_VALUE=""
    YAML_QUOTED=0
    [ -f "$file" ] || return 1
    while IFS= read -r line || [ -n "$line" ]; do
        case "$line" in
        "$key":*) ;;
        *) continue ;;
        esac
        value="${line#"$key":}"
        case "${value#"${value%%[![:space:]]*}"}" in
        '"'* | "'"*) YAML_QUOTED=1 ;;
        esac
        YAML_VALUE="$(trim_unquote "$value")"
        return 0
    done <"$file"
    return 1
}

# A LINKED WORKTREE, not a checkout of its own: `.git` is a FILE holding a
# `gitdir:` line, and `--git-dir` and `--git-common-dir` disagree. A
# single-repository project's FEATURE worktree is a whole `project.yaml` root
# by construction, so without this the walk-up would answer `<worktree_root>/
# 001-my-feature` as an estate named after the branch — and `park` would run
# the write verb inside the very worktree the extension is about to park.
# GIT'S OWN ANSWER, and not the `.git`-is-a-file shortcut, because that shape
# is shared with a SUBMODULE and a leg must not be mistaken for a worktree:
# in a linked worktree `--git-dir` is `<common>/worktrees/<name>` and the two
# differ; in a submodule both are `<super>/.git/modules/<name>`; in an
# ordinary checkout both are `.git`. Verified against all three.
is_linked_worktree() {
    local dir="$1" gitdir common
    [ -e "$dir/.git" ] || return 1
    gitdir="$(git -C "$dir" rev-parse --git-dir 2>/dev/null || true)"
    common="$(git -C "$dir" rev-parse --git-common-dir 2>/dev/null || true)"
    [ -n "$gitdir" ] && [ -n "$common" ] && [ "$gitdir" != "$common" ]
}

# The PINNED copy inside a family holder: `<Family>/<Family>/members/<X>`, so
# the grandparent carries the `family.yaml`. It is detached, it is what
# `bootstrap` and `validate` read, and nobody's in-flight work is in it — a
# walk-up that answered it would run a WRITE verb on a detached HEAD. The
# working clone BESIDE the holder is the one a person works in, which is the
# same rule #80's holder dispatch follows.
is_pinned_member() {
    local dir="$1" parent grandparent
    parent="${dir%/*}"
    [ -n "$parent" ] && [ "$parent" != "$dir" ] || return 1
    grandparent="${parent%/*}"
    [ -n "$grandparent" ] && [ "$grandparent" != "$parent" ] || return 1
    yaml_scalar "$grandparent/family.yaml" kind || return 1
    [ "$YAML_VALUE" = "family-manifest" ]
}

# Is `<dir>` a FAMILY FOLDER? All three facts have to agree: the holder clone
# sits at `<dir>/<basename>`, its `family.yaml` says `kind: family-manifest`,
# and the folder is named for the family. A family has TWO names and either
# one is the folder's (#236): its display `name:`, which is the rule this
# block began with and what an estate laid out before openRepoShape v1.1.0
# has, or the LAST PART of its `repository:`, which is v1.1.0's rule - the
# holder, and the folder around it, are named for the REPOSITORY, `name:`
# being only a title (opensoft/openRepoShape#629), and the holder's `make
# siblings` stops before cloning when the folder is not that name
# (opensoft/openRepoShape#543). A family whose two names differ has no layout
# that works for both unless both are accepted. A directory called Northwind
# is a directory called Northwind; what makes it a family folder is read,
# never guessed from a name. The repository arm is `named_for_repository`,
# below, which `status` and `resume` also ask when one family is on disk in
# both layouts (#243).
is_family_folder() {
    local dir="$1" name manifest
    name="${dir##*/}"
    [ -n "$name" ] || return 1
    manifest="$dir/$name/family.yaml"
    yaml_scalar "$manifest" kind || return 1
    [ "$YAML_VALUE" = "family-manifest" ] || return 1
    if yaml_scalar "$manifest" name && [ "$YAML_VALUE" = "$name" ]; then
        return 0
    fi
    named_for_repository "$dir"
}

# Is `<dir>` named for its holder's REPOSITORY: the LAST PART of
# `<dir>/<basename>/family.yaml`'s `repository:`, as v1.1.0's `repo_basename`
# takes it (`rsplit("/", 1)[-1]`)? That is the folder v1.1.0 prescribes, and
# where `make siblings` moves a holder that is not in it. AN UNQUOTED `null`
# OR `~` IS NO REPOSITORY (#243) - nor `Null` or `NULL`, the spellings
# v1.1.0's `_scalar` reads as null too - so a folder called `null` is not
# named for one, and `is_family_folder` has only the display `name:` left to
# go on, which is v1.1.0's own fallback. A QUOTED `"null"` is the text.
named_for_repository() {
    local dir="$1" name
    name="${dir##*/}"
    [ -n "$name" ] || return 1
    yaml_scalar "$dir/$name/family.yaml" repository || return 1
    if [ "$YAML_QUOTED" -eq 0 ]; then
        case "$YAML_VALUE" in null | Null | NULL | '~') return 1 ;; esac
    fi
    [ "${YAML_VALUE##*/}" = "$name" ]
}

is_project_root() {
    local dir="$1"
    yaml_scalar "$dir/project.yaml" kind || return 1
    [ "$YAML_VALUE" = "project-manifest" ] || return 1
    is_pinned_member "$dir" && return 1
    is_linked_worktree "$dir" && return 1
    return 0
}

ESTATE_KIND=""
ESTATE_DIR=""
ESTATE_ROOT=""
ESTATE_NAME=""

# Is THIS directory an estate? A FAMILY FOLDER WINS over a standalone root of
# the same name: the holder is what drives the members, so a family answered as
# one of its own members would act on one project and report the estate done.
estate_here() {
    local dir="$1"
    if is_family_folder "$dir"; then
        ESTATE_KIND="family"
        ESTATE_DIR="$dir"
        ESTATE_NAME="${dir##*/}"
        ESTATE_ROOT="$dir/$ESTATE_NAME"
        return 0
    fi
    if is_project_root "$dir"; then
        ESTATE_KIND="standalone"
        ESTATE_DIR="$dir"
        ESTATE_NAME="${dir##*/}"
        ESTATE_ROOT="$dir"
        return 0
    fi
    return 1
}

# The NEAREST estate at or above `<dir>`: the holder beside you, or the project
# root you are standing in. NEAREST WINS, so standing inside a member of a
# family acts on that member - the estate the person is standing in. A pinned
# copy and a linked worktree are WALKED PAST rather than answered, so
# `<Family>/<Family>/members/<X>` reaches the family folder and a feature
# worktree reaches the root that owns it.
estate_walk_up() {
    local dir="$1"
    while [ -n "$dir" ] && [ "$dir" != "/" ]; do
        estate_here "$dir" && return 0
        dir="${dir%/*}"
    done
    return 1
}

estate_by_name() {
    local want="$1" dir
    is_safe_folder_name "$want" || return 1
    for dir in "${PROJECTS_DIRS[@]}"; do
        estate_here "$dir/$want" && return 0
    done
    return 1
}

# Every estate under the projects directory, one row each. What ruling 3 asks
# a refusal to print: a person who typed the command in the wrong folder needs
# the names, not an instruction to go and look for them.
list_estates() {
    local dir child
    for dir in "${PROJECTS_DIRS[@]}"; do
        [ -d "$dir" ] || continue
        for child in "$dir"/*; do
            [ -d "$child" ] || continue
            if is_family_folder "$child"; then
                printf '  family   %-20s %s\n' "${child##*/}" "$child"
            elif is_project_root "$child"; then
                printf '  project  %-20s %s\n' "${child##*/}" "$child"
            fi
        done
    done
}

estates_or_none() {
    local rows
    rows="$(list_estates)"
    if [ -n "$rows" ]; then
        printf '%s\n' "$rows"
    else
        printf '  (none)\n'
    fi
}

# Every FAMILY FOLDER under the projects directory whose holder's
# `family.yaml` says `id: <id>`, one per line, in `list_estates`' order, and
# nothing for an empty <id>. The id is what `park.sh` files a family's record
# under (`workspace_family_name` reads it), and it is the SAME in every layout
# of one family: v1.1.0's folder named for the repository and the
# display-name folder before it are two directories, two estates and ONE
# record (#243). So this is how `resume` finds a holder its record's folder
# no longer names, and how `status` sees one family laid out twice.
#
# ONE FOLDER IS LISTED ONCE, however many spellings reach it. `PROJECTS_DIRS`
# is deduped by inode, but the folders inside two distinct directories are
# not: a `~/Projects/Quill` that is a LINK to `~/projects/Quill` is one
# holder, and listing it twice made `resume` refuse "2 family folders" and
# `status` report one family "laid out 2 times" (the review of #251, finding
# 1). So a folder that is `-ef` one already listed is skipped, and the first
# spelling, in `PROJECTS_DIRS`' order, is the one named.
#
# shellcheck disable=SC2329  # `park` never asks: it files the record, and
# never looks a holder up by it. The block is byte-identical by design.
family_folders_with_id() {
    local want="$1" dir child prior dup
    local -a found=()
    [ -n "$want" ] || return 0
    for dir in "${PROJECTS_DIRS[@]}"; do
        [ -d "$dir" ] || continue
        for child in "$dir"/*; do
            [ -d "$child" ] || continue
            is_family_folder "$child" || continue
            yaml_scalar "$child/${child##*/}/family.yaml" id || continue
            [ "$YAML_VALUE" = "$want" ] || continue
            dup=0
            for prior in ${found[@]+"${found[@]}"}; do
                if [ "$child" -ef "$prior" ]; then dup=1; fi
            done
            [ "$dup" -eq 0 ] || continue
            found+=("$child")
            printf '%s\n' "$child"
        done
    done
}

lower() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; }

# `owner/name` — CASE PRESERVED, so a refusal prints back what a person can
# type — out of any spelling git accepts: owner/name, owner/name.git,
# https://host/owner/name(.git), git@host:owner/name.git,
# ssh://git@host/owner/name.git, and a token-bearing https url. Callers
# compare with `lower` on both sides, because GitHub owners and names are
# case-insensitive.
#
# A FILESYSTEM PATH IS REFUSED rather than parsed, for the reason the
# extension's own `workspace_org_from_url` refuses one: `/srv/remotes/Name.git`
# has a name but no owner, and inventing `remotes` as the owner would let
# `--repo remotes/Name` match a bare repository on disk. Such a clone answers
# `<Name>` and the walk-up; it does not answer `--repo`.
repo_slug_of() {
    local url="$1" rest owner name
    rest="$url"
    case "$rest" in
    /* | ./* | ../* | [A-Za-z]:[/\\]* | file://*) return 1 ;;
    esac
    rest="${rest%/}"
    rest="${rest%.git}"
    case "$rest" in
    *://*)
        rest="${rest#*://}"
        rest="${rest#*@}"
        rest="${rest#*/}"
        ;;
    *@*:*)
        rest="${rest#*@}"
        rest="${rest#*:}"
        ;;
    esac
    case "$rest" in
    */*) ;;
    *) return 1 ;;
    esac
    name="${rest##*/}"
    owner="${rest%/*}"
    owner="${owner##*/}"
    [ -n "$owner" ] && [ -n "$name" ] || return 1
    case "$owner$name" in
    *[!A-Za-z0-9_.-]*) return 1 ;;
    esac
    printf '%s/%s\n' "$owner" "$name"
}

# The clone under the projects directory whose `origin` IS that repository -
# ruling 1. Two levels deep, because the estates this standard places are
# `<projects>/<Project>` and `<projects>/<Family>/<Project>`.
clone_with_origin() {
    local want dir candidate origin found
    want="$(lower "$1")"
    for dir in "${PROJECTS_DIRS[@]}"; do
        [ -d "$dir" ] || continue
        for candidate in "$dir"/*/ "$dir"/*/*/; do
            candidate="${candidate%/}"
            [ -e "$candidate/.git" ] || continue
            origin="$(git -C "$candidate" remote get-url origin 2>/dev/null || true)"
            [ -n "$origin" ] || continue
            found="$(repo_slug_of "$origin" || true)"
            [ -n "$found" ] || continue
            if [ "$(lower "$found")" = "$want" ]; then
                printf '%s\n' "$candidate"
                return 0
            fi
        done
    done
    return 1
}

# The family's members, by the same `  - project: <Project>` row `family.py`
# writes and `siblings.py` reads. The working clone is `<family folder>/
# <Project>`, beside the holder.
family_members() {
    local manifest="$1" line
    [ -f "$manifest" ] || return 0
    while IFS= read -r line || [ -n "$line" ]; do
        case "$line" in
        "  - project: "*) printf '%s\n' "$(trim_unquote "${line#  - project: }")" ;;
        esac
    done <"$manifest"
}

# The branch a checkout belongs on: its OWN manifest's `tracking_branch:` —
# the field `scripts/bootstrap.py` already reads to place the legs — falling
# back to `main`. The workspace manifest records a `tracking_branch` too, but
# that is PROVENANCE from the machine that parked; the local declaration is
# what this machine's clone actually tracks, and the extension takes the same
# line about `worktree_root`.
tracking_branch_of() {
    local root="$1"
    if yaml_scalar "$root/family.yaml" tracking_branch && [ -n "$YAML_VALUE" ]; then
        printf '%s\n' "$YAML_VALUE"
        return 0
    fi
    if yaml_scalar "$root/project.yaml" tracking_branch && [ -n "$YAML_VALUE" ]; then
        printf '%s\n' "$YAML_VALUE"
        return 0
    fi
    printf 'main\n'
}

# The legs a root declares, by path, out of `project.yaml`'s `legs:` block —
# the same rows `scripts/bootstrap.py` reads, and in the order it reads them,
# `role:` before `path:`. The assembly leg is `path: "."` and is the root
# itself, so it is not one of these.
#
# shellcheck disable=SC2329  # only `resume` bootstraps a root, so only
# `resume` has to ask where its legs are; see the note above.
legs_of() {
    local root="$1" line role="" path=""
    [ -f "$root/project.yaml" ] || return 0
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line%$'\r'}"
        case "$line" in
        '  - role:'*)
            role="$(trim_unquote "${line#*:}")"
            path=""
            ;;
        '    path:'*)
            path="$(trim_unquote "${line#*:}")"
            [ -n "$role" ] || continue
            [ "$role" != "assembly" ] || continue
            [ "$path" != "." ] || continue
            [ -n "$path" ] && printf '%s\n' "$path"
            ;;
        [![:space:]]*) role=""; path="" ;;
        esac
    done <"$root/project.yaml"
}

# The branch a leg's OWN row declares for it: `leg_branch_of <root> <leg path>`
# prints the `branch:` of the `legs:` row whose `path:` is <leg path>, and
# NOTHING when that row declares none, or no row has the path. It is the
# value `scripts/bootstrap.py` puts first — `str(leg.get("branch") or branch)`
# — so a leg is placed on its own `branch:` and on the root's
# `tracking_branch:` only when its row has no `branch:` of its own; a caller
# falls back to `tracking_branch_of` on an empty answer, which is that `or`.
#
# Read the way `legs_of` reads the same rows — `role:` before `path:`, the
# assembly row and a row with no role skipped — except that `branch:` may sit
# on either side of `path:` in its row, so the answer comes the moment the
# row has shown both, and a row that does not open with `- role:` clears what
# the row before it left. A leg is found by its path SPELLED EITHER WAY: the
# caller may hold `.gitmodules`' `spec` (`status`) or the manifest's own text
# (`resume`), and `./spec` and `spec/` are one leg, so a leading `./` and a
# trailing `/` come off both sides before they are compared.
#
# WHAT IS NO BRANCH: an empty value, `null`, `Null`, `NULL` or `~`, once a
# trailing ` # comment` is cut off the value (a branch name holds no space, so
# ` #` cannot be inside one). The loader is not matched to the letter: it keeps
# a QUOTED `"null"` and `'~'` as branches and reads `false` and `0` as no
# branch, while this does the opposite for both. No real branch is named any
# of those, so nothing here chases the difference. (Since #243 `trim_unquote`
# cuts that comment as well; the cut here runs first and is kept.)
#
# WHO ASKS IT: `resume`'s leg guard (`root_legs_ok`), `status`'s
# `estate_repos` and `read_leg`, and `park`'s not-carried report
# (`report_repos`, #242, PR #249) - each to know the branch a leg belongs on.
#
# shellcheck disable=SC2329  # a `park` from before #249 never calls it;
# the disable can go once #249 has landed.
leg_branch_of() {
    local root="$1" want="$2" line role="" path="" branch=""
    [ -n "$want" ] || return 0
    [ -f "$root/project.yaml" ] || return 0
    want="${want#./}"
    want="${want%/}"
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line%$'\r'}"
        case "$line" in
        '  - role:'*)
            role="$(trim_unquote "${line#*:}")"
            path=""
            branch=""
            ;;
        '  - '*) role=""; path=""; branch="" ;;
        '    path:'*)
            path="$(trim_unquote "${line#*:}")"
            path="${path#./}"
            path="${path%/}"
            [ -n "$role" ] || path=""
            [ "$role" != "assembly" ] || path=""
            ;;
        '    branch:'*)
            branch="${line#*:}"
            branch="$(trim_unquote "${branch%%[[:space:]]#*}")"
            case "$branch" in null | Null | NULL | '~') branch="" ;; esac
            ;;
        [![:space:]]*) role=""; path=""; branch="" ;;
        esac
        if [ -n "$path" ] && [ "$path" = "$want" ] && [ -n "$branch" ]; then
            printf '%s\n' "$branch"
            return 0
        fi
    done <"$root/project.yaml"
}
# --- END shared estate resolver ---------------------------------------------

# --- reading one repository -------------------------------------------------

# `git` FOR READING, and every git call in this file goes through it.
# `--no-optional-locks` (git 2.15, 2017) keeps `status --porcelain` from
# refreshing the index under a lock, so a `status` running beside an editor
# or a `git commit` is never the thing that says `index.lock exists`. `-C` is
# first so the call reads exactly as `git -C <repo> …` does.
g() { git --no-optional-locks -C "$@"; }

short() { printf '%s' "${1:0:7}"; }

has_ref() { g "$1" rev-parse --verify -q "$2" >/dev/null 2>&1; }

# The legs a root DECLARES in `.gitmodules` — the same file `make bootstrap`
# mounts them from — each marked `mounted` or `unmounted` by whether `.git`
# is there. Not the shared `legs_of`, which reads what `project.yaml` says:
# this report is about what is on disk, and a leg that is declared and not
# mounted is itself one of its findings. `git config --get-regexp` prints
# `key value`, so the value is everything after the first space.
declared_legs() {
    local root="$1" record path
    [ -f "$root/.gitmodules" ] || return 0
    # `-z`, so the key and the value cannot be confused: a submodule NAME may
    # carry a space (`[submodule "sp ace"]`), and splitting the plain output
    # at its first space would hand back `ace.path spec` and miss the leg;
    # with `-z` each entry is `key<LF>value<NUL>`, and a key with NO value has
    # no LF at all and is not a path — not a phantom leg declared and never
    # mounted. `park`'s `submodule_paths` reads the same way.
    while IFS= read -r -d '' record; do
        case "$record" in
        *$'\n'*) path="${record#*$'\n'}" ;;
        *) continue ;;
        esac
        [ -n "$path" ] || continue
        if [ -e "$root/$path/.git" ]; then
            printf 'mounted\t%s\n' "$path"
        else
            printf 'unmounted\t%s\n' "$path"
        fi
    done < <(g "$root" config --file .gitmodules -z \
        --get-regexp '^submodule\..*\.path$' 2>/dev/null || true)
}

# `label<TAB>branch<TAB>path` per repository this estate reads — `park`'s
# not-carried report's enumeration, with two rows it does not need: `missing`,
# for a member with no working clone beside the holder, and `unmounted`, for a
# leg declared and not there. Both are things out of sync with the estate as
# recorded, which is what this command is for. A LEG'S BRANCH COLUMN IS THE
# SAME as `park`'s: the `branch:` the leg's own row declares, else the root's
# `tracking_branch:` (`leg_branch_of`; #235, #242).
#
# `members/<Project>` INSIDE A FAMILY HOLDER IS DELIBERATELY NOT HERE, for
# park's reason: that copy is pinned and DETACHED, for `bootstrap` and
# `validate`; nobody works in it. The working clone BESIDE the holder is
# where a person works, so it is what is read.
estate_repos() {
    local member branch state leg legbranch
    if [ "$ESTATE_KIND" = "family" ]; then
        printf 'holder\t%s\t%s\n' "$(tracking_branch_of "$ESTATE_ROOT")" \
            "$ESTATE_ROOT"
        while IFS= read -r member; do
            [ -n "$member" ] || continue
            if [ ! -e "$ESTATE_DIR/$member/.git" ]; then
                printf 'missing\t%s\t%s\n' \
                    "$(tracking_branch_of "$ESTATE_ROOT")" "$ESTATE_DIR/$member"
                continue
            fi
            branch="$(tracking_branch_of "$ESTATE_DIR/$member")"
            printf 'root\t%s\t%s\n' "$branch" "$ESTATE_DIR/$member"
            while IFS=$'\t' read -r state leg; do
                [ -n "$leg" ] || continue
                # A LEG'S branch is the `branch:` its own row declares, else
                # the ROOT's `tracking_branch:` — what `scripts/bootstrap.py`
                # places it on (#235).
                legbranch="$(leg_branch_of "$ESTATE_DIR/$member" "$leg")"
                printf '%s\t%s\t%s\n' "$state" "${legbranch:-$branch}" "$ESTATE_DIR/$member/$leg"
            done < <(declared_legs "$ESTATE_DIR/$member")
        done < <(family_members "$ESTATE_ROOT/family.yaml")
        return 0
    fi
    branch="$(tracking_branch_of "$ESTATE_ROOT")"
    printf 'root\t%s\t%s\n' "$branch" "$ESTATE_ROOT"
    while IFS=$'\t' read -r state leg; do
        [ -n "$leg" ] || continue
        legbranch="$(leg_branch_of "$ESTATE_ROOT" "$leg")"
        printf '%s\t%s\t%s\n' "$state" "${legbranch:-$branch}" "$ESTATE_ROOT/$leg"
    done < <(declared_legs "$ESTATE_ROOT")
}

# The findings for the repository being read, one line each, in the order
# they were found. Printed by `read_one_estate` under the repository's own
# row, or replaced by one `in sync` line when there are none. EXPANDED ONLY
# WHEN NON-EMPTY: `"${arr[@]}"` on an empty array is an unbound-variable
# error under `set -u` in every bash before 4.4, and a Mac's is 3.2.
FINDINGS=()
finding() { FINDINGS+=("$1"); }

# Where HEAD is: `branch<TAB><name>`, `detached<TAB><sha>`, or `unborn`.
# `symbolic-ref -q` prints the branch when HEAD is symbolic and exits 1,
# silently, when it is not — which is the whole test.
head_of() {
    local repo="$1" name sha
    name="$(g "$repo" symbolic-ref -q --short HEAD 2>/dev/null || true)"
    if [ -n "$name" ]; then
        printf 'branch\t%s\n' "$name"
        return 0
    fi
    sha="$(g "$repo" rev-parse -q --verify HEAD 2>/dev/null || true)"
    if [ -n "$sha" ]; then
        printf 'detached\t%s\n' "$sha"
    else
        printf 'unborn\t\n'
    fi
}

# A ROOT OR A HOLDER belongs on its tracking branch: `resume` refuses one on a
# feature branch, and a detached root is one `make bootstrap` never ran in.
read_head() {
    local repo="$1" branch="$2" kind ref
    IFS=$'\t' read -r kind ref < <(head_of "$repo")
    case "$kind" in
    branch)
        [ "$ref" = "$branch" ] ||
            finding "on branch $ref, not $branch (\`resume\` refuses a root on a feature branch)"
        ;;
    detached) finding "detached HEAD at $(short "$ref"); expected to be on $branch" ;;
    unborn) finding "no commits at all" ;;
    esac
}

# No `origin/<branch>` locally. Without a fetch that is "never fetched, or no
# remote named origin"; right after one that WORKED it means origin has no
# such branch — deleted or renamed there — and saying "never fetched" would
# contradict the `fetched origin:` line printed just above it.
no_origin_branch_finding() {
    if [ "$FETCHED_OK" -eq 1 ]; then
        finding "no origin/$1 after the fetch: origin has no branch $1 (deleted or renamed there)"
    else
        finding "no origin/$1 here: never fetched, or no remote named origin"
    fi
}

# The tracking branch against origin's copy of it, AS OF THE LAST FETCH: park
# fetches nothing and neither does this. BOTH refs must exist locally or there
# is no question to ask, and each absence is its own finding, because a clone
# with no `origin/<branch>` is one that was never fetched or has no remote
# named origin — either way, "in sync" would be a fiction. `rev-list
# --left-right --count a...b` prints `<only in a> <only in b>`, so with origin
# on the left the first number is BEHIND and the second AHEAD.
read_tracking() {
    local repo="$1" branch="$2" counts behind ahead
    if ! has_ref "$repo" "refs/heads/$branch"; then
        finding "no local branch $branch"
        return 0
    fi
    if ! has_ref "$repo" "refs/remotes/origin/$branch"; then
        no_origin_branch_finding "$branch"
        return 0
    fi
    counts="$(g "$repo" rev-list --left-right --count \
        "origin/$branch...$branch" 2>/dev/null || true)"
    [ -n "$counts" ] || return 0
    behind="${counts%%[[:space:]]*}"
    ahead="${counts##*[[:space:]]}"
    [ "$ahead" = 0 ] ||
        finding "ahead of origin/$branch by $ahead commit(s): unpushed"
    [ "$behind" = 0 ] ||
        finding "behind origin/$branch by $behind commit(s), as of the last fetch"
}

# Dirty and stashed. `--ignore-submodules=all` at a root, because a leg whose
# checkout has moved shows up in the ROOT's porcelain as ` M <leg>`, and the
# leg's own row below reports that once, in its own words.
read_worktree_state() {
    local repo="$1" out n
    out="$(g "$repo" status --porcelain --ignore-submodules=all 2>/dev/null || true)"
    if [ -n "$out" ]; then
        n="$(printf '%s\n' "$out" | wc -l | tr -d ' ')"
        finding "dirty: $n path(s)"
    fi
    n="$(g "$repo" stash list 2>/dev/null | wc -l | tr -d ' ' || true)"
    [ "$n" = 0 ] || finding "$n stash(es)"
}

# Every local branch OTHER than the tracking branch — the feature branches —
# by what `for-each-ref` knows without a fetch: no upstream at all means
# never pushed; `[gone]` means the remote branch was deleted under it, which
# is what a merged pull request leaves behind; `[ahead N]` is unpushed work.
# `%(upstream:track)` prints `[ahead 1]`, `[behind 2]`, `[ahead 1, behind 2]`,
# `[gone]` or nothing, so the count is what follows `ahead ` up to the next
# `]` or `,`. THE FIELDS ARE SEPARATED BY 0x1F, NOT TAB: a tab is IFS
# whitespace, so a run of tabs collapses and an EMPTY middle field shifts the
# ones after it left — `name<TAB><TAB>[gone]` would read as upstream=`[gone]`
# and the finding would vanish. 0x1F (`%1f` to for-each-ref, `$'\037'` to
# bash 3.2) is not whitespace, so every field stays where it was printed.
read_branches() {
    local repo="$1" tracking="$2" name upstream track n base
    while IFS=$'\037' read -r name upstream track; do
        [ -n "$name" ] || continue
        [ "$name" != "$tracking" ] || continue
        if [ -z "$upstream" ]; then
            base="origin/$tracking"
            has_ref "$repo" "refs/remotes/$base" || base="$tracking"
            n="$(g "$repo" rev-list --count "$base..$name" 2>/dev/null || printf '?')"
            if [ "$n" = 0 ]; then
                finding "branch $name: never pushed; no commit of its own yet (no upstream branch)"
            else
                finding "branch $name: $n commit(s) never pushed (no upstream branch)"
            fi
            continue
        fi
        case "$track" in
        *gone*)
            finding "branch $name: its remote branch $upstream is gone (merged, or deleted)"
            ;;
        *ahead*)
            n="${track#*ahead }"
            n="${n%%[],]*}"
            finding "branch $name: $n commit(s) unpushed to $upstream"
            ;;
        esac
    done < <(g "$repo" for-each-ref refs/heads \
        --format='%(refname:short)%1f%(upstream:short)%1f%(upstream:track)' \
        2>/dev/null || true)
}

# The LINKED worktrees of a root — the Speckit extension's feature worktrees
# — and whether each is dirty. Their branches are already read above; what a
# branch cannot say is that the worktree holds uncommitted work, which is
# exactly the work `park` would refuse to guess about. `worktree list
# --porcelain` prints blank-line-separated blocks, the main worktree first,
# so the first block is skipped rather than compared by path (a Mac's
# `/tmp` is `/private/tmp`, and two spellings of one path would compare
# unequal).
#
# WHAT IS ON DISK IS `worktree_is_usable`'s ANSWER, below, the same rule the
# record layer and its sweep now apply — so the three readers of this
# porcelain agree about what a worktree on disk IS: a directory with its own
# `.git`. A leftover directory without one is not a worktree to read, and
# reading it is worse than useless where the feature worktrees live UNDER the
# root, which is where the extension puts them: `git status` there finds no
# repository, walks UP, and reports the ROOT's dirty paths on the worktree's
# row, under a branch that has nothing to do with them.
read_worktrees() {
    local repo="$1" line path="" branch="" first=1 out n
    while IFS= read -r line; do
        case "$line" in
        "worktree "*) path="${line#worktree }"; branch="" ;;
        "branch "*) branch="${line#branch refs/heads/}" ;;
        "")
            if [ "$first" -eq 1 ]; then
                first=0
            elif [ -n "$path" ] && worktree_is_usable "$path"; then
                out="$(g "$path" status --porcelain --ignore-submodules=all 2>/dev/null || true)"
                if [ -n "$out" ]; then
                    n="$(printf '%s\n' "$out" | wc -l | tr -d ' ')"
                    finding "worktree ${path#"$ESTATE_DIR"/} on ${branch:-a detached HEAD}: dirty, $n path(s)"
                fi
            fi
            path=""; branch=""
            ;;
        esac
    done < <(g "$repo" worktree list --porcelain 2>/dev/null; printf '\n')
}

# A LEG is read against its PIN, not against its branch's tip: the root's
# HEAD records a gitlink for it, `make bootstrap` places it there, and
# `scripts/bump-leg.py` is the one way the pin moves. So: a checkout that is
# not the gitlink has moved without being pinned; a gitlink with commits on
# `origin/<branch>` after it is a pin that has fallen behind; and a gitlink
# that `origin/<branch>` does not contain is a pin on work the leg never
# pushed. A leg detached AT its pin is the normal state of a submodule and
# is not a finding. The counts are read in the LEG's repository, where the
# commits live; if the leg has never fetched, the answer is "unknown" rather
# than "fine", and says so.
read_leg() {
    local root="$1" rel="$2" branch="$3" leg gitlink head n kind ref
    leg="$root/$rel"
    gitlink="$(g "$root" rev-parse -q --verify "HEAD:$rel" 2>/dev/null || true)"
    head="$(g "$leg" rev-parse -q --verify HEAD 2>/dev/null || true)"
    # A LEG ON A FEATURE BRANCH is the state `resume` refuses a whole root
    # for — that root's own `make bootstrap` would walk the leg back onto
    # its branch — and the superproject looks clean in it, which is why the
    # leg's own row has to say so, pin or no pin. A leg DETACHED at its pin
    # is a submodule's normal state and is not a finding; one on its branch
    # is where `make bootstrap` leaves it. THAT BRANCH IS THE LEG'S: the
    # caller passes the `branch:` the leg's own row declares, else the root's
    # `tracking_branch` (#235), and a leg that declares one is named so,
    # because "not main" would be the wrong branch to send a person back to.
    IFS=$'\t' read -r kind ref < <(head_of "$leg")
    if [ "$kind" = "branch" ] && [ "$ref" != "$branch" ]; then
        if [ -n "$(leg_branch_of "$root" "$rel")" ]; then
            finding "on branch $ref, not $branch, the \`branch:\` this leg's own row declares in project.yaml (\`resume\` refuses a root whose leg sits on any other branch, because \`make bootstrap\` would move it)"
        else
            finding "on branch $ref, not $branch (\`resume\` refuses a root whose leg sits on a feature branch)"
        fi
    fi
    if [ -z "$gitlink" ]; then
        finding "not pinned: the root's HEAD records no gitlink at $rel"
    elif [ -n "$head" ] && [ "$gitlink" != "$head" ]; then
        finding "checked out at $(short "$head"), pinned at $(short "$gitlink"): moved but not pinned (scripts/bump-leg.py)"
    fi
    if [ -n "$gitlink" ]; then
        if has_ref "$leg" "refs/remotes/origin/$branch"; then
            n="$(g "$leg" rev-list --count "$gitlink..origin/$branch" 2>/dev/null || true)"
            if [ -z "$n" ]; then
                finding "pin $(short "$gitlink") is not in this leg's history: never fetched?"
            else
                [ "$n" = 0 ] ||
                    finding "pin $(short "$gitlink") is $n commit(s) behind origin/$branch, as of the last fetch"
                n="$(g "$leg" rev-list --count "origin/$branch..$gitlink" 2>/dev/null || printf 0)"
                [ "$n" = 0 ] ||
                    finding "pin $(short "$gitlink") is not on origin/$branch: $n commit(s) unpushed in the leg"
            fi
        else
            no_origin_branch_finding "$branch"
        fi
    fi
    read_worktree_state "$leg"
    read_branches "$leg" "$branch"
}

# --- `--fetch`: ask origin first ---------------------------------------------
#
# THE ONE WRITE THIS COMMAND MAKES, and only when asked: `git fetch --prune
# origin` in the repository about to be read. A fetch moves remote-tracking
# refs (`refs/remotes/origin/*`) and writes objects; it touches no branch, no
# HEAD, no index and no working tree, which is why it is the one write a
# read-only report can make and still be one. `--prune` is part of it because
# a remote branch deleted under a feature is exactly the `[gone]` this report
# looks for, and a fetch without prune would keep the stale ref that hides
# it. `--recurse-submodules=no`, because the legs are rows of their own and
# are fetched as such; git's on-demand default would fetch them again from
# the root. `GIT_TERMINAL_PROMPT=0` so a fetch that wants an https password
# FAILS and is reported, rather than stopping the whole report at a prompt
# nobody is watching; an ssh host-key or passphrase prompt is ssh's own and
# is NOT stopped by it — that blocks exactly as `git fetch` would. The
# outcome is one line under the repository's row — not a finding when it
# worked, a finding when it did not, in which case what follows is as of the
# last fetch that did work. `FETCHED_OK` tells the reads below whether a
# missing `origin/<branch>` means "never fetched" or "origin has no such
# branch".
#
# THE REFSPEC IS PINNED ON THE COMMAND LINE, and that is what bounds the
# write. `--prune` deletes whatever the refspec's destination side no longer
# has a source for — and a repository whose `remote.origin.fetch` was
# hand-edited, or made with `--mirror`, maps heads onto LOCAL branches, so a
# configured refspec plus `--prune` would delete a local branch and this
# report would then call the repository clean; `fetch.pruneTags` set
# globally would prune local tags the same way. Naming
# `+refs/heads/*:refs/remotes/origin/*` here means the only refs that can
# move or go are under `refs/remotes/origin/`, whatever the config says (the
# review of this layer deleted a local branch through the configured form to
# prove it). AND `--refmap`, WITH THE SAME REFSPEC: a refspec on the command
# line bounds what is fetched and pruned, but git still maps every ref it
# fetched through the CONFIGURED refspecs as well — "opportunistic
# remote-tracking updates" — so under a mirror-style config the pinned fetch
# alone would still move `refs/heads/main` in a detached leg, and refuse
# outright in a checked-out root. `--refmap` (git 2.1) replaces the
# configured mapping for this one fetch, so nothing outside
# `refs/remotes/<remote>/` is written by either route.
# ONE FUNCTION FOR BOTH REMOTES: `origin` always, `upstream` when a fork has
# one (the third layer, below). The remote's name is in every line it prints,
# and in the refspec, so `--prune` on the upstream fetch can only touch
# `refs/remotes/upstream/*`.
FETCHED_OK=0
UPSTREAM_FETCHED_OK=0
#: WHAT THIS RUN'S `origin` FETCH IN THIS REPOSITORY DID, in one word, because
#: `FETCHED_OK=0` IS FOUR STATES AND ONE OF THEM IS NOT A FETCH AT ALL
#: (Copilot's second round on #23). Two of the three returns above the fetch
#: are reached without running one — a repository git cannot read, and one
#: with no `origin` remote — and both are STORED by the pre-pass like any
#: other, so "did the pre-pass enter this repository" answered yes for a
#: repository nothing was fetched in and the record layer's caveat said "the
#: fetch this run made there failed" two lines under a row that said "no
#: remote named origin, so nothing could be fetched". `ok`, `failed`,
#: `no-remote`, `unreadable` — and `none` for a repository no fetch reached,
#: which is what the store's absence means and what a run without `--fetch`
#: is. The word is the ORIGIN remote's alone: `upstream` has its own flag and
#: its own line, and no reading branches on why its fetch did not happen.
FETCH_ORIGIN_STATE=none
fetch_remote() {
    local repo="$1" branch="$2" remote="$3" before after err what="fetch"
    [ "$remote" = origin ] || what="fetch $remote"
    if [ "$remote" = origin ]; then FETCHED_OK=0; else UPSTREAM_FETCHED_OK=0; fi
    if ! g "$repo" rev-parse --git-dir >/dev/null 2>&1; then
        [ "$remote" != origin ] || FETCH_ORIGIN_STATE=unreadable
        finding "fetch: not a readable git repository (a gitfile whose gitdir is gone?), so nothing could be fetched"
        return 0
    fi
    if ! g "$repo" remote get-url "$remote" >/dev/null 2>&1; then
        [ "$remote" != origin ] || FETCH_ORIGIN_STATE=no-remote
        finding "fetch: no remote named $remote, so nothing could be fetched"
        return 0
    fi
    before="$(g "$repo" rev-parse -q --verify "refs/remotes/$remote/$branch" 2>/dev/null || true)"
    if err="$(GIT_TERMINAL_PROMPT=0 git --no-optional-locks -C "$repo" fetch --quiet \
            --prune --recurse-submodules=no \
            --refmap="+refs/heads/*:refs/remotes/$remote/*" "$remote" \
            "+refs/heads/*:refs/remotes/$remote/*" 2>&1)"; then
        if [ "$remote" = origin ]; then FETCHED_OK=1; FETCH_ORIGIN_STATE=ok; else UPSTREAM_FETCHED_OK=1; fi
        after="$(g "$repo" rev-parse -q --verify "refs/remotes/$remote/$branch" 2>/dev/null || true)"
        if [ -n "$before" ] && [ -z "$after" ]; then
            fetch_say "    fetched $remote: $remote/$branch is GONE (was $(short "$before")): deleted or renamed on $remote"
        elif [ -n "$after" ] && [ "$before" != "$after" ]; then
            if [ -n "$before" ]; then
                fetch_say "    fetched $remote: $remote/$branch moved $(short "$before") -> $(short "$after")"
            else
                fetch_say "    fetched $remote: $remote/$branch is here now, at $(short "$after")"
            fi
        elif [ -z "$after" ]; then
            fetch_say "    fetched $remote: there is no $remote/$branch"
        else
            fetch_say "    fetched $remote: nothing new on $remote/$branch"
        fi
    else
        [ "$remote" != origin ] || FETCH_ORIGIN_STATE=failed
        err="${err%%$'\n'*}"
        finding "$what failed: ${err:-git said nothing}; what follows is as of the last fetch that worked"
    fi
}

#: WHAT THE PRE-PASS BELOW STORES, one entry per REPOSITORY of the estate in
#: row order: the path it is keyed by, the lines its fetch printed, the
#: findings its fetch made, the two flags the reads branch on, and WHAT THE
#: ORIGIN FETCH DID in `fetch_remote`'s one word. Parallel arrays and a linear
#: scan, because bash 3.2 has no associative array and an estate holds a
#: handful of repositories. `FETCH_SAY` is where `fetch_remote` puts a line
#: instead of printing it.
FETCH_KEYS=()
FETCH_SAID=()
FETCH_FOUND=()
FETCH_OK_AT=()
FETCH_UP_OK_AT=()
FETCH_STATE_AT=()
FETCH_SAY=""
fetch_say() { FETCH_SAY="$FETCH_SAY$1"$'\n'; }

# THE KEY IS THE PHYSICAL PATH, the idiom `locate_record` uses on the
# workspace checkouts and for the same reason: ONE DIRECTORY HAS MORE THAN ONE
# SPELLING. The rows come from `estate_repos`, which builds a leg's path out
# of `.gitmodules`; the record layer asks about the path `project.yaml` gives
# that role — `./spec` against `spec` is the same leg and two strings — and a
# symlinked projects directory, or a Mac's `/tmp` beside `/private/tmp`, does
# the same to a root. A key that is a spelling would have said "never fetched"
# about a repository fetched two rows below.
physical_path() {
    local resolved
    resolved="$(cd "$1" 2>/dev/null && pwd -P)" || resolved="$1"
    printf '%s\n' "$resolved"
}

# WHETHER A DESTINATION EXISTS AS FAR AS `git worktree add` IS CONCERNED,
# which is NOT `[ -e ]` (Copilot's fourth round on #27, verbatim: "`[ -e
# "$want" ]` follows symlinks, so a dangling symlink at the computed
# destination is treated as absent. `git worktree add` still rejects that
# existing path"). RUN on 2026-09-13: with a dangling symlink at the path,
# `[ -e ]` is false, `[ -L ]` is true, and `git worktree add` answers "fatal:
# '…' already exists". So every test that asks "is this path free for the add"
# asks BOTH — and `path_in_the_way`'s RR3 test does not, because RR3 IS
# `[ -e "$tree" ]` at the other end and a reading harder than the other end's
# would invent a refusal `resume` does not make. A dangling link is the ADD's
# refusal, and it is said in the add's words, with the arms that share them.
dest_exists() {
    [ -e "$1" ] || [ -L "$1" ]
}

# WHETHER ANY COMPONENT OF A PATH IS A SYMLINK, asked lexically because
# `physical_path` cannot answer it for a link whose target is not there yet
# (Copilot's fourth round on #27, verbatim: "This symlink guard misses a valid
# configuration where `worktree_root` is a symlink whose target directory has
# not been created yet: `physical_path` falls back to the lexical path when
# `cd` fails, so this condition treats the root as non-symlinked and advertises
# a move to the lexical destination"). RUN the same day: `cd` into such a link
# fails, so the fallback returns the lexical path and the two compare equal —
# and `mkdir -p` through it answers "cannot create directory '…': File exists",
# so the exit dies on its first operand as well. `[ -L ]` on each prefix is the
# question that has an answer whether or not the target is there.
# `SYMLINK_COMPONENT` IS THE FIRST PREFIX THAT IS ONE, because the line has to
# name the path the person must respell and the root is not always it (Copilot's
# eighth round on #27, verbatim: "`has_symlink_component` examines every parent
# of the computed destination, but this message says only that the worktree root
# is symlinked and tells the user to rewrite `worktree_root`. For a symlink at
# `$WORKTREE_ROOT/$branch` or a declared mount parent, the root is not the
# offending component, so the diagnostic points at the wrong setting"). Right,
# and it is drift the round before it made: widening the QUESTION to every
# parent left the ANSWER naming the root it no longer means.
SYMLINK_COMPONENT=""
has_symlink_component() {
    local rest="${1#/}" walk="" part
    SYMLINK_COMPONENT=""
    while [ -n "$rest" ]; do
        part="${rest%%/*}"
        if [ "$part" = "$rest" ]; then rest=""; else rest="${rest#*/}"; fi
        [ -n "$part" ] || continue
        walk="$walk/$part"
        if [ -L "$walk" ]; then
            SYMLINK_COMPONENT="$walk"
            return 0
        fi
    done
    return 1
}

# WHAT TO DO ABOUT A SYMLINKED COMPONENT, worded once because three arms name
# it: the component itself, what git will spell instead, and the setting that
# carries it where the component IS the worktree root. Every path this layer
# computes is built on `$RECORD_WORKTREE_ROOT`, so a link at or above it is a
# `worktree_root` to respell and a link BELOW it — the feature directory, a
# mount's parent — is a directory to replace with a real one.
symlink_exit() {
    local where="$1" resolved
    resolved="$(physical_path "$where")"
    if [ "${RECORD_WORKTREE_ROOT#"$where"}" != "$RECORD_WORKTREE_ROOT" ] ||
        [ "$where" = "$RECORD_WORKTREE_ROOT" ]; then
        printf '%s\n' "$where is a symlink, and it is the worktree root this checkout computes (or a directory above it) — spell \`worktree_root\` (or \`\$SPECKIT_GIT_WORKTREE_ROOT\`) as the directory git spells${resolved:+, which is $resolved}"
    else
        printf '%s\n' "$where is a symlink under the worktree root — git registers a worktree made there at the path it resolves to${resolved:+, $resolved}, which is not the string either verb compares, so that component has to BE the directory rather than point at one"
    fi
}

# THE ONE COMPUTED PATH NO COMMAND CAN TAKE AS AN OPERAND: the mount
# `collect_legs` spells for a leg the manifest declares `path: "."`, which is
# `<worktree_root>/<branch>/.`. The tip of this branch recorded why and did not
# act on it; Copilot's second round on #27 asks for the half that is this
# file's to give, verbatim: "For a declared `path: "."`, `want` ends in
# `/<branch>/.` here. This remediation emits `git worktree move <source>
# <want>`; after `mkdir -p` creates the branch directory, Git treats that `/.`
# destination as the existing directory and nests the worktree instead of
# putting it where `resume` can register it. The next `status` run reports the
# result as an obstruction, so this new remediation is not runnable/idempotent;
# handle this mount spelling or avoid advertising a move until `collect_legs`
# is fixed." BOTH HALVES OF THAT WERE RUN on 2026-09-12 and recorded at the tip
# of this branch: `mv '<…>/001-a-thing/.' <dest>` is refused by coreutils
# outright ("Device or resource busy", GNU coreutils 9.4) and `mkdir -p
# <…>/001-a-thing && git worktree move <tree> '<…>/001-a-thing/.'` exits 0 and
# leaves the worktree at `<…>/001-a-thing/001-a-thing`. SO THE MOVE IS NOT
# ADVERTISED: the rule at the top of `check_parked_leg` is that an arm may not
# name an exit that does not run, and this is the one path for which none does
# — the cure is a `collect_legs` that computes `$WORKTREE_ROOT/$branch` for
# such a leg, which is workBenches'.
dot_mount() {
    local path="$1"
    # AND EVERY SPELLING OF IT, not the bare `/.` alone (Copilot's sixth round
    # on #27, verbatim: "`dot_mount` only recognizes a destination whose raw
    # string ends in `/.`. The manifest parser preserves other non-empty root
    # spellings such as `path: \"./\"`, so `collect_legs` can produce
    # `<worktree_root>/<branch>/./`; this helper misses it"). Right:
    # `_shape_record_leg` keeps any non-empty `path:` verbatim, so `./`, `.//`
    # and `/.` all reach `collect_legs` and all name the feature directory
    # itself. Trailing slashes come off, then the `.` is what is left.
    while [ "$path" != "${path%/}" ] && [ "$path" != "/" ]; do
        path="${path%/}"
    done
    case "$path" in
    */.) return 0 ;;
    esac
    return 1
}

# THE DIRECTORY A PATH SITS IN, AND NEVER THE EMPTY STRING (Copilot's second
# round on #27, verbatim: "When the configured worktree root is `/`, `want` can
# be `/branch`, so `${want%/*}` expands to the empty string. The generated
# recovery command becomes `mkdir -p '' && git worktree move ...`, which fails
# before the move even though `resume` only needs the already-existing `/`
# parent"). It is right and it was run: `mkdir -p ''` answers "cannot create
# directory ''", so the exit dies on its first operand. `${p%/*}` is the empty
# string for exactly one shape of path — one whose only slash is the leading
# one — and the directory it means is `/`.
parent_dir() {
    local cut="${1%/*}"
    case "$1" in
    */*) printf '%s\n' "${cut:-/}" ;;
    *) printf '.\n' ;;
    esac
}

# A PATH AS A SHELL OPERAND, QUOTED ONLY WHERE A SHELL WOULD NEED IT (Copilot's
# first round on #27, verbatim: "The remediation command here interpolates
# `$repo` and `$want` directly into a shell snippet. Valid paths containing
# spaces split the command, and shell metacharacters make the copy-pasted `git
# worktree move` unsafe, so the prescribed cleanup is not reliably runnable.
# Please emit shell-quoted operands through a shared formatter"). Every command
# this file prints is one a PERSON is told to run — "which is yours to run" —
# and the rule at the top of `check_parked_leg` is that an exit which does not
# run is worse than no exit at all, so a projects directory called `~/My
# Projects` must not turn `git -C <root> worktree move <a> <b>` into four
# operands. Single quotes, with the one escape that ends and reopens them for a
# literal `'`, are what a POSIX shell takes verbatim.
#
# AND UNQUOTED WHERE NOTHING NEEDS IT, which is why the byte-for-byte pins in
# the suite do not move: a path of letters, digits and `/._@%+:,=-` is its own
# operand in every shell, and quoting it would add noise to every line this
# command prints on an ordinary estate. The set is deliberately narrow — `~`
# and `$` and `*` are not in it, because a shell would expand them.
sh_operand() {
    case "${1-}" in
    '') printf "''\n" ;;
    *[!A-Za-z0-9/._@%+:,=-]*)
        printf "'%s'\n" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"
        ;;
    *) printf '%s\n' "$1" ;;
    esac
}

# EVERY REPOSITORY IS FETCHED BEFORE ANY ROW IS READ, which is what `--fetch`
# has said since it landed — "in every repository first, so the answers are
# current" — and what it did not quite do: the fetches ran row by row, and THE
# RECORD LAYER IS READ ON THE ROOT'S ROW, before the legs, whose own origins
# hold the commits it compares the record against. A feature parked from
# another workstation into a LEG's origin was therefore reported as a parked
# commit that "is not here" by the very run that fetched it a moment later,
# and the finding vanished on the second run (the review of #18). Nothing
# else moves: each row still prints its own fetch's lines, carries its own
# fetch's findings and reads its own fetch's flags — `replay_fetch` puts them
# back where the row-by-row version printed and counted them — so the report
# reads exactly as it did.
fetch_estate_first() {
    local label branch path key seen
    FETCH_KEYS=()
    FETCH_SAID=()
    FETCH_FOUND=()
    FETCH_OK_AT=()
    FETCH_UP_OK_AT=()
    FETCH_STATE_AT=()
    while IFS=$'\t' read -r label branch path; do
        [ -n "${path:-}" ] || continue
        case "$label" in
        holder | root | mounted) ;;
        *) continue ;;
        esac
        key="$(physical_path "$path")"
        # ONE FETCH PER REPOSITORY, not per row. `family.yaml` is a file
        # people edit, and a member listed twice is one repository: fetching
        # it twice would make the second fetch report "nothing new" about
        # what the first had just moved, and the row that replays the one
        # entry says what was really done.
        for seen in ${FETCH_KEYS[@]+"${FETCH_KEYS[@]}"}; do
            [ "$seen" != "$key" ] || continue 2
        done
        FETCH_SAY=""
        FINDINGS=()
        FETCH_ORIGIN_STATE=none
        fetch_remote "$path" "$branch" origin
        if has_upstream "$path"; then
            fetch_remote "$path" "$branch" upstream
        fi
        FETCH_KEYS+=("$key")
        FETCH_SAID+=("$FETCH_SAY")
        FETCH_FOUND+=("$(printf '%s\n' ${FINDINGS[@]+"${FINDINGS[@]}"})")
        FETCH_OK_AT+=("$FETCHED_OK")
        FETCH_UP_OK_AT+=("$UPSTREAM_FETCHED_OK")
        FETCH_STATE_AT+=("$FETCH_ORIGIN_STATE")
    done <<<"$1"
    FINDINGS=()
}

# One row's stored fetch, put back: its lines where they were printed before,
# its findings in this row's list, its flags in the two the reads branch on.
# A row the pre-pass skipped — an unmounted leg, a member with no clone — and
# every row of a run without `--fetch` leaves the flags at 0, which is what
# "as of the last fetch" means there.
replay_fetch() {
    local path i=0 line
    path="$(physical_path "$1")"
    FETCHED_OK=0
    UPSTREAM_FETCHED_OK=0
    while [ "$i" -lt "${#FETCH_KEYS[@]}" ]; do
        if [ "${FETCH_KEYS[$i]}" = "$path" ]; then
            [ -z "${FETCH_SAID[$i]}" ] || printf '%s' "${FETCH_SAID[$i]}"
            if [ -n "${FETCH_FOUND[$i]}" ]; then
                while IFS= read -r line; do
                    [ -z "$line" ] || finding "$line"
                done <<<"${FETCH_FOUND[$i]}"
            fi
            FETCHED_OK="${FETCH_OK_AT[$i]}"
            UPSTREAM_FETCHED_OK="${FETCH_UP_OK_AT[$i]}"
            return 0
        fi
        i=$((i + 1))
    done
    return 0
}

# Whether the fetch THIS RUN made in `<repo>` worked — the store's answer for
# any repository of the estate, not only the row being read, which is what
# lets the record layer tell "never fetched here" from "origin has not got
# it" for a leg it reads from the root's row. False without `--fetch`, and
# false for a repository the pre-pass never saw.
fetched_ok_at() {
    local path i=0
    [ "$FETCH" -eq 1 ] || return 1
    path="$(physical_path "$1")"
    while [ "$i" -lt "${#FETCH_KEYS[@]}" ]; do
        if [ "${FETCH_KEYS[$i]}" = "$path" ]; then
            [ "${FETCH_OK_AT[$i]}" = 1 ] || return 1
            return 0
        fi
        i=$((i + 1))
    done
    return 1
}

# WHAT THIS RUN'S `origin` FETCH IN `<repo>` DID, in `fetch_remote`'s one
# word, for the same reason `fetched_ok_at` exists: the store holds one entry
# per repository of the estate as `estate_repos` lists them, and the record
# layer asks about the path `project.yaml` gives a role, which is usually one
# of them and need not be. So a caller that only wants "did a fetch happen
# here" tests for `ok` or `failed`.
#
# AND THE STORE HAS TWO WAYS OF HOLDING NOTHING, which is Copilot's third
# round on #23 (2026-09-12), verbatim: "when `--fetch` is active,
# `fetch_state_at` can still be `none` for a mapped role path that
# `project.yaml` names but `estate_repos` did not enumerate (the code
# explicitly allows that path not to be one of the fetch-store entries). This
# fallback then tells the user to run `status --fetch`, even though that flag
# has already run and repeated runs will not fetch a path absent from
# `.gitmodules`". IT IS REAL AND IT IS THE SAME FAULT THE ROUND BEFORE FIXED
# from its other side: a run that HAS fetched being told to fetch. `none` is
# the answer for a run made WITHOUT the flag — the one `status --fetch`
# settles — and `unlisted` for a run made WITH it that never reached this
# repository, which no rerun of the flag changes: the pre-pass fetches the
# repositories `estate_repos` lists, the root and the legs `.gitmodules`
# declares, and a `spec` or `code` leg `project.yaml` mounts somewhere else is
# not among them. THE FLAG'S SCOPE IS NOT WIDENED TO CLOSE IT: `status
# --fetch`'s one write is bounded to the repositories the estate declares,
# which is the rule, so the reading says what this run did instead.
#
# IT REPLACED A `fetch_tried_at` THAT ANSWERED THE PRESENCE OF THE ENTRY
# (Copilot's second round on #23), verbatim: "`fetch_tried_at` is true for
# every repository entered by the pre-pass, including a readable repository
# where `fetch_remote` returned early because no `origin` remote exists. In
# that case this message says "the fetch this run made there failed" even
# though no fetch was attempted, which makes the new diagnostic inaccurate".
# Run against e18ec53 on 2026-09-12, with `--fetch` and a `pushed: true`
# record whose branch has no `origin/<branch>` here: a root with its `origin`
# removed, a `spec` leg with its `origin` removed in the three-leg fixture,
# and a leg whose gitdir was deleted all drew "the fetch this run made there
# failed, so nothing has settled it" — in the last two, on the same report as
# "fetch: no remote named origin, so nothing could be fetched" and "fetch: not
# a readable git repository …, so nothing could be fetched". A line that says
# a fetch failed where none was MADE is as wrong as the one S1 fixed, which
# said a fetch was still to be made where one had just failed.
fetch_state_at() {
    local path i=0
    if [ "$FETCH" -ne 1 ]; then printf 'none\n'; return 0; fi
    path="$(physical_path "$1")"
    while [ "$i" -lt "${#FETCH_KEYS[@]}" ]; do
        if [ "${FETCH_KEYS[$i]}" = "$path" ]; then
            printf '%s\n' "${FETCH_STATE_AT[$i]}"
            return 0
        fi
        i=$((i + 1))
    done
    printf 'unlisted\n'
    return 0
}

# --- the fork against its upstream --------------------------------------------
#
# THE THIRD LAYER, under Brett Heap's RULING of 2026-09-11, verbatim: "next
# layer: fork against upstream". When origin is a FORK, the one question a
# clone cannot answer about itself is whether the fork has kept up with what
# it was forked from: a fork's `main` falls behind quietly, and a feature cut
# from it is a feature cut from stale ground. Two ways to know, in order:
#
# 1. A REMOTE NAMED `upstream` — the convention every fork guide teaches — is
#    read from LOCAL refs: `origin/<branch>` against `upstream/<branch>`, as
#    of the last fetch. With `--fetch` the upstream remote is fetched too,
#    under the pinned refspec, so the write stays under `refs/remotes/`.
# 2. WITH NO SUCH REMOTE, and only under `--fetch` — the one flag that
#    reaches the network — an origin on github.com is asked about with
#    `gh api repos/<owner>/<name>`. If GitHub says it is a fork, that is a
#    finding naming the parent and the `git remote add upstream …` that makes
#    the drift readable locally from then on, and GitHub's compare endpoint
#    gives the counts once. `gh` is READ ONLY here, and every call goes
#    through `gh_api_line`, which pins the host, clears `GH_DEBUG`, and
#    decides whether the run keeps asking: not on PATH, or not logged in
#    (gh exit 4), is true of every repository and is said once; a failure on
#    one repository — a 404 the token cannot see past — is said on its row
#    and the others are still asked, three in a row settling the run so an
#    offline machine does not pay a timeout per row. Without `--fetch`
#    nothing is asked, and a fork with no upstream remote reads exactly as it
#    did before this layer.
#
# An origin that is NOT a fork says nothing here: most estates are the
# organisation's own repositories, and "not a fork" is not a finding. The
# origin URL is read from config, not `remote get-url`, because a
# `url.<base>.insteadOf` rewrite must not hide whose repository it is.
GH_STATE=""
GH_FAILS=0
GH_LINE=""

# Is `gh` there to ask? Settled ONCE per run and shared by every question this
# command puts to GitHub (the fork check, the shape pin): absent is said once,
# under the first row that wanted it; `failed` is set by `gh_api_line` when a
# failure was true of every repository, after which nobody asks again.
gh_ready() {
    local what="$1"
    if [ -z "$GH_STATE" ]; then
        if command -v gh >/dev/null 2>&1; then
            GH_STATE=ok
        else
            GH_STATE=none
            say "    $what: no \`gh\` on PATH, so GitHub was not asked (said once per run)"
        fi
    fi
    [ "$GH_STATE" = ok ]
}

# THE ONE PLACE `gh` IS RUN: `gh api <endpoint> --jq <filter>`, a GET, and the
# LAST line of its output lands in `GH_LINE` (the jq payload is always last;
# a `GH_DEBUG` trace would come first, and is cleared anyway, because a stray
# stderr line read as the payload turns a fork into "clean"). `--hostname
# github.com`, because that is the host `is_github_origin` admitted and a
# `GH_HOST` in somebody's environment must not send the question elsewhere.
# ON FAILURE the run's state is decided here, and said on the row: gh exit 4
# is "not logged in", true of every repository, so it settles the run; a 404
# on one repository is true of that one only, so the others are still asked
# — three failures in a row settle it too, so a machine with no network does
# not pay a timeout per row. Returns 1 on any failure (the caller says what
# it could not read), 0 with `GH_LINE` set on success.
gh_api_line() {
    local what="$1" endpoint="$2" filter="$3" out rc=0
    GH_LINE=""
    gh_ready "$what" || return 1
    out="$(GH_PROMPT_DISABLED=1 GH_NO_UPDATE_NOTIFIER=1 GH_DEBUG='' gh api "$endpoint" \
        --hostname github.com --jq "$filter" 2>&1)" || rc=$?
    if [ "$rc" -ne 0 ]; then
        out="${out%%$'\n'*}"
        GH_FAILS=$((GH_FAILS + 1))
        if [ "$rc" -eq 4 ] || [ "$GH_FAILS" -ge 3 ]; then
            GH_STATE=failed
            say "    $what: \`gh api ${endpoint%%\?*}\` failed (${out:-no message}); not asked again this run"
        else
            say "    $what: \`gh api ${endpoint%%\?*}\` failed (${out:-no message}); the other repositories are still asked"
        fi
        return 1
    fi
    GH_FAILS=0
    GH_LINE="${out##*$'\n'}"
}

# `github.com` THE HOST, not a substring: `github.company.com` is somebody's
# GitHub Enterprise and `notgithub.com` is somebody else's server, and the
# question goes to github.com whatever the origin says — so a match on the
# substring would ask the wrong server about a repository that is not there,
# with the person's token. Every spelling git accepts: `https://host/…`,
# `ssh://user@host:port/…`, `user@host:owner/name`.
is_github_origin() {
    local url="$1" host
    case "$url" in
    *://*) host="${url#*://}"; host="${host%%/*}" ;;
    *:*) host="${url%%:*}" ;;
    *) return 1 ;;
    esac
    host="${host##*@}"
    host="${host%%:*}"
    [ "$(lower "$host")" = "github.com" ]
}

# A count from GitHub is digits or it is not read: `tostring` on a field the
# answer lacks is the word `null`, and "null commit(s) behind" is a lie.
is_count() {
    case "$1" in
    '' | *[!0-9]*) return 1 ;;
    esac
    return 0
}

has_upstream() { g "$1" config --get remote.upstream.url >/dev/null 2>&1; }

read_fork() {
    local repo="$1" branch="$2" counts behind ahead
    if ! has_upstream "$repo"; then
        [ "$FETCH" -eq 0 ] || ask_github_fork "$repo" "$branch"
        return 0
    fi
    if ! has_ref "$repo" "refs/remotes/upstream/$branch"; then
        if [ "$UPSTREAM_FETCHED_OK" -eq 1 ]; then
            finding "fork: no upstream/$branch after the fetch: upstream has no branch $branch"
        elif [ "$FETCH" -eq 1 ]; then
            finding "fork: no upstream/$branch here, and the fetch of upstream above did not bring one"
        else
            finding "fork: no upstream/$branch here: the upstream remote was never fetched (\`--fetch\` asks it)"
        fi
        return 0
    fi
    # No `origin/<branch>` is already a finding above; nothing to compare.
    has_ref "$repo" "refs/remotes/origin/$branch" || return 0
    counts="$(g "$repo" rev-list --left-right --count \
        "upstream/$branch...origin/$branch" 2>/dev/null || true)"
    [ -n "$counts" ] || return 0
    behind="${counts%%[[:space:]]*}"
    ahead="${counts##*[[:space:]]}"
    [ "$behind" = 0 ] ||
        finding "fork: origin/$branch is $behind commit(s) behind upstream/$branch, as of the last fetch"
    [ "$ahead" = 0 ] ||
        finding "fork: origin/$branch has $ahead commit(s) upstream/$branch does not"
}

# `gh api`, read-only, twice at most per repository: is origin a fork, and
# if so how far is its <branch> from the parent's default branch. The fields
# come back joined by 0x1F for the reason `read_branches` gives: an empty
# middle field must stay where it was printed.
ask_github_fork() {
    local repo="$1" branch="$2" url slug owner is_fork parent parent_default behind ahead
    url="$(g "$repo" config --get remote.origin.url 2>/dev/null || true)"
    is_github_origin "$url" || return 0
    slug="$(repo_slug_of "$url" || true)"
    [ -n "$slug" ] || return 0
    owner="${slug%%/*}"
    gh_api_line "fork check" "repos/$slug" \
        '[(.fork|tostring), (.parent.full_name // ""), (.parent.default_branch // "")] | join("\u001f")' ||
        return 0
    IFS=$'\037' read -r is_fork parent parent_default <<<"$GH_LINE"
    [ "$is_fork" = true ] || return 0
    if [ -z "$parent" ]; then
        finding "fork of a repository GitHub does not show (deleted, or private to you?), and no remote named upstream here"
        return 0
    fi
    finding "fork of $parent (per GitHub), and no remote named upstream here: \`git remote add upstream https://github.com/$parent.git\` reads the drift locally"
    if gh_api_line "fork check" \
            "repos/$parent/compare/$parent_default...$owner:$branch?per_page=1" \
            '[(.behind_by|tostring), (.ahead_by|tostring)] | join("\u001f")'; then
        IFS=$'\037' read -r behind ahead <<<"$GH_LINE"
        if ! is_count "$behind" || ! is_count "$ahead"; then
            finding "fork: GitHub's compare of origin/$branch with $parent's $parent_default was not two counts; not read"
            return 0
        fi
        [ "$behind" = 0 ] ||
            finding "fork: origin/$branch is $behind commit(s) behind $parent's $parent_default (per GitHub)"
        [ "$ahead" = 0 ] ||
            finding "fork: origin/$branch has $ahead commit(s) $parent's $parent_default does not (per GitHub)"
    else
        finding "fork: could not compare origin/$branch with $parent's $parent_default (the fork check line above says why)"
    fi
}

# --- the shape pin ------------------------------------------------------------
#
# THE FOURTH LAYER, under Brett Heap's RULING of 2026-09-11, verbatim: "next
# layer: shape-pin drift". An assembly root and a family holder each carry
# `contracts/shape-pin.yaml` — A COPY PIN, in the standard's own words: the
# scaffold COPIED a small set of files out of openRepoShape (the Makefile, the
# validators, the bootstrap, the workflow) so a project runs its own gate with
# no upstream mounted, and the pin records the commit they came from plus a
# sha256 per copy. Three things can be out of sync with it, and each is read:
#
# 1. A COPY EDITED IN PLACE. Every `files:` row is re-hashed with whatever
#    sha256 this machine has (GNU `sha256sum`, the `shasum` a Mac ships, or
#    `openssl`); a digest that differs is DRIFT, and the standard's exit is
#    named with it — carry the change upstream, never re-digest. The same
#    check the root's own `scripts/validate-pins.py` (or the holder's
#    `validate-family.py`) makes on every pull request, read here across the
#    estate without running the validator, which also asks lockstep questions
#    a report about the shape does not. A row whose file is missing is named.
# 2. THE MIRROR. `project.yaml` (or `family.yaml`) carries a `shape:` block
#    that mirrors the pin's commit; `update-shape.py` moves both together, so
#    two different commits there is a half-done update.
# 3. THE PIN AGAINST THE SOURCE'S DEFAULT BRANCH. From a clone of the source
#    under the projects directory when there is one — found by its origin's
#    slug, the way `--repo` finds an estate, never by folder name — read from
#    local refs, as of that clone's last fetch; else, under `--fetch`, from
#    GitHub's compare endpoint, once per pin per run. Behind is a finding
#    whose exit is `update-shape.py check --root <root>`; a pin the branch
#    does not contain is a finding too, because the standard squash-merges
#    and a branch commit is orphaned the moment its pull request lands. When
#    neither source answered, the note says currency was NOT read and why,
#    so nobody mistakes "no finding" for "current".
#
# A root with no `contracts/shape-pin.yaml` says nothing here: a root adopted
# before the pin existed, or a probe, is not out of sync with a pin it never
# had.

SHA_TOOL=""
# Which sha256 this machine has: GNU `sha256sum`, the `shasum` a Mac ships, or
# `openssl`. Chosen ONCE, here, and not inside `file_sha256`, whose caller runs
# it in a command substitution — a subshell, where nothing chosen would
# survive to the next file.
choose_sha_tool() {
    [ -z "$SHA_TOOL" ] || return 0
    if command -v sha256sum >/dev/null 2>&1; then SHA_TOOL=sha256sum
    elif command -v shasum >/dev/null 2>&1; then SHA_TOOL=shasum
    elif command -v openssl >/dev/null 2>&1; then SHA_TOOL=openssl
    else SHA_TOOL=none
    fi
}

# sha256 of one file's bytes, as `file_sha256` in the standard's
# `scripts/repo_shape.py` computes it. Empty when the tool could not read the
# file. GNU `sha256sum` and `shasum` mark a name holding a backslash or a
# newline by prefixing the whole line with `\`; that is about the name, not
# the digest, and is dropped.
file_sha256() {
    local file="$1" out=""
    case "$SHA_TOOL" in
    sha256sum) out="$(sha256sum -- "$file" 2>/dev/null || true)" ;;
    shasum) out="$(shasum -a 256 -- "$file" 2>/dev/null || true)" ;;
    openssl) out="$(openssl dgst -sha256 -r -- "$file" 2>/dev/null || true)" ;;
    esac
    out="${out%% *}"
    printf '%s' "${out#\\}"
}

# `path<TAB>sha256` per row of the pin's `files:` block — the rows
# `update-shape.py` writes as `  - path: <p>` / `    sha256: "<hex>"`, in
# that order; a hand-edited row the other way round is dropped, which is one
# reason the note counts rows. A trailing comment on a path is not part of
# it. A new top-level key ends the block.
shape_pin_rows() {
    local file="$1" line path="" digest in_files=0
    [ -f "$file" ] || return 0
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line%$'\r'}"
        case "$line" in
        '#'*) continue ;;
        'files:'*) in_files=1; path=""; continue ;;
        [![:space:]]*) in_files=0; continue ;;
        esac
        [ "$in_files" -eq 1 ] || continue
        case "$line" in
        '  - path:'*)
            path="$(trim_unquote "${line#*:}")"
            case "$path" in
            *' #'*) path="$(trim_unquote "${path%% #*}")" ;;
            esac
            ;;
        '    sha256:'*)
            digest="$(trim_unquote "${line#*:}")"
            [ -z "$path" ] || printf '%s\t%s\n' "$path" "$digest"
            path=""
            ;;
        esac
    done <"$file"
}

# One two-space-indented scalar out of one top-level section of a manifest —
# `shape:` / `  commit:` — with parameter expansion only, like `yaml_scalar`.
nested_scalar() {
    local file="$1" section="$2" key="$3" line in_section=0
    [ -f "$file" ] || return 0
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line%$'\r'}"
        case "$line" in
        '#'*) continue ;;
        "$section:"*) in_section=1; continue ;;
        [![:space:]]*) [ "$in_section" -eq 0 ] || return 0 ;;
        esac
        [ "$in_section" -eq 1 ] || continue
        case "$line" in
        "  $key:"*) printf '%s\n' "$(trim_unquote "${line#*:}")"; return 0 ;;
        esac
    done <"$file"
}

# THE SOURCE'S DEFAULT BRANCH is what a pin is measured against. openRepoShape's
# is `main`, but `source_repository:` is whatever a fork of the standard was
# named, so it is READ where it can be — the clone's `origin/HEAD`, or GitHub's
# `default_branch` — and remembered per source for the run; `main` when
# neither says. And the answer about ONE pin serves every root that carries
# it: a family's holder and members are cut from the same commit, and N
# identical questions would be N round-trips and N strikes against
# `gh_api_line`'s budget, which the fork check shares.
SHAPE_DEFAULT_SOURCE=""
SHAPE_DEFAULT_BRANCH=""
SHAPE_ASKED=""
SHAPE_ASKED_BEHIND=""
SHAPE_ASKED_AHEAD=""

# The pin's commit against the source's default branch, from a clone of the
# source under the projects directory — found by its origin's slug, the way
# `--repo` finds an estate, never by folder name. `merge-base --is-ancestor`
# says whether the branch contains it, `rev-list --count` says how far behind.
# Returns 0 when it could answer (finding or not), 1 when there is no such
# clone or it has never fetched its default branch, so the caller can try
# GitHub or say "not read".
shape_currency_from_clone() {
    local root="$1" commit="$2" source="$3" clone n default
    clone="$(clone_with_origin "$source" || true)"
    [ -n "$clone" ] || return 1
    default="$(g "$clone" symbolic-ref -q --short refs/remotes/origin/HEAD 2>/dev/null || true)"
    default="${default#origin/}"
    [ -n "$default" ] || default=main
    has_ref "$clone" "refs/remotes/origin/$default" || return 1
    SHAPE_DEFAULT_SOURCE="$source"
    SHAPE_DEFAULT_BRANCH="$default"
    if ! g "$clone" cat-file -e "$commit^{commit}" 2>/dev/null; then
        finding "shape pin $(short "$commit") is not in the clone of $source at $clone, as of its last fetch: fetch it, or the pin is on a commit that was squash-merged away"
        return 0
    fi
    if g "$clone" merge-base --is-ancestor "$commit" "origin/$default" 2>/dev/null; then
        n="$(g "$clone" rev-list --count "$commit..origin/$default" 2>/dev/null || true)"
        [ -z "$n" ] || [ "$n" = 0 ] ||
            finding "shape pin $(short "$commit") is $n commit(s) behind $source $default (per the clone at $clone, as of its last fetch): update-shape.py check --root $root"
    else
        finding "shape pin $(short "$commit") is not on $source $default (per the clone at $clone): a branch commit, which the standard's squash-merge orphans"
    fi
    return 0
}

# The same question put to GitHub, under `--fetch` only and only when no clone
# answered: the source's default branch once per source, then `compare/<pin>
# ...<default>` once per pin, with the pin as base so `ahead_by` is what the
# branch has since the pin and `behind_by` is what the pin has that the
# branch never did. Returns 1 when GitHub did not answer (`gh_api_line` has
# said why, or an earlier failure settled the run).
ask_github_shape_pin() {
    local root="$1" commit="$2" source="$3" behind ahead default
    if [ "$SHAPE_DEFAULT_SOURCE" = "$source" ] && [ -n "$SHAPE_DEFAULT_BRANCH" ]; then
        default="$SHAPE_DEFAULT_BRANCH"
    else
        gh_api_line "shape check" "repos/$source" '.default_branch // "main"' || return 1
        default="$GH_LINE"
        [ -n "$default" ] || default=main
        SHAPE_DEFAULT_SOURCE="$source"
        SHAPE_DEFAULT_BRANCH="$default"
    fi
    if [ "$SHAPE_ASKED" = "$source $commit" ]; then
        behind="$SHAPE_ASKED_BEHIND"
        ahead="$SHAPE_ASKED_AHEAD"
    else
        gh_api_line "shape check" "repos/$source/compare/$commit...$default?per_page=1" \
            '[(.behind_by|tostring), (.ahead_by|tostring)] | join("\u001f")' || return 1
        IFS=$'\037' read -r behind ahead <<<"$GH_LINE"
        SHAPE_ASKED="$source $commit"
        SHAPE_ASKED_BEHIND="$behind"
        SHAPE_ASKED_AHEAD="$ahead"
    fi
    if ! is_count "$behind" || ! is_count "$ahead"; then
        finding "shape: GitHub's compare of pin $(short "$commit") with $source $default was not two counts; not read"
        return 0
    fi
    [ "$behind" = 0 ] ||
        finding "shape pin $(short "$commit") is not on $source $default: $behind commit(s) of it are not there (per GitHub) — a branch commit, which the standard's squash-merge orphans"
    [ "$ahead" = 0 ] ||
        finding "shape pin $(short "$commit") is $ahead commit(s) behind $source $default (per GitHub): update-shape.py check --root $root"
    return 0
}

# A root's or holder's shape pin, all three reads, and one note line saying
# what was pinned, how many copies matched, and where currency came from — or
# that it was NOT read, and why, so silence is never mistaken for current.
read_shape() {
    local root="$1" manifest="$2" pin commit source mirror path digest actual
    local ok=0 total=0 currency=""
    pin="$root/contracts/shape-pin.yaml"
    [ -f "$pin" ] || return 0
    commit=""; yaml_scalar "$pin" commit && commit="$YAML_VALUE"
    source=""; yaml_scalar "$pin" source_repository && source="$YAML_VALUE"
    if [ -z "$commit" ]; then
        finding "shape: contracts/shape-pin.yaml records no commit"
        return 0
    fi
    choose_sha_tool
    while IFS=$'\t' read -r path digest; do
        [ -n "$path" ] || continue
        total=$((total + 1))
        if [ ! -f "$root/$path" ]; then
            finding "shape copy $path is missing; contracts/shape-pin.yaml names it (update-shape.py check --root $root)"
            continue
        fi
        if [ "$SHA_TOOL" = none ]; then
            finding "shape copy $path not checked: no sha256sum, shasum or openssl on PATH"
            continue
        fi
        actual="$(file_sha256 "$root/$path")"
        if [ -z "$actual" ]; then
            finding "shape copy $path not checked: \`$SHA_TOOL\` could not read it"
        elif [ "$actual" != "$digest" ]; then
            finding "shape copy $path edited in place: its sha256 is not the one contracts/shape-pin.yaml records (drift: carry the change upstream, never re-digest)"
        else
            ok=$((ok + 1))
        fi
    done < <(shape_pin_rows "$pin")
    # THE MIRROR. `update-shape.py` moves the pin and the manifest's `shape:`
    # block together, and refuses a manifest that has no such block — so a
    # pin beside a manifest without one is the same half-done state as two
    # different commits.
    mirror="$(nested_scalar "$root/$manifest" shape commit)"
    if [ -z "$mirror" ]; then
        finding "shape: $manifest carries no \`shape:\` commit beside contracts/shape-pin.yaml (update-shape.py moves both together)"
    elif [ "$mirror" != "$commit" ]; then
        finding "shape: $manifest mirrors $(short "$mirror") but contracts/shape-pin.yaml pins $(short "$commit"): out of step (update-shape.py moves both together)"
    fi
    # CURRENCY: the clone first, because it is local and free, and reading it
    # before asking GitHub is what the documents promise; GitHub only under
    # `--fetch`, and only when no clone answered.
    if [ -n "$source" ]; then
        if shape_currency_from_clone "$root" "$commit" "$source"; then
            currency="; against $source $SHAPE_DEFAULT_BRANCH per a clone under the projects directory"
        elif [ "$FETCH" -eq 1 ] && ask_github_shape_pin "$root" "$commit" "$source"; then
            currency="; against $source $SHAPE_DEFAULT_BRANCH per GitHub"
        elif [ "$FETCH" -eq 1 ]; then
            currency="; currency NOT read: no clone of $source with its default branch fetched under the projects directory, and GitHub did not answer (an earlier line says why)"
        else
            currency="; currency not read (no clone of $source with its default branch fetched under the projects directory; --fetch asks GitHub)"
        fi
    fi
    say "    shape: pinned at $(short "$commit") of ${source:-an unnamed source}; $ok/$total copies match their digests$currency"
}

# --- the parked record against disk -------------------------------------------
#
# THE FIFTH LAYER, under Brett Heap's RULING of 2026-09-11, verbatim: "next
# layer: parked record against disk". `park` RECORDS every open feature of an
# estate in the person's private workspace repository — `workspaces/<org>/
# <id>.yaml`, one block per project, one entry per feature, and per leg the
# commit it was parked at and whether that commit was pushed — and `resume`
# rebuilds the worktrees from that record on the next workstation. Between
# the two, the record and the disk drift apart in exactly the ways `resume`
# later refuses, and this layer reads them beforehand, the way `resume.sh`
# will:
#
# 1. A RECORDED FEATURE WITH NO WORKTREE HERE on its branch: parked, and not
#    resumed on this machine. `resume <Name>` is the exit; nothing here adds
#    a worktree. Unless the record is a `--no-push` one (3), or names no
#    parked commit for that leg (2): then there is nothing here to resume
#    FROM, and the exit is the workstation that has it.
#    And where the porcelain still holds a REGISTRATION for that branch that
#    is no longer a worktree — its directory gone, or git's own `prunable`
#    with the directory left behind — what clears it is named first: a prune,
#    after an unlock where the registration is locked, and a move of any
#    leftover directory. `resume` cannot recreate a worktree git still
#    believes it has, nor write over a directory it did not create.
# 2. A WORKTREE WHOSE BRANCH TIP IS NOT THE PARKED COMMIT: moved on since it
#    was parked (park again before leaving); or BEHIND it — the record is
#    newer, parked later on another workstation, which `resume` refuses to
#    reset; or diverged from it; or a parked commit this machine never
#    fetched. ONE GAP IS NOT A FINDING: `resume` recreates the worktree at the
#    parked commit and SOFT-RESETS the WIP commits `park` made, so a resumed
#    feature's tip is legitimately `<parked>~<wip_depth>` with the work back
#    in the working tree — `resume.sh`'s `wip_gap_is_ours` is what keeps it
#    from refusing that, and the same test is made here.
#    AND A RECORD THAT NAMES NO PARKED COMMIT FOR THE LEG is a state of this
#    field, not a leg to pass over (the independent review of #20,
#    2026-09-11, note 7; this repository's #21, recorded there as out of scope). There is nothing
#    for a tip to be compared WITH, so none of those arms can run; and with
#    no worktree here `resume` does not bring it back either — its RR1
#    refuses a leg whose parked commit is not origin's tip, and an absent
#    one never is — so the line that named `resume <Name>` there named an
#    exit that cannot work rather than merely staying quiet. Both halves say
#    what the record does not say, and the exit is a park that parks the
#    feature THROUGH: a park that refuses it carries the hole forward.
# 3. A LEG PARKED WITH `--no-push`: the record says `pushed: false`, only the
#    workstation that parked it has the WIP commit, and `resume` refuses it —
#    so no line under this state names `resume` as the exit, and none blames
#    a missing parked commit on a fetch that never had one to make.
#    AND A `pushed:` THAT IS NEITHER `true` NOR `false` — a bare `pushed:`,
#    `pushed: ""`, `pushed: yes` — IS A THIRD READING OF THAT FIELD, not a
#    fourth spelling of one of the two (the second independent review of #18,
#    2026-09-11, note (d); this repository's #19). `park.sh` writes one of
#    those two words for every leg it parks and carries an existing entry
#    forward untouched for one it refuses, so a value that is neither came
#    from a hand-edit or a bad merge of the record; every arm below used to
#    fall through the `= false`
#    tests and say "the record says it was pushed", which is a claim the
#    record does not make. It is named for what it is instead — a `pushed:`
#    that cannot be read — and what `resume` will do with it is said with it:
#    `resume.sh`'s RR6 is `[ "$pushed" != true ]`, so it refuses such a leg
#    exactly as it refuses a `--no-push` one, and no line here offers
#    `resume` as the exit either.
#    AND NO `pushed:` KEY AT ALL IS A FOURTH STATE OF THE FIELD, not a
#    quieter spelling of the third (the independent review of #19,
#    2026-09-11, recorded there as out of scope; this repository's #20). It
#    reads WORSE than the third did: `record_rows` emitted a leg's row at its
#    `pushed:` line, so a
#    leg without one had no row, and this layer said nothing whatever about
#    the record — no finding, exit 0 — while `workspace-common.sh` seeds
#    every leg's pushed with `false` and RR6 refuses that leg in the
#    `--no-push` words. A wrong line can be argued with; a missing one
#    cannot. The row now goes out where the LEG ENDS, a field of its own
#    keeps `absent` apart from the bare `pushed:` that is `empty`, and the
#    state gets a finding shaped like the third's: what the record does not
#    say, what `resume` makes of that, and the one exit that works.
# 4. A WORKTREE ON DISK THAT THE RECORD DOES NOT KNOW: a feature that was
#    never parked, which is the one thing `park` exists to carry.
# 5. A LEG THE RECORD GIVES NO ROLE: a state of the RECORD rather than of the
#    disk, and read here because `resume` refuses the WHOLE FEATURE for it —
#    `collect_legs` maps each leg's role onto this checkout and its `*)
#    return 1` takes every other leg of that feature with it (the independent
#    review of #20, 2026-09-11, note 8; this repository's #21, recorded there as out of scope). It is
#    the ROOT's finding, since no leg repository can be found for a leg that
#    names no role, and the exit is a park that parks the feature through.
# 6. A LEG WHOSE ROLE THIS CHECKOUT'S SHAPE DOES NOT HAVE — a misspelling, or
#    another shape's role — which `resume` refuses the WHOLE FEATURE for in
#    the same breath and the same "shape mismatch" words as 5 (the
#    independent review of #21, 2026-09-11). The line for it named neither
#    the refusal nor an exit; it does both now, and it is worded for a role
#    that is genuinely another shape's as well as for a typo, because this
#    layer cannot tell those apart and a park cannot make this root mount
#    what its shape has not got.
# 7. A FEATURE THE RECORD LISTS NO LEG FOR — an empty `legs:`, the `legs: []`
#    `workspace_write_manifest` writes for a feature it has no legs for, or
#    leg items with no `- role:` line — which `collect_legs` refuses whole at
#    its closing `[ "${#LEG_ROLES[@]}" -gt 0 ]`, in the same "shape mismatch"
#    words again (note 1 of the independent review of #21, 2026-09-11; this repository's #22, and
#    the note-8 commit's own "recorded for later"). The first of these states
#    that is not a LEG's: there is no leg to hang a reading on, so it is read
#    per FEATURE, in `read_record`, where the comment above that function
#    says how and why the features are judged before their legs are read.
#
# 8. A LEG WHOSE BRANCH ORIGIN HAS LOST — no `origin/<branch>` in that leg's
#    repository — which `resume` refuses the WHOLE feature for at RR2, in
#    words and with exits of its own: "<branch> is no longer on origin in the
#    <role> leg", then the record's `- branch: <branch>` block to delete where
#    the feature landed, or the branch to push again from the workstation that
#    parked it where it went by mistake (the independent review of #21,
#    2026-09-11, recorded there as out of scope; verified against the
#    extension on 2026-09-12). Read only where there is no worktree here,
#    because RR4 comes first and calls a leg whose worktree is already at the
#    path it computes recreated; read before 2, because RR2 comes before RR1;
#    and READ AS OF THE LAST FETCH, because RR2's own test is a REF in that
#    repository and this is the same ref. Which is why it is TWO readings and
#    not one: after a fetch that worked here the ref is origin's answer and
#    the finding is RR2's refusal with RR2's exits, while without one the
#    commonest reason for a missing `origin/<branch>` is a feature parked
#    elsewhere since this clone last fetched — `resume` fetches that branch
#    itself and brings it back — so state 1's line keeps its exit and carries
#    the other reading, and `status --fetch` is what settles them. THE
#    WORKTREE THE TWO ENDS LOOK FOR IS NOT QUITE THE SAME WORKTREE:
#    `worktree_on_branch` finds one on that branch ANYWHERE in the leg's
#    repository, while `resume.sh`'s `leg_registered_at` matches the PATH it
#    computes under the worktree root, so a worktree on the branch at some
#    other path leaves this reading quiet where RR2 would still refuse (the
#    independent review of this branch, 2026-09-12, note 4). Pre-existing —
#    every arm here that asks "is there a worktree" asks it that way — and a
#    reading of its own, recorded here and not taken.
#    AND WHERE THE FETCH SETTLES IT THE REFUSAL IS THE FEATURE'S, made with
#    the three below in `read_record`'s first pass and carried onto every leg
#    of it: the paragraph under this one is what that means for the lines.
#
# 5, 6, 7 AND 8 ARE REFUSALS OF THE WHOLE FEATURE, SO NO LINE OF THAT FEATURE
# NAMES `resume` (Copilot on #21, second round, 2026-09-11; 8 by the
# independent review of this branch, 2026-09-12, which found that reading
# PRINTED as a whole-feature refusal and not MADE one). A leg of such a
# feature that is otherwise perfectly resumable — parked, pushed, no worktree
# here — used to draw "`resume <Name>` brings it back" two lines under the
# one that says `resume` refuses the whole feature, and a person reads the
# REPORT, not one line of it: the two together name an exit that cannot work,
# which is the rule at the top of `check_parked_leg`. `read_record`'s first
# pass makes the verdict for every feature before any of its legs is read and
# hands it down, so those lines say what `resume` will do and end at the one
# exit there is. 8 DIFFERS FROM THE OTHER THREE TWICE OVER: its exits are its
# own, because a re-park settles 5, 6 and 7 and is the wrong thing to name for
# a branch origin has lost — the feature may have LANDED — and it is a verdict
# only UNDER `--fetch`, because without one the feature is not known to be
# refused at all and state 1's line keeps its exit with the other reading
# beside it. Everything else a sibling can say — dirty, moved on,
# behind, a commit that is not here — is unchanged: it was never `resume`
# that answered those.
#
# WHAT THE OTHER END'S LOADER DOES WITH AN ABSENT `pushed:` IS CHANGING, AND
# NO LINE HERE DEPENDS ON IT (the independent review of workBenches #60,
# 2026-09-11). Paragraph 3's account of the fourth state is workBenches
# before #60 (ec2b450): `workspace_load_project` seeds every leg's pushed
# with `false`, so `resume` reads a missing key as `false` and RR6 refuses
# the leg in the `--no-push` words. workBenches #60, merged as 98b8bd1 and
# that repository's main since, stops that invention — the seed is the
# empty string, a new `MANIFEST_LEG_PUSHED_SEEN[]` keeps absent apart from
# bare, and RR6 refuses the leg in words of its own, "the record has no
# `pushed:` for this leg" (both run on 2026-09-11, exit 2 each). THE OUTCOME
# IS THE SAME UNDER BOTH — the leg is not recreated, and only a park that
# parks the feature through writes a record that reads — so that is what the
# findings say, and none of them says how the loader reached it.
#
# THE RECORD IS FOUND WHERE `park` FILES IT AND `resume` LOOKS FOR IT:
# `${AGENT_PROTOCOL_ROOT:-~/.agents}/workspace.yaml` names the workspace
# repository and its checkout (`path:`), plus per-org overrides under
# `orgs:`, each checkout counted ONCE by physical path (an override pointing
# at the default's clone is one clone, not two orgs); every checkout's
# `workspaces/<org>/*.yaml` is scanned for the estate's ID — a standalone
# project's `project.yaml` `id:`, a family's `family.yaml` `id:`, which is
# the name `park` files under — and then, if none, for the estate's FOLDER
# name, which is what `resume <Name>` looks up; basenames are compared
# case-insensitively, as `resume` compares them. No config, a checkout that
# is not on this machine, or no record for this estate, is a NOTE, not a
# finding: a machine that never parked has nothing to be out of sync with.
# Two orgs recording the same id is a note too, unless one of them is the org
# this root's origin names. Nothing here reads the workspace repository's
# remote: the record is as of its last pull, and the estate's line says so.
#
# A FAMILY's record has one block per MEMBER, matched by its `root:` —
# `<family folder>/<member folder>`, the casing `workspace_manifest_root_value`
# writes — and the holder has none. Each leg is read in the repository it
# lives in: the root for the `repo` and `assembly` roles, the leg's own
# checkout for `spec`, `code` and any other role `project.yaml` places.

WORKSPACE_CONFIG="${AGENT_PROTOCOL_ROOT:-$HOME/.agents}/workspace.yaml"
RECORD_FILE=""
RECORD_NOTE=""
RECORD_ID=""
#: The `shape:` of the block being read, which `read_record` fills from
#: `record_rows`'s project row and only the shape reading in
#: `check_parked_leg` looks at. Declared here because `set -u` is on and a
#: record with no project row leaves it as it was.
RECORD_SHAPE=""
#: WHERE THIS CHECKOUT'S FEATURE WORKTREES BELONG, computed once per root by
#: `read_record` — `record_worktree_root` reads a config file and the answer is
#: the ROOT's, not the leg's, so asking it per leg read the same file over
#: again. Declared here because `set -u` is on and the record layer is entered
#: for a root whose block the record may not carry.
RECORD_WORKTREE_ROOT=""
NL=$'\n'

# shellcheck disable=SC2088  # a literal leading ~ is what is matched here
expand_home() {
    local value="$1"
    case "$value" in
    '~') printf '%s\n' "$HOME" ;;
    '~/'*) printf '%s\n' "$HOME/${value#\~/}" ;;
    *) printf '%s\n' "$value" ;;
    esac
}

# Every workspace checkout the config names: the top-level `path:` and each
# `    path:` under `orgs:`. Read only; a malformed block is a block not found.
workspace_checkouts() {
    local file="$1" line
    [ -r "$file" ] || return 0
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line%$'\r'}"
        case "$line" in
        '#'*) continue ;;
        'path:'* | '    path:'*) expand_home "$(trim_unquote "${line#*:}")" ;;
        esac
    done <"$file"
}

# `<org><TAB><file>` for every `workspaces/<org>/<id>.yaml` in every checkout,
# basename compared case-insensitively — `resume`'s `manifest_candidates`.
#
# ONE ROW PER `<org>/<basename>`, because that is what `resume` counts as one
# record (F4 of the review on openRepoShape #83): the same `<org>/<id>.yaml`
# reached through TWO CHECKOUTS of one workspace repository — a second clone
# of it named by an `orgs:` override — is ONE record, and `--org` could not
# have told the two apart. The checkouts are already deduped by physical path
# in `locate_record`; this is the other half of the same rule, for two clones
# that really are two directories. Without it they counted as two, which read
# as "two orgs record this id" and, where origin names no owner to settle it,
# SKIPPED a record that was never ambiguous. BOTH HALVES OF THE KEY ARE
# LOWERED, because both were MATCHED that way — the basename two lines above,
# the org as `resume` folds it — so `Atlas.yaml` in one checkout and
# `atlas.yaml` in another are one record here too (Copilot on #18).
record_candidates() {
    local want checkout dir file base key seen
    local -a keys=()
    want="$(lower "$1")"
    shift
    for checkout in "$@"; do
        [ -d "$checkout/workspaces" ] || continue
        for dir in "$checkout"/workspaces/*/; do
            dir="${dir%/}"
            [ -d "$dir" ] || continue
            for file in "$dir"/*.yaml; do
                [ -f "$file" ] || continue
                base="${file##*/}"
                [ "$(lower "${base%.yaml}")" = "$want" ] || continue
                key="$(lower "${dir##*/}")/$(lower "$base")"
                for seen in ${keys[@]+"${keys[@]}"}; do
                    [ "$seen" != "$key" ] || continue 2
                done
                keys+=("$key")
                printf '%s\t%s\n' "${dir##*/}" "$file"
            done
        done
    done
}

# THIS estate's record, once per estate: RECORD_FILE, or RECORD_NOTE saying
# why there is none to read. RECORD_ID is what a standalone block is matched
# by.
locate_record() {
    local manifest checkouts candidates count org file owner url line physical seen missing=""
    local -a dirs=()
    RECORD_FILE=""
    RECORD_NOTE=""
    RECORD_ID=""
    if [ "$ESTATE_KIND" = "family" ]; then
        manifest="$ESTATE_ROOT/family.yaml"
    else
        manifest="$ESTATE_ROOT/project.yaml"
    fi
    if yaml_scalar "$manifest" id; then RECORD_ID="$YAML_VALUE"; fi
    [ -n "$RECORD_ID" ] || RECORD_ID="$(lower "$ESTATE_NAME")"
    if [ ! -f "$WORKSPACE_CONFIG" ]; then
        RECORD_NOTE="parked record: no $WORKSPACE_CONFIG on this machine, so parked features are not read (\`resume --workspace <owner>/<repo>\` writes it)"
        return 0
    fi
    if [ ! -r "$WORKSPACE_CONFIG" ]; then
        RECORD_NOTE="parked record: $WORKSPACE_CONFIG cannot be read (permissions), so parked features are not read"
        return 0
    fi
    checkouts="$(workspace_checkouts "$WORKSPACE_CONFIG")"
    if [ -z "$checkouts" ]; then
        RECORD_NOTE="parked record: $WORKSPACE_CONFIG names no \`path:\`, so parked features are not read"
        return 0
    fi
    # ONE ENTRY PER DIRECTORY, by PHYSICAL path — `resume`'s `add_workspace`,
    # for its reason (F4 of the review on openRepoShape #83): an `orgs:`
    # override pointing at the default's clone would otherwise be scanned
    # twice and every record in it counted twice, which reads as "two orgs
    # record this id". A checkout the config names and this machine does not
    # have is skipped and, if none is left, said.
    while IFS= read -r line; do
        [ -n "$line" ] || continue
        [ -d "$line" ] || { missing="${missing:+$missing }$line"; continue; }
        physical="$(cd "$line" 2>/dev/null && pwd -P)" || physical="$line"
        for seen in ${dirs[@]+"${dirs[@]}"}; do
            [ "$seen" != "$physical" ] || continue 2
        done
        dirs+=("$physical")
    done <<<"$checkouts"
    if [ ${#dirs[@]} -eq 0 ]; then
        RECORD_NOTE="parked record: the workspace checkout $WORKSPACE_CONFIG names is not on this machine ($missing), so parked features are not read (\`resume\` clones it)"
        return 0
    fi
    candidates="$(record_candidates "$RECORD_ID" "${dirs[@]}")"
    if [ -z "$candidates" ] && [ "$(lower "$ESTATE_NAME")" != "$(lower "$RECORD_ID")" ]; then
        # Filed under the id by `park`; looked up by folder by `resume <Name>`.
        candidates="$(record_candidates "$ESTATE_NAME" "${dirs[@]}")"
    fi
    if [ -z "$candidates" ]; then
        RECORD_NOTE="parked record: none for '$RECORD_ID' under ${dirs[*]} — nothing of this estate has been parked, or not into this workspace"
        return 0
    fi
    count="$(printf '%s\n' "$candidates" | grep -c . || true)"
    if [ "$count" -eq 1 ]; then
        RECORD_FILE="${candidates#*	}"
        return 0
    fi
    # Two orgs record this id — genuinely two, the rows being one per
    # `<org>/<basename>`: the one this root's origin belongs to, if any.
    url="$(g "$ESTATE_ROOT" config --get remote.origin.url 2>/dev/null || true)"
    owner="$(repo_slug_of "$url" || true)"
    owner="${owner%%/*}"
    while IFS=$'\t' read -r org file; do
        [ -n "$org" ] || continue
        if [ -n "$owner" ] && [ "$(lower "$org")" = "$(lower "$owner")" ]; then
            RECORD_FILE="$file"
            return 0
        fi
    done <<<"$candidates"
    RECORD_NOTE="parked record: $count records for '$RECORD_ID' in different orgs, and none is the org this root's origin names; not read (\`resume --org\` is how one is chosen)"
}

# One project block of a record as rows, fields joined by 0x1F (a tab is IFS
# whitespace, and an absent optional key — `park` writes no key for an empty
# value — would shift every later field left, the hazard `read_branches`
# names): `project` with parked_at, parked_on, lane and active, once; `branch`
# with the branch, per feature; `leg` with branch, role, parked_commit,
# pushed, wip_depth and whether the record carried a `pushed:` KEY at all,
# per leg; and, for the lines `workspace_load_project` reads NEITHER a feature
# nor a leg out of, `nofeature` with the branch and `noleg` with the branch
# and the role. The block is the one whose `id:` (a standalone) or `root:` (a
# family member) is the selector's. The layout is the fixed one
# `workspace_write_manifest` emits — two spaces per level, `pushed:` the last
# key of a leg. A `pushed:` with no VALUE is read for what it is rather than
# as either of the two words, which is `check_parked_leg`'s business.
#
# THE LEG'S ROW GOES OUT WHERE THE LEG ENDS, NOT AT ITS `pushed:` LINE (the
# independent review of #19, 2026-09-11). Emitting at `pushed:` was the same
# position for every record `park` writes, because that key is always last
# and always there — until it is NOT there, and then the leg had no row and
# VANISHED from this layer: no finding, exit 0, silence about a record
# `resume.sh` refuses: RR6 is `[ "$pushed" != true ]`, and no loader of that
# record makes an absent key `true` — on workBenches before #60 (ec2b450)
# `workspace-common.sh` defaults the leg's pushed to `false` at the `- role:`
# line when no `pushed:` follows (the review cited its line 777), and under
# workBenches #60 the seed is the empty string with the key's absence carried
# in a field of its own, so the leg is refused either way. The comment that
# stood here called the dropped leg harmless —
# its branch is still known from the `branch` row, so it is never accused of
# being unparked — which is true and beside the point: not being accused is
# not the same as being read, and this layer exists to read the record the
# way `resume` will judge it. So the row is emitted when the leg ENDS: the
# next `- role:`, the next feature's `- branch:`, the next key at any
# SHALLOWER indent (a project-level `features:` or `root:`, the next project
# block's `- id:`, a top-level key), or the end of the file. `role` is
# cleared as the row goes out, so two enders in a row emit once and a block
# with no leg emits nothing. For a leg whose `pushed:` is there AND LAST —
# which is every leg `workspace_write_manifest` emits — this is the same
# line and the same order as before, which is what keeps a well-formed
# record's rows byte-identical; where a hand-edit moved the key, the row now
# carries the keys AFTER it too, which is what the loader reads.
#
# AND THE PROJECT ROW CARRIES WHAT THE LOADER WOULD HAVE READ, WHICH MEANS
# EMITTING IT WHERE THE BLOCK ENDS (Copilot's first round on #23): "the
# project row is serialized when `features:` is encountered, but
# `workspace_load_project` accepts the other project-level keys at the same
# indent in any order. If a hand-edited or merged manifest places `shape:`
# after the feature list … `RECORD_SHAPE` remains empty and the new
# shape-disagreement diagnostic is silently omitted even though `resume` reads
# the field." It holds for all five of them — the loader's indent-4 arm is one
# `case` over `shape`, `root`, `parked_at`, `parked_on`, `parked_by_lane`,
# `active_feature` and the rest, reached by INDENT and not by position. Run
# against the extension on 2026-09-12 on a record whose `shape:`, `parked_at:`,
# `parked_on:`, `parked_by_lane:` and `active_feature:` all sit BELOW its
# feature list: `workspace_load_project` returned every one of them
# (`MANIFEST_SHAPE=three-leg`, `MANIFEST_PARKED_AT=2026-09-10T20:00:00Z`,
# `MANIFEST_PARKED_ON=Falcon`, the lane and the active feature), with the
# feature and its leg intact — while this reader, emitting at `features:`,
# read them as absent: the root's note line said "parked ? on ?", the
# unmappable-role finding lost its "the record calls the project three-leg
# where this checkout is single" clause, and its re-park exit said "the record
# does not say where" about a record that says Falcon. So the row is built at
# the FLUSH, from the values the whole block left behind, and `$has_features`
# — set by the `features:` key and nothing else — is what decides whether
# there is a row at all, exactly as the key did before. A block with no
# `features:` key still emits none — UNLESS a line of it is one this reader
# owes a finding about, which is the `$unread` half of that gate and the one
# thing added to it: a block whose `- branch:` sits above every `features:`
# key has no feature for the row to count, and the finding it gets instead
# names the date and the workstation out of exactly those fields ("park it
# again from the workstation that has it"). A record `park` wrote is byte for
# byte the rows it always was: one block matches, `features:` is the last
# project-level key in it, nothing in it goes unread, and the row goes out in
# front of the branch and leg rows it sat in front of.
#
# AND THE SIXTH FIELD IS HOW `absent` IS TOLD FROM `empty`, which are two
# states and not one: a bare `pushed:` is PRESENT and unreadable (note (d) of
# the second independent review of #18), and no `pushed:` at all is the
# record saying nothing about that leg. Both leave the `pushed` field empty,
# so the difference cannot live in it — and a sentinel word put there could
# be spelled by the hand-edit that produced the record and read back as the
# sentinel. The sixth field is written by THIS function and never from the
# file: the word `pushed` where the key was seen, empty where it was not.
#
# AND A LEG IS OPEN FROM ITS `- role:` LINE, WHATEVER THAT LINE SAYS (the
# independent review of #20, 2026-09-11, note 8, recorded there as out of
# scope). `[ -n "$role" ]` was doing two jobs at both emit sites — "a leg is
# open" and "the leg has a role" — and the second DROPPED the row for a leg
# whose `- role:` value is EMPTY, while `workspace_load_project` creates that
# leg exactly as it creates any other: its indent-10 arm takes the `- ` item,
# reads `role` as the key, and assigns whatever `_SHAPE_SCALAR` holds, empty
# or not. A leg the loader has and this reader has not is a leg `resume`
# refuses the WHOLE FEATURE for while this layer says nothing — the shape of
# hole the two notes before it closed. `$open` does the first job now, set at
# the `- role:` line and cleared where the leg ends, so the role's value is
# left to mean only what it says and the row goes out with an EMPTY role
# field for `check_parked_leg` to read; the row's SIX FIELDS are unchanged,
# because an empty field between two 0x1F separators shifts nothing.
# A LEG WITH NO `- role:` LINE AT ALL is a different thing, and the two
# readers agree about that one already: the loader's `[ "$key" = "role" ] ||
# continue` starts no leg for it, and with no `- role:` line there is nothing
# to open one here either, so neither reader has a leg to read.
# EVERY VALUE THIS PARSER READS LOSES A `#` COMMENT FIRST, because
# `workspace_load_project` takes `#` and everything after it off EVERY line
# (`content="${raw%%#*}"`) before it splits key from value: `- role: repo # the
# leg` is `repo` there, and `- role: # note` is a leg with no role there. Read
# whole here, the first was a role this root does not mount and the second was
# not empty at all, so both went to `leg_repo_for_role` and drew "the record
# names a leg this root does not mount here" — the mounting arm's true words
# about a record `resume` refuses for a different reason entirely (Copilot on
# #21, after #19 had closed the same gap for `pushed:` alone). The parity is
# per LINE, as the loader's is: a value that itself contains a `#` is cut
# there too, exactly as `resume.sh` will cut it — this layer reads the record
# the way `resume.sh` will judge it, and not the way YAML would.
record_scalar() {
    local value="$1"
    case "$value" in *'#'*) value="${value%%#*}" ;; esac
    trim_unquote "$value"
}

record_rows() {
    local file="$1" sel="$2" line matched=0 value open=0 named=0 held="" has_features=0
    local in_features=0 in_legs=0 dropped=0 unread=0 leg_why=outer why=""
    local parked_at="" parked_on="" lane="" active="" branch="" role="" commit="" pushed="" depth="" seen="" shape=""
    [ -f "$file" ] || return 0
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line%$'\r'}"
        # THE LEG ENDS WHERE THE NEXT THING STARTS. Every alternative here is
        # a line whose first non-space character sits SHALLOWER than a leg's
        # own keys (which the layout puts at twelve spaces), plus the next
        # leg's own `- role:` at ten. A blank line is none of them, AND NOR IS
        # A FULL-LINE COMMENT at any indent: `workspace_load_project` takes
        # `#` and everything after it off every line before it reads one
        # (`content="${raw%%#*}"`), so a `# note` a hand left at column 0
        # between two of a leg's keys is nothing to the loader — and read as
        # an ender here it closed the leg before its `pushed:`, and the leg
        # went out as if the record had none (the review of this follow-up,
        # 2026-09-11). A record with a blank line or a comment between two
        # keys still has one leg, not two. An indent-10 line that is NOT
        # `- role:` is deliberately not an ender either: `workspace_load_project`
        # starts no leg there, and its twelve-space keys keep landing on the
        # leg above — so the two readers agree.
        case "${line#"${line%%[![:space:]]*}"}" in
        '#'*) ;;
        *)
            case "$line" in
            [![:space:]]* | '  '[![:space:]]* | '    '[![:space:]]* | \
                '      '[![:space:]]* | '        '[![:space:]]* | '          - role:'*)
                if [ "$named" -eq 1 ] && [ "$open" -eq 1 ]; then
                    held="$held$(printf 'leg\037%s\037%s\037%s\037%s\037%s\037%s' "$branch" "$role" "$commit" "$pushed" "$depth" "$seen")$NL"
                fi
                role=""; open=0
                ;;
            esac
            ;;
        esac
        # AND A LIST IS OPEN ONLY WHERE THE LOADER HAS IT OPEN. This is the
        # reading recorded and not taken by the commit before this one: an
        # indent-4 key CLOSES the feature list at `workspace_load_project` —
        # `in_features=false` is the first line of that arm, before the `case`
        # that sets it true again for `features:` alone — and an indent-8 key
        # closes a feature's leg list the same way, `in_legs=false` at the top
        # of ITS arm. Under a shut list the loader reads nothing: its indent-6
        # arm is behind `[ "$in_features" = true ] || continue` and its
        # indent-10 arm behind `[ "$in_legs" = true ] || continue`, so a
        # `- branch:` below a project-level key — or before any `features:` key
        # at all — is a feature it never makes, and a `- role:` below a
        # feature-level key is a leg it never makes. Run against the extension
        # on 2026-09-12 on nine records: a project key between two features
        # loaded the FIRST feature and its leg and no second feature; one
        # between a feature's `- branch:` and its `legs:` loaded that feature
        # with ZERO legs; a `- branch:` above every `features:` key loaded no
        # feature at all; a feature-level key between `legs:` and its items,
        # and between two of them, loaded the feature with none and with the
        # first alone; and a SECOND `features:` key reopened the list, both
        # features and both legs intact.
        # This reader had no such state — it matched the indent of a line and
        # nothing above it — so it emitted a `branch` row for a feature
        # `resume` never sees and a `leg` row for a leg it never makes, which
        # is work `status` claimed and `resume` will not bring back: the
        # direction that most needs a line, because a person reading it
        # believes the feature is safe. The first two flags below are the
        # loader's two, set in the same places: every line at indent 0, 2 or 4
        # shuts the feature list and `features:` alone reopens it, every line
        # at indent 6 or 8 shuts the leg list and `legs:` reopens it INSIDE an
        # open feature list. Nothing at indent 10 or 12 moves either, as
        # nothing does at the loader, and a full-line comment is not a line at
        # all to either reader. The rows the lines under a shut list get
        # instead are `nofeature` and `noleg`, read by `read_record`: this
        # layer drops nothing silently.
        #
        # AND `$leg_why` IS WHICH LINE THE PERSON HAS TO GO AND FIND, kept
        # beside them because a finding that names the wrong one sends a hand
        # to an indent the record has nothing out of place at. It is three
        # states and not two (Copilot's first round on this PR, suppressed):
        # `outer`, a key at PROJECT indent, which shuts the feature list and
        # every leg list inside it — and which a `features:` key is too, so a
        # list REOPENED by one is still `outer` until something nearer shuts
        # it; `legs`, a key at FEATURE indent shutting a list a `legs:` key
        # had opened; and `nolegs`, where no `legs:` key has opened one at
        # all since this feature's `- branch:` line. Read whole, the third
        # was told as the second: "shuts a feature's `legs:` list" for a
        # record whose feature has no `legs:` key above that line to shut.
        #
        # AND `nolegs` IS THE `- branch:` LINE'S AND NO OTHER LINE'S AT
        # INDENT 6, which is the loader again: its indent-6 arm makes a
        # feature only where the item's key is `branch` (`[ "$key" = branch ]
        # || continue`), and at any other line there it reads no feature and
        # stays in the one above — whose `legs:` key IS above that line. So
        # that line is a key that SHUT a list, `legs` like the indent-8 keys
        # beside it, and a `nolegs` there would send a hand looking for a
        # `legs:` key the feature has got. The third state is the one this
        # round ADDED, and it may not swallow the second on its way in.
        case "${line#"${line%%[![:space:]]*}"}" in
        '#'*) ;;
        *)
            case "$line" in
            '    features:'*)
                in_features=1; in_legs=0; leg_why=outer
                # AND A REOPENED LIST IS A CLEAN SLATE FOR `$dropped`
                # (Copilot's first round on this PR, suppressed). The legs of
                # a list reopened below an unread `- branch:` are collected
                # onto the feature ABOVE it — record (k) of the probe — so a
                # `- role:` under one is that feature's defect and not the
                # unread branch's said twice. Left set, it suppressed the
                # `noleg` row for exactly that line, and where the feature
                # above had no other leg the report then said "the record
                # lists no leg for it" about a record that lists one, which is
                # the sentence this commit exists to stop making.
                dropped=0
                ;;
            [![:space:]]* | '  '[![:space:]]* | '    '[![:space:]]*)
                in_features=0; in_legs=0; leg_why=outer
                ;;
            '        legs:'*)
                in_legs="$in_features"
                [ "$in_features" -eq 0 ] || leg_why=legs
                ;;
            '      - branch:'*)
                in_legs=0
                [ "$in_features" -eq 0 ] || leg_why=nolegs
                ;;
            '      '[![:space:]]* | '        '[![:space:]]*)
                in_legs=0
                [ "$in_features" -eq 0 ] || [ "$leg_why" = nolegs ] ||
                    leg_why=legs
                ;;
            esac
            ;;
        esac
        # A ROW BELONGS TO ITS BLOCK, AND A BLOCK IS NOT KNOWN TO BE THE
        # SELECTED ONE UNTIL THE KEY THAT SELECTS IT IS READ (Copilot's second
        # round on #22). An `id=` selector is answered by the block's FIRST
        # line, so every row after it knew where it stood; a `root=` selector
        # — the one a family member is matched by, and the one a record found
        # under the folder name falls back to — is answered at the `root:`
        # key, which `workspace_write_manifest` writes above `features:` and a
        # hand-edit or a bad merge can leave below it. Every row emitted
        # before that line was then thrown away for a block that DID match:
        # the `project` row, each feature's `branch` row, and every leg that
        # ended above it. For the layout `park` writes — `features:` last —
        # that is the whole block, so this function returned nothing and the
        # layer said "no block for this root" about a record it had just
        # matched; for a `root:` left INSIDE the feature it was the mangling
        # Copilot named, a blank `- branch:` row suppressed while the leg row
        # under it went out, reaching `read_record` with no branch row before
        # it, taking no feature's verdict and drawing "`resume <Name>` brings
        # it back" for a feature `resume` refuses at RR2 under a blank name.
        # `workspace_load_project` has no such order: it matches at the
        # `- id:` line and reads a block's keys by their INDENT, wherever they
        # sit in it.
        #
        # SO THE ROWS ARE HELD AND FLUSHED, NOT EMITTED AND HOPED FOR. Every
        # arm below appends to `$held`, the buffer goes out whole where the
        # block ENDS — in the record's own order, which is the order it was
        # appended in — and a block that never matched is dropped there
        # instead. THE EMITTED BYTES ARE UNCHANGED for every record `park`
        # writes: one block matches, its rows go out in the order they were
        # appended, with the same fields, and the caller reads this
        # function's whole output in a `$(…)` either way.
        case "$line" in
        '  - id:'*)
            # THE BLOCK ABOVE ENDS HERE. Its `project` row goes out in front
            # of the rows it was gathered beside, which is where it has
            # always been read — see the note above the row itself.
            if [ "$matched" -eq 1 ]; then
                if [ "$has_features" -eq 1 ] || [ "$unread" -eq 1 ]; then
                    printf 'project\037%s\037%s\037%s\037%s\037%s\n' "$parked_at" "$parked_on" "$lane" "$active" "$shape"
                fi
                [ -z "$held" ] || printf '%s' "$held"
            fi
            held=""; has_features=0; unread=0; dropped=0
            matched=0
            parked_at=""; parked_on=""; lane=""; active=""; branch=""; role=""; open=0; named=0; shape=""
            value="$(record_scalar "${line#*:}")"
            case "$sel" in
            id=*) [ "$(lower "$value")" != "$(lower "${sel#id=}")" ] || matched=1 ;;
            esac
            ;;
        '    root:'*)
            value="$(record_scalar "${line#*:}")"
            case "$sel" in
            root=*) [ "$(lower "$value")" != "$(lower "${sel#root=}")" ] || matched=1 ;;
            esac
            ;;
        # THE RECORD'S OWN `shape:`, which `workspace_write_manifest` writes for
        # every project block it merges (its `PROJECT_KEYS`, second entry) and
        # `workspace_load_project` loads as `MANIFEST_SHAPE`. IT DECIDES
        # NOTHING AT EITHER END — `collect_legs` maps roles onto the shape of
        # THIS CHECKOUT and never reads it — and `resume.sh` only prints it,
        # in "was parked from a $MANIFEST_SHAPE project and this checkout is
        # $REPO_SHAPE", which is the sentence the shape reading below
        # explains. It is read here so that a disagreement between the two can
        # be NAMED where it is relevant, and nowhere else: a record whose legs
        # this checkout maps is a record this layer says nothing about,
        # whatever its `shape:` says.
        '    shape:'*) shape="$(record_scalar "${line#*:}")" ;;
        '    parked_at:'*) parked_at="$(record_scalar "${line#*:}")" ;;
        '    parked_on:'*) parked_on="$(record_scalar "${line#*:}")" ;;
        '    parked_by_lane:'*) lane="$(record_scalar "${line#*:}")" ;;
        '    active_feature:'*) active="$(record_scalar "${line#*:}")" ;;
        '    features:'*) has_features=1 ;;
        '      - branch:'*)
            # A `- branch:` WITH NO VALUE IS STILL A FEATURE TO THE LOADER:
            # `workspace_load_project` appends `MANIFEST_FEATURE_BRANCHES`
            # at every `- branch:` line, empty or not, and `resume.sh` then
            # reads a feature named nothing — refusing it at RR2 in RR2's
            # words with the name blank ("Error:  is no longer on origin in
            # the repo leg", exit 2, run against the extension 2026-09-12)
            # where it has legs, and as a shape mismatch where it has none.
            # Read whole this row was dropped and its legs with it, and the
            # feature vanished from this layer (Copilot on #22). The row goes
            # out with the empty name, and `$named` — not the name — is what
            # lets a leg row out: a leg before any `- branch:` line at all
            # still has no feature to belong to and is still dropped. A
            # `- branch:` READ BEFORE THE BLOCK WAS KNOWN TO MATCH — a `root:`
            # selector answered below it — goes out with the rest of the held
            # rows and in the record's order, which is the flush above.
            #
            # AND A `- branch:` UNDER A SHUT FEATURE LIST IS NO FEATURE AT
            # ALL, which is the reading above: it gets a `nofeature` row
            # instead, so the branch the record lists is named in a finding of
            # its own rather than counted as a feature `resume` will rebuild.
            # IT DOES NOT BECOME `$branch` EITHER. `$branch` is this reader's
            # `feature_index` — the last feature the loader MADE — and the
            # loader's does not move at a line it reads no feature out of: put
            # a second `features:` key below such a line and the legs under
            # THAT reopen onto the feature above it, which is the name their
            # rows have to carry (run against the extension 2026-09-12, record
            # (k) of the probe). `$dropped` is the other half, and it carries
            # down to the leg lines under an unread branch: their own rows
            # would be the same defect said twice, and the branch's is the one
            # with a name on it — as far as the next `features:` key, which
            # reopens the list onto the feature above and clears it.
            #
            # AND THE ROW CARRIES WHICH LINE TO GO AND FIND, as the `noleg`
            # row does and for the same reason (Copilot's first round on this
            # PR, suppressed): `$has_features` is whether a `features:` key
            # has been read in this block at all, so a `- branch:` ABOVE every
            # one of them — or in a block that has none — is told that the key
            # which OPENS the list is missing above it, and not that a key at
            # project indent below it shut a list there is none of.
            value="$(record_scalar "${line#*:}")"
            if [ "$in_features" -eq 1 ]; then
                branch="$value"
                dropped=0
                named=1
                held="$held$(printf 'branch\037%s' "$branch")$NL"
            else
                dropped=1
                unread=1
                why=feature
                [ "$has_features" -eq 1 ] || why=nolist
                held="$held$(printf 'nofeature\037%s\037%s' "$value" "$why")$NL"
            fi
            ;;
        '          - role:'*)
            # A `- role:` UNDER A SHUT LEG LIST IS NO LEG, for the reason
            # above and in the loader's own words: `$open` is what says a leg
            # is open, and it opens only where the loader's `in_legs` does.
            # The row it gets instead is `noleg`, carrying the branch first
            # and the role second exactly as a `leg` row does — `read_record`
            # files both under the last `branch` row, which is the feature the
            # loader files a leg under. `$named` and `$dropped` together are
            # "the last `- branch:` line was one the loader READ": an unread
            # one sets `$dropped` and its own finding already names the block,
            # so the legs written under it are not said twice, and a `- role:`
            # before any `- branch:` line at all has no feature to be a leg of
            # in either reader.
            role="$(record_scalar "${line#*:}")"; commit=""; pushed=""; depth=""; seen=""
            open="$in_legs"
            if [ "$in_legs" -eq 0 ] && [ "$named" -eq 1 ] && [ "$dropped" -eq 0 ]; then
                unread=1
                # AND WHICH LIST WAS SHUT IS THE THIRD FIELD, which is
                # `$leg_why` above and the line the person has to go and find.
                held="$held$(printf 'noleg\037%s\037%s\037%s' "$branch" "$role" "$leg_why")$NL"
            fi
            ;;
        '            parked_commit:'*) commit="$(record_scalar "${line#*:}")" ;;
        '            wip_depth:'*) depth="$(record_scalar "${line#*:}")" ;;
        '            pushed:'*)
            # The first value to lose its `#` comment here, in #19 — read
            # whole, `pushed: true # note` was "neither true nor false" here
            # and `true` to `resume.sh`; `record_scalar` above now does it for
            # every value this parser reads, for the same reason.
            pushed="$(record_scalar "${line#*:}")"
            seen=pushed
            ;;
        esac
    done <"$file"
    # THE LAST LEG OF THE BLOCK, which no later line closes. Spelled out
    # rather than shared with the arm above, because the alternative is a
    # helper reading this function's locals by dynamic scope — five lines
    # twice against an idiom this file uses nowhere else. AND THE LAST FLUSH
    # WITH IT: the end of the file is the last chance the block has to be the
    # matched one, and a `root:` that sits at the very bottom of it is
    # answered on the line after which there is none.
    if [ "$named" -eq 1 ] && [ "$open" -eq 1 ]; then
        held="$held$(printf 'leg\037%s\037%s\037%s\037%s\037%s\037%s' "$branch" "$role" "$commit" "$pushed" "$depth" "$seen")$NL"
    fi
    if [ "$matched" -eq 1 ]; then
        if [ "$has_features" -eq 1 ] || [ "$unread" -eq 1 ]; then
            printf 'project\037%s\037%s\037%s\037%s\037%s\n' "$parked_at" "$parked_on" "$lane" "$active" "$shape"
        fi
        [ -z "$held" ] || printf '%s' "$held"
    fi
    return 0
}

# THIS CHECKOUT'S SHAPE, COMPUTED THE WAY `git-common.sh` COMPUTES IT, because
# it is what `resume.sh` judges every recorded leg against and the one thing
# this layer could not say (the feature-level follow-up's own note, 2026-09-11:
# "a role this root DOES mount that `collect_legs` refuses anyway … Reading
# that one means computing this root's shape the way `load_repo_shape` does …
# which is a reading of its own and is recorded in this commit's message, not
# taken here").
#
# THE RULE IS FOUR CONDITIONS AND EVERY ONE OF THEM IS A DECLARATION IN
# `project.yaml`, read off `load_repo_shape` itself: the file is there, its
# indent-zero `kind:` is `project-manifest`, its indent-zero `schema:` is
# `project-repo-schema`, and its `legs:` list declares BOTH a `spec` LEG and a
# `code` LEG. THE LEG, NOT ITS `path:` — the two are easy to conflate and the
# difference is real: `_shape_record_leg` stores `"${path:-spec}"` and
# `"${path:-code}"`, so a `- role: spec` with no `path:` at all still makes
# `_SHAPE_LEG_SPEC_PATH` non-empty and still declares the leg, and the test
# `load_repo_shape` then makes is on those two variables being non-empty (the
# independent review of this branch's base, 2026-09-12, which found the rule
# written down in this file as "a `spec` and a `code` leg path" — wrong on
# both counts, and wrong in the direction that matters: it reads as though a
# leg declared without one were no leg at all).
# Anything short of the four is `single`: no manifest at all, another `kind:`,
# no `schema:` line, one leg of the pair missing. A FAMILY HOLDER IS SINGLE
# TOO, having no `project.yaml` — `load_repo_shape` says so out loud, "using
# the single-repository layout" — and this suite's own Atlas fixture is single
# with a `spec` leg mounted: `kind: project-manifest`, no `schema:` line,
# `assembly` and `spec` legs and no `code` one.
#
# THE PARSER IS THAT PARSER, not `legs_of`'s. `legs_of` reads the same file for
# another question and drops what this one needs — the `assembly` leg and the
# `.` path — while `load_repo_shape` keys on INDENT: `#` and everything after
# it comes off every line first, a `legs:` at indent zero opens the list, a
# `- ` item at any indent sets the item's key column two past its own, keys at
# that column belong to the item, an indent-zero line closes the list, and the
# last item is closed by the end of the file. Both readers therefore agree
# about a manifest with unusual indentation, which is the only kind where they
# could differ at all.
ROOT_SHAPE=single
ROOT_SHAPE_WHY=""
#: WHAT THE WALK BELOW FOUND, and the reason it is a walk of its own no
#: longer: `_shape_record_leg`'s two jobs at every leg boundary. The flags are
#: the two of the four conditions that live in the `legs:` list; the parallel
#: arrays are every leg's ROLE and the `path:` the manifest gave it, in the
#: file's order, which is what `leg_repo_for_role` reads back instead of
#: scanning `project.yaml` a second time (the independent review of this
#: branch, 2026-09-12: "ONE parser of project.yaml in this file, not two").
#: Arrays and a linear scan, because bash 3.2 has no associative array and a
#: manifest declares a handful of legs; globals because the closer is a
#: function, and reset at the top of every walk because the next root is a
#: different manifest.
ROOT_LEG_ROLES=()
ROOT_LEG_PATHS=()
_SHAPE_SAW_SPEC=0
_SHAPE_SAW_CODE=0
_shape_close_leg() {
    case "$1" in
    spec) _SHAPE_SAW_SPEC=1 ;;
    code) _SHAPE_SAW_CODE=1 ;;
    esac
    # A leg item with no `- role:` line is no leg at either end: `collect_legs`
    # starts none for it, `_shape_record_leg`'s `case` matches nothing, and a
    # nameless role is not one this reader could be asked about.
    [ -n "$1" ] || return 0
    ROOT_LEG_ROLES+=("$1")
    ROOT_LEG_PATHS+=("$2")
}
read_root_shape() {
    local root="$1" manifest raw content trimmed indent_ws indent key value rest
    local kind="" schema="" in_legs=0 key_col=-1 cur_role="" cur_path="" missing=""
    manifest="$root/project.yaml"
    ROOT_SHAPE=single
    ROOT_SHAPE_WHY="there is no \`project.yaml\` here"
    ROOT_LEG_ROLES=()
    ROOT_LEG_PATHS=()
    _SHAPE_SAW_SPEC=0
    _SHAPE_SAW_CODE=0
    [ -f "$manifest" ] || return 0
    while IFS= read -r raw || [ -n "$raw" ]; do
        content="${raw%%#*}"
        content="${content%$'\r'}"
        trimmed="${content#"${content%%[![:space:]]*}"}"
        trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
        [ -n "$trimmed" ] || continue
        indent_ws="${content%%[![:space:]]*}"
        indent=${#indent_ws}
        if [ "$in_legs" -eq 1 ]; then
            case "$trimmed" in
            '- '* | '-')
                if [ "$indent" -gt 0 ]; then
                    _shape_close_leg "$cur_role" "$cur_path"
                    cur_role=""
                    cur_path=""
                    key_col=$((indent + 2))
                    rest="${trimmed#-}"
                    rest="${rest#"${rest%%[![:space:]]*}"}"
                    key="${rest%%:*}"
                    if [ -n "$rest" ] && [ "$key" != "$rest" ]; then
                        case "$key" in
                        role) cur_role="$(trim_unquote "${rest#*:}")" ;;
                        path) cur_path="$(trim_unquote "${rest#*:}")" ;;
                        esac
                    fi
                    continue
                fi
                ;;
            esac
            if [ "$indent" -eq 0 ]; then
                _shape_close_leg "$cur_role" "$cur_path"
                cur_role=""
                cur_path=""
                in_legs=0
            elif [ "$indent" -eq "$key_col" ]; then
                key="${trimmed%%:*}"
                if [ "$key" != "$trimmed" ]; then
                    case "$key" in
                    role) cur_role="$(trim_unquote "${trimmed#*:}")" ;;
                    path) cur_path="$(trim_unquote "${trimmed#*:}")" ;;
                    esac
                fi
                continue
            else
                continue
            fi
        fi
        if [ "$indent" -eq 0 ]; then
            key="${trimmed%%:*}"
            [ "$key" != "$trimmed" ] || continue
            value="$(trim_unquote "${trimmed#*:}")"
            case "$key" in
            kind) kind="$value" ;;
            schema) schema="$value" ;;
            legs) in_legs=1; key_col=-1 ;;
            esac
        fi
    done <"$manifest"
    _shape_close_leg "$cur_role" "$cur_path"
    if [ "$kind" = project-manifest ] && [ "$schema" = project-repo-schema ] &&
        [ "$_SHAPE_SAW_SPEC" -eq 1 ] && [ "$_SHAPE_SAW_CODE" -eq 1 ]; then
        ROOT_SHAPE=three-leg
        ROOT_SHAPE_WHY="its \`project.yaml\` declares both a \`spec\` and a \`code\` leg"
        return 0
    fi
    # EVERY CONDITION THAT FAILED, not the first: a person who reads "no
    # `schema:`" and adds one finds the checkout still single, because this
    # fixture's manifest has no `code` leg either. `kind:` is its own clause
    # because a file that is not a project manifest is not a project manifest
    # with something missing.
    ROOT_SHAPE=single
    if [ "$kind" != project-manifest ]; then
        ROOT_SHAPE_WHY="its \`project.yaml\` is not \`kind: project-manifest\`"
        return 0
    fi
    [ "$schema" = project-repo-schema ] || missing="no \`schema: project-repo-schema\`"
    [ "$_SHAPE_SAW_SPEC" -eq 1 ] || missing="${missing:+$missing, }no \`spec\` leg"
    [ "$_SHAPE_SAW_CODE" -eq 1 ] || missing="${missing:+$missing, }no \`code\` leg"
    case "$missing" in
    *', '*) missing="${missing%, *} and ${missing##*, }" ;;
    esac
    ROOT_SHAPE_WHY="its \`project.yaml\` declares $missing"
    return 0
}

# WHETHER `collect_legs` CAN MAP THIS ROLE ONTO THIS CHECKOUT, which is the
# whole of its `case`: `spec` and `code` only where `REPO_SHAPE` is
# `three-leg`, `repo` only where it is `single`, and `*) return 1` for every
# other word — `assembly` included, which is a role the three-leg SHAPE has
# and `resume` maps in neither shape, because the assembly root's own worktree
# is the `repo` leg's. A role it cannot map takes the WHOLE feature with it.
role_maps_here() {
    case "$1" in
    spec | code) [ "$ROOT_SHAPE" = three-leg ] ;;
    repo) [ "$ROOT_SHAPE" = single ] ;;
    *) return 1 ;;
    esac
}

# The two clauses for a role `collect_legs` cannot map: the SHORT one, which
# the feature's verdict carries onto every sibling leg's line, and the LONG
# one, which is the culprit leg's own finding and says which shape this
# checkout is and why. Both name the ROLE, because "shape mismatch" — all
# `resume` says out loud — names neither the role nor anything else about it.
role_shape_short() {
    case "$1" in
    spec | code | repo)
        if [ "$ROOT_SHAPE" = single ]; then
            printf "its \`%s\` leg names a role \`resume\` maps only onto a three-leg checkout, and this one is single" "$1"
        else
            printf "its \`%s\` leg names a role \`resume\` maps only onto a single checkout, and this one is three-leg" "$1"
        fi
        ;;
    *) printf "its \`%s\` leg names a role \`resume\` maps onto no checkout of either shape" "$1" ;;
    esac
}
role_shape_long() {
    case "$1" in
    spec | code | repo)
        if [ "$ROOT_SHAPE" = single ]; then
            printf "\`resume\` maps the role \`%s\` onto a three-leg checkout only, and this one is single (%s)" "$1" "$ROOT_SHAPE_WHY"
        else
            printf "\`resume\` maps the role \`%s\` onto a single checkout only, and this one is three-leg (%s)" "$1" "$ROOT_SHAPE_WHY"
        fi
        ;;
    *) printf "\`resume\` maps the role \`%s\` onto no checkout of either shape — it knows \`spec\` and \`code\` in a three-leg project and \`repo\` in a single one — and this one is %s (%s)" "$1" "$ROOT_SHAPE" "$ROOT_SHAPE_WHY" ;;
    esac
}

# AND THE EXITS ARE NOT THE SAME FOR THE TWO KINDS OF UNMAPPABLE ROLE, which
# is what the one tail both arms below carried said (Copilot's first round on
# #23): "for roles such as `assembly` or `docs`, `role_shape_long` correctly
# says that `resume` maps the role onto no checkout of either shape, but this
# common suffix still says the feature can come back in a checkout of the
# other shape … contradicts the new help/README wording that a role of neither
# shape has no resumable checkout." THE REMEDIATION IS NOT IMPOSSIBLE, which
# is the half of that to disagree with: the tail's "that shape" is the
# WORKSTATION's, named two clauses earlier, and a park from a three-leg
# workstation writes `spec` and `code` — `park.sh`'s `build_legs` is
# `LEG_ROLES=("spec" "code")` in a three-leg root and `("repo")` in a single
# one, and it writes no other word in either — so the re-park does produce a
# record some checkout resumes, and this suite's own end-to-end run of
# `resume.sh` on a `code` leg brought the feature back, exit 0. WHAT IS
# MISSING IS WHAT THE HELP AND THE README ALREADY SAY: "where the role is
# really another shape's, only a checkout of that shape, and where it is
# NEITHER shape's, no checkout at all". The tail said the first of those to
# both kinds, so a person reading `assembly` was left to think some checkout
# somewhere resumes this record as it stands, and none does: run against the
# extension on 2026-09-12, `role: assembly` and `role: docs` in a real
# three-leg root — both legs mounted, the four conditions met — answered
# "Error: 001-a-thing was parked from a three-leg project and this checkout is
# three-leg; that feature was NOT recreated", "REFUSED: 001-a-thing — shape
# mismatch", exit 2, which is the same refusal both words draw in a single
# root (the commit before this one, the same estates). Refused in BOTH shapes
# is a record no checkout brings back, so the `*)` exit says that first and
# then the one thing that changes it.
role_shape_exits() {
    case "$1" in
    spec | code | repo)
        printf "park that feature again from the workstation that has it (%s), which writes the roles its own shape has; where that shape is not this checkout's, the feature comes back in a checkout of that shape, not here" "$2"
        ;;
    *)
        printf "nor does any other checkout, of either shape, so the one exit is to park that feature again from the workstation that has it (%s), which writes the roles its own shape has — \`park\` writes \`spec\` and \`code\` in a three-leg project and \`repo\` in a single one, and never this word — and the feature comes back in a checkout of that shape, which is this one only where the two agree" "$2"
        ;;
    esac
}

# The repository a leg role lives in: the root for `repo` and `assembly`, else
# the path `project.yaml` gives that role, else a folder named for the role,
# else — for a `spec` or a `code` leg the manifest DECLARES — the role's own
# name, because that is the path the other end computes for it.
#
# THE LOADER DEFAULTS THOSE TWO PATHS, AND ANSWERING NOTHING FOR THEM NAMED A
# REFUSAL THAT IS NOT THE ONE MADE (the independent review of this branch,
# 2026-09-12). `git-common.sh`'s `_shape_record_leg` is `spec)
# _SHAPE_LEG_SPEC_PATH="${path:-spec}"` and `code) … "${path:-code}"`, so a
# `- role: spec` with no `path:` mounts at `spec/` to `load_repo_shape`, and
# `collect_legs` maps the role onto that path without ever looking at the
# directory. Answering NOTHING for such a leg sent a genuinely three-leg root
# whose `spec/` is not checked out to the whole-feature refusal below — while
# what `resume` does there is refuse the whole ESTATE before it reads a
# feature at all: "Error: the spec leg 'spec' is not an initialised Git
# checkout at '…/spec'. / Run 'git submodule update --init' (or 'make
# bootstrap') in the project root first.", exit 1 — neither the refusal that
# line claims nor the exits it offers. Answering the default path returns that
# state to the short line under it — the leg is not mounted here — which is
# what this layer can prove.
#
# AND THE ANSWER IS `read_root_shape`'s WALK, NOT A SECOND SCAN OF THE SAME
# FILE (the independent review of this branch, 2026-09-12, verbatim: "have
# read_root_shape also record the two leg paths it walks past, and let
# leg_repo_for_role consult those — ONE parser of project.yaml in this file,
# not two"). What stood here read `project.yaml` again, by COLUMN — `'  - role:'*`
# and `'    path:'*` — which is not how either loader reads it, and the two
# answers came apart wherever the file was not indented the way this suite
# indents it. TWO STATES PROVED IT, both of them manifests `load_repo_shape`
# reads without blinking: a `legs:` list at FOUR spaces (`    - role: spec` /
# `      path: docs`) matched neither pattern, so this function answered
# NOTHING for `spec` and for `code` — and a root the shape reader above calls
# three-leg then had every leg of every recorded feature refused as "a role
# this root does not mount", a whole-feature refusal `resume` does not make;
# and a `path:` under a column-0 `# note` inside the item, which the arm that
# closed an item on `[![:space:]]*` threw away, leaving a declared `path: docs`
# on the default `spec/` and the leg read against the wrong repository. Both
# were fixed AT THIS END twice already — Copilot's two rounds on #22 taught
# these values to lose a `#` the way `load_repo_shape` loses one, and taught
# the closer that a full-line comment closes nothing — and the walk above had
# both from the day it was written, because it is `load_repo_shape`'s own
# parser: `#` and everything after it comes off every line first, an empty
# line is skipped, a `- ` item at ANY indent opens a leg, and keys two past
# its own belong to it. So the walk records each leg's role and `path:` as it
# passes, and this function reads them back.
#
# WHERE A ROLE IS DECLARED TWICE THE LAST ONE WINS, which is
# `_shape_record_leg` again — it assigns, it does not test for empty — and
# where the last one gives no `path:` the arms below default it exactly as
# that function's `${path:-spec}` does.
#
# AND `path: .` IS A PATH LIKE ANY OTHER — THE ROOT (Copilot's first round on
# #23): "a declared `spec` or `code` leg with `path: "."` is mapped to the
# root repository. Status will instead inspect `$root/spec` or `$root/code`
# (or report it missing), so its worktree/origin findings disagree with
# `resume`." `_shape_record_leg` stores `"${path:-spec}"`, and `.` is not
# empty, so `load_repo_shape` keeps it and builds `SPEC_LEG`/`CODE_LEG` as
# `$root/$SHAPE_*_PATH` — `$root/.`, which `collect_legs` hands straight to
# `LEG_REPOS`. Run against the extension on 2026-09-12 on a manifest whose
# `code` leg carries `path: "."`, `path: .` and `path: '.'` in turn:
# `REPO_SHAPE=three-leg`, `SHAPE_CODE_PATH=.`, `CODE_LEG=<root>/.` in all
# three, and an EMPTY `path:` the only spelling that falls to the default
# `code`. End to end on a scratch estate the same day, that manifest and a
# record parking a `code` leg: `resume.sh` RESUMED it out of the root —
# "code  worktrees/001-a-thing/.  <sha>  clean", exit 0, the worktree
# registered in the ROOT's `git worktree list` — while this reader skipped
# the declared `.`, found no `code/`, answered the default `$root/code` and
# drew "the record names a leg this root does not mount here", a whole-feature
# refusal `resume` does not make. So the declared path is taken whatever it
# says, and `.` is answered as `$root` rather than as `$root/.`: they are one
# directory, and `$root` is the spelling `estate_repos` uses, which is what
# `fetched_ok_at` keys the run's fetches by.
leg_repo_for_role() {
    local root="$1" want="$2" i=0 path="" declared=0
    case "$want" in
    repo | assembly) printf '%s\n' "$root"; return 0 ;;
    esac
    while [ "$i" -lt "${#ROOT_LEG_ROLES[@]}" ]; do
        if [ "${ROOT_LEG_ROLES[$i]}" = "$want" ]; then
            declared=1
            path="${ROOT_LEG_PATHS[$i]}"
        fi
        i=$((i + 1))
    done
    if [ -n "$path" ]; then
        case "$path" in
        .) printf '%s\n' "$root" ;;
        *) printf '%s\n' "$root/$path" ;;
        esac
        return 0
    fi
    if [ -d "$root/$want" ]; then
        printf '%s\n' "$root/$want"
        return 0
    fi
    # AND THE DEFAULT IS THE ROLE'S OWN NAME, for the two roles
    # `_shape_record_leg` defaults a path for and no others: every other word
    # is one `collect_legs` refuses at its `*) return 1` however this root is
    # laid out, and inventing a path for it would hide that.
    case "$want" in
    spec | code) [ "$declared" -eq 0 ] || { printf '%s\n' "$root/$want"; return 0; } ;;
    esac
    return 1
}

# THE MOUNT `collect_legs` HANGS THE LEG ON UNDER THE FEATURE DIRECTORY, which
# is a different question from the one above and gives a different answer for
# one manifest in every hundred. `collect_legs` builds `LEG_TREES` out of
# `$SHAPE_SPEC_PATH` / `$SHAPE_CODE_PATH` — `_shape_record_leg`'s
# `"${path:-spec}"` and `"${path:-code}"`, so the DECLARED path wherever the
# manifest gives one and the role's own name where it does not — and out of
# NOTHING for a `repo` leg, whose tree is `$WORKTREE_ROOT/$branch` with no
# mount under it at all.
#
# SUBTRACTING THE LEG'S REPOSITORY FROM THE ROOT ANSWERS THE SAME STRING for
# every path but one, and the exception is the one `path: "."` makes: that leg
# IS the root repository at both ends, so there is nothing left to subtract,
# while `collect_legs` still computes `$WORKTREE_ROOT/$branch/.` for it — a
# string `leg_registered_at` matches nothing against, because git spells no
# registered path with a trailing `/.`. Verified against the extension on
# 2026-09-12 on a three-leg estate whose `code` leg carried `path: "."`, the
# `spec` leg a real submodule: with nothing at that path `resume.sh` RESUMED
# the feature, "code  worktrees/001-a-thing/.", exit 0; with the feature's own
# worktree on the recorded branch AT `worktrees/001-a-thing` it REFUSED —
# "Error: 'worktrees/001-a-thing/.' exists and is not a registered worktree of
# the code leg", "REFUSED: 001-a-thing — an unrelated path is in the way",
# exit 2 — so such a leg has no already-recreated state at all, and reading
# the mount any other way would call that feature present and clean.
leg_mount_for_role() {
    local want="$1" i=0 path=""
    case "$want" in
    repo | assembly) return 0 ;;
    esac
    while [ "$i" -lt "${#ROOT_LEG_ROLES[@]}" ]; do
        if [ "${ROOT_LEG_ROLES[$i]}" = "$want" ]; then
            path="${ROOT_LEG_PATHS[$i]}"
        fi
        i=$((i + 1))
    done
    printf '%s\n' "${path:-$want}"
}

# `get_config_value`'s scalar, WHICH IS NOT `record_scalar`'s. The record is
# read the way `workspace_load_project` reads it — a `#` cut wherever it
# stands — and `.specify/extensions/git/git-config.yml` is read by a different
# function with a different rule: `git-common.sh`'s `get_config_value` takes
# everything after the first colon and runs `sed -E 's/[[:space:]]+#.*$//;
# s/^[[:space:]]+//; s/[[:space:]]+$//; s/^['\''"]//; s/['\''"]$//'` over it.
# So a `#` is a comment there only where whitespace comes in front of it —
# `worktree_root: a#b` is the directory `a#b` to the other end — and ONE
# leading and ONE trailing quote character come off whether or not they pair.
# Reading it the other way would compute a path `resume` does not.
config_scalar() {
    local value="$1"
    value="${value%$'\r'}"
    case "$value" in
    *[[:space:]]'#'*) value="${value%%[[:space:]]'#'*}" ;;
    esac
    value="${value#"${value%%[![:space:]]*}"}"
    value="${value%"${value##*[![:space:]]}"}"
    case "$value" in '"'* | "'"*) value="${value#?}" ;; esac
    case "$value" in *'"' | *"'") value="${value%?}" ;; esac
    printf '%s' "$value"
}

# `os.path.abspath`'s normalisation, in bash: `.` dropped, `..` popped, empty
# segments collapsed, and no `readlink` anywhere — which is the point, because
# `resolve_path_from_repo_root` is `os.path.abspath(os.path.join(REPO_ROOT,
# raw))` and that function resolves no symlink either. A path normalised any
# harder than the other end normalises it would not compare equal to the one
# the other end computes, which is the whole use this has.
lexical_path() {
    local rest="$1" part out=""
    while [ -n "$rest" ]; do
        part="${rest%%/*}"
        if [ "$part" = "$rest" ]; then rest=""; else rest="${rest#*/}"; fi
        case "$part" in
        '' | .) ;;
        ..) out="${out%/*}" ;;
        *) out="$out/$part" ;;
        esac
    done
    printf '%s\n' "${out:-/}"
}

# WHERE THIS CHECKOUT'S FEATURE WORKTREES BELONG, computed the way `park.sh`
# and `resume.sh` compute it and out of the same three sources in the same
# order: `$SPECKIT_GIT_WORKTREE_ROOT` if it carries a value, else a
# `worktree_root:` in `.specify/extensions/git/git-config.yml` — the LAST one
# in the file, at any indent, which is `grep … | tail -n 1` — else the
# default, and THE DEFAULT IS THE SHAPE'S: `worktrees` under the root for a
# three-leg checkout, `../<root folder>-worktrees` BESIDE it for a single one.
# An absolute value is taken as it stands, because `resolve_path_from_repo_root`
# takes it as it stands.
#
# IT IS NOT READ OUT OF THE RECORD, although the record carries a
# `worktree_root:` of its own: `resume.sh` loads that key into
# `MANIFEST_WORKTREE_ROOT` and never reads it again, computing the path from
# THIS checkout's config, and `park.sh` records the value it computed the same
# way. So the record's copy says where the feature lived on the workstation
# that parked it, and this says where it goes here; where they differ, this is
# the one both verbs will use.
record_worktree_root() {
    local root="$1" file="$1/.specify/extensions/git/git-config.yml" raw line
    raw="${SPECKIT_GIT_WORKTREE_ROOT:-}"
    if [ -z "$raw" ] && [ -f "$file" ]; then
        line="$(grep -E '^[[:space:]]*worktree_root:[[:space:]]*' "$file" 2>/dev/null | tail -n 1 || true)"
        [ -z "$line" ] || raw="$(config_scalar "${line#*:}")"
    fi
    if [ -z "$raw" ]; then
        if [ "$ROOT_SHAPE" = three-leg ]; then
            raw="worktrees"
        else
            raw="../${root##*/}-worktrees"
        fi
    fi
    # AND AN ABSOLUTE VALUE IS TAKEN AS IT STANDS, which is not an oversight
    # and was checked again for Copilot's second round on #27, which read it as
    # one: `resolve_path_from_repo_root` tests `[[ "$raw_path" == /* ]]` FIRST
    # and `printf`s the string back unchanged — the `os.path.abspath` it runs
    # is in the `elif`, for a RELATIVE path only. Sourced and run on
    # 2026-09-13, it answered `/tmp/wt/../real/` for `/tmp/wt/../real/` and
    # `/tmp/wt/../real` for `/tmp/wt/../real`, normalising neither. Normalising
    # here would compute a path the other end does not, which is the one thing
    # `lexical_path`'s own comment forbids.
    case "$raw" in
    /*) printf '%s\n' "$raw" ;;
    *) lexical_path "$root/$raw" ;;
    esac
}

# THE PATH `collect_legs` COMPUTES FOR ONE LEG, which is the only path either
# verb looks at for it: `$WORKTREE_ROOT/$branch` for a `repo` leg, whose tree
# carries no mount at all, and `$WORKTREE_ROOT/$branch/$SHAPE_SPEC_PATH` (or
# `…CODE…`) for a mounted one — the mount being `leg_mount_for_role`'s answer
# and not the leg repository's own name under the root, which is the same
# string for every manifest but the one that declares `path: "."`.
leg_tree_path() {
    local root="$1" role="$2" branch="$3" mount
    mount="$(leg_mount_for_role "$role")"
    printf '%s\n' "$RECORD_WORKTREE_ROOT/$branch${mount:+/$mount}"
}

# A REGISTERED PATH IS A WORKTREE ONLY WHILE ITS OWN `.git` IS THERE. A linked
# worktree's is a FILE holding `gitdir: <repo>/.git/worktrees/<name>`; the main
# worktree's is a DIRECTORY; a path with neither is a leftover directory git
# will not resume into. Git says as much itself for an unlocked registration
# — `prunable gitdir file points to non-existent location` — but it computes
# no `prunable` for a LOCKED one (verified against git 2.43: lock a worktree,
# delete its `.git` file, and the block says only `locked`), and that pair is
# what this test catches: the directory is there, the worktree is not, and
# `resume` is blocked by a registration nothing had reported (Copilot on #18).
worktree_is_usable() {
    [ -d "$1" ] && [ -e "$1/.git" ]
}

# The path of the worktree checked out on `<branch>` in `<repo>`, main or
# linked. FOUR ANSWERS, because the porcelain has four: 0 and the path when a
# worktree is really there; 2 AND THE PATH when the only block for that
# branch is a REGISTRATION WITHOUT A WORKTREE; 3 and the path when such a
# registration is also LOCKED; 1 and nothing when there is no block at all.
#
# GIT'S OWN VERDICT FIRST, THEN THE WORKTREE ITSELF. `worktree list
# --porcelain` keeps the block of a linked worktree that is no longer one
# until somebody prunes it, and says so with `prunable <reason>` — which it
# prints whether or not the DIRECTORY is still there: delete only the
# worktree's `.git` file and the directory stays, the block says "prunable
# gitdir file points to non-existent location", and a `-d` test alone would
# call that live and the recorded feature CLEAN (Copilot on #18). So a
# `prunable` block is stale whatever is on disk, and what decides the rest is
# `worktree_is_usable` rather than the directory alone — because git computes
# no `prunable` for a `locked` block, and a locked one can have a directory
# with no `.git` in it, or no directory at all, with nothing said either way.
#
# AND A STALE BLOCK IS NOT "NOTHING HERE" EITHER, which is why it comes back
# as 2 or 3 rather than as 1: it is the thing that STOPS the exit this layer
# would otherwise name — see `check_parked_leg`. The two are told apart
# because THE EXIT IS NOT THE SAME: `worktree prune` clears an unlocked
# registration — even one whose directory is still on disk, leaving the
# directory — and SKIPS a locked one (verified against git 2.43: `prune -v`
# prints nothing for a locked block, `unlock` then `prune` removes it, and
# `worktree remove` refuses both a locked one, "cannot remove a locked
# working tree; use 'remove -f -f' to override or unlock first", and one
# whose `.git` is gone, "validation failed, cannot remove working tree:
# '…/.git' does not exist"). Naming a prune that cannot clear it would leave
# `resume` blocked by the very thing the finding said to clear.
#
# TWO STALE REGISTRATIONS FOR ONE BRANCH is a state `worktree add --force`
# makes, and the LOCKED one is the one to name: `prune` takes every unlocked
# one in the repository with it, so an `unlock` of the locked path plus that
# prune clears both, while naming the unlocked path alone would leave the
# locked registration standing and `resume` still blocked.
#
# `locked` AND `prunable` COME AFTER `branch` IN THE BLOCK, so every block is
# read to its blank line before it is judged, and the whole list is read
# before the answer is chosen: a live worktree on the branch wins over any
# stale registration for it, and a locked stale registration wins over an
# unlocked one.
worktree_on_branch() {
    local repo="$1" want="$2" line path="" hit=0 prunable=0 locked=0
    local live="" stale="" stale_locked=""
    while IFS= read -r line; do
        case "$line" in
        "worktree "*) path="${line#worktree }"; hit=0; prunable=0; locked=0 ;;
        "branch refs/heads/$want") hit=1 ;;
        locked | "locked "*) locked=1 ;;
        prunable | "prunable "*) prunable=1 ;;
        "")
            if [ "$hit" -eq 1 ]; then
                if [ "$prunable" -eq 0 ] && worktree_is_usable "$path"; then
                    [ -n "$live" ] || live="$path"
                elif [ "$locked" -eq 1 ]; then
                    [ -n "$stale_locked" ] || stale_locked="$path"
                else
                    [ -n "$stale" ] || stale="$path"
                fi
            fi
            hit=0; prunable=0; locked=0; path=""
            ;;
        esac
    done < <(g "$repo" worktree list --porcelain 2>/dev/null; printf '\n')
    [ -z "$live" ] || { printf '%s\n' "$live"; return 0; }
    [ -z "$stale_locked" ] || { printf '%s\n' "$stale_locked"; return 3; }
    [ -z "$stale" ] || { printf '%s\n' "$stale"; return 2; }
    return 1
}

# WHAT IS REGISTERED AT ONE PATH, read the way `resume.sh`'s
# `leg_registered_at` reads it: an EXACT STRING match against the paths
# `load_git_worktrees` staged, and NOTHING ABOUT THE BRANCH until the path has
# matched. That order is the whole point — RR4 asks "is a worktree registered
# AT this path", and `worktree_on_branch` above asks "where is the worktree on
# this branch", which is a different question with a different answer whenever
# the two are not the same path.
#
# THREE ANSWERS: 0 for a block `resume` WILL see; 2 for one git calls
# `prunable`, which `_git_worktree_finalize_record` DROPS before that walk
# ever starts (`if [ "$prunable" = false ]` around its four `+=` lines), so
# the other end is blind to it whatever is on disk; 1 where there is no block
# for that path at all. `REGISTERED_BRANCH` is the branch on the block — the
# empty string for a detached head, exactly as
# `"${GIT_WORKTREE_BRANCH_REFS[$index]#refs/heads/}"` is — and
# `REGISTERED_LOCKED` is 1 where git called it `locked`, which changes no
# verdict and decides an EXIT: `git worktree move` REFUSES a locked worktree
# ("fatal: cannot move a locked working tree; use 'move -f -f' to override or
# unlock first") and `worktree prune` SKIPS a locked registration, so the
# `unlock` goes in front of either (git 2.43, 2026-09-12).
#
# GLOBALS AND NOT A PRINTED STRING, because the caller wants all three and a
# `$(…)` would take the other two down with the subshell; the `while` reads a
# process substitution, which bash runs in THIS shell.
REGISTERED_BRANCH=""
REGISTERED_LOCKED=0
registered_at() {
    local repo="$1" want="$2" line path="" hit=0 prunable=0 locked=0 ref=""
    REGISTERED_BRANCH=""; REGISTERED_LOCKED=0
    while IFS= read -r line; do
        case "$line" in
        "worktree "*)
            path="${line#worktree }"; prunable=0; locked=0; ref=""; hit=0
            [ "$path" != "$want" ] || hit=1
            ;;
        "branch refs/heads/"*) ref="${line#branch refs/heads/}" ;;
        locked | "locked "*) locked=1 ;;
        prunable | "prunable "*) prunable=1 ;;
        "")
            if [ "$hit" -eq 1 ]; then
                REGISTERED_BRANCH="$ref"; REGISTERED_LOCKED="$locked"
                [ "$prunable" -eq 0 ] || return 2
                return 0
            fi
            hit=0; prunable=0; locked=0; ref=""; path=""
            ;;
        esac
    done < <(g "$repo" worktree list --porcelain 2>/dev/null; printf '\n')
    return 1
}

# WHAT IS IN THE WAY AT THE PATH `resume` COMPUTES, and the one exit, which is
# the PERSON'S. `IN_THE_WAY_WHAT` is what sits there, `IN_THE_WAY_EXIT` is the
# clause both the culprit leg's own finding and every sibling leg's carry; 1
# and all three empty where nothing is in the way.
#
# THE TWO REFUSALS ARE ONE STATE READ FROM TWO SIDES, and `resume.sh` words
# them identically down to the `look:` — "'<path>' exists and is not a
# registered worktree of the <role> leg … Nothing here overwrites a directory
# it did not create … Move it aside" — differing only in the REFUSED line's
# reason. RR4's second arm is `leg_registered_at` matching the path and
# `[ "$LEG_REGISTERED_BRANCH" = "$branch" ]` failing: "an unrelated worktree
# is in the way". RR3 is the `[ -e "$tree" ]` under it: "an unrelated path is
# in the way". Both `break` the leg loop, so BOTH TAKE THE WHOLE FEATURE, its
# other legs included — and both are reached before RR2 reads origin and
# before RR1 compares any commit.
#
# VERIFIED AGAINST THE EXTENSION on 2026-09-12, on scratch single-shape
# estates — a bare remote in a temp dir, a fake `$HOME`, workBenches' own
# `resume.sh`, `park.sh`, `git-common.sh` and `workspace-common.sh` — whose
# record carried `pushed: true`, `worktree_root: worktrees` and a parked
# commit that was origin's tip, the feature having no worktree here: a plain
# DIRECTORY at `worktrees/001-a-thing` answered "REFUSED: 001-a-thing — an
# unrelated path is in the way", exit 2, and a plain FILE answered the same;
# a worktree of `002-other` registered there answered "REFUSED: 001-a-thing —
# an unrelated worktree is in the way", exit 2, and so did one with a DETACHED
# head, one git held LOCKED, and one locked whose `.git` had been deleted. A
# PRUNABLE block there — same worktree, `.git` deleted, no lock — answered
# "an unrelated path is in the way" instead, because `resume` never sees the
# block and what refuses it is the directory. With nothing at that path at
# all the same estate RESUMED the feature, exit 0.
#
# WHAT IS NOT IN THE WAY. A block `resume` SEES at that path on the RECORDED
# branch is RR4's FIRST arm — the leg is already recreated, which is not a
# refusal at all — whether or not the worktree is really there, because a
# LOCKED block git calls no prunable is one `leg_registered_at` matches and
# reads as already done, and whether or not a worktree of that branch sits
# somewhere else too. And a PRUNABLE block on the recorded branch is the
# stale-registration arms' own state, WHERE THOSE ARMS NAME IT: the prune, the
# unlock in front of it and the leftover directory behind it are already one
# line there, ending in the `resume` that follows them, and naming the path
# twice would be two findings about it. `worktree_on_branch` is asked which
# block it answered with, because it is the one that decides whether that line
# is printed at all — with a live worktree on the branch somewhere else it
# answers with THAT, nothing names this path, and the reading below is the
# only one that does.
IN_THE_WAY_WHAT=""
IN_THE_WAY_WHY=""
IN_THE_WAY_EXIT=""
path_in_the_way() {
    local repo="$1" want="$2" branch="$3" role="$4" rc=0 ghost=""
    IN_THE_WAY_WHAT=""; IN_THE_WAY_WHY=""; IN_THE_WAY_EXIT=""
    registered_at "$repo" "$want" || rc=$?
    if [ "$rc" -eq 0 ]; then
        # RR4's FIRST arm, which is no refusal: the header's "what is not in
        # the way", and the branch is the only thing asked after the path.
        [ "$REGISTERED_BRANCH" != "$branch" ] || return 1
        if [ -n "$REGISTERED_BRANCH" ]; then
            IN_THE_WAY_WHAT="a worktree on \`$REGISTERED_BRANCH\`"
        else
            IN_THE_WAY_WHAT="a worktree with a detached head"
        fi
        IN_THE_WAY_WHY="an unrelated worktree is in the way"
        # A LINKED worktree's `.git` is a FILE and a main one's is a
        # DIRECTORY, and a mounted leg's own checkout carries a `.git` file
        # too — the path test answers that one and the directory test answers
        # a root reached by a spelling git canonicalises differently. Either
        # way what is registered there is the leg's own checkout, and `git
        # worktree move` refuses one: "'…' is a main working tree" (git 2.43,
        # 2026-09-12). Naming it would be naming an exit that does not work.
        #
        # AND THE PATH TEST IS MADE PHYSICAL, because `$want` is built on
        # `$RECORD_WORKTREE_ROOT`, which the commit that answered blocker 1
        # made physical, while `$repo` is `leg_repo_for_role`'s string on
        # `$root` — the lexical one (Copilot's first round on #27). Comparing
        # one with the other is the shape of that blocker, and the directory
        # test should not be the only thing standing between a person and an
        # exit git refuses.
        if [ "$want" = "$(physical_path "$repo")" ] || [ -d "$want/.git" ]; then
            IN_THE_WAY_EXIT="$want is the $role leg's OWN checkout rather than a worktree of it, and no \`worktree move\` moves a main working tree, so there is nothing here to move aside: while the worktree root this checkout computes puts this feature on top of that checkout, neither verb can do anything with it"
        elif worktree_is_usable "$want"; then
            IN_THE_WAY_EXIT="move it aside with \`git -C $(sh_operand "$repo") worktree move $(sh_operand "$want") <a path of your choosing>\`, which is yours to run — a registered worktree moved with \`mv\` leaves git holding the path it was at"
            [ "$REGISTERED_LOCKED" -eq 0 ] ||
                IN_THE_WAY_EXIT="git holds that worktree LOCKED and \`worktree move\` refuses a locked one (\"cannot move a locked working tree\"), so the unlock comes first: \`git -C $(sh_operand "$repo") worktree unlock $(sh_operand "$want")\`, then \`git -C $(sh_operand "$repo") worktree move $(sh_operand "$want") <a path of your choosing>\`, both yours to run — a registered worktree moved with \`mv\` leaves git holding the path it was at"
        else
            # NOT PRUNABLE AND NOT A WORKTREE IS LOCKED, and only locked: git
            # computes `prunable` for every other broken registration, and a
            # prunable one is answered 2 above and never reaches here. `prune`
            # is what clears this one once the lock is off — the same pair the
            # stale-registration arms name, for the same reason, and the same
            # leftover DIRECTORY behind it, because the prune clears the
            # registration and touches nothing on disk.
            IN_THE_WAY_EXIT="the worktree it registers is not there and git holds the registration LOCKED, which is why it calls it no \`prunable\` — clear it with \`git -C $(sh_operand "$repo") worktree unlock $(sh_operand "$want")\`, then \`git -C $(sh_operand "$repo") worktree prune\`, which is yours to run"
            # AND WHAT THE PRUNE LEAVES BEHIND IS A PATH, NOT A DIRECTORY
            # (Copilot's second round on #27): `git worktree add` refuses the
            # destination in the same three words whatever is there — "fatal:
            # '…' already exists", run on 2026-09-13 with a regular FILE at a
            # pruned registration's path — so a test for `-d` sent a person
            # to a `resume` that refuses for a file the line never mentioned.
            ! dest_exists "$want" ||
                IN_THE_WAY_EXIT="$IN_THE_WAY_EXIT, and move aside whatever the prune leaves behind there, which \`resume\` refuses for existing whatever git holds"
        fi
        return 0
    fi
    # AND THE STALE-REGISTRATION ARMS' OWN PATH IS THEIRS, where they name it
    # — the other half of "what is not in the way" above, and the only place
    # this function asks anything of `worktree_on_branch`.
    if [ "$rc" -eq 2 ] && [ "$REGISTERED_BRANCH" = "$branch" ]; then
        ghost="$(worktree_on_branch "$repo" "$branch" || true)"
        [ "$ghost" != "$want" ] || return 1
    fi
    [ -e "$want" ] || return 1
    if [ -d "$want" ]; then
        IN_THE_WAY_WHAT="a directory"
    else
        IN_THE_WAY_WHAT="a file"
    fi
    IN_THE_WAY_WHY="an unrelated path is in the way"
    # AND THE MOVE IS THE WHOLE EXIT ONLY WHERE GIT HOLDS NOTHING THERE. With
    # a PRUNABLE registration still on that path the `mv` clears RR3 and the
    # `git worktree add` at the end of the run dies on the registration
    # instead — "is a missing but already registered worktree; use 'add -f'
    # to override, or 'prune' or 'remove' to clear" — whether or not the
    # directory was moved first (git 2.43, 2026-09-12, both spellings run).
    # So the prune is named with it, and neither half is optional.
    IN_THE_WAY_EXIT="move it aside with \`mv $(sh_operand "$want") <a path of your choosing>\`, which is yours to run — nothing here moves or deletes a path it did not create"
    [ "$rc" -ne 2 ] ||
        IN_THE_WAY_EXIT="move it aside with \`mv $(sh_operand "$want") <a path of your choosing>\` and clear the stale registration git still holds for that path with \`git -C $(sh_operand "$repo") worktree prune\`, both yours to run — nothing here moves or deletes a path it did not create, and \`git worktree add\` refuses a path it is still registered at (\"a missing but already registered worktree\")"
    # AND FOR THE ONE PATH NO `mv` TAKES, no command is named at all: see
    # `dot_mount`. `[ -e "<…>/001-a-thing/." ]` is the feature directory
    # itself, so this arm is reachable for such a leg, and every operand it
    # could print was run and did not work.
    ! dot_mount "$want" ||
        IN_THE_WAY_EXIT="and there is no command that clears it: this leg is declared \`path: \".\"\` in \`project.yaml\`, so the path \`collect_legs\` computes for it ends in \`/.\`, which \`mv\` refuses as an operand outright — what answers it is a \`collect_legs\` that computes \`<worktree_root>/<branch>\` for a leg declared at the root, which is the standard's and not this command's"
    # AND A SYMLINKED PATH NEEDS THE SPELLING FIXED AS WELL AS THE PATH
    # CLEARED (Copilot's sixth round on #27, whose own example — the `elif
    # worktree_is_usable "$want"` arm above — cannot be reached under such a
    # root, because `registered_at` is an exact-string match against git's
    # ALWAYS-PHYSICAL spelling and the two never compare equal: run on
    # 2026-09-13, git spelled a worktree made through a link `…/real/wt` where
    # the config's path was `…/link/wt`, so `$rc` is 1 and this tail is the arm
    # that speaks. The point stands HERE: clearing the path lets `resume` reach
    # its add, and the worktree it then makes is registered at the resolved
    # path, which the next run compares against the lexical one and refuses
    # again. So the spelling is named beside the move, for the same reason
    # `off_path_exit` names it instead of one.
    ! has_symlink_component "$(parent_dir "$want")" ||
        IN_THE_WAY_EXIT="$IN_THE_WAY_EXIT; and clearing it is only half, because the path itself is reached through a symlink — \`resume\` compares the spelling this checkout computes against the ALWAYS-PHYSICAL one git records, and no worktree made under that spelling ever matches: $(symlink_exit "$SYMLINK_COMPONENT")"
    return 0
}

# WHAT BLOCKS THE `git worktree add` THAT NO RR LOOKS AT, asked of a leg row
# for the reason every question in that pass is: the verdict is the FEATURE's,
# and a sibling leg that says "`resume <Name>` brings it back" beside a line
# saying the feature is refused is the pair the rule at the top of
# `check_parked_leg` forbids (Copilot's seventh and eighth rounds on #27:
# "In a multi-leg record, the culprit line can then say to clear the path and
# resume while sibling legs still advertise `resume`, even though the run
# refuses the whole feature"). THREE STATES, all of them read by
# `check_parked_leg` already and none of them by an RR: a registration git
# still holds AT the path with nothing on disk, a DANGLING SYMLINK there —
# both of which `[ -e ]` and therefore RR3 look straight through — and a
# feature directory that is not a directory, which `resume`'s own `mkdir -p`
# refuses before it adds any leg. Each was run against git or the extension on
# 2026-09-13 and each ends the run; what this adds is that every OTHER leg of
# the feature now knows.
ADD_BLOCKED_WHAT=""
ADD_BLOCKED_EXIT=""
leg_add_blocked() {
    local root="$1" role="$2" branch="$3" repo want parent held=0
    local what="" exits=""
    ADD_BLOCKED_WHAT=""; ADD_BLOCKED_EXIT=""
    role_maps_here "$role" || return 1
    repo="$(leg_repo_for_role "$root" "$role" 2>/dev/null || true)"
    [ -n "$repo" ] || return 1
    [ -e "$repo/.git" ] || return 1
    want="$(leg_tree_path "$root" "$role" "$branch")"
    # A worktree git SEES at that path is RR4's first arm, and a path `[ -e ]`
    # finds is RR3's: both are read before this and neither is the add's.
    registered_at "$repo" "$want" || held=$?
    [ "$held" -ne 0 ] || return 1
    [ ! -e "$want" ] || return 1
    parent="$(parent_dir "$want")"
    # AND THEY ARE NOT EXCLUSIVE, so the verdict is built rather than chosen
    # (Copilot's ninth round on #27, verbatim: "When `held == 2` and
    # `[ ! -e "$want" ]`, this branch wins for both a truly absent destination
    # and a dangling symlink (and can also hide a missing final component
    # beneath a non-directory parent). `ADD_BLOCKED_EXIT` then contains only
    # `worktree prune`, but prune does not remove the dangling link or parent
    # file; in a multi-leg record, siblings carry that incomplete exit and
    # `resume` still fails"). Right, and it is the same mistake `off_path_exit`
    # stopped making a round earlier: the prune clears a REGISTRATION and
    # touches nothing on disk, so where something is also in the way on disk
    # both steps belong in the one exit a person pastes.
    if [ "$held" -eq 2 ]; then
        what="git still holds a worktree registration at the $want it computes for its \`$role\` leg, with nothing on disk there"
        exits="\`git -C $(sh_operand "$repo") worktree prune\`"
    fi
    if [ -L "$want" ]; then
        what="${what:+$what, and }a dangling symlink is at the $want it computes for its \`$role\` leg, which \`[ -e ]\` looks straight through and \`git worktree add\` refuses all the same"
        exits="${exits:+$exits, then }\`mv $(sh_operand "$want") <a path of your choosing>\`"
    fi
    if [ "$parent" != "$want" ] && dest_exists "$parent" && [ ! -d "$parent" ]; then
        what="${what:+$what, and }the $parent it makes that leg's worktrees under is not a directory, and \`resume\` runs \`mkdir -p\` on it before it adds any leg"
        exits="${exits:+$exits, then }\`mv $(sh_operand "$parent") <a path of your choosing>\`"
    fi
    [ -n "$what" ] || return 1
    ADD_BLOCKED_WHAT="$what"
    ADD_BLOCKED_EXIT="clear it with $exits, which is yours to run"
    return 0
}

# THE SAME QUESTION ASKED OF A RECORD ROW, which is how `read_record`'s first
# pass asks it — one call in a chain of RRs, where the arms around it are one
# test each. It works the leg's repository and the path `collect_legs` computes
# out of the ROLE, leaves that path in `LEG_TREE` for the verdict to name, and
# answers 1 — nothing in the way — for the three states that are not this
# reading's to judge: a role this checkout's shape has no place for, which
# `collect_legs` has already refused the feature for on the line above; a leg
# whose repository is not a checkout here, which `resume` refuses the whole
# ESTATE for before it reads a feature; and a path with nothing at it.
LEG_TREE=""
leg_path_in_the_way() {
    local root="$1" role="$2" branch="$3" repo
    LEG_TREE=""
    role_maps_here "$role" || return 1
    repo="$(leg_repo_for_role "$root" "$role" 2>/dev/null || true)"
    [ -n "$repo" ] || return 1
    [ -e "$repo/.git" ] || return 1
    LEG_TREE="$(leg_tree_path "$root" "$role" "$branch")"
    path_in_the_way "$repo" "$LEG_TREE" "$branch" "$role"
}

# THE GAP A RESUME ITSELF LEAVES. `park` commits the WIP under a fixed subject
# prefix and records how many such commits sit on the tip (`wip_depth`);
# `resume` recreates the worktree at the parked commit and SOFT-RESETS exactly
# those commits away, so a resumed feature's tip is legitimately `<parked>~
# <wip_depth>` with the work back in the working tree. `resume.sh`'s
# `wip_gap_is_ours` is what keeps it from refusing that state, and this is
# the same test: the gap is exactly `wip_depth` commits, every one of them a
# `wip: park …` commit. Read-only (`log -1 --format=%s`).
WIP_PREFIX='wip: park '
wip_gap_is_ours() {
    local repo="$1" from="$2" to="$3" depth="$4" count subject step=0
    is_count "$depth" || return 1
    [ "$depth" -gt 0 ] || return 1
    count="$(g "$repo" rev-list --count "$from..$to" 2>/dev/null || printf 0)"
    [ "$count" = "$depth" ] || return 1
    while [ "$step" -lt "$depth" ]; do
        subject="$(g "$repo" log -1 --format=%s "$to~$step" 2>/dev/null || true)"
        case "$subject" in
        "$WIP_PREFIX"*) ;;
        *) return 1 ;;
        esac
        step=$((step + 1))
    done
    return 0
}

# A WORKTREE ON `<branch>` IN `<repo>`, COUNTED AS `check_parked_leg` COUNTS
# ONE: `worktree_on_branch`'s rc 0 and no other. Its 2 and 3 are the stale
# registrations, which are not worktrees at all — `resume` is BLOCKED by one,
# never satisfied by it — and its 1 is nothing found. Written as a test of its
# own because the first pass asks the same question a leg row ahead of the
# arms below, and the two must count the same thing.
worktree_here() { worktree_on_branch "$1" "$2" >/dev/null; }

# RR2's TWO EXITS, WORDED ONCE, because the verdict they belong to is the
# FEATURE's and they are said twice: on the culprit leg's own line and on
# every sibling's. They are not one exit, and neither of them is the re-park
# every other exception in this layer ends in — a feature that LANDED is a
# record entry to delete, a branch deleted by mistake is a branch to push
# again from where it was parked.
origin_gone_exits() {
    printf "the exits are to delete its \`- branch: %s\` block from the record where the feature landed, or to push the branch again from %s where it went by mistake" "$1" "${2:-the workstation that parked it}"
}

# THE FOUR WAYS RR6 REFUSES, WORDED FOR A LEG THAT IS NOT THE CULPRIT, which
# is why the leg is named: this clause is printed on a SIBLING's line, where
# "that leg" would have no antecedent. `resume.sh`'s RR6 is `[ "$pushed" !=
# true ]` and its own three-armed message is the words `check_parked_leg`'s
# `$unread` carries for the culprit; these are the same four states said about
# somebody else's leg. `true` never reaches here — the caller tests it first —
# and the empty value is two states, told apart by the record's own `$seen`
# the way that function tells them apart.
pushed_verdict() {
    case "$2" in
    false) printf "its \`%s\` leg was parked with --no-push" "$1" ;;
    '')
        if [ -z "$3" ]; then
            printf "the record has no \`pushed:\` for its \`%s\` leg" "$1"
        else
            printf "its \`%s\` leg's \`pushed:\` has no value, which is neither true nor false" "$1"
        fi
        ;;
    *) printf "its \`%s\` leg's \`pushed:\` says '%s', which is neither true nor false" "$1" "$2" ;;
    esac
}

# WHETHER RR1 WILL REFUSE THIS LEG AS MOVED ON, asked of the record's absent
# `parked_commit:` alone — the one reading of RR1 this layer already makes,
# per leg, in the arm below. `resume.sh`'s RR1 is `[ "$remote_tip" !=
# "$parked_commit" ]` and an absent parked commit never equals a tip, so the
# leg is refused wherever origin's tip is. The two conditions under it are the
# two RRs in front of RR1 that this layer can answer: a leg whose repository
# is not a checkout here is none of RR1's business — `resume` refuses the
# whole ESTATE for that, before it reads a feature — and a leg with a worktree
# on the branch here is RR4's, which calls it already recreated. RR2 is not
# tested here: the caller asks it first, in `resume.sh`'s order.
#
# AND `worktree_here` IS A WIDER QUESTION THAN RR4's, WHICH THIS BRANCH IS
# WHAT SAYS SO: RR4 matches the path `collect_legs` computes and no other, so
# a worktree on the branch sitting ANYWHERE ELSE leaves RR1 reached and this
# answer 1 where `resume` refuses. Run on 2026-09-13 against the extension on
# a scratch three-leg estate, the `spec` leg's `parked_commit:` left out and
# its worktree at `$HOME/elsewhere/001-a-thing`: "Error: 001-a-thing (spec
# leg) has moved since it was parked", "REFUSED: 001-a-thing — divergence",
# exit 2, while the same estate with that worktree AT
# `worktrees/001-a-thing/spec` RESUMED, exit 0 — RR4's first arm skipping the
# leg before RR1 is reached. THE READING IS NOT NARROWED HERE, because the
# state is one the arms below already answer and answer with an exit that
# WORKS: the off-path arm names that leg's worktree and the `git worktree
# move` to the computed path, and after that move RR4 skips the leg and the
# feature resumes — the second half of the run above. Narrowing it would
# trade a line about THIS workstation for RR1's re-park, which is a park on
# another one. It is recorded rather than taken, and the line the off-path
# arm prints hedges for exactly this: "at whichever of its own checks this
# leg reaches first".
moved_on_leg() {
    local root="$1" role="$2" branch="$3" commit="$4" repo
    [ -z "$commit" ] || return 1
    repo="$(leg_repo_for_role "$root" "$role" 2>/dev/null || true)"
    [ -n "$repo" ] || return 1
    [ -e "$repo/.git" ] || return 1
    if worktree_here "$repo" "$branch"; then
        return 1
    fi
    return 0
}

# RR4's FIRST ARM, ASKED OF A LEG ROW: is a worktree REGISTERED AT the path
# `collect_legs` computes for this leg, on this branch — which is the one state
# `resume.sh` calls already recreated and `continue`s past, and therefore the
# one state in which RR2, RR1 and RR5 are never reached for that leg.
#
# IT REPLACED `worktree_here`, WHICH IS A WIDER QUESTION (Copilot's third round
# on #27, verbatim: "The `worktree_here` check above delegates to
# `worktree_on_branch`, so it returns true for any live worktree on this
# branch, not just the path RR4 computes. With `--fetch`, an off-path worktree
# plus a successfully fetched missing `origin/<branch>` should reach RR2 in
# `resume`, but this return suppresses the first-pass `gone` verdict and can
# make sibling legs recommend the wrong re-park/move exit"). IT IS REAL: run
# against the extension on 2026-09-13 on a scratch three-leg estate, one leg,
# its worktree at `$HOME/elsewhere/001-a-thing` and its branch deleted on
# origin, `resume.sh` answered "Error: 001-a-thing is no longer on origin in
# the spec leg; that feature was NOT recreated", "REFUSED: 001-a-thing — gone
# from origin", exit 2 — RR4 missing on the path and RR2 refusing — while this
# layer named a `git worktree move`, hiding RR2's two exits behind one that
# does not bring a lost branch back.
already_recreated() {
    local repo="$1" want="$2" branch="$3"
    registered_at "$repo" "$want" || return 1
    [ "$REGISTERED_BRANCH" = "$branch" ] || return 1
    return 0
}

# WHETHER RR2 WILL REFUSE THIS LEG, asked a leg row ahead of the arms below
# because the answer is the FEATURE's: `read_record`'s first pass asks it of
# every leg, and `check_parked_leg` asks the same three things again of the
# leg it has in front of it, where a repository, a worktree and a stale
# registration are already in hand. THE THREE ARE `resume.sh`'s OWN ORDER:
# RR4 comes first and calls a leg whose worktree is already there recreated,
# so a leg with one is not RR2's; the fetch must have WORKED in that leg's
# repository this run, because without one a missing `origin/<branch>` has a
# second and commoner reading; and then the ref itself, which is what RR2
# reads and refuses the whole feature on. A leg whose repository is not a
# checkout here is none of RR2's business either — `resume` refuses the whole
# ESTATE for that, before it reads a feature.
origin_lost_leg() {
    local root="$1" role="$2" branch="$3" repo
    repo="$(leg_repo_for_role "$root" "$role" 2>/dev/null || true)"
    [ -n "$repo" ] || return 1
    [ -e "$repo/.git" ] || return 1
    fetched_ok_at "$repo" || return 1
    if has_ref "$repo" "refs/remotes/origin/$branch"; then
        return 1
    fi
    if already_recreated "$repo" "$(leg_tree_path "$root" "$role" "$branch")" "$branch"; then
        return 1
    fi
    return 0
}

# THE EXIT FOR A WORKTREE THAT IS NOT AT THE COMPUTED PATH, worded ONCE
# because two readings name it: the leg's own line below, and — through
# `read_record`'s first pass — every sibling leg of the feature it costs.
#
# WHAT THE FIRST TEST ANSWERS IS THE SINGLE-SHAPE `repo` LEG, whose repository
# IS the root: there `worktree_on_branch` can answer with the root's own
# checkout, and `git worktree move` refuses one — "'…' is a main working tree"
# (git 2.43, 2026-09-12) — so naming that move would be naming an exit that
# does not work. It is NOT a mounted leg's own checkout: git 2.43 reports an
# absorbed submodule's main worktree at `<root>/.git/modules/<leg>` and never
# at `<root>/<leg>` (verified the same day on a real submodule), so that path
# is one `worktree_is_usable` calls a stale registration long before this arm
# is reached. The directory test answers the same root reached by a spelling
# git canonicalises differently, where the string comparison misses.
#
# AND A DEAD REGISTRATION AT THE DESTINATION MAKES THE PRUNE THE FIRST HALF:
# `git worktree move` refuses a destination git still holds with nothing on
# disk there, in the same sentence `git worktree add` refuses it — "is a
# missing but already registered worktree; use 'move -f' to override, or
# 'prune' or 'remove' to clear" — so the move alone is an exit that was run
# and did not work.
off_path_exit() {
    local repo="$1" role="$2" tree="$3" want="$4" held="$5"
    local resolved="" unlock="" prune="" clear_link="" clear_parent=""
    resolved="$(physical_path "$RECORD_WORKTREE_ROOT")"
    [ "$resolved" != "$RECORD_WORKTREE_ROOT" ] || resolved=""
    # AND THE SOURCE'S OWN LOCK GOES IN FRONT OF THE MOVE (Copilot's fourth
    # round on #27, verbatim: "When the off-path worktree itself is locked,
    # `worktree_on_branch` still returns it as live, but this branch only
    # checks `held` for the destination and prints `git worktree move <tree>
    # <want>`. Git refuses moving a locked source, so the recovery command
    # cannot run"). RUN on 2026-09-13: `git worktree move` with a locked
    # source answered "fatal: cannot move a locked working tree; use 'move -f
    # -f' to override or unlock first", and after `git worktree unlock` the
    # same move exited 0. `$held` is the DESTINATION's registration; this is
    # the source's, and it is the same pair the in-the-way arm already names.
    if registered_at "$repo" "$tree" && [ "$REGISTERED_LOCKED" -eq 1 ]; then
        unlock="git -C $(sh_operand "$repo") worktree unlock $(sh_operand "$tree") && "
    fi
    # AND EVERY STEP THE DESTINATION WANTS IS IN THE COMMAND, not in the prose
    # beside it (Copilot's fifth round on #27, verbatim: "In the `held == 2`
    # recovery, the required destination prune appears only in the prose; the
    # copy-pastable command is `${unlock}mkdir -p ... && git worktree move ...`
    # and never runs `git worktree prune`. With a live off-path source and a
    # stale destination registration, the advertised command therefore still
    # fails at `worktree move`"). It is right, and it is this file's own rule:
    # an exit a person can run is one they can paste. The prune clears the
    # registration git holds at the destination; the `mv` clears a DANGLING
    # SYMLINK left there, which `[ -e ]` looks through and `git worktree add`
    # refuses all the same (the round before this, run 2026-09-13) — so both
    # go in front of the `mkdir -p`, in the order the destination needs them.
    [ "$held" -ne 2 ] ||
        prune="git -C $(sh_operand "$repo") worktree prune && "
    if [ ! -e "$want" ] && [ -L "$want" ]; then
        # AND IT NAMES NO DESTINATION OF ITS OWN (Copilot's sixth round on
        # #27: "The dangling-link cleanup hard-codes `$want.aside` as the `mv`
        # destination. If that path already exists as a regular file, the
        # copy-paste command overwrites it, contradicting the surrounding
        # guarantee that status never asks the user to overwrite an unrelated
        # path"). Right, and every other `mv` this file prints has said
        # `<a path of your choosing>` since the commit that first named one.
        clear_link="mv $(sh_operand "$want") <a path of your choosing> && "
    fi
    # AND THE PARENT THE `mkdir -p` MUST MAKE (Copilot's seventh round on #27:
    # "When an off-path worktree exists but the computed destination's
    # feature-directory parent is a regular file, this branch emits `mkdir -p
    # <parent> && git worktree move ...`. `resume` cannot create that parent,
    # so `mkdir` exits with "Not a directory" and the move never runs"). RUN on
    # 2026-09-13: `mkdir -p <file>/spec` answers "mkdir: cannot create
    # directory '…': Not a directory". So the parent is moved aside first, in
    # the same sequence, for the same reason the link above it is.
    if [ "$want" != "$(parent_dir "$want")" ] &&
        dest_exists "$(parent_dir "$want")" &&
        [ ! -d "$(parent_dir "$want")" ]; then
        clear_parent="mv $(sh_operand "$(parent_dir "$want")") <a path of your choosing> && "
    fi
    # THE PATH TEST IS MADE PHYSICAL for the reason `path_in_the_way`'s twin
    # is: `$tree` is `git worktree list`'s, which is always physical, and
    # `$repo` is `leg_repo_for_role`'s string on `$root`, which is not.
    if [ "$tree" = "$(physical_path "$repo")" ] || [ -d "$tree/.git" ]; then
        printf '%s\n' "$tree is the $role leg's OWN checkout rather than a worktree of it, and no \`worktree move\` moves a main working tree, so there is nothing here to move: neither verb sees this feature while that checkout is on that branch"
    elif dot_mount "$want"; then
        printf '%s\n' "there is no command that puts it where \`resume\` looks: this leg is declared \`path: \".\"\` in \`project.yaml\`, so the path \`collect_legs\` computes for it ends in \`/.\` — \`mv\` refuses such an operand outright and \`git worktree move\` lands the worktree INSIDE it — and \`resume\` matches that string against no worktree git can spell, so it refuses this feature after every run, including one of its own. What answers it is a \`collect_legs\` that computes \`<worktree_root>/<branch>\` for a leg declared at the root, which is the standard's and not this command's"
    # AND THE SYMLINKED ROOT COMES BEFORE EVERY ONE OF THEM (Copilot's fifth
    # round on #27: "The symlink-root safeguard is bypassed when the
    # destination also has a prunable registration: `held == 2` selects this
    # branch before `has_symlink_component`. It then recommends pruning and
    # moving the off-path worktree to the lexical symlink path; Git can
    # complete that move but records the physical target, so `resume` still
    # cannot match the lexical path and refuses again"). Right: no sequence
    # ending in a move to that path works, whatever is cleared out of the way
    # first, so the spelling is the only exit and it is named before any of
    # them. `dot_mount` and the leg's own checkout stay above it, because
    # neither is a path a spelling would fix.
    # AND OF THE WHOLE COMPUTED PATH, not the root alone (Copilot's sixth
    # round on #27: "If an existing `$WORKTREE_ROOT/$branch` (or a parent from
    # a declared mount) is a symlink while the final `$want` is absent, this
    # branch advertises `mkdir -p ... && git worktree move ...`; both commands
    # follow the link and Git records the physical target, while `resume`
    # later compares the lexical `$want` and still refuses at RR3"). Right,
    # and it is the same fault one level down: `has_symlink_component` walks
    # every prefix, so asking it of the destination's PARENT asks it of the
    # root and of every directory between the root and the mount. The final
    # component is deliberately left out — a link THERE is the leftover the
    # `mv` above clears, not a spelling the person has to correct.
    elif has_symlink_component "$(parent_dir "$want")"; then
        # AND NO MOVE WHERE THE WORKTREE ROOT ITSELF IS A LINK, because the
        # move succeeds and changes nothing (Copilot's third round on #27,
        # verbatim: "`off_path_exit` still advertises this move when
        # `worktree_root` is a symlink. `record_worktree_root` deliberately
        # preserves that lexical spelling, while Git reports the physical
        # worktree path; moving an off-path worktree to this `want` therefore
        # leaves `registered_at` unable to match it, so the next `resume` still
        # refuses at RR3"). RUN, end to end, on 2026-09-13: the move exited 0,
        # git then held the worktree at the PHYSICAL `…/real-wt/001-a-thing/
        # spec`, and `resume.sh` answered "Error: '…/wtlink/001-a-thing/spec'
        # exists and is not a registered worktree of the spec leg", "REFUSED:
        # 001-a-thing — an unrelated path is in the way", exit 2. THE EXIT
        # THAT DOES WORK is the spelling: the same estate with `worktree_root`
        # set to the path git spells RESUMED the feature, exit 0, on the same
        # run — so that is what this names, and it is the person's to write.
        printf '%s\n' "no \`worktree move\` fixes this one, because the path it would land in is reached through a symlink: both verbs compute $want and git registers whatever is made there under the path that link resolves to, so \`resume\` matches nothing however the worktree is moved — $(symlink_exit "$SYMLINK_COMPONENT"), and the \`git worktree move\` this line would otherwise name becomes one that works"
    else
        printf '%s\n' "move it where both look: \`${unlock}${prune}${clear_link}${clear_parent}mkdir -p $(sh_operand "$(parent_dir "$want")") && git -C $(sh_operand "$repo") worktree move $(sh_operand "$tree") $(sh_operand "$want")\`, which is yours to run — \`worktree move\` creates no parent directory of its own${unlock:+, and it refuses a LOCKED source until the unlock in front of it}${prune:+, and it refuses a destination git still holds registered (\"a missing but already registered worktree\") until the prune}${clear_link:+, and \`git worktree add\` refuses the dangling symlink at that path (\"already exists\") whatever \`[ -e ]\` says of it}${clear_parent:+, and the \`mkdir -p\` cannot make a parent that is not a directory (\"Not a directory\"), which is why that one is moved first}"
    fi
}

# One recorded leg against disk: the four states above, read in the leg's own
# repository. `$9` is `record_rows`'s sixth field — the word `pushed` where
# the record carried that key for this leg, empty where it did not — and it
# is the only thing that tells an ABSENT `pushed:` from a bare one, since
# both leave `$pushed` empty.
check_parked_leg() {
    local root="$1" branch="$2" role="$3" commit="$4" pushed="$5" parked_at="$6" parked_on="$7" depth="$8" seen="$9"
    # `${10}` and not a tenth name in the line above, because that line is
    # already the widest in this file; `:-` because `set -u` is on and the
    # verdict is the one argument a caller may leave off. `${11}` says WHICH
    # refusal that verdict is — empty for the ones `collect_legs` makes
    # before any RR runs, `pushed`, `inway`, `gone` or `moved` for the RR that
    # refuses first in the record's order, and `add` for the `git worktree
    # add` the other end makes last of all — because they differ in both
    # directions: RR2's exits are its own, and the leg RR2 refuses for still
    # says so in RR2's words, where a leg `collect_legs` throws out has said
    # everything it has to say on the line above this one.
    #
    # AND `${12}` IS AN EXIT THE CALLER ALREADY HAS, for the one verdict this
    # function cannot word for itself. Every `collect_legs` verdict is settled
    # by a park that parks the feature THROUGH, RR6's and RR1's by the same
    # park, and RR2's by one of its own two, all of which this function can
    # say from the leg in front of it; a verdict about a PATH IN THE WAY turns
    # on what git holds AT that path in ANOTHER leg's repository, which the
    # first pass has already looked at and this function has not — a park
    # somewhere else moves none of it, so the leg that costs the feature and
    # every sibling leg carry the same exit, and it is the one that works.
    # AND `${13}` AND `${14}` ARE WHICH LEG THIS IS AND WHICH LEG THE VERDICT
    # BELONGS TO, both ordinals within the feature, because the two arms below
    # that read a PATH may only speak for the leg `resume` stopped at
    # (Copilot's first round on #27, verbatim: "If a later leg also has an
    # obstruction, its own refusal/exit is reported even though `resume` stops
    # at the earlier leg and the first-pass verdict is meant to be carried to
    # all siblings. Track the refusing leg and only run these path checks for
    # that leg"). IT IS REAL: run against the extension on 2026-09-13 on a
    # scratch three-leg estate with a stray DIRECTORY at the `spec` leg's
    # computed path and a worktree of another branch at the `code` leg's,
    # `resume.sh` answered "Error: 'worktrees/001-a-thing/spec' exists and is
    # not a registered worktree of the spec leg", "REFUSED: 001-a-thing — an
    # unrelated path is in the way", exit 2, ONCE — while this layer drew both
    # legs' lines, the second quoting a REFUSED reason that run never printed.
    local refused="${10:-}" refused_kind="${11:-}" refused_exit="${12:-}"
    local leg_no="${13:-}" refused_leg="${14:-}"
    # WHETHER THE LEG IN FRONT OF THIS FUNCTION IS THE ONE THE RUN STOPPED AT.
    # True where there is no verdict at all — then no leg refuses and each
    # speaks for itself — and where the verdict's ordinal is this leg's. It
    # SUBSUMES the RR6 carve-out the rebase needed: a leg whose own `pushed:`
    # is not `true` IS the leg RR6 stops at, so its own line still stands
    # beside the `pushed:` arms, and a later leg's does not.
    local mine=0
    { [ -n "$refused" ] && [ "$refused_leg" != "$leg_no" ]; } || mine=1
    local repo tree="" ghost="" ghost_is="" clear="" out rc=0 tip n unread="" refuses="" where=""
    local gone_exits="" gone_caveat="" shape_line="" shape_says="" settles="" exits=""
    local want="" move="" held=0 ghost_moved=0
    # A LEG THE RECORD GIVES NO ROLE IS ANSWERED BEFORE THE ROOT IS ASKED
    # ABOUT IT, and it is not the arm below (the independent review of #20,
    # 2026-09-11, note 8, recorded there as out of scope). `record_rows` used
    # to drop such a leg — `[ -n "$role" ]` at both emit sites — so this layer
    # said NOTHING about it, and the other end refuses the whole feature for
    # it: `resume.sh`'s `collect_legs` maps every leg's role onto this
    # checkout, `spec`, `code` and `repo` being all it knows, and its `*)
    # return 1` takes the FEATURE with it — every other leg of that feature
    # included, verified the same day against a record whose good `repo` leg
    # sat beside an empty-role one. The words it refuses in are about the
    # SHAPE, which is not what is wrong: "Error: 001-a-thing was parked from a
    # single project and this checkout is single; that feature was NOT
    # recreated", a sentence that contradicts itself, and this line is what
    # explains it in advance.
    #
    # IT IS ANSWERED FIRST BECAUSE `leg_repo_for_role` WOULD ANSWER IT WRONG:
    # asked for the empty role it falls past the `repo | assembly` case and
    # past every `project.yaml` role, to `[ -d "$root/$want" ]` — which for an
    # empty `$want` is `[ -d "$root/" ]`, always true — and hands back the
    # ROOT. The leg would then be read as if the record had named the `repo`
    # leg, which is a claim about a leg the record does not name. And it is
    # not the "does not mount here" arm either: those words say this root has
    # no such leg, which is a true thing to say about `role: nope` and a
    # misleading thing to say about a record that names no role at all — the
    # fix is not to mount anything, and the refusal is of the whole feature.
    #
    # THE EXIT IS A PARK THAT PARKS IT, as it is for the states above, and for
    # the same reason at the same end: `emit_recorded_feature` carries a
    # REFUSED feature's legs forward out of the loaded manifest, and `role` is
    # the ONE leg key `workspace_write_manifest` does not drop when its value
    # is empty (`if value == "" and out_key != "role": continue`), so a park
    # that refuses this feature writes the leg back as `- role: ""` — which
    # both readers unquote to the empty string again. Only a park that parks
    # the feature THROUGH writes the roles this checkout's own shape has.
    if [ -z "$role" ]; then
        where="the record does not say where"
        [ -z "$parked_on" ] || where="the record says $parked_on"
        finding "parked feature $branch: a leg of it in the record has no role; \`resume\` maps each leg's role onto this checkout and refuses the WHOLE feature — the other legs with it — where one does not map, reporting it as a shape mismatch, so nothing here brings it back — park that feature again from the workstation that has it ($where), which writes the roles this checkout's own shape has"
        return 0
    fi
    # AND A ROLE THIS CHECKOUT'S SHAPE DOES NOT HAVE COSTS THE WHOLE FEATURE
    # TOO, WHICH THE LINE BELOW LEFT OUT ALONG WITH THE EXIT (the independent
    # review of #21, 2026-09-11, recorded there as nobody's ruling). "The
    # record names a leg this root does not mount here" is true and stops
    # there, and it reads as ONE leg going unread — while `resume.sh`'s
    # `collect_legs` maps EVERY leg's role onto this checkout (`spec` and
    # `code` only where `REPO_SHAPE` is three-leg, `repo` only where it is
    # single, `*) return 1` for every other word) and one that does not map
    # takes the feature with it, its good legs included. Run on 2026-09-11
    # against a scratch single-shape estate, `role: nope` answered "Error:
    # 001-a-thing was parked from a single project and this checkout is
    # single; that feature was NOT recreated", "REFUSED: 001-a-thing — shape
    # mismatch", exit 2 — the same sentence about the SHAPE that says nothing
    # about the role, and the same answer with a good `repo` leg beside it.
    # That is the refusal the empty-role arm above explains in advance, so
    # this arm explains it in the same words and the same order.
    #
    # WHICH ROLES ARE CERTAINLY REFUSED, AND WHICH ARE ONLY UNMOUNTED. The
    # arm below fired on two different states and now says the refusal for
    # only one of them, because only one of them is one `collect_legs`
    # certainly refuses. `leg_repo_for_role` answers the root for `repo` and
    # `assembly`, else the path `project.yaml` gives the role, else a folder
    # named for it, else the role's own name where the manifest declares a
    # `spec` or a `code` leg and gives it no `path:` — the default
    # `_shape_record_leg` gives it — else NOTHING, and nothing is the state
    # that proves it: `git-common.sh`'s `load_repo_shape` calls a checkout
    # `three-leg` only where `project.yaml` says `kind: project-manifest`
    # AND `schema: project-repo-schema` AND declares BOTH a `spec` and a
    # `code` LEG — the leg, not its `path:`, which `_shape_record_leg`
    # defaults to the role's own name. So a role this root has no repository
    # for is a role `collect_legs` cannot map: a `spec` or a `code` this
    # manifest declares no leg for, which is a manifest that cannot be
    # three-leg, or any other word at its `*) return 1` — whether it is a
    # misspelling (`nope`) or another shape's role (`code` here, `spec` in
    # the family fixture's members, which declare an `assembly` leg and no
    # other). Both were verified on 2026-09-11 against a scratch
    # single-shape estate: "shape mismatch", exit 2, the whole feature.
    #
    # THE OTHER STATE IS A LEG THIS ROOT MOUNTS THAT IS NOT A CHECKOUT — a
    # submodule that was never fetched, which is a directory with no `.git`
    # in it, the state `git-common.sh`'s own `shape_leg_is_checkout` guards,
    # and, since `leg_repo_for_role` learned the loader's default, a `spec`
    # or a `code` this manifest declares whose path is not on disk at all.
    # `collect_legs` maps that role by NAME onto paths `load_repo_shape`
    # computed from `project.yaml`, and never looks at the directory, so in a
    # genuinely three-leg root it maps `spec` and moves on and the feature is
    # NOT refused for the shape. Claiming the refusal there would be this
    # layer inventing a `resume.sh` line, so those words keep the sentence
    # they had: what is true of that state is that the leg is not mounted
    # here, and it is `make bootstrap`'s to mount.
    #
    # THE EXIT IS WORDED FOR BOTH KINDS OF UNMAPPABLE ROLE: a park from the
    # workstation that has the feature writes the roles ITS shape has, which
    # is what settles a misspelling; where the role really is another
    # shape's, the record is right and this checkout is not where that
    # feature comes back, so the line says so rather than promising that a
    # re-park will make this root mount it. It is `role_shape_exits` that
    # words it, because a role of NEITHER shape needs the other half of what
    # the help and the README say and this arm reaches those words too — a
    # `spec` or a `code` this manifest declares no leg for is another shape's
    # role, and any other word that matches no directory here is neither's.
    #
    # WHAT THIS ARM DOES NOT SEE, and what is therefore still unsaid: a role
    # this root DOES mount that `collect_legs` refuses anyway — `assembly`,
    # `repo` in a root `load_repo_shape` calls three-leg (one project checked
    # out at two shapes, the commonest mismatch there is), `spec` in a root
    # whose `project.yaml` declares no `code` leg, or any other word that
    # matches a directory under the root — for which `leg_repo_for_role`
    # hands back a repository, so the leg is read against disk and nothing
    # here mentions the refusal (`role: assembly` in that same scratch
    # estate: "shape mismatch", exit 2, the same day). Reading that one
    # means computing this root's shape the way `load_repo_shape` does and
    # holding the record's `shape:` against it, which is a reading of its
    # own and is recorded in this commit's message, not taken here.
    repo="$(leg_repo_for_role "$root" "$role" || true)"
    if [ -z "$repo" ]; then
        where="the record does not say where"
        [ -z "$parked_on" ] || where="the record says $parked_on"
        finding "parked feature $branch ($role leg): the record names a leg this root does not mount here; \`resume\` maps each leg's role onto this checkout and refuses the WHOLE feature — the other legs with it — where one does not map, reporting it as a shape mismatch, so nothing here brings it back — $(role_shape_exits "$role" "$where")"
        return 0
    fi
    # AND A ROLE THIS ROOT MOUNTS THAT `collect_legs` REFUSES ANYWAY IS THE
    # STATE THE ARM ABOVE CANNOT SEE, which is what that arm's own note
    # recorded and left (the feature-level follow-up, 2026-09-11): "a role
    # this root DOES mount that `collect_legs` refuses anyway — `assembly`,
    # or `spec` in a root whose `project.yaml` declares no `code` leg, which
    # `load_repo_shape` therefore calls `single` — for which
    # `leg_repo_for_role` hands back a repository, so the leg is read against
    # disk and nothing here mentions the refusal". `read_root_shape` computes
    # the shape `collect_legs` maps against, so the question can be asked now,
    # and it is asked of EVERY leg: `spec` and `code` map only onto a
    # three-leg checkout, `repo` only onto a single one, and every other word
    # maps onto neither — `assembly`, a role the three-leg shape HAS, and any
    # word that merely matches a directory under the root, which
    # `leg_repo_for_role`'s last case answers by folder name. Run against the
    # extension on 2026-09-12, on scratch estates shaped like this suite's own
    # fixtures: `role: assembly` and `role: spec` in a single root that mounts
    # an `assembly` leg at `.` and a `spec` leg at `spec/` both answered
    # "Error: 001-a-thing was parked from a three-leg project and this
    # checkout is single; that feature was NOT recreated", "REFUSED:
    # 001-a-thing — shape mismatch", exit 2; `role: repo` in a real three-leg
    # root — `kind: project-manifest`, `schema: project-repo-schema`, both
    # legs mounted — answered the same with "and this checkout is three-leg",
    # which is ONE PROJECT CHECKED OUT AT TWO SHAPES and the commonest way
    # into this state; and `role: spec` in that root RESUMED.
    #
    # WHAT IS WORTH SAYING ABOUT A SHAPE MISMATCH IS THE SHAPE, which is
    # exactly what `resume` does not say: its sentence names the record's
    # `shape:` and this checkout's and stops — no role, and where the record
    # agrees it contradicts itself, "parked from a single project and this
    # checkout is single". So this line names the ROLE, the shape this
    # checkout is AND WHY it is that shape in `load_repo_shape`'s own terms,
    # and then the refusal and the exits the two arms above already word. THE
    # RECORD'S OWN `shape:` IS HELD AGAINST IT AND NOT JUDGED: a record that
    # calls the project three-leg where this checkout is single is either a
    # record from a workstation whose checkout is the three-leg one, or a
    # record written before this project's shape changed, and nothing in
    # either file says which — so the disagreement is named and the exit is
    # the one that is right under both readings.
    #
    # IT IS SAID BEFORE THE MOUNT ARM BELOW, which is the same disk read more
    # narrowly. A leg this root mounts that is NOT A CHECKOUT wants `make
    # bootstrap` — true, and beside the point where `collect_legs` refuses the
    # whole feature whatever that directory holds, because it maps roles by
    # NAME onto paths `load_repo_shape` computed and never looks at them.
    # Where the shape DOES map the role that arm keeps its sentence, and its
    # silence about any refusal, untouched: in a genuinely three-leg root an
    # unfetched `spec/` is not a shape mismatch at all — `resume.sh` refuses
    # the whole ESTATE before it reads a feature, "the spec leg 'spec' is not
    # an initialised Git checkout … Run 'git submodule update --init' (or
    # 'make bootstrap')", exit 1 — which is that arm's own advice and none of
    # this arm's business.
    if ! role_maps_here "$role"; then
        where="the record does not say where"
        [ -z "$parked_on" ] || where="the record says $parked_on"
        if [ -n "$RECORD_SHAPE" ] && [ "$RECORD_SHAPE" != "$ROOT_SHAPE" ]; then
            shape_says="; the record calls the project $RECORD_SHAPE where this checkout is $ROOT_SHAPE, which is the disagreement \`resume\` reports and not one this layer can settle"
        fi
        # AND THE DISAGREEMENT CLAUSE GOES LAST, BECAUSE THE EXIT'S FIRST
        # WORD LEANS ON THE CLAUSE BEFORE IT (the independent verification of
        # this branch, 2026-09-12). `role_shape_exits`'s `*)` arm opens "nor
        # does any other checkout, of either shape", which answers "nothing
        # here brings it back" — and with the disagreement spliced between the
        # two the line read "… not one this layer can settle — nor does any
        # other checkout", where the "nor" has nothing left to deny. Both
        # halves are true in either order, and only one of them is an
        # antecedent, so the antecedent keeps its place: the shape the record
        # names and the shape this checkout is follow "which is this one only
        # where the two agree", which is the clause they are about.
        shape_line="parked feature $branch ($role leg): $(role_shape_long "$role"), so it refuses the WHOLE feature — the other legs with it — reporting it as a shape mismatch, and nothing here brings it back — $(role_shape_exits "$role" "$where")$shape_says"
        # SAID HERE FOR A LEG THAT IS NOT A CHECKOUT, because the arm below
        # would say the smaller thing instead and there is nothing left to
        # read in a directory with no `.git` in it; said after the worktree
        # probe for every other leg, where what is left to read decides what
        # follows it.
        if [ ! -e "$repo/.git" ]; then
            finding "$shape_line"
            return 0
        fi
    fi
    if [ ! -e "$repo/.git" ]; then
        finding "parked feature $branch ($role leg): the record names a leg this root does not mount here"
        return 0
    fi
    out="$(worktree_on_branch "$repo" "$branch")" || rc=$?
    case "$rc" in
    0) tree="$out" ;;
    2)
        ghost="$out"
        ghost_is="a stale worktree registration for it is still recorded at $ghost"
        clear="\`git -C $(sh_operand "$repo") worktree prune\`"
        ;;
    3)
        ghost="$out"
        ghost_is="a stale worktree registration for it is still recorded at $ghost and LOCKED, which \`worktree prune\` skips"
        clear="\`git -C $(sh_operand "$repo") worktree unlock $(sh_operand "$ghost")\`, then \`git -C $(sh_operand "$repo") worktree prune\`"
        ;;
    esac
    # AND A REGISTRATION AT THE COMPUTED PATH IS THE SAME STATE READ FROM THE
    # PATH'S SIDE, which the arms above cannot see: they ask
    # `worktree_on_branch`, which looks for the RECORDED BRANCH and never at
    # the path. A PRUNABLE block at that path for ANOTHER branch with nothing
    # on disk is missed by RR4 — `load_git_worktrees` drops a prunable block —
    # and by RR3, whose `[ -e "$tree" ]` finds nothing, so `resume` runs every
    # check it has and dies on the LAST thing it does: "fatal: '…' is a
    # missing but already registered worktree; use 'add -f' to override, or
    # 'prune' or 'remove' to clear", "REFUSED: 001-a-thing — git worktree add
    # failed", exit 2 (run against the extension on 2026-09-12, on the same
    # scratch estates). Its REFUSED line is neither RR3's nor RR4's, and what
    # settles it is the prune the arms above already name — so it is said in
    # their words, on their line, and the `resume` after it is the one that
    # works once the path is clear.
    if [ -z "$ghost" ] && [ -z "$tree" ] && role_maps_here "$role"; then
        want="$(leg_tree_path "$root" "$role" "$branch")"
        held=0
        registered_at "$repo" "$want" || held=$?
        if [ "$held" -eq 2 ] && [ ! -e "$want" ]; then
            ghost="$want"
            if [ -n "$REGISTERED_BRANCH" ]; then
                ghost_is="a stale worktree registration for \`$REGISTERED_BRANCH\` is still recorded at the $ghost \`resume\` computes for this leg, with nothing on disk there"
            else
                ghost_is="a stale worktree registration with a detached head is still recorded at the $ghost \`resume\` computes for this leg, with nothing on disk there"
            fi
            clear="\`git -C $(sh_operand "$repo") worktree prune\`"
        # AND A DANGLING SYMLINK IS THE SAME REFUSAL WITH NOTHING REGISTERED
        # AT ALL (Copilot's fourth round on #27). `[ -e ]` follows the link and
        # finds nothing, so RR3 passes at BOTH ends — `resume.sh`'s RR3 is the
        # same `[ -e "$tree" ]` — and the run dies on the last thing it does:
        # `git worktree add` answered "fatal: '…' already exists" for exactly
        # this path on 2026-09-13. It is neither RR3's nor RR4's, its REFUSED
        # line is the add's, and what settles it is not a prune but the `mv`
        # every other leftover at that path wants, because nothing is
        # registered there to clear.
        elif [ "$held" -ne 0 ] && [ ! -e "$want" ] && [ -L "$want" ]; then
            ghost="$want"
            ghost_is="a dangling symlink is at the $ghost \`resume\` computes for this leg, which \`[ -e ]\` looks straight through and \`git worktree add\` refuses all the same (\"already exists\")"
            clear="a move of it aside (\`mv $(sh_operand "$want") <a path of your choosing>\`)"
            ghost_moved=1
        # AND THE FEATURE DIRECTORY `resume` MAKES FIRST IS A PATH TOO
        # (Copilot's sixth round on #27, verbatim: "`dest_exists` only checks
        # the final component. For a three-leg destination, `want` is
        # `<worktree_root>/<branch>/<mount>`, but `resume` first runs `mkdir -p
        # <worktree_root>/<branch>`; if that feature directory is a regular
        # file or dangling symlink, the final path appears absent and status
        # can recommend `resume` … even though the mkdir fails before the
        # worktree is added"). RUN on 2026-09-13: `mkdir -p <file>/spec`
        # answers "cannot create directory '…': Not a directory", so the run
        # stops before any leg is added — earlier even than the add, and in
        # `resume.sh`'s own words rather than git's. It is read here because
        # it is the same shape: a path in the way of a step no RR looks at,
        # cleared by the same `mv`.
        elif [ "$want" != "$(parent_dir "$want")" ] &&
            dest_exists "$(parent_dir "$want")" &&
            [ ! -d "$(parent_dir "$want")" ]; then
            ghost="$(parent_dir "$want")"
            ghost_is="the $ghost \`resume\` makes the feature's worktrees under is not a directory, and \`resume\` runs \`mkdir -p\` on it before it adds any leg (\"cannot create directory …: Not a directory\")"
            clear="a move of it aside (\`mv $(sh_operand "$ghost") <a path of your choosing>\`)"
            ghost_moved=1
        fi
    fi
    # WHAT THE PRUNE LEAVES BEHIND is the second half of that exit where git
    # called the registration stale with something still on disk: the prune
    # clears the registration and touches nothing on disk, and `git worktree
    # add` then refuses the path in its own words, "already exists". Whatever
    # is there holds whatever the worktree held, so it is moved aside rather
    # than deleted here or anywhere else.
    #
    # AND IT IS ASKED WITH `-e`, NOT `-d` (Copilot's second round on #27):
    # "For a stale registration on the recorded branch whose old worktree path
    # is now a regular file, the `-d` check is false, so status tells the user
    # to prune and then resume even though the file remains and `resume` still
    # rejects the existing destination." Run on 2026-09-13 — a worktree
    # registered, its directory replaced by a FILE, git calling the block
    # `prunable`, the prune clearing it — `git worktree add` at that path
    # answered "fatal: '…' already exists". The clause says "whatever" now,
    # because the reason is the path existing and not what kind of path it is.
    if [ -n "$ghost" ] && [ "$ghost_moved" -eq 0 ] && dest_exists "$ghost"; then
        ghost_is="$ghost_is, whose path is still on disk and is no longer a worktree"
        clear="$clear and a move of what is there aside (\`git worktree add\` refuses a path that \"already exists\")"
    fi
    # AND THE DESTINATION IS ASKED EVEN WHERE THE ARMS ABOVE FOUND A
    # REGISTRATION SOMEWHERE ELSE (Copilot's fifth round on #27, verbatim:
    # "Restricting this probe to `[ -z "$ghost" ]` misses a destination
    # obstruction whenever the recorded branch has a stale registration at some
    # other path … so the destination is never examined and the report offers
    # `resume` even though its final `git worktree add` is blocked by the
    # destination registration"). THE PRUNE ALREADY CLEARS BOTH — it walks
    # every registration in the repository, not one — so the exit those arms
    # name is not wrong; what the prune does NOT do is move anything on disk,
    # and a leftover at the DESTINATION is what `git worktree add` refuses
    # after it. So the path is named where the two are not the same path.
    if [ -n "$ghost" ] && [ -z "$tree" ] && role_maps_here "$role"; then
        want="$(leg_tree_path "$root" "$role" "$branch")"
        if [ "$want" != "$ghost" ] && dest_exists "$want"; then
            clear="$clear, and a move aside of what is at the $want \`resume\` computes for this leg, which the prune does not touch and \`git worktree add\` refuses for existing"
        # AND THE PARENT IS A PATH TOO (Copilot's seventh round on #27: "If a
        # stale registration exists elsewhere and the three-leg feature
        # directory (`parent_dir "$want"`) is a regular file or dangling
        # symlink, the final path is absent, so the report says `prune`, then
        # `resume`; after pruning, `resume` still fails its `mkdir -p` before
        # adding any worktree"). The prune touches nothing on disk either way.
        elif [ "$want" != "$(parent_dir "$want")" ] &&
            [ "$(parent_dir "$want")" != "$ghost" ] &&
            dest_exists "$(parent_dir "$want")" &&
            [ ! -d "$(parent_dir "$want")" ]; then
            clear="$clear, and a move aside of the $(parent_dir "$want") \`resume\` makes the feature's worktrees under, which is not a directory and which its \`mkdir -p\` refuses before it adds any leg"
        fi
    fi
    # THE SHAPE REFUSAL IS SAID ONCE, AND WHAT IS LEFT TO READ DECIDES
    # WHETHER ANYTHING FOLLOWS IT. With a worktree here the arms below are
    # about THIS workstation's own work — moved on, behind, diverged, a parked
    # commit that is not here — and they are true whatever `collect_legs` does
    # with the feature, so they run and the line above sits in front of them.
    # With NO worktree here they are the arms that answer "what would `resume`
    # do", and the line above has answered it: the feature's verdict below
    # would say the same thing in shorter words, the `--no-push` and `pushed:`
    # arms would name a refusal that never runs because `collect_legs` refuses
    # first, and a `worktree prune` cannot make `resume` recreate a feature it
    # throws out. So the leg ends here, as it does for the two arms above that
    # read the record's roles.
    if [ -n "$shape_line" ]; then
        finding "$shape_line"
        [ -n "$tree" ] || return 0
    fi
    # A STALE REGISTRATION IS NAMED BEFORE THE EXIT IT BLOCKS, and what
    # clears it before that, because this layer reads the record THE WAY
    # `resume.sh` WILL JUDGE IT and `resume.sh` judges this one badly: its
    # `leg_registered_at` matches on the registered PATH alone — fed by
    # `git-common.sh`'s `load_git_worktrees`, which takes `worktree list
    # --porcelain -z` raw, with no test that the directory is there — so a
    # stale block AT the path it computes reads as already done ("worktree
    # already registered at …; left as it is") and nothing is recreated,
    # while a stale block at ANOTHER path leaves its plain `git worktree add`
    # to be refused ("already used by worktree at …"). `resume.sh` names the
    # hazard itself, where it rolls back what it created: "a phantom
    # registration a later resume reads as 'already registered' for a path
    # that is not there". Naming `resume` alone would be naming an exit that
    # does not work — and the house rule is that the exit named is one that
    # does, which is also why the LOCKED registration gets the `unlock` in
    # front of the prune that would otherwise skip it (Copilot on #18).
    #
    # THE RECORD'S `pushed:` HAS THREE READINGS, NOT TWO (the second
    # independent review of #18, 2026-09-11, note (d)). `park.sh` writes
    # `true` or `false` for every leg it parks and carries an existing entry
    # forward untouched for one it refuses — `workspace_write_manifest` drops
    # a key whose value is empty, so it never writes `pushed:` bare — and
    # anything else is a hand-edit or a bad merge of the record. Read as
    # `false` it would be a `--no-push` claim nobody made; read as `true`,
    # which is what every arm below did with it, `pushed: maybe` and a bare
    # `pushed:` alike falling through the `= false` tests, it became "the
    # record says it was pushed". A READER NEVER GUESSES, so it is named for
    # what it is, and `$unread` non-empty is that state, in the words each
    # finding needs. THE KEY MISSING ALTOGETHER is not this case, and when
    # these lines were written it reached this function not at all:
    # `record_rows` emitted a leg's row AT its `pushed:` line, so a leg
    # without one had no row. It does now, and the paragraph after next is
    # what it means.
    #
    # AND `resume` IS NOT SILENT ABOUT IT, which these lines have to say
    # because this layer reads the record THE WAY `resume.sh` WILL JUDGE IT:
    # `resume.sh`'s RR6 is `[ "$pushed" != true ]`, so an unreadable value is
    # refused there exactly as `--no-push` is — the leg is not recreated —
    # and offering `resume` as the exit would be naming an exit that does not
    # work, the thing the rule above exists to prevent. The exit that does is
    # the workstation that parked it: parking there again writes the record
    # afresh, with a `pushed:` that reads.
    #
    # A FOURTH STATE: NO `pushed:` KEY AT ALL (the independent review of #19,
    # 2026-09-11). Not a fourth spelling of the third — `record_rows` keeps
    # the two apart in `$seen`, because the record is not saying something
    # that cannot be read, it is saying NOTHING — and what makes it a finding
    # rather than a shrug is the other end. On workBenches before #60
    # (ec2b450) `workspace_load_project` seeds every leg's
    # `MANIFEST_LEG_PUSHED` with `false` AT THE `- role:` LINE and overwrites
    # it only where a `pushed:` key follows, so `resume.sh` reads a missing
    # one as `false` and RR6 refuses the leg in the `--no-push` words: run
    # against a record with no `pushed:` on 2026-09-11, the extension
    # answered "Error: 001-a-thing
    # (repo leg) was parked with --no-push; that feature was NOT recreated",
    # exit 2 — and the paragraph after next is what workBenches #60 changes
    # about that, which is the words and not the refusal. Until
    # this change `status` was SILENT about that record — no row, no finding,
    # exit 0 — which is the worse half of note (d): a wrong line can be
    # argued with, a missing one cannot. So it is read exactly as the
    # unreadable one is and rules out exactly as little; only the words
    # differ, because what is wrong with the record differs, and `$refuses`
    # is the clause that carries the difference into each arm.
    #
    # AND THE EXIT IS A PARK THAT PARKS IT, which is why every arm says from
    # THERE. `park.sh`'s `emit_recorded_feature` carries a REFUSED feature's
    # entry forward out of the loaded manifest untouched — `pushed` included,
    # which for an absent key is whatever that loader seeded: on workBenches
    # before #60 (ec2b450) the defaulted `false`, so a park that refuses this
    # feature rewrites the record as `pushed: false`, a `--no-push` claim
    # nobody made; under workBenches #60 the empty string, which
    # `workspace_write_manifest` drops rather than writes, so the same park
    # carries the hole forward as a hole. Neither is a record that says what
    # happened, and `resume` refuses it after either. Only the workstation
    # that has the worktree parks it through, and that path
    # writes `true` or `false` from the push it just made. It is also where
    # this state comes from WITHOUT a hand-edit: a bare `pushed:` loads as
    # the empty string, `emit_recorded_feature` carries the empty string
    # forward, and `workspace_write_manifest` drops any key whose value is
    # empty — so one park that refuses a note-(d) record degrades it into
    # this one (both halves verified against those functions the same day).
    #
    # AND WHICH LOADER IT IS DECIDES THE WORDS AND NOTHING ELSE, WHICH IS WHY
    # THE CLAUSE BELOW NAMES NEITHER (the independent review of workBenches
    # #60, 2026-09-11). That pull request stops `workspace_load_project`
    # inventing `pushed: false` for a key the record does not carry: the seed
    # becomes the empty string, and a new `MANIFEST_LEG_PUSHED_SEEN[]` keeps
    # absent apart from bare exactly as `$seen` does here. So under it
    # `resume` does not READ a missing one as not pushed — it reads it as
    # nothing and says so: run against the same record on 2026-09-11 with
    # #60's scripts, the extension answered "Error: 001-a-thing (repo leg):
    # the record has no `pushed:` for this leg; that feature was NOT
    # recreated. / Whether its parked commit <sha> ever left Falcon cannot be
    # read from the record. / Park it again from there, which writes the
    # record afresh", exit 2, and its REFUSED line read "the record has no
    # `pushed:` for the repo leg" where main's read "parked with --no-push".
    # The clause that stood here — "`resume` reads a missing one as not
    # pushed and refuses it" — was main's MECHANISM copied into this
    # command's output, and it would have gone stale the day #60 landed: a
    # line about the other end that the other end had stopped agreeing with.
    # WHAT IS TRUE UNDER BOTH is the refusal and the exit, and this layer
    # only ever needed those: RR6 is `[ "$pushed" != true ]` in both, an
    # absent key loads as `false` in one and as the empty string in the
    # other and neither is `true`, so the leg is not recreated either way;
    # and either way the record is written afresh only by a park that parks
    # the feature THROUGH, as the paragraph above says. The clause says that
    # much and stops.
    if [ -z "$seen" ]; then
        unread="the record has no \`pushed:\` for that leg"
        refuses="\`resume\` refuses a leg whose record has no \`pushed:\`"
    else
        case "$pushed" in
        true | false) ;;
        '') unread="its \`pushed:\` has no value, which is neither true nor false" ;;
        *) unread="its \`pushed:\` says '$pushed', which is neither true nor false" ;;
        esac
        [ -z "$unread" ] || refuses="\`resume\` refuses a leg whose \`pushed:\` is not true"
    fi
    # `--no-push` IS THE ONE RECORD `park` WRITES THAT `resume` CANNOT
    # ANSWER, so no line under it offers `resume` as the exit: the WIP commit
    # is on the workstation that parked it and nowhere else, which is exactly
    # what `resume.sh` refuses the leg for. Saying "`resume` refuses it" and
    # then "`resume <Name>` brings it back" sent the person to the command the
    # line above had just ruled out, so where both states hold they are ONE
    # line, and it names the only exit there is: park it again, with the push,
    # from there. The unreadable `pushed:` above is the record `park` does NOT
    # write that `resume` refuses just as flatly, so its lines are that pair
    # again, in its own words.
    if [ "$pushed" = false ] && [ -z "$tree" ]; then
        if [ -n "$ghost" ]; then
            finding "parked feature $branch ($role leg): parked with --no-push on ${parked_on:-another workstation} and no worktree on that branch here, but $ghost_is — clear it with $clear; only that workstation has the WIP commit, so nothing here brings it back — park it again from there"
        else
            finding "parked feature $branch ($role leg): parked with --no-push on ${parked_on:-another workstation} and no worktree on that branch here; only that workstation has the WIP commit, so nothing here brings it back — park it again from there"
        fi
        return 0
    fi
    if [ -n "$unread" ] && [ -z "$tree" ]; then
        if [ -n "$ghost" ]; then
            finding "parked feature $branch ($role leg): $unread, and no worktree on that branch here, but $ghost_is — clear it with $clear; whether its parked commit ever left ${parked_on:-that workstation} cannot be read, and $refuses, so nothing here brings it back — park it again from there, which writes the record afresh"
        else
            finding "parked feature $branch ($role leg): $unread, and no worktree on that branch here; whether its parked commit ever left ${parked_on:-that workstation} cannot be read, and $refuses, so nothing here brings it back — park it again from there, which writes the record afresh"
        fi
        return 0
    fi
    if [ "$pushed" = false ]; then
        finding "parked feature $branch ($role leg): parked with --no-push on ${parked_on:-another workstation}; only that workstation has the WIP commit, and \`resume\` refuses it"
    fi
    if [ -n "$unread" ]; then
        finding "parked feature $branch ($role leg): $unread, so whether its parked commit ever left ${parked_on:-that workstation} cannot be read; $refuses, so park it again from there to write the record afresh"
    fi
    # AND WHAT IS AT THAT PATH DECIDES THE FEATURE BEFORE ANYTHING BELOW IT
    # (the two readings the commit above verified and recorded rather than
    # took: "`resume.sh`'s RR3 is the same question from the other side — a
    # path that EXISTS at `<worktree_root>/<branch>` and is not a registered
    # worktree of that leg refuses the feature … and this layer still says
    # "`resume <Name>` brings it back" for a record whose feature has no
    # worktree here and a stray directory in the way"). The arm above asks
    # where this leg's worktree IS; this one asks what is AT the one path the
    # other end will look at, which is a question with an answer even where
    # the feature has no worktree here at all — the ordinary state of a
    # feature parked on another workstation, and the state where this layer
    # offered `resume` for a run that cannot get past RR3.
    #
    # IT IS BOTH ARMS OF ONE REFUSAL. `resume.sh` reaches `leg_registered_at
    # "$leg_repo" "$tree"` first: a registration AT that path whose branch is
    # not this feature's is its second arm, "an unrelated worktree is in the
    # way", and a path that merely EXISTS there is the `[ -e "$tree" ]` under
    # it, "an unrelated path is in the way". The stderr they refuse in is the
    # same sentence, they `break` the same leg loop, and each takes the WHOLE
    # feature — every other leg of it included — before RR2 reads origin and
    # before RR1 compares any commit. `path_in_the_way` is where the two are
    # told apart and where what is NOT in the way is ruled out.
    #
    # WHERE IT SITS IS `resume.sh`'s OWN ORDER, and the order decides more
    # than tidiness here. After the `pushed:` arms, because RR6 is first. In
    # FRONT of the path-parity arm below, because RR4 and RR3 are reached
    # before the `git worktree add` that arm predicts: verified on 2026-09-12
    # on a scratch estate carrying BOTH states — the feature's own worktree at
    # `$HOME/elsewhere/001-a-thing` and a stray directory at
    # `worktrees/001-a-thing` — where the extension answered "REFUSED:
    # 001-a-thing — an unrelated path is in the way", exit 2, and the arm
    # below named a `git worktree move` INTO that directory. `git worktree
    # move` into a path that exists does not fail: it behaves like `mv` and
    # puts the worktree INSIDE it, leaving it at `worktrees/001-a-thing/
    # 001-a-thing` — still not where either verb looks, and now with the stray
    # directory on top of it (git 2.43, the same day). An exit that quietly
    # misfiles the work is worse than one that refuses, which is why this
    # reading comes first and ends the leg.
    #
    # AND IT ENDS THE LEG for the reason every reading in this function ends
    # it: the arms below name exits that cannot run past this one. RR2's two
    # exits and the absent-parked-commit arm's re-park are both reached only
    # after RR3, and `resume` never gets there; the no-worktree arm's `resume
    # <Name>` is the sentence this reading exists to withdraw. What they would
    # have said is said on the run after the path is clear.
    #
    # AND NOT FOR A LEG RR6 REFUSES, whose `pushed:` is anything but `true`:
    # `resume.sh` tests that at the top of the leg loop and `break`s there, so
    # no path is ever looked at for it and the REFUSED line this one quotes is
    # a line the other end does not print. The two arms above say what that
    # record is and what settles it — a park from the workstation that has the
    # WIP commit — and they answer it whatever is at the path.
    #
    # AND NOT FOR A LEG THE RUN NEVER REACHES, which is `$mine`: the REFUSED
    # reason this line quotes is the one `resume` prints for the leg it stops
    # at, and it stops at the FIRST leg in the record's order. A second
    # obstructed leg's path is read by nothing.
    if [ "$pushed" = true ] && role_maps_here "$role" && [ "$mine" -eq 1 ]; then
        want="$(leg_tree_path "$root" "$role" "$branch")"
        if path_in_the_way "$repo" "$want" "$branch" "$role"; then
            finding "parked feature $branch ($role leg): $IN_THE_WAY_WHAT is at $want, which is the path \`resume\` computes for this leg and is not this branch's worktree — \`resume\` reads that path and no other, overwrites nothing it did not create, and refuses the WHOLE feature, the other legs with it (\"… exists and is not a registered worktree of the $role leg\", refused as \"$IN_THE_WAY_WHY\"), so nothing here brings it back while that is there — $IN_THE_WAY_EXIT"
            return 0
        fi
    fi
    # A WORKTREE ON THE BRANCH IS NOT THIS FEATURE'S WORKTREE UNLESS IT IS AT
    # THE PATH `resume` COMPUTES (the independent reviewer's note of
    # 2026-09-12: "`worktree_on_branch` matches any path while RR4 matches
    # only the computed one"). `worktree_on_branch` above answers with the
    # worktree checked out on that branch WHEREVER IT SITS, and every arm
    # below reads "there is one" as "the feature is here". `resume.sh`'s RR4
    # is `leg_registered_at "$leg_repo" "$tree"`, which walks
    # `load_git_worktrees`' registered paths looking for ONE string:
    # `collect_legs` computes `$WORKTREE_ROOT/$branch` for a `repo` leg and
    # `$WORKTREE_ROOT/$branch/$SHAPE_SPEC_PATH` (or `…CODE…`) for a three-leg
    # one, out of THIS checkout's `worktree_root` and never out of the
    # record's. A worktree anywhere else is not that string, so RR4 misses,
    # RR3 finds nothing in the way, and the run ends at a `git worktree add`
    # that cannot take a branch another worktree already holds.
    #
    # VERIFIED AGAINST THE EXTENSION on 2026-09-12, on a scratch single-shape
    # estate — a bare remote in a temp dir, a fake `$HOME`, the extension's own
    # scripts — whose record carried `pushed: true` and a parked commit that
    # was origin's tip, with the worktree on the branch at
    # `$HOME/elsewhere/001-a-thing`: "Error: could not add the repo leg
    # worktree 'worktrees/001-a-thing' for '001-a-thing'; that feature was NOT
    # recreated. / fatal: '001-a-thing' is already used by worktree at
    # '…/elsewhere/001-a-thing'", "REFUSED: 001-a-thing — git worktree add
    # failed", exit 2. `status` at this branch's base answered "0 finding(s)
    # in 1 repository", exit 0 — the feature read as CLEAN AND PRESENT where
    # the command this layer exists to predict refuses it whole. With that one
    # worktree instead at `worktrees/001-a-thing` the extension answered
    # "[specify] 001-a-thing (repo): worktree already registered at
    # worktrees/001-a-thing; left as it is", exit 0, and answered the same
    # after a `git worktree move` between the two runs — which is where the
    # exit below comes from, and it is a command that works. The `mkdir -p` in
    # front of it is not decoration: `worktree move` creates no parent
    # directory, and with the worktree root not yet on disk — which is the
    # ordinary state where nothing has been resumed here — it answers "fatal:
    # failed to move '…' to '…': No such file or directory", exit 128
    # (git 2.43, the same day). `resume.sh` makes that directory itself,
    # `mkdir -p "$WORKTREE_ROOT/$branch"`, before it adds anything.
    #
    # THE LEG ENDS HERE BECAUSE `park` IS BLIND TO IT TOO. Every arm below
    # exits through "park again" — moved on, and a record naming no parked
    # commit to read the tip against — and `park.sh` collects a single
    # checkout's features through `verify_git_worktree_candidate "$REPO_ROOT"
    # "$WORKTREE_ROOT" …` and a three-leg one's through `shape_load_features
    # "$WORKTREE_ROOT"`, each of which drops every worktree outside that root.
    # On the same estate `park --dry-run` answered "[specify] No open features
    # under 'worktrees'; nothing to park.", and answered "PARKED: 001-a-thing"
    # with the worktree at the computed path. So those arms would name an exit
    # that cannot run, which is the one thing the rule at the top of this
    # function forbids; the move is the exit that can, and after it the next
    # run says whatever they would have said.
    #
    # NOT FOR A FEATURE `resume` REFUSES WHOLE, whose verdict is made before
    # any RR runs and is the arm below's to say, and not for a role this
    # checkout's shape has no place for, which never reaches RR4 either. AND
    # NOT WITH A `worktree move` WHERE THE PATH FOUND IS THE LEG'S OWN
    # CHECKOUT: git refuses that in its own words, "'…' is a main working
    # tree" (git 2.43, verified the same day), so that spelling names no
    # command — `resume` refuses a root on a feature branch in any case, which
    # is the local layer's own line on the row this one is printed under.
    #
    # AND ONLY FOR THE LEG THE RUN STOPPED AT, which `$mine` is: `resume`
    # breaks at the first refusing leg in the RECORD's order, so a line about
    # what a LATER leg's path holds is a refusal this layer would be inventing
    # — and a line about an EARLIER leg's is one the verdict every sibling
    # carries has already made. Where no leg refuses, every leg is its own and
    # `$mine` is true for all of them. It is also how "AFTER the `pushed:`
    # arms and NOT INSTEAD OF THEM" survives `main`'s third round, which made
    # RR6 a whole-feature verdict: a leg whose own `pushed:` is not `true` IS
    # the leg RR6 stops at, so this line still stands beside the arms above
    # it, and a later leg's does not.
    #
    # AND ONLY WHERE THE COMPUTED PATH HOLDS NOTHING, because RR4's question
    # is about that PATH and this arm's exit is a `worktree move` ONTO it (the
    # independent review of this commit's base, 2026-09-12, blocker 2). A
    # block `resume` SEES there on the recorded branch is RR4's first arm: the
    # leg is already recreated and the feature RESUMES, whatever worktree of
    # that branch sits elsewhere — run against the extension the same day with
    # a second worktree force-added on the branch outside the root, "[specify]
    # 001-a-thing (repo): worktree already registered at worktrees/
    # 001-a-thing; left as it is", exit 0, while this arm at the base refused
    # the feature whole and named a move onto the worktree that had resumed
    # it. A block `resume` does NOT see there — a prunable one — is the arm
    # above's or the stale-registration arms', and it is not a destination
    # either: `git worktree move` onto a path git still holds registered dies
    # ("is a missing but already registered worktree"), and onto a path that
    # EXISTS it exits 0 and puts the worktree INSIDE it, leaving it at
    # `<want>/<branch>` — still where neither verb looks (git 2.43, the same
    # day). And a path that merely EXISTS is the same hazard from the other
    # side — that is the nesting one — so this arm wants BOTH: git holding
    # nothing there, and nothing on disk there. The arm above answers both
    # for every leg whose `pushed:` is `true` and returns; RR6 keeps it from
    # running for the rest, and those are the legs the two conditions below
    # are here for. The question is therefore asked in its own right rather
    # than read back off that arm: `registered_at` answers 1, and only 1, for
    # a path git holds nothing at.
    #
    # AND THE MECHANISM IS NOT QUOTED, BECAUSE IT IS ONLY ONE OF FIVE (the
    # independent review of this branch, 2026-09-12). The `git worktree add`
    # that cannot take the branch is what refuses this leg where the branch
    # is still AT the parked commit; off the path and moved on it is RR1's
    # divergence, off it and behind it is RR5, off it with the branch gone
    # from origin it is RR2, and with no parked commit recorded it is RR1
    # again with the commit blank — all four run against the extension the
    # same day. The CONCLUSION is the same in all five and so is the exit, so
    # the line names the outcome and keeps the quoted sentence for the state
    # that prints it: a line about the other end that the other end does not
    # print is the drift this file's own rules forbid.
    if [ -n "$tree" ] && role_maps_here "$role" && [ "$mine" -eq 1 ]; then
        want="$(leg_tree_path "$root" "$role" "$branch")"
        held=0
        registered_at "$repo" "$want" || held=$?
        # AND A DANGLING SYMLINK AT THE DESTINATION DOES NOT SILENCE THIS
        # (Copilot's fifth round on #27, verbatim: "`dest_exists` treats a
        # dangling symlink as occupied, but `resume`'s RR3 uses `[ -e ]` and
        # therefore proceeds to its final `git worktree add`, which still
        # rejects that symlink. With an off-path live worktree and a dangling
        # symlink at this computed destination, this condition is false, so no
        # add verdict is carried to sibling legs and the report can claim the
        # feature is clean even though `resume` fails"). It is right, and the
        # round before it had made that silence: `[ -e ]` is the test RR3
        # makes, so a path only a link occupies is one the run walks past and
        # the ADD refuses — the feature is refused, the finding is this one,
        # and clearing the link is a STEP IN ITS EXIT rather than a reason to
        # say nothing.
        if [ "$tree" != "$want" ] && [ "$held" -ne 0 ] && [ ! -e "$want" ]; then
            # AND THE EXIT IS RR2's WHERE RR2 IS WHAT REFUSES (Copilot's third
            # round on #27): with the branch gone from origin and a fetch that
            # WORKED there, `resume` reaches RR2 before the add it never makes
            # — run on 2026-09-13, "Error: 001-a-thing is no longer on origin
            # in the spec leg", "REFUSED: 001-a-thing — gone from origin",
            # exit 2 — and a `git worktree move` brings back no branch origin
            # has lost. The FINDING is still this one, because where the
            # worktree sits is a fact about this workstation that RR2's line
            # does not carry and `park` here will not collect either; the
            # EXITS are RR2's own two, and the line says which refusal it is.
            if [ "$refused_kind" = gone ]; then
                move="there is no \`origin/$branch\` in this leg after this run's fetch either, so \`resume\` refuses at that before it reaches any add — $(origin_gone_exits "$branch" "$parked_on")"
            else
                move="$(off_path_exit "$repo" "$role" "$tree" "$want" "$held")"
            fi
            finding "parked feature $branch ($role leg): its worktree is at $tree, which is not the $want \`resume\` computes for it — \`resume\` matches a leg's worktree on that path alone, so it does not read this one as already recreated and refuses the WHOLE feature, the other legs with it, at whichever of its own checks this leg reaches first (with the branch still at the parked commit that is the \`git worktree add\` it makes last of all, which cannot take a branch another worktree holds); \`park\` does not carry it either, because it parks the features under $RECORD_WORKTREE_ROOT and no others — $move"
            return 0
        fi
    fi
    # A BRANCH ORIGIN HAS LOST IS A RECORD `resume` REFUSES BEFORE IT COMPARES
    # ANY COMMIT, and this layer never asked (the independent review of #21,
    # 2026-09-11, recorded there as out of scope). Every arm below reads the
    # leg against ITS OWN history — the branch tip here, the parked commit
    # here — and the arm that names `resume` promised it for any leg with no
    # worktree. `resume.sh` reads origin between RR3 and RR1: its RR2 refuses
    # the WHOLE feature where the leg's repository has no
    # `refs/remotes/origin/<branch>`, in words and with exits of its own.
    #
    # WHAT RR2 TESTS IS A REF IN THAT REPOSITORY, NOT A QUESTION TO ORIGIN,
    # which is why the test here is `has_ref` and nothing new: RR2 fetches
    # `+refs/heads/<branch>:refs/remotes/origin/<branch>` in the leg's
    # repository first — quietly, `|| true`, and not at all under
    # `--dry-run` — and then reads that ref, refusing on the empty string. So
    # the question is the one the local layer already asks of a tracking
    # branch, `has_ref "$repo" "refs/remotes/origin/$branch"`, AS OF THE LAST
    # FETCH. Run on 2026-09-12 against a scratch single-shape estate — a bare
    # remote in a temp dir, a fake `$HOME`, the extension's own scripts at
    # workBenches' main — with the branch deleted in the bare and pruned here,
    # the extension answered "Error: 001-a-thing is no longer on origin in the
    # repo leg; that feature was NOT recreated. / The manifest recorded it at
    # <sha>, parked <at> on Falcon. / If the feature landed, delete its entry:
    # / <workspace>/workspaces/<org>/atlas.yaml  ->  remove the `- branch:
    # 001-a-thing` block / If it was deleted by mistake, push it again from
    # the workstation that parked it.", "REFUSED: 001-a-thing — gone from
    # origin", exit 2. BOTH of those exits are in the line below, because they
    # are not one: a feature that LANDED is a record entry to delete, a branch
    # deleted by mistake is a branch to push again, and neither is a `resume`.
    #
    # AND A STALE `origin/<branch>` READS AS PRESENT AT BOTH ENDS, which is
    # the other half of reading the ref rather than origin: a branch deleted
    # on origin and never pruned here still has its remote-tracking ref, and
    # RR2's own fetch neither creates nor deletes one — it carries no
    # `--prune`, and a fetch of a branch origin has not got fails and is
    # ignored — so RR2 sees the stale ref and passes. Verified the same day on
    # the same estate: with the ref put back by hand the extension RESUMED the
    # feature. This layer therefore claims exactly what the ref says.
    #
    # WHERE IT SITS IS `resume.sh`'s OWN ORDER. After the `pushed:` arms,
    # because RR6 is first and refuses those legs before origin is read at
    # all. Only where there is NO WORKTREE here, because RR4 is next and calls
    # a leg whose worktree is already at the path it computes recreated —
    # "[specify] 001-a-thing (repo): worktree already registered at …; left as
    # it is", exit 0, verified the same day with the branch gone from origin —
    # so a worktree here is not a leg RR2 judges, and the tip arms below go on
    # saying exactly what they said. Never for a feature `collect_legs`
    # refuses WHOLE, whose verdict is made before any RR runs and is the arm
    # below's to say. And before the absent-parked-commit arm, because RR2 is
    # before RR1: with no parked commit AND the branch gone, that same estate
    # answered RR2's refusal and not "has moved since it was parked".
    #
    # WITHOUT A FETCH THIS RUN THE PROMISE IS KEPT AND CAVEATED, NOT WITHDRAWN
    # — `no_origin_branch_finding`'s rule in the one place where the missing
    # ref is USUALLY BENIGN. A recorded feature this workstation has not
    # resumed was parked somewhere else, and a branch pushed there after this
    # clone's last fetch has no `origin/<branch>` here for the most ordinary
    # reason there is; RR2's own fetch then finds it and the feature comes
    # back. Refusing to say `resume <Name>` there would withdraw the exit that
    # works from the commonest state this layer reports, and saying "origin
    # has not got it" would be inventing a refusal — so the line keeps its
    # exit and names the other reading with the one command that settles it.
    # AFTER A FETCH THAT WORKED IN THIS REPOSITORY there is nothing left to
    # settle: origin has not got the branch, RR2 will refuse the feature, and
    # the line says so and gives RR2's two exits instead of `resume`.
    # `fetched_ok_at` is the pre-pass's answer for THIS repository, root or
    # leg, as the missing-commit arms below use it.
    # AND WHERE THE FETCH SETTLED IT THE REFUSAL IS THE FEATURE'S, WHICH IS
    # WHAT THIS ARM PRINTED AND DID NOT MAKE (the independent review of this
    # branch, 2026-09-12, its blocker). The line said `resume` refuses the
    # WHOLE feature and the verdict machinery below knew nothing about it, so
    # a sibling leg of that same feature — pushed, parked, its own branch
    # still on origin — went on drawing "`resume <Name>` brings it back" two
    # lines under it: the report as a whole then named an exit that cannot
    # work, which is the one thing the rule above forbids. The question is
    # asked of every leg in `read_record`'s first pass now, beside the two
    # `collect_legs` asks, and the answer comes back as `$refused` with
    # `$refused_kind` set to `gone`. So this arm runs for a leg of a feature
    # already refused FOR THIS READING — the culprit says it in RR2's own
    # words, and a second leg origin has also lost says it for itself, which
    # is how the shape arm above treats two unmappable roles — and stays
    # silent for a feature `collect_legs` throws out first, whose verdict is
    # made before any RR runs and is the arm below's to say — and silent,
    # too, for a feature an EARLIER LEG refuses at RR6 or RR1, whose verdict
    # arrives here as a kind that is not `gone` (Copilot's third round on
    # #23). `resume.sh` breaks at the first refusing leg in the record's
    # order, so where one of those comes first origin is never read for this
    # feature at all, and a line claiming it — with RR2's exits, which say to
    # delete the record's entry — would be this layer inventing the refusal
    # the round before it stopped inventing.
    if [ -z "$tree" ] && { [ -z "$refused" ] || [ "$refused_kind" = gone ]; } &&
        ! has_ref "$repo" "refs/remotes/origin/$branch"; then
        if fetched_ok_at "$repo"; then
            gone_exits="$(origin_gone_exits "$branch" "$parked_on")"
            if [ -n "$ghost" ]; then
                finding "parked feature $branch ($role leg): no worktree on that branch here, but $ghost_is — clear it with $clear; and no \`origin/$branch\` here after this run's fetch, so origin has not got that branch and \`resume\` refuses the WHOLE feature for it, \"$branch is no longer on origin in the $role leg\" — nothing here brings it back: $gone_exits"
            else
                finding "parked feature $branch ($role leg): no worktree on that branch here, parked ${parked_at:-?} on ${parked_on:-?}, and no \`origin/$branch\` here after this run's fetch: origin has not got that branch, so \`resume\` refuses the WHOLE feature for it, \"$branch is no longer on origin in the $role leg\", and nothing here brings it back — $gone_exits"
            fi
            return 0
        fi
        # AND A FETCH THAT FAILED HERE SETTLED NOTHING, so the caveat does not
        # send the person to the flag they have just run (the same review,
        # S1). `fetched_ok_at` is false for FOUR different runs — one without
        # the flag, one whose fetch in THIS repository failed, one whose fetch
        # never ran there because the repository has no `origin` remote or
        # could not be read at all, and one that never fetched there at all —
        # and only the first and the last are answered by `status --fetch`.
        # The middle two are answered by the row above, which has already
        # printed why nothing was fetched there and that what follows is as of
        # the last fetch that worked; this clause says that the reading below
        # is one of those things, IN THE WORDS OF WHAT ACTUALLY HAPPENED
        # (Copilot's second round on #23): the first version of it read the
        # presence of the store's entry and called all three of them a fetch
        # that failed, which for the two that never ran is a claim about this
        # run that the row two lines above contradicts.
        if [ -z "$refused" ]; then
            case "$(fetch_state_at "$repo")" in
            failed) settles="the fetch this run made there failed, so nothing has settled it" ;;
            no-remote) settles="that repository has no \`origin\` remote for this run's fetch to have asked, so nothing has settled it" ;;
            unreadable) settles="that repository could not be read this run, so no fetch was made there and nothing has settled it" ;;
            # AND A REPOSITORY THIS RUN'S FETCH NEVER LISTED IS NOT ONE THE
            # FLAG WILL SETTLE (Copilot's third round on #23): the pre-pass
            # fetches what `estate_repos` lists — the root and the legs
            # `.gitmodules` declares — while this layer reads the path
            # `project.yaml` gives the role, and a leg mounted somewhere else
            # is fetched by no rerun of the flag. So the line says what this
            # run did rather than naming the flag the person is holding.
            unlisted) settles="this run's fetch did not reach that repository: it fetches the root and the legs \`.gitmodules\` declares, and \`project.yaml\` maps this role onto a path that is not one of them, so nothing has settled it" ;;
            # `ok` cannot reach here — `fetched_ok_at` answered it above and
            # returned — so what is left is `none`, which is every repository
            # of a run made without the flag.
            *) settles="\`status --fetch\` settles which" ;;
            esac
            gone_caveat=", unless origin has lost that branch: there is no \`origin/$branch\` here as of the last fetch, and \`resume\` refuses a leg whose branch is not on origin, taking the WHOLE feature with it (\"$branch is no longer on origin in the $role leg\") — $settles"
        fi
    fi
    # THE RECORD'S `parked_commit:` HAS TWO READINGS, PRESENT AND ABSENT, and
    # the second was read as neither (the independent review of #20,
    # 2026-09-11, note 7, recorded there as out of scope). `park.sh` records
    # the commit it parked each leg at, and `workspace_write_manifest` drops
    # any key whose value is empty — so a leg it could name none for is
    # written with no `parked_commit:` line at all. ABSENT AND BARE ARE ONE
    # STATE HERE, unlike `pushed:`, and the sixth field's argument does not
    # apply: `workspace_load_project` leaves `MANIFEST_LEG_COMMIT` at the
    # empty string either way and `trim_unquote` gives the empty string
    # either way, so the two readers make one state of them and so does this.
    #
    # IT IS NOT A LEG LIKE ANY OTHER, BECAUSE `resume.sh` WILL NOT RECREATE
    # IT. With no worktree here: RR6 passes (`pushed: true` is the only way
    # through the arms above on that path — with a worktree here the RR6
    # states fall through to the arm below and it is a second finding),
    # RR4 finds no worktree to call already-done, RR3 nothing in the way, RR2
    # the branch on origin, and RR1 — `[ "$remote_tip" != "$parked_commit" ]`
    # — refuses it, because an absent parked commit never equals a tip: run
    # against such a record on 2026-09-11 the extension answered "Error:
    # 001-a-thing (repo leg) has moved since it was parked; that feature was
    # NOT recreated. / parked commit     (parked 2026-09-10T20:00:00Z on
    # Falcon) / current tip    050d1ee…   (origin/001-a-thing)", exit 2.
    # Until this change the `[ -n "$commit" ] || return 0` that stood here
    # sat AFTER the arm below, so the line a person got was "no worktree on
    # that branch here; parked … — `resume Atlas` brings it back": the layer
    # did not merely stay silent about a record `resume` refuses, it NAMED
    # AN EXIT THAT CANNOT WORK, which is the one thing the rule at the top
    # of this function forbids.
    #
    # AND WITH A WORKTREE HERE IT WAS SILENT, the same hole from its other
    # side: that `return 0` skipped the three tip arms below, which are the
    # only thing this layer says about a worktree that IS here. They cannot
    # run — there is no parked commit to compare the tip with, and the empty
    # string is not one: the FIRST of them, `cat-file -e "$commit^{commit}"`,
    # fails on it, so what would fire is "its parked commit  is not here —
    # the record says it was pushed, so this repository has never fetched it,
    # or origin no longer has it" — a blank sha and a reason invented for a
    # commit the record never named (the independent review of this branch,
    # 2026-09-11, proved it by removing this arm). So the line says what
    # cannot be compared instead of guessing at one of them, and it names no
    # refusal: with the worktree at the path it computes, `resume.sh`'s RR4
    # calls that leg already recreated and only warns — "HEAD is 7b4773d…,
    # not the parked commit ; the parked WIP was NOT un-committed" (verified
    # the same day) — so the exit is the moved-on arm's, park again, and the
    # workstation to park from is the one the worktree is on, which is this.
    #
    # THE EXIT IS A PARK THAT PARKS IT, for the reason the paragraph above
    # gives for an absent `pushed:`: `emit_recorded_feature` carries a
    # REFUSED feature's entry forward out of the loaded manifest untouched —
    # the empty commit with it — and `workspace_write_manifest` drops the
    # empty key again, so a park that refuses this feature writes the hole
    # back unchanged. Only a park that parks the feature THROUGH records a
    # commit for it, which is why the no-worktree arm sends the person to the
    # workstation that has the feature rather than saying "park again" here.
    if [ -z "$commit" ]; then
        if [ -z "$tree" ]; then
            if [ -n "$ghost" ]; then
                finding "parked feature $branch ($role leg): the record names no parked commit for that leg, and no worktree on that branch here, but $ghost_is — clear it with $clear; \`resume\` matches origin's tip against the parked commit before it recreates anything, an absent one never matches, and it refuses the leg as having moved on since it was parked, so nothing here brings it back — park it again from ${parked_on:-the workstation that has it}, which writes the record afresh"
            else
                finding "parked feature $branch ($role leg): the record names no parked commit for that leg, and no worktree on that branch here; \`resume\` matches origin's tip against the parked commit before it recreates anything, an absent one never matches, and it refuses the leg as having moved on since it was parked, so nothing here brings it back — park it again from ${parked_on:-the workstation that has it}, which writes the record afresh"
            fi
        else
            finding "parked feature $branch ($role leg): the record names no parked commit for that leg, so there is nothing to read this worktree's tip against; whether it moved on, fell behind or diverged since it was parked cannot be told here — park again before leaving, which writes that leg's parked commit"
        fi
        return 0
    fi
    # NO LINE OF A FEATURE `resume` REFUSES WHOLE MAY NAME `resume` (Copilot
    # on #21, second round, 2026-09-11). These two arms are the only ones in
    # this function that offer it — the `--no-push`, unreadable-`pushed:` and
    # absent-parked-commit arms all rule it out for themselves — and they
    # offered it to a leg whose FEATURE `collect_legs` throws out: a record
    # with a roleless leg beside a good `repo` leg drew "the record has no
    # role … `resume` refuses the WHOLE feature" on one line and "`resume
    # Atlas` brings it back" on the next, and the two together recommend a
    # command that cannot recreate it. The leg that costs the feature says so
    # on its own line, but a person reads the REPORT, not one line of it.
    #
    # THE VERDICT IS THE FEATURE'S, SO IT IS MADE WHERE THE FEATURES ARE:
    # `read_record`'s first pass — the one that counts each feature's legs
    # for state 7 — also asks of each leg the two questions `collect_legs`
    # asks, has it a role and can this root answer that role, and hands the
    # answer down as `$refused`, a clause naming the leg that costs the
    # feature. Empty for every feature `resume` does not refuse whole, which
    # is what keeps every other record's lines exactly as they were. It is
    # not computed HERE because by the time this function has the leg in
    # front of it the sibling that spoils the feature may not have been read
    # yet: the record's order is the loader's, not the reader's convenience.
    if [ -z "$tree" ]; then
        if [ -n "$refused" ]; then
            # THE EXIT IS THE VERDICT'S, NOT THE ARM'S. A re-park is what
            # settles every refusal `collect_legs` makes — a misspelt role, a
            # role of another shape, a feature with no legs — and it is the
            # wrong thing to name for a branch origin has lost: the feature
            # may have LANDED, and a park from the workstation that still has
            # the worktree would push it back. So the gone verdict carries
            # RR2's own two exits onto the sibling's line, in the words the
            # culprit leg's line uses. RR6 and RR1 are the re-park again —
            # the record is what is wrong, and only a park that parks the
            # feature THROUGH writes it afresh: with the push RR6 wants, and
            # with the commit RR1 matches against origin's tip — so those two
            # say it in the words their own culprit lines end in.
            #
            # AND A PATH IN THE WAY CARRIES AN EXIT THE FIRST PASS BUILT: a
            # park somewhere else moves nothing out of this checkout's way,
            # so neither the re-park nor RR2's two answer it, and the command
            # that does turns on what git holds at a path in ANOTHER leg's
            # repository. `read_record` hands down one verdict and the kind
            # it is, and `inway` and `add` are the two kinds whose exit
            # travels with it — the second for the same reason as the first,
            # a worktree of the culprit leg sitting where neither verb looks,
            # which no park anywhere moves.
            # AND THE CULPRIT LEG KEEPS ITS OWN LINE where that line already
            # names the blocker AND the command that clears it — which is the
            # `add` kind read on the leg it belongs to: the stale-registration
            # arms, the dangling symlink and the feature directory all end in
            # "clear it with X, then `resume <Name>` brings it back", and that
            # `resume` is the run AFTER the clearing, which is the sentence
            # this branch's third commit chose deliberately. Every OTHER leg
            # carries the verdict, which is what the first pass exists for.
            if [ "$refused_kind" = add ] && [ "$mine" -eq 1 ] && [ -n "$ghost" ]; then
                :
            else
            case "$refused_kind" in
            inway | add) exits="$refused_exit" ;;
            gone) exits="$(origin_gone_exits "$branch" "$parked_on")" ;;
            pushed | moved)
                exits="park that feature again from ${parked_on:-the workstation that has it}, which writes the record afresh"
                ;;
            *)
                where="the record does not say where"
                [ -z "$parked_on" ] || where="the record says $parked_on"
                exits="park that feature again from the workstation that has it ($where)"
                ;;
            esac
            if [ -n "$ghost" ]; then
                finding "parked feature $branch ($role leg): no worktree on that branch here, but $ghost_is — clear it with $clear; and \`resume\` refuses the WHOLE feature in any case, because $refused, so it does not bring this leg back either — $exits"
            else
                finding "parked feature $branch ($role leg): no worktree on that branch here, parked ${parked_at:-?} on ${parked_on:-?}; \`resume\` refuses the WHOLE feature, because $refused, so it does not bring this leg back — $exits"
            fi
            return 0
            fi
        fi
        if [ -n "$ghost" ]; then
            finding "parked feature $branch ($role leg): no worktree on that branch here, but $ghost_is — clear it with $clear, then \`resume $ESTATE_NAME\` brings it back$gone_caveat"
        else
            finding "parked feature $branch ($role leg): no worktree on that branch here; parked ${parked_at:-?} on ${parked_on:-?} — \`resume $ESTATE_NAME\` brings it back$gone_caveat"
        fi
        return 0
    fi
    tip="$(g "$repo" rev-parse -q --verify "refs/heads/$branch" 2>/dev/null || true)"
    [ -n "$tip" ] || return 0
    [ "$tip" != "$commit" ] || return 0
    if ! g "$repo" cat-file -e "$commit^{commit}" 2>/dev/null; then
        # WHAT THE RECORD ITSELF RULES OUT IS NOT OFFERED AS A REASON. With
        # `pushed: true` the commit left that workstation, so what is left is
        # this repository never fetching it, or origin not having it any more;
        # `pushed: false` is the other case, has its own line above, and here
        # only says where the commit is. A `pushed:` THAT RULES NOTHING OUT
        # rules nothing out in the line either: the unreadable arms name every
        # reason still standing, rather than the one the record would have to
        # be read as `true` to support. AND AFTER A FETCH THAT WORKED IN THIS
        # REPOSITORY the first of those is ruled out too, so the line says so
        # — the way `no_origin_branch_finding` does for a missing
        # `origin/<branch>`, and for the same reason: contradicting the
        # `fetched origin:` line printed a moment earlier is worse than saying
        # nothing. `fetched_ok_at` is the pre-pass's answer for THIS
        # repository, root or leg, because every one of them is fetched before
        # any row is read.
        if [ "$pushed" = false ]; then
            finding "parked feature $branch ($role leg): its parked commit $(short "$commit") is not here, which is what --no-push means — ${parked_on:-that workstation} is the only place it is"
        elif [ -n "$unread" ] && fetched_ok_at "$repo"; then
            finding "parked feature $branch ($role leg): its parked commit $(short "$commit") is not here after this run's fetch, and $unread — so neither reason is ruled out: it may never have left ${parked_on:-that workstation}, and origin may not have it either"
        elif [ -n "$unread" ]; then
            finding "parked feature $branch ($role leg): its parked commit $(short "$commit") is not here, and $unread — so no reason is ruled out: it may never have left ${parked_on:-that workstation}, or this repository has never fetched it, or origin no longer has it"
        elif fetched_ok_at "$repo"; then
            finding "parked feature $branch ($role leg): its parked commit $(short "$commit") is not here after this run's fetch — the record says it was pushed, so origin has not got it either: rewritten there, or pushed somewhere this clone is not looking"
        else
            finding "parked feature $branch ($role leg): its parked commit $(short "$commit") is not here — the record says it was pushed, so this repository has never fetched it, or origin no longer has it"
        fi
    elif g "$repo" merge-base --is-ancestor "$commit" "$tip" 2>/dev/null; then
        n="$(g "$repo" rev-list --count "$commit..$tip" 2>/dev/null || true)"
        finding "parked feature $branch ($role leg): moved on since it was parked, ${n:-?} commit(s) after $(short "$commit") — park again before leaving"
    elif g "$repo" merge-base --is-ancestor "$tip" "$commit" 2>/dev/null; then
        # RESUMED HERE, not behind: the gap is the WIP `resume` un-committed.
        if wip_gap_is_ours "$repo" "$tip" "$commit" "$depth"; then
            return 0
        fi
        finding "parked feature $branch ($role leg): this worktree is BEHIND the parked commit $(short "$commit"), parked ${parked_at:-?} on ${parked_on:-?} — the record is newer, and \`resume\` refuses to reset it"
    else
        finding "parked feature $branch ($role leg): diverged from the parked commit $(short "$commit"), parked ${parked_at:-?} on ${parked_on:-?} — reconcile by hand: \`git -C $(sh_operand "$repo") log --oneline $(sh_operand "$(short "$commit")..$branch")\`"
    fi
    return 0
}

# Linked worktrees on disk — in the root and in each mounted leg — whose
# branch the record does not know: work that was never parked. ON DISK is
# meant literally, for `worktree_on_branch`'s reason: the porcelain keeps a
# deleted worktree's block until `git worktree prune` runs, and telling
# somebody that a directory which is not there was never parked is a finding
# about nothing. `read_worktrees` checks the same path before reading it.
# GIT'S `prunable` IS READ HERE TOO, and for the same reason: a registration
# git itself calls stale is not a worktree `park` could carry, whatever is
# left in its directory — that leftover is the person's to move aside, and
# `park` would no more see it than `resume` could write to it. And where git
# says nothing because the block is `locked`, `worktree_is_usable` is what
# says it: a directory with no `.git` in it is not a worktree either, and
# accusing it of never having been parked is the same finding about nothing.
unrecorded_worktrees() {
    local root="$1" recorded="$2" repo where line path="" branch="" first=1 state leg prunable=0
    local -a repos=("$root")
    while IFS=$'\t' read -r state leg; do
        [ "$state" = mounted ] || continue
        repos+=("$root/$leg")
    done < <(declared_legs "$root")
    for repo in "${repos[@]}"; do
        where="${repo#"$root"}"
        where="${where#/}"
        [ -n "$where" ] || where="root"
        first=1; path=""; branch=""
        while IFS= read -r line; do
            case "$line" in
            "worktree "*) path="${line#worktree }"; branch=""; prunable=0 ;;
            "branch "*) branch="${line#branch refs/heads/}" ;;
            prunable | "prunable "*) prunable=1 ;;
            "")
                if [ "$first" -eq 1 ]; then
                    first=0
                elif [ -n "$branch" ] && [ "$prunable" -eq 0 ] &&
                    worktree_is_usable "$path"; then
                    case "$recorded" in
                    *"$NL$branch$NL"*) ;;
                    *) finding "worktree on $branch ($where): not in the parked record — never parked; \`park\` carries the features under this root's worktree root, anything else is yours to keep or remove" ;;
                    esac
                fi
                path=""; branch=""; prunable=0
                ;;
            esac
        done < <(g "$repo" worktree list --porcelain 2>/dev/null; printf '\n')
    done
}

# WHY A LINE THE RECORD CARRIES IS A LINE `resume` NEVER READS, AND WHAT THE
# PERSON DOES ABOUT IT — both worded once, because the five findings below
# say them between them and they are one reading: `workspace_load_project`
# reads a block by INDENT and shuts a list at the first key shallower than
# its items. The `features:` key opens the feature list and every later key at
# project indent shuts it (`in_features=false` is the first line of that
# arm); the `legs:` key opens a feature's leg list and every later key at
# feature indent shuts it (`in_legs=false`, the same way); and under a shut
# list the loader makes no feature and no leg, because its `- branch:` arm
# sits behind `[ "$in_features" = true ] || continue` and its `- role:` arm
# behind `[ "$in_legs" = true ] || continue`. THE ARM IS NAMED, not both:
# a line that loses a LEG is not told about the feature list, because the
# key that shut the list is the one the reader has to go and find.
#
# THE FIRST EXIT IS THE RECORD'S OWN KEY ORDER, and it is the person's to
# make, never this command's: `status` READS AND CHANGES NOTHING, and a hand
# that moves the key is a hand that has read what it moves. The order to put
# it back into is the one `workspace_write_manifest` writes, whose
# `PROJECT_KEYS` it emits before `features:` and whose leg keys it emits under
# `legs:`. THE SECOND IS THE RE-PARK, and only from the workstation that has
# the feature, which is stronger here than in the findings around it: a park
# rewrites this project's WHOLE BLOCK from what its own `workspace_load_project`
# read plus what it finds on disk — `emit_recorded_feature` carries a REFUSED
# feature forward out of `MANIFEST_FEATURE_BRANCHES`, and a feature that
# loader never made is in no such array — so a park run ANYWHERE ELSE writes
# the block back without these lines at all. That is why the order comes
# first: the lines are one park from gone.
record_unread_why() {
    case "$1" in
    feature)
        printf "\`resume\` reads a record by INDENT, and \`workspace_load_project\` SHUTS the \`features:\` list at the next key at project indent, making no feature of a \`- branch:\` below one"
        ;;
    # AND A LIST THAT WAS NEVER OPENED HAS NO KEY THAT SHUT IT (Copilot's
    # first round on this PR, suppressed). `features:` is what OPENS the
    # feature list and `legs:` a feature's leg list, so a `- branch:` above
    # every `features:` key — or in a block that has none — and a `- role:`
    # above its feature's `legs:` key are unread for the opposite reason, and
    # an arm that names a shutting key sends the reader to a line the record
    # has not got.
    nolist)
        printf "\`resume\` reads a record by INDENT, and the \`features:\` key is what OPENS the list \`workspace_load_project\` reads a \`- branch:\` out of — this block has no such key ABOVE that line, so the loader is in no feature list when it reaches it"
        ;;
    nolegs)
        printf "\`resume\` reads a record by INDENT, and the \`legs:\` key is what OPENS the list \`workspace_load_project\` reads a \`- role:\` out of — this feature has no such key ABOVE that line, so the loader is in no leg list when it reaches it"
        ;;
    outer)
        printf "\`resume\` reads a record by INDENT, and \`workspace_load_project\` SHUTS the \`features:\` list at the next key at project indent, and every feature's \`legs:\` list inside it with it, making no leg of a \`- role:\` below one"
        ;;
    *)
        printf "\`resume\` reads a record by INDENT, and \`workspace_load_project\` SHUTS a feature's \`legs:\` list at the next line shallower than its \`- role:\` items, making no leg of one below it"
        ;;
    esac
}
record_unread_exits() {
    printf "the exits are to put the block back in the order \`park\` writes it (every project-level key above \`features:\`, every feature-level key above \`legs:\`) in %s, which is yours to do and not \`status\`'s, or to park that feature again FROM THE WORKSTATION THAT HAS IT (%s), which rewrites the whole block in that order; a park from any other workstation rewrites it out of what its own loader read and takes these lines with it" "$1" "$2"
}
# AND THE LEG IS NAMED BY ITS ROLE, OR BY THE ROLE IT HAS NOT GOT (Copilot's
# second round on this PR, suppressed, verbatim: "When an unread `- role:`
# has an empty value (for example `- role: # note` under a closed `legs:`
# list), `feature_drop_role` is empty and this renders `the record lists its
# `` leg`. That makes the new recovery diagnostic unclear for a roleless
# record; use an explicit label such as \"a leg with no role\" (and cover this
# malformed spelling in the regression tests)."). IT IS RIGHT, AND THE
# SPELLING IS ONE THIS LAYER ALREADY KNOWS: `record_scalar` takes `#` and
# everything after it off every value because `workspace_load_project` takes
# it off every LINE, so `- role: # note` is a leg with no role to both
# readers — the reading #21's round put there, four functions above this one.
# An empty role between two backticks is a name where the reader is looking
# for one, and the line it is on exists to send a hand to a line.
record_unread_leg() {
    if [ -n "$1" ]; then
        printf "\`%s\` leg" "$1"
    else
        printf "leg with no role"
    fi
}

# A root's block of the record against its disk: the note line, one finding
# per leg that is out of step, one per FEATURE the record leaves with no legs
# at all, and the worktrees the record does not know. The selectors are tried
# in order until one names a block: a standalone root by its `id:` first, then
# by its folder as `root:` — the block a record found by folder name carries —
# and a family member by its `root:` alone.
#
# A FEATURE THE RECORD LISTS NO LEG FOR IS A READING THIS LAYER OWES, AND IT
# IS PER FEATURE, NOT PER LEG (the independent review of #21, 2026-09-11,
# note 1, and the note-8 commit's own "recorded for later"; nobody has ruled
# on it). `collect_legs` ends `[ "${#LEG_ROLES[@]}" -gt 0 ]`, so a feature
# whose `legs:` is empty — the key with nothing under it, `legs: []`, or leg
# items with no `- role:` line, which start no leg in either reader — is
# refused WHOLE, in the shape words again: run on 2026-09-11 against a
# scratch single-shape estate, each of those three spellings answered "Error:
# 001-a-thing was parked from a single project and this checkout is single;
# that feature was NOT recreated", "REFUSED: 001-a-thing — shape mismatch",
# exit 2. This layer said NOTHING about any of them — no finding, exit 0 —
# because every reading it had was a LEG's, and there is no leg here to hang
# one on. That is the hole the last three notes closed for legs, in its
# per-feature shape.
#
# SO THE FEATURES ARE JUDGED BEFORE THE LEGS ARE READ. `record_rows` emits a
# `branch` row per feature and a `leg` row per leg, in the record's order, so
# a first pass counts each feature's legs and the main loop reads the count
# back at that feature's `branch` row — before any of its legs' arms run,
# which is where the second job needs it. THE SECOND JOB IS THE VERDICT
# (Copilot on #21, second round, 2026-09-11): the same pass asks of each leg
# the two questions `collect_legs` asks — has it a role, and can this root
# answer that role — and keeps the first clause that costs the feature, so
# `check_parked_leg` can be told that the feature its leg belongs to is one
# `resume` throws out whole and stop offering `resume` for it. It cannot be
# asked of a leg as the leg arrives: the leg that spoils the feature may come
# after the one being read, and the record's order is the loader's, not the
# reader's convenience. ONE ENTRY PER `branch` ROW, NOT
# PER BRANCH NAME: `workspace_load_project` makes a new feature of every
# `- branch:` line it reads, so a name that appears twice is two features to
# the loader and `collect_legs` judges each on its own legs; `$recorded` and
# the feature COUNT still fold the duplicates, because those answer "is this
# branch in the record", which is one question however many entries there
# are.
read_record() {
    local root="$1" sel rows="" kind a b c d e f n_features=0
    local parked_at="" parked_on="" lane="" active="" recorded="$NL"
    local seen_branches=0 legs where="" refused="" refused_kind="" unnamed=0
    local refused_exit="" refused_leg="" legno=0 verdict=""
    local legrepo="" legtree="" legwt="" legheld=0 legrc=0
    local -a feature_legs=() feature_why=() feature_rr=() feature_rr_kind=()
    local -a feature_exit=() feature_rr_leg=()
    local -a feature_add=() feature_addexit=() feature_add_leg=()
    local -a feature_drops=() feature_drop_role=() feature_drop_why=()
    shift
    RECORD_SHAPE=""
    [ -n "$RECORD_FILE" ] || return 0
    # THIS ROOT'S SHAPE IS READ ONCE, HERE, because every leg of every feature
    # is judged against it and `load_repo_shape` reads a whole file to answer.
    read_root_shape "$root"
    # AND THE WORKTREE ROOT WITH IT, for the same reason and in the same
    # place: it is the ROOT's answer, not the leg's — `$SPECKIT_GIT_WORKTREE_ROOT`,
    # else this checkout's `git-config.yml`, else the shape's default — and
    # both passes below want it for every leg of every feature.
    #
    # OUT OF THE ROOT GIT WOULD SPELL, which is the first blocker of the
    # independent review of this branch (2026-09-12): `$root` is
    # `estate_walk_up`'s or `$PROJECTS_DIRS`' string, symlinks intact, while
    # `git worktree list --porcelain` is ALWAYS physical and `resume.sh` sets
    # `REPO_ROOT="$(git rev-parse --show-toplevel)"`, which is physical too.
    # Comparing one with the other turned a HEALTHY feature into a
    # whole-feature refusal wherever the projects directory — or the spelling
    # the person typed — went through a symlink: run both ways on one estate
    # the same day, `resume.sh` answered "worktree already registered at
    # worktrees/001-a-thing; left as it is", exit 0, and this layer answered
    # "a directory is at …/proj-link/Atlas-worktrees/001-a-thing, which is the
    # path `resume` computes", refusing the feature and naming an `mv` of the
    # very worktree it had just resumed from.
    #
    # AND NO FURTHER THAN GIT RESOLVES IT. `get_config_value` resolves no
    # symlink in `worktree_root:` and `resolve_path_from_repo_root` is
    # `os.path.abspath`, so where the WORKTREE ROOT is itself a link both ends
    # compute the unresolved path, git reports the resolved one, and RR4
    # misses at the other end exactly as it misses here — verified the same
    # day, `resume.sh` refusing such an estate at RR3, "REFUSED: 001-a-thing —
    # an unrelated path is in the way", exit 2. Resolving harder than the
    # other end resolves would delete a refusal it makes.
    RECORD_WORKTREE_ROOT="$(record_worktree_root "$(physical_path "$root")")"
    for sel in "$@"; do
        rows="$(record_rows "$RECORD_FILE" "$sel")"
        [ -z "$rows" ] || break
    done
    if [ -z "$rows" ]; then
        # A root the record does not mention: every worktree here is unparked,
        # which is the one thing worth saying about it.
        say "    parked record: no block for this root in ${RECORD_FILE##*/}"
    else
        # THE FIRST PASS: how many legs each feature entry has, in order. A
        # `leg` row belongs to the `branch` row before it — `record_rows`
        # emits one only while a branch is open — so the count goes onto the
        # last entry; the `-gt 0` guard is for a record so mangled that a leg
        # row arrives with no branch row before it, which is a row this pass
        # then ignores rather than an index it invents.
        while IFS=$'\037' read -r kind a b c d e f; do
            case "$kind" in
            branch)
                feature_legs+=(0); feature_why+=("")
                feature_rr+=(""); feature_rr_kind+=(""); feature_exit+=("")
                feature_rr_leg+=(""); feature_add_leg+=("")
                feature_add+=(""); feature_addexit+=("")
                feature_drops+=(0); feature_drop_role+=(""); feature_drop_why+=("")
                ;;
            # A LEG LINE THE LOADER READS NO LEG OUT OF is counted onto the
            # same feature, by the same rule and with the same guard: it is
            # filed under the `- branch:` above it, and the FIRST of them is
            # the one named, as the first leg that costs the feature is. It
            # takes no verdict and costs the feature nothing here, because
            # `collect_legs` never sees it — what it costs is the leg, and
            # where it costs the feature every leg it has, the feature's own
            # line below says so. It is no leg to the VERDICT walk either:
            # the four questions below are asked of the `leg` rows alone, and
            # `workspace_load_project` puts no such line in `MANIFEST_LEG_ROLE`
            # for `collect_legs` or for any RR to read.
            noleg)
                legs=${#feature_legs[@]}
                if [ "$legs" -gt 0 ]; then
                    if [ "${feature_drops[legs - 1]}" -eq 0 ]; then
                        feature_drop_role[legs - 1]="$b"
                        feature_drop_why[legs - 1]="$c"
                    fi
                    feature_drops[legs - 1]=$((feature_drops[legs - 1] + 1))
                fi
                ;;
            leg)
                legs=${#feature_legs[@]}
                if [ "$legs" -gt 0 ]; then
                    feature_legs[legs - 1]=$((feature_legs[legs - 1] + 1))
                    # THE FIRST LEG THAT COSTS THE FEATURE IS THE ONE NAMED:
                    # `collect_legs` walks the legs in the record's order and
                    # returns at the first role it cannot map, so that is the
                    # one `resume`'s refusal is about, and a second would only
                    # lengthen a line about a feature that is already gone.
                    #
                    # AND THE THIRD QUESTION IS THE SHAPE'S, ASKED LAST (the
                    # feature-level follow-up's own note, 2026-09-11).
                    # `collect_legs` asks one question — can this role be
                    # mapped onto a checkout of this shape — and the two above
                    # answer it for the roles that can be answered without
                    # knowing the shape: no role at all, and a role this root
                    # has no repository for. A role this root DOES mount can
                    # still be one no shape maps (`assembly`, or a word that
                    # happens to match a directory here) or one only the OTHER
                    # shape maps (`spec` in a single checkout, `repo` in a
                    # three-leg one), and that is this question. It is asked
                    # LAST so the two above keep the clauses they had, which
                    # is what keeps the lines they already explain
                    # byte-identical.
                    #
                    # AND THE FOURTH IS RR2's, WHICH IS NOT `collect_legs`'s
                    # AT ALL AND IS KEPT APART FROM THE THREE (the independent
                    # review of this branch, 2026-09-12, its blocker).
                    # `collect_legs` runs to the end before any RR does, so a
                    # feature it refuses is refused for THAT, whatever origin
                    # has lost — which is why this answer goes in an array of
                    # its own and is read back only where the three above
                    # found nothing. What it asks is what RR2 will ask of this
                    # leg: no worktree on the branch here, a fetch that WORKED
                    # in that leg's repository this run, and no
                    # `refs/remotes/origin/<branch>` in it — the ref RR2 reads
                    # and refuses the WHOLE feature on. `[ "$d" = true ]` is
                    # RR6 coming first: a leg whose `pushed:` is not the
                    # literal `true` is refused before origin is read at all,
                    # and that refusal is the leg's own line below, not a
                    # verdict this layer makes. The origin question is
                    # skipped without `--fetch`, where a missing ref has a
                    # second and commoner reading and this layer chooses
                    # neither.
                    #
                    # AND THE FOURTH QUESTION IS ASKED IN `resume.sh`'s OWN
                    # ORDER, OF THE LEGS IN THE RECORD's (Copilot's third
                    # round on #23, 2026-09-12): "`feature_gone` is propagated
                    # to every leg without accounting for an earlier per-leg
                    # refusal. For example, if the first leg has `pushed:
                    # false` (RR6) or no `parked_commit:` (RR1) and a later
                    # leg has a missing origin branch, `resume` stops at the
                    # first leg in record order, but this makes the whole
                    # feature report RR2's gone-origin verdict and exits."
                    # `workspace_load_project` appends each leg in FILE order,
                    # `collect_legs` walks `MANIFEST_LEG_ROLE` in that order,
                    # and the loop under it breaks at the first refusal — so
                    # the refusal the person gets is the first one in the
                    # record, and a later leg's may not be printed in front of
                    # it. Run against the extension on 2026-09-12 on a scratch
                    # three-leg estate, one feature, two legs in two
                    # repositories, the order the variable: `code` (`pushed:
                    # false`) then `spec` (branch deleted on origin) answered
                    # "Error: 001-a-thing (code leg) was parked with
                    # --no-push", "REFUSED: 001-a-thing — parked with
                    # --no-push"; `code` (no `parked_commit:`) then the same
                    # `spec` answered "Error: 001-a-thing (code leg) has moved
                    # since it was parked", "REFUSED: 001-a-thing —
                    # divergence"; and the gone leg FIRST answered "Error:
                    # 001-a-thing is no longer on origin in the spec leg",
                    # "REFUSED: 001-a-thing — gone from origin". All exit 2.
                    #
                    # SO ONE ARRAY HOLDS THE FIRST REFUSAL AND ITS KIND, and
                    # the arms under it are RR6, RR4 and RR3, RR2 and RR1 in
                    # that order, which is the order one leg is tested in.
                    # RR6 is the record alone and is certain — it is the
                    # FIRST test of the loop, before RR4, so a worktree here
                    # does not save the leg. RR1 is asked of an absent
                    # `parked_commit:`, which is the one reading of RR1 this
                    # layer makes, and only after RR2 has been asked of the
                    # same leg, because that is where `resume.sh` asks it.
                    # What this still cannot see is RR5 and the divergence
                    # half of RR1, which would be `check_parked_leg`'s whole
                    # job done a second time for every leg of every feature.
                    #
                    # AND RR4 AND RR3 ARE THE PATH'S, WHICH IS A REFUSAL OF
                    # THE WHOLE FEATURE MADE ONE LEG AT A TIME. `collect_legs`
                    # is answered before any leg is read and the three
                    # questions above this chain are its; RR3 and RR4's second
                    # arm are per leg, inside the loop that `break`s — so the
                    # feature is gone the moment ONE leg's computed path is
                    # obstructed, and every other leg of it goes with it,
                    # whatever its own path holds. It sits between RR6 and RR2
                    # because that is where `resume.sh` asks it: after the
                    # `pushed:` test at the top of the loop, and before origin
                    # is read — so a leg whose `pushed:` is not `true` refuses
                    # the feature with no path looked at for it, and a leg
                    # whose path is obstructed refuses it with origin never
                    # read. A leg the record gives no branch name is not asked
                    # either: there is no branch to compute a path from, and
                    # `resume` refuses that feature at RR2 under a blank name,
                    # which `read_record` says once on the feature's own line.
                    #
                    # THE ORDER BETWEEN LEGS IS THE ARRAY'S OWN GATE: `[ -z
                    # "${feature_rr[legs - 1]}" ]` is "no earlier leg has
                    # refused", so the walk stops at the first refusing leg
                    # whichever RR refuses it, and one reading never overtakes
                    # another by being read back in a different order. That is
                    # the round above extended to the two readings the stacked
                    # commits add: an in-the-way leg BEFORE a gone-origin one
                    # is the in-the-way refusal, and a gone-origin leg before
                    # an in-the-way one is the gone one.
                    if [ -z "${feature_why[legs - 1]}" ]; then
                        if [ -z "$b" ]; then
                            feature_why[legs - 1]="a leg of it in the record has no role"
                        elif ! leg_repo_for_role "$root" "$b" >/dev/null 2>&1; then
                            feature_why[legs - 1]="its \`$b\` leg names a role this root does not mount"
                        elif ! role_maps_here "$b"; then
                            feature_why[legs - 1]="$(role_shape_short "$b")"
                        elif [ -z "${feature_rr[legs - 1]}" ]; then
                            if [ "$d" != true ]; then
                                feature_rr[legs - 1]="$(pushed_verdict "$b" "$d" "$f")"
                                feature_rr_kind[legs - 1]=pushed
                            elif [ -n "$a" ] && leg_path_in_the_way "$root" "$b" "$a"; then
                                feature_rr[legs - 1]="$IN_THE_WAY_WHAT is at the $LEG_TREE it computes for its \`$b\` leg"
                                feature_rr_kind[legs - 1]=inway
                                feature_exit[legs - 1]="$IN_THE_WAY_EXIT"
                            elif [ "$FETCH" -eq 1 ] && origin_lost_leg "$root" "$b" "$a"; then
                                feature_rr[legs - 1]="there is no \`origin/$a\` in its \`$b\` leg after this run's fetch"
                                feature_rr_kind[legs - 1]=gone
                            elif moved_on_leg "$root" "$b" "$a" "$c"; then
                                feature_rr[legs - 1]="the record names no parked commit for its \`$b\` leg"
                                feature_rr_kind[legs - 1]=moved
                            fi
                            # WHICH LEG THE VERDICT BELONGS TO, kept beside it
                            # because the arms in `check_parked_leg` that read
                            # a path have to know whether the leg in front of
                            # them is the one `resume` stopped at (Copilot's
                            # first round on #27).  It is the leg's ORDINAL in
                            # this feature, which `feature_legs` has just been
                            # incremented to, and not its role: a record may
                            # name one role twice, and the second of them is
                            # not the leg the run refused.
                            [ -z "${feature_rr[legs - 1]}" ] ||
                                feature_rr_leg[legs - 1]="${feature_legs[legs - 1]}"
                        fi
                    fi
                    # AND THE FIFTH QUESTION IS THE `git worktree add`'s,
                    # WHICH CLOSES NOTHING, because it is the LAST thing the
                    # other end does. `resume.sh` runs its RR loop over every
                    # leg of the feature first and only then walks the legs
                    # again to add the worktrees, so an RR refusal on ANY leg
                    # — including one after this — beats an add that would
                    # fail on this one, and the walk above must go on. The
                    # first leg whose add fails is the one named, as in every
                    # question above it, because the create loop `break`s
                    # there too, and `[ -z "${feature_rr[legs - 1]}" ]` is
                    # the walk that stopped: an RR has refused an EARLIER leg
                    # and no add is reached at all. A LATER leg's RR is not
                    # visible here and does not need to be — the second pass
                    # reads the RR array first and this one only where that
                    # found nothing.
                    #
                    # WHAT MAKES IT FAIL is the reading this branch is for: a
                    # worktree on the recorded branch that is NOT at the
                    # computed path. RR4 matches on that path alone, so the
                    # leg is neither `present` nor skipped, and `git worktree
                    # add` cannot take a branch another worktree holds
                    # ("already used by worktree at …", verified against the
                    # extension on 2026-09-12). Without this the leg said so
                    # on its own line while every SIBLING leg of the same
                    # feature still said "`resume <Name>` brings it back" —
                    # the pair the rule at the top of `check_parked_leg`
                    # forbids, and the one this layer's own reading created.
                    if [ -z "${feature_rr[legs - 1]}" ] &&
                        [ -z "${feature_why[legs - 1]}" ] &&
                        [ -z "${feature_add[legs - 1]}" ] &&
                        [ -n "$a" ] && [ -n "$b" ] && [ "$d" = true ] &&
                        role_maps_here "$b"; then
                        legrepo="$(leg_repo_for_role "$root" "$b" 2>/dev/null || true)"
                        if [ -n "$legrepo" ] && [ -e "$legrepo/.git" ]; then
                            legtree="$(leg_tree_path "$root" "$b" "$a")"
                            legrc=0
                            legwt="$(worktree_on_branch "$legrepo" "$a")" || legrc=$?
                            [ "$legrc" -eq 0 ] || legwt=""
                            legheld=0
                            registered_at "$legrepo" "$legtree" || legheld=$?
                            if [ -n "$legwt" ] && [ "$legwt" != "$legtree" ] &&
                                [ "$legheld" -ne 0 ] && [ ! -e "$legtree" ]; then
                                feature_add[legs - 1]="its \`$b\` leg's worktree is at $legwt, which is not the $legtree \`resume\` computes for it"
                                feature_addexit[legs - 1]="$(off_path_exit "$legrepo" "$b" "$legwt" "$legtree" "$legheld")"
                                feature_add_leg[legs - 1]="${feature_legs[legs - 1]}"
                            # AND THE DESTINATION CAN BLOCK THE ADD WITH NO
                            # WORKTREE ANYWHERE, which the question above
                            # cannot see because it starts from one: a
                            # registration git holds at that path with nothing
                            # on disk, a dangling symlink there, or a feature
                            # directory that is not a directory. Each is read
                            # by `check_parked_leg` on the culprit leg's own
                            # line already; what this adds is the FEATURE's
                            # verdict, so no sibling says `resume <Name>` for a
                            # run that refuses (Copilot's seventh round on #27).
                            elif leg_add_blocked "$root" "$b" "$a"; then
                                feature_add[legs - 1]="$ADD_BLOCKED_WHAT"
                                feature_addexit[legs - 1]="$ADD_BLOCKED_EXIT"
                                feature_add_leg[legs - 1]="${feature_legs[legs - 1]}"
                            fi
                        fi
                    fi
                fi
                ;;
            esac
        done <<<"$rows"
        while IFS=$'\037' read -r kind a b c d e f; do
            case "$kind" in
            project) parked_at="$a"; parked_on="$b"; lane="$c"; active="$d"; RECORD_SHAPE="$e" ;;
            branch)
                seen_branches=$((seen_branches + 1))
                legno=0
                # A FEATURE THE RECORD GIVES NO BRANCH NAME (Copilot on #22)
                # is one to the loader and is refused by `resume` whatever its
                # legs say — with legs, at RR2 with the name blank, "Error:  is
                # no longer on origin in the repo leg", because no branch is
                # named nothing on origin; with none, as a shape mismatch
                # before any leg is read (both run against the extension
                # 2026-09-12). It is counted as the loader counts it, its
                # name is not added to the recorded set (an empty name would
                # match every sweep), and its legs are not read one by one:
                # there is no branch to find a worktree for, and each would
                # only print the refusal again with the name blank. One line,
                # on the root's row, with RR2's own two exits.
                if [ -z "$a" ]; then
                    unnamed=1
                    n_features=$((n_features + 1))
                    where="the record does not say where"
                    [ -z "$parked_on" ] || where="the record says $parked_on"
                    if [ "${feature_drops[seen_branches - 1]:-0}" -gt 0 ] &&
                        [ "${feature_legs[seen_branches - 1]:-0}" -eq 0 ]; then
                        # TWO DEFECTS AND ONE LINE: the name is missing AND
                        # the legs listed under it sit where the loader reads
                        # none, so "lists no leg" — true of the loader, false
                        # of the record — is not what this record is told.
                        # `resume`'s refusal is the one the arm below names,
                        # because a feature with no leg is refused before a
                        # leg is read; the exits are still this arm's, since
                        # both of them settle the branch name and the key
                        # order together.
                        finding "parked feature with no branch name: a \`- branch:\` in the record has no value, and the $(record_unread_leg "${feature_drop_role[seen_branches - 1]:-}") listed under it sits where \`resume\` reads no leg, because $(record_unread_why "${feature_drop_why[seen_branches - 1]:-legs}"); \`resume\` reads the feature as one named nothing, collects no leg for it, and refuses it whole as a shape mismatch before it reads one, so nothing here brings it back — if that feature landed, remove its \`- branch:\` block from ${RECORD_FILE##*/}; if it was parked, park it again from the workstation that has it ($where), which writes the branch and the legs it parks"
                    elif [ "${feature_drops[seen_branches - 1]:-0}" -gt 0 ]; then
                        # AND THE SAME TWO DEFECTS WHERE THE FEATURE KEEPS A
                        # LEG (Copilot's third round on this PR, verbatim:
                        # "When a blank `- branch:` has both a readable leg
                        # and a `noleg`, this condition skips the new combined
                        # diagnostic because it only enters the unread-leg
                        # branch when `feature_legs` is zero. Control then
                        # falls through to the older blank-branch message;
                        # `unnamed=1` suppresses all readable leg rows and the
                        # later `noleg` arm is a no-op, so the unread leg/key-
                        # order defect is never reported."). IT IS RIGHT, AND
                        # IT IS THE ONE HOLE THIS WHOLE BRANCH EXISTS TO SHUT:
                        # a line the record lists, that `resume` reads nothing
                        # of, said NOWHERE. The blank name suppresses every
                        # per-leg line by design — there is no branch to find
                        # a worktree for — and the `noleg` arm below only
                        # counts, so this line is the only place the unread
                        # one can be named, and the arm above it was reached
                        # only where the feature keeps none.
                        #
                        # THE REFUSAL IS THE ONE THE ARM BELOW NAMES, not the
                        # shape mismatch: the feature DOES keep a leg
                        # `collect_legs` collects, so the run gets as far as
                        # RR2 and refuses it there with the name blank. The
                        # exits are still the blank name's — a key put back
                        # leaves a feature named nothing, which no re-ordering
                        # settles — and they carry the leg's lines too,
                        # because the park that writes the branch writes the
                        # legs it parks with it.
                        finding "parked feature with no branch name: a \`- branch:\` in the record has no value, and a $(record_unread_leg "${feature_drop_role[seen_branches - 1]:-}") listed under it sits where \`resume\` reads no leg, because $(record_unread_why "${feature_drop_why[seen_branches - 1]:-legs}"); \`resume\` reads the feature as one named nothing and refuses it as a branch origin has not got (\"is no longer on origin\", the name blank), so nothing here brings it back — if that feature landed, remove its \`- branch:\` block from ${RECORD_FILE##*/}; if it was parked, park it again from the workstation that has it ($where), which writes the branch and the legs it parks"
                    elif [ "${feature_legs[seen_branches - 1]:-0}" -eq 0 ]; then
                        finding "parked feature with no branch name: a \`- branch:\` in the record has no value and lists no leg; \`resume\` reads it as a feature named nothing and refuses it whole as a shape mismatch before it reads a leg, so nothing here brings it back — if that feature landed, remove its \`- branch:\` block from ${RECORD_FILE##*/}; if it was parked, park it again from the workstation that has it ($where), which writes the branch it parks"
                    else
                        finding "parked feature with no branch name: a \`- branch:\` in the record has no value; \`resume\` reads it as a feature named nothing and refuses it as a branch origin has not got (\"is no longer on origin\", the name blank), so nothing here brings it back — if that feature landed, remove its \`- branch:\` block from ${RECORD_FILE##*/}; if it was parked, park it again from the workstation that has it ($where), which writes the branch it parks"
                    fi
                    continue
                fi
                unnamed=0
                case "$recorded" in
                *"$NL$a$NL"*) ;;
                *) recorded="$recorded$a$NL"; n_features=$((n_features + 1)) ;;
                esac
                # NO LEGS, NO LEG TO SAY IT ON: the finding is the feature's,
                # printed where the feature is, and the exit is the exit the
                # roleless arm names — a park that parks the feature THROUGH.
                # `workspace_write_manifest` writes `legs: []` for a feature
                # it has no legs for, and `emit_recorded_feature` carries a
                # REFUSED feature's legs forward out of the loaded manifest,
                # which for this one is none: a park that refuses it writes
                # the hole back as a hole (read off those two functions the
                # same day).
                #
                # AND "LISTS NO LEG" IS NOT WHAT A RECORD THAT LISTS ONE IS
                # TOLD. Where the legs are there and the loader reads none of
                # them, the feature is refused for the same reason and the
                # sentence is a different one: it names the leg the record
                # lists, why `resume` reads nothing of it, and the key order
                # that is the cheap way back. Where SOME of its legs are read,
                # the feature is not refused at all and the line is about the
                # one leg that will not come back with it.
                if [ "${feature_drops[seen_branches - 1]:-0}" -gt 0 ]; then
                    where="the record does not say where"
                    [ -z "$parked_on" ] || where="the record says $parked_on"
                    # AND "BRINGS THE FEATURE BACK" IS NOT SAID OVER A
                    # FEATURE THIS RUN ALREADY REFUSES (Copilot's first round
                    # on this PR, suppressed). The legs it DOES read can carry
                    # a whole-feature verdict of their own — `collect_legs`'s,
                    # an RR's, or the `git worktree add`'s — and
                    # `check_parked_leg` then prints that refusal on every one
                    # of their lines and offers no `resume` at all. A line
                    # here promising the feature back is the contradictory
                    # pair the rule at the top of `check_parked_leg` forbids,
                    # made by this layer against itself. The verdict is read
                    # back in the order that arm reads it, and where there is
                    # one the sentence says so and stops.
                    verdict="${feature_why[seen_branches - 1]:-}"
                    [ -n "$verdict" ] || verdict="${feature_rr[seen_branches - 1]:-}"
                    [ -n "$verdict" ] || verdict="${feature_add[seen_branches - 1]:-}"
                    if [ "${feature_legs[seen_branches - 1]:-0}" -eq 0 ]; then
                        finding "parked feature $a: the record lists its $(record_unread_leg "${feature_drop_role[seen_branches - 1]:-}") where \`resume\` reads no leg, because $(record_unread_why "${feature_drop_why[seen_branches - 1]:-legs}"); \`resume\` collects NO leg for this feature and refuses it WHOLE, reporting it as a shape mismatch, so nothing here brings it back — $(record_unread_exits "${RECORD_FILE##*/}" "$where")"
                    elif [ -n "$verdict" ]; then
                        finding "parked feature $a: the record lists a $(record_unread_leg "${feature_drop_role[seen_branches - 1]:-}") for it where \`resume\` reads no leg, because $(record_unread_why "${feature_drop_why[seen_branches - 1]:-legs}"); \`resume\` refuses this feature WHOLE in any case, for the reason the legs it does read give on their own lines below, so nothing here brings it back — $(record_unread_exits "${RECORD_FILE##*/}" "$where")"
                    else
                        finding "parked feature $a: the record lists a $(record_unread_leg "${feature_drop_role[seen_branches - 1]:-}") for it where \`resume\` reads no leg, because $(record_unread_why "${feature_drop_why[seen_branches - 1]:-legs}"); \`resume\` brings the feature back with the legs it does read and leaves that one's work where it was parked — $(record_unread_exits "${RECORD_FILE##*/}" "$where")"
                    fi
                elif [ "${feature_legs[seen_branches - 1]:-0}" -eq 0 ]; then
                    where="the record does not say where"
                    [ -z "$parked_on" ] || where="the record says $parked_on"
                    finding "parked feature $a: the record lists no leg for it; \`resume\` collects a feature's legs before it recreates anything and refuses the WHOLE feature where it finds none, reporting it as a shape mismatch, so nothing here brings it back — park that feature again from the workstation that has it ($where), which writes the record afresh with the legs it parks"
                fi
                ;;
            # A `- branch:` THE LOADER MAKES NO FEATURE OF, which is the row
            # `record_rows` emits in place of the `branch` row it used to
            # emit for it. It is NOT counted, NOT added to the recorded set —
            # a worktree on that branch really is one `park` has never
            # recorded, and `park` reading this record through the same loader
            # would park it as new — and it takes no leg row with it, because
            # the legs under it were never emitted either. The second field is
            # WHICH LINE TO GO AND FIND, as a `noleg` row's third is: a key at
            # project indent that shut the list, or no `features:` key above
            # the line at all.
            nofeature)
                where="the record does not say where"
                [ -z "$parked_on" ] || where="the record says $parked_on"
                if [ -z "$a" ]; then
                    finding "parked feature with no branch name: the record lists a \`- branch:\` with no value where \`resume\` reads no feature, because $(record_unread_why "${b:-feature}"); it is in none of the features a bare \`resume\` walks, so nothing here brings it back — $(record_unread_exits "${RECORD_FILE##*/}" "$where")"
                else
                    finding "parked feature $a: the record lists this branch where \`resume\` reads no feature, because $(record_unread_why "${b:-feature}"); it is in none of the features a bare \`resume\` walks and none \`--feature\` can name, so nothing here brings it back — $(record_unread_exits "${RECORD_FILE##*/}" "$where")"
                fi
                ;;
            # Counted in the first pass and said on the feature's own line
            # above, where the count of the legs it DOES have decides what
            # `resume` does with it.
            noleg) ;;
            leg)
                # The legs of a feature with no branch name were answered on
                # the feature's own line, above.
                [ "$unnamed" -eq 0 ] || continue
                # THE SAME GUARD THE FIRST PASS MAKES, for the same mangled
                # record: a leg row before any branch row has no feature to
                # take a verdict from, and `${feature_why[-1]}` is "bad array
                # subscript" on bash 3.2 and the LAST feature's verdict on
                # bash 5 — a clause about a feature this leg is no leg of
                # (the independent review of this branch, 2026-09-12).
                # THE RECORD THAT REACHED IT WAS THE ROW `record_rows` WAS
                # DROPPING, and it drops it no longer: a leg row goes out only
                # while a branch is open, `$named` is set by the very line
                # that appends that branch's row, and the two are flushed in
                # the record's order — so no record builds this shape now
                # (Copilot's second round on #22). The guard stays as the belt
                # to that brace, matching the first pass's, because an index
                # this reader invents is a claim about a feature the record
                # does not make.
                refused=""; refused_kind=""; refused_exit=""; refused_leg=""
                legno=$((legno + 1))
                if [ "$seen_branches" -gt 0 ]; then
                    refused="${feature_why[seen_branches - 1]:-}"
                    # `collect_legs` BEFORE THE RRs, in the reading as in the
                    # extension: it runs to the end before any RR does, so a
                    # feature it refuses is refused for THAT whatever the legs
                    # go on to say, and the RR verdict is read back only where
                    # it found nothing. Which RR that verdict is, which leg it
                    # belongs to and the exit that travels with it were all
                    # decided in the pass above, in the record's order and in
                    # `resume.sh`'s — RR6, then RR4 and RR3, then RR2, then
                    # RR1 — so there is one verdict here and no priority left
                    # to settle between readings.
                    if [ -z "$refused" ] &&
                        [ -n "${feature_rr[seen_branches - 1]:-}" ]; then
                        refused="${feature_rr[seen_branches - 1]}"
                        refused_kind="${feature_rr_kind[seen_branches - 1]}"
                        refused_exit="${feature_exit[seen_branches - 1]}"
                        refused_leg="${feature_rr_leg[seen_branches - 1]}"
                    fi
                    # AND THE ADD IS READ BACK LAST, because it runs last:
                    # every RR of every leg is asked before the first worktree
                    # is added, so any verdict above this one is the refusal
                    # the run actually makes.
                    if [ -z "$refused" ]; then
                        refused="${feature_add[seen_branches - 1]:-}"
                        refused_exit="${feature_addexit[seen_branches - 1]:-}"
                        refused_leg="${feature_add_leg[seen_branches - 1]:-}"
                        [ -z "$refused" ] || refused_kind=add
                    fi
                fi
                check_parked_leg "$root" "$a" "$b" "$c" "$d" "$parked_at" "$parked_on" "$e" "$f" "$refused" "$refused_kind" "$refused_exit" "$legno" "$refused_leg"
                ;;
            esac
        done <<<"$rows"
        say "    parked record: $n_features feature(s), parked ${parked_at:-?} on ${parked_on:-?}${lane:+ (lane $lane)}${active:+; active $active}"
    fi
    unrecorded_worktrees "$root" "$recorded"
}

# --- one family laid out twice ----------------------------------------------
#
# A FAMILY FOLDER IS NAMED FOR ITS REPOSITORY OR FOR ITS DISPLAY `name:`
# (#236), so ONE FAMILY CAN BE ON DISK TWICE - `Quill/Quill`, openRepoShape
# v1.1.0's layout, and `QuillWorks/QuillWorks`, the one before it, which a
# person has after resuming a record parked from one layout on a machine that
# has the other. Each is found by its own name, as an estate of its own. They
# are ONE RECORD: `park.sh` files a family under `family.yaml`'s `id:` and
# merges each member's block by that member's own id, so a park from either
# replaces the member blocks a park from the other one wrote (#243).
# NOTHING DE-DUPLICATES THEM, deliberately: which copy holds the work is not
# this command's to decide, and it moves nothing. The holder's row says so,
# naming every folder of that id and the one to keep - the folder named for
# the repository, v1.1.0's rule and where `make siblings` moves a holder
# (opensoft/openRepoShape#543). `resume` refuses to choose between two such
# holders for the same reason.
read_family_layouts() {
    local holder="$1" id folder folders="" count=0 keep="" keeps=0
    yaml_scalar "$holder/family.yaml" id && [ -n "$YAML_VALUE" ] || return 0
    id="$YAML_VALUE"
    while IFS= read -r folder; do
        [ -n "$folder" ] || continue
        count=$((count + 1))
        folders="${folders:+$folders and }$folder"
        if named_for_repository "$folder"; then
            keep="$folder"
            keeps=$((keeps + 1))
        fi
    done < <(family_folders_with_id "$id")
    [ "$count" -gt 1 ] || return 0
    if [ "$keeps" -eq 1 ]; then
        keep="Keep $keep, the folder named for its repository (openRepoShape v1.1.0's layout, where \`make siblings\` moves a holder)"
    else
        keep="Keep the one folder named for its repository's last part (openRepoShape v1.1.0's layout, where \`make siblings\` moves a holder)"
    fi
    finding "family \`$id\` is laid out $count times under the projects directory, at $folders: ONE record holds it, so a park from one replaces the member blocks a park from another wrote. $keep, and move the rest aside; nothing here moves either"
}

ESTATE_FINDINGS=0
ESTATE_REPOS=0
#: The footer's last clause, decided once by `--fetch`: what this run changed.
CHANGED_CLAUSE="nothing was changed."

# --- one estate -------------------------------------------------------------
#
# ESTATE_KIND / ESTATE_DIR / ESTATE_ROOT / ESTATE_NAME must already be set —
# by `estate_walk_up`, `estate_by_name`, `clone_with_origin`, or, for the
# sweep, a direct `estate_here` on a discovered child. RETURNS 1 when
# anything was found and 0 when nothing was, so the sweep can carry on and
# the single-estate call site can turn that straight into its exit code.
read_one_estate() {
    local label branch path current_root="" rel line rows
    ESTATE_FINDINGS=0
    ESTATE_REPOS=0
    if [ "$ESTATE_KIND" = "family" ]; then
        say "status: $ESTATE_NAME - the family holder at $ESTATE_ROOT"
        say "  the members' working clones beside it, and their legs, are read too."
    else
        say "status: $ESTATE_NAME - the assembly root at $ESTATE_ROOT"
    fi
    if [ "$FETCH" -eq 1 ]; then
        say "  fetched first (--fetch): origin — and upstream, where a fork has one — is asked in every repository below, then it is read."
    else
        say "  as of the last fetch: status reads local refs only and fetches nothing."
    fi
    # THE ROWS ARE READ ONCE and fetched in full before any of them is
    # printed, so "then it is read" above is the order this runs in — the
    # record layer included, which is read on the root's row and compares
    # against commits the LEGS' origins hold.
    rows="$(estate_repos)"
    [ "$FETCH" -eq 0 ] || fetch_estate_first "$rows"
    locate_record
    if [ -n "$RECORD_FILE" ]; then
        say "  parked record: $RECORD_FILE, as of its last pull"
    else
        say "  $RECORD_NOTE"
    fi
    say ""
    while IFS=$'\t' read -r label branch path; do
        [ -n "${path:-}" ] || continue
        FINDINGS=()
        FETCHED_OK=0
        UPSTREAM_FETCHED_OK=0
        ESTATE_REPOS=$((ESTATE_REPOS + 1))
        case "$label" in
        holder | root)
            current_root="$path"
            printf '  %-6s %-6s %s\n' "$label" "$branch" "$path"
            replay_fetch "$path"
            read_head "$path" "$branch"
            read_tracking "$path" "$branch"
            read_fork "$path" "$branch"
            if [ "$label" = holder ]; then
                read_shape "$path" family.yaml
                read_family_layouts "$path"
            elif [ "$ESTATE_KIND" = "family" ]; then
                read_shape "$path" project.yaml
                read_record "$path" "root=$ESTATE_NAME/${path##*/}"
            else
                read_shape "$path" project.yaml
                read_record "$path" "id=$RECORD_ID" "root=$ESTATE_NAME"
            fi
            read_worktree_state "$path"
            read_branches "$path" "$branch"
            read_worktrees "$path"
            ;;
        mounted)
            printf '  %-6s %-6s %s\n' "leg" "$branch" "$path"
            replay_fetch "$path"
            rel="${path#"$current_root"/}"
            read_leg "$current_root" "$rel" "$branch"
            read_fork "$path" "$branch"
            ;;
        unmounted)
            printf '  %-6s %-6s %s\n' "leg" "$branch" "$path"
            finding "declared in .gitmodules and not mounted (\`make bootstrap\` places it)"
            ;;
        missing)
            printf '  %-6s %-6s %s\n' "root" "$branch" "$path"
            finding "not here: no working clone beside the holder (\`resume $ESTATE_NAME\` places it)"
            ;;
        esac
        if [ ${#FINDINGS[@]} -eq 0 ]; then
            if [ "$label" = "mounted" ]; then
                say "    at its pin, which is origin/$branch's tip as of the last fetch; clean"
            else
                say "    in sync with origin/$branch; clean"
            fi
        else
            for line in "${FINDINGS[@]}"; do
                say "    - $line"
            done
            ESTATE_FINDINGS=$((ESTATE_FINDINGS + ${#FINDINGS[@]}))
        fi
    done <<<"$rows"
    say ""
    if [ "$ESTATE_REPOS" -eq 1 ]; then
        say "status: $ESTATE_NAME - $ESTATE_FINDINGS finding(s) in 1 repository; $CHANGED_CLAUSE"
    else
        say "status: $ESTATE_NAME - $ESTATE_FINDINGS finding(s) in $ESTATE_REPOS repositories; $CHANGED_CLAUSE"
    fi
    [ "$ESTATE_FINDINGS" -eq 0 ]
}

# --- every estate -----------------------------------------------------------

# Every estate under the projects directory, `<name><TAB><dir>` per row, in
# NAME order — `park`'s own discovery, spelled the same way and for the same
# reason: `~/projects` and `~/Projects` can each hold estates when both
# exist, and name order reads them together.
all_estate_dirs() {
    local dir child
    for dir in "${PROJECTS_DIRS[@]}"; do
        [ -d "$dir" ] || continue
        for child in "$dir"/*; do
            [ -d "$child" ] || continue
            if is_family_folder "$child" || is_project_root "$child"; then
                printf '%s\t%s\n' "${child##*/}" "$child"
            fi
        done
    done | LC_ALL=C sort -t $'\t' -k1,1
}

# Read every estate in `$1`, in name order, and end with a summary. An estate
# with findings does not stop the others being read; the exit code is 1 when
# any had findings. NOTHING IS SKIPPED HERE: `park`'s sweep skips a root
# without the Speckit overlay because there is nothing to RUN in it; there is
# always something to read.
status_every_estate() {
    local rows="$1" name dir total=0 bad=0 estate_status line
    local -a summary=()
    while IFS=$'\t' read -r name dir; do
        [ -n "$name" ] || continue
        estate_here "$dir" || continue
        say "=== status $ESTATE_NAME ==="
        total=$((total + 1))
        estate_status=0
        read_one_estate || estate_status=$?
        if [ "$estate_status" -eq 0 ]; then
            summary+=("  in sync    $ESTATE_NAME")
        else
            summary+=("  findings   $ESTATE_NAME ($ESTATE_FINDINGS)")
            bad=$((bad + 1))
        fi
        say ""
    done <<<"$rows"
    say "status: summary"
    if [ ${#summary[@]} -gt 0 ]; then
        for line in "${summary[@]}"; do
            say "$line"
        done
    fi
    say "status: $((total - bad)) estate(s) in sync, $bad with findings; $CHANGED_CLAUSE"
    [ "$bad" -eq 0 ]
}

# --- arguments -------------------------------------------------------------

# THE VALUE OF A VALUE-TAKING FLAG, in either spelling, read in ONE place so
# that the ways of not giving one are ONE refusal. `flag_value "$@"` from an
# arm sets `FLAG_VALUE` and `FLAG_SHIFT` — the arguments that arm then
# consumes, 2 for `--flag value` and 1 for `--flag=value` — or it returns 1
# with `FLAG_HINT` carrying whatever that refusal has to add. THE WORDS STAY
# AT THE ARM: each flag says what its own value is, and this says only whether
# one was given.
# A VALUE BEGINNING WITH `-` IS NOT ONE in the spaced spelling — the hole #17
# named and left open. `park Atlas --lane --dry-run` ran a REAL park with the
# lane set to `--dry-run`: the flag a person typed was swallowed as a value,
# `--dry-run` itself never took effect, and the only sign was a WIP commit
# subject labelled with it. Nothing here swallows the next word whatever it
# is, so that is a refusal.
# `--flag=value` IS THE OTHER HALF, and the escape that refusing `-*` would
# otherwise foreclose: a value that really does begin with `-` is spelled
# `--lane=--weird`, which takes whatever follows the FIRST `=` and asks
# nothing about it. An `=` with nothing after it is an empty value and is
# refused as #17 refuses one.
# BYTE-IDENTICAL IN `park`, `resume` AND `status`, held by
# `tests/test_repo_hygiene.py` the way the estate resolver and `expand_home`
# are: three files a person has on PATH, and one answer to what a flag's value
# is.
flag_value() {
    FLAG_VALUE=""
    FLAG_SHIFT=2
    FLAG_HINT=""
    case "$1" in
    *=*)
        FLAG_SHIFT=1
        FLAG_VALUE="${1#*=}"
        [ -n "$FLAG_VALUE" ] || return 1
        return 0 ;;
    esac
    [ $# -ge 2 ] && [ -n "$2" ] || return 1
    # THE NEXT ARGUMENT, ONLY IF IT CANNOT BE A FLAG. The hint names the `=`
    # spelling because that is the whole exit: this refuses nothing a person
    # cannot then say.
    case "$2" in
    -*)
        FLAG_HINT="
    \`$2\` begins with a \`-\`, so it is a flag and not this one's value:
    nothing here swallows the next word whatever it is. A value that really
    does begin with \`-\` is given after an \`=\`:

      $1=$2"
        return 1 ;;
    esac
    FLAG_VALUE="$2"
}

while [ $# -gt 0 ]; do
    case "$1" in
    -h | --help) usage; exit 0 ;;
    -a | --all) STATUS_ALL=1; shift ;;
    # A MISSING, EMPTY OR FLAG-SHAPED VALUE IS A REFUSAL, exit 2, in this
    # command's own words: `${2:?…}` would be bash's message and bash's exit 1
    # — and 1 is what this command means by "findings were printed", so a typo
    # must not be able to read as a report.
    # EMPTY IS REFUSED THE SAME WAY, and so is a flag where a value belongs:
    # `flag_value` above asks all three questions once for both arms below,
    # and each arm keeps its own words. THESE TWO WERE NEVER
    # `${2:?…}`: they have been arity-only since this file's first commit, so
    # when Copilot's review of #14 said that `[ $# -ge 2 ]` checks only that
    # an argument slot exists — said of `park` and `resume`, which had just
    # swapped `${2:?…}` for exactly that — `status` was not in front of it,
    # and it kept the hole the other two then closed. It is the same hole:
    # every value here is read further down as "was this given at all?", so
    # `status --repo ""` fell past the `--repo` branch of the resolver into
    # the walk-up and reported the estate around the cwd as though `--repo`
    # had never been typed. A wrong answer is worse here than in either verb,
    # not better: nothing was changed, so there is nothing to undo and no
    # reason for anybody to look twice.
    --repo | --repo=*)
        flag_value "$@" ||
            die "--repo needs a value: <owner>/<name>, or any url spelling of one.$FLAG_HINT"
        REPO_SLUG="$FLAG_VALUE"; REPO_ARG="$REPO_SLUG"; shift "$FLAG_SHIFT" ;;
    --name | --project | --name=* | --project=*)
        flag_value "$@" ||
            die "${1%%=*} needs a value: one estate folder name under your projects directory.$FLAG_HINT"
        NAME="$FLAG_VALUE"; shift "$FLAG_SHIFT" ;;
    # THE SECOND LAYER (Brett Heap's RULING of 2026-09-11, "next layer:
    # --fetch"): ask origin in every repository first. See `fetch_remote`.
    --fetch) FETCH=1; CHANGED_CLAUSE="only remote-tracking refs and fetched objects were changed (--fetch)."; shift ;;
    -*)
        usage >&2
        die "$SELF_NAME does not know $1. Its flags are listed above; it takes nothing for
    the extension, because it runs nothing."
        ;;
    *)
        [ -z "$NAME" ] ||
            die "two estate names, '$NAME' and '$1'. One positional <Name>, then flags."
        NAME="$1"; shift ;;
    esac
done

[ -z "$NAME" ] || [ -z "$REPO_SLUG" ] ||
    die "both <Name> ('$NAME') and --repo '$REPO_SLUG'. One estate per run: name it
    either way, not both."
if [ "$STATUS_ALL" -eq 1 ]; then
    [ -z "$NAME" ] ||
        die "both <Name> ('$NAME') and --all. \`--all\` reads EVERY estate under your
    projects directory; <Name> reads one. Drop one of them."
    [ -z "$REPO_SLUG" ] ||
        die "both --repo '$REPO_ARG' and --all. \`--all\` reads EVERY estate under your
    projects directory; \`--repo\` reads the one that clone belongs to. Drop one
    of them."
fi

# GIT IS THE WHOLE MECHANISM, and every call below is `|| true`-guarded so a
# missing ref reads as a finding rather than a crash — which means a git that
# cannot run AT ALL would otherwise be reported as an estate with no commits,
# no branches and no legs, exit 1, and no line saying nothing was read.
# `--no-optional-locks` is git 2.15 (2017) and is what makes this command take
# no lock, so a git that does not know it is refused BY NAME rather than
# answered wrongly. `--help` has already exited above and needs neither.
command -v git >/dev/null 2>&1 ||
    die "no \`git\` on PATH, so nothing can be read."
git --no-optional-locks --version >/dev/null 2>&1 ||
    die "this \`git\` does not know \`--no-optional-locks\` (git 2.15, 2017), which is
    what stops \`$SELF_NAME\` taking a lock on a repository somebody else is using.
    Upgrade git and re-run; every answer here would otherwise be read off a git
    that refused every command."

# --- resolve ---------------------------------------------------------------
if [ "$STATUS_ALL" -eq 1 ]; then
    ALL_ROWS="$(all_estate_dirs)"
    [ -n "$ALL_ROWS" ] ||
        die "--all found no estate under ${PROJECTS_DIRS[*]}, so nothing was read.
    An estate is a FAMILY folder (<Name>/<Name>/family.yaml) or a standalone
    project root (<Name>/project.yaml), one level under your projects
    directory; \`resume <Name>\` brings one back."
    say "status --all: reading every estate under ${PROJECTS_DIRS[*]}:"
    say ""
    say "$(estates_or_none)"
    say ""
elif [ -n "$REPO_SLUG" ]; then
    # ANY SPELLING A PERSON MIGHT PASTE, through the same reader that reads a
    # clone's `origin` — `park`'s rule, for `park`'s reasons.
    REPO_SLUG="$(repo_slug_of "$REPO_SLUG" || true)"
    [ -n "$REPO_SLUG" ] || die "--repo '$REPO_ARG' is not a repository: it wants <owner>/<name>, or any
    url spelling of one (https://…/<owner>/<name>.git, git@host:<owner>/<name>).
    A family's members may live in another organisation, so there is no owner
    to assume, and a path on disk has none to read."
    MATCH="$(clone_with_origin "$REPO_SLUG" || true)"
    [ -n "$MATCH" ] || die "no clone under your projects directory has origin $REPO_SLUG.
    \`--repo\` names a clone you ALREADY HAVE; nothing here clones one. Bring the
    estate back with \`resume <Name>\`, or read one of these:

$(estates_or_none)"
    estate_walk_up "$MATCH" ||
        die "$MATCH is a clone of $REPO_SLUG, but nothing at or above it is an
    estate: no \`family.yaml\` holder beside it and no \`project.yaml\` root. It
    is a clone, not a project of this shape."
elif [ -n "$NAME" ]; then
    is_safe_folder_name "$NAME" ||
        die "'$NAME' is not an estate name: it is a path, or it carries a
    character an estate folder may not. <Name> is ONE folder name under your
    projects directory — no slash, no leading dot. To read a checkout
    somewhere else, stand in it and run \`$SELF_NAME\` with no <Name>."
    estate_by_name "$NAME" || die "no estate named '$NAME' under your projects directory.
    An estate is a FAMILY folder ($NAME/$NAME/family.yaml) or a standalone
    project root ($NAME/project.yaml). Bring it back with \`resume $NAME\`, or
    read one of these:

$(estates_or_none)"
else
    # THE WALK-UP WINS FIRST, as it does for `park`: standing inside an estate
    # reads THAT estate. Outside every estate, every estate is read — WITHOUT
    # the question `park` asks, because a read changes nothing and the
    # answer to "which estate did you mean" is, for a report, all of them.
    if ! estate_walk_up "$PWD"; then
        ALL_ROWS="$(all_estate_dirs)"
        [ -n "$ALL_ROWS" ] ||
            die "no estate around $PWD, no <Name> was given, and there is no estate under
    ${PROJECTS_DIRS[*]} at all, so there is nothing to read. An estate is a
    FAMILY folder (<Name>/<Name>/family.yaml) or a standalone project root
    (<Name>/project.yaml), one level under your projects directory;
    \`resume <Name>\` brings one back."
        STATUS_ALL=1
        say "status: no estate around $PWD; reading every estate under ${PROJECTS_DIRS[*]}:"
        say ""
        say "$(estates_or_none)"
        say ""
    fi
fi

if [ "$STATUS_ALL" -eq 1 ]; then
    STATUS=0
    status_every_estate "$ALL_ROWS" || STATUS=$?
    exit "$STATUS"
fi

STATUS=0
read_one_estate || STATUS=$?
exit "$STATUS"
