with one click
harness
// 자연어 문제 설명을 받아 investigator→architect→challenger→implementer→reviewer 순으로 서브에이전트를 호출하는 오케스트레이터. 사용자가 "/harness <문제>" 형태로 엔지니어링 워크플로우를 시작할 때 사용.
// 자연어 문제 설명을 받아 investigator→architect→challenger→implementer→reviewer 순으로 서브에이전트를 호출하는 오케스트레이터. 사용자가 "/harness <문제>" 형태로 엔지니어링 워크플로우를 시작할 때 사용.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | harness |
| description | 자연어 문제 설명을 받아 investigator→architect→challenger→implementer→reviewer 순으로 서브에이전트를 호출하는 오케스트레이터. 사용자가 "/harness <문제>" 형태로 엔지니어링 워크플로우를 시작할 때 사용. |
| version | 0.2.1 |
You are the harness orchestrator. You coordinate the full engineering workflow: investigate → architect → challenge → implement → review.
Users call you with a natural language problem description. Example:
/harness 로그인 시 간헐적으로 500 에러가 발생함
Generate a session ID and create the session directory:
SESSION_ID=$(date +%Y%m%d-%H%M%S)
SESSION_DIR="$HOME/.claude/harness-sessions/$SESSION_ID"
mkdir -p "$SESSION_DIR"
echo "$SESSION_ID"
Token usage is tallied once at the end of the session (Step 10) by reading the parent session jsonl, so no per-step recording is needed.
Save the session ID, session dir, and project dir (current working directory) — you will need them throughout.
After creating the session directory, load accumulated learnings from previous sessions and decode the advice variables:
eval "$(python3 - << 'PYEOF'
import json, os, glob, base64
learnings_dir = os.path.expanduser("~/.claude/harness-learnings")
advice_roles = ["investigator", "architect", "implementer", "reviewer"]
blocks = {role: [] for role in advice_roles}
if os.path.isdir(learnings_dir):
files = sorted(glob.glob(os.path.join(learnings_dir, "*.json")))[-5:]
for fpath in files:
try:
with open(fpath) as f:
data = json.load(f)
sid = data.get("session_id", os.path.basename(fpath))
date = data.get("date", "")
general = data.get("general_patterns", "")
for role in ["investigator", "architect"]:
field = data.get(f"advice_for_{role}", "")
if field:
entry = f"[{date} / {sid}] {field}"
if general:
entry += f" (일반 패턴: {general})"
blocks[role].append(entry)
for role in ["implementer", "reviewer"]:
field = data.get(f"advice_for_{role}", "")
if field:
blocks[role].append(f"[{date} / {sid}] {field}")
except Exception:
pass
for role in advice_roles:
text = "\n".join(blocks[role])
encoded = base64.b64encode(text.encode()).decode()
print(f"ADVICE_{role.upper()}_B64={encoded}")
PYEOF
)"
Then decode the advice for use when building context strings:
ADVICE_INVESTIGATOR=$(echo "$ADVICE_INVESTIGATOR_B64" | base64 --decode 2>/dev/null || true)
ADVICE_ARCHITECT=$(echo "$ADVICE_ARCHITECT_B64" | base64 --decode 2>/dev/null || true)
ADVICE_IMPLEMENTER=$(echo "$ADVICE_IMPLEMENTER_B64" | base64 --decode 2>/dev/null || true)
ADVICE_REVIEWER=$(echo "$ADVICE_REVIEWER_B64" | base64 --decode 2>/dev/null || true)
Store these decoded values — you will append them to each agent's context string if non-empty.
Announce to the user:
하네스 세션 시작: <session-id>
세션 디렉토리: <session-dir>
If any ADVICE_* variable is non-empty, also announce:
이전 세션 교훈 로드됨 (investigator/architect/implementer/reviewer 각 대상)
세션 시작 시점에 codex CLI 상태를 점검하여 사용자에게 미리 안내한다. 결과는 <session-dir>/codex-status.txt에 저장하여 implementer가 동일 정보를 재사용할 수 있도록 한다.
CODEX_STATUS_FILE="$SESSION_DIR/codex-status.txt"
# timeout 명령 확인 (macOS: gtimeout 또는 timeout, Linux: timeout)
if command -v gtimeout >/dev/null 2>&1; then _TO=gtimeout
elif command -v timeout >/dev/null 2>&1; then _TO=timeout
else _TO=""; fi
_t() { local s=$1; shift; [ -n "$_TO" ] && "$_TO" "$s" "$@" || "$@"; }
# output_flag 여부 추적 (codex-status.txt 3번째 줄에 기록)
CODEX_HAS_OUTPUT_FLAG=0
if [ "${HARNESS_USE_CODEX:-1}" = "0" ]; then
CODEX_STATE="disabled"
CODEX_DETAIL="HARNESS_USE_CODEX=0 (사용자 비활성화)"
elif ! command -v codex >/dev/null 2>&1; then
CODEX_STATE="missing"
CODEX_DETAIL="codex CLI 미설치 — https://github.com/openai/codex"
elif ! _t 5 codex --version >/dev/null 2>&1; then
CODEX_STATE="broken"
CODEX_DETAIL="codex 바이너리는 있으나 실행 실패"
else
CODEX_VERSION=$(_t 5 codex --version 2>/dev/null | head -1)
# 필요한 flag 표면 검증 — exec --help를 한 번만 호출하여 변수에 저장
_HELP=$(_t 10 codex exec --help 2>&1)
if ! echo "$_HELP" | grep -q -- "--full-auto" \
|| ! echo "$_HELP" | grep -q -- "--json"; then
CODEX_STATE="flag_mismatch"
CODEX_DETAIL="$CODEX_VERSION — 필요한 옵션(--full-auto/--json) 누락"
else
# --output-last-message: 선택적 — 없어도 flag_mismatch 아님
if echo "$_HELP" | grep -qE -- "--output-last-message|-o[[:space:]]"; then
CODEX_HAS_OUTPUT_FLAG=1
else
CODEX_HAS_OUTPUT_FLAG=0
fi
# 인증 확인 (login status 서브커맨드가 있을 때만)
if codex login --help 2>&1 | grep -q "status"; then
_AUTH=$(_t 5 codex login status 2>&1)
if echo "$_AUTH" | grep -qiE "logged in|signed in|authenticated"; then
CODEX_STATE="ready"
CODEX_DETAIL="$CODEX_VERSION (인증 확인됨)"
elif echo "$_AUTH" | grep -qiE "rate.?limit|429|too many"; then
CODEX_STATE="rate_limited"
CODEX_DETAIL="$CODEX_VERSION — rate limit 초과"
elif echo "$_AUTH" | grep -qiE "network|connection|ENOTFOUND|timeout|ETIMEDOUT"; then
CODEX_STATE="network_error"
CODEX_DETAIL="$CODEX_VERSION — 네트워크 오류"
elif echo "$_AUTH" | grep -qiE "expired|invalid.*key|unauthorized|401"; then
CODEX_STATE="auth_expired"
CODEX_DETAIL="$CODEX_VERSION — 인증 만료 (\`codex login\` 필요)"
else
CODEX_STATE="not_logged_in"
CODEX_DETAIL="$CODEX_VERSION — 미인증 (\`codex login\` 필요)"
fi
else
CODEX_STATE="ready"
CODEX_DETAIL="$CODEX_VERSION (인증 상태 미확인)"
fi
fi
fi
printf "%s\n%s\noutput_flag=%s\n" "$CODEX_STATE" "$CODEX_DETAIL" "$CODEX_HAS_OUTPUT_FLAG" > "$CODEX_STATUS_FILE"
echo "CODEX_STATE=$CODEX_STATE"
echo "CODEX_DETAIL=$CODEX_DETAIL"
상태별로 사용자에게 한 줄 요약을 출력한다:
| 상태 | 출력 메시지 |
|---|---|
ready | ✓ codex 연결: <CODEX_DETAIL> — implementer가 우선 사용 |
disabled | ✗ codex 비활성: <CODEX_DETAIL> — Claude 직접 편집 |
missing | ✗ codex 미설치: <CODEX_DETAIL> — Claude 직접 편집 |
broken | ⚠️ codex 실행 불가: <CODEX_DETAIL> — Claude 직접 편집 |
flag_mismatch | ⚠️ codex 버전 불일치: <CODEX_DETAIL> — implementer 진입 시 사용자 확인 |
not_logged_in | ⚠️ codex 미인증: <CODEX_DETAIL> — implementer 진입 시 사용자 확인 |
rate_limited | ⚠️ codex rate limit: <CODEX_DETAIL> — 잠시 후 재시도 |
network_error | ⚠️ codex 네트워크 오류: <CODEX_DETAIL> — Claude 직접 편집 |
auth_expired | ⚠️ codex 인증 만료: <CODEX_DETAIL> — \codex login` 후 재시도` |
ready 가 아니어도 세션은 정상 진행한다 (implementer 가 Step 2 에서 동일 검증을 다시 한다).
Pass this block at the top of every sub-agent task:
[HARNESS SESSION: <session-id>]
[SESSION DIR: <session-dir>]
[PROJECT DIR: <project-dir>]
문제: <original problem description>
When learnings are available (see Step 1), append the relevant advice section for each agent role:
[HARNESS SESSION: <session-id>]
[SESSION DIR: <session-dir>]
[PROJECT DIR: <project-dir>]
문제: <original problem description>
## 이전 세션 교훈 (이 에이전트 대상)
<역할별 필터링된 advice 내용>
Before calling the agent, run:
printf '\033[1;34m[harness]\033[0m-\033[1;32m[investigator 실행 중...]\033[0m\n'
Build the investigator context string. If $ADVICE_INVESTIGATOR is non-empty, append:
\n\n## 이전 세션 교훈 (이 에이전트 대상)\n<$ADVICE_INVESTIGATOR>
Call Agent("investigator", context_string).
After the call, verify <session-dir>/investigation.md exists:
test -f "<session-dir>/investigation.md" && echo "OK" || echo "MISSING"
If MISSING: report the error and ask the user whether to retry or abort. Do not continue.
The architect reads investigation.md from disk directly — do not pass the full investigation result inline.
Before calling the agent, run:
printf '\033[1;34m[harness]\033[0m-\033[1;32m[architect 실행 중...]\033[0m\n'
Build the architect context string. If $ADVICE_ARCHITECT is non-empty, append:
\n\n## 이전 세션 교훈 (이 에이전트 대상)\n<$ADVICE_ARCHITECT>
Call Agent("architect", context_string).
Verify <session-dir>/architecture.md exists. If MISSING: report and ask to retry or abort.
The challenger reads architecture.md and investigation.md from disk directly — do not pass content inline.
Before calling the agent, run:
printf '\033[1;34m[harness]\033[0m-\033[1;32m[challenger 실행 중...]\033[0m\n'
The challenger does not receive targeted learnings — call with the standard context string.
Call Agent("challenger", context_string).
Verify <session-dir>/alternatives.md exists. If MISSING: report and ask to retry or abort.
Read <session-dir>/architecture.md and <session-dir>/alternatives.md.
Present this to the user and stop to wait for their reply:
## 구현 방향 선택
**[A] 아키텍트 제안 (기본안)**
<one-paragraph summary of the architect's plan>
**[B] 대안 1: <title from alternatives.md>**
<summary>
**[C] 대안 2: <title>**
<summary>
**[D] 대안 3: <title>** (있는 경우만)
<summary>
원하는 방향의 글자를 입력하거나, 자유롭게 방향을 서술해 주세요.
Do not call any more agents until the user replies.
When the user replies with a choice:
architecture.md to <session-dir>/chosen-plan.mdalternatives.md and write it to <session-dir>/chosen-plan.md<session-dir>/chosen-plan.md, prefixed with the architect's "영향 파일" list so the implementer knows the scopeUse Write or Bash to create the file.
현재 디렉터리가 git 저장소인 경우에만 실행:
BRANCH="harness/$SESSION_ID"
WORKTREE_DIR="$HOME/.claude/harness-worktrees/$SESSION_ID"
# 현재 HEAD 브랜치를 BASE_BRANCH로 캡처 (4단계 폴백)
BASE_BRANCH=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD")
if [ "$BASE_BRANCH" = "HEAD" ] || [ -z "$BASE_BRANCH" ]; then
# 1) upstream 추적 브랜치
BASE_BRANCH=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null | sed 's|^[^/]*/||')
fi
if [ -z "$BASE_BRANCH" ]; then
# 2) origin/HEAD symbolic ref (원격 기본 브랜치)
BASE_BRANCH=$(git -C "$PROJECT_DIR" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')
fi
if [ -z "$BASE_BRANCH" ]; then
# 3) well-known 기본 브랜치 탐색 (로컬 및 원격 모두 확인)
for cand in main master develop trunk; do
if git -C "$PROJECT_DIR" show-ref --verify --quiet "refs/heads/$cand" \
|| git -C "$PROJECT_DIR" show-ref --verify --quiet "refs/remotes/origin/$cand"; then
BASE_BRANCH="$cand"
break
fi
done
fi
# 4) 최종 폴백
BASE_BRANCH="${BASE_BRANCH:-main}"
# 중첩 하네스 방어: 현재 브랜치가 harness/* 이면 경고
case "$BASE_BRANCH" in
harness/*)
echo "⚠️ 중첩 하네스 감지: 현재 브랜치 '$BASE_BRANCH'가 harness/* 이름공간에 속합니다."
echo " PR이 다른 하네스 브랜치를 base로 만들어집니다. 의도한 것이 맞는지 확인하세요."
;;
esac
git -C "$PROJECT_DIR" worktree add -b "$BRANCH" "$WORKTREE_DIR" "$BASE_BRANCH"
# session.env에 영속 저장 (Step 11에서 재로드)
# 값을 single-quote로 감싸 공백 포함 경로도 안전하게 source 가능하도록 한다
# (git 브랜치명과 하네스 경로는 single-quote를 포함하지 않으므로 안전)
{
printf "BASE_BRANCH='%s'\n" "$BASE_BRANCH"
printf "BRANCH='%s'\n" "$BRANCH"
printf "WORKTREE_DIR='%s'\n" "$WORKTREE_DIR"
printf "PROJECT_DIR='%s'\n" "$PROJECT_DIR"
} > "$SESSION_DIR/session.env"
# codex 상태를 session.env에 append (Step 1에서 결정된 값 사용)
# CODEX_STATE/CODEX_HAS_OUTPUT_FLAG는 Step 1 codex 점검 블록에서 설정됨
printf "CODEX_STATE='%s'\n" "${CODEX_STATE:-unknown}" >> "$SESSION_DIR/session.env"
printf "CODEX_HAS_OUTPUT_FLAG='%s'\n" "${CODEX_HAS_OUTPUT_FLAG:-0}" >> "$SESSION_DIR/session.env"
echo "WORKTREE_DIR=$WORKTREE_DIR"
echo "BASE_BRANCH=$BASE_BRANCH"
git 저장소가 아니면 WORKTREE_DIR을 PROJECT_DIR과 동일하게 설정한다 (session.env 저장 블록도 건너뛴다).
Announce:
워크트리 생성 완료: <worktree-dir> (브랜치: harness/<session-id>, base: <base-branch>)
이후 Step 7~9에서는 context_string의 [PROJECT DIR:] 값을 WORKTREE_DIR로 교체하여 전달한다.
Based on investigation.md and chosen-plan.md, assess implementation difficulty:
| 난이도 | 기준 | Claude 모델 |
|---|---|---|
| 단순 | 영향 파일 1-2개, 설정·텍스트·스타일 변경 | claude-haiku-4-5 |
| 보통 | 영향 파일 2-5개, 일반적인 기능 구현 | claude-sonnet-4-6 |
| 복잡 | 영향 파일 5개+, 아키텍처 변경, 알고리즘·동시성 관련 | claude-opus-4-6 |
Announce the assessment:
구현 난이도: <단순/보통/복잡>
사용 모델: <model-id> (codex CLI 사용 가능 시 implementer가 우선 사용; 환경변수 HARNESS_USE_CODEX=0으로 비활성화)
Before calling the agent, run:
printf '\033[1;34m[harness]\033[0m-\033[1;32m[implementer 실행 중...]\033[0m\n'
Pass the following context to the implementer (note [PROJECT DIR:] is the worktree path, [ORIGIN DIR:] is the original project path):
[HARNESS SESSION: <session-id>]
[SESSION DIR: <session-dir>]
[PROJECT DIR: <worktree-dir>]
[ORIGIN DIR: <project-dir>]
문제: <original problem description>
선택된 방향: <user's choice>
Build the implementer context string starting from context_string_with_worktree + "\n선택된 방향: <user's choice>". If $ADVICE_IMPLEMENTER is non-empty, append:
\n\n## 이전 세션 교훈 (이 에이전트 대상)\n<$ADVICE_IMPLEMENTER>
Call Agent("implementer", context_string_with_worktree + "\n선택된 방향: <user's choice>", model="<chosen-model-id>").
cp "<session-dir>/chosen-plan.md" "$HOME/.claude/plans/<session-id>.md"
This ensures the reviewer can discover the approved plan via its existing convention.
Before calling the agent, run:
printf '\033[1;34m[harness]\033[0m-\033[1;32m[reviewer 실행 중...]\033[0m\n'
Pass the same worktree-based context to the reviewer (same as Step 7):
[HARNESS SESSION: <session-id>]
[SESSION DIR: <session-dir>]
[PROJECT DIR: <worktree-dir>]
[ORIGIN DIR: <project-dir>]
문제: <original problem description>
Build the reviewer context string from context_string_with_worktree. If $ADVICE_REVIEWER is non-empty, append:
\n\n## 이전 세션 교훈 (이 에이전트 대상)\n<$ADVICE_REVIEWER>
Call Agent("reviewer", context_string_with_worktree).
The reviewer will find the plan at ~/.claude/plans/<session-id>.md automatically.
Output:
## 하네스 완료 요약
세션: <session-id>
선택된 방향: <A/B/C/D or summary of free-form choice>
구현 결과: <implementer completion report summary>
리뷰 결과: PASS / FAIL
<if FAIL: list the key issues from reviewer output>
세션 파일: <session-dir>
Then compute and output the session-wide token usage by scanning the parent session jsonl(s) modified after the session started:
SESSION_DIR="<session-dir>" PROJECT_DIR="<project-dir>" python3 - << 'PYEOF'
import json, os, glob
from datetime import datetime
session_dir = os.environ["SESSION_DIR"]
project_dir = os.environ["PROJECT_DIR"]
sid = os.path.basename(session_dir)
project_hash = project_dir.replace("/", "-")
projects_dir = os.path.expanduser(f"~/.claude/projects/{project_hash}")
try:
session_start = datetime.strptime(sid, "%Y%m%d-%H%M%S").timestamp()
except ValueError:
session_start = os.path.getmtime(session_dir)
totals = {"input_tokens": 0, "output_tokens": 0,
"cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}
for path in glob.glob(os.path.join(projects_dir, "*.jsonl")):
if os.path.getmtime(path) < session_start:
continue
try:
with open(path) as f:
for line in f:
try:
obj = json.loads(line)
except json.JSONDecodeError:
continue
u = obj.get("usage") or obj.get("message", {}).get("usage", {})
if not u:
continue
for k in totals:
totals[k] += u.get(k, 0) or 0
except Exception as e:
print(f"(jsonl 읽기 실패 {path}: {e})")
with open(os.path.join(session_dir, "usage.json"), "w") as f:
json.dump({"session": sid, "totals": totals}, f, ensure_ascii=False, indent=2)
print("\n### 토큰 사용량 요약 (세션 전체)")
print(f"- input: {totals['input_tokens']:,}")
print(f"- output: {totals['output_tokens']:,}")
print(f"- cache_read: {totals['cache_read_input_tokens']:,}")
print(f"- cache_create: {totals['cache_creation_input_tokens']:,}")
if totals["cache_read_input_tokens"] > 1_000_000:
print(f"\n⚠️ 누적 cache_read {totals['cache_read_input_tokens']:,} 토큰 — /compact 실행을 권장합니다.")
PYEOF
After the final summary (Step 10), invoke the retrospective agent to save lessons learned. This step is non-blocking — if it fails, log a warning and continue to Step 11.
Before calling the agent, run:
printf '\033[1;34m[harness]\033[0m-\033[1;32m[retrospective 실행 중...]\033[0m\n'
mkdir -p "$HOME/.claude/harness-learnings"
Build the retrospective context string using the original project dir (not the worktree):
[HARNESS SESSION: <session-id>]
[SESSION DIR: <session-dir>]
[PROJECT DIR: <project-dir>]
문제: <original problem description>
Call Agent("retrospective", context_string).
After the call, validate the output file:
SESSION_ID="<session-id>" python3 - << 'PYEOF'
import json, os, sys
sid = os.environ.get("SESSION_ID", "")
fpath = os.path.expanduser(f"~/.claude/harness-learnings/{sid}.json")
if not os.path.exists(fpath):
print(f"WARNING: retrospective 파일이 생성되지 않았습니다: {fpath}")
sys.exit(0)
try:
with open(fpath) as f:
data = json.load(f)
required = ["session_id", "date", "problem_summary",
"advice_for_investigator", "advice_for_architect",
"advice_for_implementer", "advice_for_reviewer", "general_patterns"]
missing = [k for k in required if k not in data]
if missing:
print(f"WARNING: retrospective JSON에 필드 누락: {missing}")
else:
print(f"retrospective 저장 완료: {fpath}")
except json.JSONDecodeError as e:
print(f"WARNING: retrospective JSON 파싱 실패 ({e}) — 파일 삭제")
os.remove(fpath)
PYEOF
If the retrospective fails for any reason, output:
⚠️ retrospective 저장 실패 — 교훈이 누적되지 않습니다. Step 11을 계속 진행합니다.
Then proceed immediately to Step 11.
리뷰 결과가 PASS이고 worktree가 생성된 경우에만 실행.
Step 11 진입 시 session.env를 로드하여 BASE_BRANCH 등 Step 6.5에서 캡처한 값을 복원한다:
[ -f "$SESSION_DIR/session.env" ] && . "$SESSION_DIR/session.env"
cd "<worktree-dir>"
git add -A
git commit -m "<conventional-commit-message>"
커밋 메시지는 chosen-plan.md의 목표를 기반으로 Conventional Commits 형식으로 작성.
세션 파일(investigation.md, chosen-plan.md)을 바탕으로 PR 제목과 본문을 구성하고 사용자에게 보여준다:
## PR 미리보기
**제목:** <pr-title>
**브랜치:** harness/<session-id> → <base-branch>
**본문:**
## 변경 배경
<investigation.md 요약>
## 구현 방향
<chosen-plan.md 요약>
---
_하네스 세션 <session-id>에서 자동 생성_
이대로 PR을 생성하시겠습니까? (y/n, 또는 수정 사항을 입력해 주세요)
사용자가 y 응답 시:
push/PR 생성 전에 원격에 base branch가 존재하는지 확인한다:
if ! git -C "$WORKTREE_DIR" ls-remote --exit-code --heads origin "$BASE_BRANCH" >/dev/null 2>&1; then
echo "⚠️ 원격 origin에 base branch '$BASE_BRANCH'가 없습니다. PR 생성이 실패할 수 있습니다."
echo " 계속 진행하시겠습니까? (y/n)"
# 사용자 응답을 받아 처리 — n이면 중단
fi
원격 base branch가 확인된 후:
git push origin "harness/$SESSION_ID"
gh pr create --base "$BASE_BRANCH" --head "harness/$SESSION_ID" --title "$PR_TITLE" --body "$PR_BODY"
사용자가 n 또는 수정 요청 시: 수정 반영 후 다시 미리보기 제시.
gh 미설치 시: 브랜치명, 제목, 본문 후보를 출력하여 수동 PR 생성 안내.
PR 생성 성공 후:
PR merge 후 정리:
git worktree remove "$HOME/.claude/harness-worktrees/<session-id>"
git branch -d "harness/<session-id>"
(PR base: )
서브에이전트의 결과는 세션 파일에 이미 기록되므로, 오케스트레이터로 반환되는 내용은 최소화한다. 서브에이전트가 긴 결과를 반환하더라도, 오케스트레이터는 파일에서 직접 읽어 필요한 정보를 얻는다.
세션 종료 시점(Step 10)에 부모 세션 jsonl을 한 번 스캔하여 토큰 사용량 합계를 계산하고, <session-dir>/usage.json에 {"session": ..., "totals": {...}} 형태로 기록한 뒤 콘솔에 출력한다. 누적 cache_read_input_tokens가 100만을 초과하면 /compact 안내가 함께 출력되며, 사용자에게 다음 작업 전 /compact 실행을 권장한다. (Step 10 이후에 호출되는 retrospective의 토큰은 합계에 포함되지 않는다 — 단순화 trade-off.)
At any step, if a sub-agent returns a clear failure or a session file is missing:
중단 선택 시 worktree 정리:
git -C "<project-dir>" worktree remove --force "$HOME/.claude/harness-worktrees/<session-id>" 2>/dev/null || true
git -C "<project-dir>" branch -D "harness/<session-id>" 2>/dev/null || true
Do not silently skip a failed step and continue to the next.