product-specify
Interactive PRD exploration and Product Decision Record (PDR) creation for greenfield products. Facilitates product discovery discussions, surfaces trade-offs, and documents decisions as individual PDR files. Use when starting a new product or major pivot.
Install
npx skills add https://github.com/tikalk/adlc-team-skills/tree/main/skills/product/product-specify
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-specify
What this skill does
Transforms a high-level product idea into documented Product Decision Records (PDRs) through interactive exploration and trade-off analysis.
Key insight: Discussion and exploration happen before committing to formal documentation. The goal is to surface trade-offs, validate assumptions, and make informed decisions collaboratively.
Output: Individual PDR-{NNN}.md files (status Proposed) in .adlc/drafts/pdr/ with an auto-generated pdr.md index.
When to use
Note: Routine decision capture is handled by team-boot's continuous capture mechanism, which writes lightweight drafts directly to
.adlc/drafts/. This skill is for interactive deep-dive exploration — when you want guided trade-off analysis, multi-option comparison, or structured decision facilitation before documenting.
- New product from scratch
- Major product pivots
- Documenting verbal decisions formally
- Team onboarding — walking through product rationale
When NOT to use
- Existing product (use
/product-initinstead) - Minor PDR updates (use
/product-clarifyinstead)
Execution Steps
Phase 0: Environment Setup
sh: scripts/bash/setup-product-specify.sh [--json]
ps: scripts/powershell/setup-product-specify.ps1
Setup output (JSON):
{
"REPO_ROOT": "/path/to/project",
"PDR_DRAFTS_DIR": "/path/to/project/.adlc/drafts/pdr",
"PRD_FILE": "/path/to/project/PRD.md",
"next_pdr": "001"
}
Phase 1: Feature-Area Decomposition (Optional)
Analyze the product for distinct business domains. Auto-decompose if multiple domains detected. Use --no-decompose to skip.
Present detected areas:
## Detected Feature Areas
| # | Feature Area | Key Domains | Rationale |
|---|--------------|-------------|-----------|
| 1 | **Auth** | Authentication, Authorization | Core user entry |
| 2 | **Core** | User Management, Profiles | Core data |
| 3 | **Business** | Payments, Subscriptions | Revenue domain |
Reply: Y to confirm, n for monolithic, or suggest changes.
Threshold:
- ≤3 areas: Auto-approve
- 4-6 areas: Confirm with user
-
6 areas: Suggest grouping
Phase 2: Product Analysis
Extract product drivers:
- Problem Drivers: Core problem, who experiences it, current workarounds
- Market Drivers: Target segments, competitive landscape, trends
- Business Drivers: Revenue model, scaling expectations, strategic importance
- Constraint Drivers: Technology mandates, budget, team skills, regulatory
- Load Constitution: Read
{REPO_ROOT}/.adlc/memory/constitution.mdif exists - Check Existing Docs: Scan
README.md,AGENTS.md,CONTRIBUTING.mdfor context
Phase 3: Product Exploration (Interactive)
For each major decision area, present options and facilitate discussion:
Decision areas (5-7 key decisions):
- Problem Scope
- Target Personas
- Solution Approach (build vs buy vs partner)
- Monetization
- Go-to-Market
- Success Metrics
Exploration format:
## Product Decision: [Decision Area]
**Context**: [Why this decision matters]
**Options**:
| Option | Description | Trade-offs |
|--------|-------------|------------|
| A | [Option A] | Pros: [X] / Cons: [Y] |
| B | [Option B] | Pros: [X] / Cons: [Y] |
**Recommended**: Option [X]
**Questions**:
1. [Question about constraints]
2. [Question about trade-off priorities]
Rules:
- Present one decision area at a time
- Always provide a recommended option with reasoning
- Ask targeted questions to surface hidden requirements
- Skip decisions already determined by context or constitution
- Limit to 5-7 key decisions
Phase 4: Cross-Feature-Area Pre-Analysis
During exploration, watch for:
| Pattern | Detection | Action |
|---|---|---|
| Shared Personas | Same user type in multiple areas | Note for cross-area metadata |
| Priority Tensions | Areas prioritize differently | ⚠️ Flag potential conflict |
| Feature Overlap | Similar features in different areas | ⚠️ Flag for consolidation |
| Metric Conflicts | Same metric, different targets | ⚠️ Flag for alignment |
Phase 5: Decision Documentation
After each decision is confirmed, create a PDR file.
PDR file format (individual file, YAML frontmatter — use the shared template):
Use the template at {REPO_ROOT}/.agents/skills/product/templates/pdr-template.md as the canonical PDR format. The template includes YAML frontmatter (status, date, owner, category, feature-area, title) as the single source of truth for index generation, plus the standard PDR body sections (Context, Decision, Consequences, Alternatives, Links).
---
status: proposed
date: YYYY-MM-DD
owner: [User/AI collaboration]
category: Feature
feature-area: [core | business | growth | ...]
title: [Decision Title]
---
# PDR-[NNN]: [Decision Title]
## Context
**Problem/Opportunity:**
[Clear description]
**Market Forces:**
- [Market factor 1]
- [Customer feedback]
## Decision
**Decision Statement:**
[Clear statement of what was decided]
**Rationale:**
[Why this option was chosen]
### Consequences
#### Positive
- [Benefit 1]
#### Negative
- [Trade-off 1]
#### Risks
- [Risk with mitigation]
### Success Metrics
| Metric | Target | Measurement Method |
|--------|--------|-------------------|
| [Metric] | [Target] | [Method] |
### Alternatives Considered
#### Option A: [Alternative Name]
**Description:** [Brief description]
**Trade-offs:** [Neutral comparison]
### Constitution/Vision Alignment
| Principle | Alignment | Notes |
|-----------|-----------|-------|
| [Vision Principle] | ✅ Compliant / ⚠️ Deviation | [Explanation] |
Phase 6: Write PDR Files and Regenerate Index
- Number sequentially: Start from highest existing PDR number + 1
- Write individual files:
{REPO_ROOT}/.adlc/drafts/pdr/PDR-{NNN}.md - Regenerate index:
{REPO_ROOT}/.adlc/drafts/pdr/pdr.md
Index format:
# Product Decision Records
## PDR Index
| ID | Feature-Area | Category | Status | Date | Owner |
|----|--------------|----------|--------|------|-------|
| PDR-001 | System | Target Market | Proposed | 2026-03-09 | User/AI |
| PDR-002 | Auth | Primary Persona | Proposed | 2026-03-09 | User/AI |
---
*Individual PDR files: PDR-*.md in this directory*
Phase 7: Summary Report
## Feature Area Decomposition Summary
### Feature Areas Identified: 3
| # | Feature Area | PDRs Created |
|---|--------------|--------------|
| 1 | System-Level | PDR-001: Target Market |
| 2 | Auth | PDR-002: Primary Persona, PDR-003: Authentication Approach |
| 3 | Business | PDR-004: Pricing Model, PDR-005: Payment Integration |
### Next Steps
1. Review PDRs with /product-clarify
2. Generate PRD.md with /product-implement
PDR Numbering Rules
- Scan
{REPO_ROOT}/.adlc/drafts/pdr/for existingPDR-*.mdfiles - Extract numeric suffix, find maximum
- Next PDR = max + 1, zero-padded to 3 digits
- Never reuse numbers
Key Rules
Exploration First
- Do NOT generate PRD directly from product description
- Engage in discussion to validate assumptions
- Surface trade-offs before committing to decisions
- Allow iteration — user can revisit earlier decisions
Constitution Compliance
- PDRs must align with constitution principles
- Flag conflicts between product requirements and constitution
- Constitution violations require explicit override with justification
Incremental PDRs
- Create focused PDRs — one decision per PDR
- Link related PDRs when decisions interact
- Defer decisions that can be made later
- Mark provisional decisions that may need revision
Configuration
PDR_DRAFTS_DIR—{REPO_ROOT}/.adlc/drafts/pdrPDR_INDEX—{REPO_ROOT}/.adlc/drafts/pdr/pdr.mdPRD_FILE—{REPO_ROOT}/PRD.mdCONSTITUTION—{REPO_ROOT}/.adlc/memory/constitution.md
12-Factor Alignment
- Factor III (Mission Definition): Defines the product mission before execution
- Factor XI (Directives as Code): PDRs are version-controlled decision records
Common Rationalizations
| Rationalization | Reality |
|---|---|
| "Let's just write the PRD directly." | PRDs without PDRs lack traceable rationale. Decisions become undocumented assumptions. |
| "We already know what to build." | Even "obvious" decisions have alternatives. Documenting them prevents future reversal. |
| "Exploration takes too long." | A 10-minute discussion now prevents weeks of rework later. |
Red Flags
- Generating PRD before PDRs are accepted —
/product-implementrequires Accepted status; Proposed PDRs will be skipped. - Skipping the constitution check — misaligned decisions propagate into the PRD and become expensive to fix.
- No alternatives documented — a PDR without alternatives is a statement, not a decision.
Verification
- Setup script returns valid JSON with all paths
-
.adlc/drafts/pdr/directory exists - At least one
PDR-*.mdfile created with status "Proposed" -
pdr.mdindex auto-generated with correct table - Constitution alignment checked (if constitution exists)
- Cross-feature-area conflicts flagged
- No duplicate PDR IDs
- Each PDR has at least 2 alternatives documented
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-specify.sh 1.2 KB
#!/bin/bash # product-specify setup script set -euo pipefail JSON_MODE=false for arg in "$@"; do case "$arg" in --json) JSON_MODE=true ;; esac done 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" PRD_FILE="$REPO_ROOT/PRD.md" mkdir -p "$PDR_DRAFTS_DIR" next_pdr_number() { local max=0 if [[ -d "$PDR_DRAFTS_DIR" ]]; then for f in "$PDR_DRAFTS_DIR"/PDR-*.md; do if [[ -f "$f" ]]; then local num; num=$(basename "$f" | sed 's/PDR-//' | sed 's/\.md//') if [[ "$num" =~ ^[0-9]+$ ]]; then ((10#$num > max)) && max=$((10#$num)); fi fi done fi printf '%03d' $((max + 1)) } NEXT_PDR=$(next_pdr_number) PDR_COUNT=$(find "$PDR_DRAFTS_DIR" -name 'PDR-*.md' 2>/dev/null | wc -l) if $JSON_MODE; then cat <<EOF {"REPO_ROOT":"$REPO_ROOT","PDR_DRAFTS_DIR":"$PDR_DRAFTS_DIR","PRD_FILE":"$PRD_FILE","next_pdr":"$NEXT_PDR","pdr_count":$PDR_COUNT} EOF else echo "[INFO] product-specify setup" echo " REPO_ROOT: $REPO_ROOT" echo " PDR_DRAFTS_DIR: $PDR_DRAFTS_DIR" echo " Next PDR: PDR-$NEXT_PDR" echo " Existing PDRs: $PDR_COUNT" fi
-
-
powershell
-
pdr-lib.ps1 10.7 KB · in bundle
-
setup-product-specify.ps1 1.1 KB · in bundle
-
-
-
SKILL.md 9.5 KB
--- name: product-specify description: Use when you want guided trade-off analysis, multi-option comparison, or structured product decision facilitation before documenting. Optional for routine capture — team-boot writes lightweight PDR drafts directly. disable-model-invocation: true --- # product-specify ## What this skill does Transforms a high-level product idea into documented Product Decision Records (PDRs) through **interactive exploration** and trade-off analysis. **Key insight**: Discussion and exploration happen *before* committing to formal documentation. The goal is to surface trade-offs, validate assumptions, and make informed decisions collaboratively. **Output**: Individual `PDR-{NNN}.md` files (status **Proposed**) in `.adlc/drafts/pdr/` with an auto-generated `pdr.md` index. ## When to use > **Note**: Routine decision capture is handled by team-boot's continuous > capture mechanism, which writes lightweight drafts directly to > `.adlc/drafts/`. This skill is for **interactive deep-dive exploration** > — when you want guided trade-off analysis, multi-option comparison, or > structured decision facilitation before documenting. - New product from scratch - Major product pivots - Documenting verbal decisions formally - Team onboarding — walking through product rationale ## When NOT to use - Existing product (use `/product-init` instead) - Minor PDR updates (use `/product-clarify` instead) ## Execution Steps ### Phase 0: Environment Setup ```bash sh: scripts/bash/setup-product-specify.sh [--json] ps: scripts/powershell/setup-product-specify.ps1 ``` **Setup output** (JSON): ```json { "REPO_ROOT": "/path/to/project", "PDR_DRAFTS_DIR": "/path/to/project/.adlc/drafts/pdr", "PRD_FILE": "/path/to/project/PRD.md", "next_pdr": "001" } ``` ### Phase 1: Feature-Area Decomposition (Optional) Analyze the product for distinct business domains. Auto-decompose if multiple domains detected. Use `--no-decompose` to skip. **Present detected areas**: ```markdown ## Detected Feature Areas | # | Feature Area | Key Domains | Rationale | |---|--------------|-------------|-----------| | 1 | **Auth** | Authentication, Authorization | Core user entry | | 2 | **Core** | User Management, Profiles | Core data | | 3 | **Business** | Payments, Subscriptions | Revenue domain | Reply: Y to confirm, n for monolithic, or suggest changes. ``` **Threshold**: - ≤3 areas: Auto-approve - 4-6 areas: Confirm with user - >6 areas: Suggest grouping ### Phase 2: Product Analysis Extract product drivers: 1. **Problem Drivers**: Core problem, who experiences it, current workarounds 2. **Market Drivers**: Target segments, competitive landscape, trends 3. **Business Drivers**: Revenue model, scaling expectations, strategic importance 4. **Constraint Drivers**: Technology mandates, budget, team skills, regulatory 5. **Load Constitution**: Read `{REPO_ROOT}/.adlc/memory/constitution.md` if exists 6. **Check Existing Docs**: Scan `README.md`, `AGENTS.md`, `CONTRIBUTING.md` for context ### Phase 3: Product Exploration (Interactive) For each major decision area, present options and facilitate discussion: **Decision areas** (5-7 key decisions): 1. Problem Scope 2. Target Personas 3. Solution Approach (build vs buy vs partner) 4. Monetization 5. Go-to-Market 6. Success Metrics **Exploration format**: ```markdown ## Product Decision: [Decision Area] **Context**: [Why this decision matters] **Options**: | Option | Description | Trade-offs | |--------|-------------|------------| | A | [Option A] | Pros: [X] / Cons: [Y] | | B | [Option B] | Pros: [X] / Cons: [Y] | **Recommended**: Option [X] **Questions**: 1. [Question about constraints] 2. [Question about trade-off priorities] ``` **Rules**: - Present **one decision area at a time** - Always provide a **recommended option** with reasoning - Ask **targeted questions** to surface hidden requirements - Skip decisions already determined by context or constitution - Limit to **5-7 key decisions** ### Phase 4: Cross-Feature-Area Pre-Analysis During exploration, watch for: | Pattern | Detection | Action | |---------|-----------|--------| | Shared Personas | Same user type in multiple areas | Note for cross-area metadata | | Priority Tensions | Areas prioritize differently | ⚠️ Flag potential conflict | | Feature Overlap | Similar features in different areas | ⚠️ Flag for consolidation | | Metric Conflicts | Same metric, different targets | ⚠️ Flag for alignment | ### Phase 5: Decision Documentation After each decision is confirmed, create a PDR file. **PDR file format** (individual file, YAML frontmatter — use the shared template): Use the template at `{REPO_ROOT}/.agents/skills/product/templates/pdr-template.md` as the canonical PDR format. The template includes YAML frontmatter (status, date, owner, category, feature-area, title) as the single source of truth for index generation, plus the standard PDR body sections (Context, Decision, Consequences, Alternatives, Links). ```markdown --- status: proposed date: YYYY-MM-DD owner: [User/AI collaboration] category: Feature feature-area: [core | business | growth | ...] title: [Decision Title] --- # PDR-[NNN]: [Decision Title] ## Context **Problem/Opportunity:** [Clear description] **Market Forces:** - [Market factor 1] - [Customer feedback] ## Decision **Decision Statement:** [Clear statement of what was decided] **Rationale:** [Why this option was chosen] ### Consequences #### Positive - [Benefit 1] #### Negative - [Trade-off 1] #### Risks - [Risk with mitigation] ### Success Metrics | Metric | Target | Measurement Method | |--------|--------|-------------------| | [Metric] | [Target] | [Method] | ### Alternatives Considered #### Option A: [Alternative Name] **Description:** [Brief description] **Trade-offs:** [Neutral comparison] ### Constitution/Vision Alignment | Principle | Alignment | Notes | |-----------|-----------|-------| | [Vision Principle] | ✅ Compliant / ⚠️ Deviation | [Explanation] | ``` ### Phase 6: Write PDR Files and Regenerate Index 1. **Number sequentially**: Start from highest existing PDR number + 1 2. **Write individual files**: `{REPO_ROOT}/.adlc/drafts/pdr/PDR-{NNN}.md` 3. **Regenerate index**: `{REPO_ROOT}/.adlc/drafts/pdr/pdr.md` **Index format**: ```markdown # Product Decision Records ## PDR Index | ID | Feature-Area | Category | Status | Date | Owner | |----|--------------|----------|--------|------|-------| | PDR-001 | System | Target Market | Proposed | 2026-03-09 | User/AI | | PDR-002 | Auth | Primary Persona | Proposed | 2026-03-09 | User/AI | --- *Individual PDR files: PDR-*.md in this directory* ``` ### Phase 7: Summary Report ```markdown ## Feature Area Decomposition Summary ### Feature Areas Identified: 3 | # | Feature Area | PDRs Created | |---|--------------|--------------| | 1 | System-Level | PDR-001: Target Market | | 2 | Auth | PDR-002: Primary Persona, PDR-003: Authentication Approach | | 3 | Business | PDR-004: Pricing Model, PDR-005: Payment Integration | ### Next Steps 1. Review PDRs with /product-clarify 2. Generate PRD.md with /product-implement ``` ## PDR Numbering Rules - Scan `{REPO_ROOT}/.adlc/drafts/pdr/` for existing `PDR-*.md` files - Extract numeric suffix, find maximum - Next PDR = max + 1, zero-padded to 3 digits - Never reuse numbers ## Key Rules ### Exploration First - **Do NOT generate PRD directly** from product description - **Engage in discussion** to validate assumptions - **Surface trade-offs** before committing to decisions - **Allow iteration** — user can revisit earlier decisions ### Constitution Compliance - PDRs must **align with constitution** principles - **Flag conflicts** between product requirements and constitution - Constitution violations require explicit override with justification ### Incremental PDRs - Create **focused PDRs** — one decision per PDR - **Link related PDRs** when decisions interact - **Defer decisions** that can be made later - Mark **provisional decisions** that may need revision ## Configuration - `PDR_DRAFTS_DIR` — `{REPO_ROOT}/.adlc/drafts/pdr` - `PDR_INDEX` — `{REPO_ROOT}/.adlc/drafts/pdr/pdr.md` - `PRD_FILE` — `{REPO_ROOT}/PRD.md` - `CONSTITUTION` — `{REPO_ROOT}/.adlc/memory/constitution.md` ## 12-Factor Alignment - **Factor III (Mission Definition)**: Defines the product mission before execution - **Factor XI (Directives as Code)**: PDRs are version-controlled decision records ## Common Rationalizations | Rationalization | Reality | |-----------------|---------| | "Let's just write the PRD directly." | PRDs without PDRs lack traceable rationale. Decisions become undocumented assumptions. | | "We already know what to build." | Even "obvious" decisions have alternatives. Documenting them prevents future reversal. | | "Exploration takes too long." | A 10-minute discussion now prevents weeks of rework later. | ## Red Flags - **Generating PRD before PDRs are accepted** — `/product-implement` requires Accepted status; Proposed PDRs will be skipped. - **Skipping the constitution check** — misaligned decisions propagate into the PRD and become expensive to fix. - **No alternatives documented** — a PDR without alternatives is a statement, not a decision. ## Verification - [ ] Setup script returns valid JSON with all paths - [ ] `.adlc/drafts/pdr/` directory exists - [ ] At least one `PDR-*.md` file created with status "Proposed" - [ ] `pdr.md` index auto-generated with correct table - [ ] Constitution alignment checked (if constitution exists) - [ ] Cross-feature-area conflicts flagged - [ ] No duplicate PDR IDs - [ ] Each PDR has at least 2 alternatives documented
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.