#!/usr/bin/env bash## Create a draft documentation PR by auto-applying batched suggestions.## Usage:# script/docs-suggest-publish [--dry-run] [--model MODEL]## This script:# 1. Reads pending suggestions from the docs/suggestions-pending branch# 2. Uses Droid to apply suggestions in batches (default 10 per batch)# 3. Runs docs formatting# 4. Validates docs build (action references, JSON schemas, links)# 5. Creates a draft PR for human review/merge# 6. Optionally resets the suggestions branch after successful PR creation## Options:# --dry-run Show what would be done without creating PR# --keep-queue Don't reset the suggestions branch after PR creation# --model MODEL Override Droid model used for auto-apply# --batch-size N Suggestions per Droid invocation (default: 10)# --skip-validation Skip the docs build validation step# --verbose Show detailed progress## Run this as part of the preview release workflow. set -euo pipefail DRY_RUN=falseKEEP_QUEUE=falseVERBOSE=falseSKIP_VALIDATION=falseBATCH_SIZE=10MODEL="${DROID_MODEL:-claude-sonnet-4-5-latest}" SUGGESTIONS_BRANCH="docs/suggestions-pending" # ColorsRED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[0;33m'BLUE='\033[0;34m'NC='\033[0m' log() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[docs-publish]${NC} $*" >&2 fi} error() { echo -e "${RED}Error:${NC} $*" >&2 exit 1} # Parse argumentswhile [[ $# -gt 0 ]]; do case $1 in --dry-run) DRY_RUN=true shift ;; --keep-queue) KEEP_QUEUE=true shift ;; --verbose) VERBOSE=true shift ;; --model) MODEL="$2" shift 2 ;; --batch-size) BATCH_SIZE="$2" shift 2 ;; --skip-validation) SKIP_VALIDATION=true shift ;; -h|--help) head -30 "$0" | tail -28 exit 0 ;; *) error "Unknown option: $1" ;; esacdone # Get repo rootREPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"cd "$REPO_ROOT" # Check if suggestions branch existslog "Checking for suggestions branch..."if ! git ls-remote --exit-code --heads origin "$SUGGESTIONS_BRANCH" > /dev/null 2>&1; then echo "No pending suggestions found (branch $SUGGESTIONS_BRANCH doesn't exist)." echo "Suggestions are queued automatically when PRs are merged to main." exit 0fi # Fetch the suggestions branchlog "Fetching suggestions branch..."git fetch origin "$SUGGESTIONS_BRANCH" # Check for manifestif ! git show "origin/$SUGGESTIONS_BRANCH:manifest.json" > /dev/null 2>&1; then echo "No manifest found on suggestions branch." exit 0fi # Read manifestMANIFEST=$(git show "origin/$SUGGESTIONS_BRANCH:manifest.json")SUGGESTION_COUNT=$(echo "$MANIFEST" | jq '.suggestions | length') if [[ "$SUGGESTION_COUNT" -eq 0 ]]; then echo "No pending suggestions in queue." exit 0fi echo "Found $SUGGESTION_COUNT pending suggestion(s):"echo ""echo "$MANIFEST" | jq -r '.suggestions[] | " PR #\(.pr): \(.title)"'echo "" if [[ "$DRY_RUN" == "true" ]]; then echo -e "${YELLOW}=== DRY RUN ===${NC}" echo "" echo "Would auto-apply suggestions to docs via Droid and create a draft PR." echo "Model: $MODEL" echo "" # Show each suggestion file for file in $(echo "$MANIFEST" | jq -r '.suggestions[].file'); do echo "--- $file ---" git show "origin/$SUGGESTIONS_BRANCH:$file" 2>/dev/null || echo "(file not found)" echo "" done echo -e "${YELLOW}=== END DRY RUN ===${NC}" echo "" echo "Run without --dry-run to create the PR." exit 0fi # Ensure clean working state (ignore untracked files with grep -v '??')if [[ -n "$(git status --porcelain | grep -v '^??' || true)" ]]; then error "Working directory has uncommitted changes. Please commit or stash first."fi REQUIRED_COMMANDS=(git gh jq droid)if [[ "$SKIP_VALIDATION" != "true" ]]; then REQUIRED_COMMANDS+=(mdbook)fifor command in "${REQUIRED_COMMANDS[@]}"; do if ! command -v "$command" > /dev/null 2>&1; then error "Required command not found: $command" fidone # Remember current branchORIGINAL_BRANCH=$(git branch --show-current)log "Current branch: $ORIGINAL_BRANCH" # Create new branch for docs PR from latest maingit fetch origin mainDOCS_BRANCH="docs/preview-auto-$(date +%Y-%m-%d-%H%M%S)"log "Creating docs branch: $DOCS_BRANCH" git checkout -b "$DOCS_BRANCH" origin/main TMPDIR=$(mktemp -d)trap 'rm -rf "$TMPDIR"' EXIT APPLY_SUMMARY_FILE="$TMPDIR/apply-summary.md"touch "$APPLY_SUMMARY_FILE" # Collect suggestion files into an arraySUGGESTION_FILES=()while IFS= read -r file; do SUGGESTION_FILES+=("$file")done < <(echo "$MANIFEST" | jq -r '.suggestions[].file') # Determine which PRs are already in the latest stable release.# Suggestions queued with --preview may reference features that shipped in stable# by the time this script runs, so their Preview callouts should be stripped.STABLE_PRS=()STABLE_TAG=$(git tag -l 'v*' --sort=-v:refname | grep -v 'pre' | head -1 || true)if [[ -n "$STABLE_TAG" ]]; then log "Latest stable release tag: $STABLE_TAG" for file in "${SUGGESTION_FILES[@]}"; do pr_num=$(echo "$MANIFEST" | jq -r --arg f "$file" '.suggestions[] | select(.file == $f) | .pr') # Find the merge commit for this PR merge_sha=$(gh pr view "$pr_num" --json mergeCommit --jq '.mergeCommit.oid' 2>/dev/null || true) if [[ -n "$merge_sha" ]] && git merge-base --is-ancestor "$merge_sha" "$STABLE_TAG" 2>/dev/null; then STABLE_PRS+=("$pr_num") log "PR #$pr_num is in stable ($STABLE_TAG)" fi done if [[ ${#STABLE_PRS[@]} -gt 0 ]]; then echo -e "${YELLOW}Note:${NC} ${#STABLE_PRS[@]} suggestion(s) are for PRs already in stable ($STABLE_TAG)." echo " Preview callouts will be stripped for: ${STABLE_PRS[*]}" echo "" fielse log "No stable release tag found, treating all suggestions as preview-only"fi # Determine which PRs touch code gated behind feature flags.# Features behind flags aren't generally available and shouldn't be documented yet.FLAGGED_PRS=()FLAGS_FILE="$REPO_ROOT/crates/feature_flags/src/flags.rs"if [[ -f "$FLAGS_FILE" ]]; then # Extract feature flag struct names (e.g. SubagentsFeatureFlag, GitGraphFeatureFlag) FLAG_NAMES=$(grep -oE 'pub struct \w+FeatureFlag' "$FLAGS_FILE" | awk '{print $3}') if [[ -n "$FLAG_NAMES" ]]; then FLAG_PATTERN=$(echo "$FLAG_NAMES" | tr '\n' '|' | sed 's/|$//') log "Feature flags found: $(echo "$FLAG_NAMES" | tr '\n' ' ')" for file in "${SUGGESTION_FILES[@]}"; do pr_num=$(echo "$MANIFEST" | jq -r --arg f "$file" '.suggestions[] | select(.file == $f) | .pr') # Skip PRs already marked as stable (no need to double-check) is_already_stable=false for stable_pr in "${STABLE_PRS[@]+"${STABLE_PRS[@]}"}"; do if [[ "$stable_pr" == "$pr_num" ]]; then is_already_stable=true break fi done if [[ "$is_already_stable" == "true" ]]; then continue fi # Check if the PR diff references any feature flag pr_diff=$(gh pr diff "$pr_num" 2>/dev/null || true) if [[ -n "$pr_diff" ]] && echo "$pr_diff" | grep -qE "$FLAG_PATTERN"; then matched_flags=$(echo "$pr_diff" | grep -oE "$FLAG_PATTERN" | sort -u | tr '\n' ', ' | sed 's/,$//') FLAGGED_PRS+=("$pr_num") log "PR #$pr_num is behind feature flag(s): $matched_flags" fi done if [[ ${#FLAGGED_PRS[@]} -gt 0 ]]; then echo -e "${YELLOW}Note:${NC} ${#FLAGGED_PRS[@]} suggestion(s) are for features behind feature flags." echo " These will be skipped: ${FLAGGED_PRS[*]}" echo "" fi fielse log "Feature flags file not found, skipping flag detection"fi # Split into batchesTOTAL=${#SUGGESTION_FILES[@]}BATCH_COUNT=$(( (TOTAL + BATCH_SIZE - 1) / BATCH_SIZE )) if [[ "$BATCH_COUNT" -gt 1 ]]; then echo "Processing $TOTAL suggestions in $BATCH_COUNT batches of up to $BATCH_SIZE..."else echo "Processing $TOTAL suggestions..."fiecho "" for (( batch=0; batch<BATCH_COUNT; batch++ )); do START=$(( batch * BATCH_SIZE )) END=$(( START + BATCH_SIZE )) if [[ "$END" -gt "$TOTAL" ]]; then END=$TOTAL fi BATCH_NUM=$(( batch + 1 )) BATCH_SUGGESTIONS_FILE="$TMPDIR/batch-${BATCH_NUM}-suggestions.md" BATCH_PROMPT_FILE="$TMPDIR/batch-${BATCH_NUM}-prompt.md" BATCH_SUMMARY_FILE="$TMPDIR/batch-${BATCH_NUM}-summary.md" echo -e "${BLUE}Batch $BATCH_NUM/$BATCH_COUNT${NC} (suggestions $(( START + 1 ))-$END of $TOTAL)" # Combine suggestion files for this batch, skipping flagged PRs and annotating stable PRs BATCH_HAS_SUGGESTIONS=false for (( i=START; i<END; i++ )); do file="${SUGGESTION_FILES[$i]}" pr_num=$(echo "$MANIFEST" | jq -r --arg f "$file" '.suggestions[] | select(.file == $f) | .pr') # Skip PRs behind feature flags entirely is_flagged=false for flagged_pr in "${FLAGGED_PRS[@]+"${FLAGGED_PRS[@]}"}"; do if [[ "$flagged_pr" == "$pr_num" ]]; then is_flagged=true break fi done if [[ "$is_flagged" == "true" ]]; then log "Skipping PR #$pr_num (behind feature flag)" { echo "### Skipped: PR #$pr_num" echo "" echo "This PR is behind a feature flag and was not applied." echo "" } >> "$APPLY_SUMMARY_FILE" continue fi BATCH_HAS_SUGGESTIONS=true # Check if PR is already in stable is_stable=false for stable_pr in "${STABLE_PRS[@]+"${STABLE_PRS[@]}"}"; do if [[ "$stable_pr" == "$pr_num" ]]; then is_stable=true break fi done { echo "## Source: $file" if [[ "$is_stable" == "true" ]]; then echo "" echo "> **ALREADY IN STABLE**: PR #$pr_num shipped in $STABLE_TAG." echo "> Do NOT add Preview or Changed-in-Preview callouts for this suggestion." echo "> Apply the documentation content only, without any preview-related callouts." fi echo "" git show "origin/$SUGGESTIONS_BRANCH:$file" 2>/dev/null || error "Suggestion file missing: $file" echo "" echo "---" echo "" } >> "$BATCH_SUGGESTIONS_FILE" done # Skip this batch if all its suggestions were flagged if [[ "$BATCH_HAS_SUGGESTIONS" == "false" ]]; then echo -e " ${YELLOW}Batch $BATCH_NUM skipped (all suggestions behind feature flags)${NC}" continue fi # Build auto-apply prompt for this batch cat > "$BATCH_PROMPT_FILE" << 'EOF'# Documentation Auto-Apply Request (Preview Release) Apply all queued documentation suggestions below directly to docs files in this repository. Before making edits, read and follow these rule files:- `.rules`- `docs/.rules` ## Required behavior 1. Apply concrete documentation edits (not suggestion text) to the appropriate files.2. Edit only docs content files under `docs/src/` unless a suggestion explicitly requires another docs path.3. For every docs file you modify, run a full-file brand voice pass (entire file, not only edited sections).4. Enforce the brand rubric exactly; final file content must score 4+ on every criterion: - Technical Grounding - Natural Syntax - Quiet Confidence - Developer Respect - Information Priority - Specificity - Voice Consistency - Earned Claims5. Keep SEO/frontmatter/linking requirements from the suggestions where applicable.6. Keep preview callout semantics correct: - Additive features: `> **Preview:** ...` - Behavior modifications: `> **Changed in Preview (vX.XXX).** ...` - **Exception**: Suggestions marked "ALREADY IN STABLE" must NOT get any preview callouts. These features already shipped in a stable release. Apply the content changes only. - Suggestions for features behind feature flags have been pre-filtered and excluded. If you encounter references to feature-flagged functionality, do not document it.7. If a suggestion is too ambiguous to apply safely, skip it and explain why in the summary.8. **Do not invent `{#kb}` or `{#action}` references.** Only use action names that already appear in the existing docs files you are editing. If unsure whether an action name is valid, use plain text instead. The docs build validates all action references against the compiled binary and will reject unknown names. ## Output format (after making edits) Return markdown with: - `## Applied Suggestions`- `## Skipped Suggestions`- `## Files Updated`- `## Brand Voice Verification` (one line per updated file confirming full-file pass) Do not include a patch in the response; apply edits directly to files. ## Queued Suggestions EOF cat "$BATCH_SUGGESTIONS_FILE" >> "$BATCH_PROMPT_FILE" log "Running Droid auto-apply (batch $BATCH_NUM) with model: $MODEL" if ! droid exec -m "$MODEL" -f "$BATCH_PROMPT_FILE" --auto high > "$BATCH_SUMMARY_FILE" 2>&1; then echo "Droid exec output (batch $BATCH_NUM):" cat "$BATCH_SUMMARY_FILE" error "Droid exec failed on batch $BATCH_NUM. See output above." fi # Append batch summary { echo "### Batch $BATCH_NUM" echo "" cat "$BATCH_SUMMARY_FILE" echo "" } >> "$APPLY_SUMMARY_FILE" echo -e " ${GREEN}Batch $BATCH_NUM complete${NC}"doneecho "" log "All batches completed, checking results..." if [[ -n "$(git status --porcelain | grep -v '^??' | grep -vE '^.. docs/' || true)" ]]; then error "Auto-apply modified non-doc files. Revert and re-run."fi if [[ -z "$(git status --porcelain docs/ | grep '^.. docs/src/' || true)" ]]; then error "Auto-apply produced no docs/src changes."fi log "Running docs formatter"./script/prettier --write if [[ -z "$(git status --porcelain docs/ | grep '^.. docs/src/' || true)" ]]; then error "No docs/src changes remain after formatting; aborting PR creation."fi # Validate docs build before creating PRif [[ "$SKIP_VALIDATION" != "true" ]]; then echo "Validating docs build..." log "Generating action metadata..." if ! ./script/generate-action-metadata > /dev/null 2>&1; then echo -e "${YELLOW}Warning:${NC} Could not generate action metadata (cargo build may have failed)." echo "Skipping docs build validation. CI will still catch errors." else VALIDATION_DIR="$TMPDIR/docs-validation" if ! mdbook build ./docs --dest-dir="$VALIDATION_DIR" 2>"$TMPDIR/validation-errors.txt"; then echo "" echo -e "${RED}Docs build validation failed:${NC}" cat "$TMPDIR/validation-errors.txt" echo "" error "Fix the errors above and re-run, or use --skip-validation to bypass." fi echo -e "${GREEN}Docs build validation passed.${NC}" fi echo ""fi # Build PR body from suggestionsPR_BODY_FILE="$TMPDIR/pr-body.md"cat > "$PR_BODY_FILE" << 'EOF'# Documentation Updates for Preview Release This draft PR auto-applies queued documentation suggestions collected fromrecently merged PRs. ## How to Use This PR 1. Review the applied changes file-by-file.2. Verify brand voice on each touched file as a full-file pass.3. Ensure docs use the correct callout type: - Additive features: Preview callout - Behavior modifications: Changed in Preview callout4. Merge when ready. ## Auto-Apply Summary EOF cat "$APPLY_SUMMARY_FILE" >> "$PR_BODY_FILE" cat >> "$PR_BODY_FILE" << 'EOF' ## Preview Callouts Use the Preview callout for new/additive features: ```markdown> **Preview:** This feature is available in Zed Preview. It will be included in the next Stable release.``` Use this callout for behavior modifications to existing functionality: ```markdown> **Changed in Preview (v0.XXX).** See [release notes](/releases#0.XXX).``` --- ## Pending Suggestions EOF # Append each suggestion to PR bodyfor file in $(echo "$MANIFEST" | jq -r '.suggestions[].file'); do log "Adding $file to PR body..." echo "" >> "$PR_BODY_FILE" git show "origin/$SUGGESTIONS_BRANCH:$file" >> "$PR_BODY_FILE" 2>/dev/null || true echo "" >> "$PR_BODY_FILE" echo "---" >> "$PR_BODY_FILE"done # Add tracking infoLines 1–500 of 591. Load more
#!/usr/bin/env bash # # Create a draft documentation PR by auto-applying batched suggestions. # # Usage: # script/docs-suggest-publish [--dry-run] [--model MODEL] # # This script: # 1. Reads pending suggestions from the docs/suggestions-pending branch # 2. Uses Droid to apply suggestions in batches (default 10 per batch) # 3. Runs docs formatting # 4. Validates docs build (action references, JSON schemas, links) # 5. Creates a draft PR for human review/merge # 6. Optionally resets the suggestions branch after successful PR creation # # Options: # --dry-run Show what would be done without creating PR # --keep-queue Don't reset the suggestions branch after PR creation # --model MODEL Override Droid model used for auto-apply # --batch-size N Suggestions per Droid invocation (default: 10) # --skip-validation Skip the docs build validation step # --verbose Show detailed progress # # Run this as part of the preview release workflow. set -euo pipefail DRY_RUN=false KEEP_QUEUE=false VERBOSE=false SKIP_VALIDATION=false BATCH_SIZE=10 MODEL="${DROID_MODEL:-claude-sonnet-4-5-latest}" SUGGESTIONS_BRANCH="docs/suggestions-pending" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' log() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[docs-publish]${NC} $*" >&2 fi } error() { echo -e "${RED}Error:${NC} $*" >&2 exit 1 } # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --dry-run) DRY_RUN=true shift ;; --keep-queue) KEEP_QUEUE=true shift ;; --verbose) VERBOSE=true shift ;; --model) MODEL="$2" shift 2 ;; --batch-size) BATCH_SIZE="$2" shift 2 ;; --skip-validation) SKIP_VALIDATION=true shift ;; -h|--help) head -30 "$0" | tail -28 exit 0 ;; *) error "Unknown option: $1" ;; esac done # Get repo root REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$REPO_ROOT" # Check if suggestions branch exists log "Checking for suggestions branch..." if ! git ls-remote --exit-code --heads origin "$SUGGESTIONS_BRANCH" > /dev/null 2>&1; then echo "No pending suggestions found (branch $SUGGESTIONS_BRANCH doesn't exist)." echo "Suggestions are queued automatically when PRs are merged to main." exit 0 fi # Fetch the suggestions branch log "Fetching suggestions branch..." git fetch origin "$SUGGESTIONS_BRANCH" # Check for manifest if ! git show "origin/$SUGGESTIONS_BRANCH:manifest.json" > /dev/null 2>&1; then echo "No manifest found on suggestions branch." exit 0 fi # Read manifest MANIFEST=$(git show "origin/$SUGGESTIONS_BRANCH:manifest.json") SUGGESTION_COUNT=$(echo "$MANIFEST" | jq '.suggestions | length') if [[ "$SUGGESTION_COUNT" -eq 0 ]]; then echo "No pending suggestions in queue." exit 0 fi echo "Found $SUGGESTION_COUNT pending suggestion(s):" echo "" echo "$MANIFEST" | jq -r '.suggestions[] | " PR #\(.pr): \(.title)"' echo "" if [[ "$DRY_RUN" == "true" ]]; then echo -e "${YELLOW}=== DRY RUN ===${NC}" echo "" echo "Would auto-apply suggestions to docs via Droid and create a draft PR." echo "Model: $MODEL" echo "" # Show each suggestion file for file in $(echo "$MANIFEST" | jq -r '.suggestions[].file'); do echo "--- $file ---" git show "origin/$SUGGESTIONS_BRANCH:$file" 2>/dev/null || echo "(file not found)" echo "" done echo -e "${YELLOW}=== END DRY RUN ===${NC}" echo "" echo "Run without --dry-run to create the PR." exit 0 fi # Ensure clean working state (ignore untracked files with grep -v '??') if [[ -n "$(git status --porcelain | grep -v '^??' || true)" ]]; then error "Working directory has uncommitted changes. Please commit or stash first." fi REQUIRED_COMMANDS=(git gh jq droid) if [[ "$SKIP_VALIDATION" != "true" ]]; then REQUIRED_COMMANDS+=(mdbook) fi for command in "${REQUIRED_COMMANDS[@]}"; do if ! command -v "$command" > /dev/null 2>&1; then error "Required command not found: $command" fi done # Remember current branch ORIGINAL_BRANCH=$(git branch --show-current) log "Current branch: $ORIGINAL_BRANCH" # Create new branch for docs PR from latest main git fetch origin main DOCS_BRANCH="docs/preview-auto-$(date +%Y-%m-%d-%H%M%S)" log "Creating docs branch: $DOCS_BRANCH" git checkout -b "$DOCS_BRANCH" origin/main TMPDIR=$(mktemp -d) trap 'rm -rf "$TMPDIR"' EXIT APPLY_SUMMARY_FILE="$TMPDIR/apply-summary.md" touch "$APPLY_SUMMARY_FILE" # Collect suggestion files into an array SUGGESTION_FILES=() while IFS= read -r file; do SUGGESTION_FILES+=("$file") done < <(echo "$MANIFEST" | jq -r '.suggestions[].file') # Determine which PRs are already in the latest stable release. # Suggestions queued with --preview may reference features that shipped in stable # by the time this script runs, so their Preview callouts should be stripped. STABLE_PRS=() STABLE_TAG=$(git tag -l 'v*' --sort=-v:refname | grep -v 'pre' | head -1 || true) if [[ -n "$STABLE_TAG" ]]; then log "Latest stable release tag: $STABLE_TAG" for file in "${SUGGESTION_FILES[@]}"; do pr_num=$(echo "$MANIFEST" | jq -r --arg f "$file" '.suggestions[] | select(.file == $f) | .pr') # Find the merge commit for this PR merge_sha=$(gh pr view "$pr_num" --json mergeCommit --jq '.mergeCommit.oid' 2>/dev/null || true) if [[ -n "$merge_sha" ]] && git merge-base --is-ancestor "$merge_sha" "$STABLE_TAG" 2>/dev/null; then STABLE_PRS+=("$pr_num") log "PR #$pr_num is in stable ($STABLE_TAG)" fi done if [[ ${#STABLE_PRS[@]} -gt 0 ]]; then echo -e "${YELLOW}Note:${NC} ${#STABLE_PRS[@]} suggestion(s) are for PRs already in stable ($STABLE_TAG)." echo " Preview callouts will be stripped for: ${STABLE_PRS[*]}" echo "" fi else log "No stable release tag found, treating all suggestions as preview-only" fi # Determine which PRs touch code gated behind feature flags. # Features behind flags aren't generally available and shouldn't be documented yet. FLAGGED_PRS=() FLAGS_FILE="$REPO_ROOT/crates/feature_flags/src/flags.rs" if [[ -f "$FLAGS_FILE" ]]; then # Extract feature flag struct names (e.g. SubagentsFeatureFlag, GitGraphFeatureFlag) FLAG_NAMES=$(grep -oE 'pub struct \w+FeatureFlag' "$FLAGS_FILE" | awk '{print $3}') if [[ -n "$FLAG_NAMES" ]]; then FLAG_PATTERN=$(echo "$FLAG_NAMES" | tr '\n' '|' | sed 's/|$//') log "Feature flags found: $(echo "$FLAG_NAMES" | tr '\n' ' ')" for file in "${SUGGESTION_FILES[@]}"; do pr_num=$(echo "$MANIFEST" | jq -r --arg f "$file" '.suggestions[] | select(.file == $f) | .pr') # Skip PRs already marked as stable (no need to double-check) is_already_stable=false for stable_pr in "${STABLE_PRS[@]+"${STABLE_PRS[@]}"}"; do if [[ "$stable_pr" == "$pr_num" ]]; then is_already_stable=true break fi done if [[ "$is_already_stable" == "true" ]]; then continue fi # Check if the PR diff references any feature flag pr_diff=$(gh pr diff "$pr_num" 2>/dev/null || true) if [[ -n "$pr_diff" ]] && echo "$pr_diff" | grep -qE "$FLAG_PATTERN"; then matched_flags=$(echo "$pr_diff" | grep -oE "$FLAG_PATTERN" | sort -u | tr '\n' ', ' | sed 's/,$//') FLAGGED_PRS+=("$pr_num") log "PR #$pr_num is behind feature flag(s): $matched_flags" fi done if [[ ${#FLAGGED_PRS[@]} -gt 0 ]]; then echo -e "${YELLOW}Note:${NC} ${#FLAGGED_PRS[@]} suggestion(s) are for features behind feature flags." echo " These will be skipped: ${FLAGGED_PRS[*]}" echo "" fi fi else log "Feature flags file not found, skipping flag detection" fi # Split into batches TOTAL=${#SUGGESTION_FILES[@]} BATCH_COUNT=$(( (TOTAL + BATCH_SIZE - 1) / BATCH_SIZE )) if [[ "$BATCH_COUNT" -gt 1 ]]; then echo "Processing $TOTAL suggestions in $BATCH_COUNT batches of up to $BATCH_SIZE..." else echo "Processing $TOTAL suggestions..." fi echo "" for (( batch=0; batch<BATCH_COUNT; batch++ )); do START=$(( batch * BATCH_SIZE )) END=$(( START + BATCH_SIZE )) if [[ "$END" -gt "$TOTAL" ]]; then END=$TOTAL fi BATCH_NUM=$(( batch + 1 )) BATCH_SUGGESTIONS_FILE="$TMPDIR/batch-${BATCH_NUM}-suggestions.md" BATCH_PROMPT_FILE="$TMPDIR/batch-${BATCH_NUM}-prompt.md" BATCH_SUMMARY_FILE="$TMPDIR/batch-${BATCH_NUM}-summary.md" echo -e "${BLUE}Batch $BATCH_NUM/$BATCH_COUNT${NC} (suggestions $(( START + 1 ))-$END of $TOTAL)" # Combine suggestion files for this batch, skipping flagged PRs and annotating stable PRs BATCH_HAS_SUGGESTIONS=false for (( i=START; i<END; i++ )); do file="${SUGGESTION_FILES[$i]}" pr_num=$(echo "$MANIFEST" | jq -r --arg f "$file" '.suggestions[] | select(.file == $f) | .pr') # Skip PRs behind feature flags entirely is_flagged=false for flagged_pr in "${FLAGGED_PRS[@]+"${FLAGGED_PRS[@]}"}"; do if [[ "$flagged_pr" == "$pr_num" ]]; then is_flagged=true break fi done if [[ "$is_flagged" == "true" ]]; then log "Skipping PR #$pr_num (behind feature flag)" { echo "### Skipped: PR #$pr_num" echo "" echo "This PR is behind a feature flag and was not applied." echo "" } >> "$APPLY_SUMMARY_FILE" continue fi BATCH_HAS_SUGGESTIONS=true # Check if PR is already in stable is_stable=false for stable_pr in "${STABLE_PRS[@]+"${STABLE_PRS[@]}"}"; do if [[ "$stable_pr" == "$pr_num" ]]; then is_stable=true break fi done { echo "## Source: $file" if [[ "$is_stable" == "true" ]]; then echo "" echo "> **ALREADY IN STABLE**: PR #$pr_num shipped in $STABLE_TAG." echo "> Do NOT add Preview or Changed-in-Preview callouts for this suggestion." echo "> Apply the documentation content only, without any preview-related callouts." fi echo "" git show "origin/$SUGGESTIONS_BRANCH:$file" 2>/dev/null || error "Suggestion file missing: $file" echo "" echo "---" echo "" } >> "$BATCH_SUGGESTIONS_FILE" done # Skip this batch if all its suggestions were flagged if [[ "$BATCH_HAS_SUGGESTIONS" == "false" ]]; then echo -e " ${YELLOW}Batch $BATCH_NUM skipped (all suggestions behind feature flags)${NC}" continue fi # Build auto-apply prompt for this batch cat > "$BATCH_PROMPT_FILE" << 'EOF' # Documentation Auto-Apply Request (Preview Release) Apply all queued documentation suggestions below directly to docs files in this repository. Before making edits, read and follow these rule files: - `.rules` - `docs/.rules` ## Required behavior 1. Apply concrete documentation edits (not suggestion text) to the appropriate files. 2. Edit only docs content files under `docs/src/` unless a suggestion explicitly requires another docs path. 3. For every docs file you modify, run a full-file brand voice pass (entire file, not only edited sections). 4. Enforce the brand rubric exactly; final file content must score 4+ on every criterion: - Technical Grounding - Natural Syntax - Quiet Confidence - Developer Respect - Information Priority - Specificity - Voice Consistency - Earned Claims 5. Keep SEO/frontmatter/linking requirements from the suggestions where applicable. 6. Keep preview callout semantics correct: - Additive features: `> **Preview:** ...` - Behavior modifications: `> **Changed in Preview (vX.XXX).** ...` - **Exception**: Suggestions marked "ALREADY IN STABLE" must NOT get any preview callouts. These features already shipped in a stable release. Apply the content changes only. - Suggestions for features behind feature flags have been pre-filtered and excluded. If you encounter references to feature-flagged functionality, do not document it. 7. If a suggestion is too ambiguous to apply safely, skip it and explain why in the summary. 8. **Do not invent `{#kb}` or `{#action}` references.** Only use action names that already appear in the existing docs files you are editing. If unsure whether an action name is valid, use plain text instead. The docs build validates all action references against the compiled binary and will reject unknown names. ## Output format (after making edits) Return markdown with: - `## Applied Suggestions` - `## Skipped Suggestions` - `## Files Updated` - `## Brand Voice Verification` (one line per updated file confirming full-file pass) Do not include a patch in the response; apply edits directly to files. ## Queued Suggestions EOF cat "$BATCH_SUGGESTIONS_FILE" >> "$BATCH_PROMPT_FILE" log "Running Droid auto-apply (batch $BATCH_NUM) with model: $MODEL" if ! droid exec -m "$MODEL" -f "$BATCH_PROMPT_FILE" --auto high > "$BATCH_SUMMARY_FILE" 2>&1; then echo "Droid exec output (batch $BATCH_NUM):" cat "$BATCH_SUMMARY_FILE" error "Droid exec failed on batch $BATCH_NUM. See output above." fi # Append batch summary { echo "### Batch $BATCH_NUM" echo "" cat "$BATCH_SUMMARY_FILE" echo "" } >> "$APPLY_SUMMARY_FILE" echo -e " ${GREEN}Batch $BATCH_NUM complete${NC}" done echo "" log "All batches completed, checking results..." if [[ -n "$(git status --porcelain | grep -v '^??' | grep -vE '^.. docs/' || true)" ]]; then error "Auto-apply modified non-doc files. Revert and re-run." fi if [[ -z "$(git status --porcelain docs/ | grep '^.. docs/src/' || true)" ]]; then error "Auto-apply produced no docs/src changes." fi log "Running docs formatter" ./script/prettier --write if [[ -z "$(git status --porcelain docs/ | grep '^.. docs/src/' || true)" ]]; then error "No docs/src changes remain after formatting; aborting PR creation." fi # Validate docs build before creating PR if [[ "$SKIP_VALIDATION" != "true" ]]; then echo "Validating docs build..." log "Generating action metadata..." if ! ./script/generate-action-metadata > /dev/null 2>&1; then echo -e "${YELLOW}Warning:${NC} Could not generate action metadata (cargo build may have failed)." echo "Skipping docs build validation. CI will still catch errors." else VALIDATION_DIR="$TMPDIR/docs-validation" if ! mdbook build ./docs --dest-dir="$VALIDATION_DIR" 2>"$TMPDIR/validation-errors.txt"; then echo "" echo -e "${RED}Docs build validation failed:${NC}" cat "$TMPDIR/validation-errors.txt" echo "" error "Fix the errors above and re-run, or use --skip-validation to bypass." fi echo -e "${GREEN}Docs build validation passed.${NC}" fi echo "" fi # Build PR body from suggestions PR_BODY_FILE="$TMPDIR/pr-body.md" cat > "$PR_BODY_FILE" << 'EOF' # Documentation Updates for Preview Release This draft PR auto-applies queued documentation suggestions collected from recently merged PRs. ## How to Use This PR 1. Review the applied changes file-by-file. 2. Verify brand voice on each touched file as a full-file pass. 3. Ensure docs use the correct callout type: - Additive features: Preview callout - Behavior modifications: Changed in Preview callout 4. Merge when ready. ## Auto-Apply Summary EOF cat "$APPLY_SUMMARY_FILE" >> "$PR_BODY_FILE" cat >> "$PR_BODY_FILE" << 'EOF' ## Preview Callouts Use the Preview callout for new/additive features: ```markdown > **Preview:** This feature is available in Zed Preview. It will be included in the next Stable release. ``` Use this callout for behavior modifications to existing functionality: ```markdown > **Changed in Preview (v0.XXX).** See [release notes](/releases#0.XXX). ``` --- ## Pending Suggestions EOF # Append each suggestion to PR body for file in $(echo "$MANIFEST" | jq -r '.suggestions[].file'); do log "Adding $file to PR body..." echo "" >> "$PR_BODY_FILE" git show "origin/$SUGGESTIONS_BRANCH:$file" >> "$PR_BODY_FILE" 2>/dev/null || true echo "" >> "$PR_BODY_FILE" echo "---" >> "$PR_BODY_FILE" done # Add tracking info