Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 79 additions & 10 deletions .aod/memory/constitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ Product artifacts in docs/product/ are the strategic foundation, and project arc
- **Architecture Review**: Sound product strategy without technical consistency creates fragmented systems. The Architect ensures every technical decision aligns with project architecture, maintains system integrity, and avoids technical debt.

**Non-Negotiable Requirements**:
- ALWAYS create PRD before spec.md (use `/triad.prd`)
- ALWAYS create PRD before spec.md (use `/aod.define`)
- ALWAYS validate spec.md aligns with product vision, OKRs, and user stories
- ALWAYS get PM sign-off on spec.md before creating plan.md
- ALWAYS get Architect review on plan.md for technical decisions and architecture alignment
Expand Down Expand Up @@ -412,19 +412,19 @@ Product Manager is responsible for maintaining alignment between:
- Architect ensures technical consistency (architecture ↔ plan.md ↔ tasks.md ↔ code)

**Enforcement**:
- Use `/triad.analyze` to validate product-spec-architecture consistency
- Use `/aod.analyze` to validate product-spec-architecture consistency
- Triad workflows enforce dual sign-off (PM + Architect) before progression
- PRs without PM and Architect approval are blocked from merge
- Sign-offs are documented in artifact metadata

**Tools & Skills**:
- **product-manager agent**: Product Manager with alignment validation expertise
- **architect agent**: Architect with technical design and review expertise
- **/triad.prd**: Create PRD documents
- **/triad.specify**: Create specifications from PRD inputs
- **/triad.plan**: Create technical plans from specifications
- **/triad.tasks**: Create implementation tasks from plans
- **/triad.analyze**: Validate consistency across artifacts
- **/aod.define**: Create PRD documents
- **/aod.spec**: Create specifications from PRD inputs
- **/aod.project-plan**: Create technical plans from specifications
- **/aod.tasks**: Create implementation tasks from plans
- **/aod.analyze**: Validate consistency across artifacts

**Reference**: See `.claude/agents/product-manager.md` for PM responsibilities, `.claude/agents/architect.md` for Architect responsibilities, and `docs/standards/PRODUCT_SPEC_ALIGNMENT.md` for comprehensive alignment guide

Expand Down Expand Up @@ -454,15 +454,15 @@ Product Manager is responsible for maintaining alignment between:

0. **PM**: Analyze product need (What & Why) - read product vision, OKRs, user stories
0.5. **Architect**: Provide baseline report documenting current infrastructure state
1. **PM**: Draft PRD via `/triad.prd`, incorporating Architect baseline into "Current State" section
1. **PM**: Draft PRD via `/aod.define`, incorporating Architect baseline into "Current State" section
2. **Tech-Lead**: Feasibility review - estimate timeline, identify agents needed, validate capacity
3. **Architect**: Technical review - validate infrastructure claims match baseline, confirm technical feasibility
4. **PM**: Finalize PRD incorporating all Triad feedback

**For Feature PRDs** (greenfield work):

0. **PM**: Analyze product need (What & Why)
1. **PM**: Draft PRD via `/triad.prd`
1. **PM**: Draft PRD via `/aod.define`
2. **[Parallel]** Architect + Tech-Lead: Review PRD for technical feasibility and timeline accuracy
3. **PM**: Finalize PRD incorporating Triad feedback

Expand Down Expand Up @@ -540,6 +540,75 @@ All PRDs MUST have:

---

## AOD Lifecycle Model

### Lifecycle Stages

The AOD Lifecycle is a single, named sequence of **5 stages** organized into **2 phases**. All work passes through these stages in order.

**Discovery Phase** (What to build):

| Stage | Purpose | Primary Command |
|-------|---------|-----------------|
| **Discover** | Capture ideas, score with ICE, gather evidence | `/aod.discover` |
| **Define** | Create PRD with Triad validation | `/aod.define` |

**Delivery Phase** (How to build and ship):

| Stage | Purpose | Primary Command |
|-------|---------|-----------------|
| **Plan** | Create spec, architecture plan, and task breakdown | `/aod.plan` |
| **Build** | Implement tasks with Architect checkpoints | `/aod.build` |
| **Deliver** | Validate against DoD, run retrospective, close feature | `/aod.deliver` |

### Governance Gates

Governance gates are a **separate layer** from lifecycle stages. Gates are Triad approval checkpoints that operate **at stage boundaries** -- they determine who approves, not what work is done.

| Gate Location | Checkpoint | Approvers |
|---------------|------------|-----------|
| Discover exit | ICE score + PM validation | PM |
| Define exit | PRD Triad review | PM + Architect + Team-Lead |
| Plan: spec | Spec sign-off | PM |
| Plan: project-plan | Plan sign-off | PM + Architect |
| Plan: tasks | Triple sign-off | PM + Architect + Team-Lead |
| Build (continuous) | Architect checkpoints | Architect |
| Deliver exit | Definition of Done check | PM + Architect + Team-Lead |

**Invariants**:
- Triple sign-off (PM + Architect + Team-Lead on tasks.md) is the **minimum governance floor** for all tiers
- DoD check applies to **all tiers**
- Architect build checkpoints apply to **all tiers**

### Governance Tiers

Three tiers determine which gates are active. Tiers affect only the Discover, Define, and Plan stage gates.

| Tier | Discover Gate | Define Gate | Plan Gates | Build Gate | Deliver Gate |
|------|---------------|-------------|------------|------------|--------------|
| **Light** | Optional | Skip | Triple sign-off only | On | DoD |
| **Standard** (default) | On | On | PM+Arch + Triple | On | DoD |
| **Full** | On | On | PM spec + PM+Arch plan + Triple | On | DoD |

**When to use each tier**:

- **Light** (2 gates): Solo developers, prototypes, internal tools. Minimizes ceremony while preserving the governance floor (Triple sign-off + DoD).
- **Standard** (6 gates, default): Team projects and production features. All Discover, Define, and Plan gates active with Architect checkpoints in Build.
- **Full** (all gates): Regulated industries, critical systems, high-risk deployments. Adds a separate PM spec sign-off in the Plan stage for maximum traceability.

### Governance Configuration

Configure the active governance tier in the constitution frontmatter or project configuration:

```yaml
governance:
tier: standard # valid values: light | standard | full
```

The tier is configured **per project**, not per feature. The default tier is `standard`.

---

## System Architecture Constraints

### Backend Requirements
Expand Down Expand Up @@ -628,7 +697,7 @@ All PRDs MUST have:

- All pull requests MUST verify compliance with constitution principles
- Architecture decisions MUST reference relevant principles
- Use `/triad.analyze` to verify consistency across spec, plan, and tasks
- Use `/aod.analyze` to verify consistency across spec, plan, and tasks
- Constitution supersedes all other practices and conventions

### Living Document
Expand Down
247 changes: 247 additions & 0 deletions .aod/scripts/bash/backlog-regenerate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
#!/usr/bin/env bash

# BACKLOG.md regeneration from GitHub Issues
#
# Queries GitHub Issues with stage:* labels and generates a grouped
# Markdown file at docs/product/_backlog/BACKLOG.md.
#
# Usage: ./backlog-regenerate.sh [--json]
#
# Options:
# --json Output summary as JSON instead of text
#
# Algorithm:
# 1. gh issue list with structured JSON fields
# 2. Warn if exactly 100 items returned (pagination limit)
# 3. Group by stage:* label
# 4. Extract stage-specific fields from issue body (defensive parsing)
# 5. Render Markdown tables per stage
# 6. Items without stage:* label → "Untracked" section
# 7. Idempotent: same GitHub state → identical output

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"
source "$SCRIPT_DIR/github-lifecycle.sh"

JSON_MODE=false
for arg in "$@"; do
case "$arg" in
--json) JSON_MODE=true ;;
esac
done

REPO_ROOT=$(get_repo_root)
BACKLOG_DIR="$REPO_ROOT/docs/product/_backlog"
BACKLOG_FILE="$BACKLOG_DIR/BACKLOG.md"

# Ensure output directory exists
mkdir -p "$BACKLOG_DIR"

# Check gh availability
if ! aod_gh_check_available; then
echo "[aod] Cannot regenerate BACKLOG.md without GitHub access." >&2
exit 0
fi

# Fetch all issues (open and closed) with stage:* labels
RAW_JSON=$(gh issue list --json number,title,labels,updatedAt,state,body --state all --limit 100 2>/dev/null) || {
echo "[aod] Warning: Failed to fetch GitHub Issues. BACKLOG.md not updated." >&2
exit 0
}

# Pagination warning (T061)
ISSUE_COUNT=$(echo "$RAW_JSON" | grep -o '"number"' | wc -l | tr -d ' ')
if [[ "$ISSUE_COUNT" -eq 100 ]]; then
echo "[aod] Warning: Backlog may be truncated (100 item limit). Consider implementing pagination or archiving old items." >&2
fi

# Helper: extract a section value from issue body (defensive, T062)
# Args: $1 = body text, $2 = section header (e.g., "## ICE Score")
# Returns: first non-empty line after header, or "—" if not found
extract_section() {
local body="$1"
local header="$2"
local value

# Find content after the header, take first non-empty line
value=$(echo "$body" | sed -n "/^${header}/,/^##/{/^${header}/d;/^##/d;/^$/d;p;}" | head -1 | sed 's/^[[:space:]]*//')

if [[ -z "$value" ]]; then
echo "—"
else
echo "$value"
fi
}

# Helper: extract field from Metadata section
# Args: $1 = body text, $2 = field name (e.g., "Source")
extract_metadata() {
local body="$1"
local field="$2"
local value

value=$(echo "$body" | grep -o "- ${field}: .*" | head -1 | sed "s/- ${field}: //")

if [[ -z "$value" ]]; then
echo "—"
else
echo "$value"
fi
}

# Generate BACKLOG.md
generate_backlog() {
local timestamp
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

cat <<HEADER
# Backlog

> Auto-generated from GitHub Issues on ${timestamp}.
> Source of truth: GitHub Issues with \`stage:*\` labels.
> Regenerate: \`/aod.status\` or \`.aod/scripts/bash/backlog-regenerate.sh\`

HEADER

# Process each stage
for stage in discover define plan build deliver; do
local label="stage:${stage}"

echo "## ${stage^}"
echo ""

# Filter issues for this stage using label name matching
local stage_issues
stage_issues=$(echo "$RAW_JSON" | python3 -c "
import json, sys
data = json.load(sys.stdin)
for issue in data:
labels = [l['name'] for l in issue.get('labels', [])]
if '${label}' in labels:
# Escape pipe chars in title for markdown table
title = issue['title'].replace('|', '\\\\|')
body = issue.get('body', '') or ''
updated = issue.get('updatedAt', '—')[:10]
number = issue['number']
print(f'{number}|{title}|{updated}|{body}')
" 2>/dev/null) || stage_issues=""

# Stage-specific table headers
case "$stage" in
discover)
echo "| # | Title | ICE | Evidence | Updated |"
echo "|---|-------|-----|----------|---------|"
;;
define)
echo "| # | Title | PRD | Updated |"
echo "|---|-------|-----|---------|"
;;
plan)
echo "| # | Title | Spec | Plan | Tasks | Updated |"
echo "|---|-------|------|------|-------|---------|"
;;
build)
echo "| # | Title | Progress | Updated |"
echo "|---|-------|----------|---------|"
;;
deliver)
echo "| # | Title | Delivered | Retro | Updated |"
echo "|---|-------|-----------|-------|---------|"
;;
esac

if [[ -z "$stage_issues" ]]; then
echo "| — | *No items in this stage* | | |"
else
while IFS='|' read -r num title updated body; do
case "$stage" in
discover)
local ice
ice=$(extract_section "$body" "## ICE Score")
local evidence
evidence=$(extract_section "$body" "## Evidence")
# Truncate evidence for table display
if [[ ${#evidence} -gt 60 ]]; then
evidence="${evidence:0:57}..."
fi
echo "| #${num} | ${title} | ${ice} | ${evidence} | ${updated} |"
;;
define)
local prd
prd=$(extract_metadata "$body" "PRD")
echo "| #${num} | ${title} | ${prd} | ${updated} |"
;;
plan)
echo "| #${num} | ${title} | — | — | — | ${updated} |"
;;
build)
echo "| #${num} | ${title} | In progress | ${updated} |"
;;
deliver)
echo "| #${num} | ${title} | ${updated} | — | ${updated} |"
;;
esac
done <<< "$stage_issues"
fi

echo ""
done

# Untracked section: issues without any stage:* label
local untracked
untracked=$(echo "$RAW_JSON" | python3 -c "
import json, sys
data = json.load(sys.stdin)
stage_labels = {'stage:discover', 'stage:define', 'stage:plan', 'stage:build', 'stage:deliver'}
for issue in data:
labels = {l['name'] for l in issue.get('labels', [])}
if not labels.intersection(stage_labels):
title = issue['title'].replace('|', '\\\\|')
updated = issue.get('updatedAt', '—')[:10]
number = issue['number']
state = issue.get('state', 'OPEN')
print(f'{number}|{title}|{state}|{updated}')
" 2>/dev/null) || untracked=""

if [[ -n "$untracked" ]]; then
echo "## Untracked"
echo ""
echo "> These issues have no \`stage:*\` label. Add a label to track them in the lifecycle."
echo ""
echo "| # | Title | State | Updated |"
echo "|---|-------|-------|---------|"
while IFS='|' read -r num title state updated; do
echo "| #${num} | ${title} | ${state} | ${updated} |"
done <<< "$untracked"
echo ""
fi
}

# Write to file
OUTPUT=$(generate_backlog)
echo "$OUTPUT" > "$BACKLOG_FILE"

# Summary
if $JSON_MODE; then
# Count items per stage
python3 -c "
import json, sys
data = json.load(sys.stdin)
counts = {'discover': 0, 'define': 0, 'plan': 0, 'build': 0, 'deliver': 0, 'untracked': 0}
stage_labels = {'stage:discover', 'stage:define', 'stage:plan', 'stage:build', 'stage:deliver'}
for issue in data:
labels = {l['name'] for l in issue.get('labels', [])}
matched = labels.intersection(stage_labels)
if matched:
stage = list(matched)[0].split(':')[1]
counts[stage] += 1
else:
counts['untracked'] += 1
print(json.dumps({'file': '${BACKLOG_FILE}', 'total': len(data), 'stages': counts}))
" <<< "$RAW_JSON"
else
echo "[aod] BACKLOG.md regenerated at ${BACKLOG_FILE}"
echo "[aod] Total issues: ${ISSUE_COUNT}"
fi
Loading