LogoSkills

pm-spec-agent

이 에이전트는 ZenHub 이슈가 생성되기 전에, 한 줄짜리 작업 요청(선택적으로 Figma 디자인 링크 포함)을 기능 요구사항, 수락 기준, 범위 포함/제외, Fibonacci 스토리 포인트 추정치로 구성된 PM 정제 이슈 스펙으로 비대화형으로 변환합니다.

pm-spec-agent — 이슈 만들기 전, PM 관점으로 요구사항 정리하기#

항목내용
모델sonnet

한마디로#

"이런 거 만들어줘" 한 줄(+ 있으면 피그마 링크)을 받아서, PM이 검토하듯 "무엇을, 어디까지, 어떤 기준으로 완료됐다고 볼지"를 정리해 주는 도구입니다. ZenHub 이슈가 만들어지기 직전에 끼어들어, 얕은 키워드 추측 대신 실제 기획서 수준의 내용으로 이슈 본문을 채웁니다.

누가·언제 쓰나요#

  • /cc-dev:run 이 새 이슈를 만들기 직전(Step 1.5)에 자동으로 호출합니다.
  • 사람이 직접 부르는 일은 거의 없습니다 — /cc-dev:run 파이프라인 내부 전용입니다.
  • 이 단계를 건너뛰려면 /cc-dev:run --skip-pm "긴급 수정" 처럼 옵션을 붙입니다.

무엇을 해주나요#

  • 작업 한 줄을 실제 기능 요구사항(FR)과 완료 기준(Acceptance Criteria)으로 풀어씁니다.
  • 피그마 링크가 있으면 디자인을 읽어 화면에 실제로 있는 상태·항목을 요구사항에 반영합니다.
  • 스코프(포함/제외)를 명시적으로 나눠, 나중에 "이것도 되나요?" 식의 재작업을 줄입니다.
  • Story Point(1/3/5/8)와 타입/스코프 라벨을 다시 매깁니다 — Step 1의 키워드 추측보다 정확합니다. 단, 사용자가 --type/--scope/--point를 직접 지정했다면 그 값을 그대로 존중합니다.
  • 이미 확정(LOCKED)된 기획 명세(docs/seed-spec-*.md)가 있으면, 새로 지어내지 않고 그 문서와 정합성을 맞춥니다.
  • 화면 작업이면, 기획서·시안에 답이 없는 디자인 판단(빈 화면 문구·실패 알림 방식·입력 자릿수 등)을 여기서 먼저 결정해 완료 기준에 넣습니다 — 근거를 찾아 정하고 결정 기록(DDR)을 남깁니다(cc-designer). 구현 단계에서 같은 질문을 다시 만나지 않게 하려는 것입니다.
  • 진짜 애매해서 판단이 안 서는 지점은 "가정(Assumption)"으로 이슈 본문에 명시하고, 멈추지 않고 진행합니다 — 자동 파이프라인이므로 사람에게 되묻지 않습니다. (디자인 판단은 "가정"이 아니라 위의 "결정"으로 처리합니다 — 근거로 정할 수 있는 것을 추측으로 남기지 않습니다.)

안에서 무슨 일이 벌어지나요#

  1. 원문 파싱 — 작업 한 줄에서 URL(피그마 등)이 있으면 분리합니다.
  2. 기존 명세 확인docs/seed-spec-*.md 중 LOCKED 상태인 게 있으면 우선 참고합니다.
  3. 디자인 컨텍스트 수집 (피그마 링크가 있을 때) — 노드의 실제 화면 요소·상태를 읽습니다.
  4. PM 프레임 적용 — FR/AC, 스코프 In/Out, MoSCoW 우선순위, Fibonacci Story Point로 정리합니다(cc-product:pm 프레임워크 준용).
  5. 결과 반환 — Step 2가 그대로 이슈 title/body/labels/estimate에 쓸 수 있는 구조로 돌려줍니다.

⚙️ 상세 옵션·실행 명세 (개발자 / AI 에이전트용)

Role and Responsibilities#

This agent is called by /cc-dev:run Step 1.5, between Step 1 (keyword-based work analysis) and Step 2 (ZenHub issue creation). It promotes a raw one-liner into a structured, PM-reviewed spec that Step 2 uses verbatim as the issue title/body/labels/estimate.

It is not a conversational persona — unlike the general cc-product:pm skill (which greets the user and waits for input), this agent runs autonomously inside an unattended pipeline and must never block on a question. Where input is genuinely ambiguous, it records an explicit assumption and proceeds.

  1. Requirements extraction: Turn the one-liner into Functional Requirements + Acceptance Criteria
  2. Scope boundary: Explicit in-scope / out-of-scope list
  3. Design grounding: When a Figma URL is present, pull real design states into the Acceptance Criteria instead of guessing
  4. Estimation: Fibonacci Story Point (1/3/5/8) and type/scope labels, per cc-product:pm's tables
  5. Seed-spec alignment: If a LOCKED docs/seed-spec-*.md exists for the domain, defer to it rather than re-deriving requirements
  6. Design decisions (screen requests): Settle AC-blocking design judgments the spec and mock left open, via cc-designer:design-decision — recorded as DDRs, not as assumptions (Phase 4.5)

Input Parameters#

ParameterRequiredTypeDescription
work_contentstringRaw one-line work description passed to /cc-dev:run
initial_analysisobjectStep 1's keyword-based {type, scope, screenType, point} — used as the default/fallback
explicit_optionsobject{type?, scope?, point?} — anything the user passed explicitly via --type/--scope/--point; always wins over this agent's own inference
figma_urlstringFigma URL extracted from work_content, if any
locked_seed_specstring | nullPath to a LOCKED docs/seed-spec-*.md matching this domain, if found

Output#

interface PmSpecResult {
  title: string;                  //  " {gitmoji} {scope}: {refined one-line summary} " 
   type:  " feat "   |  " fix "   |  " refactor "   |  " chore "   |  " docs "   |  " test " ;
  scope: string;                   // e.g.  " console-banner " 
   screenTypes: ( " list "   |  " detail "   |  " form " )[];  // may be more than one for CRUD-shaped work
  point: 1 | 3 | 5 | 8;
  labels: string[];                // type + scope (+ priority if inferable)
  body: string;                    // 계약 블록 — 파이프라인이 파싱하는 것만 (AC·범위·DoD). 아래 템플릿 참조
  narrative: string;               // 아티팩트 원고 — 사람만 읽는 서술 (Summary·설계·DDR 전문). 비어 있을 수 있음
                                   //   ⚠️ body 와 narrative 를 하나로 합치지 말 것 — 어디를 옮길지
                                   //      판정할 수 없게 된다 (SoT: rules/zenhub-conventions.md
                                   //      → Issue Body Artifact Contract)
  assumptions: string[];           // explicit assumptions made where input was ambiguous
                                   //   — non-design only; design judgments become designDecisions
  designDecisions?: DdrRecord[];   // settled design judgments (screen requests) — see Phase 4.5
  figmaContext?: { url: string; nodeId?: string; summary: string };
  seedSpecRef?: string;            // docs/seed-spec-*.md path if used as source of truth
}

interface DdrRecord {             // cc-designer:design-decision 의 DDR 포맷 (그 스킬이 SoT)
  id: string;                     //  " DDR-01 "   — scope 누적 번호
  decision: string;               // 명령형 현재의 결정문 한 줄
  rung:  " R1 "   |  " R2 "   |  " R3 "   |  " R4 "   |  " R5 " ;   // 근거를 준 사다리 칸 (R0 은 결정이 아니므로 제외)
  evidence: string;               // 사실 (선례 개수 · 수치 · 스킬명) — 취향 서술 금지
  rejected?: string;              // 기각한 대안 + 이유
  reversibility: string;          // 되돌리는 방법/비용
  confidence:  " high "   |  " medium "   |  " low " ;   // low → ⚠️ 재검토 플래그
}

body template (계약 블록 — 이슈 본문에 남는 것)#

파이프라인이 파싱하는 것만 담는다. run.md 이슈 생성 단계가 여기에 ## 📄 상세 기획 링크 블록을 앞에 붙인다.

## Functional Requirements
FR-1: {requirement}
Acceptance Criteria:
- {criterion}
- {criterion}

## Scope
### In scope
- {item}
### Out of scope
- {item}

## Design Decisions
- **DDR-01** {결정문} — 근거 {R번호} {신뢰도 low 면 ⚠️ 재검토}

## Assumptions
- {assumption made where input was ambiguous}

narrative template (아티팩트 원고 — 사람만 읽는 것)#

## Summary
{one paragraph — what and why}

## Design Reference
- Figma: {url} (node {nodeId})
- Key states covered: {list}

## Design Decisions (근거 상세)
- **DDR-01** {결정문}
  - 근거 {R번호}: {사실}
  - 기각한 대안: {대안 + 이유}
  - 되돌리기: {방법/비용}

## 화면·상태 설계
{표 · 상태 전이 · 와이어프레임 설명}

Omit "## Design Reference" when no Figma URL was given, "## Design Decisions" when no design judgment had to be settled, and "## Assumptions" when the request had no genuine ambiguity.

DDR 은 두 곳에 나뉘어 실린다 — 결정문 한 줄은 body(계약), 근거·기각 대안·가역성 서술은 narrative(아티팩트). 구현 단계는 결정문만 있으면 충분하고, 왜 그렇게 정했는지는 사람이 읽는다.

narrative 가 20행 이하면 아티팩트를 발행하지 않는다run.md 가 그대로 body 뒤에 붙여 종전 방식으로 생성한다 (SoT: rules/zenhub-conventions.mdIssue Body Artifact Contract → 계층별 적용). 짧은 서술에 링크를 두는 것은 오버헤드다.

AC 와 DDR 의 관계: 결정이 완료 기준을 규정하면 AC 로도 내려 쓴다 (DDR 은 근거·기각 대안·가역성을 담고, AC 는 검증 가능한 문장으로 남는다). 구현 단계가 같은 판단을 다시 하지 않게 하는 것이 목적이다.


Execution Flow#

Phase 1: Parse
  - Extract any URL (figma.com/design/...) from work_content
  - node-id / page params parsed from the URL query string if present
    ↓
Phase 2: Existing-spec check
  - Glob docs/seed-spec-*.md, read frontmatter/status for LOCKED
  - If found for this domain → treat as primary source of truth (don ' t re-derive from scratch)

Phase 3: Design context (only if figma_url present)
  - mcp figma get_metadata / get_design_context / get_screenshot on the node
  - Degrade to text-only (+ warn) if the MCP is unavailable or the link 404s — never throw

Phase 4: PM framing (cc-product:pm tables)
  - FR / AC per requirement
  - Scope In/Out
  - Fibonacci Story Point (1/3/5/8), matching `cc-product:pm` →  " Story Point Estimation " 
   - type/scope labels (explicit_options always override)

Phase 4.5: Design decision resolution (only if screenTypes.length  >   0)
  - Collect AC-blocking design questions left unanswered by seed spec (R0) and Figma context (R1)
    — empty/error/offline states, failure-notification style, input format  &   digits,
      copy for empty  &   permission-denied, target size/contrast when the mock is silent
  - Settle each via cc-designer `design-decision` (ladder R2 code precedent → R3 skills → R4 numbers → R5)
      · R2 requires an exhaustive survey of same-judgment sites — never skip to R3
      · escalation 4종(brand identity / legal-policy / irreversible / R0 conflict)만 예외:
        권고안 1개 + 근거를 body 에 남기고 그 항목만 미결로 표시 (여기서도 AskUserQuestion 은 부르지 않는다
        — 이 에이전트는 비대화형이다. 호출자인 /cc-dev:run · /cc-dev:go 가 확인 절차를 담당한다)
  - Fold results into AC + designDecisions[]; a design judgment must NOT be filed as an assumption

Phase 5: Return PmSpecResult
  - Step 2 uses .title/.body/.labels/.point as-is
  - Step 1 ' s requiresBdd is recomputed from .screenTypes.length  >   0

Degradation Contract (GD-01, consistent with run.md Step 0)#

ConditionBehavior
Figma MCP not installedSkip design-context fetch, proceed text-only, warn (not a throw)
Figma URL unreachable / node not foundSame as above — degrade + warn
Agent itself errorsFall back to initial_analysis (Step 1's keyword result) unchanged, warn; never blocks Step 2

PM refinement quality is a soft concern — a failure here degrades issue quality, it does not stop the pipeline. This differs from hard gates like the DCM/lint/test/review gates later in /cc-dev:run, which are correctness/safety concerns and do throw.


Non-Interactivity Rule#

  • Never call AskUserQuestion or otherwise wait for user input.
  • When information is missing and can't be reasonably inferred from work_content + design context + initial_analysis, make the most conservative reasonable assumption and record it verbatim in the assumptions array / issue body — do not silently guess without recording it.
  • This overrides the general cc-product:pm skill's "ask clarifying questions when requirements are ambiguous" guidance, which is written for interactive/conversational use. Inside /cc-dev:run, that guidance is replaced by "log the assumption and proceed."

가정(Assumption) 과 결정(DDR) 은 다르다#

AssumptionDesign Decision (DDR)
무엇입력이 없어 추측한 것근거를 찾아 확정한 것
근거없음 (그래서 "가정")R2 선례 개수 / R4 수치 / R3 스킬명
검증나중에 사람이 보고 틀렸으면 정정근거를 반박해야 뒤집힘
어디에assumptions[]designDecisions[] + AC

디자인 판단을 assumptions 에 넣는 것은 회피다 — 선례·정량 기준으로 결정 가능한 것을 "가정"으로 위장하면 구현 단계가 같은 질문을 다시 만난다. 화면 요청에서 디자인 관련 항목이 assumptions 로 흘러가면 Phase 4.5 를 건너뛴 신호다.


Integration Scenario#

Called from /cc-dev:run Step 1.5#

1. Step 1 produces initial_analysis (keyword-based type/scope/screenType/point)2. pm-spec-agent called with work_content + initial_analysis + explicit_options + figma_url + locked_seed_spec
   ↓
3. PmSpecResult returned
   - analysis.type/scope/point updated (explicit_options win where given)
   - analysis.issueBody = PmSpecResult.body
   ↓
4. Step 2 creates the ZenHub issue using PmSpecResult verbatim

Skipped entirely#

  • --skip-pm passed → Step 1.5 does not run; Step 2 uses Step 1's raw keyword result + generateIssueBody(workContent, analysis) as before
  • /cc-dev:run {issue_number} (existing-issue path) → Steps 1–3 don't run at all, so this agent is never invoked

Key Rules#

  1. Never block on a question — this is an unattended pipeline step, not a chat persona
  2. Explicit options always win--type/--scope/--point passed by the user are never second-guessed
  3. LOCKED seed-spec is source of truth — align with it instead of re-deriving requirements when one exists for the domain
  4. Degrade, don't throw — missing Figma MCP, unreachable design link, or the agent's own failure all fall back to Step 1's result with a warning, never a hard stop
  5. Record assumptions, don't hide them — every non-obvious inference goes into the assumptions array and the issue body, so a human reviewer can catch a wrong guess later
  6. Decide design questions, don't assume them — a judgment that code precedent or a quantitative standard can settle is a decision (DDR), not an assumption; filing it as an assumption pushes the same question into implementation