adding local testing and removing step 2

morgankrey created

Change summary

.factory/prompts/docs-automation/phase2-explore.md |  55 -
.github/workflows/docs_automation.yml              | 107 +
docs/AGENTS.md                                     |   4 
script/test-docs-automation                        | 588 ++++++++++++++++
4 files changed, 665 insertions(+), 89 deletions(-)

Detailed changes

.factory/prompts/docs-automation/phase2-explore.md 🔗

@@ -1,55 +0,0 @@
-# Phase 2: Explore Repository
-
-You are analyzing a codebase to understand its structure before reviewing documentation impact.
-
-## Objective
-Produce a structured overview of the repository to inform subsequent documentation analysis.
-
-## Instructions
-
-1. **Identify Primary Languages and Frameworks**
-   - Scan for Cargo.toml, package.json, or other manifest files
-   - Note the primary language(s) and key dependencies
-
-2. **Map Documentation Structure**
-   - This project uses **mdBook** (https://rust-lang.github.io/mdBook/)
-   - Documentation is in `docs/src/`
-   - Table of contents: `docs/src/SUMMARY.md` (mdBook format: https://rust-lang.github.io/mdBook/format/summary.html)
-   - Style guide: `docs/.rules`
-   - Agent guidelines: `docs/AGENTS.md`
-   - Formatting: Prettier (config in `docs/.prettierrc`)
-
-3. **Identify Build and Tooling**
-   - Note build systems (cargo, npm, etc.)
-   - Identify documentation tooling (mdbook, etc.)
-
-4. **Output Format**
-Produce a JSON summary:
-
-```json
-{
-  "primary_language": "Rust",
-  "frameworks": ["GPUI"],
-  "documentation": {
-    "system": "mdBook",
-    "location": "docs/src/",
-    "toc_file": "docs/src/SUMMARY.md",
-    "toc_format": "https://rust-lang.github.io/mdBook/format/summary.html",
-    "style_guide": "docs/.rules",
-    "agent_guidelines": "docs/AGENTS.md",
-    "formatter": "prettier",
-    "formatter_config": "docs/.prettierrc",
-    "custom_preprocessor": "docs_preprocessor (handles {#kb action::Name} syntax)"
-  },
-  "key_directories": {
-    "source": "crates/",
-    "docs": "docs/src/",
-    "extensions": "extensions/"
-  }
-}
-```
-
-## Constraints
-- Read-only: Do not modify any files
-- Focus on structure, not content details
-- Complete within 2 minutes

.github/workflows/docs_automation.yml 🔗

@@ -83,19 +83,6 @@ jobs:
         env:
           GH_TOKEN: ${{ github.token }}
 
-      # Phase 0: Guardrails are loaded via AGENTS.md in each phase
-
-      # Phase 2: Explore Repository (Read-Only - default)
-      - name: "Phase 2: Explore Repository"
-        id: phase2
-        run: |
-          "$DROID_BIN" exec \
-            -m "$DROID_MODEL" \
-            -f .factory/prompts/docs-automation/phase2-explore.md \
-            > /tmp/phase2-output.txt 2>&1 || true
-          echo "Repository exploration complete"
-          cat /tmp/phase2-output.txt
-
       # Phase 3: Analyze Changes (Read-Only - default)
       - name: "Phase 3: Analyze Changes"
         id: phase3
@@ -103,19 +90,6 @@ jobs:
           CHANGED_FILES=$(tr '\n' ' ' < /tmp/changed_files.txt)
           echo "Analyzing changes in: $CHANGED_FILES"
 
-          # Build prompt with context
-          cat > /tmp/phase3-prompt.md << 'EOF'
-          $(cat .factory/prompts/docs-automation/phase3-analyze.md)
-
-          ## Context
-
-          ### Changed Files
-          $CHANGED_FILES
-
-          ### Phase 2 Output
-          $(cat /tmp/phase2-output.txt)
-          EOF
-
           "$DROID_BIN" exec \
             -m "$DROID_MODEL" \
             "$(cat .factory/prompts/docs-automation/phase3-analyze.md)
@@ -129,9 +103,20 @@ jobs:
       - name: "Phase 4: Plan Documentation Impact"
         id: phase4
         run: |
+          CHANGED_FILES=$(tr '\n' ' ' < /tmp/changed_files.txt)
+          PHASE3_OUTPUT=$(cat /tmp/phase3-output.md)
+          
           "$DROID_BIN" exec \
             -m "$DROID_MODEL" \
-            -f .factory/prompts/docs-automation/phase4-plan.md \
+            "$(cat .factory/prompts/docs-automation/phase4-plan.md)
+
+            ## Context from Phase 3
+
+            ### Changed Files
+            $CHANGED_FILES
+
+            ### Phase 3 Analysis
+            $PHASE3_OUTPUT" \
             > /tmp/phase4-plan.md 2>&1 || true
           echo "Documentation plan complete"
           cat /tmp/phase4-plan.md
@@ -148,10 +133,15 @@ jobs:
         id: phase5
         if: steps.phase4.outputs.updates_required == 'true'
         run: |
+          PHASE4_PLAN=$(cat /tmp/phase4-plan.md)
+          
           "$DROID_BIN" exec \
             -m "$DROID_MODEL" \
             --auto medium \
-            -f .factory/prompts/docs-automation/phase5-apply.md \
+            "$(cat .factory/prompts/docs-automation/phase5-apply.md)
+
+            ## Documentation Plan from Phase 4
+            $PHASE4_PLAN" \
             > /tmp/phase5-report.md 2>&1 || true
           echo "Documentation updates applied"
           cat /tmp/phase5-report.md
@@ -176,10 +166,23 @@ jobs:
         run: |
           # Get git diff of docs
           git diff docs/src/ > /tmp/docs-diff.txt || true
+          
+          PHASE5_REPORT=$(cat /tmp/phase5-report.md)
+          DOCS_DIFF=$(cat /tmp/docs-diff.txt)
 
           "$DROID_BIN" exec \
             -m "$DROID_MODEL" \
-            -f .factory/prompts/docs-automation/phase6-summarize.md \
+            "$(cat .factory/prompts/docs-automation/phase6-summarize.md)
+
+            ## Context
+
+            ### Phase 5 Report
+            $PHASE5_REPORT
+
+            ### Documentation Diff
+            \`\`\`diff
+            $DOCS_DIFF
+            \`\`\`" \
             > /tmp/phase6-summary.md 2>&1 || true
           echo "Summary generated"
           cat /tmp/phase6-summary.md
@@ -202,6 +205,21 @@ jobs:
           # Daily batch branch - one branch per day, multiple commits accumulate
           BRANCH_NAME="docs/auto-update-$(date +%Y-%m-%d)"
 
+          # Get source PR info for attribution
+          SOURCE_PR_INFO=""
+          if [ "${{ steps.changed.outputs.source }}" == "pr" ]; then
+            PR_NUM="${{ steps.changed.outputs.ref }}"
+            PR_DETAILS=$(gh pr view "$PR_NUM" --json title,author,url 2>/dev/null || echo "{}")
+            SOURCE_TITLE=$(echo "$PR_DETAILS" | jq -r '.title // "Unknown"')
+            SOURCE_AUTHOR=$(echo "$PR_DETAILS" | jq -r '.author.login // "Unknown"')
+            SOURCE_URL=$(echo "$PR_DETAILS" | jq -r '.url // ""')
+            SOURCE_PR_INFO="
+          ---
+          **Source**: [#$PR_NUM]($SOURCE_URL) - $SOURCE_TITLE
+          **Author**: @$SOURCE_AUTHOR
+          "
+          fi
+
           # Stash local changes from phase 5
           git stash push -m "docs-automation-changes" -- docs/src/
 
@@ -232,16 +250,37 @@ jobs:
           # Push
           git push -u origin "$BRANCH_NAME"
 
-          # Check if PR already exists for this branch
-          EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number' || echo "")
+          # Build the PR body section for this update
+          PR_BODY_SECTION="## Update from $(date '+%Y-%m-%d %H:%M')
+          $SOURCE_PR_INFO
+          $(cat /tmp/phase6-summary.md)
+          "
 
-          if [ -n "$EXISTING_PR" ]; then
-            echo "PR #$EXISTING_PR already exists for branch $BRANCH_NAME, updated with new commit"
+          # Check if PR already exists for this branch
+          EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number,url,body --jq '.[0]' || echo "")
+
+          if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
+            PR_NUM=$(echo "$EXISTING_PR" | jq -r '.number')
+            PR_URL=$(echo "$EXISTING_PR" | jq -r '.url')
+            EXISTING_BODY=$(echo "$EXISTING_PR" | jq -r '.body // ""')
+            
+            # Append new summary to existing PR body
+            NEW_BODY="${EXISTING_BODY}
+
+          ---
+
+          ${PR_BODY_SECTION}"
+            
+            echo "$NEW_BODY" > /tmp/updated-pr-body.md
+            gh pr edit "$PR_NUM" --body-file /tmp/updated-pr-body.md
+            
+            echo "PR #$PR_NUM updated: $PR_URL"
           else
             # Create new PR
+            echo "$PR_BODY_SECTION" > /tmp/new-pr-body.md
             gh pr create \
               --title "docs: automated documentation update ($(date +%Y-%m-%d))" \
-              --body-file /tmp/phase6-summary.md \
+              --body-file /tmp/new-pr-body.md \
               --base main || true
             echo "PR created on branch: $BRANCH_NAME"
           fi

docs/AGENTS.md 🔗

@@ -2,6 +2,10 @@
 
 This file governs automated documentation updates triggered by code changes. All automation phases must comply with these rules.
 
+## Repository Context
+
+This is the **Zed code editor** repository, a Rust-based application using the custom **GPUI** UI framework. The project is a large monorepo with ~200 crates organized under `crates/`. Documentation is built with **mdBook** and uses a custom preprocessor (`docs_preprocessor`) that handles special syntax like `{#kb action::Name}` for keybindings. The documentation source is in `docs/src/` with a table of contents in `SUMMARY.md`, and all docs must pass Prettier formatting (80 char line width). The style guide (`docs/.rules`) and agent guidelines (`docs/AGENTS.md`) provide specific conventions for documentation writing.
+
 ## Documentation System
 
 This documentation uses **mdBook** (https://rust-lang.github.io/mdBook/).

script/test-docs-automation 🔗

@@ -0,0 +1,588 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+PROMPTS_DIR="$REPO_ROOT/.factory/prompts/docs-automation"
+OUTPUT_DIR="${TMPDIR:-/tmp}/docs-automation-test"
+
+# Default values
+BASE_BRANCH="main"
+DROID_MODEL="${DROID_MODEL:-claude-opus-4-5-20251101}"
+DRY_RUN=false
+VERBOSE=false
+PR_NUMBER=""
+SOURCE_BRANCH=""
+
+usage() {
+    cat << EOF
+Usage: $(basename "$0") [OPTIONS]
+
+Test the documentation automation workflow locally.
+
+OPTIONS:
+    -p, --pr NUMBER      PR number to analyze (uses gh pr diff)
+    -r, --branch BRANCH  Remote branch to compare (e.g., origin/feature-branch)
+    -b, --base BRANCH    Base branch to compare against (default: main)
+    -m, --model MODEL    Droid model to use (default: $DROID_MODEL)
+    -d, --dry-run        Run phases 2-4 only (no file modifications)
+    -s, --skip-apply     Alias for --dry-run
+    -v, --verbose        Show full output from each phase
+    -o, --output DIR     Output directory for phase artifacts (default: $OUTPUT_DIR)
+    -h, --help           Show this help message
+
+EXAMPLES:
+    # Analyze a PR (most common use case)
+    $(basename "$0") --pr 12345
+
+    # Analyze a PR with dry run (no file changes)
+    $(basename "$0") --pr 12345 --dry-run
+
+    # Analyze a remote branch against main
+    $(basename "$0") --branch origin/feature-branch
+
+    # Test current branch against main
+    $(basename "$0")
+
+    # Use a different model
+    $(basename "$0") --pr 12345 --model claude-sonnet-4-20250514
+
+ENVIRONMENT:
+    FACTORY_API_KEY      Required: Your Factory API key
+    DROID_MODEL          Override default model
+    GH_TOKEN             Required for --pr option (or gh auth login)
+
+EOF
+    exit 0
+}
+
+while [[ $# -gt 0 ]]; do
+    case $1 in
+        -p|--pr)
+            PR_NUMBER="$2"
+            shift 2
+            ;;
+        -r|--branch)
+            SOURCE_BRANCH="$2"
+            shift 2
+            ;;
+        -b|--base)
+            BASE_BRANCH="$2"
+            shift 2
+            ;;
+        -m|--model)
+            DROID_MODEL="$2"
+            shift 2
+            ;;
+        -d|--dry-run|-s|--skip-apply)
+            DRY_RUN=true
+            shift
+            ;;
+        -v|--verbose)
+            VERBOSE=true
+            shift
+            ;;
+        -o|--output)
+            OUTPUT_DIR="$2"
+            shift 2
+            ;;
+        -h|--help)
+            usage
+            ;;
+        *)
+            echo "Unknown option: $1"
+            usage
+            ;;
+    esac
+done
+
+# Cleanup function for restoring original branch
+cleanup_on_exit() {
+    if [[ -f "$OUTPUT_DIR/original-branch.txt" ]]; then
+        ORIGINAL_BRANCH=$(cat "$OUTPUT_DIR/original-branch.txt")
+        CURRENT=$(git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
+        if [[ "$CURRENT" != "$ORIGINAL_BRANCH" && -n "$ORIGINAL_BRANCH" ]]; then
+            echo ""
+            echo "Restoring original branch: $ORIGINAL_BRANCH"
+            git -C "$REPO_ROOT" checkout "$ORIGINAL_BRANCH" 2>/dev/null || true
+            if [[ "$CURRENT" == temp-analysis-* ]]; then
+                git -C "$REPO_ROOT" branch -D "$CURRENT" 2>/dev/null || true
+            fi
+        fi
+    fi
+}
+trap cleanup_on_exit EXIT
+
+# Check for required tools
+if ! command -v droid &> /dev/null; then
+    echo "Error: droid CLI not found. Install from https://app.factory.ai/cli"
+    exit 1
+fi
+
+if [[ -z "${FACTORY_API_KEY:-}" ]]; then
+    echo "Error: FACTORY_API_KEY environment variable is not set"
+    exit 1
+fi
+
+# Check gh CLI if PR mode
+if [[ -n "$PR_NUMBER" ]]; then
+    if ! command -v gh &> /dev/null; then
+        echo "Error: gh CLI not found. Install from https://cli.github.com/"
+        echo "Required for --pr option"
+        exit 1
+    fi
+fi
+
+# Create output directory
+mkdir -p "$OUTPUT_DIR"
+echo "Output directory: $OUTPUT_DIR"
+echo "Model: $DROID_MODEL"
+echo ""
+
+cd "$REPO_ROOT"
+
+# Copy prompts to output directory BEFORE any branch checkout
+# This ensures we have access to prompts even if PR branch doesn't have them
+if [[ -d "$PROMPTS_DIR" ]]; then
+    cp -r "$PROMPTS_DIR" "$OUTPUT_DIR/prompts"
+    PROMPTS_DIR="$OUTPUT_DIR/prompts"
+    echo "Prompts copied to: $PROMPTS_DIR"
+else
+    echo "Error: Prompts directory not found: $PROMPTS_DIR"
+    exit 1
+fi
+echo ""
+
+# Get changed files based on mode
+echo "=== Getting changed files ==="
+
+if [[ -n "$PR_NUMBER" ]]; then
+    # PR mode: use gh pr diff like the workflow does
+    echo "Analyzing PR #$PR_NUMBER"
+    
+    # Get PR info for context
+    echo "Fetching PR details..."
+    gh pr view "$PR_NUMBER" --json title,headRefName,baseRefName,state > "$OUTPUT_DIR/pr-info.json" 2>/dev/null || true
+    if [[ -f "$OUTPUT_DIR/pr-info.json" ]]; then
+        PR_TITLE=$(jq -r '.title // "Unknown"' "$OUTPUT_DIR/pr-info.json")
+        PR_HEAD=$(jq -r '.headRefName // "Unknown"' "$OUTPUT_DIR/pr-info.json")
+        PR_BASE=$(jq -r '.baseRefName // "Unknown"' "$OUTPUT_DIR/pr-info.json")
+        PR_STATE=$(jq -r '.state // "Unknown"' "$OUTPUT_DIR/pr-info.json")
+        echo "  Title: $PR_TITLE"
+        echo "  Branch: $PR_HEAD -> $PR_BASE"
+        echo "  State: $PR_STATE"
+    fi
+    echo ""
+    
+    # Get the list of changed files
+    gh pr diff "$PR_NUMBER" --name-only > "$OUTPUT_DIR/changed_files.txt"
+    
+    # Also save the full diff for analysis
+    gh pr diff "$PR_NUMBER" > "$OUTPUT_DIR/pr-diff.patch" 2>/dev/null || true
+    
+    # Checkout the PR branch to have the code available for analysis
+    echo "Checking out PR branch for analysis..."
+    ORIGINAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+    echo "$ORIGINAL_BRANCH" > "$OUTPUT_DIR/original-branch.txt"
+    
+    gh pr checkout "$PR_NUMBER" --force 2>/dev/null || {
+        echo "Warning: Could not checkout PR branch. Analysis will use current branch state."
+    }
+
+elif [[ -n "$SOURCE_BRANCH" ]]; then
+    # Remote branch mode
+    echo "Analyzing branch: $SOURCE_BRANCH"
+    echo "Base branch: $BASE_BRANCH"
+    
+    # Fetch the branches
+    git fetch origin 2>/dev/null || true
+    
+    # Resolve branch refs
+    SOURCE_REF="$SOURCE_BRANCH"
+    BASE_REF="origin/$BASE_BRANCH"
+    
+    # Get merge base
+    MERGE_BASE=$(git merge-base "$BASE_REF" "$SOURCE_REF" 2>/dev/null) || {
+        echo "Error: Could not find merge base between $BASE_REF and $SOURCE_REF"
+        exit 1
+    }
+    echo "Merge base: $MERGE_BASE"
+    
+    # Get changed files
+    git diff --name-only "$MERGE_BASE" "$SOURCE_REF" > "$OUTPUT_DIR/changed_files.txt"
+    
+    # Checkout the source branch for analysis
+    echo "Checking out $SOURCE_BRANCH for analysis..."
+    ORIGINAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+    echo "$ORIGINAL_BRANCH" > "$OUTPUT_DIR/original-branch.txt"
+    
+    git checkout "$SOURCE_BRANCH" 2>/dev/null || git checkout -b "temp-analysis-$$" "$SOURCE_REF" || {
+        echo "Warning: Could not checkout branch. Analysis will use current branch state."
+    }
+
+else
+    # Current branch mode (original behavior)
+    CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+    echo "Analyzing current branch: $CURRENT_BRANCH"
+    echo "Base branch: $BASE_BRANCH"
+    
+    # Fetch the base branch
+    git fetch origin "$BASE_BRANCH" 2>/dev/null || true
+    
+    # Get merge base
+    MERGE_BASE=$(git merge-base "origin/$BASE_BRANCH" HEAD 2>/dev/null || git merge-base "$BASE_BRANCH" HEAD)
+    echo "Merge base: $MERGE_BASE"
+    
+    git diff --name-only "$MERGE_BASE" HEAD > "$OUTPUT_DIR/changed_files.txt"
+fi
+
+if [[ ! -s "$OUTPUT_DIR/changed_files.txt" ]]; then
+    echo "No changed files found."
+    exit 0
+fi
+
+echo ""
+echo "Changed files ($(wc -l < "$OUTPUT_DIR/changed_files.txt" | tr -d ' ') files):"
+cat "$OUTPUT_DIR/changed_files.txt"
+echo ""
+
+# Phase 3: Analyze Changes
+echo "=== Phase 3: Analyze Changes ==="
+CHANGED_FILES=$(tr '\n' ' ' < "$OUTPUT_DIR/changed_files.txt")
+
+droid exec \
+    -m "$DROID_MODEL" \
+    --auto low \
+    "$(cat "$PROMPTS_DIR/phase3-analyze.md")
+
+    Changed files: $CHANGED_FILES" \
+    > "$OUTPUT_DIR/phase3-output.md" 2>&1 || true
+
+if [[ "$VERBOSE" == "true" ]]; then
+    cat "$OUTPUT_DIR/phase3-output.md"
+else
+    echo "Output saved to: $OUTPUT_DIR/phase3-output.md"
+fi
+echo ""
+
+# Phase 4: Plan Documentation Impact
+echo "=== Phase 4: Plan Documentation Impact ==="
+PHASE3_OUTPUT=$(cat "$OUTPUT_DIR/phase3-output.md")
+
+droid exec \
+    -m "$DROID_MODEL" \
+    --auto low \
+    "$(cat "$PROMPTS_DIR/phase4-plan.md")
+
+## Context from Phase 3
+
+### Changed Files
+$CHANGED_FILES
+
+### Phase 3 Analysis
+$PHASE3_OUTPUT" \
+    > "$OUTPUT_DIR/phase4-plan.md" 2>&1 || true
+
+if [[ "$VERBOSE" == "true" ]]; then
+    cat "$OUTPUT_DIR/phase4-plan.md"
+else
+    echo "Output saved to: $OUTPUT_DIR/phase4-plan.md"
+fi
+echo ""
+
+# Check if updates are required
+if grep -q "NO_UPDATES_REQUIRED" "$OUTPUT_DIR/phase4-plan.md" || \
+   grep -qi "Documentation Updates Required: No" "$OUTPUT_DIR/phase4-plan.md"; then
+    echo "=== No documentation updates required ==="
+    echo "Phase 4 determined no documentation changes are needed."
+    exit 0
+fi
+
+if [[ "$DRY_RUN" == "true" ]]; then
+    echo "=== Phase 5 (Preview): Generate Proposed Changes ==="
+    PHASE4_PLAN=$(cat "$OUTPUT_DIR/phase4-plan.md")
+    
+    droid exec \
+        -m "$DROID_MODEL" \
+        --auto low \
+        "CRITICAL INSTRUCTION: You MUST output the ACTUAL markdown content, not a summary.
+
+Your task: Generate a preview showing EXACTLY what documentation changes would be made.
+
+REQUIRED STEPS:
+1. Read the file docs/src/ai/edit-prediction.md using the Read tool
+2. Find the Codestral section (### Codestral {#codestral})
+3. Output the EXACT current text and your EXACT proposed replacement
+
+YOUR OUTPUT MUST FOLLOW THIS EXACT FORMAT (no deviations, no summaries):
+
+---
+## File: docs/src/ai/edit-prediction.md
+
+### CURRENT CONTENT (copy exactly from file):
+\`\`\`markdown
+### Codestral {#codestral}
+
+[PASTE THE EXACT CURRENT CONTENT OF THIS SECTION HERE - EVERY LINE]
+\`\`\`
+
+### PROPOSED CONTENT (your changes):
+\`\`\`markdown
+### Codestral {#codestral}
+
+[WRITE YOUR COMPLETE REPLACEMENT FOR THIS SECTION HERE - EVERY LINE]
+\`\`\`
+
+### UNIFIED DIFF:
+\`\`\`diff
+[Generate a unified diff showing - lines removed and + lines added]
+\`\`\`
+
+### Why this change:
+[One sentence explanation]
+---
+
+DO NOT:
+- Summarize the changes
+- Skip reading the actual file
+- Provide abbreviated content
+- Say 'the section that...' - show the ACTUAL text
+
+Context for what changed in the codebase:
+$PHASE3_OUTPUT
+
+Documentation plan:
+$PHASE4_PLAN" \
+        > "$OUTPUT_DIR/phase5-preview.md" 2>&1 || true
+    
+    echo "Preview saved to: $OUTPUT_DIR/phase5-preview.md"
+    echo ""
+    echo "=== Preview Content ==="
+    cat "$OUTPUT_DIR/phase5-preview.md"
+    
+    echo ""
+    echo "=== Dry run complete ==="
+    echo "To apply changes for real, run without --dry-run flag."
+    echo ""
+    echo "All outputs saved to: $OUTPUT_DIR/"
+    exit 0
+fi
+
+# Phase 5: Apply Documentation Plan
+echo "=== Phase 5: Apply Documentation Plan ==="
+PHASE4_PLAN=$(cat "$OUTPUT_DIR/phase4-plan.md")
+
+droid exec \
+    -m "$DROID_MODEL" \
+    --auto medium \
+    "$(cat "$PROMPTS_DIR/phase5-apply.md")
+
+## Documentation Plan from Phase 4
+$PHASE4_PLAN" \
+    > "$OUTPUT_DIR/phase5-report.md" 2>&1 || true
+
+if [[ "$VERBOSE" == "true" ]]; then
+    cat "$OUTPUT_DIR/phase5-report.md"
+else
+    echo "Output saved to: $OUTPUT_DIR/phase5-report.md"
+fi
+echo ""
+
+# Phase 5b: Format with Prettier (only changed files)
+echo "=== Phase 5b: Format with Prettier ==="
+cd "$REPO_ROOT"
+
+# Get list of changed doc files (relative to docs/ directory)
+CHANGED_DOCS=$(git diff --name-only docs/src/ 2>/dev/null | sed 's|^docs/||' | tr '\n' ' ')
+
+if [[ -n "$CHANGED_DOCS" ]]; then
+    echo "Formatting changed files: $CHANGED_DOCS"
+    # Use pnpm dlx like the project's prettier script does
+    if command -v pnpm &> /dev/null; then
+        (cd docs && pnpm dlx prettier@3.5.0 $CHANGED_DOCS --write) 2>/dev/null || true
+    elif command -v prettier &> /dev/null; then
+        (cd docs && prettier --write $CHANGED_DOCS) 2>/dev/null || true
+    else
+        echo "Warning: neither pnpm nor prettier found, skipping formatting"
+    fi
+    echo "Prettier formatting applied"
+else
+    echo "No changed docs files to format"
+fi
+echo ""
+
+# Phase 6: Summarize Changes
+echo "=== Phase 6: Summarize Changes ==="
+git -C "$REPO_ROOT" diff docs/src/ > "$OUTPUT_DIR/docs-diff.txt" 2>/dev/null || true
+
+PHASE5_REPORT=$(cat "$OUTPUT_DIR/phase5-report.md")
+DOCS_DIFF=$(cat "$OUTPUT_DIR/docs-diff.txt")
+
+droid exec \
+    -m "$DROID_MODEL" \
+    --auto low \
+    "$(cat "$PROMPTS_DIR/phase6-summarize.md")
+
+## Context
+
+### Phase 5 Report
+$PHASE5_REPORT
+
+### Documentation Diff
+\`\`\`diff
+$DOCS_DIFF
+\`\`\`" \
+    > "$OUTPUT_DIR/phase6-summary.md" 2>&1 || true
+
+if [[ "$VERBOSE" == "true" ]]; then
+    cat "$OUTPUT_DIR/phase6-summary.md"
+else
+    echo "Output saved to: $OUTPUT_DIR/phase6-summary.md"
+fi
+echo ""
+
+# Phase 7: Create Branch and PR
+echo "=== Phase 7: Create Branch and PR ==="
+
+# Check if there are actual changes
+if git -C "$REPO_ROOT" diff --quiet docs/src/; then
+    echo "No documentation changes detected after Phase 5"
+    echo ""
+    echo "=== Test Complete (no changes to commit) ==="
+    exit 0
+fi
+
+# Check if gh CLI is available
+if ! command -v gh &> /dev/null; then
+    echo "Warning: gh CLI not found. Skipping PR creation."
+    echo "Install from https://cli.github.com/ to enable automatic PR creation."
+    echo ""
+    echo "Documentation changes (git status):"
+    git -C "$REPO_ROOT" status --short docs/src/
+    echo ""
+    echo "To review the diff:"
+    echo "  git diff docs/src/"
+    echo ""
+    echo "To discard changes:"
+    echo "  git checkout docs/src/"
+    exit 0
+fi
+
+cd "$REPO_ROOT"
+
+# Daily batch branch - one branch per day, multiple commits accumulate
+BRANCH_NAME="docs/auto-update-$(date +%Y-%m-%d)"
+
+# Stash local changes from phase 5
+echo "Stashing documentation changes..."
+git stash push -m "docs-automation-changes" -- docs/src/
+
+# Check if branch already exists on remote
+if git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then
+    echo "Branch $BRANCH_NAME exists, checking out and updating..."
+    git fetch origin "$BRANCH_NAME"
+    git checkout -B "$BRANCH_NAME" "origin/$BRANCH_NAME"
+else
+    echo "Creating new branch $BRANCH_NAME from main..."
+    git fetch origin main
+    git checkout -B "$BRANCH_NAME" origin/main
+fi
+
+# Apply stashed changes
+echo "Applying documentation changes..."
+git stash pop || true
+
+# Stage and commit
+git add docs/src/
+
+# Get source PR info for attribution
+SOURCE_PR_INFO=""
+TRIGGER_INFO=""
+if [[ -n "$PR_NUMBER" ]]; then
+    # Fetch PR details: title, author, url
+    PR_DETAILS=$(gh pr view "$PR_NUMBER" --json title,author,url 2>/dev/null || echo "{}")
+    SOURCE_TITLE=$(echo "$PR_DETAILS" | jq -r '.title // "Unknown"')
+    SOURCE_AUTHOR=$(echo "$PR_DETAILS" | jq -r '.author.login // "Unknown"')
+    SOURCE_URL=$(echo "$PR_DETAILS" | jq -r '.url // ""')
+    
+    TRIGGER_INFO="Triggered by: PR #$PR_NUMBER"
+    SOURCE_PR_INFO="
+---
+**Source**: [#$PR_NUMBER]($SOURCE_URL) - $SOURCE_TITLE
+**Author**: @$SOURCE_AUTHOR
+"
+elif [[ -n "$SOURCE_BRANCH" ]]; then
+    TRIGGER_INFO="Triggered by: branch $SOURCE_BRANCH"
+    SOURCE_PR_INFO="
+---
+**Source**: Branch \`$SOURCE_BRANCH\`
+"
+fi
+
+# Build commit message
+SUMMARY=$(head -50 < "$OUTPUT_DIR/phase6-summary.md" 2>/dev/null || echo "Automated documentation update")
+
+git commit -m "docs: auto-update documentation
+
+${SUMMARY}
+
+${TRIGGER_INFO}
+
+Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>" || {
+    echo "Nothing to commit"
+    exit 0
+}
+
+# Push
+echo "Pushing to origin/$BRANCH_NAME..."
+git push -u origin "$BRANCH_NAME"
+
+# Build the PR body section for this update
+PR_BODY_SECTION="## Update from $(date '+%Y-%m-%d %H:%M')
+$SOURCE_PR_INFO
+$(cat "$OUTPUT_DIR/phase6-summary.md")
+"
+
+# Check if PR already exists for this branch
+EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number,url,body --jq '.[0]' 2>/dev/null || echo "")
+
+if [[ -n "$EXISTING_PR" && "$EXISTING_PR" != "null" ]]; then
+    PR_NUM=$(echo "$EXISTING_PR" | jq -r '.number')
+    PR_URL=$(echo "$EXISTING_PR" | jq -r '.url')
+    EXISTING_BODY=$(echo "$EXISTING_PR" | jq -r '.body // ""')
+    
+    # Append new summary to existing PR body
+    echo "Updating PR body with new summary..."
+    NEW_BODY="${EXISTING_BODY}
+
+---
+
+${PR_BODY_SECTION}"
+    
+    echo "$NEW_BODY" > "$OUTPUT_DIR/updated-pr-body.md"
+    gh pr edit "$PR_NUM" --body-file "$OUTPUT_DIR/updated-pr-body.md"
+    
+    echo ""
+    echo "=== Updated existing PR ==="
+    echo "PR #$PR_NUM: $PR_URL"
+    echo "New commit added and PR description updated."
+else
+    # Create new PR with full body
+    echo "Creating new PR..."
+    echo "$PR_BODY_SECTION" > "$OUTPUT_DIR/new-pr-body.md"
+    
+    PR_URL=$(gh pr create \
+        --title "docs: automated documentation update ($(date +%Y-%m-%d))" \
+        --body-file "$OUTPUT_DIR/new-pr-body.md" \
+        --base main 2>&1) || {
+        echo "Failed to create PR: $PR_URL"
+        exit 1
+    }
+    echo ""
+    echo "=== PR Created ==="
+    echo "$PR_URL"
+fi
+
+echo ""
+echo "=== Test Complete ==="
+echo "All phase outputs saved to: $OUTPUT_DIR/"