maestro:resume

SkillWeb & browsing

A single re-entrant entry point that pops and executes the next action from the Gran Maestro workflow queue. Keeps the interactive resume skill itself, while the headless queue drain runtime path uses the repository-local `mst.py queue drain-headless` contract. If the queue is empty, checks the reso

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the maestro:resume skill

What this skill tells your AI

The instructions your AI receives, as published by myrtlepn/gran-maestro in skills/resume/SKILL.md and read by ahel’s review.

목적: .gran-maestro/pending.ndjson queue에서 다음 action을 하나 pop하여 해당 스킬을 호출한다. interactive resume과 외부 wrapper(scripts/mst-loop.sh)는 같은 queue contract를 공유하지만, headless runtime implementation path는 python3 {PLUGIN_ROOT}/scripts/mst.py queue drain-headless --json 이다. 세션 교차/재진입/동시 세션에서는 queue를 1순위 SSoT로 사용하고, queue가 비어 있을 때만 resolver fallback(workflow_state, wakeup-hint)으로 다음 action을 큐에 복원한다. Claude print-mode로 /mst:resume를 직접 호출하던 방식은 historical 설명 문자열일 뿐 active runtime path가 아니다.

질문 답변으로 재개

--answer Q-... --value "..."가 있으면 일반 queue 처리 전에 저장된 질문에 답을 기록하고 소비한다.

python3 {PLUGIN_ROOT}/scripts/mst.py question answer {Q_ID} --answer "{ANSWER_VALUE}" --json
python3 {PLUGIN_ROOT}/scripts/mst.py question consume {Q_ID} --json

두 명령이 성공하면 question artifact에 저장된 resume_skill/resume_args가 queue에 한 번 들어간다. 이후 아래의 기존 peek → pop → skill 호출 흐름을 그대로 수행한다. 호출 인자로 resume target을 새로 받거나 바꾸지 않는다. Codex request_user_input이 여러 답을 객체로 반환한 경우에는 그 JSON을 파일로 저장해 첫 명령의 --answer-file에 전달한 뒤 같은 consume 흐름을 사용한다.

Gate

Entry

  • .gran-maestro/pending.ndjson queue에서 정확히 한 개의 action만 pop하여 실행한다.
  • AUTO_MODE 판정은 queue entry의 auto 필드와 args-a 플래그를 그대로 사용한다. 재판단 금지.

경로 우선순위 (MANDATORY)

resume 경로는 queue entry를 SSoT로 사용합니다. plan.json 또는 tmp/mst-state-{PPID}.json의 next_action.auto_mode 등을 fallback으로 읽어 args를 재조합하지 않습니다. 자율 모드 의도는 enqueue 시점에 queue entry의 auto 필드와 args-a 토큰으로 함께 표현되어야 하며, 이 불변 계약은 scripts/mst_cmds/_common.py::queue_enqueue가 강제합니다 (REQ-639 T01 참조).

DOD-007 canonical identity boundary: MST_SESSION_ID / mst_session_id만 canonical identity source다. Legacy-only input(MST_STATE_PPID, owner_ppid, owner_session_id, owner_pid, Claude hook session_id, transcript UUID, MST_SNAPSHOT_SESSION_ID, legacy aliases sessionId/session_id)은 diagnostic-only이며 canonical source, fallback, alias, migration requirement가 아니다. Legacy-only input은 session/state/history/snapshot/recovery/lock mutation 없이 structured non-success로 종료해야 한다. Canonical MST_SESSION_ID/mst_session_id와 legacy 값이 충돌하면 canonical identity가 우선하고 legacy 값은 override/repair/merge/persist source가 될 수 없다.

DOD-009 session identity glossary: mst_session_id is the canonical state machine identity payload/context field issued by mst.py as MST-{root_mst_id}-{started_at_compact}-{random}; it partitions .gran-maestro/state/{mst_session_id}/snapshot.json and .gran-maestro/sessions/{mst_session_id}/history.*. MST_SESSION_ID is the environment variable carrying the same canonical identity through child invocation, subprocess, and hook execution. A root resource ID such as AGI-030, PLN-638, or REQ-* can be the root component inside mst_session_id, but it is not the full canonical session identity. A process diagnostic ID such as owner_pid, MST_STATE_PPID, hook session_id, or transcript UUID is diagnostic-only; diagnostic output is allowed, but those values are not canonical source, fallback, alias, migration requirement. legacy aliases such as session_id, sessionId, or MST_SNAPSHOT_SESSION_ID are compatibility diagnostics and not canonical source, fallback, alias, migration requirement. source precedence is validated history ledger, validated state snapshot, then prompt summary as diagnostic-only context.

Exit

  • 한 action의 Skill 호출이 완료되면 complete 또는 fail로 queue 상태를 확정한 뒤 정상 종료한다.
  • queue가 비어있으면 resolver fallback을 정확히 한 번 실행한다. resolver가 action을 enqueue하면 다시 queue peek/pop으로 진입하고, source == "no-op" 또는 resolver 실패이면 "queue empty" 메시지 출력 후 즉시 종료.
  • 한 iteration에서 2개 이상의 action을 pop하지 않는다 (mst-loop wrapper가 다음 iteration을 담당).

금지 패턴

  • queue peek 없이 추측으로 action 선택
  • complete/fail 없이 다음 iteration 진입
  • Skill 호출 없이 queue만 비우는 동작
  • queue entry의 args를 수정/재조합하여 Skill 호출

Anti-Rationalization Checklist

  • 합리화 패턴: "큐에 action 2개가 있으니 한 번에 다 처리하자." | 확인 증거: 한 iteration에서 queue pop 호출은 정확히 1회.
  • 합리화 패턴: "auto 필드가 true인데 args에 -a가 없어 보여 내가 붙이자." | 확인 증거: args를 원본 그대로 전달한다. enqueue 시점에 -a 포함이 호출측 책임.
  • 합리화 패턴: "complete가 귀찮으니 pop만 하고 끝내자." | 확인 증거: 각 iteration의 실행 로그에 complete 또는 fail 명령 호출이 존재.

실행 프로토콜

경로 규칙 (MANDATORY): 이 스킬의 모든 .gran-maestro/ 경로는 절대경로로 사용합니다. 스킬 실행 시작 시 PROJECT_ROOT를 취득하고, 이후 모든 경로에 {PROJECT_ROOT}/ 접두사를 붙입니다.

PROJECT_ROOT=$(pwd)

{PLUGIN_ROOT}는 이 스킬의 "Base directory"에서 skills/{스킬명}/을 제거한 절대경로입니다. 상대경로(.claude/...)는 절대 사용하지 않습니다.

Step 1/5: 큐 확인 (peek)

현재 큐의 머리 entry를 확인한다. 상태를 변경하지 않는다.

python3 {PLUGIN_ROOT}/scripts/mst.py queue peek --json
  • 출력이 null 또는 빈 객체: 아래 resolver fallback 분기로 진행한다.
  • 출력이 JSON entry: 다음 Step 진행. 메모리에 action = {id, skill, args, source_skill, source_id, auto, resource_id, ...} 보관.
Queue empty fallback: resolver enqueue

queue가 비어 있을 때만 resolver를 호출한다. queue head가 있으면 이 분기를 실행하지 않는다.

python3 {PLUGIN_ROOT}/scripts/mst.py resolve-next-action --enqueue --json

사용자가 /mst:resume --wakeup-hint stop-recover로 호출한 경우에는 hint를 그대로 resolver에 전달한다.

python3 {PLUGIN_ROOT}/scripts/mst.py resolve-next-action --enqueue --wakeup-hint stop-recover --json
  • resolver 호출 실패, JSON 파싱 실패, 또는 출력이 비정상인 경우: warn만 출력하고 "queue empty — nothing to resume" 알림 후 정상 종료한다.
  • resolver 결과의 source == "no-op": "queue empty — nothing to resume" 알림 후 정상 종료한다.
  • resolver 결과의 source != "no-op": resolver가 --enqueue로 queue entry를 생성한 것으로 보고 queue peek --json을 다시 한 번 실행한다.
  • 재실행한 queue peek --json 결과가 null 또는 빈 객체이면 "queue empty — nothing to resume" 알림 후 정상 종료한다.
  • 재실행한 queue peek --json 결과가 JSON entry이면 다음 Step 진행. 메모리에 action = {id, skill, args, source_skill, source_id, auto, resource_id, ...} 보관.

이 fallback은 queue가 진짜 비어 있을 때만 동작한다. resume은 fallback source에서 직접 enqueue하지 않으며, enqueue는 반드시 resolve-next-action --enqueue가 수행한다.

Step 2/5: 큐 Pop (큐 머리 entry를 running으로 전이)

python3 {PLUGIN_ROOT}/scripts/mst.py queue pop --json
  • 반환 entry 확인. status가 running으로 전이되고 consumed_at 기록됨.
  • Step 1에서 peek한 entry와 id가 일치하는지 확인. 일치하지 않으면 (다른 세션이 중간에 pop) warn 출력 후 재진입 권장 종료.
  • 일치하면 다음 Step 진행.

Step 3/5: Skill 호출

action.skill 필드가 허용된 exact mst:{name}인지 확인한 뒤 해당 Skill 도구를 호출한다. Queue의 업무 args bytes는 재조합하거나 수정하지 않고, parent가 소유한 reserved binding만 별도 suffix로 추가한다.

Skill(skill: "{action.skill}", args: "{action.args}")

Dynamic child_skill={action.skill}은 queue의 allowlisted exact skill identity, trusted host-native metadata, 현재 호출 대상과 모두 같아야 한다. 불일치·duplicate·replay면 queue complete/fail이나 child delegation을 수행하지 않고 zero mutation으로 거부한다.

예시:

  • action.skill == "mst:request"Skill(skill: "mst:request", args: "--plan PLN-437 -a")
  • action.skill == "mst:approve"Skill(skill: "mst:approve", args: "-a REQ-584")
  • action.skill == "mst:agile"Skill(skill: "mst:agile", args: "--resume AGI-010 -a")

AUTO_MODE / -a 전파 규칙:

  • queue entry의 auto: true이면 args-a 또는 --auto가 이미 포함되어 있어야 한다 (enqueue 시점에 호출측이 기록).
  • resume은 auto 필드를 재판단하거나 args-a를 추가하지 않는다 — 원본 그대로 전달한다.
  • 호출된 하위 스킬이 args-a를 감지하여 AUTO_MODE를 활성화한다 (기존 스킬 프로토콜 그대로).
  • 이로써 "-a가 스킬 경계에서 흐려지는" 문제를 해결한다: queue에 한 번만 기록하면 이후 pop/호출에서도 유지된다.

사용자 대면 재개 안내:

  • queue entry나 fallback action에 잘못된 task ID가 포함되면 공통 parse_task_id 검증 에러를 그대로 보여주고, 임의로 split('-') 결과를 해석해 다른 요청/태스크를 실행하지 않는다.
  • 하위 스킬이 merge_conflict 복구 상태로 진입하면 /mst:recover의 explicit recovery 안내를 따른다. 충돌은 자동 성공 처리하지 않고, 수동 해소 또는 worktree 재생성 선택지를 노출한다.
  • .gran-maestro/ 경로는 queue/resolver가 제공한 절대경로 또는 공통 path helper 산출값만 사용한다. 문자열 concat으로 프로젝트 루트와 .gran-maestro를 재구성하지 않는다.

Step 4/5: 완료 기록 (complete | fail)

Step 3의 Skill 호출 결과를 기준으로 queue 상태를 확정한다.

성공 시:

python3 {PLUGIN_ROOT}/scripts/mst.py queue complete --id {action.id} --result "ok" --json

실패 시 (Skill 호출 중 예외 또는 하위 스킬이 명시적 실패 반환):

python3 {PLUGIN_ROOT}/scripts/mst.py queue fail --id {action.id} --error "{요약 메시지}" --json
  • complete/fail 중 하나는 반드시 호출한다. 생략 시 entry가 영구 running 상태로 남아 다음 iteration이 처리할 수 없다.
  • 중복 complete/fail은 no-op + warn이므로 안전하다 (queue 서브커맨드의 멱등성).

Step 5/5: Exit

  • 한 iteration 종료. 다음 action은 wrapper의 다음 iteration에서 처리한다.
  • "한 iteration = 한 action" 원칙. 루프 내부에서 여러 action을 처리하지 않는다.
  • 종료 메시지 예시: [resume] completed action {id} ({action.skill}) 또는 [resume] failed action {id}: {error}

예시: mst-loop wrapper에서 호출

# 무한 루프 (wrapper가 queue count=0 감지 시 break)
bash scripts/mst-loop.sh

# 또는 repository-local headless continuation runner를 한 번 호출
python3 {PLUGIN_ROOT}/scripts/mst.py queue drain-headless --json

현재 제한사항 (Phase 1+2 스코프)

  • Lease 없음 (Phase 3 예정): 동일 리소스(AGI/REQ/PLN)에 대한 동시 pop이 race condition을 일으킬 수 있다. 현재는 사용자가 수동으로 중복 실행을 피해야 한다. fcntl.flock으로 queue 파일 자체의 원자성은 보장되지만, "동일 REQ에 대해 두 세션이 각각 pop해서 동시 실행" 같은 도메인 레벨 직렬화는 Phase 3에서 lease manager로 해결 예정.
  • Outbox 없음 (Phase 4 예정): 백그라운드 codex/agy dispatch 중 세션 크래시 시 재진입 복원이 불완전할 수 있다. 현재는 각 Skill 호출이 단일 iteration 내에서 동기적으로 완료되는 경로만 안전하게 재진입 가능.
  • POSIX 파일시스템 가정: fcntl.flock은 로컬 파일시스템에서만 안정. NFS/네트워크 FS는 미지원.
  • 인라인 체이닝과 공존: 기존 스킬들의 인라인 Skill() 체이닝은 그대로 작동한다. queue + resume은 외부 재진입 경로이며 인라인 경로를 대체하지 않는다.

Signals

GitHub stars
24
Forks
5
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
resume-myrtlepn
Source
github.com/myrtlepn/gran-maestro