WCAG 2.1 AA audit — perceivable, operable, understandable, robust criteria. Deep-dive for /launch-check accessibility.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Record per-PR CEO approval and merge in one turn. ONLY on an explicit per-PR "approved" — never on umbrella "go".
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
실행 점수 미검증
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
Writing: read docs/skills/shared/writing-rule.md before you write a durable artifact.
Write per-PR approval, then merge through the tracker adapter. Rationale: read docs/skills/approve-merge-rationale.md for gate history and AgDR references.
Invoke only on explicit user approval for this PR. A plan-level "go" is not merge approval. Stop and ask when the PR or intent is unclear. Only the human can invoke this skill (#1042).
The operator can also approve from one AskUserQuestion menu. Ask exactly Approve and merge <owner>/<repo>#<N> at <40-hex-sha>? with options Approve and merge and Not yet only. A hook writes the record; never write it yourself. See AgDR-0234.
Before addressing the user in any confirmation question or report, read the configured display title for the human per-PR merge approver:
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
APPROVER_TITLE=$(config_get_or '.review_markers.human_approver_title' 'CEO')
Use $APPROVER_TITLE in prose only. Keep the -ceo filename and marker fields unchanged.
If config or jq is unavailable, use CEO. This display setting must not block the skill.
Extract the PR number from $ARGUMENTS. If no number is given, try to infer from:
gh pr view --json number --jq '.number'If the PR number is ambiguous (multiple PRs on the branch, unclear which was approved), STOP and ask the user which PR.
Resolve REPO from owner/repo#N or an explicit owner/repo argument.
Otherwise, parse the current checkout's git remote get-url origin. Do not use ambient gh resolution or headRepository (#887).
Scope each host query to REPO, then PR_HOST_REPO after step 4.
Recognise --no-merge. It writes approval without merging.
Re-read the latest message and apply the one rule above. Treat a reply to your "Ready to merge PR #X — approved?" question as naming this PR. Stop on an umbrella "go" and ask:
"PR #X is ready to merge. Just confirming — explicit approval to merge PR #X, now?" Proceed only after an unambiguous per-PR approval.
Keep this bootstrap inline. It locates the script from workspace clones and split-portfolio forks (#229, #230, #559).
OPS_ROOT=""
PIN_FILE="${APEXYARD_OPS_PIN_DIR:-$HOME/.claude/apexyard}/ops-root-${CLAUDE_CODE_SESSION_ID:-}"
if [ -z "${APEXYARD_OPS_DISABLE_PIN:-}" ] && [ -n "${CLAUDE_CODE_SESSION_ID:-}" ] && [ -f "$PIN_FILE" ]; then
IFS= read -r OPS_ROOT < "$PIN_FILE" || OPS_ROOT=""
fi
# Validate the pin (self-heal a stale one): must satisfy a fork anchor.
if [ -n "$OPS_ROOT" ] && [ ! -f "$OPS_ROOT/.apexyard-fork" ] && \
{ [ ! -f "$OPS_ROOT/onboarding.yaml" ] || [ ! -f "$OPS_ROOT/apexyard.projects.yaml" ]; }; then
OPS_ROOT=""
fi
# Fallback: walk up from git toplevel (pre-#381 behaviour, safety net).
if [ -z "$OPS_ROOT" ]; then
r=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
while [ -n "$r" ] && [ "$r" != "/" ]; do
if [ -f "$r/.apexyard-fork" ] || { [ -f "$r/onboarding.yaml" ] && [ -f "$r/apexyard.projects.yaml" ]; }; then
OPS_ROOT="$r"; break
fi
r=$(dirname "$r")
done
fi
MARKER_HOME="${OPS_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
Run this command once. Capture stdout. Show stderr to the user.
CHECK_JSON=$(bash "$MARKER_HOME/.claude/skills/approve-merge/scripts/approve-merge.sh" <pr> "$REPO" "$MARKER_HOME")
CHECK_RC=$?
[ "$CHECK_RC" -eq 0 ] || exit "$CHECK_RC"
printf '%s' "$CHECK_JSON" | jq -e '
type == "object" and
(.marker_home | type == "string" and length > 0) and
(.pr_host_repo | type == "string" and length > 0) and
(.strategy | type == "string" and length > 0) and
(.head_sha | type == "string" and length > 0) and
(.release_class | type == "boolean")' >/dev/null || exit 1
MARKER_HOME=$(printf '%s' "$CHECK_JSON" | jq -r '.marker_home') || exit 1
PR_HOST_REPO=$(printf '%s' "$CHECK_JSON" | jq -r '.pr_host_repo') || exit 1
MERGE_STRATEGY=$(printf '%s' "$CHECK_JSON" | jq -r '.strategy') || exit 1
RELEASE_CLASS=$(printf '%s' "$CHECK_JSON" | jq -r '.release_class') || exit 1
case "$MERGE_STRATEGY" in
squash|merge|rebase) ;;
*) echo "Invalid merge strategy." >&2; exit 1 ;;
esac
source "$MARKER_HOME/.claude/hooks/_lib-review-markers.sh" || exit 1
CHECK_SHA=$(printf '%s' "$CHECK_JSON" | jq -r '.head_sha') || exit 1
HEAD_SHA=$(gh pr view <pr> --repo "$PR_HOST_REPO" --json headRefOid -q '.headRefOid') || exit 1
[ -n "$HEAD_SHA" ] && [ "$HEAD_SHA" = "$CHECK_SHA" ] || { echo "PR head changed after the check." >&2; exit 1; }
Stop on any non-zero exit, invalid JSON, or missing field. NEVER assume a pass. Accept only squash, merge, rebase as strategy values.
gh pr update-branch <pr> --repo "$REPO". Wait for green CI. Run the carry-over check before requesting a Rex delta review.
Use AskUserQuestion with "Run it yourself (Recommended)" and "Approve me to run it".
The first choice means the operator runs the command. The second permits the agent to push the branch update.
Update nothing before the operator chooses. If the harness lacks this tool, show the same choices as a numbered list.rm to delete the gate-invisible file named in stderr. Re-run /code-review <pr> without a marker path.The marker is a key/value file with required fields. The format:
sha=<40-char hex — must be the PR HEAD from step 3>
approved_by=user
approved_at=<ISO-8601 UTC timestamp, e.g. 2026-05-03T13:25:42Z>
skill_version=2
approval_summary="<truncated user approval message, ≤200 chars>"
Keep all marker fields below. Use the ops root and the repo-qualified review_marker_path helper (AgDR-0060, #765).
HEAD_SHA is the script's head_sha, confirmed by a fresh read in step 4.
# (MARKER_HOME and PR_HOST_REPO already resolved in step 4 — reuse them here.)
mkdir -p "$MARKER_HOME/.claude/session/reviews"
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Sanitise: drop newlines, drop shell-special chars, truncate to 200.
summary=$(echo "<user approval message>" | tr '\n' ' ' | tr -d '"`$\\' | cut -c1-200)
# CEO marker keyed on the BASE repo — same key as Rex's marker and the gate's
# lookup (#765). Keying it on the fork would leave a cross-fork merge blocked.
CEO=$(review_marker_path "$PR_HOST_REPO" <pr> ceo "$MARKER_HOME")
cat > "$CEO" <<EOF
sha=${HEAD_SHA}
approved_by=user
approved_at=${ts}
skill_version=2
approval_summary="${summary}"
EOF
Skip this step for --no-merge. Run this entire block in one shell.
Shell variables from step 4 do not carry over. The block re-runs the script and stops on a missing or invalid strategy or release_class (#1196). Fill in <marker_home>, <repo> and <pr> with literal values.
Pass those same <repo> and <pr> literals to tracker_pr_merge. The merge gate reads the command text before the shell expands variables (#1758).
The block stops when the checked host repo is not <repo>.
The tracker_pr_merge call MUST be invoked as a bare top-level statement, never inside $(...) or backticks. The merge-gate matcher reads the raw command text and may not fire on a wrapped call. Redirect stdout to a file, then read the result in a separate step.
MARKER_HOME="<marker_home>"
CHECK_JSON=$(bash "$MARKER_HOME/.claude/skills/approve-merge/scripts/approve-merge.sh" <pr> "<repo>" "$MARKER_HOME") || exit 1
PR_HOST_REPO=$(printf '%s' "$CHECK_JSON" | jq -r '.pr_host_repo | tostring') || exit 1
MERGE_STRATEGY=$(printf '%s' "$CHECK_JSON" | jq -r '.strategy | tostring') || exit 1
RELEASE_CLASS=$(printf '%s' "$CHECK_JSON" | jq -r '.release_class | tostring') || exit 1
[ -n "$PR_HOST_REPO" ] && [ "$PR_HOST_REPO" != null ] || exit 1
case "$MERGE_STRATEGY" in squash|merge|rebase) ;; *) echo "Invalid strategy." >&2; exit 1 ;; esac
case "$RELEASE_CLASS" in true|false) ;; *) echo "Invalid release_class." >&2; exit 1 ;; esac
[ "$PR_HOST_REPO" = "<repo>" ] || { echo "Checked host repo does not match <repo>." >&2; exit 1; }
source "$MARKER_HOME/.claude/hooks/_lib-tracker.sh" || exit 1
RELEASE_SUBJECT=""
RELEASE_BODY_FILE=""
if [ "$RELEASE_CLASS" = true ]; then
RELEASE_SUBJECT=$(gh pr view <pr> --repo "$PR_HOST_REPO" --json title -q '.title') || exit 1
if [ -z "$RELEASE_SUBJECT" ]; then
echo "ERROR: could not read the release PR's title." >&2
exit 1
fi
RELEASE_BODY_FILE=$(mktemp) || exit 1
if ! gh pr view <pr> --repo "$PR_HOST_REPO" --json body -q '.body' > "$RELEASE_BODY_FILE" \
|| [ ! -s "$RELEASE_BODY_FILE" ]; then
echo "ERROR: could not read the release PR's body. Refusing a bare squash." >&2
rm -f "$RELEASE_BODY_FILE"
exit 1
fi
fi
MERGE_RESULT_FILE=$(mktemp) || exit 1
tracker_pr_merge "<repo>" "<pr>" "${MERGE_STRATEGY}" true "$RELEASE_SUBJECT" "$RELEASE_BODY_FILE" > "$MERGE_RESULT_FILE"
MERGE_RC=$?
MERGE_RESULT="$(cat "$MERGE_RESULT_FILE")"
MERGE_SHA=$(printf '%s' "$MERGE_RESULT" | jq -r '.sha // empty' 2>/dev/null)
rm -f "$MERGE_RESULT_FILE" "$RELEASE_BODY_FILE"
On MERGE_RC 0, report MERGE_SHA and continue.
On any other result, show the exact error. The CEO marker stays on disk for a retry.
MERGE_RC 3 means tracker.kind is none. Report approval and ask the user to merge manually on the host.
No further /approve-merge invocation is needed after that manual merge.
After a successful merge, call board_move_card to signal that the work has
shipped and is entering the measurement/observe phase. This is a no-op unless
enable_auto_moves is true in the fork's github_projects config.
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-project-board.sh"
board_move_card "<pr>" "measurement"
board_move_card degrades gracefully — any failure warns to stderr and returns
0. It never blocks the merge report.
Single-line confirmation (include the merge strategy used so the operator can see it):
✓ Merged PR #<pr> as commit <MERGE_SHA> (strategy: squash). Branch deleted.
or for sync PRs:
✓ Merged PR #<pr> as commit <MERGE_SHA> (strategy: merge, auto-detected sync PR — ancestry preserved). Branch deleted.
After a successful merge (MERGE_RC 0), add this final report line:
Next: run /compact to free context before the next task.
If the merge gate blocked (MERGE_RC non-zero and not 3), surface the exact error and tell the user how to retry:
✗ Merge blocked: <reason from gate>. Marker still on disk at <CEO path from review_marker_path> — fix the issue and re-invoke `/approve-merge <pr>` (the marker is still valid, no need to re-approve).
If MERGE_RC = 3 (tracker.kind: none, no host CLI configured):
✓ CEO approval recorded for PR #<pr>. No tracker CLI is configured (tracker.kind: none) — merge <pr> manually on the host; the marker on disk covers the approval, no further /approve-merge invocation needed.
When the merged PR adds a slice record, print Next: /orbit handoff --slice <path> to file the proposed slice.
Skip unless MERGE_RC is 0 without --no-merge. Follow docs/skills/approve-merge-worktree-offer.md.
List candidates with .claude/skills/approve-merge/remove-worktree.sh list "<source-tree>" "<head-branch>". Ask with AskUserQuestion ("Remove it" or "Keep it") and show the ignored files the helper lists. On "Remove it", run remove-worktree.sh remove "<source-tree>" "<path>" "<head-branch>". Never remove a path the helper did not list. Never pass --force. Never remove without a yes.
Do this only when the user explicitly asks. Do not auto-close a child issue. Confirm each close on its own. Full rules: docs/skills/approve-merge-rationale.md.
/approve-merge <pr> --no-merge writes the marker and stops. Useful when:
The skill never writes the marker AND defers the merge in any other case. The default IS auto-merge.
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
WCAG 2.1 AA audit — perceivable, operable, understandable, robust criteria. Deep-dive for /launch-check accessibility.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
WCAG 2.2 AA audit — perceivable, operable, understandable, robust criteria. Deep-dive for /launch-check accessibility.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Browse / search / show / stats AgDRs across the portfolio — recalls "have we decided this before?".
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Analytics audit — SDK config, event naming, funnel completeness, dashboards. Deep-dive for /launch-check analytics.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Record per-PR architecture-review approval for design-artifact PRs (required by the architecture gate). ONLY on an explicit per-PR architect "approved".
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Record per-PR design-review approval (UI merge gate). ONLY on an explicit per-PR designer "approved".
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.