Skip to content
Back to skills

Stack Mode

ASecurity

Detects shared stack membership and iterates a command across all PRs in base-to-tip order. Use when a command supports --stack flag for multi-PR iteration.

  • 342 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 29, 2026
ai-agentsgobashgit

Security analysis

A100/100

Scanned May 29, 2026

npx -y skills add athola/claude-night-market --skill stack-mode --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Stack Mode?

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

Security grade badge for Stack Mode
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/athola-stack-mode/badge)](https://www.skillsdirectory.com/skills/athola-stack-mode)

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: stack-mode
description: Detects shared stack membership and iterates a command across all PRs in base-to-tip order. Use when a command supports --stack flag for multi-PR iteration.
alwaysApply: false
category: workflow-automation
tags:
- git
- stacked-diffs
- pr
- review
- fix-pr
- iteration
tools: []
complexity: medium
model_hint: standard
estimated_tokens: 1100
dependencies:
- sanctum:stack-create
- sanctum:stack-push
- sanctum:stack-rebase
- leyline:git-platform
---

# Stack Mode

Shared contract for commands that need to operate across a
whole stack of dependent PRs in one invocation.
Used by `/pr-review --stack` and `/fix-pr --stack`.

The goal is one simple thing: when a PR is part of a stack
targeting a common base branch, loop the command's normal
workflow across every PR in the stack in base-to-tip order
and emit one consolidated summary on the stack root.

## When to Use

Load this skill when a command accepts a `--stack` flag
(or auto-detects stack membership) and needs to iterate
its normal workflow across multiple PRs.

Do NOT load this skill for single-PR workflows. The
per-PR workflow stays unchanged; stack mode wraps it.

## Contract

A calling command MUST:

1. Accept a `--stack` boolean flag AND a `--base <branch>`
   override (default: `master`).
2. Call Step 1 below to resolve stack membership BEFORE
   running its main workflow.
3. Run its main workflow per PR in the resolved order.
4. Emit per-PR outputs unchanged (thread replies, issue
   links, etc.).
5. Emit ONE stack-level summary comment on the root PR
   using the format in Step 4.
6. Respect per-PR Gate rules (every PR still needs its own
   thread resolution and issue tracking).

A calling command MAY:

- Auto-detect stack membership and prompt the user rather
  than requiring `--stack` explicitly.
- Short-circuit iteration when a per-PR failure leaves
  downstream PRs with stale context.

## Required Progress Tracking

The caller creates `TodoWrite` items:

1. `stack-mode:membership-resolved`
2. `stack-mode:iteration-complete`
3. `stack-mode:root-summary-posted`

## Step 1: Resolve Stack Membership (`membership-resolved`)

Given a starting PR number `$PR_NUM` and optional base
`$BASE` (default: `master`), try three strategies in
order and stop at the first that yields a stack of size
>= 2.

### Strategy A: Branch-name convention (cheapest)

```bash
START_HEAD=$(gh pr view "$PR_NUM" \
  --json headRefName -q .headRefName)

# stack/<feature-name>/<slice-name>
if [[ "$START_HEAD" =~ ^stack/([^/]+)/.+$ ]]; then
  FEATURE="${BASH_REMATCH[1]}"
  PREFIX="stack/${FEATURE}/"

  # All local branches with the same prefix
  STACK_BRANCHES=$(git branch --list "${PREFIX}*" \
    | sed 's/^[* ]*//' | sort)

  # Map branches -> PR numbers (skip branches with no PR)
  STACK_PRS=()
  for branch in $STACK_BRANCHES; do
    pr=$(gh pr list --head "$branch" \
      --json number --jq '.[0].number')
    [ -n "$pr" ] && STACK_PRS+=("$pr")
  done

  echo "strategy=A prs=${STACK_PRS[*]}"
fi
```

### Strategy B: Stack summary comment (most reliable)

`stack-push` posts a `## Stack` markdown table on the
root PR with columns `# | Branch | PR`. Parse it:

```bash
# Find the root PR of the stack containing $PR_NUM
# The current PR may be the root, or may link to it via
# a "Part of stack `stack/<feature>`" phrase in its body
BODY=$(gh pr view "$PR_NUM" --json body -q .body)

# Look for a "## Stack" table in THIS PR's comments
TABLE=$(gh pr view "$PR_NUM" --json comments \
  --jq '.comments[] | select(.body | contains("## Stack"))
        | .body' | head -1)

# If not on this PR, look for the root PR reference
if [ -z "$TABLE" ]; then
  ROOT_REF=$(echo "$BODY" \
    | grep -oE 'stack/[^ `]+' | head -1)
  # ... resolve ROOT_REF -> root PR number and re-fetch
fi

# Parse the table: extract all `#<num>` from the PR column
STACK_PRS=($(echo "$TABLE" \
  | grep -oE '#[0-9]+' | tr -d '#' | sort -un))

echo "strategy=B prs=${STACK_PRS[*]}"
```

### Strategy C: Base-chain walk (fallback)

Walk both directions from `$PR_NUM`:

- Up: repeatedly resolve `baseRefName` until it equals
  `$BASE`. Each visited head is a stack member.
- Down: find open PRs whose `baseRefName` equals one of
  the visited heads. Recurse.

```bash
# Ascend from $PR_NUM to the root
CUR=$PR_NUM
CHAIN=($CUR)
while true; do
  BASE_REF=$(gh pr view "$CUR" \
    --json baseRefName -q .baseRefName)
  [ "$BASE_REF" = "$BASE" ] && break

  PARENT=$(gh pr list --head "$BASE_REF" \
    --state open --json number --jq '.[0].number')
  [ -z "$PARENT" ] && break

  CHAIN=("$PARENT" "${CHAIN[@]}")
  CUR=$PARENT
done

# Descend from the root collecting children
ROOT=${CHAIN[0]}
FRONTIER=("$ROOT")
while [ ${#FRONTIER[@]} -gt 0 ]; do
  PARENT=${FRONTIER[0]}
  FRONTIER=("${FRONTIER[@]:1}")

  PARENT_HEAD=$(gh pr view "$PARENT" \
    --json headRefName -q .headRefName)

  CHILDREN=($(gh pr list --base "$PARENT_HEAD" \
    --state open --json number --jq '.[].number'))

  for c in "${CHILDREN[@]}"; do
    CHAIN+=("$c")
    FRONTIER+=("$c")
  done
done

STACK_PRS=("${CHAIN[@]}")
echo "strategy=C prs=${STACK_PRS[*]}"
```

### Ordering

Regardless of strategy, order the final list **base-to-tip**
(root PR first, leaf PR last). The root is the PR whose
`baseRefName` equals `$BASE`.

```bash
ROOT_PR=""
for p in "${STACK_PRS[@]}"; do
  base=$(gh pr view "$p" --json baseRefName -q .baseRefName)
  if [ "$base" = "$BASE" ]; then
    ROOT_PR=$p
    break
  fi
done
```

If the stack size is 1, the command MUST fall back to
single-PR mode and emit a warning: `stack-mode: only one
PR found; --stack has no effect, proceeding as single-PR`.

### GitLab / Bitbucket

See `Skill(leyline:git-platform)` for `glab` / Bitbucket
equivalents of the `gh` calls above. The three strategies
translate directly: GitLab MR dependencies replace
base-chain walks, and the `glab mr view --output json`
shape matches `gh pr view`.

## Step 2: Confirm With User (if not explicit)

When the caller did NOT pass `--stack` but Step 1 resolved
a stack of size >= 2, print the stack and ask:

```
Detected stack of N PRs rooted at #<root>:
  1. #<root>   <title>
  2. #<p2>     <title>
  ...

Run the workflow on the full stack? [y/N]
```

Default to "N" (single-PR mode). With `--stack` explicit,
skip this prompt.

## Step 3: Iterate (`iteration-complete`)

Run the caller's main workflow once per PR in base-to-tip
order:

```bash
FAILED_PRS=()
for p in "${STACK_PRS[@]}"; do
  echo "=== stack-mode: PR #$p ($(($idx+1))/${#STACK_PRS[@]}) ==="

  # Caller hook: run the single-PR workflow on $p
  # This includes Gate 1 (thread resolution) and
  # Gate 2 (issue tracking) per the caller's contract.
  if ! run_single_pr_workflow "$p"; then
    FAILED_PRS+=("$p")
    # Stop iteration: downstream PRs may now be stale
    break
  fi
done
```

If any PR fails, the command MUST stop and report
`stack-mode: halted at #<pr>; downstream PRs untouched`.
Downstream PRs are intentionally left alone because
their context may have shifted.

## Step 4: Post Stack Summary on Root (`root-summary-posted`)

After successful iteration, post ONE consolidated summary
comment on the root PR. This does NOT replace per-PR
summaries: it links them together.

```markdown
## Stack <Command Name> Summary

**Stack**: N PRs rooted at #<root>, base `<base-branch>`
**Command**: <command> (e.g., /fix-pr, /pr-review)
**Run**: <timestamp>

| # | PR | Branch | Status | Per-PR Summary |
|---|----|--------|--------|----------------|
| 1 | #<root> | `stack/<feat>/<root-slice>` | <done/skipped/failed> | [link to comment] |
| 2 | #<p2>   | `stack/<feat>/<slice-2>`    | <done/skipped/failed> | [link to comment] |
| ... | ... | ... | ... | ... |

### Cross-PR Notes

- <Any observations that spanned multiple PRs>
- <Issues created that reference multiple stack PRs>
- <Base-PR-merged cascade reminders, if any>

### Next Steps

- If root PR merges, run `Skill(sanctum:stack-rebase)` to
  cascade the base update through descendants.
- Reviewers: rebase-triggered stale-review flags on
  descendants are expected after any force-push.
```

Post via `gh pr comment "$ROOT_PR" --body-file <file>`.

## Failure Modes and Recovery

| Failure | Detection | Recovery |
|---------|-----------|----------|
| Stack of size 1 | Step 1 returns one PR | Emit warning, fall back to single-PR mode |
| Base chain loops | Visited set on ascent | Halt, report `stack-mode: cycle detected` |
| Mid-stack PR failed its Gates | Caller raises | Halt iteration, leave downstream untouched |
| Strategy B table malformed | Parse fails | Fall through to Strategy C |
| Auth scope insufficient | `gh` returns 403 | Halt with clear error; caller surfaces to user |

## Notes

- Stack mode is ADDITIVE: single-PR behavior is unchanged.
  Only the iteration and root summary are new.
- Per-PR Gate 1 (thread resolution) and Gate 2 (issue
  tracking) remain unchanged: each PR still passes its
  own gates independently.
- Never merge Gate 1 across the stack. A resolved thread
  on one PR is not a resolved thread on another.
- The root summary is idempotent: re-running stack mode
  should update (not duplicate) the summary comment.
  Match on `## Stack <Command Name> Summary` as the key.
- `stack-mode` is a read/orchestration skill; it does NOT
  push, rebase, or edit branches. Those concerns are in
  `stack-create`, `stack-push`, and `stack-rebase`.

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.