Skip to content
Back to skills

Git Branch Consolidate

ASecurity

Audit every git worktree and branch for spec/plan/test completeness, then plan the consolidation down to one origin/main. Use for 'audit all worktrees', 'what's unmerged', 'consolidate branches', 'get everything to main', 'branch estate'.

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 26, 2026
ai-agentspythonrustgoshellbashtestinggitapi

Works with

  • terminal
  • api

Security analysis

A93/100
  • highPerforms destructive filesystem operations

Pro scans all 2 files and shows the line behind each finding

Scanned September 26, 2026

npx -y skills add austinmao/feature-fix-swarm --skill git-branch-consolidate --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Git Branch Consolidate?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Git Branch Consolidate
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/austinmao-git-branch-consolidate/badge)](https://www.skillsdirectory.com/skills/austinmao-git-branch-consolidate)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: git-branch-consolidate
description: "Audit every git worktree and branch for spec/plan/test completeness, then plan the consolidation down to one origin/main. Use for 'audit all worktrees', 'what's unmerged', 'consolidate branches', 'get everything to main', 'branch estate'."
version: "1.0.0"
user-invocable: true
---

# Git Branch Consolidate

## Host dispatch contract

- Codex: `$skill`, Codex collaboration roles, and GPT-5.6 tiers.
- Claude: `/skill`, Agent/Skill tools, and Claude aliases.
- A bare `/skill` in this shared source denotes the Claude form; Codex dispatches the same named skill as `$skill`.

Answers two questions with evidence, never recall:

1. **What is actually unfinished?** Per branch: does its content already live in
   the base branch, what does it still owe, does it have tests, and is its
   spec/plan/tasks trail complete.
2. **What is the shortest ordered path to one `origin/main`?** Merge set, cleanup
   set, and the testing gaps that must close first.

Read-only toward every branch. Never checks out, fetches, merges, or deletes —
it emits the commands and stops. Execution of the deletion set belongs to
`/git-branch-cleanup`, which owns the approval flow.

## Step 1 — Collect (deterministic, ~30s)

```bash
python3 skills/git-branch-consolidate/scripts/collect-estate.py \
  --repo "$(git rev-parse --show-toplevel)" --base main \
  > "${TMPDIR:-/tmp}/estate-$(date -u +%Y%m%d-%H%M).json"
```

Per branch: `landed` · `residual_files` / `residual_code` · `src_files` /
`test_files` / `test_gap` · `spec` artifacts + `tasks.md` counts · `.planning`
ROADMAP+STATE · worktree dirt · PR state · `disposition`.

**`landed` is the load-bearing field.** `git merge-base --is-ancestor` alone
reports every squash-merged branch as unmerged — on a repo with a squash-merge
convention this yields dozens of false positives. `landed` is true when ancestry
OR a merged PR OR zero residual code says the base branch already carries the
content. Trust `landed`, not `ahead`.

`--no-gh` skips the PR lookup (offline / rate-limited). Dispositions then lose
`merge-ready` and `stale-abandoned` — say so in the report.

## Step 2 — Fan out (every spawn model-pinned; contracts per `docs/model-tiers.md`)

| Agent | Tier | Contract | Job |
|---|---|---|---|
| spec-trail scout | volume | scout ≤15 | For each `review-then-land` spec: spec.md/plan.md/tasks.md present? open checkboxes? evidence dir populated? |
| testing analyst | execution | build ≤20 | Every `test_gap` branch + every branch whose residual touches an API surface, auth, access-control, or schema migrations. Name the missing suite, not "add tests". |
| runner analyst | execution | build ≤20 | Worktrees with a live `.planning/run-state/gsd-run.pid` or `status != complete`: progressing / stopped-at-gate / stalled / dead. |
| conflict scout | volume | scout ≤15 | For the proposed merge set only: which branches touch the same files (collision map + suggested order). |
| risk assessor | judgment | deep ≤40 | Conclusion first: what blocks consolidation, what only the operator can decide, the ordered next 3. |

Synthesis reconciles contradictions and names which report won. Producer ≠
reviewer: the synthesizer never re-runs a scout's search.

Run `/spec-status NNN` only for specs the collector flags as **not landed with an
open tasks trail** — it is expensive and most specs are already closed.

## Step 3 — Report

Write `.planning/ESTATE-<UTC yyyymmdd-HHMM>.md`:

- **Headline** — N branches, N landed, N owing work, N testing gaps.
- **Merge set** — ordered, with the reason each is safe to land and its gate command.
- **Testing gaps** — branch → the specific suite missing → the command that would prove it.
- **Cleanup set** — `delete-safe` branches + their worktrees, with the exact
  `git worktree remove` / `git branch -d` lines. Never `-D`.
- **Blocked / operator-reserved** — each with its one-command unblock.
- **Next 3** — from the risk assessor.

Print headline + merge set + Next 3 inline; the file carries the rest.

## Step 4 — Execute (only on explicit approval, one step at a time)

Order matters: **land before cleanup**, or you delete the only copy.

1. Merge the `merge-ready` set (grant-gated; `/review-gate` first on
   auth/access-control/payments/infra diffs).
2. Re-run Step 1. `landed` must have grown by exactly the merged count.
3. Hand the refreshed `delete-safe` set to `/git-branch-cleanup` — it owns the
   deletion approval flow. Remove worktrees before their branches.
4. Detached-HEAD build worktrees whose `head` is an ancestor of the base branch
   are pure scratch: `git worktree remove`, no branch to delete.

## Step 4-A — Autonomous consolidation (spec-006 Phase 3, REQ-301/302/305)

`--autonomous` closes the queue-to-estate loop WITHOUT widening MAX-AUTH:
authority is an exact queue-derived `consolidate:estate:<sha256(target
tuples)>` grant minted by the landed queue itself (land-queue.sh at its
all-items-terminal boundary, via `queue-journal.py read-landed-tuples` and
`gates.py grant-consolidate`), never prose, scouts, or shell arguments.

**Report-only is the default.** Every run rebuilds the canonical target set
`(branch ref, expected tip OID, PR#, observed merge commit)` from the durable
queue journal's `read-landed-tuples` projection for the exact queue id (the
intake collector carries no PR/merge identity and is never consulted),
refreshes `collect-estate.py`, requires `landed==true`, checks the
exact scope-aware grant, and runs `assert-merged.sh` once per target PR —
then prints the evidence and the PLANNED `run-finalizer.sh` delegation and
deletes nothing.  Explicit `--execute` (activated after the Phase 3
checkpoint review) reruns the exact scope-aware grant check, rereads the
local ref OID (when run inside a git worktree) and the PR head OID
immediately before EVERY effect, and delegates each deletion exactly once to
`bash scripts/gsd/run-finalizer.sh` — the sole deletion owner, whose
merged-head proof holds the only sanctioned `git branch -D`.  The finalizer
is fail-soft: the block prints its exit code and the observed ref
postcondition rather than treating rc 0 alone as deletion proof.
Execution is idempotent after the queue's own cleanup (WR-01): a missing
local ref is accepted only in the defined already-finalized state — the
exact PR head re-verified against the journal-pinned OID — reporting
completion when the remote ref is gone too, and otherwise delegating the
remaining remote cleanup to the finalizer under the journal-pinned
identity.  A drifted or unreadable head still refuses before any effect.

Environment contract: `CONSOLIDATE_RUN_ID` (queue run id owning the grant),
`CONSOLIDATE_EVIDENCE_DIR` (must contain `grant`, `fresh-estate`,
`target-set`, `assert-merged`), optional `CONSOLIDATE_QUEUE_ID` (journal
queue id, default the run id), `CONSOLIDATE_QUEUE_STORE` (land-queue journal
store, default `$(lib/gates.py store-dir)/land-queue`), `CONSOLIDATE_REPO`
(default `.`) and `CONSOLIDATE_BASE` (default `main`).  Run from the
repository root.

<!-- PHASE3_CONSOLIDATE_BEGIN -->
```bash
set -euo pipefail

# ── mode: report-only unless an EXPLICIT --execute is passed ─────────────
MODE="report-only"
for ARG in "$@"; do
  case "$ARG" in
    --execute) MODE="execute" ;;
    --autonomous) : ;;
    *) echo "CONSOLIDATE-REFUSED:unknown-flag $ARG"; exit 1 ;;
  esac
done

RUN_ID="${CONSOLIDATE_RUN_ID:?CONSOLIDATE_RUN_ID (queue run id) is required}"
EVIDENCE_DIR="${CONSOLIDATE_EVIDENCE_DIR:?CONSOLIDATE_EVIDENCE_DIR is required}"
REPO="${CONSOLIDATE_REPO:-.}"
BASE="${CONSOLIDATE_BASE:-main}"

# ── one physical repository (CR-07): resolve, validate, pin cwd ──────────
# Tool paths stay anchored to the invoking checkout; every repo-sensitive
# proof and effect below runs from the SAME resolved physical root that is
# baked into the grant scope — proofs and deletions can never split across
# checkouts.
TOOLS="$PWD"
REPO_ROOT="$( { cd -- "$REPO" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null; } || true )"
if [ -z "$REPO_ROOT" ]; then
  echo "CONSOLIDATE-REFUSED:repo CONSOLIDATE_REPO is not a git repository: $REPO"
  exit 1
fi
REPO_ROOT="$( cd -- "$REPO_ROOT" && pwd -P )"
cd -- "$REPO_ROOT"

# ── evidence conjunction: every member present, refused BY NAME ──────────
for EV in grant fresh-estate target-set assert-merged; do
  if [ ! -f "$EVIDENCE_DIR/$EV" ]; then
    echo "CONSOLIDATE-REFUSED:missing-evidence $EV"
    exit 1
  fi
done

WORK="$(mktemp -d "${TMPDIR:-/tmp}/consolidate-4a.XXXXXX")"
trap 'rm -rf "$WORK"' EXIT

# ── canonical manifest: the durable queue journal is the ONLY source ─────
# (CR-01) The intake collector cannot carry PR/merge identity and returns an
# empty item list in production; consolidation identity comes exclusively
# from the journal read-landed-tuples projection for this exact queue id.
QUEUE_ID="${CONSOLIDATE_QUEUE_ID:-$RUN_ID}"
QUEUE_STORE="${CONSOLIDATE_QUEUE_STORE:-}"
if [ -z "$QUEUE_STORE" ]; then
  QUEUE_STORE="$("$TOOLS/lib/gates.py" store-dir)/land-queue"
fi
if ! "$TOOLS/skills/land-queue/scripts/queue-journal.py" read-landed-tuples   --store "$QUEUE_STORE" --queue-id "$QUEUE_ID" > "$WORK/tuples"; then
  echo "CONSOLIDATE-REFUSED:target-set queue journal read failed for queue $QUEUE_ID"
  exit 1
fi
"$TOOLS/skills/git-branch-consolidate/scripts/collect-estate.py"   --repo "$REPO_ROOT" --base "$BASE" > "$WORK/estate.json"

# ── canonical tuple validation + exact target-set digest (no shell JSON) ─
if ! python3 - "$WORK/tuples" "$WORK/estate.json" "$BASE" "$REPO_ROOT" "$WORK/plan" <<'PYVALIDATE'
import hashlib, json, re, sys
tuples_path, estate_path, base, repo_root, out_path = sys.argv[1:6]
OID = re.compile(r"^[0-9a-f]{40}$")
PRN = re.compile(r"^[0-9]{1,9}$")
def refuse(reason):
    print("CONSOLIDATE-REFUSED:" + reason)
    sys.exit(3)
try:
    raw = open(tuples_path, "rb").read()
except Exception:
    refuse("target-set journal projection unreadable")
parts = raw.split(b"\0")
if parts and parts[-1] == b"":
    parts.pop()
if not parts:
    refuse("target-set empty target set")
if len(parts) % 4:
    refuse("target-set malformed journal projection")
try:
    fields = [part.decode("utf-8") for part in parts]
except UnicodeDecodeError:
    refuse("target-set malformed journal projection")
seen, tuples = set(), []
for i in range(0, len(fields), 4):
    b, h, pr, m = fields[i:i + 4]
    if not b or any(c.isspace() for c in b):
        refuse("target-set malformed branch ref")
    if b == base:
        refuse("target-set base branch is never a deletion target: " + b)
    if not OID.match(h):
        refuse("target-set malformed expected tip OID for " + b)
    if not PRN.match(pr):
        refuse("target-set malformed PR number for " + b)
    if not OID.match(m):
        refuse("not-landed no observed merge commit for " + b)
    key = (b, h)
    if key in seen:
        refuse("duplicate-target " + b)
    seen.add(key)
    tuples.append((b, h, str(int(pr)), m))
try:
    est = json.load(open(estate_path))
except Exception:
    refuse("fresh-estate estate output unreadable")
# CR-02: fail closed — exactly ONE estate row per target, with the literal
# boolean true.  A missing record is not positive fresh evidence.
by_branch, dup = {}, set()
for rec in (est.get("branches") or []) if isinstance(est, dict) else []:
    if isinstance(rec, dict) and isinstance(rec.get("branch"), str):
        if rec["branch"] in by_branch:
            dup.add(rec["branch"])
        by_branch[rec["branch"]] = rec
for b, h, pr, m in tuples:
    if b in dup:
        refuse("fresh-estate duplicate estate rows for " + b)
    rec = by_branch.get(b)
    if rec is None:
        refuse("fresh-estate target missing for " + b)
    if rec.get("landed") is not True:
        refuse("fresh-estate landed!=true for " + b)
digest = hashlib.sha256(json.dumps(
    [repo_root, base, sorted([[b, h, p, m] for b, h, p, m in tuples])],
    separators=(",", ":")).encode()).hexdigest()
with open(out_path, "w") as fh:
    fh.write(digest + "\n")
    for b, h, pr, m in tuples:
        fh.write("\t".join((b, h, pr, m)) + "\n")
PYVALIDATE
then
  exit 1
fi

read -r DIGEST < "$WORK/plan"
SCOPE="consolidate:estate:$DIGEST"

# ── exact target-set equality: rebuilt digest vs the granted manifest ────
if [ -n "${CONSOLIDATE_SCOPE:-}" ] && [ "$SCOPE" != "$CONSOLIDATE_SCOPE" ]; then
  echo "CONSOLIDATE-REFUSED:target-set mismatch rebuilt scope $SCOPE != granted scope $CONSOLIDATE_SCOPE"
  exit 1
fi

# ── exact scope-aware grant check (queue-derived scope, never substituted)
if ! "$TOOLS/lib/gates.py" check-grant "$RUN_ID" --action "$SCOPE"; then
  echo "CONSOLIDATE-REFUSED:grant no exact unexpired queue-derived grant for scope $SCOPE"
  exit 1
fi

# ── one green assert-merged per canonical target PR ──────────────────────
while IFS="$(printf '\t')" read -r T_BRANCH T_OID T_PR T_MERGE; do
  if ! "$TOOLS/scripts/gsd/assert-merged.sh" "$T_PR"; then
    echo "CONSOLIDATE-REFUSED:assert-merged PR $T_PR is not proven merged (branch $T_BRANCH)"
    exit 1
  fi
done < <(tail -n +2 "$WORK/plan")

# ── post-proof tip confirmation: observed PR head must equal expected OID ─
while IFS="$(printf '\t')" read -r T_BRANCH T_OID T_PR T_MERGE; do
  OBSERVED="$(gh pr view "$T_PR" --json headRefOid -q .headRefOid < /dev/null)" || {
    echo "CONSOLIDATE-REFUSED:oid-drift head OID for PR $T_PR unreadable"
    exit 1
  }
  if [ "$OBSERVED" != "$T_OID" ]; then
    echo "CONSOLIDATE-REFUSED:oid-drift branch $T_BRANCH tip moved from $T_OID to $OBSERVED after proof"
    exit 1
  fi
done < <(tail -n +2 "$WORK/plan")

# ── evidence report + finalizer delegation plan ──────────────────────────
echo "CONSOLIDATE REPORT mode=$MODE run=$RUN_ID"
echo "EVIDENCE grant=ok scope=$SCOPE"
echo "EVIDENCE fresh-estate=ok landed=true for every target"
echo "EVIDENCE target-set=ok digest=$DIGEST"
while IFS="$(printf '\t')" read -r T_BRANCH T_OID T_PR T_MERGE; do
  echo "EVIDENCE assert-merged=ok pr=$T_PR"
  echo "EVIDENCE tip-oid=confirmed pr=$T_PR oid=$T_OID"
  echo "TARGET branch=$T_BRANCH oid=$T_OID pr=$T_PR merge=$T_MERGE"
  echo "PLANNED-FINALIZER: bash scripts/gsd/run-finalizer.sh --run-id $RUN_ID --expected-head $T_OID $T_PR"
done < <(tail -n +2 "$WORK/plan")

if [ "$MODE" = "execute" ]; then
  # ── execute: immediate pre-effect rechecks, then finalizer-only delegation
  # Races between proof and effect fail closed: the exact scope-aware grant
  # and the target tip OID are rechecked immediately before EVERY effect.
  DELEGATION_FAILED=0
  while IFS="$(printf '\t')" read -r T_BRANCH T_OID T_PR T_MERGE; do
    if ! "$TOOLS/lib/gates.py" check-grant "$RUN_ID" --action "$SCOPE" < /dev/null; then
      echo "CONSOLIDATE-REFUSED:grant expired, revoked, or scope-substituted at the effect boundary (scope $SCOPE)"
      exit 1
    fi
    ALREADY_FINALIZED=0
    if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
      # CR-06/WR-01: inside a git worktree the pre-effect fence requires the
      # local ref to exist AND match — except in the DEFINED already-finalized
      # state the queue's own fail-soft finalizer produces.  Blind absence is
      # never success: a missing local ref is accepted ONLY after the exact
      # PR head re-verifies against the journal-pinned OID, and then
      #   remote ref also gone -> the target is proven finished: report
      #                           completion, delegate nothing;
      #   remote ref survives  -> remaining remote cleanup routes through
      #                           run-finalizer.sh under the journal-pinned
      #                           identity (its merged-head proof still owns
      #                           the only sanctioned deletion).
      if LOCAL_OID="$(git rev-parse -q --verify "refs/heads/$T_BRANCH" 2>/dev/null)"; then
        if [ "$LOCAL_OID" != "$T_OID" ]; then
          echo "CONSOLIDATE-REFUSED:oid-drift local refs/heads/$T_BRANCH is $LOCAL_OID, expected $T_OID"
          exit 1
        fi
        # CR-04 (round 3): the LIVE remote ref must ALSO equal the pinned
        # OID.  The historical PR headRefOid and the local ref can both
        # match while the remote branch NAME was recreated or advanced with
        # new, unmerged work — a name-only remote delete would destroy it.
        REMOTE_ROW="$(git ls-remote origin "refs/heads/$T_BRANCH" < /dev/null)" || {
          echo "CONSOLIDATE-REFUSED:oid-drift remote refs/heads/$T_BRANCH unreadable at the pre-effect fence"
          exit 1
        }
        if [ -n "$REMOTE_ROW" ]; then
          REMOTE_OID="$(printf '%s\n' "$REMOTE_ROW" | head -1 | cut -f1)"
          if [ "$REMOTE_OID" != "$T_OID" ]; then
            echo "CONSOLIDATE-REFUSED:oid-drift remote refs/heads/$T_BRANCH is $REMOTE_OID, expected $T_OID"
            exit 1
          fi
        fi
      else
        HEAD_ABSENT="$(gh pr view "$T_PR" --json headRefOid -q .headRefOid < /dev/null)" || {
          echo "CONSOLIDATE-REFUSED:oid-drift head OID for PR $T_PR unreadable while local refs/heads/$T_BRANCH is missing"
          exit 1
        }
        if [ "$HEAD_ABSENT" != "$T_OID" ]; then
          echo "CONSOLIDATE-REFUSED:oid-drift local refs/heads/$T_BRANCH is missing and PR $T_PR head moved from $T_OID to $HEAD_ABSENT"
          exit 1
        fi
        REMOTE_ROW="$(git ls-remote origin "refs/heads/$T_BRANCH" < /dev/null)" || {
          echo "CONSOLIDATE-REFUSED:oid-drift remote refs/heads/$T_BRANCH unreadable while the local ref is missing"
          exit 1
        }
        if [ -z "$REMOTE_ROW" ]; then
          echo "POSTCONDITION branch=$T_BRANCH already-finalized (local and remote refs gone; PR $T_PR head $T_OID verified)"
          ALREADY_FINALIZED=1
        else
          # CR-04 (round 3): a surviving remote row is delegable ONLY when
          # its live OID equals the journal-pinned OID — any drift refuses
          # before the finalizer is reached.
          REMOTE_OID="$(printf '%s\n' "$REMOTE_ROW" | head -1 | cut -f1)"
          if [ "$REMOTE_OID" != "$T_OID" ]; then
            echo "CONSOLIDATE-REFUSED:oid-drift remote refs/heads/$T_BRANCH is $REMOTE_OID, expected $T_OID (local ref missing)"
            exit 1
          fi
          echo "REMOTE-CLEANUP-REMAINING branch=$T_BRANCH local ref gone, remote survives; delegating to run-finalizer.sh under the journal-pinned identity"
        fi
      fi
    fi
    if [ "$ALREADY_FINALIZED" -eq 1 ]; then
      continue
    fi
    REREAD="$(gh pr view "$T_PR" --json headRefOid -q .headRefOid < /dev/null)" || {
      echo "CONSOLIDATE-REFUSED:oid-drift head OID for PR $T_PR unreadable at the effect boundary"
      exit 1
    }
    if [ "$REREAD" != "$T_OID" ]; then
      echo "CONSOLIDATE-REFUSED:oid-drift branch $T_BRANCH tip moved from $T_OID to $REREAD between check and effect"
      exit 1
    fi
    # sole deletion owner: delegate exactly as the landed Phase 2 caller does;
    # the finalizer is fail-soft, so rc alone is never treated as deletion
    # proof.  CR-04: the journal-pinned OID rides along as --expected-head so
    # the finalizer's remote cleanup is compare-and-delete, never name-only.
    FIN_RC=0
    bash "$TOOLS/scripts/gsd/run-finalizer.sh" --run-id "$RUN_ID" --expected-head "$T_OID" "$T_PR" < /dev/null || FIN_RC=$?
    echo "DELEGATED run-finalizer.sh rc=$FIN_RC pr=$T_PR branch=$T_BRANCH"
    if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
      if git rev-parse -q --verify "refs/heads/$T_BRANCH" >/dev/null 2>&1; then
        echo "POSTCONDITION branch=$T_BRANCH still-present (finalizer kept or refused it)"
      else
        echo "POSTCONDITION branch=$T_BRANCH ref-gone"
      fi
    else
      echo "POSTCONDITION branch=$T_BRANCH not-observable (no git worktree at cwd)"
    fi
    [ "$FIN_RC" -eq 0 ] || DELEGATION_FAILED=1
  done < <(tail -n +2 "$WORK/plan")
  if [ "$DELEGATION_FAILED" -ne 0 ]; then
    echo "CONSOLIDATE-DELEGATION-INCOMPLETE: a finalizer delegation returned nonzero; inspect postconditions above"
    exit 1
  fi
  echo "execute complete: every deletion was delegated to run-finalizer.sh"
  exit 0
fi

echo "report-only: no deletion performed; run-finalizer.sh not invoked"
```
<!-- PHASE3_CONSOLIDATE_END -->

```bash verify
# Phase 3 autonomous-consolidation levers exist and stay wired
test -f "$REPO_ROOT/skills/git-branch-consolidate/SKILL.md"
test -f "$REPO_ROOT/scripts/gsd/land-queue.sh"
test -f "$REPO_ROOT/skills/land-queue/scripts/queue-journal.py"
test -x "$REPO_ROOT/lib/gates.py"
test -x "$REPO_ROOT/skills/land-queue/scripts/collect-queue.py"
test -x "$REPO_ROOT/skills/git-branch-consolidate/scripts/collect-estate.py"
test -f "$REPO_ROOT/scripts/gsd/assert-merged.sh"
test -f "$REPO_ROOT/scripts/gsd/run-finalizer.sh"
grep -q "PHASE3_CONSOLIDATE_END" "$REPO_ROOT/skills/git-branch-consolidate/SKILL.md"
grep -q "read-landed-tuples" "$REPO_ROOT/skills/land-queue/scripts/queue-journal.py"
grep -q "grant-consolidate" "$REPO_ROOT/scripts/gsd/land-queue.sh"
grep -q "consolidate:estate" "$REPO_ROOT/lib/gates.py"
```

## Constraints

- READ-ONLY until Step 4, which requires explicit per-step approval.
- Never `git add -A`; never `-D`; never delete an unmerged branch or one checked
  out in a worktree; never delete a worktree with uncommitted changes without
  showing the diff first.
- A dirty worktree is a finding, not an obstacle — report the file count, and
  route stalled work to `/adopt-wip` rather than discarding it.
- Secrets: filenames only, never content.

Files in this skill

  • SKILL.md21.3 KB
  • scripts/collect-estate.py14.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.