# gatekit 공통 변수·헬퍼. 직접 실행하지 않는다.
# 사용: source "$(dirname "$0")/env.sh" <마일스톤 N>
# 스모크 전용: GATEKIT_PLAN_REL 로 계획서 경로를, GATEKIT_SMOKE=1 로 clean·lint 검사를 뺀다.

GK_N="${1:-}"
if ! [[ "$GK_N" =~ ^[0-9]+[a-z]?$ ]]; then  # 보완 계획은 7b 처럼 숫자+소문자 1자
  echo "usage: $(basename "$0") <milestone N>" >&2
  exit 2
fi

WT=/home/madsc/work/insuniverse-temp/.worktrees/admin-rbac
PLUGIN=/home/madsc/.claude/plugins/cache/mccp/mccp/1.33.6
export CLAUDE_PLUGIN_ROOT="$PLUGIN"
export MCCP_PLAN_REVIEW="${MCCP_PLAN_REVIEW:-hybrid}"
export MCCP_PLAN_REVIEW_L3="${MCCP_PLAN_REVIEW_L3:-1}"
export MCCP_GATE_ROUND_CAP="${MCCP_GATE_ROUND_CAP:-3}"
export MCCP_PLAN_FANOUT=off
# 세션 폭주 상한(기본 24)을 RBAC 전 마일스톤 실행 몫으로 제한 상향 — 남은 M3~M11 × 3라운드 × 패널 4 = 108 + 사용분 24 + 여유.
# 문서화된 복구 수단(plan.md 5.2b "raise MCCP_ORCHESTRATION_MAX_AGENTS"). 세션 ID 를 바꿔 계수를 초기화하지 않는다.
export MCCP_ORCHESTRATION_MAX_AGENTS="${MCCP_ORCHESTRATION_MAX_AGENTS_RBAC:-220}"  # 10-21: 보완 계획 증가로 140 소진 → 남은 m6b R3·M9·M10·M11·보완 2회 ≈ 64 + 여유

cd "$WT" || { echo "cannot cd to $WT" >&2; exit 12; }

PRD_REL=.claude/prds/admin-rbac.prd.md
PLAN_REL="${GATEKIT_PLAN_REL:-.claude/PRPs/plans/admin-rbac-m${GK_N}.plan.md}"
STATE="$WT/.claude/state/plan-review"
MCCP_TMP="$(git rev-parse --git-path mccp/tmp)"
mkdir -p "$STATE" "$MCCP_TMP"

# 슬러그는 계획서 경로로 유도한다 (PRD 경로로 유도하면 원장이 PRD 단위로 공유돼 2번째 마일스톤부터 0).
SLUG="$(node "$PLUGIN/scripts/receipt/cli.js" derive-decision --command mccp:plan --args "$PLAN_REL")"
if [ -z "$SLUG" ]; then
  echo "HALT_STAGE=env"; echo "derive-decision 이 빈 슬러그를 냈다 ($PLAN_REL)"; exit 12
fi

PLAN_NODE_CLI="$PLUGIN/scripts/lib/plan-review/cli.js"
RUNAWAY="$PLUGIN/scripts/lib/orchestration-runaway.js"
ROUNDS_CLI="$PLUGIN/scripts/lib/review-rounds/cli.js"
RECEIPT_CLI="$PLUGIN/scripts/receipt/cli.js"

# 명령 본문의 IPC 정리 목록 (5.2 진입). dispatch-log 는 원장이라 지우지 않는다.
GK_PURGE=(codex-verdict codex-class decision.json proof.json l1.json l2.json l3.json
  reservation.json workflow-args.json backlog.json started-at l3-run-nonce l3-deadline
  l3-pid l3-findings.json)

# 저자 확인 값 읽기: node 로 JSON 파일의 경로를 평가한다. 실패하면 기본값.
#   jread <file> <js-expr-on-j> <default>   예: jread f.json 'j.fleet.length' 0
jread() {
  node -e 'try{const j=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));const v=eval(process.argv[2]);process.stdout.write(v===undefined||v===null||v===""?process.argv[3]:String(v))}catch(e){process.stdout.write(process.argv[3])}' "$1" "$2" "$3"
}

# 단계 기록 (5.2 의 record 는 항상 exit 0 이고 halt_stage 를 남긴다).
record_stop() {
  node "$PLAN_NODE_CLI" record --slug "$SLUG" --plan "$PLAN_REL" --halt-stage "$1" >/dev/null 2>&1 || true
}
record_pass() {
  node "$PLAN_NODE_CLI" record --slug "$SLUG" --plan "$PLAN_REL" >/dev/null 2>&1 || true
}

# 예약 반환: 발사 전 중단이면 --actual 0 (명령 본문 5.2b·5.2c-emit 과 같은 처리).
release_reservation() {
  local rid
  rid="$(jread "$STATE/reservation.json" 'j.reservationId||""' "")"
  if [ -n "$rid" ]; then
    node "$RUNAWAY" reconcile --reservation "$rid" --actual 0 >/dev/null 2>&1 || true
  fi
}

# 라운드 현황 "k/cap". k 는 원장에 기록된 소진 수, cap 은 봉인된 값(없으면 env).
rounds_now() {
  node "$ROUNDS_CLI" status --gate mccp-plan-codex --decision "$SLUG" --json 2>/dev/null \
    | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);const cap=(j.seal&&j.seal.found&&j.seal.cap)||process.env.MCCP_GATE_ROUND_CAP;process.stdout.write(String(j.rounds_so_far??0)+"/"+String(cap))}catch(e){process.stdout.write("?/"+process.env.MCCP_GATE_ROUND_CAP)}})'
}

# 중단 출력 규약: HALT_STAGE=<stage> 한 줄 + 사유. 호출자가 exit 한다.
halt_msg() {
  echo "HALT_STAGE=$1"
  shift
  printf '%s\n' "$@"
}
