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.
Install
npx skills add https://github.com/tikalk/adlc-team-skills/tree/main/skills/product/product-clarify
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install tikalk-adlc-team-skills@llmmart
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-specifyor/product-initto refine initial PDRs - Before
/product-implementto 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-specifyor/product-initfirst)
Execution Steps
Phase 1: Load PDRs
- Run setup script:
sh: scripts/bash/setup-product-clarify.sh [--json]
ps: scripts/powershell/setup-product-clarify.ps1
- Read all PDR files from
{REPO_ROOT}/.adlc/drafts/pdr/PDR-*.md - Read constitution from
{REPO_ROOT}/.adlc/memory/constitution.mdif exists - 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:
- Read the draft's captured fields (Context, Decision, Rejected Alternatives, Reason)
- 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
- Enrich from session context (add details the lightweight draft may have omitted)
- 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
- Conflicting Decisions: Same concept, different decisions across PDRs
- Missing Dependencies: PDRs that should reference each other
- Terminology Drift: Same concept named differently
- 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
- Apply clarifications per-file
- Update status where applicable
- Update "Last Updated" timestamps
- Regenerate
pdr/pdr.mdindex — callgenerate_pdr_index draftsfrompdr-lib.sh(orGenerate-PdrIndex draftsfrompdr-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/pdrPDR_INDEX—{REPO_ROOT}/.adlc/drafts/pdr/pdr.mdCONSTITUTION—{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.mdindex 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.
Reviews (0)
No reviews yet.
No comments yet.