Claude Skill

product-clarify

Refine and validate Product Decision Records through targeted clarification questions. Review PDR completeness, detect conflicts, approve decisions, and update status to Accepted. Use before /product-implement.

LLM Mart · 0 points · 9 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download tikalk-adlc-team-skills-skills_product_product-clarify-3035db2.zip · 12 KB
Part of tikalk/adlc-team-skills — 21 skills

Install

skills CLI npx skills add https://github.com/tikalk/adlc-team-skills/tree/main/skills/product/product-clarify
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install tikalk-adlc-team-skills@llmmart
Git git clone https://github.com/tikalk/adlc-team-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole tikalk/adlc-team-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

product-clarify

What this skill does

Reviews existing PDRs for quality gaps, asks targeted clarification questions, and promotes approved PDRs to Accepted status. This is the quality gate before PRD generation.

Input: Individual PDR-*.md files in .adlc/drafts/pdr/ (status Proposed or Discovered)

Output: Updated PDR-*.md files (status Accepted where approved), regenerated pdr.md index

When to use

  • After /product-specify or /product-init to refine initial PDRs
  • Before /product-implement to approve PDRs (required — implement skips non-Accepted)
  • Periodic PDR review before milestones
  • Resolving inconsistency flags from cross-feature-area analysis

When NOT to use

  • No PDRs exist (use /product-specify or /product-init first)

Execution Steps

Phase 1: Load PDRs

  1. Run setup script:
sh: scripts/bash/setup-product-clarify.sh [--json]
ps: scripts/powershell/setup-product-clarify.ps1
  1. Read all PDR files from {REPO_ROOT}/.adlc/drafts/pdr/PDR-*.md
  2. Read constitution from {REPO_ROOT}/.adlc/memory/constitution.md if exists
  3. Build inventory:
| PDR | Title | Status | Category |
|-----|-------|--------|----------|
| PDR-001 | Target Market | Proposed | Problem |
| PDR-002 | Primary Persona | Proposed | Persona |

Step 0: Detect Lightweight Draft Format

Check if the draft being reviewed uses the lightweight draft template (indicated by presence of type, evidence, source, revisit-when fields in frontmatter and ## Rejected Alternatives / ## Reason body sections without the full formal template sections).

If lightweight:

  1. Read the draft's captured fields (Context, Decision, Rejected Alternatives, Reason)
  2. Transform to full formal template:
    • ADR: full MADR format with Decision Drivers, Considered Options, Pros/Cons, Constitution Alignment, Related ADRs
    • PDR: full PDR format with Market Forces, Consequences, Alternatives Considered, Links
    • ChDR: full ChDR format with Issue Links, Commits, Consequences, Evidence
    • CDR: full CDR format with Context Type, Target Module, Descriptor, Evidence
    • EVAL: full eval format with Error Analysis, Pass/Fail Examples, Implementation Notes
  3. Enrich from session context (add details the lightweight draft may have omitted)
  4. Present the enriched draft for review

If already full format, proceed with normal review.

Phase 2: PDR Quality Analysis

Check each PDR against standards:

Dimension Check Severity if Missing
Context Problem clearly stated MEDIUM
Decision Actionable, testable HIGH
Consequences Positive AND negative HIGH
Success Metrics Defined with targets HIGH
Alternatives At least 2 with neutral trade-offs HIGH
Constitution Aligns with vision CRITICAL

Quality checklist:

  • Clear context explaining the problem/opportunity
  • Explicit, actionable decision statement
  • Positive AND negative consequences documented
  • At least 2 alternatives with neutral trade-offs (not "rejected because")
  • Success metrics defined
  • Risks identified with mitigation strategies
  • Valid status value
  • No conflicts with other PDRs
  • Alignment with constitution/vision principles

Phase 3: Cross-PDR Consistency

  1. Conflicting Decisions: Same concept, different decisions across PDRs
  2. Missing Dependencies: PDRs that should reference each other
  3. Terminology Drift: Same concept named differently
  4. Priority Conflicts: One PDR says B2B, another assumes B2C

Phase 4: Gap Identification

Generate gap report:

## PDR Clarification Report

| PDR | Title | Gap Type | Severity |
|-----|-------|----------|----------|
| PDR-001 | [Title] | Missing alternatives | HIGH |
| PDR-002 | [Title] | Incomplete consequences | MEDIUM |

Severity:

  • CRITICAL: Constitution violations, missing decision statement, no success metrics
  • HIGH: No alternatives, missing consequences
  • MEDIUM: Incomplete risks, unclear context
  • LOW: Minor phrasing

Phase 5: Interactive Refinement

For each gap, present one clarification at a time:

## Clarification [N]: PDR-XXX — [Gap Type]

**Current State**:
[Quote current PDR content]

**Gap Identified**:
[Explain what's missing]

**Question**:
[Specific question]

**Suggested Options**:
| Option | Description |
|--------|-------------|
| A | [Option A] |
| B | [Option B] |
| C | [Custom response] |

Rules:

  • Present one clarification at a time
  • Prioritize by severity — CRITICAL/HIGH first
  • Limit to 5 clarifications per session
  • Allow user to skip non-critical clarifications
  • Summarize changes after each answer

Phase 6: Constitution Violations (if any)

For Duplicates:

## Constitution Duplication Detected

**PDR**: PDR-XXX — [Title]
**Constitution Principle**: §[Section] — [Principle]

**Options**:
| Option | Action |
|--------|--------|
| A | Remove PDR |
| B | Convert to Reference |
| C | Add Context |
| D | Extend |

For Violations (⭐ RECOMMENDED: Amend Constitution):

## Constitution Violation Detected ⭐

**⭐ RECOMMENDED: A. Amend Constitution**
Update constitution §[Section] to accommodate this decision.

**Alternatives**:
B. Override in PDR — Document justification
C. Revise PDR — Change decision to comply
D. Remove PDR — Delete and follow constitution

Phase 7: Update PDR Files

  1. Apply clarifications per-file
  2. Update status where applicable
  3. Update "Last Updated" timestamps
  4. Regenerate pdr/pdr.md index — call generate_pdr_index drafts from pdr-lib.sh (or Generate-PdrIndex drafts from pdr-lib.ps1). This uses a frontmatter-primary + heading-fallback parser that handles both H2 and H3 metadata, preventing the blank-row bug.

Phase 8: PDR Approval ⭐

Critical: /product-implement only processes PDRs with Accepted status.

## PDR Approval ⭐

**Total PDRs**: [N]
**Status Distribution**:
- Accepted: [N]
- Proposed: [N]
- Discovered: [N]

**Options**:
| Option | Action |
|--------|--------|
| A | Accept All — Change Proposed/Discovered → Accepted |
| B | Review Specific — Select individual PDRs |
| C | Defer — Keep current status |

Bulk approval (Option A):

## Confirm Bulk Approval

Change [N] PDRs to Accepted?

| PDR | Current | New |
|-----|---------|-----|
| PDR-001 | Proposed | Accepted |
| PDR-002 | Discovered | Accepted |

Post-approval:

## PDRs Approved

**Ready for Implementation**:
- Accepted PDRs: [N]
- Pending: [N]

Run `/product-implement` to generate PRD.md.

Key Rules

Non-Destructive Refinement

  • Never delete existing PDRs without explicit user approval
  • Preserve original intent when updating wording
  • Add, don't replace consequences and alternatives
  • Mark changes with updated timestamps

Focused Clarification

  • Ask one question at a time
  • Make questions specific and answerable
  • Provide suggested options when possible
  • Respect user's time

Constitution Authority

  • Constitution violations are always flagged
  • PDRs cannot override MUST principles without justification
  • Suggest constitution updates if conflict is systemic

Configuration

  • PDR_DRAFTS_DIR — {REPO_ROOT}/.adlc/drafts/pdr
  • PDR_INDEX — {REPO_ROOT}/.adlc/drafts/pdr/pdr.md
  • CONSTITUTION — {REPO_ROOT}/.adlc/memory/constitution.md

12-Factor Alignment

  • Factor III (Mission Definition): Ensures product mission is well-defined before execution
  • Factor VII (Quality Gates): Applies human judgment before automated generation

Common Rationalizations

Rationalization Reality
"The PDRs look fine — let's skip clarify." Implement will skip non-Accepted PDRs, silently dropping decisions from the PRD.
"I'll just accept all at once." Bulk accept without review risks embedding bad decisions into the PRD.
"A warning on constitution is basically okay." Constitution violations are CRITICAL because they undermine product coherence.

Red Flags

  • Skipping the approval step — without Accepted status, implement ignores the PDR.
  • Accepting without reading — defeats the purpose of the quality gate.
  • Not flagging cross-PDR conflicts — inconsistent PDRs produce an incoherent PRD.

Verification

  • All PDR files read and inventoried
  • Quality checklist applied to each PDR
  • Cross-PDR consistency verified
  • Constitution alignment checked
  • Gaps identified and prioritized
  • Clarifications applied per-file
  • pdr.md index regenerated
  • Approval phase completed (Accept/Review/Defer)
  • Accepted PDRs have status "Accepted" in their files
  • No duplicate or missing PDR IDs
Files (adlc-team-skills)
  • scripts
    • bash
      • pdr-lib.sh 17.8 KB
        #!/usr/bin/env bash
        #
        # pdr-lib.sh — Shared PDR lifecycle library for adlc-skills product-* skills.
        #
        # Mirrors the ADR-side tooling in setup-architect.sh (generate_adr_index, move_adr,
        # parse_fm_field, parse_fm_title) so that the PDR side has the same structural
        # robustness: script-driven index generation, atomic move promotion, and a
        # frontmatter-primary + heading-fallback parser that handles both legacy H2
        # (## Status) and current H3 (### Status) metadata.
        #
        # Bundled with each product-* skill so it works standalone.
        # Sourced by setup-product-*.sh scripts and callable directly.
        #
        # Usage:
        #   source pdr-lib.sh                          # load functions
        #   generate_pdr_index memory                  # regenerate .adlc/memory/pdr/pdr.md
        #   generate_pdr_index drafts                  # regenerate .adlc/drafts/pdr/pdr.md
        #   move_pdr 020 drafts memory                # atomically move PDR-020 drafts→memory
        #   migrate_pdr_to_frontmatter .adlc/memory/pdr/PDR-001.md   # one-time legacy migration
        #
        
        set -euo pipefail
        
        # ============================================================================
        # Project root resolution (mirror of common.sh _get_project_root)
        # ============================================================================
        
        _get_project_root() {
            local dir
            dir="$(pwd)"
            while [ "$dir" != "/" ]; do
                if [ -d "$dir/.adlc" ] || [ -d "$dir/.git" ]; then
                    echo "$dir"
                    return 0
                fi
                dir="$(dirname "$dir")"
            done
            pwd
        }
        
        # Resolve REPO_ROOT if not already set by the caller.
        if [ -z "${REPO_ROOT:-}" ]; then
            REPO_ROOT="$(_get_project_root)"
        fi
        export REPO_ROOT
        
        # ============================================================================
        # YAML frontmatter parser (copied from setup-architect.sh parse_fm_field)
        # ============================================================================
        
        # Parse a YAML frontmatter field from a markdown file.
        # Usage: parse_fm_field "file" "fieldname"
        # Returns the field value with quotes, inline comments, and surrounding whitespace stripped.
        parse_fm_field() {
            local file="$1"
            local field="$2"
            [ -f "$file" ] || return 0
            awk -v fld="^[[:space:]]*${field}:[[:space:]]*" '
                /^---[[:space:]]*$/ { fm++; next }
                fm == 1 && $0 ~ fld {
                    sub(fld, "")
                    sub(/[[:space:]]+#.*$/, "")
                    gsub(/^["'\'']|["'\'']$/, "")
                    gsub(/^\[|\]$/, "")
                    sub(/^[[:space:]]+/, ""); sub(/[[:space:]]+$/, "")
                    print
                    exit
                }
            ' "$file"
        }
        
        # Extract the H1 title (first "# " line after frontmatter) from a markdown file.
        # Usage: parse_fm_title "file"
        parse_fm_title() {
            [ -f "$1" ] || return 0
            awk '/^---[[:space:]]*$/ { fm++; next } fm >= 2 && /^#[[:space:]]+/ { sub(/^#[[:space:]]+/, ""); sub(/[[:space:]]+$/, ""); print; exit }' "$1"
        }
        
        # ============================================================================
        # Heading-based fallback parser (handles legacy H2 and current H3 PDR files)
        # ============================================================================
        
        # Parse a metadata field from Markdown headings (## or ###).
        # Looks for a heading line matching "^###+ <field>$", returns the next non-empty
        # non-heading line with ** bold markers stripped.
        # Usage: parse_pdr_heading_field "file" "Status"  (case-sensitive field name)
        parse_pdr_heading_field() {
            local file="$1"
            local field="$2"
            [ -f "$file" ] || return 0
            awk -v fld="$field" '
                $0 ~ "^#+[[:space:]]*" fld "[[:space:]]*$" { found=1; next }
                found && /^#+/ { exit }
                found && NF > 0 {
                    gsub(/\*\*/, "")
                    sub(/^[[:space:]]+/, ""); sub(/[[:space:]]+$/, "")
                    print
                    exit
                }
            ' "$file"
        }
        
        # Extract the title from a "# PDR-NNN: <title>" or "## PDR-NNN: <title>" heading.
        # Strips the heading marker and "PDR-NNN:" prefix.
        # Usage: parse_pdr_heading_title "file"
        parse_pdr_heading_title() {
            [ -f "$1" ] || return 0
            awk '
                $0 ~ "^#+[[:space:]]*PDR-[0-9]+:[[:space:]]*" {
                    sub(/^#+[[:space:]]*PDR-[0-9]+:[[:space:]]*/, "")
                    sub(/[[:space:]]+$/, "")
                    print
                    exit
                }
            ' "$1"
        }
        
        # ============================================================================
        # Combined parsers (frontmatter primary, heading fallback)
        # ============================================================================
        
        # Parse a PDR metadata field: tries YAML frontmatter first, falls back to headings.
        # Usage: parse_pdr_field "file" "status"  (frontmatter field name, lowercase hyphenated)
        #        parse_pdr_field "file" "Status"  (heading field name, Title Case)
        # Note: frontmatter uses lowercase hyphenated keys (status, feature-area);
        #       headings use Title Case (Status, Feature-Area). This function tries both.
        parse_pdr_field() {
            local file="$1"
            local field="$2"
            local value=""
        
            # Try frontmatter (lowercase the field name for YAML key matching)
            local fm_field
            fm_field=$(echo "$field" | tr '[:upper:]' '[:lower:]')
            value=$(parse_fm_field "$file" "$fm_field")
            if [ -n "$value" ]; then
                echo "$value"
                return
            fi
        
            # Fallback: heading-based (Title Case the field name for heading matching)
            # Accept both "Feature-Area" and "feature-area" heading styles
            local title_field
            title_field=$(echo "$field" | awk -F'-' '{for (i=1; i<=NF; i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1' OFS='-')
            value=$(parse_pdr_heading_field "$file" "$title_field")
            if [ -n "$value" ]; then
                echo "$value"
                return
            fi
            # Try the raw field name (handles "Feature-Area" where only first letter caps)
            value=$(parse_pdr_heading_field "$file" "$field")
            if [ -n "$value" ]; then
                echo "$value"
                return
            fi
        
            # Empty string — caller should apply defaults
            echo ""
        }
        
        # Extract the PDR title: tries frontmatter "title" field, falls back to H1 parsing.
        # Usage: parse_pdr_title "file"
        parse_pdr_title() {
            [ -f "$1" ] || return 0
            local value
            value=$(parse_fm_field "$1" "title")
            if [ -n "$value" ]; then
                echo "$value"
                return
            fi
            # Fallback: strip "PDR-NNN:" prefix from H1/H2
            value=$(parse_pdr_heading_title "$1")
            if [ -n "$value" ]; then
                echo "$value"
                return
            fi
            # Last resort: bare H1 (no PDR-NNN prefix)
            value=$(parse_fm_title "$1")
            echo "$value"
        }
        
        # ============================================================================
        # Index generation (mirrors generate_adr_index from setup-architect.sh)
        # ============================================================================
        
        # Generate pdr.md index from individual PDR files.
        # Usage: generate_pdr_index [scope]   (scope = drafts | memory; default: drafts)
        # Writes to: $REPO_ROOT/.adlc/{scope}/pdr/pdr.md  (INSIDE the pdr/ directory)
        # Schema: 7 columns — ID | Feature-Area | Category | Status | Date | Owner | Title
        # Fails loudly on blank cells (warns to stderr; applies defaults so no row is blank).
        generate_pdr_index() {
            local scope="${1:-drafts}"
            local pdr_dir="$REPO_ROOT/.adlc/$scope/pdr"
            local index_file="$pdr_dir/pdr.md"
        
            if [ ! -d "$pdr_dir" ]; then
                return 0
            fi
        
            local index_content="# Product Decision Records"
            if [ "$scope" = "memory" ]; then
                index_content="$index_content (Memory)
        
        > Auto-generated by /product-implement. Accepted PDRs only.
        > Source: .adlc/$scope/pdr/PDR-*.md"
            else
                index_content="$index_content (Drafts)
        
        > Auto-generated by /product-clarify. Proposed PDRs only.
        > Source: .adlc/$scope/pdr/PDR-*.md"
            fi
        
            index_content="$index_content
        
        ## PDR Index
        
        | ID | Feature-Area | Category | Status | Date | Owner | Title |
        |----|--------------|----------|--------|------|-------|-------|
        "
        
            # Sort PDR files numerically
            local f fname id title status date owner category feature_area padded_id
            local blank_warnings=""
        
            for f in $(ls -1 "$pdr_dir"/PDR-*.md 2>/dev/null | sort -t'-' -k2 -n); do
                fname=$(basename "$f")
                id=$(echo "$fname" | sed -E 's/PDR-([0-9]+)\.md/\1/')
                padded_id=$(printf "%03d" "$((10#$id))")
        
                title=$(parse_pdr_title "$f")
                status=$(parse_pdr_field "$f" "status")
                date=$(parse_pdr_field "$f" "date")
                owner=$(parse_pdr_field "$f" "owner")
                category=$(parse_pdr_field "$f" "category")
                feature_area=$(parse_pdr_field "$f" "feature-area")
        
                # Defaults — no silent blank cells
                [ -z "$status" ] && { status="Unknown"; blank_warnings="$blank_warnings\n  - PDR-$padded_id: Status"; }
                [ -z "$date" ] && { date="YYYY-MM-DD"; blank_warnings="$blank_warnings\n  - PDR-$padded_id: Date"; }
                [ -z "$owner" ] && { owner="Unknown"; blank_warnings="$blank_warnings\n  - PDR-$padded_id: Owner"; }
                [ -z "$category" ] && { category="Unknown"; blank_warnings="$blank_warnings\n  - PDR-$padded_id: Category"; }
                [ -z "$feature_area" ] && { feature_area="system"; blank_warnings="$blank_warnings\n  - PDR-$padded_id: Feature-Area"; }
                [ -z "$title" ] && { title="PDR-$padded_id"; blank_warnings="$blank_warnings\n  - PDR-$padded_id: Title"; }
        
                index_content="$index_content| PDR-$padded_id | $feature_area | $category | $status | $date | $owner | $title |
        "
            done
        
            # Write the index
            printf '%s\n' "$index_content" > "$index_file"
        
            # Warn on any blank cells that needed defaults
            if [ -n "$blank_warnings" ]; then
                echo "[WARN] generate_pdr_index ($scope): the following PDRs had blank metadata cells;" >&2
                echo "       defaults were applied. Run /product-clarify to fix the source files." >&2
                printf '%b\n' "$blank_warnings" >&2
            fi
        }
        
        # ============================================================================
        # Atomic move (mirrors move_adr from setup-architect.sh)
        # ============================================================================
        
        # Move a PDR from one scope to another (e.g., drafts -> memory).
        # Usage: move_pdr <pdr_id> [from_scope] [to_scope]
        # Performs an atomic mv, then regenerates both scopes' indexes.
        # Fails if the source file does not exist. Verifies no duplicates remain.
        move_pdr() {
            local pdr_id="$1"
            local from_scope="${2:-drafts}"
            local to_scope="${3:-memory}"
        
            local from_dir="$REPO_ROOT/.adlc/$from_scope/pdr"
            local to_dir="$REPO_ROOT/.adlc/$to_scope/pdr"
        
            local numeric_id
            numeric_id=$(echo "$pdr_id" | sed -E 's/[^0-9]//g')
            local padded_id
            padded_id=$(printf "%03d" "$((10#$numeric_id))")
        
            mkdir -p "$to_dir"
        
            if [ -f "$from_dir/PDR-$padded_id.md" ]; then
                mv "$from_dir/PDR-$padded_id.md" "$to_dir/PDR-$padded_id.md"
            else
                echo "[WARN] move_pdr: source file not found: $from_dir/PDR-$padded_id.md" >&2
                return 1
            fi
        
            # Duplicate check — the source must be gone
            if [ -f "$from_dir/PDR-$padded_id.md" ]; then
                echo "[ERROR] move_pdr: duplicate detected — PDR-$padded_id still exists in $from_scope after move" >&2
                return 1
            fi
        
            # Regenerate both scopes
            generate_pdr_index "$from_scope"
            generate_pdr_index "$to_scope"
        }
        
        # ============================================================================
        # One-time migration: heading-based metadata → YAML frontmatter
        # ============================================================================
        
        # Migrate a legacy PDR file (heading-based metadata) to YAML frontmatter.
        # Reads Status/Date/Owner/Category/Feature-Area/Title from headings, prepends
        # frontmatter, and preserves the body unchanged. Skips files that already have
        # frontmatter. Strips ** bold markers from Status.
        # Usage: migrate_pdr_to_frontmatter <file>
        migrate_pdr_to_frontmatter() {
            local file="$1"
            [ -f "$file" ] || { echo "[WARN] migrate: file not found: $file" >&2; return 1; }
        
            # Skip if already has frontmatter
            if head -1 "$file" | grep -q '^---[[:space:]]*$'; then
                return 0
            fi
        
            local title status date owner category feature_area
        
            title=$(parse_pdr_heading_title "$file")
            status=$(parse_pdr_heading_field "$file" "Status")
            date=$(parse_pdr_heading_field "$file" "Date")
            owner=$(parse_pdr_heading_field "$file" "Owner")
            category=$(parse_pdr_heading_field "$file" "Category")
            feature_area=$(parse_pdr_heading_field "$file" "Feature-Area")
        
            # Strip ** bold markers from status
            status="${status//\*\*/}"
        
            # Build frontmatter
            local fm="---
        status: ${status:-Unknown}
        date: ${date:-YYYY-MM-DD}
        owner: ${owner:-Unknown}
        category: ${category:-Unknown}
        feature-area: ${feature_area:-system}
        title: ${title:-Untitled}
        ---
        "
        
            # Prepend frontmatter to the original body
            printf '%s\n%s\n' "$fm" "$(cat "$file")" > "$file.tmp" && mv "$file.tmp" "$file"
        }
        
        # Migrate all PDR files in a directory.
        # Usage: migrate_pdr_dir <dir>
        migrate_pdr_dir() {
            local dir="$1"
            [ -d "$dir" ] || { echo "[WARN] migrate_pdr_dir: dir not found: $dir" >&2; return 1; }
            local f count=0 skipped=0
            for f in "$dir"/PDR-*.md; do
                [ -f "$f" ] || continue
                if head -1 "$f" | grep -q '^---[[:space:]]*$'; then
                    skipped=$((skipped + 1))
                else
                    migrate_pdr_to_frontmatter "$f" && count=$((count + 1))
                fi
            done
            echo "[INFO] migrate_pdr_dir: migrated $count file(s), skipped $skipped (already had frontmatter)"
        }
        
        # ============================================================================
        # Fix frontmatter: re-extract metadata from body headings and update frontmatter
        # ============================================================================
        
        # Fix PDR frontmatter by re-reading heading-based metadata from the body.
        # For files that were migrated with incorrect/blank frontmatter (e.g., due to
        # a parser bug), this function re-extracts Status/Date/Owner/Category/Feature-Area/Title
        # from the body headings and rewrites the frontmatter block.
        # Usage: fix_pdr_frontmatter <file>
        fix_pdr_frontmatter() {
            local file="$1"
            [ -f "$file" ] || { echo "[WARN] fix_pdr_frontmatter: file not found: $file" >&2; return 1; }
        
            # Must have frontmatter to fix
            if ! head -1 "$file" | grep -q '^---[[:space:]]*$'; then
                return 0  # Not migrated yet — skip
            fi
        
            # Extract metadata from body headings (these are always present, even after migration)
            local title status date owner category feature_area
            title=$(parse_pdr_heading_title "$file")
            status=$(parse_pdr_heading_field "$file" "Status")
            date=$(parse_pdr_heading_field "$file" "Date")
            owner=$(parse_pdr_heading_field "$file" "Owner")
            category=$(parse_pdr_heading_field "$file" "Category")
            feature_area=$(parse_pdr_heading_field "$file" "Feature-Area")
        
            # Strip ** bold markers
            status="${status//\*\*/}"
        
            # Skip if all fields are empty (can't fix)
            if [ -z "$status" ] && [ -z "$date" ] && [ -z "$owner" ] && [ -z "$category" ] && [ -z "$feature_area" ] && [ -z "$title" ]; then
                return 0
            fi
        
            # Read current frontmatter values to preserve any non-empty ones
            local fm_status fm_date fm_owner fm_category fm_feature_area fm_title
            fm_status=$(parse_fm_field "$file" "status")
            fm_date=$(parse_fm_field "$file" "date")
            fm_owner=$(parse_fm_field "$file" "owner")
            fm_category=$(parse_fm_field "$file" "category")
            fm_feature_area=$(parse_fm_field "$file" "feature-area")
            fm_title=$(parse_fm_field "$file" "title")
        
            # Use heading value if non-empty, else keep frontmatter value, else default
            [ -n "$status" ] && fm_status="$status"
            [ -n "$date" ] && fm_date="$date"
            [ -n "$owner" ] && fm_owner="$owner"
            [ -n "$category" ] && fm_category="$category"
            [ -n "$feature_area" ] && fm_feature_area="$feature_area"
            [ -n "$title" ] && fm_title="$title"
        
            # Defaults for any remaining blanks
            [ -z "$fm_status" ] && fm_status="Unknown"
            [ -z "$fm_date" ] && fm_date="YYYY-MM-DD"
            [ -z "$fm_owner" ] && fm_owner="Unknown"
            [ -z "$fm_category" ] && fm_category="Unknown"
            [ -z "$fm_feature_area" ] && fm_feature_area="system"
            [ -z "$fm_title" ] && fm_title="Untitled"
        
            # Build new frontmatter
            local new_fm="---
        status: $fm_status
        date: $fm_date
        owner: $fm_owner
        category: $fm_category
        feature-area: $fm_feature_area
        title: $fm_title
        ---"
        
            # Replace the old frontmatter block (everything between the first and second `---`)
            # with the new frontmatter, keeping the body unchanged.
            local body
            body=$(awk '
                BEGIN { fm_count = 0; printing = 0 }
                /^---[[:space:]]*$/ { fm_count++; if (fm_count == 2) { printing = 1; next } else { next } }
                fm_count >= 2 && printing { print }
            ' "$file")
        
            printf '%s\n\n%s\n' "$new_fm" "$body" > "$file.tmp" && mv "$file.tmp" "$file"
        }
        
        # Fix all PDR files in a directory.
        # Usage: fix_pdr_dir <dir>
        fix_pdr_dir() {
            local dir="$1"
            [ -d "$dir" ] || { echo "[WARN] fix_pdr_dir: dir not found: $dir" >&2; return 1; }
            local f count=0
            for f in "$dir"/PDR-*.md; do
                [ -f "$f" ] || continue
                fix_pdr_frontmatter "$f" && count=$((count + 1))
            done
            echo "[INFO] fix_pdr_dir: fixed $count file(s)"
        }
        
        # ============================================================================
        # Level-agnostic Accepted counter (replaces the H3-hardcoded grep)
        # ============================================================================
        
        # Count PDRs with Accepted status in a directory, handling both YAML frontmatter
        # and heading-based metadata (H2 or H3). Replaces setup-product-clarify.sh:14
        # which hardcoded '^### Status'.
        # Usage: count_pdr_accepted <dir>
        count_pdr_accepted() {
            local dir="$1"
            [ -d "$dir" ] || return 0
            local count=0 f status
            for f in "$dir"/PDR-*.md; do
                [ -f "$f" ] || continue
                status=$(parse_pdr_field "$f" "status")
                # Normalize: strip ** bold, lowercase, trim
                status=$(echo "$status" | sed 's/\*\*//g' | tr '[:upper:]' '[:lower:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
                if [ "$status" = "accepted" ] || [ "$status" = "completed" ]; then
                    count=$((count + 1))
                fi
            done
            echo "$count"
        }
        
      • setup-product-clarify.sh 1.1 KB
        #!/bin/bash
        # product-clarify setup script
        set -euo pipefail
        JSON_MODE=false
        for arg in "$@"; do case "$arg" in --json) JSON_MODE=true ;; esac; done
        
        # Source pdr-lib.sh for _get_project_root (walks up to find .adlc, not just .git)
        source "$(dirname "${BASH_SOURCE[0]}")/pdr-lib.sh" 2>/dev/null || true
        REPO_ROOT="${REPO_ROOT:-$(_get_project_root)}"
        PDR_DRAFTS_DIR="$REPO_ROOT/.adlc/drafts/pdr"
        PDR_MEMORY_DIR="$REPO_ROOT/.adlc/memory/pdr"
        PRD_FILE="$REPO_ROOT/PRD.md"
        mkdir -p "$PDR_DRAFTS_DIR"
        PDR_COUNT=$(find "$PDR_DRAFTS_DIR" -name 'PDR-*.md' 2>/dev/null | wc -l)
        ACCEPTED_COUNT=0
        if [[ "$PDR_COUNT" -gt 0 ]]; then
          ACCEPTED_COUNT=$(count_pdr_accepted "$PDR_DRAFTS_DIR" 2>/dev/null || grep -lE '^#+\s*Status' "$PDR_DRAFTS_DIR"/PDR-*.md 2>/dev/null | xargs -I{} grep -l '^\*\*Accepted\*\*' {} 2>/dev/null | wc -l)
        fi
        if $JSON_MODE; then
          cat <<EOF
        {"REPO_ROOT":"$REPO_ROOT","PDR_DRAFTS_DIR":"$PDR_DRAFTS_DIR","PDR_MEMORY_DIR":"$PDR_MEMORY_DIR","PRD_FILE":"$PRD_FILE","pdr_count":$PDR_COUNT,"accepted_count":$ACCEPTED_COUNT}
        EOF
        else
          echo "[INFO] product-clarify setup"
          echo "  PDRs found: $PDR_COUNT"
          echo "  Accepted: $ACCEPTED_COUNT"
        fi
        
    • powershell
      • pdr-lib.ps1 10.7 KB · in bundle
      • setup-product-clarify.ps1 1.3 KB · in bundle
  • SKILL.md 8.9 KB
    ---
    name: product-clarify
    description: Use when PDRs need refinement, conflict detection, or approval to Accepted status before /product-implement.
    disable-model-invocation: true
    ---
    
    # product-clarify
    
    ## What this skill does
    
    Reviews existing PDRs for quality gaps, asks targeted clarification questions, and promotes approved PDRs to **Accepted** status. This is the quality gate before PRD generation.
    
    **Input**: Individual `PDR-*.md` files in `.adlc/drafts/pdr/` (status Proposed or Discovered)
    
    **Output**: Updated `PDR-*.md` files (status Accepted where approved), regenerated `pdr.md` index
    
    ## When to use
    
    - After `/product-specify` or `/product-init` to refine initial PDRs
    - Before `/product-implement` to approve PDRs (required — implement skips non-Accepted)
    - Periodic PDR review before milestones
    - Resolving inconsistency flags from cross-feature-area analysis
    
    ## When NOT to use
    
    - No PDRs exist (use `/product-specify` or `/product-init` first)
    
    ## Execution Steps
    
    ### Phase 1: Load PDRs
    
    1. **Run setup script**:
    ```bash
    sh: scripts/bash/setup-product-clarify.sh [--json]
    ps: scripts/powershell/setup-product-clarify.ps1
    ```
    
    2. **Read all PDR files** from `{REPO_ROOT}/.adlc/drafts/pdr/PDR-*.md`
    3. **Read constitution** from `{REPO_ROOT}/.adlc/memory/constitution.md` if exists
    4. **Build inventory**:
    ```markdown
    | PDR | Title | Status | Category |
    |-----|-------|--------|----------|
    | PDR-001 | Target Market | Proposed | Problem |
    | PDR-002 | Primary Persona | Proposed | Persona |
    ```
    
    ### Step 0: Detect Lightweight Draft Format
    
    Check if the draft being reviewed uses the lightweight draft template (indicated
    by presence of `type`, `evidence`, `source`, `revisit-when` fields in frontmatter
    and `## Rejected Alternatives` / `## Reason` body sections without the full
    formal template sections).
    
    If lightweight:
    1. Read the draft's captured fields (Context, Decision, Rejected Alternatives, Reason)
    2. Transform to full formal template:
       - ADR: full MADR format with Decision Drivers, Considered Options, Pros/Cons, Constitution Alignment, Related ADRs
       - PDR: full PDR format with Market Forces, Consequences, Alternatives Considered, Links
       - ChDR: full ChDR format with Issue Links, Commits, Consequences, Evidence
       - CDR: full CDR format with Context Type, Target Module, Descriptor, Evidence
       - EVAL: full eval format with Error Analysis, Pass/Fail Examples, Implementation Notes
    3. Enrich from session context (add details the lightweight draft may have omitted)
    4. Present the enriched draft for review
    
    If already full format, proceed with normal review.
    
    ### Phase 2: PDR Quality Analysis
    
    Check each PDR against standards:
    
    | Dimension | Check | Severity if Missing |
    |-----------|-------|---------------------|
    | Context | Problem clearly stated | MEDIUM |
    | Decision | Actionable, testable | HIGH |
    | Consequences | Positive AND negative | HIGH |
    | Success Metrics | Defined with targets | HIGH |
    | Alternatives | At least 2 with neutral trade-offs | HIGH |
    | Constitution | Aligns with vision | CRITICAL |
    
    **Quality checklist**:
    - [ ] Clear context explaining the problem/opportunity
    - [ ] Explicit, actionable decision statement
    - [ ] Positive AND negative consequences documented
    - [ ] At least 2 alternatives with neutral trade-offs (not "rejected because")
    - [ ] Success metrics defined
    - [ ] Risks identified with mitigation strategies
    - [ ] Valid status value
    - [ ] No conflicts with other PDRs
    - [ ] Alignment with constitution/vision principles
    
    ### Phase 3: Cross-PDR Consistency
    
    1. **Conflicting Decisions**: Same concept, different decisions across PDRs
    2. **Missing Dependencies**: PDRs that should reference each other
    3. **Terminology Drift**: Same concept named differently
    4. **Priority Conflicts**: One PDR says B2B, another assumes B2C
    
    ### Phase 4: Gap Identification
    
    Generate gap report:
    ```markdown
    ## PDR Clarification Report
    
    | PDR | Title | Gap Type | Severity |
    |-----|-------|----------|----------|
    | PDR-001 | [Title] | Missing alternatives | HIGH |
    | PDR-002 | [Title] | Incomplete consequences | MEDIUM |
    ```
    
    **Severity**:
    - **CRITICAL**: Constitution violations, missing decision statement, no success metrics
    - **HIGH**: No alternatives, missing consequences
    - **MEDIUM**: Incomplete risks, unclear context
    - **LOW**: Minor phrasing
    
    ### Phase 5: Interactive Refinement
    
    For each gap, present one clarification at a time:
    
    ```markdown
    ## Clarification [N]: PDR-XXX — [Gap Type]
    
    **Current State**:
    [Quote current PDR content]
    
    **Gap Identified**:
    [Explain what's missing]
    
    **Question**:
    [Specific question]
    
    **Suggested Options**:
    | Option | Description |
    |--------|-------------|
    | A | [Option A] |
    | B | [Option B] |
    | C | [Custom response] |
    ```
    
    **Rules**:
    - Present **one clarification at a time**
    - **Prioritize by severity** — CRITICAL/HIGH first
    - **Limit to 5 clarifications** per session
    - Allow user to **skip** non-critical clarifications
    - **Summarize changes** after each answer
    
    ### Phase 6: Constitution Violations (if any)
    
    **For Duplicates**:
    ```markdown
    ## Constitution Duplication Detected
    
    **PDR**: PDR-XXX — [Title]
    **Constitution Principle**: §[Section] — [Principle]
    
    **Options**:
    | Option | Action |
    |--------|--------|
    | A | Remove PDR |
    | B | Convert to Reference |
    | C | Add Context |
    | D | Extend |
    ```
    
    **For Violations** (⭐ RECOMMENDED: Amend Constitution):
    ```markdown
    ## Constitution Violation Detected ⭐
    
    **⭐ RECOMMENDED: A. Amend Constitution**
    Update constitution §[Section] to accommodate this decision.
    
    **Alternatives**:
    B. Override in PDR — Document justification
    C. Revise PDR — Change decision to comply
    D. Remove PDR — Delete and follow constitution
    ```
    
    ### Phase 7: Update PDR Files
    
    1. **Apply clarifications** per-file
    2. **Update status** where applicable
    3. **Update "Last Updated"** timestamps
    4. **Regenerate `pdr/pdr.md` index** — call `generate_pdr_index drafts` from `pdr-lib.sh` (or `Generate-PdrIndex drafts` from `pdr-lib.ps1`). This uses a frontmatter-primary + heading-fallback parser that handles both H2 and H3 metadata, preventing the blank-row bug.
    
    ### Phase 8: PDR Approval ⭐
    
    **Critical**: `/product-implement` only processes PDRs with **Accepted** status.
    
    ```markdown
    ## PDR Approval ⭐
    
    **Total PDRs**: [N]
    **Status Distribution**:
    - Accepted: [N]
    - Proposed: [N]
    - Discovered: [N]
    
    **Options**:
    | Option | Action |
    |--------|--------|
    | A | Accept All — Change Proposed/Discovered → Accepted |
    | B | Review Specific — Select individual PDRs |
    | C | Defer — Keep current status |
    ```
    
    **Bulk approval** (Option A):
    ```markdown
    ## Confirm Bulk Approval
    
    Change [N] PDRs to Accepted?
    
    | PDR | Current | New |
    |-----|---------|-----|
    | PDR-001 | Proposed | Accepted |
    | PDR-002 | Discovered | Accepted |
    ```
    
    **Post-approval**:
    ```markdown
    ## PDRs Approved
    
    **Ready for Implementation**:
    - Accepted PDRs: [N]
    - Pending: [N]
    
    Run `/product-implement` to generate PRD.md.
    ```
    
    ## Key Rules
    
    ### Non-Destructive Refinement
    - **Never delete** existing PDRs without explicit user approval
    - **Preserve original intent** when updating wording
    - **Add, don't replace** consequences and alternatives
    - **Mark changes** with updated timestamps
    
    ### Focused Clarification
    - Ask **one question at a time**
    - Make questions **specific and answerable**
    - Provide **suggested options** when possible
    - Respect user's time
    
    ### Constitution Authority
    - Constitution violations are **always flagged**
    - PDRs cannot override MUST principles without justification
    - Suggest constitution updates if conflict is systemic
    
    ## Configuration
    
    - `PDR_DRAFTS_DIR` — `{REPO_ROOT}/.adlc/drafts/pdr`
    - `PDR_INDEX` — `{REPO_ROOT}/.adlc/drafts/pdr/pdr.md`
    - `CONSTITUTION` — `{REPO_ROOT}/.adlc/memory/constitution.md`
    
    ## 12-Factor Alignment
    
    - **Factor III (Mission Definition)**: Ensures product mission is well-defined before execution
    - **Factor VII (Quality Gates)**: Applies human judgment before automated generation
    
    ## Common Rationalizations
    
    | Rationalization | Reality |
    |-----------------|---------|
    | "The PDRs look fine — let's skip clarify." | Implement will skip non-Accepted PDRs, silently dropping decisions from the PRD. |
    | "I'll just accept all at once." | Bulk accept without review risks embedding bad decisions into the PRD. |
    | "A warning on constitution is basically okay." | Constitution violations are CRITICAL because they undermine product coherence. |
    
    ## Red Flags
    
    - **Skipping the approval step** — without Accepted status, implement ignores the PDR.
    - **Accepting without reading** — defeats the purpose of the quality gate.
    - **Not flagging cross-PDR conflicts** — inconsistent PDRs produce an incoherent PRD.
    
    ## Verification
    
    - [ ] All PDR files read and inventoried
    - [ ] Quality checklist applied to each PDR
    - [ ] Cross-PDR consistency verified
    - [ ] Constitution alignment checked
    - [ ] Gaps identified and prioritized
    - [ ] Clarifications applied per-file
    - [ ] `pdr.md` index regenerated
    - [ ] Approval phase completed (Accept/Review/Defer)
    - [ ] Accepted PDRs have status "Accepted" in their files
    - [ ] No duplicate or missing PDR IDs
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related