From mst
Analyzes user requirements and generates implementation specs (spec.md) for Gran Maestro workflow tasks. Bootstraps project directories/configs and enforces separate approval via /mst:approve.
npx claudepluginhub myrtlepn/gran-maestro --plugin mstThis skill uses the workspace's default tool permissions.
Gran Maestro ์ํฌํ๋ก์ฐ์ ์์์ . ์ฌ์ฉ์์ ์์ฒญ์ ๋ฐ์ PM ๋ถ์ Phase์ ์ง์ ํฉ๋๋ค.
Approves PM-written implementation specs and starts Phase 2 execution in Gran Maestro workflow. Supports single/batch approvals, dependency checks, DAG chaining, and non-stop execution rules.
Orchestrates spec-driven development workflow (Requirements โ Design โ Tasks โ Implementation) with approval gates. Activates for structured feature planning or 'use spec-driven'.
Transforms ideas into structured specifications (requirements, design, tasks) before implementation. Use when building features, fixing bugs, refactoring, or designing systems.
Share bugs, ideas, or general feedback.
Gran Maestro ์ํฌํ๋ก์ฐ์ ์์์ . ์ฌ์ฉ์์ ์์ฒญ์ ๋ฐ์ PM ๋ถ์ Phase์ ์ง์ ํฉ๋๋ค.
Maestro ๋ชจ๋ ๋นํ์ฑ ์ ์๋ ํ์ฑํ:
{PROJECT_ROOT}/.gran-maestro/ ๋๋ ํ ๋ฆฌ ์์ฑ, .gitignore์ ๋ฑ๋ก (๋ฏธ์กด์ฌ ์)
config.json / agents.json ์์ผ๋ฉด templates/defaults/์์ ๋ณต์ฌ
mode.json ํ์ธ: active: false์ด๊ฑฐ๋ ํ์ผ ์์ โ ์๋ ๋ด์ฉ์ผ๋ก ์์ฑ/์
๋ฐ์ดํธ:
โฑ๏ธ ํ์์คํฌํ ์ทจ๋ (MANDATORY):
TS=$(python3 {PLUGIN_ROOT}/scripts/mst.py timestamp now)์ ๋ช ๋ น ์คํจ ์ ํด๋ฐฑ:python3 -c "from datetime import datetime, timezone; print(datetime.now(timezone.utc).isoformat())"์ถ๋ ฅ๊ฐ์activated_atํ๋์ ๊ธฐ์ ํ๋ค. ๋ ์ง๋ง ๊ธฐ์ ๊ธ์ง.
{
"active": true,
"activated_at": "{TS โ mst.py timestamp now ์ถ๋ ฅ๊ฐ}",
"auto_deactivate": true,
}
requests/, worktrees/ ๋๋ ํ ๋ฆฌ ํ์ธ, ์์ผ๋ฉด ์์ฑ
์ฌ์ฉ์์๊ฒ ๋ชจ๋ ์ ํ ์๋ฆผ (์ฒซ ํ์ฑํ ์์๋ง)
workflow.default_agent๋ฅผ ์ฝ์ด Assigned Agent ๊ฒฐ์ ๊ทผ๊ฑฐ๋ฅผ ํ๋ณดํ๋ค.--resume/--plan ํด์๊ณผ source_plan ์ ํฉ์ฑ์ ์๋ฃํ ๋ค์๋ง spec ์์ฑ ๋จ๊ณ๋ก ์ง์
ํ๋ค.request.json, tasks/*/spec.md)๋ก ์ ํํ๊ณ ๊ตฌํ ํ์๋ ์ฐจ๋จํ๋ค.spec.md๊ฐ ์์ฑ๋๊ณ request.json.tasks ๋ฉํ๋ฐ์ดํฐ๊ฐ ๋๊ธฐํ๋์ด์ผ ํ๋ค.request.json.status๊ฐ spec_ready(๋๋ auto approve ์ฐ๊ณ ์ํ)๋ก ๊ฐฑ์ ๋์ด์ผ ํ๋ค./mst:approve ๋๋ /mst:approve -a) ๋๋ ์๋ ์ง์
๋ก๊ทธ๊ฐ ๋จ์์ผ ํ๋ค.workflow.default_agent ํ์ธ ์์ด Assigned Agent๋ฅผ ์ถ์ ์ผ๋ก ๊ธฐ์
ํ๋ค.codex exec + master ์ปค๋ฐ์ผ๋ก ์ ํํ๋ค. ๊ฒฉ๋ฆฌ ์คํ์ด ํ์ํ๋ฉด ๋ฐ๋์ mst:codex --dispatch ๋๋ mst:claude --dispatch ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๋ค.ยง0 Context Manifest์ ์ค์ ํ์ ๊ทผ๊ฑฐ ํ์ผ ๊ฒฝ๋ก๋ฅผ ๋จ๊ธด๋ค.request.json.tasks[].covers_ac์ ๋งคํํ๋ค.AUTO_APPROVE ๊ฒฐ์ ๊ทผ๊ฑฐ(CLI/config)์ ์ ์ฉ ๊ฒฝ๋ก๋ฅผ ์ถ๋ ฅํ๋ค.mst:codex --dispatch ๋๋ mst:claude --dispatch ์ฌ์ฉ ๋ก๊ทธ๊ฐ auto-decisions.md ๋๋ retrospective.md์ ๋จ์์์ด์ผ ํ๋ค.๊ฒฝ๋ก ๊ท์น (MANDATORY): ์ด ์คํฌ์ ๋ชจ๋
.gran-maestro/๊ฒฝ๋ก๋ ์ ๋๊ฒฝ๋ก๋ก ์ฌ์ฉํฉ๋๋ค. ์คํฌ ์คํ ์์ ์PROJECT_ROOT๋ฅผ ์ทจ๋ํ๊ณ , ์ดํ ๋ชจ๋ ๊ฒฝ๋ก์{PROJECT_ROOT}/์ ๋์ฌ๋ฅผ ๋ถ์ ๋๋ค.PROJECT_ROOT=$(pwd)
{PLUGIN_ROOT}๋ ์ด ์คํฌ์ "Base directory"์์skills/{์คํฌ๋ช }/์ ์ ๊ฑฐํ ์ ๋๊ฒฝ๋ก์ ๋๋ค. ์๋๊ฒฝ๋ก(.claude/...)๋ ์ ๋ ์ฌ์ฉํ์ง ์์ต๋๋ค.
python3 {PLUGIN_ROOT}/scripts/mst.py hooks sync --silent || true
ํ๋ฌ๊ทธ์ธ ๋ฒ์ ์ด .claude/hooks/.mst-hook-version๊ณผ ๋ค๋ฅด๋ฉด hook ํ์ผ์ ์๋ ๋๊ธฐํํฉ๋๋ค. ๋์ผ ๋ฒ์ ์ด๋ฉด no-op(์ ms). ์คํจํด๋ ์ํฌํ๋ก์ฐ๋ฅผ ์ฐจ๋จํ์ง ์์ต๋๋ค.
~/.claude/user-profile.json (AskUserQuestion ์ปจํ
์คํธ, ๋น์ฐจ๋จ)~/.claude/user-profile.json์ Readํ๋ค.
user_profile_context = null๋ก ์ฒ๋ฆฌํ๊ณ ๊ธฐ์กด ๋์์ ์ ์งํ๋ค (graceful fallback).role (string)experience_level (string)domain_knowledge (string[])communication_style (string)user_profile_context = null๋ก ์ฒ๋ฆฌํ๋ค (์ํฌํ๋ก์ฐ ์ฐจ๋จ ๊ธ์ง).AskUserQuestion๊ณผ ์ฌ์ฉ์ ์ค๋ช
ํ
์คํธ ์์ฑ ์:
communication_style์ ์ต์ฐ์ ๋ฐ์ํ๋ค.experience_level/domain_knowledge์ ๋ง์ถฐ ์ฉ์ด ์์ค๊ณผ ์ค๋ช
๊น์ด๋ฅผ ์กฐ์ ํ๋ค.์ธ๋ถ ์์กด์ฑ(๋ผ์ด๋ธ๋ฌ๋ฆฌ/API/ํ๋ ์์ํฌ/๋ฒ์ /ํ๋กํ ์ฝ) ํ๋จ์ ์๋ ๊ณตํต ํ๋กํ ์ฝ์ ๋ฐ๋ฅธ๋ค.
Bash(python3 {PLUGIN_ROOT}/scripts/mst.py config get reference.auto_search)๋ก reference.auto_search๋ฅผ ํ์ธํ๋ค. true์ผ ๋๋ง ์๋ WebSearch๋ฅผ ํ์ฉํ๋ค. ์ค์ ๋ฏธ์กด์ฌ ์ ๊ธฐ๋ณธ๊ฐ์ cache_ttl_days=2, cutoff_threshold_months=0.5, max_searches_per_step=5, llm_auto_trigger=true, auto_fact_check=true.reference.llm_auto_trigger == true์ด๋ฉด PM์ด ์ต์ ์ ๋ณด๊ฐ ํ์ํ๋ค๊ณ ํ๋จํ ๋๋ WebSearch๋ฅผ ํธ๋ฆฌ๊ฑฐํ๋ค. false์ด๋ฉด ํค์๋ ๋งค์นญ ๊ธฐ๋ฐ ๋์๋ง ์ ์งํ๋ค..gran-maestro/references/ ์บ์๋ฅผ reference search --keyword "{keyword}" --json์ผ๋ก ํ์ธ, (b) searched_at + cache_ttl_days ๊ธฐ์ค fresh/stale ํ์ , (c) ํ์ฌ ์๊ฐ ๋๋น cutoff_threshold_months ์ด๊ณผ ์ expired ํ์ .stale/expired์ผ ๋๋ง ๊ฒ์ํ๋ค. reference.auto_search == true์ผ ๋๋ง ์คํํ๊ณ Step๋น max_searches_per_step์ ๋์ง ์๋๋ค. reference.auto_fact_check == true์ด๋ฉด ํต์ฌ claim์ 1ํ์ฑ ๊ต์ฐจ WebSearch๋ก ๊ฒฝ๋ ๊ฒ์ฆํ๋ค.Bash๋ก mst.py reference add๋ฅผ ํธ์ถํ๋ค. ํ/ํ
์คํธ ๊ฒฐ๋ก ์์ฝ๋ง์ผ๋ก๋ ์ ์ฅ ์๋ฃ๊ฐ ์๋๋ฉฐ content.md๋ raw ๋ฐ์ท(์๋ฌธ ๊ทผ๊ฑฐ) ์ค์ฌ์ผ๋ก ๋จ๊ธด๋ค.
python3 {PLUGIN_ROOT}/scripts/mst.py reference add --topic "{topic}" --url "{url}" --summary "{summary}" --content "{raw ๋ฐ์ท ๋ณธ๋ฌธ}"summary๋ ํ ์ค ์ธ๋ฑ์ค ์ ์ง).> ์ธ์ฉ: "{์๋ฌธ ํต์ฌ ๋ฌธ์ฅ}" (์ถ์ฒ: {URL}, ๋ ์ง: {YYYY-MM-DD})| ์ด | ๊ฐ | ํํ์ raw markdown table๊ณผ ์ถ์ฒ URL์ ๋ณด์กดํ๋ค.content.md Read ํ์): summary๋ง์ผ๋ก ๋ถ์กฑํ๊ฑฐ๋ ํ/์ฝ๋/์๋ฌธ ๋์์ค๊ฐ ๊ฒฐ๋ก ์ ์ํฅ์ ์ฃผ๋ฉด ๋ฐ๋์ content.md๋ฅผ Readํ๋ค.[REFERENCE_CONTEXT]๋ฅผ ์ฃผ์
ํ๋ค. ํ์: current_date, model_cutoff, references: REF-001 (fresh|stale|expired) {topic} | {url}. ์ฐธ์กฐ๊ฐ ์์ผ๋ฉด references: none์ผ๋ก ๋ช
์ํ๋ค.archive.auto_archive_on_create๊ฐ true์ด๋ฉด:
python3 {PLUGIN_ROOT}/scripts/mst.py request count --active โ ์ด๊ณผ ์ python3 {PLUGIN_ROOT}/scripts/mst.py archive run --max {max_active_sessions}์์ธ ์์นด์ด๋ธ ๋ก์ง์ /mst:archive ์คํฌ์ "์๋ ์์นด์ด๋ธ ํ๋กํ ์ฝ" ์ฐธ์กฐ.
โ ๏ธ ์ด ๋จ๊ณ๋ ๊ฑด๋๋ธ ์ ์์: spec.md Assigned Agent ๊ฒฐ์ ์ ๋ฐ๋์ ์คํ. ์ด ๋จ๊ณ ์์ด spec.md ์์ฑ ๊ธ์ง.
Bash(python3 {PLUGIN_ROOT}/scripts/mst.py config get workflow.default_agent) โ workflow.default_agent ์ถ์ถ โ DEFAULT_AGENT ๋ณ์ ๋ณด๊ด.
ํ์ผ์ด ์์ผ๋ฉด templates/defaults/config.json์์ workflow.default_agent์ agent_assignments๋ฅผ Readํ์ฌ DEFAULT_AGENT ๋ฐ ๋๋ฉ์ธ ์ถ๋ก ๊ธฐ์ค์ผ๋ก ์ฌ์ฉํ๋ค.
๋ชจ๋ spec.md์ Assigned Agent ํ๋๋ ๋ฐ๋์ [config: {DEFAULT_AGENT}] โ ... ํ์์ผ๋ก DEFAULT_AGENT๋ฅผ ๋ช
์ํด์ผ ํ๋ค. DEFAULT_AGENT ๋ฏธํ์ธ ์ํ์ Assigned Agent ๊ฒฐ์ ์ ์๋ฌ๋ก ์ฒ๋ฆฌํ๋ค. agent_assignments ์ฝ๊ธฐ ์ _๋ก ์์ํ๋ ํค๋ ์์ด์ ํธ๋ช
์ผ๋ก ๊ฐ์ฃผํ์ง ์๋๋ค. config.resolved.json ์์ผ๋ฉด templates/defaults/config.json์ agent_assignments๋ฅผ fallback์ผ๋ก Readํ๋ค.
--auto / -a ๊ฐ์ง ์ AUTO_APPROVE=true๋ก ์ค์ ํ๋ค (CLI ์ต์ฐ์ ).
1.5. CLI ์ธ์์ -a/--auto๊ฐ ์์ผ๋ฉด state guarded fallback์ ์๋ํ๋ค:
{PLUGIN_ROOT}/scripts ๊ฒฝ์ ๋ก read_workflow_state_auto_mode("mst:request", expected_source_id) ํธ์ถexpected_source_id: --plan PLN-NNN์ด ์์ผ๋ฉด PLN-NNN, --resume REQ-NNN์ด ์์ผ๋ฉด REQ-NNN, ๋ ๋ค ์์ผ๋ฉด NoneAUTO_APPROVE์ ์ฑํ; None์ด๋ฉด Step 2(config)๋ก ์งํBash(python3 {PLUGIN_ROOT}/scripts/mst.py config get auto_mode.request)๋ก ๊ฐ์ ํ์ธํ๋ค. true๋ฉด AUTO_APPROVE=true.args > state(guarded) > config > default(false).โ ๏ธ CONTINUATION GUARD: ์๋ธ์คํฌ ๋ฐํ ํ ์ฆ์ ๋ค์ Step ์งํ (hook์ด ์๋ ๊ฐ์ ).
์ฌ๊ฐ ๋์ ๊ฐ์ง:
--resume REQ-NNN์ด ์์ผ๋ฉด RESUME_REQ_ID=REQ-NNN--resume ์์ด๋ ์์ ์ธ์์ ๋จ์ผ REQ-NNN ํจํด์ด ์์ผ๋ฉด ํ์ ํธํ์ผ๋ก ์ฌ๊ฐ๋ก ํด์--resume ๊ฐ๊ณผ ์์ ์ธ์ REQ-ID๊ฐ ๋์์ ์กด์ฌํ๊ณ ์๋ก ๋ค๋ฅด๋ฉด ์ค๋ฅ๋ก ์ค๋จRESUME_REQ_ID๊ฐ ์ค์ ๋ ๊ฒฝ์ฐ(๊ธฐ์กด REQ ์ฌ๊ฐ ๋ถ๊ธฐ):
{PROJECT_ROOT}/.gran-maestro/requests/{RESUME_REQ_ID}/request.json Read์ ๊ท REQ ์์ฑ ๊ธ์ง)status ๊ฒ์ฆ: ํ์ฉ = pending_dependency, phase1_analysis, spec_ready; ๊ฑฐ๋ถ = done, completed, accepted, cancelledpending_dependency ์ํ๋ฉด dependencies.blockedBy๋ฅผ ํ์ธ: ๋น์ด์์ง ์์ผ๋ฉด ์ค๋จ, ๋น์ด์์ผ๋ฉด phase1_analysis๋ก ์ ์ด ํ ์งํ--plan PLN-NNN ํจ๊ป ์ ๊ณต ์ source_plan ์ ํฉ์ฑ ํ์ธ: ๊ธฐ์กด source_plan ์์ผ๋ฉด ๋ณด๊ฐ, ๋ค๋ฅธ ๊ฐ์ด๋ฉด ์ค๋ฅrequest.json.auto_approve๋ ํ์ฌ ์คํ ์ปจํ
์คํธ์ AUTO_APPROVE ๊ฐ์ผ๋ก ๋๊ธฐํRESUME_REQ_ID๊ฐ ์๋ ๊ฒฝ์ฐ(์ ๊ท REQ ์์ฑ ๋ถ๊ธฐ):
python3 {PLUGIN_ROOT}/scripts/mst.py counter next โ ์ถ๋ ฅ ID ์ฌ์ฉ{PROJECT_ROOT}/.gran-maestro/requests/counter.json Read โ next_id = last_id + 1; ํ์ผ ๋ฏธ์กด์ฌ ์ requests/, requests/completed/, archive/requests-*์์ ์ต๋ ๋ฒํธ ๊ฒฐ์ ํ counter.json ์์ฑ{PROJECT_ROOT}/.gran-maestro/requests/REQ-NNN/ ๋๋ ํ ๋ฆฌ ์์ฑ (NNN์ 3์๋ฆฌ zero-padded), ํ์ tasks/, discussion/, design/ ์๋ธ๋๋ ํ ๋ฆฌ๋ ํจ๊ป ์์ฑrequest.json):โฑ๏ธ ํ์์คํฌํ ์ทจ๋ (MANDATORY):
TS=$(python3 {PLUGIN_ROOT}/scripts/mst.py timestamp now)์ ๋ช ๋ น ์คํจ ์ ํด๋ฐฑ:python3 -c "from datetime import datetime, timezone; print(datetime.now(timezone.utc).isoformat())"์ถ๋ ฅ๊ฐ์created_atํ๋์ ๊ธฐ์ ํ๋ค. ๋ ์ง๋ง ๊ธฐ์ ๊ธ์ง.
{
"id": "REQ-NNN",
"title": "{์ฌ์ฉ์ ์์ฒญ ์์ฝ}",
"original_request": "{์ ์ฒด ์์ฒญ ํ
์คํธ}",
"status": "phase1_analysis",
"current_phase": 1,
"created_at": "{TS โ mst.py timestamp now ์ถ๋ ฅ๊ฐ}",
"auto_approve": false,
"source_plan": null,
"dag_auto_chain": false,
"tasks": [],
"dependencies": { "blockedBy": [], "relatedTo": [], "blocks": [] },
"stitch_screens": []
}
auto_approve: CLI --auto/-a ๊ฐ์ง ์ true (์ต์ฐ์ ); ์๊ณ config.auto_mode.request=true์ด๋ฉด true; ๊ทธ ์ธ falsesource_plan: null(plan ์์ด ์์ฑ), "PLN-NNN"(plan ๊ธฐ๋ฐ), ํ๋ ๋ถ์ฌ(๊ตฌ๋ฒ์ )dag_auto_chain: false(๊ธฐ๋ณธ, ํ์ฌ REQ๋ง), true(DAG ์ฐ์ ์คํ), ํ๋ ๋ถ์ฌ ์ false ๊ฐ์ฃผAUTO_APPROVE=true์ด๋ฉด workflow state๋ฅผ ๊ธฐ๋กํ๋ค (non-blocking):
ACTIVE_REQ_ID๋ ์ฌ๊ฐ ๋ถ๊ธฐ์์๋ RESUME_REQ_ID, ์ ๊ท ์์ฑ ๋ถ๊ธฐ์์๋ REQ-NNNMST_STATE_PPID="${PPID}" python3 {PLUGIN_ROOT}/scripts/mst.py state set-workflow \
--active true \
--skill mst:request \
--req "${ACTIVE_REQ_ID}" \
--next-skill mst:approve \
--next-source "${ACTIVE_REQ_ID}" \
--source-skill mst:request \
--auto true \
|| echo "[mst:request] warning: failed to update workflow state" >&2
AUTO_APPROVE=false์์๋ ์ด ํธ์ถ์ ์คํํ์ง ์๋๋ค.PM Conductor ์ญํ ๋ก Phase 1 ๋ถ์ ์ํ (agents/pm-conductor.md์ <phase1_protocol> ์ค์):
a. ์์ฒญ ํ์ฑ ๋ฐ ๋ณต์ก๋ ๋ถ๋ฅ (simple | standard | complex)
b. Simple โ ๋จ๋
๋ถ์ / StandardยทComplex โ Analysis Squad ํ ์ํ
c. ์ฝ๋๋ฒ ์ด์ค ํ์ (config.phase1_exploration.roles ๊ธฐ๋ฐ ๋ณ๋ ฌ):
โ ๏ธ ํ์ ๋ชฉ์ : โ ๊ธฐ์กด ํจํดยท์ปจ๋ฒค์ ํ์ โก ํต์ฌ ์ง์ ์ ์๋ณ(1~3๊ฐ ํ์ผ/๋๋ ํ ๋ฆฌ) โข ์ถฉ๋ ๊ฐ๋ฅ์ฑ ๊ฐ์ง. ๊ตฌ์ฒด์ ์ธ ๊ตฌํ ๋ฐฉ๋ฒ์ ์์ด์ ํธ๊ฐ worktree์์ ์ง์ ํ๋จํ๋ค.
config ์ฝ๊ธฐ: Bash(python3 {PLUGIN_ROOT}/scripts/mst.py config get phase1_exploration.roles) ์ถ๋ ฅ์ phase1_exploration.roles ์ฐธ์กฐ
๊ฐ role์ ๋ชจ๋ธ ๊ฒฐ์ : phase1_exploration.roles.{role}.tier โ providers[agent][tier]๋ก resolve (tier ๋ฏธ์ง์ ์ providers[agent].default_tier ์ฌ์ฉ)
โ symbol_tracing role agent [background dispatch] โ enabled=true์ธ ๊ฒฝ์ฐ
โก broad_scan role agent [background dispatch] โ enabled=true์ธ ๊ฒฝ์ฐ (โ ๊ณผ ๋์ผ ์๋ต์์ dispatch)
โข Claude ์ง์ ํ์ [์ฆ์ ์์] โ Read/Glob/Grep ์์จ ์คํ (ํ์ ๋ฒ์ ์์จ ํ๋จ, ์ค๋ณต ํ์ฉ)
์์ ๋ ๊ฒฐ๊ณผ๋ฅผ Claude ์ง์ ํ์ ์ปจํ
์คํธ์ ํจ๊ป ์ข
ํฉ. ์ด ์์ = max(enabled_roles_time, claude_direct_time).
๋ฐ๋์ Skill(skill: "mst:codex/gemini", ...) ๋๊ตฌ๋ก ํธ์ถ โ MCP ์ง์ ํธ์ถ ๊ธ์ง. role agent ๊ธฐ๋ณธ๊ฐ: symbol_tracing=codex, broad_scan=gemini
c-ref. Reference Lookup ์คํ (Step 1c ์๋ฃ ์งํ, Step h ์ด์ , MANDATORY):
Reference Lookup Protocol์ ์คํํ๋ค.reference.auto_search != true์ด๋ฉด ๊ธฐ์กด REF ์บ์ ์กฐํ๋ง ์ํํ๋ค.[REFERENCE_CONTEXT]๋ฅผ reference_context_block์ผ๋ก ๋ณด๊ดํ๊ณ Step h spec ์์ฑ ํ๋กฌํํธ์ ๋ฐ๋์ ์ฃผ์
ํ๋ค.
c-arch. ์ํคํ
์ฒ ๋
ผ์ ๊ฒ์ดํธ (Step 1d-arch):pm_arch_confidence(0.0~1.0) ์ฐ์ ๊ธฐ์ค:
arch_gate_threshold ์ฝ๊ธฐ: config get workflow.arch_gate_threshold โ fallback templates/defaults/config.json โ fallback 0.7workflow.high_pass_guard ์ฝ๊ธฐ: config get workflow.high_pass_guard โ fallback templates/defaults/config.json โ fallback: enabled=true, confidence_supporting_only=true, require_external_execution_evidence=true, require_independent_judgement=true, block_self_report_only_pass=true, plan_bypass_requires_explicit_rationale=trueenabled=true ๊ธฐ์ค):
self_report_only_block, external_evidence_missing, independent_judgement_required, "risk_signal_review_required".confidence๋ ๋ณด์กฐ ์ ํธ์ด๋ฉฐ, risk signal(A/B/C) ์ค ํ๋๋ผ๋ ๊ฐ์ง๋๋ฉด confidence ๊ฐ๋ง์ผ๋ก gate๋ฅผ ๋ซ์ ์ ์๋ค.req-arch-decision.md์ reason์ ํด๋น reason token์ ๊ธฐ๋กํ๊ณ ์ถ๊ฐ ๊ฒํ ๊ฒฝ๋ก๋ฅผ ์ ์งํ๋ค.pm_arch_confidence >= arch_gate_threshold ๋๋ high_pass_guard.enabled=true ๋๋ self-report only + ์ฆ๊ฑฐ ๋ถ์กฑ)pm_arch_confidence < arch_gate_threshold ๋๋ ๋ช
์์ ๋น์ํ ๊ทผ๊ฑฐ ๊ธฐ๋ก)--plan bypass: plan_bypass_requires_explicit_rationale=true์ด๋ฉด plan ๊ธฐ๋ฐ ๋ช
์์ ์ฐํ ๊ทผ๊ฑฐ(์ธ๋ถ ์คํ ์ฆ๊ฑฐ + ๋ถ๋ฆฌ๋ ํ์ ๋จ๊ณ ์ถ์ฒ) ํ์. ์์ผ๋ฉด bypass ๊ฑฐ๋ถ โ Gate Open. ํ์ฉ ์์๋ req-arch-decision.md์ gate: skip, reason: "plan ์ฐธ์กฐ + explicit rationale" ์ ์ฅ.AskUserQuestion์ผ๋ก ๋ฐฉํฅ ์ ํ ์์ฒญ (โ "์ ์ ๋ฐฉํฅ์ผ๋ก ์งํ" โก "๋ฐฉํฅ์ ๋ฐ๊ฟ์ ์ง์ ์
๋ ฅ" โข PM ํ๋จ ์ ideation/discussion/explore ๋ณด์กฐ ์ ํ์ง)REQ-NNN/discussion/req-arch-decision.md์ ์ ์ฅ:
gate: open | close | skip
reason: "..."
confidence: 0.75
threshold: 0.7
triggers:
A: true | false
B: true | false
C: true | false
result: ideation | discussion | none
arch_direction: "๋ฐฉํฅ ์์ฝ (gate open ์๋ง)"
## ์ํคํ
์ฒ ์ํฅ๋ ๊ฒํ ์น์
์ ์ฝ์
ํ๋ค. Gate Close/skip ์ ๋ฏธ์ฝ์
.
d-1. --from-debug DBG-NNN ์ ๊ณต ์ฌ๋ถ ์ฒ๋ฆฌ:debug/DBG-NNN/debug-report.md Read (๋ฏธ์กด์ฌ ์ ๊ฒฝ๊ณ ํ ํ๋๊ทธ ๋ฌด์)debug_context ๋ณด๊ด: linked_debug_id, root_cause, fix_suggestions, affected_filesrequest.json์ "linked_debug": "DBG-NNN" ํ๋ ์ถ๊ฐ; spec.md์ ## ๋๋ฒ๊ทธ ์ฐ๊ณ ์น์
์๋ ์ฝ์
--from-debug์ --plan ๋์ ์: --plan ์ฐ์ , debug_context๋ ๋ณด์กฐ ์ ์ง
d-0. active plan resolver (--plan ๋ฏธ์ง์ ์, Step d ์ง์ ):PLN-NNN์ด ๊ฐ์ง๋์ง ์์ ๊ฒฝ์ฐplans/*/plan.json์์ status == "active"์ธ plan๋ง updated_at ๋ด๋ฆผ์ฐจ์์ผ๋ก ์์งsource_plan: null ์ ์งAUTO_APPROVE=false โ AskUserQuestion์ผ๋ก ํ์ธ; AUTO_APPROVE=true โ ์๋ ์ฑํAUTO_APPROVE=false โ AskUserQuestion์ผ๋ก ์ ํ; AUTO_APPROVE=true โ ์ต์ 1๊ฑด ์๋ ์ฑํ, ๋๋จธ์ง๋ ๋ก๊ทธ์ ํ๋ณด๋ก ๊ธฐ๋กresolved_plan_id๊ฐ ์ค์ ๋๋ฉด Step d์์ --plan PLN-NNN๊ณผ ๋์ผํ๊ฒ ์ฒ๋ฆฌํ๋ค.
d. --plan ์ ๊ณต ์ฌ๋ถ ์ฒ๋ฆฌ:--plan PLN-NNN ๋๋ ์์ฐ์ด PLN-NNN ๋๋ resolved_plan_id ๊ฐ์ง ์ plans/PLN-NNN/plan.json + plan.md Readrequest.json์ source_plan: "PLN-NNN" ๊ธฐ๋ก; plan.json์ linked_requests์ REQ-NNN ์ถ๊ฐ, status active โ in_progress## Objective ์ปจํ
์คํธ ์น์
์ด ์กด์ฌํ๋ฉด ํ์ฑํด objective_context ๋ณ์๋ก ๋ณด๊ด (objective_md_path, jtbd_summary, project_dod_items[], success_metrics[]).objective_context=null๋ก ์ฒ๋ฆฌ (ํ์ ํธํ). ์ผ๋ถ ํญ๋ชฉ ๋น์ด์์ผ๋ฉด ๊ฐ๋ฅํ ํญ๋ชฉ๋ง ๋ณด๊ด (๋น์ฐจ๋จ).plan_type = plan.json.type (๋ฏธ์กด์ฌ ์ "code")plan_strategy = type_strategies[plan_type] || type_strategies["code"] ({PLUGIN_ROOT}/templates/defaults/type-strategies.json Read){"template":"templates/impl-request.md","worktree_policy":"required","review_mode":"code","accept_mode":"squash-merge"}plan_strategy.template == "templates/doc-request.md" โ DOC_SPEC_MODE=true; ๊ทธ ์ธ โ DOC_SPEC_MODE=false์์์ ํํธ ํ์ผ ๋ชฉ๋ก ์ถ์ถ โ context_manifest_filescontext_manifest_files๋ ์ต์ 1๊ฐ ์ด์ ์ ์ง (๋น ๋ชฉ๋ก ๊ธ์ง)plan.json์ linked_designs ๋ฐฐ์ด ๋น์ด์์ง ์์ ๋):
designs/DES-NNN/design.json Read (๋ฏธ์กด์ฌ ์ silent skip)stitch_project_url + screens[] ์ถ์ถ โ des_context ๋ณด๊ด; html_file์ด null/๋น ๋ฌธ์์ด์ด๋ฉด "N/A", ์กด์ฌํ๋ฉด ์ ๋๊ฒฝ๋ก๋ก ๋ณํrequest.json์ "linked_designs": ["DES-NNN", ...] ์ถ๊ฐ## 10. UI ์ค๊ณ (Stitch)
- Stitch ํ๋ก์ ํธ: {stitch_project_url}
- ์์ฑ ํ๋ฉด:
- {screens[0].title}: {screens[0].url}
๊ตฌํ ์ฝ๋: {screens[0].html_file ์ ๋๊ฒฝ๋ก ๋๋ N/A}
screens[] ๋น์ด์์ผ๋ฉด ํ๋ก์ ํธ URL๋ง, linked_designs ๋น์ด์์ผ๋ฉด ์ ์ฒด skip## ์บก์ฒ ์ฐธ์กฐ ์น์
์ด ์กด์ฌํ ๋):
## ์บก์ฒ ์ฐธ์กฐ ํ
์ด๋ธ ํ์ฑ โ ๊ฐ CAP-NNN์ capture.json Read (selector, css_path, memo, screenshot_path ์ถ์ถ)request.json์ "linked_captures": ["CAP-NNN", ...] ์ถ๊ฐ## ์บก์ฒ ์ปจํ
์คํธ ์๋ ์ฑ์:
## 11. ์บก์ฒ ์ปจํ
์คํธ
| CAP ID | ์์ | CSS Path | Memo | Screenshot |
|--------|------|----------|------|------------|
| CAP-001 | {selector} | {css_path} | {memo} | {screenshot_path} |
10๊ฐ ์ด์: ์์ 5๊ฐ๋ง ์ธ๋ผ์ธ, "์ถ๊ฐ N๊ฐ ์บก์ฒ โ captures/ ๋๋ ํ ๋ฆฌ์ capture.json ์ฐธ์กฐ" ์๋ด. ์น์
์์ผ๋ฉด ์ ์ฒด skip.plan.json์ linked_intent ํ๋ ์กด์ฌ ์):
python3 {PLUGIN_ROOT}/scripts/mst.py intent get {INTENT_ID} --json## Intent (JTBD) ์น์
์ ์ฃผ์
. ์คํจ ์ warn๋ง ์ถ๋ ฅ, ์ํฌํ๋ก์ฐ ์ฐจ๋จ ๊ธ์ง.## ๋ถ๋ฆฌ ์คํ ์น์
์ 2๊ฐ ์ด์ ๋จ๊ณ ์ ๋ค์ค REQ ์์ฑ ๋ชจ๋:
status: "pending_dependency", blockedBy ์ค์ ). ๋ชจ๋ ๋จ๊ณ REQ์ request.json์ source_plan: "PLN-NNN"์ ๋์ผํ๊ฒ ๊ธฐ๋ก.request.json์ dependencies.blocks ์ค์ , plan.json์ ๋ชจ๋ REQ ID ์ถ๊ฐAUTO_APPROVE=true: AskUserQuestion ์๋ต, 1๋จ๊ณ dag_auto_chain=true ์๋ ์ค์ AUTO_APPROVE=false: AskUserQuestion์ผ๋ก "์(DAG ์์๋ก ์๋ ์ฐ์)" / "์๋์ค(์ด REQ๋ง ์คํ)" ํ์ธsource_plan์ ๊ธฐ์กด ๊ฐ ์ ์ง)
d-doc. ยง Doc Spec Flow (DOC_SPEC_MODE=true์ผ ๋๋ง ์คํ):## TOC ์ด์, ## ์์ค ์กฐ์ฌ ๊ฒฐ๊ณผ, ## ๊ฒ์ฆ ๊ณํ ์น์
Read โ doc_toc, doc_sources, doc_verification_plan ๋ณด๊ด. ํ๋๋ผ๋ ๋๋ฝ ์ ๊ฒฝ๊ณ ํ ์ค๋จ.doc_sub_type: plan.json.doc_sub_type ์ฐ์ (tutorial|howto|reference|explanation|adr|operational๋ง ํ์ฉ) โ plan.md ๋ชฉ์ ํ
์คํธ ๋งคํ โ ์ต์ข
fallback "explanation"type-strategies.json์ doc.sub_types[doc_sub_type]์์ verification/checklist ์ฝ๊ธฐ (๋๋ฝ ์ explanation ์ ๋ต์ผ๋ก fallback)ยง1 ์์ฝ/ยง2 ๋ฒ์๋ ๋ฌธ์ ๋ชฉ์ ยท๋
์ยท์ฐ์ถ๋ฌผ ๊ธฐ์ค์ผ๋ก ์์ฑ; AC๋ ์ ํ์ฑยท์๊ฒฐ์ฑยท๋
์์ ํฉ์ฑยทsubtype ์ ์ฉ ๊ธฐ์ค์ผ๋ก ์์ฑdoc_toc์ ์์ ์น์
1๊ฐ = 1๊ฐ ์งํ ํ์คํฌ; ๊ฐ ํ์คํฌ์ doc_sources ๊ทผ๊ฑฐ์ doc_subtype_checklist ๊ฒ์ฆ ํญ๋ชฉ ํฌํจ; TOC ์์ ๊ธฐ๋ฐ ์คํSkill(skill: "mst:ideation"/"mst:discussion", args: "{์ฃผ์ } --from-request") ์คํ โ ํต์ฌ 3~5๊ฐ ์ถ์ถ ํ ์๋ ์งํ โ discussion/req-ambiguity-{synthesis|consensus}.md์ ์ ์ฅ โ spec.md ## 9. ํ ํ๋จ ๊ธฐ๋ฐ ๊ฒฐ์ ์น์
์ ๊ธฐ๋ก
f. ๋๋ฒ๊ทธ ์๋ ๊ฐ์ง (LLM ํ๋จ): ๋ฒ๊ทธ/์๋ฌ/์์ธ๋ถ์ ๋ฑ ๋๋ฒ๊น
์๋ ๊ฐ์ง ์:auto_trigger_from_request=true: /mst:debug ์๋ ํธ์ถ ํ ์ด ์ํฌํ๋ก์ฐ ์ข
๋ฃfalse: /mst:debug ์ฌ์ฉ ์๋ด ํ ์ผ๋ฐ ์ํฌํ๋ก์ฐ ์งํ
g. ์ ๊ทผ ๋ฐฉ์ ๊ฒฐ์ ์ Ideation ์๋ ํธ๋ฆฌ๊ฑฐ (LLM ํ๋จ): ์๋ ์ค ํ๋ ํด๋น ์ Skill(skill: "mst:ideation", args: "{์ฃผ์ } --from-request") ํธ์ถ:complex ๋ถ๋ฅ, ํธ๋ ์ด๋์คํ ๋ถ๋ช
ํ, ๊ณ ์ํฅ ์์ฌ๊ฒฐ์ , PM ๋จ๋
ํ๋จ ํ์ ๋ถ์กฑโ ๏ธ plan์์ ๊ธฐ์ ์ ๊ทผ๋ฒ์ ๊ฒฐ์ ํ์ง ์๋ ๊ฒ์ ์๋๋ ์ค๊ณ์ ๋๋ค. ์ฝ๋๋ฒ ์ด์ค๋ฅผ ์ง์ ๋ณธ ์ํ์์ ๊ฒฐ์ ํ๋ ์ด ๋จ๊ณ๊ฐ ๋ ์ ํํ ํ๋จ์ ์ ๊ณตํฉ๋๋ค.
๊ฒ์ดํธ ์คํ ์ฒ๋ฆฌ:
AUTO_APPROVE=false: AskUserQuestion์ผ๋ก ์ฌ์ฉ์์๊ฒ ์ ๊ทผ ๋ฐฉ์ ํ์ธ (ํ๋ณด 2~3๊ฐ, ํธ๋ ์ด๋์คํ ํฌํจ, ๋ณด์กฐ ์ ํ์ง "ideation์ผ๋ก ๋ค๊ฐ๋ ๊ฒํ " ํฌํจ) โ ๋ต๋ณ ๋ฐ์ ํ g-1๋ก ์งํAUTO_APPROVE=true: Skill(skill: "mst:ideation", args: "{์ฃผ์ } --from-request") ์์จ ์คํ โ ๊ฒฐ๊ณผ๋ฅผ spec ์์ฑ์ ๋ฐ์ โ discussion/req-approach-synthesis.md์ ์ ์ฅํธ๋ฆฌ๊ฑฐ ์กฐ๊ฑด (ํ๋ ์ด์ ํด๋น ์ ์คํ): complex ๋ถ๋ฅ, ์ ๊ท ๋ผ์ด๋ธ๋ฌ๋ฆฌยทํจํดยทAPI ๋์
, Step g์์ ideation/discussion ์คํ, PM ๊ตฌํ ์ ๊ทผ๋ฒ ํ์ ๋ 0.7 ๋ฏธ๋ง
skip ์กฐ๊ฑด (ํ๋๋ผ๋ ํด๋น ์ skip): simple ๋ถ๋ฅ AND ์ฝ๋๋ฒ ์ด์ค ๋ด ๋์ผ ํจํด ์ ๋ก ์กด์ฌ; --plan ์ ๊ณต AND plan.md์ ๊ตฌํ ๋ฐฉ์ ๊ตฌ์ฒด์ ์ผ๋ก ๋ช
์; req-impl-research.md ์กด์ฌ; AUTO_APPROVE=true(๋จ, ์๋ ์์จ ์ฒ๋ฆฌ ์ฐธ์กฐ)
AUTO_APPROVE=true ์ฒ๋ฆฌ:
impl_confidence(0.0~1.0) ์์ฒด ์ฐ์ ํ ์๋ ฅ์ผ๋ก ๋์ด๋ ๊ฒ ์ต์ฐ์ : โ ์ฝ๋๋ฒ ์ด์ค ์ฌํ์ โ โก WebSearch ๊ตฌํ ์์ค ํ์ค ํ์ธ โ โข ์ฌ์ฐ์ impl_confidence >= 0.7: PM ์์จ ํ๋จ์ผ๋ก spec ๋ณด์ ํ h-0 ์งํimpl_confidence < 0.7: ์ ๊ทผ๋ฒ ๋ฐฉํฅ ๋ถ๋ช
ํ/๋ฐ์ฐ ํ์ โ ideation; ๋ฆฌ์คํฌยทํธ๋ ์ด๋์คํ ํฉ์ ํ์ โ discussion; ๊ทธ ์ธ โ PM ์์จ ํ๋จ (ideation/discussion ํธ์ถ ๊ธ์ง)req-impl-research.md์ ๊ธฐ๋ก์คํ ์ ์ฐจ (AUTO_APPROVE=false์ธ ๊ฒฝ์ฐ):
{๋ผ์ด๋ธ๋ฌ๋ฆฌ} {๋ฒ์ } best practices, {ํจํด} implementation guide. ์ ๋ต ์์ค ๊ฒ์ ๊ธ์ง โ ๊ตฌํ ๋ฐฉ์์ ํ์ .ALIGNED(ํ์ค ์ผ์น), DEVIATION(ํ์ค์์ ๋ฒ์ด๋จ), DUPLICATE(๊ธฐ์กด ๊ตฌํ ์กด์ฌ), BETTER_ALTERNATIVE(๋ ๋จ์ํ ๋ฐฉ๋ฒ ๋ฐ๊ฒฌ)ALIGNED: ๊ฒฐ๊ณผ ์ ์ฅ ํ h-0 ์งํDEVIATION/BETTER_ALTERNATIVE: AskUserQuestion์ผ๋ก ํ์ฌ spec ์ ๊ทผ๋ฒ vs ๋ฐ๊ฒฌ๋ ํ์ค/๋์ ๋๋ํ ์ ์ (์ฅ์ /๋จ์ /์ ํฉํ ์ํฉ ํฌํจ). "ํ์ฌ ๋ฐฉํฅ ์ ์ง" ๋๋ "ํ์ค/๋์์ผ๋ก spec ์์ " ์ ํ.DUPLICATE (CRITICAL): AskUserQuestion์ผ๋ก ๊ธฐ์กด ๊ตฌํ ์์นยท๋ฒ์ vs ์ ๊ท ๊ตฌํ ์๋ ์ ์. "๊ธฐ์กด ์ฌ์ฌ์ฉ์ผ๋ก spec ์์ " ๋๋ "์๋ก ๊ตฌํ(์ด์ ์์)" ์ ํ.{PROJECT_ROOT}/.gran-maestro/requests/REQ-NNN/discussion/req-impl-research.md
trigger: complex | new_library | post_ideation | low_confidence
impl_confidence: 0.0~1.0 # AUTO_APPROVE=true ์๋ง
codebase_findings:
- type: DUPLICATE | BETTER_ALTERNATIVE | NONE
detail: "..."
web_search:
- query: "..."
finding: "..."
result: ALIGNED | DEVIATION | DUPLICATE | BETTER_ALTERNATIVE
spec_changes: "๋ณ๊ฒฝ ์์ | {๋ณ๊ฒฝ ๋ด์ฉ ์์ฝ}"
1.7.5. ์ ๋์ ๊ฒํ silent review (์ง๋ฌธ ์์ฑ ๋จ๊ณ ์ง์ ):
Bash(python3 {PLUGIN_ROOT}/scripts/mst.py config get agile.adversarial_review)๋ก ์ค์ ์ ์ฝ๋๋ค.agile.adversarial_review.enabled != true์ด๋ฉด graceful skip ํ Step 1.8๋ก ์งํํ๋ค.edge, flow, integration์ด๋ฉฐ persona, nfr์ ์ค์ ์ด true์ธ ๊ฒฝ์ฐ์๋ง ์คํํ๋ค.python3 {PLUGIN_ROOT}/scripts/mst.py request review --req-path {path} --perspective {name} --json
Skill(skill:"mst:codex") ๋๋ Task(subagent_type:"general-purpose")์ด๋ค. ํ๋กฌํํธ์๋ request ์๋ฌธ, plan ์๋ฌธ, spec ์ด์, DoD/JTBD ์๋ฌธ์ ์ ๋ ํฌํจํ์ง ์๋๋ค.
์ญํ : {perspective} ๊ด์ ์ ์ ๋์ ๊ฒํ ์.
Read๋ก context_files ๊ฒฝ๋ก๋ฅผ ๋ก๋ํ๊ณ output_schema์ ๋ง๊ฒ findings JSON์ ๋ฐํํ์์ค.
{PROJECT_ROOT}/.gran-maestro/requests/REQ-NNN/adversarial-review-findings.md์ ๊ธฐ๋กํ๋ค.findings ๋ฐฐ์ด์ด ๋น์ด์์ OR current_round >= max_rounds์ด๋ค. max_rounds ๊ธฐ๋ณธ๊ฐ์ 3, current_round ์ด๊ธฐ๊ฐ์ 1์ด๋ค.AUTO_MODE=true: parallel_in_auto_mode=true์ด๋ฉด enabled perspective๋ฅผ ๋ณ๋ ฌ ์คํํ๊ณ , false์ด๋ฉด ์์ฐจ ์คํํ๋ค. severity=critical finding์ spec ์์ฑ ์ปจํ
์คํธ์ ์๋ ๋ฐ์ํ๊ณ {PROJECT_ROOT}/.gran-maestro/requests/REQ-NNN/auto-decisions.md์ ๊ธฐ๋กํ๋ค.AUTO_MODE=false: ๊ธฐ๋ณธ ์คํ์ edge + flow 2์ข
์ ์์ฐจ ์คํํ๋ค. critical severity finding๋ง ์ฌ์ฉ์์๊ฒ [์ ๋์ ๊ฒํ silent] N๊ฐ critical finding ๊ฐ์ง ํ์์ผ๋ก ๋
ธ์ถํ๊ณ , ๋ฐ์์ด ํ์ํ critical์ ๊ธฐ์กด ์ง๋ฌธ ์ฌ๋กฏ ์์ AskUserQuestion confirm์ ํตํฉํ๋ค. ๋ณ๋ ์ง๋ฌธ์ ์ถ๊ฐํด ์ฌ์ฉ์์๊ฒ ๋
ธ์ถ๋๋ ์ง๋ฌธ ์๋ฅผ ์ ๋ ์ฆ๊ฐ์ํค์ง ์๋๋ค.silent_review_findings ์ถ๊ฐ ์ปจํ
์คํธ๋ก ์ ๊ณตํ๋, ์ง๋ฌธ ์ ํ๋์ ๊ธฐ์กด ์ง๋ฌธ ์์ฑ ์์๋ฅผ ์ ์งํ๋ค.
1.8. ๊ตฌํ ์ธ๋ถ Q&A Pass (Step 1g ์๋ฃ ์งํ, Step h-0 ์ด์ ):AUTO_APPROVE=true๋ฉด ์ด ๋จ๊ณ ์ ์ฒด๋ฅผ ์์ skipํ๊ณ Step h-0์ผ๋ก ์ฆ์ ์งํAUTO_APPROVE=false๋ฉด ์๋ 7๊ฐ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ๊ณ ์ ์์๋ก ์์ฐจ ์ฒ๋ฆฌํ๋ค:
--plan PLN-NNN์ด ์๋ ๊ฒฝ์ฐ: ๊ฐ ์นดํ
๊ณ ๋ฆฌ๋ง๋ค plan.md์์ ๋์ ๊ฐ์ ๋จผ์ ํ์. ๋ช
ํํ ๋งคํ๋๋ฉด ์ง๋ฌธ ์๋ต "plan์์ ํ์ธ๋จ" ์์ฝ ์ถ๋ ฅ. ์๊ฑฐ๋ ๋ถํ์คํ๋ฉด AskUserQuestion ์คํ (์ถ์ ๊ธ์ง).--plan ์์ผ๋ฉด 7๊ฐ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ๋ชจ๋ AskUserQuestion์ผ๋ก ์ง๋ฌธํ๋ค.AskUserQuestion์ ๋์ 1๊ฐ๋ง; ๊ฐ ์ง๋ฌธ์ ๋ฐ๋์ "ํด๋น ์์" ์ ํ์ง ํฌํจ; ์ด 6๊ฐ ์ด๋ด ์ ํ์ง.test_enforcement ๋ก๋ (์์: config get test_enforcement โ templates/defaults/config.json โ ๊ธฐ๋ณธ๊ฐ: enabled=true, backend_tdd=true, web_execution_test=true, exempt_patterns=["*.md","*.json","*.yml","*.yaml","*.txt","*.css"], require_exemption_reason=true).
enabled=true์ด๋ฉด ํญ์ ์คํ; enabled=false์ด๋ฉด plan.md์ ## ํ
์คํธ ์ ๋ต ์น์
์กด์ฌ + ์ ์ฉ ์ฌ๋ถ: ์ ์ฉ์ธ ๊ฒฝ์ฐ์๋ง ์คํ.package.json์์ test runner ๊ฐ์ง; tsconfig.json, eslintrc*, prettier.config.* ์กด์ฌ ์ฌ๋ถ; playwright.config.*/cypress.config.* ์กด์ฌ ์ฌ๋ถ; test//__tests__/ ๋๋ ํ ๋ฆฌ; ๋น๋ ์คํฌ๋ฆฝํธ.| ํ๊ทธ | ํธ๋ฆฌ๊ฑฐ |
|---|---|
[build-check] | ๋น๋ ๋ช ๋ น ๊ฐ์ง |
[lint-check] | ๋ฆฐํฐ/ํฌ๋งทํฐ ์ค์ ๊ฐ์ง |
[unit-test] | ํ ์คํธ ๋ฌ๋ ๊ฐ์ง |
[integration]/[api-test] | DB ์ฐ๋ ๋๋ API ์๋ํฌ์ธํธ ๋ณ๊ฒฝ ์ ํธ |
[e2e-browser] | playwright/cypress ์ค์ ๊ฐ์ง |
[visual] | ๊ณ ๋น์ฉ ํ ์คํธ ์ ์ฑ ๋ชจ๋ ์ถฉ์กฑ ์๋ง |
[performance] | ๊ณ ๋น์ฉ ํ ์คํธ ์ ์ฑ ๋ชจ๋ ์ถฉ์กฑ ์๋ง |
AUTO_APPROVE=false โ AskUserQuestion์ผ๋ก ์ถ์ฒ ๋ชฉ๋ก ํ์ธ/์์ ; AUTO_APPROVE=true โ PM์ด ์์จ ํ์ .enabled=true ์):
exempt_patterns์ 100% ๋งค์นญ โ ํ
์คํธ ๋ฉด์ ์ ์ฉ + ์ฌ์ ๊ธฐ๋ก"ํ
์คํธ ํ๋ ์์ํฌ ๋ฏธ์กด์ฌ")require_exemption_reason=true์ด๋ฉด ๋ฉด์ ์ฌ์ ๋ฏธ๊ธฐ๋ก ์ ์๋ฌ[automatable]/[manual]/[browser-test] ๋ค์ ์ถ๊ฐ). ๊ฐ ์ ํ๋ณ ์์น์ ํด๋น ํ๊ทธ AC์๋ง 2~3์ค ์ ๋ณ ์ฃผ์
(์ ์ฒด ์ผ๊ด ์ฃผ์
๊ธ์ง).
backend_tdd=true + ๋ฐฑ์๋ ๋๋ฉ์ธ: ํต์ฌ ํ
์คํธ AC์ [tdd-required] + "ํ
์คํธ๋ฅผ ๋จผ์ ์์ฑํ ํ ๊ตฌํํ์ธ์ (TDD)" (MANDATORY)web_execution_test=true + ์น/ํ๋ก ํธ์๋: ์ต์ 1๊ฐ AC์ [e2e-browser]/[browser-test] + "์คํ ํ
์คํธ๋ฅผ ์์ฑํ๊ณ ํต๊ณผ์ํจ ํ ๊ตฌํ ์๋ฃ๋ก ํ๋จํ์ธ์" (MANDATORY)[build-check]: "๋น๋ ๋ช
๋ น์ด ์ฑ๊ณต(exit 0) ํ์ธ. ๋น๋ ์ค๋ฅ ์ ๊ตฌํ ์คํจ๋ก ๊ฐ์ฃผ."[lint-check]: "๋ฆฐํฐ/ํฌ๋งทํฐ ๊ท์น ์๋ฐ 0๊ฑด ํ์ธ. --fix ์๋ ์์ ํ์ฉ."[unit-test]: "AAA ํจํด(Arrange-Act-Assert) ์ค์. ๊ฐ ํ
์คํธ๋ ํ๋์ ๋์๋ง ๊ฒ์ฆ. ์ธ๋ถ ์์กด์ฑ๋ง mock."[integration]: "์ค์ DB/์๋น์ค ์ฌ์ฉ. ํ
์คํธ ์๋ฉ ํ์. ๊ฒฉ๋ฆฌ๋ ํ๊ฒฝ์์ ์คํ."[e2e-browser]: "Page Object ํจํด ๊ถ์ฅ. ์คํฌ๋ฆฐ์ท ์ฒจ๋ถ. ์์ ์ selector ์ฌ์ฉ."[api-test]: "Given-When-Then ๊ตฌ์กฐ. status + body + schema ๊ฒ์ฆ."[visual]: "๋ฒ ์ด์ค๋ผ์ธ ์ด๋ฏธ์ง ๋น๊ต. ์บก์ฒ ์กฐ๊ฑด(๋ทฐํฌํธ, ์ง์ฐ) ๋ช
์."[performance]: "์๋ฐ์
/๋ฐ๋ณต/์๊ณ๊ฐ ๋ช
์. ํ๊ท ๊ฐ์ง ๊ธฐ์ค ์ ์."enabled=true์์ test runner ์์ฒด ๋ฏธ๊ฐ์ง ์ "ํ
์คํธ ํ๋ ์์ํฌ ๋ฏธ์กด์ฌ" ๋ฉด์ ์ฌ์ ๋ฐ๋์ ๊ธฐ๋ก. ์ํฌํ๋ก์ฐ ์ฐจ๋จ ๊ธ์ง.
h-0. Stitch ํธ๋ฆฌ๊ฑฐ ๊ฐ์ง (config.stitch.enabled=true์ธ ๊ฒฝ์ฐ):mcp__stitch__* ๋๊ตฌ๋ฅผ ์ง์ ํธ์ถํ๋ ๊ฒ์ ์ ๋ ๊ธ์ง.
๋ฐ๋์ Skill(skill: "mst:stitch", args: "--req REQ-NNN {์์ฒญ ๋ด์ฉ}") ์คํฌ์ ํตํด์๋ง ํธ์ถํฉ๋๋ค.
โ Stitch ์๋ฃ ํ spec.md ์์ฑ ๊ณ์ui_related ํ๋๊ทธ: ๋ช
์์ ๋์์ธ ์์ฒญ ๋๋ ์ ํ๋ฉด/์ฝํ UI ์ ํธ โ true; ๊ทธ ์ธ โ false
h-0.5. Assigned Agent ๊ธฐ๋ณธ๊ฐ ๋ณด๊ด: spec.md ์์ฑ ์ง์ , Bash(python3 {PLUGIN_ROOT}/scripts/mst.py config get workflow.default_agent)๋ก ๊ธฐ๋ณธ๊ฐ ์ค์ . templates/spec.md์ Decision Tree๋ ์ด ๊ธฐ๋ณธ๊ฐ์ override ์กฐ๊ฑด์ผ๋ก๋ง ๋์. config ๋ฏธ์ฐธ์กฐ ์ claude-dev ์๋ ์ ํ์ ๊ธ์ง. config.resolved.json ์์ผ๋ฉด templates/defaults/config.json์ agent_assignments์ workflow.default_agent๋ฅผ fallback์ผ๋ก ํจ๊ป Readํ๋ค.
h-0.6. Intent Context Load (MANDATORY):{PROJECT_ROOT}/.gran-maestro/request-context.md๋ฅผ ๋ฐ๋์ Readํ๋ค. ํ์ผ์ด ์์ผ๋ฉด ์๋ ์ด๊ธฐ ํ
ํ๋ฆฟ์ผ๋ก ์์ฑ ํ ์ฆ์ Readํ๋ค (๋น์ฐจ๋จ):
# Request Q&A ์ ํธ ํจํด
_๋ง์ง๋ง ๊ฐฑ์ : ์์ (์ด๊ธฐ ์ํ)_
_์ธ์
์: 0_
_schema_version: 1_
> ์์ง ์ ํธ ํจํด์ด ๊ธฐ๋ก๋์ง ์์์ต๋๋ค. ์ถฉ๋ถํ Q&A ์ธ์
ํ ์๋์ผ๋ก ์ฑ์์ง๋๋ค.
## ์ ํธ ํจํด (Preference Table)
| id | domain | type | statement | weight | freq | last_seen | tags |
|----|--------|------|-----------|--------|------|-----------|------|
## Prompt Hints
(ํจํด ์ถ์ ํ ์๋ ์์ฑ๋ฉ๋๋ค)
request-context.md์์ ํ์ฌ ์์ฒญ๊ณผ ๊ด๋ จ๋ ํจํด ์ต๋ 3๊ฐ๋ฅผ request_preference_hints๋ก ์ถ์ถํ๋ค.AskUserQuestion์์ ๊ณผ๊ฑฐ ์ ํธ๋ฅผ description์ ๋ช
์์ ์ผ๋ก ์ธ์ฉํ๋ค (๊ถ์ฅ ํ์: ์ด์ ์ "{statement}"๋ฅผ ์ ํธํ์
จ์ต๋๋ค. ์ด๋ฒ์๋ ๋์ผํ๊ฒ ์ ์ฉํ ๊น์?). freq ์ซ์ ์ง์ ์ธ์ฉ ๊ธ์ง.request_disputed_preferences์ ์์งํ๊ณ Step 6์ ์์ฝ ํธ๋ฆฌ๊ฑฐ ์
๋ ฅ์ผ๋ก ์ ๋ฌํ๋ค.intent_fidelity.enabled ํ์ธ: config get intent_fidelity.enabled โ templates/defaults/config.json โ ๊ธฐ๋ณธ๊ฐ true. false์ด๋ฉด ์ด ๋จ๊ณ ์ ์ฒด skip.--plan PLN-NNN ์ ๊ณต ์:
## ์์ฒญ (Refined) + ## Intent (JTBD) + ## ์ธ์ ๊ธฐ์ค ์ด์์ Readํ์ฌ intent_context๋ก ๋ณด๊ดํ๋ค.{PROJECT_ROOT}/.gran-maestro/plans/PLN-NNN/plan.ids.json์ Readํ์ฌ PAC preflight ์ํ (๋น์ฐจ๋จ):
[{id,text,grade,tags?}] ๋ชฉ๋ก์ pac_preflight_checklist๋ก ๋ก๋. tags์์ TIER-A/TIER-B ์ธ์ํด pac_tier ๊ฒฐ์ (๋ ๋ค ์์ผ๋ฉด TIER-B ๊ธฐ๋ณธ).## ์ธ์ ๊ธฐ์ค ์ด์์์ ์์ PAC ๋ชฉ๋ก ์ถ๋ก (๋น์ฐจ๋จ).pac_anchor_list๋ก ๋ณด๊ดํ๊ณ ์ดํ spec AC ์์ฑ ํ๋กฌํํธ ์๋จ์ ๊ณ ์ ์ฃผ์
.## ์ฐ๊ด ์ปจํ
์คํธ ํ ๋ฐ ๋ณธ๋ฌธ ๋ด docs/ ๊ฒฝ๋ก ์์ง.--plan ๋ฏธ์ ๊ณต ์: request.json.original_request๋ฅผ intent_context๋ก ๋ณด๊ด. docs ํ๋ณด๋ Step 1c ํ์ ๋ฐ๊ฒฌ docs/ ํ์ผ + ์ฌ์ฉ์ ์์ฒญ ๋ช
์ ๊ฒฝ๋ก๋ง ์ฌ์ฉ.docs_context ๊ตฌ์ฑ: dedupe ํ ์กด์ฌํ๋ ํ์ผ๋ง Read. ๊ฐ ํญ๋ชฉ path, last_modified, ํต์ฌ ์๊ตฌ์ฌํญ 1~3์ค ์์ฝ ์ถ์ถ.--plan + intent ์น์
ํ๋ณด ๋๋ docs_context 1๊ฐ ์ด์ โ intent_context_active=true; ๊ทธ ์ธ โ false
h. Implementation Spec ์์ฑ (templates/spec.md ํ
ํ๋ฆฟ ์ฌ์ฉ); --plan ์์ผ๋ฉด ## ๊ฐ์ ์ฌํญ ์น์
ํฌํจโ ๏ธ spec.md ์์ฑ ์์น: ํฌํจ = AC(์๋ฃ ๊ธฐ์ค), ๋ฒ์ ๊ฒฝ๊ณ, ์ ์ฝ ์กฐ๊ฑด, ํจํด ํํธ, ์์์ 1~3๊ฐ, ์์กด์ฑ. ์ ์ธ = ์์ ํ์ผ exhaustive ๋ชฉ๋ก, ๋จ๊ณ๋ณ ๊ตฌํ ์ ์ฐจ, ์์ง์ผ์ด์ค ์ฌ์ ์ด๊ฑฐ. ๊ตฌ์ฒด์ ์ธ ๊ตฌํ ๋ฐฉ๋ฒ์ ์์ด์ ํธ๊ฐ worktree๋ฅผ ์ง์ ํ์ํ๋ฉฐ ๊ฒฐ์ ํ๋ค.
ui_related=true + {PROJECT_ROOT}/.gran-maestro/designs/DESIGN.md ํ์ผ ์กด์ฌdesign_system_context ๋ณด๊ด โ context_manifest_files์ dedupe ์ถ๊ฐ (spec.md ยง0 ๋๋ ## ๋์์ธ ์์คํ
์ฐธ์กฐ ์น์
์ ๊ฒฝ๋ก ๊ธฐ๋ก){{IMPL_CONTEXT}}์ "DESIGN.md๋ฅผ ๋ฐ๋์ Readํ์ฌ ๋์์ธ ๊ท์น์ ์ค์ํ์ธ์." ์ฃผ์
[automatable], [manual], [browser-test] 3๊ฐ์ง ํ์ฉ. browser-test๋ ์ค์ ๋ธ๋ผ์ฐ์ ์ํธ์์ฉ ๊ฒ์ฆ ํ์ AC์ ์ฌ์ฉ. plan ๋๋ ๋ํ์ ๋ธ๋ผ์ฐ์ ํ
์คํธ/Playwright/Claude in Chrome ๋ฑ ์ ํธ ์์ผ๋ฉด [browser-test]๋ก ๋ถ๋ฅ. web_execution_test=true + ์น/ํ๋ก ํธ์๋ ํ์คํฌ์ด๋ฉด ์ ํธ ์ฝํด๋ ์ต์ 1๊ฐ ํฌํจ. [browser-test] AC๋ Given/When/Then/Test ํ์ ์ ์ง. Test:์๋ ์คํ ๋์ URL/ํ๋ฉด + ํต์ฌ ๋์ + ๊ธฐ๋ ๊ฒฐ๊ณผ ํฌํจ.pac_tier="TIER-A"์ธ PAC์ ๋งคํ๋ Spec AC ํค๋์ [tdd-required] ์๋ ๋ถ์ฌbackend_tdd=true + ๋ฐฑ์๋ ๋๋ฉ์ธ ํ์คํฌ์ด๋ฉด ํต์ฌ ํ
์คํธ AC์๋ ์๋ ๋ถ์ฌ (PAC tier ๋ฌด๊ด)TIER-B PAC๋ ์๋ ๋ถ์ฌ ์ ํจ; ๊ธฐ์กด ๋ณด์กฐ ํ๊ทธ์ ๋
๋ฆฝ ๊ณต์กดํ๋ฉฐ ๊ธฐ์กด ๋ผ์ฐํ
๋ณ๊ฒฝ ์์[IMPACT] ํ๊ทธ ๋๋ plan.ids.json ํญ๋ชฉ tags์ "IMPACT" ํฌํจ ์, ๋ณํ๋ Spec AC ํค๋์ [impact-check] ๋ณด์กฐ ํ๊ทธ๋ฅผ ๋ฐ๋์ ํฌํจ. ๋ณต์ ๋ณด์กฐ ํ๊ทธ ๊ณต์กด ์ [impact-check]๊ฐ ์ต์ฐ์ ๋ผ์ฐํ
. [IMPACT] PAC ์๋ ๊ฒฝ์ฐ graceful skip (ํ์ ํธํ).TIER-B โ TIER-A ์ํฅ ๊ฐ๋ฅ (์ํฅ ์ [tdd-required] ์ถ๊ฐ ์ ์ฉ). TIER-A โ TIER-B ํํฅ ์์ค์ปฌ๋ ์ด์
์ ๋ช
์์ ์ผ๋ก ๊ธ์ง.--plan ์ ๊ณต ์, MANDATORY):
pac_preflight_checklist์ ๊ฐ PAC๋ฅผ spec AC์ ์ต์ 1ํ ๋งคํ## 3.3 PAC Mapping ์น์
: PAC ID | Grade(MUST/SHOULD) | Mapped Spec AC IDs | CoverageMapped Spec AC IDs๋ ๋น์์๋ ์ ๋จ; plan์ ์๋ ์ ๊ท spec AC๋ Coverage๋ฅผ SPEC_ONLY๋ก ํ์## ยง0 Context Manifest ์๋ ์ฑ์ ๊ท์น (MANDATORY):
context_manifest_files๋ฅผ bullet ๋ชฉ๋ก์ผ๋ก ์ฝ์
objective_context.objective_md_path๊ฐ ์์ผ๋ฉด dedupe ์ถ๊ฐ; ์์ผ๋ฉด skip (graceful)--plan ์๋ ๊ฒฝ์ฐ์๋ ๋์ผ ๊ท์น ์ ์ฉ (Step 1c ํ์ + ์์ฒญ ๋ถ์ ๊ธฐ๋ฐ)## 3.2 Intent Trace ์์ฑ ๊ท์น (MANDATORY): intent_context_active=true์ผ ๋๋ง ยง3.2 ์ฑ์. ๊ฐ AC๋ง๋ค ์ต์ 1๊ฐ ์๋ ๊ทผ๊ฑฐ ์ฐ๊ฒฐ. ๊ทผ๊ฑฐ๋ฅผ ์ฐพ์ง ๋ชปํ๋ฉด [INTENT-GAP] ํ๊ธฐ. docs ๊ทผ๊ฑฐ ์ฌ์ฉ ์ intent_snapshot์ doc_path, last_modified, spec_generated_at ๊ธฐ๋ก. intent_context_active=false๋ฉด ยง3.2 ์ ์ฒด skip.## 3.4 Epic DoD Mapping ์์ฑ ๊ท์น (์กฐ๊ฑด๋ถ, MANDATORY): objective_context ์กด์ฌ + project_dod_items[] 1๊ฐ ์ด์์ผ ๋๋ง ์์ฑ. ํ ํ์: DoD ID(or ํญ๋ชฉ) | DoD ์ค๋ช
| Mapped Spec AC IDs | Coverage. ๋งคํ ๊ฐ๋ฅํ AC ์์ผ๋ฉด [UNMAPPED]/Gap. objective_context ์๊ฑฐ๋ ๋น์ด์์ผ๋ฉด ยง3.4 ์ ์ฒด skip (graceful fallback).
h-0.7. Regression Test ์ ํ ํ์คํฌ ์์ฑ (Step h-1 ์ด์ , MANDATORY):"๊ธฐ์กด ๊ธฐ๋ฅ regression test ์์ฑ" ์ฑ๊ฒฉ์ ์ ํ ํ์คํฌ ์์ฑblockedBy ์ ํ ์กฐ๊ฑด์ผ๋ก ์ฐ๊ฒฐ[regression-test] ๋ณด์กฐ ํ๊ทธ ํฌํจ. [impact-check] ์ฐ์ ๋ผ์ฐํ
์ ์ ์งํ๊ณ [regression-test]๋ Pass A์ ํ๊ท ํ
์คํธ ์คํ ๋์์ผ๋ก ๋ณด์กด.[impact-check] = ์์ ์ํฅ์ผ๋ก ๋ค๋ฅธ ์์ญ์ด ๊นจ์ง์ง ์์๋์ง. [regression-test] = ์์ ๋์ ์์ฒด์ ๊ธฐ์กด ๋์์ด ์ ์ง๋๋์ง. ๋์ผ ํ์ผ ๋์์ด๋ผ๋ ๊ฒ์ฆ ๋ชฉ์ ์ด ๋ค๋ฅด๋ฏ๋ก ์ค๋ณต์ด ์๋๋ค.h-1. ๋ค์ค ํ์คํฌ ๋ถํด ์ฒ๋ฆฌ (PM ์์จ ํ๋จ โ plan ์ ๋ฌด์ ๋ฌด๊ด):
## ํ์คํฌ ๋ถํด ์น์
์ด ์๋๋ผ๋ ๋ฌด์ํ๋ค. ์ฝ๋๋ฒ ์ด์ค ํ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ์ผ๋ก PM์ด ๋
์์ ์ผ๋ก ๊ฒฐ์ ํ๋ค.ํ์คํฌ ๋ถํด ๊ธฐ์ค (์ฐ์ ์์ ์):
blockedBy/blocks๋ก ์์ ๋ช
์. ๋จ์ "๋์ค์ ํ๋ฉด ์ข๊ฒ ๋ค" ์์ค์ ๋์์ด ์๋..ts + .md)๋ ๋ถ๋ฆฌ์ ํ์์ถฉ๋ถ์กฐ๊ฑด์ด ์๋.blockedBy ์ค์ : ๋ฐฑ์๋ API ์๋ฃ ํ ์ฐ๋ ํ์ํ๋ฉด blockedBy: [๋ฐฑ์๋T], UI ์คํ์ผ๋ง ๋
๋ฆฝ ๊ฐ๋ฐ ๊ฐ๋ฅํ๋ฉด ๋ณ๋ ฌ ํ์ฉ.์คํ
0.5 (์ ํ): ์ฑ
์ ๊ฒน์นจ ๋ฐฉ์ง ๊ฒ์ฆ: ๋ถํด๋ ๊ฐ ํ์คํฌ์ ๊ธฐ๋ฅ ์ฑ
์์ ํ ์ค์ฉ ์ด๊ฑฐ. ์์ ๋์ผ์ด๋ฉด ๋ณํฉ, ์ ํ ๊ด๊ณ์ด๋ฉด blockedBy๋ก ์ง๋ ฌํ. ๊ฒน์นจ ์์ด ๊ฒ์ฆ ํต๊ณผ ํ์๋ง ์คํ
0์ผ๋ก ์งํ.
์คํ
0 (์ ํ): ์์กด์ฑ ๋ฐ ๋ฐฐ์ ํ์ : ๋ชจ๋ ํ์คํฌ ID, blockedBy/blocks, ์์ด์ ํธ ๋ฐฐ์ ์ ๋จ์ผ thinking์์ ํ์ (์ดํ Write ๋๋ ์๋ธ์์ด์ ํธ ์ด๋ ๊ฒฝ๋ก๋ ๋ถ๋ณ ์
๋ ฅ์ผ๋ก ์ฌ์ฉ).
์คํ 1 (๋ถ๊ธฐ): ๋ ๋ฆฝ ํ์คํฌ ์ ํ๋จ
blocks/blockedBy ์๋ ๊ฒ) ์ ๊ณ์ฐ:
Task(subagent_type: "general-purpose", run_in_background: true) N๊ฐ ๋ณ๋ ฌ dispatch. ๊ฐ ์๋ธ์์ด์ ํธ์ ์์กด์ฑ ํ
์ด๋ธ + ์์ด์ ํธ ๋ฐฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ์ฃผ์
(ํ๋กฌํํธ์ ํฌํจ): ์๋ธ์์ด์ ํธ๋ ยง7, ยง8์ ๊ทธ๋๋ก ๊ธฐ์
, ์์กด์ฑ/๋ฐฐ์ ๊ฒฐ์ ๊ธ์ง. Phase B๋ก spec ์์ฑ ์ ๋ณ๊ฐ๋ก PM์ด prereview ์์ด์ ํธ dispatch. PM ์ฌ๋๋ง์ผ๋ก Phase A ๋ฏธ์คํ์ ๊ธ์ง.h-1.5 ํ ์คํธ ํ์คํฌ ์๋ ์์ฑ (์คํ 1 ์งํ, MANDATORY):
ยง5 blockedBy์ ๋ชจ๋ ๊ตฌํ ํ์คํฌ ID ๊ธฐ์
. ๊ฐ ๊ตฌํ ํ์คํฌ ยง5 blocks์ ํ
์คํธ ํ์คํฌ ID ์ถ๊ฐ (dedupe).templates/test-spec.md ํ
ํ๋ฆฟ ์ฌ์ฉ. ยง2 ํ
์คํธ ๋ฒ์์ ํตํฉ ๊ฒ์ฆ+์ฆ๋ถ ํ
์คํธ+ํ๊ท ํ
์คํธ ๋ช
์. ยง3 ํตํฉ AC๋ ๊ตฌํ ํ์คํฌ ํต์ฌ AC๋ฅผ Given/When/Then+Test ํ์์ผ๋ก ์ข
ํฉ ์ฌ๊ตฌ์ฑ. ยง4 ํ๊ท ํ
์คํธ ํญ๋ชฉ ์ต์ 1๊ฐ ์ด์.request.json.tasks[]์ ํ
์คํธ ํ์คํฌ๋ ์ผ๋ฐ ํ์คํฌ์ ๋์ผํ๊ฒ ๋ฉํ๋ฐ์ดํฐ ๊ธฐ๋ก.์คํ 2 (๊ฒ์ฆ): ์๋ฐฉํฅ ์์กด์ฑ ๊ฒ์ฆ ํ (๋ชจ๋ spec.md Write ์๋ฃ ์งํ ์คํ)
blocks ๋ชฉ๋ก์ ์ฝ์ด ๋์ ํ์คํฌ spec์ blockedBy ํฌํจ ์ฌ๋ถ ํ์ธ; ์ญ๋ฐฉํฅ๋ ๋์ผํ๊ฒ ๊ฒ์ฆ.request.json tasks ๋ฐฐ์ด ์
๋ฐ์ดํธ ์ฐจ๋จ (spec.md๋ ์ ์ง, PM์ด ์๋ ์์ ํ ์ฌ์๋).h-1.5์์ ์ถ๊ฐํ ๊ตฌํ ํ์คํฌ blocks โ ํ
์คํธ ํ์คํฌ blockedBy ๊ด๊ณ๋ ๋์ผ ๊ท์น์ผ๋ก ๊ฒ์ฆ.i. ํ์คํฌ ๋๋ ํ ๋ฆฌ ์ผ๊ด ์์ฑ: {PROJECT_ROOT}/.gran-maestro/requests/REQ-NNN/tasks/01..N (N๊ฐ ๋์ ์์ฑ)
j. spec.md ๋ณ๋ ฌ Write: (์์กด์ฑ ๊ณ ์ ํ) ๋จ์ผ ์๋ต ๋ด N๊ฐ Write ๋์ ํธ์ถ๋ก ์ ์ฅ
h-2. Spec Pre-review Pass (๋ชจ๋ spec.md Write ์๋ฃ + ๊ฒ์ฆ ํ
ํต๊ณผ ํ ์คํ)
์คํ ์กฐ๊ฑด (์์๋๋ก): --prereview โ ๊ฐ์ ์คํ; --auto/-a ๋๋ AUTO_APPROVE=true โ skip; --no-prereview โ skip; workflow.spec_prereview=false โ skip; ๋ชจ๋ ํต๊ณผ ์ ์คํ.
์์ค์ปฌ๋ ์ด์
๋ชจ๋: AUTO_APPROVE=true๋ฉด "pm-self", ๊ทธ ์ธ๋ "user"
config ์ฝ๊ธฐ: max_iterations = config.workflow.spec_prereview_max_iterations (๊ธฐ๋ณธ 3); escalation_trigger = config.workflow.spec_prereview_escalation_trigger (๊ธฐ๋ณธ "major"); minor_escalation_threshold = config.workflow.spec_prereview_minor_escalation_threshold (๊ธฐ๋ณธ null โ ๋นํ์ฑ); current_iteration = 1
[PREREVIEW LOOP]
prereview-prompt.md N๊ฐ ๋์ Write: ๊ฐ tasks/NN/prereview-prompt.md๋ฅผ templates/spec-prereview-prompt.md + ๋ณ์ ์นํ์ผ๋ก ์์ฑ
์์ด์ ํธ ๋ณ๋ ฌ dispatch: claude-dev 2๊ฐ+ ํ์คํฌ โ Task(run_in_background: true) ์ง์ ํธ์ถ; codex-dev/gemini-dev โ Skill(skill: "mst:{agent}", run_in_background: true)
๊ฒฐ๊ณผ ์์ง: ํ์คํฌ๋ณ ๊ฒฐ๊ณผ๋ฅผ CRITICAL/MAJOR/MINOR๋ก ๋ถ๋ฅ. NO_ISSUES โ ์ด์ ์์. ์คํจ ์๋ต โ "[Pre-review skip]" ํ ๊ฑด๋๋.
escalate ํ๋จ: "critical" โ CRITICAL 1๊ฐ ์ด์; "major" โ CRITICAL/MAJOR 1๊ฐ ์ด์; "minor" โ ์ด์ 1๊ฐ ์ด์
[escalate = true]:
current_iteration < max_iterations โ ๋ฃจํ ์ฌ์ง์
; >= max_iterations โ ๋ฃจํ ์ข
๋ฃ.current_iteration < max_iterations โ ๋ฃจํ ์ฌ์ง์
; >= max_iterations โ ๋ฃจํ ์ข
๋ฃ.[escalate = false] (escalation_trigger ๋ฏธ๋ง ์ด์๋ง ์กด์ฌ ๋๋ ์ ์ฒด NO_ISSUES):
MINOR ์๊ณ๊ฐ ์์ค์ปฌ๋ ์ด์
์ฒดํฌ (minor_escalation_threshold != null์ธ ๊ฒฝ์ฐ):
[PREREVIEW LOOP ์ข ๋ฃ]
์ด์๊ฐ 1๊ฐ ์ด์ ์กด์ฌํ๋ ๊ฒฝ์ฐ spec.md ๋์ ## ๊ตฌํ ์ ๊ฒํ (Pre-review Q&A) ํ
์ด๋ธ ์ถ๊ฐ (์ต์ข
iteration์ ์ด์ ๋ฐ ๋ฐ์ ๊ฒฐ๊ณผ ๊ธฐ์ค).
k. request.json์ tasks ๋ฐฐ์ด์ ํ์คํฌ ๋ฉํ๋ฐ์ดํฐ ์ถ๊ฐ (spec.md ์ ์ฅ ์งํ): id, title, status: "pending", agent(ํ์ โ ๋๋ฝ ๊ธ์ง), spec: "tasks/01/spec.md", covers_ac: ["AC-001", ...]
h-exit. A Gate (PAC Coverage, blocking):
--plan PLN-NNN ์ ๊ณต && pac_trace.enabled == trueMapped Spec AC IDs๊ฐ 0๊ฑด์ธ ํญ๋ชฉ์ด 1๊ฐ๋ผ๋ ์์ผ๋ฉด blocking. SHOULD PAC ๋๋ฝ์ warning (blocking ์๋).pac-missing-report.md ์์ฑ โข ๋๋ฝ MUST PAC ID ๋ชฉ๋ก + ๋ณด์ ๊ฐ์ด๋ ์ถ๋ ฅ โฃ MUST ์ปค๋ฒ๋ฆฌ์ง 100% ๋ฌ์ฑ ํ gate ํด์ SPEC_ONLY)๋ scope creep warning ๊ธฐ๋ก (pac_trace.scope_creep_warning=true ์).
l. (A Gate ํต๊ณผ ํ) request.json์ status๋ฅผ "spec_ready"๋ก ์
๋ฐ์ดํธโ ๏ธ spec.md ์์ฑ ์๋ฃ ํ์ธ โ spec.md ๋ฏธ์กด์ฌ ์ ์คํฌ ์ข ๋ฃ ๊ธ์ง
์คํ ์์ฝ ํ์ + ์น์ธ ์๋ด (๋ ๊ฐ์ง ๋ช ๋ น์ ๋ชจ๋ ๋ช ์):
/mst:approve REQ-NNN/mst:approve -a REQ-NNN (์ดํ ๋จ๊ณ๋ฅผ ์ค๊ฐ ์น์ธ ์์ด ์๋ ์คํ)[ํ ๋น ์์ ] REQ-NNN โ {agent๋ช
} ({provider}) ํ์์ผ๋ก ๋ช
์ (๋ค์ค REQ ์ ๊ฐ๋ณ ๋ช
์)/mst:approve ์์ ์ ๊น์ง: ์ฝ๋ ์์ ยทํ์ผ ํธ์งยท์ปค๋ฐ ์ ๋ฉด ๊ธ์งrun_in_background: true):
{PROJECT_ROOT}/.gran-maestro/qa-raw/REQ-NNN.jsonl{PROJECT_ROOT}/.gran-maestro/request-context.mdweight=HIGH ์๋ ๋ถ์ฌ โข request_disputed_preferences์ [DISPUTED] ํ๊ทธ ๋ฐ์ โฃ Prompt Hints๋ Table ๊ธฐ๋ฐ ํ์์ผ๋ก ์ฌ์์ฑ, ๋น๋ ์ซ์ ์ง์ ์ธ์ฉ ๊ธ์ง โค 200์ค ์ด๊ณผ ์ 150์ค๋ก ์์ถ ([DISPUTED] ์ฐ์ ์ ๊ฑฐ, HIGH ๋ณดํธ)Task(subagent_type: "general-purpose", run_in_background: true, prompt: "{REQ-NNN QA ์์ฝ ํ๋กฌํํธ}")auto_approve=true ์ํ์์๋ ์น์ธ ๋จ๊ณ๋ฅผ ์คํตํ๊ณ Skill(skill: "mst:approve", args: "-a REQ-NNN")๋ก ์๋ ์ง์
ํ๋ค (-a ์๋ต ๊ธ์ง)/mst:approve -a REQ-NNN์ผ๋ก ์๋ ์ง์
ํ๋ค[MST skill={name} step={N}/{M} return_to={parent_skill/step | null}]skill: ํ์ฌ ์คํ ์ค์ธ ์คํฌ ์ด๋ฆstep: ํ์ฌ ๋จ๊ณ(N/M) ๋๋ ์๋ธ์คํฌ ์ข
๋ฃ ์ returnedreturn_to: ์ต์์ ์คํฌ์ด๋ฉด null, ์๋ธ์คํฌ์ด๋ฉด {parent_skill}/{step_number}[MST skill={subskill} step=returned return_to={parent/step}][MST skill={name} step=1/3 return_to=null][MST skill={subskill} step=returned return_to={parent_skill}/{step_number}]--auto / -a: ์คํ ์๋ ์น์ธ ๋ชจ๋ (์ฌ์ฉ์ ์น์ธ ๋จ๊ณ ์คํต, auto_approve: true)
/mst:request --auto "์์ฒญ") ๋๋ ๋ค(/mst:request "์์ฒญ" --auto) ๋ชจ๋ ํ์ฉ--auto/-a ๋๋ config.auto_mode.request=true(AUTO_APPROVE=true)์์๋ Spec Pre-review Pass(h-2)๋ฅผ skipํ๋ค--resume REQ-NNN: ๊ธฐ์กด REQ ์ฌ๊ฐ ๋ชจ๋ (์ ๊ท REQ ์์ฑ ๊ธ์ง)
Skill(skill: "mst:request", args: "--plan PLN-NNN --resume REQ-NNN -a")--resume ์์ด REQ-NNN ๋จ๋
์ธ์๋ ์ฌ๊ฐ๋ก ํด์--prereview: config ์ค์ ๋ฌด๊ดํ๊ฒ Pre-review Pass ๊ฐ์ ์คํ--no-prereview: config ์ค์ ๋ฌด๊ดํ๊ฒ Pre-review Pass skip/mst:request "JWT ๊ธฐ๋ฐ ์ฌ์ฉ์ ์ธ์ฆ ๊ธฐ๋ฅ์ ์ถ๊ฐํด์ค"
/mst:request --auto "๋ก๊ทธ์ธ ๋ฒํผ ์์์ ํ๋์์ผ๋ก ๋ณ๊ฒฝ"
/mst:request --plan PLN-233 --resume REQ-352 -a
.gran-maestro/ ์์ฑ ์คํจ โ git ์ ์ฅ์ ์ฌ๋ถ ๋ฐ ์ฐ๊ธฐ ๊ถํ ํ์ธmode.json ์ ๊ธ ์ถฉ๋ โ mode.json.lock ์๋ ์ญ์