workflow: auto-update recipe-reference on release (#5988)

This commit is contained in:
dianed-square
2025-12-15 12:11:00 -08:00
committed by GitHub
parent eec5df76c6
commit e2c3c98cb4
15 changed files with 2439 additions and 0 deletions
@@ -0,0 +1,293 @@
# Automatically updates the Recipe Reference Guide when recipe struct fields,
# validation rules, or schema constraints change between releases.
#
# Triggers: Manual (for testing) or on release (production)
# Testing: Use dry_run mode to review outputs without creating PRs
# See: documentation/automation/recipe-schema-tracking/TESTING.md
name: Update Recipe Documentation
on:
workflow_dispatch: # Manual trigger for testing
inputs:
old_version:
description: 'Previous version (e.g., v1.14.0). Leave empty to auto-detect.'
required: false
type: string
new_version:
description: 'New version (e.g., v1.15.0). Leave empty to use HEAD.'
required: false
type: string
dry_run:
description: 'Dry run mode - generate files but do not create PR'
required: false
type: boolean
default: true
# Uncomment to enable automatic triggering on releases
# release:
# types: [published]
permissions:
contents: write # Create branches and commit files
pull-requests: write # Create PRs
jobs:
update-docs:
name: Update Recipe Documentation
runs-on: ubuntu-latest
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for version comparison
fetch-tags: true # Fetch all tags so we can checkout version tags
- name: Fetch upstream tags (for forks)
if: github.repository != 'block/goose'
run: |
# Add upstream remote and fetch tags (only needed when testing in forks)
git remote add upstream https://github.com/block/goose.git || git remote set-url upstream https://github.com/block/goose.git
git fetch upstream --tags --force
echo "✅ Fetched tags from upstream (fork mode)"
echo "Total tags available: $(git tag | wc -l)"
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y jq ripgrep
- name: Set up Node.js
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # pin@v3
with:
node-version: '20'
- name: Install goose CLI
run: |
mkdir -p /home/runner/.local/bin
curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh \
| CONFIGURE=false GOOSE_BIN_DIR=/home/runner/.local/bin bash
echo "/home/runner/.local/bin" >> $GITHUB_PATH
goose --version
- name: Configure goose for CI
env:
GOOSE_PROVIDER: ${{ vars.GOOSE_PROVIDER || 'openai' }}
GOOSE_MODEL: ${{ vars.GOOSE_MODEL || 'gpt-4o' }}
run: |
mkdir -p ~/.config/goose
cat <<EOF > ~/.config/goose/config.yaml
GOOSE_PROVIDER: $GOOSE_PROVIDER
GOOSE_MODEL: $GOOSE_MODEL
keyring: false
EOF
echo "✅ Created goose config:"
cat ~/.config/goose/config.yaml
- name: Determine versions to compare
id: versions
env:
GH_TOKEN: ${{ github.token }}
INPUT_OLD_VERSION: ${{ github.event.inputs.old_version }}
INPUT_NEW_VERSION: ${{ github.event.inputs.new_version }}
EVENT_NAME: ${{ github.event_name }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
get_previous_release() {
gh release list --limit 2 --json tagName --jq '.[].tagName' | sed -n '2p'
}
if [ -n "$INPUT_OLD_VERSION" ]; then
OLD_VERSION="$INPUT_OLD_VERSION"
else
OLD_VERSION=$(get_previous_release)
fi
if [ -n "$INPUT_NEW_VERSION" ]; then
NEW_VERSION="$INPUT_NEW_VERSION"
elif [ "$EVENT_NAME" = "release" ]; then
NEW_VERSION="$RELEASE_TAG"
else
NEW_VERSION="HEAD" # For testing unreleased changes
fi
if [ -z "$OLD_VERSION" ] || [ -z "$NEW_VERSION" ]; then
echo "Error: Could not determine versions to compare"
exit 1
fi
echo "old_version=$OLD_VERSION" >> $GITHUB_OUTPUT
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "OLD_VERSION=$OLD_VERSION" >> $GITHUB_ENV
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
echo "✅ Comparing $OLD_VERSION → $NEW_VERSION"
- name: Extract and compare schemas
id: extract
timeout-minutes: 15
working-directory: documentation/automation/recipe-schema-tracking
env:
GOOSE_REPO: ${{ github.workspace }}
run: |
set -o pipefail # Ensure pipeline failures are caught
mkdir -p output
./scripts/run-pipeline.sh "$OLD_VERSION" "$NEW_VERSION" 2>&1 | tee output/pipeline.log
HAS_CHANGES=$(jq -r '.has_changes' output/validation-changes.json)
echo "has_changes=$HAS_CHANGES" >> $GITHUB_OUTPUT
if [ "$HAS_CHANGES" = "false" ]; then
echo "✅ No changes detected"
else
echo "✅ Changes detected"
fi
- name: Update recipe-reference.md (AI synthesis)
if: steps.extract.outputs.has_changes == 'true'
timeout-minutes: 10
working-directory: documentation/automation/recipe-schema-tracking/output
env:
RECIPE_REF_PATH: ${{ github.workspace }}/documentation/docs/guides/recipes/recipe-reference.md
run: |
echo "🔍 Environment diagnostics:"
echo " GOOSE_PROVIDER: $GOOSE_PROVIDER"
echo " GOOSE_MODEL: $GOOSE_MODEL"
echo " OPENAI_API_KEY: ${OPENAI_API_KEY:0:8}..." # Show first 8 chars only
echo " RECIPE_REF_PATH: $RECIPE_REF_PATH"
echo " HOME: $HOME"
echo " PATH: $PATH"
echo ""
echo "📁 Goose config file:"
cat ~/.config/goose/config.yaml || echo "Config file not found!"
echo ""
echo "📁 Current directory:"
pwd
ls -la
echo ""
echo "🤖 Step 1: Running validation changes synthesis..."
goose run --recipe ../recipes/synthesize-validation-changes.yaml
echo ""
echo "🤖 Step 2: Applying changes to recipe-reference.md..."
goose run --recipe ../recipes/update-recipe-reference.yaml
- name: Upload automation outputs
if: always()
uses: actions/upload-artifact@v4
with:
name: recipe-docs-update-${{ steps.versions.outputs.old_version }}-to-${{ steps.versions.outputs.new_version }}
path: |
documentation/automation/recipe-schema-tracking/output/*.json
documentation/automation/recipe-schema-tracking/output/*.md
documentation/automation/recipe-schema-tracking/output/*.log
retention-days: 30
- name: Create Pull Request
if: |
steps.extract.outputs.has_changes == 'true' &&
(github.event.inputs.dry_run != 'true' || github.event_name == 'release')
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6
with:
branch: docs/auto-recipe-reference-${{ steps.versions.outputs.new_version }}
delete-branch: true
commit-message: |
docs: Update recipe reference for ${{ steps.versions.outputs.new_version }}
Automated update based on schema changes between ${{ steps.versions.outputs.old_version }} and ${{ steps.versions.outputs.new_version }}.
title: "docs: Update Recipe Reference Guide for ${{ steps.versions.outputs.new_version }}"
body: |
## Summary
This PR updates the Recipe Reference Guide based on schema and validation changes detected between **${{ steps.versions.outputs.old_version }}** and **${{ steps.versions.outputs.new_version }}**.
### Type of Change
- [x] Documentation
### AI Assistance
- [x] This PR was created or reviewed with AI assistance
#### 🤖 Automation Details
- **Workflow**: `docs-update-recipe-ref.yml`
- **Triggered by**: ${{ github.event_name }}
- **Previous version**: ${{ steps.versions.outputs.old_version }}
- **New version**: ${{ steps.versions.outputs.new_version }}
#### 📋 Changes Detected
Review the workflow artifacts for detailed change analysis:
- `validation-changes.json` - Structured diff of changes
- `validation-changes.md` - Human-readable change documentation
- `update-summary.md` - Summary of documentation updates applied
Download artifacts from the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).
### ✅ Review Checklist
- [ ] Verify all schema changes are accurately documented
- [ ] Check that examples are updated correctly
- [ ] Ensure validation rules are clearly explained
- [ ] Confirm no unintended changes were made
- [ ] Do changes require additional updates in this or other recipe topics?
### 🔗 Related
- Release: ${{ github.event.release.html_url || 'N/A' }}
---
*This PR was automatically generated by the Recipe Documentation Automation workflow.*
labels: |
documentation
automated
recipe-reference
- name: Workflow summary
if: always()
env:
OLD_VERSION: ${{ steps.versions.outputs.old_version }}
NEW_VERSION: ${{ steps.versions.outputs.new_version }}
HAS_CHANGES: ${{ steps.extract.outputs.has_changes }}
DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}
run: |
echo "## 📊 Recipe Documentation Update Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version Comparison**: $OLD_VERSION → $NEW_VERSION" >> $GITHUB_STEP_SUMMARY
echo "**Changes Detected**: $HAS_CHANGES" >> $GITHUB_STEP_SUMMARY
echo "**Dry Run Mode**: $DRY_RUN" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$HAS_CHANGES" = "true" ]; then
echo "### ✅ Documentation Updated" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The Recipe Reference Guide has been updated to reflect changes in $NEW_VERSION." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$DRY_RUN" = "true" ]; then
echo "**Note**: Running in dry-run mode - no PR was created. Review the artifacts to see the generated changes." >> $GITHUB_STEP_SUMMARY
else
echo "A pull request has been created with the documentation updates." >> $GITHUB_STEP_SUMMARY
fi
else
echo "### ️ No Changes Needed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "No recipe schema or validation changes were detected between $OLD_VERSION and $NEW_VERSION." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📦 Artifacts" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Download the workflow artifacts to review:" >> $GITHUB_STEP_SUMMARY
echo "- Extracted schemas and validation structures" >> $GITHUB_STEP_SUMMARY
echo "- Change detection results" >> $GITHUB_STEP_SUMMARY
echo "- Human-readable change documentation" >> $GITHUB_STEP_SUMMARY
echo "- Documentation update summary" >> $GITHUB_STEP_SUMMARY
+58
View File
@@ -0,0 +1,58 @@
# Documentation Automation
This directory contains automated pipelines for keeping goose documentation synchronized with code changes.
## Overview
Each automation project tracks specific types of changes and updates corresponding documentation:
| Project | Status | Tracks | Updates |
|---------|--------|--------|---------|
| [recipe-schema-tracking](./recipe-schema-tracking/) | ✅ Active | Recipe schema & validation rules | Recipe Reference Guide |
| cli-command-tracking | 🔮 Planned | CLI commands & options | CLI documentation |
| provider-tracking | 🔮 Planned | Supported AI providers | Provider documentation |
| extension-tracking | 🔮 Planned | Built-in extensions | Extension documentation |
## Architecture
Each automation project follows a consistent pattern:
```
project-name/
├── README.md # Project-specific documentation
├── TESTING.md # How to test this automation
├── config/ # Configuration files
├── scripts/ # Deterministic extraction/diff scripts
└── recipes/ # AI-powered synthesis/update recipes
```
### Design Principles
1. **Modular**: Each project is self-contained
2. **Testable**: Clear inputs/outputs at each stage
3. **Transparent**: Intermediate files can be inspected
4. **Reusable**: Common patterns across projects
### Hybrid Approach
- **Shell scripts**: Deterministic extraction and comparison
- **AI recipes**: Synthesis and documentation updates
## GitHub Actions Integration
Automation projects can be triggered via GitHub Actions workflows in `.github/workflows/`.
See individual project TESTING.md files for workflow usage.
## Adding New Automations
When creating a new automation project:
1. Create a subdirectory: `documentation/automation/your-project/`
2. Follow the standard structure (README, TESTING, config, scripts, recipes)
3. Create corresponding GitHub Actions workflow (if needed)
4. Update this README with the new project
## Questions?
For project-specific questions, see the README in each project directory.
@@ -0,0 +1,5 @@
# Generated output files
output/
# macOS
.DS_Store
@@ -0,0 +1,398 @@
# Recipe Schema Tracking
Automated pipeline for detecting and documenting Recipe schema and validation rule changes between goose releases.
## Overview
This automation keeps the [Recipe Reference Guide](https://block.github.io/goose/docs/guides/recipes/recipe-reference) synchronized with code changes by:
1. **Extracting** schema and validation rules from source code (deterministic)
2. **Detecting** changes between versions (deterministic diff)
3. **Synthesizing** human-readable change documentation (AI-powered)
4. **Updating** the Core Recipe Schema, Field Specifications, and Validation Rule sections in the Recipe Reference Guide (AI-powered)
The automation runs automatically on new releases via GitHub Actions, or can be run manually for testing.
## Quick Start
### Automated (GitHub Actions)
The automation runs automatically when a new release is published. See [TESTING.md](./TESTING.md) for testing instructions.
### Manual (Local Testing)
```bash
# Run the complete pipeline
./scripts/run-pipeline.sh v1.14.0 v1.15.0
# Or run individual steps:
# 1. Extract validation structures
./scripts/extract-validation-structure.sh v1.14.0 > output/old-validation-structure.json
./scripts/extract-validation-structure.sh v1.15.0 > output/new-validation-structure.json
# 2. Extract schemas
./scripts/extract-schema.sh v1.15.0 > output/new-schema.json
# 3. Detect changes
./scripts/diff-validation-structures.sh output/old-validation-structure.json \
output/new-validation-structure.json \
> output/validation-changes.json
# 4. Generate human-readable change documentation
cd output && goose run --recipe ../recipes/synthesize-validation-changes.yaml
# 5. Update recipe-reference.md
export RECIPE_REF_PATH=/path/to/recipe-reference.md
goose run --recipe ../recipes/update-recipe-reference.yaml
```
## Architecture
### Modular Pipeline Design
The automation uses a **hybrid approach**: deterministic shell scripts for data extraction/diffing, AI recipes for analysis and documentation updates.
```
┌─────────────────────────────────────────────────────────────────┐
│ EXTRACTION (Deterministic) │
├─────────────────────────────────────────────────────────────────┤
│ extract-schema.sh extract-validation-structure.sh │
│ ↓ ↓ │
│ new-schema.json new-validation-structure.json │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ DIFFING (Deterministic) │
├─────────────────────────────────────────────────────────────────┤
│ diff-validation-structures.sh │
│ ↓ │
│ validation-changes.json │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ SYNTHESIS (AI-Powered) │
├─────────────────────────────────────────────────────────────────┤
│ synthesize-validation-changes.yaml │
│ ↓ │
│ validation-changes.md (human-readable) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ UPDATE (AI-Powered) │
├─────────────────────────────────────────────────────────────────┤
│ update-recipe-reference.yaml │
│ ↓ │
│ recipe-reference.md (updated) + update-summary.md │
└─────────────────────────────────────────────────────────────────┘
```
### Why This Design?
**Scripts handle deterministic tasks:**
- Version-specific code extraction using `git show`
- JSON schema parsing and comparison
- No interpretation or inference - direct text extraction
**AI recipes handle synthesis and updates:**
- Analyzing changes and explaining implications
- Generating migration guidance and examples
- Updating documentation with proper formatting and context
**Benefits:**
- **Reliability**: Extraction is deterministic and reproducible
- **Testability**: Each stage has clear inputs/outputs
- **Maintainability**: Easy to update individual components
- **Transparency**: Intermediate files can be inspected
### Data Flow
All stages communicate via JSON/Markdown files in the `output/` directory:
| File | Producer | Consumer | Purpose |
|------|----------|----------|---------|
| `old-schema.json` | `extract-schema.sh` | `synthesize-validation-changes.yaml` | Previous version OpenAPI schema |
| `new-schema.json` | `extract-schema.sh` | `synthesize-validation-changes.yaml` | Current version OpenAPI schema |
| `old-validation-structure.json` | `extract-validation-structure.sh` | `diff-validation-structures.sh` | Previous version struct fields + validation functions |
| `new-validation-structure.json` | `extract-validation-structure.sh` | `diff-validation-structures.sh` | Current version struct fields + validation functions |
| `validation-changes.json` | `diff-validation-structures.sh` | `synthesize-validation-changes.yaml` | Detected changes (structured) |
| `validation-changes.md` | `synthesize-validation-changes.yaml` | `update-recipe-reference.yaml` | Human-readable change documentation |
| `update-summary.md` | `update-recipe-reference.yaml` | Human review | Summary of documentation updates |
## Configuration
### Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `RECIPE_REF_PATH` | No | - | Full path to `recipe-reference.md` file (overrides `GOOSE_REPO` construction) |
| `GOOSE_REPO` | No | Auto-detect | Path to goose repository root |
**Example (for local testing):**
```bash
export RECIPE_REF_PATH=/path/to/local/goose/documentation/docs/guides/recipes/recipe-reference.md
# OR
export GOOSE_REPO=/path/to/local/goose
```
### Configuration Files
#### `config/serde-attributes.json`
Defines Serde attribute meanings for parsing struct fields:
```json
{
"skip_serializing_if": "Field is optional and skipped when value matches condition",
"default": "Field uses default value when missing during deserialization",
"flatten": "Field's contents are flattened into parent struct",
"rename": "Field is serialized with a different name"
}
```
**When to update:** When new Serde attributes are introduced in Recipe struct definitions.
#### `config/known-validation-files.json`
Lists source files containing recipe validation logic:
```json
{
"validation_files": [
"crates/goose/src/recipe/validate_recipe.rs",
"crates/goose/src/agents/types.rs"
]
}
```
**When to update:** When validation logic is added to new files or moved to different locations.
### Scope and Exclusions
#### In Scope
- Top-level Recipe struct fields (all fields in `Recipe` struct)
- Validation functions in `validate_recipe.rs`
- Field types, optionality, and default values
- Validation error messages and requirements
- Enum value changes (e.g., new input types)
#### Excluded (By Design)
- **Extension schema deep-dives**: Extensions use a dual-purpose type (`ExtensionConfig`) shared across recipes, CLI, and runtime with mismatched validation requirements. The automation documents basic structure only. Extension-specific validation is documented separately.
**Why extensions are excluded:** The `ExtensionConfig` type serves multiple contexts with different validation needs:
- **Recipe context**: Looser validation for user-authored configurations
- **CLI context**: Stricter validation for command-line arguments
- **Runtime context**: Additional validation for server connections
Attempting to document all extension validation rules in the Recipe Reference would create confusion about which rules apply when. Extension documentation is maintained separately.
## Scripts
### `extract-schema.sh`
Extracts OpenAPI schema from the goose codebase.
**Usage:**
```bash
./scripts/extract-schema.sh [version] > output/new-schema.json
```
**Arguments:**
- `version` (optional): Git tag or commit to extract from (default: current working directory)
**Output:** JSON schema with field descriptions, types, and constraints
**Example:**
```bash
# Extract from current code
./scripts/extract-schema.sh > output/new-schema.json
# Extract from specific version
./scripts/extract-schema.sh v1.15.0 > output/old-schema.json
```
### `extract-validation-structure.sh`
Extracts Recipe struct fields and validation functions from source code.
**Usage:**
```bash
./scripts/extract-validation-structure.sh [version] > output/new-validation-structure.json
```
**Arguments:**
- `version` (optional): Git tag or commit to extract from (default: current working directory)
**Output:** JSON with struct fields (name, type, optionality, comments) and validation functions (signature, error messages)
**Example:**
```bash
# Extract from current code
./scripts/extract-validation-structure.sh > output/new-validation-structure.json
# Extract from v1.15.0
./scripts/extract-validation-structure.sh v1.15.0 > output/old-validation-structure.json
```
### `diff-validation-structures.sh`
Compares two validation structure files and outputs detected changes.
**Usage:**
```bash
./scripts/diff-validation-structures.sh <old-file> <new-file> > output/validation-changes.json
```
**Arguments:**
- `old-file`: Path to old validation structure JSON
- `new-file`: Path to new validation structure JSON
**Output:** JSON with categorized changes:
- `struct_fields.added`: New fields
- `struct_fields.removed`: Deleted fields
- `struct_fields.type_changed`: Type modifications
- `struct_fields.comment_changed`: Comment updates
- `validation_functions.added`: New validation rules
- `validation_functions.removed`: Deleted validation rules
**Example:**
```bash
./scripts/diff-validation-structures.sh \
output/old-validation-structure.json \
output/new-validation-structure.json \
> output/validation-changes.json
```
## Recipes
### `synthesize-validation-changes.yaml`
Analyzes detected changes and generates human-readable documentation.
**Inputs:**
- `output/validation-changes.json` - Detected changes from diff script
- `output/old-schema.json` - Previous version schema (for descriptions)
- `output/new-schema.json` - Current version schema (for descriptions)
**Output:**
- `output/validation-changes.md` - Human-readable change documentation with:
- Breaking changes with migration guidance
- Non-breaking changes with usage examples
- Validation rule additions/removals/modifications
- Migration checklist
**Usage:**
```bash
cd output
goose run --recipe ../recipes/synthesize-validation-changes.yaml
```
**What it does:**
- Compares old and new schemas to detect enum changes and required field changes
- Analyzes struct field changes (additions, removals, type changes)
- Explains validation rule changes with examples
- Generates migration guidance for breaking changes
- Creates actionable checklist for recipe authors
### `update-recipe-reference.yaml`
Updates the Recipe Reference Guide based on synthesized changes.
**Inputs:**
- `output/validation-changes.md` - Change documentation from synthesis recipe
- `recipe-reference.md` - Target documentation file (path from `RECIPE_REF_PATH` or `GOOSE_REPO` env var)
**Outputs:**
- Updated `recipe-reference.md` with changes applied
- `output/update-summary.md` - Summary of changes for review
**Usage:**
```bash
export RECIPE_REF_PATH=/path/to/recipe-reference.md
goose run --recipe recipes/update-recipe-reference.yaml
```
**What it does:**
- Updates Core Recipe Schema table (field additions/removals/type changes)
- Adds/removes/updates Field Specification sections for complex fields
- Updates Validation Rules section with new/modified/removed rules
- Updates enum lists in Field Specifications (e.g., input types)
- Generates summary of all changes for review
**Target sections:**
1. **Core Recipe Schema table** - Field-level changes
2. **Field Specifications sections** - Detailed documentation for complex fields
3. **Validation Rules section** - Validation function changes
## Directory Structure
```
recipe-schema-tracking/
├── README.md # This file
├── TESTING.md # Testing guide for GitHub Actions workflow
├── .gitignore # Excludes output/ directory
├── config/ # Configuration files
│ ├── serde-attributes.json # Serde attribute definitions
│ ├── known-validation-files.json # Validation source files
│ ├── extraction-output-schema.json # Schema for extraction output
│ └── validation-output-schema.json # Schema for validation output
├── scripts/ # Shell scripts (deterministic)
│ ├── extract-schema.sh # Extract OpenAPI schema
│ ├── extract-validation-structure.sh # Extract struct fields + validation
│ ├── diff-validation-structures.sh # Compare structures
│ └── run-pipeline.sh # End-to-end pipeline runner
├── recipes/ # AI recipes
│ ├── synthesize-validation-changes.yaml # Generate change docs
│ └── update-recipe-reference.yaml # Update documentation
└── output/ # Generated files (gitignored)
├── old-schema.json # Previous version schema
├── new-schema.json # Current version schema
├── old-validation-structure.json # Previous version structure
├── new-validation-structure.json # Current version structure
├── validation-changes.json # Detected changes (structured)
├── validation-changes.md # Change documentation (human-readable)
├── update-summary.md # Documentation update summary
└── pipeline.log # Pipeline execution log
```
## GitHub Actions Workflow
The automation runs via `.github/workflows/docs-update-recipe-ref.yml`:
- **Trigger**: Automatically on new releases, or manually for testing
- **Process**: Extracts schemas, detects changes, updates documentation
- **Output**: Creates a PR with updated `recipe-reference.md` if changes detected
- **Testing**: See [TESTING.md](./TESTING.md) for detailed testing instructions
## What Gets Tracked
### Struct Fields (6 structs)
- `Recipe` - Top-level recipe structure
- `Author` - Recipe author information
- `Settings` - Recipe settings (model, provider, etc.)
- `Response` - Structured output schema
- `SubRecipe` - Sub-recipe definitions
- `RecipeParameter` - Parameter definitions
### Changes Detected
- ✅ Fields added/removed
- ✅ Field type changes (e.g., `Option<T>``T`)
- ✅ Comment changes (inline documentation)
- ✅ Validation functions added/removed/modified
- ✅ Error messages changed
- ✅ Enum value changes
## Maintenance
When modifying the automation:
1. **Test locally first**: Run `./scripts/run-pipeline.sh` with test versions
2. **Verify outputs**: Check generated files against source code
3. **Update configuration**: If validation files move or new attributes added
4. **Test in fork**: Use GitHub Actions workflow with dry-run mode
5. **Document changes**: Update this README with design decisions
## Related Documentation
- [TESTING.md](./TESTING.md) - How to test the GitHub Actions workflow
- [Automation Overview](../README.md) - All automation projects
- [Recipe Reference Guide](../../docs/guides/recipes/recipe-reference.md) - Target documentation
@@ -0,0 +1,229 @@
# Testing Recipe Schema Tracking Automation
This guide covers how to test the recipe schema tracking automation both locally and via GitHub Actions.
## Local Testing
### Prerequisites
- goose CLI installed
- jq installed (for JSON processing)
- Git repository with goose source code
### Manual Pipeline Execution
Test the complete pipeline locally:
```bash
cd documentation/automation/recipe-schema-tracking
# Test with no changes expected
./scripts/run-pipeline.sh v1.14.0 v1.15.0
# Test with changes expected
./scripts/run-pipeline.sh v1.9.0 v1.15.0
```
### Individual Script Testing
Test each script independently:
```bash
# Extract schema from a version
./scripts/extract-schema.sh v1.15.0 > output/test-schema.json
# Extract validation structure
./scripts/extract-validation-structure.sh v1.15.0 > output/test-validation.json
# Compare two validation structures
./scripts/diff-validation-structures.sh output/old.json output/new.json > output/test-changes.json
```
### Recipe Testing
Test the AI recipes:
```bash
# Generate change documentation
cd output
goose run --recipe ../recipes/synthesize-validation-changes.yaml
# Update recipe-reference.md
export RECIPE_REF_PATH=/path/to/recipe-reference.md
goose run --recipe ../recipes/update-recipe-reference.yaml
```
## GitHub Actions Testing
### Test in Your Fork
The workflow can be tested in your fork without affecting the upstream repository.
#### Step 1: Push Branch to Fork
```bash
git push origin your-branch-name
```
#### Step 2: Enable GitHub Actions
1. Go to your fork on GitHub
2. Click "Actions" tab
3. Enable workflows if prompted
#### Step 3: Run Workflow Manually
1. Click "Update Recipe Documentation" workflow (docs-update-recipe-ref.yml)
2. Click "Run workflow" button
3. Select your branch
4. Configure inputs (see test scenarios below)
5. Click "Run workflow"
### Test Scenarios
#### Scenario 1: Dry-Run with No Changes
**Purpose**: Verify the workflow runs successfully when no changes are detected.
**Inputs**:
- `old_version`: `v1.14.0`
- `new_version`: `v1.15.0`
- `dry_run`: `true`
**Expected Results**:
- ✅ Workflow completes successfully
- ✅ "No changes detected" message in summary
- ✅ Artifacts uploaded with extraction results
- ✅ No PR created
#### Scenario 2: Dry-Run with Changes
**Purpose**: Test change detection and documentation generation without creating a PR.
**Inputs**:
- `old_version`: `v1.9.0`
- `new_version`: `v1.15.0`
- `dry_run`: `true`
**Expected Results**:
- ✅ Workflow detects changes (4 validation rules, 1 field removal)
- ✅ Generates `validation-changes.md` with documentation
- ✅ Updates `recipe-reference.md`
- ✅ Artifacts uploaded with all generated files
- ✅ No PR created (dry-run mode)
**Review Artifacts**:
1. Download artifact zip from workflow run
2. Check `validation-changes.md` - should document all changes
3. Check `update-summary.md` - should show what was updated
4. Compare updated `recipe-reference.md` with original
#### Scenario 3: Full Run with PR Creation
**Purpose**: Test end-to-end including PR creation.
**Inputs**:
- `old_version`: `v1.9.0`
- `new_version`: `v1.15.0`
- `dry_run`: `false`
**Expected Results**:
- ✅ Workflow runs successfully
- ✅ Creates PR: `docs/recipe-reference-v1.15.0`
- ✅ PR contains updated `recipe-reference.md`
- ✅ PR description includes change summary and checklist
**Review PR**:
1. Check only `recipe-reference.md` was modified
2. Verify changes match dry-run artifacts
3. Confirm no unintended modifications
4. Test documentation renders correctly
#### Scenario 4: Auto-Detection
**Purpose**: Test automatic version detection (simulates production mode).
**Inputs**:
- `old_version`: *(leave empty)*
- `new_version`: *(leave empty)*
- `dry_run`: `true`
**Expected Results**:
- ✅ Auto-detects two most recent releases
- ✅ Compares them automatically
- ✅ Uploads artifacts
### Reviewing Workflow Results
#### Check Workflow Summary
Each workflow run provides a summary with:
- Version comparison performed
- Whether changes were detected
- Dry-run mode status
- Links to artifacts
#### Download and Review Artifacts
Artifacts include:
- `old-validation-structure.json` - Extracted from old version
- `new-validation-structure.json` - Extracted from new version
- `validation-changes.json` - Structured diff
- `validation-changes.md` - Human-readable changes
- `update-summary.md` - Documentation update summary
- `pipeline.log` - Full pipeline execution log
#### Check Workflow Logs
For detailed debugging:
1. Click on the workflow run
2. Click on the "Update Recipe Documentation" job
3. Expand each step to see detailed logs
4. Look for error messages or unexpected behavior
## Troubleshooting
### Workflow doesn't appear in Actions tab
- Verify workflow file is in `.github/workflows/`
- Check file has `.yml` or `.yaml` extension
- Ensure GitHub Actions is enabled in fork
### "No changes detected" when expecting changes
- Check artifact `validation-changes.json` to see what was compared
- Verify versions exist: `git tag | grep v1.15.0`
- Review extraction script logs
### goose CLI installation fails
- Workflow installs from current repository
- Ensure `crates/goose-cli` builds successfully
- Check Rust toolchain installation
### PR creation fails
- Verify workflow has required permissions
- Check branch name doesn't already exist
- Review workflow logs for error messages
## Production Deployment
Once testing is complete:
1. **Merge automation to main**: Create PR for `documentation/automation/recipe-schema-tracking/`
2. **Merge baseline docs**: Create PR for revised `recipe-reference.md`
3. **Merge workflow**: Create PR for `.github/workflows/docs-update-recipe-ref.yml`
4. **Enable release trigger**: Uncomment `release:` section in workflow
After deployment, the workflow will automatically:
- Trigger on new releases
- Compare with previous release
- Create PR if changes detected
- Notify team for review
## Related Documentation
- [Recipe Schema Tracking README](./README.md) - Automation details
- [Recipe Reference Guide](../../docs/guides/recipes/recipe-reference.md) - Target documentation
- [Automation Overview](../README.md) - All automation projects
@@ -0,0 +1,73 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Validation Structure Extraction Output Schema",
"description": "Schema for the output of extract-validation-structure.sh script",
"type": "object",
"required": ["version", "extracted_at", "struct_fields", "validation_functions"],
"properties": {
"version": {
"type": "string",
"description": "The goose version tag that was extracted (e.g., 'v1.15.0')"
},
"extracted_at": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp when extraction occurred"
},
"struct_fields": {
"type": "array",
"description": "Array of Recipe struct fields extracted from mod.rs",
"items": {
"type": "object",
"required": ["field", "type", "optional", "inline_comment"],
"properties": {
"field": {
"type": "string",
"description": "Field name (e.g., 'version', 'title', 'instructions')"
},
"type": {
"type": "string",
"description": "Rust type (e.g., 'String', 'Option<String>', 'Option<Vec<RecipeParameter>>')"
},
"optional": {
"type": "boolean",
"description": "Whether the field is optional (true if type is Option<T>)"
},
"inline_comment": {
"type": "string",
"description": "Inline comment from source code after // (may be empty string if no comment)"
}
}
}
},
"validation_functions": {
"type": "array",
"description": "Array of validation functions extracted from validate_recipe.rs",
"items": {
"type": "object",
"required": ["function", "signature", "error_messages", "code_snippet"],
"properties": {
"function": {
"type": "string",
"description": "Function name (e.g., 'validate_prompt_or_instructions')"
},
"signature": {
"type": "string",
"description": "Full function signature from source code"
},
"error_messages": {
"type": "array",
"items": {
"type": "string"
},
"description": "Array of error message strings extracted from anyhow::anyhow! calls"
},
"code_snippet": {
"type": "string",
"description": "Relevant code excerpt (currently placeholder '...')"
}
}
}
}
}
}
@@ -0,0 +1,17 @@
{
"description": "List of source files containing recipe validation logic",
"validation_files": [
"crates/goose/src/recipe/validate_recipe.rs",
"crates/goose/src/recipe/mod.rs"
],
"exclude_patterns": [
"extension",
"build_recipe",
"template_recipe"
],
"notes": {
"validate_recipe.rs": "Contains parse-time validation functions (validate_prompt_or_instructions, validate_optional_parameters, etc.)",
"mod.rs": "Contains Recipe struct definition with field types and serde annotations",
"exclude_patterns": "Patterns to exclude: extensions (manual docs), build_recipe (runtime validation), template_recipe (rendering validation)"
}
}
@@ -0,0 +1,79 @@
{
"version": "v1.15.0",
"last_updated": "2025-11-21",
"description": "Serde attributes that affect deserialization behavior and defaults. This file should be manually updated when serde attributes change in the codebase.",
"fields_with_defaults": [
{
"type": "Recipe",
"field": "version",
"rust_type": "String",
"serde_attribute": "#[serde(default = \"default_version\")]",
"default_value": "1.0.0",
"default_function": "default_version",
"location": "crates/goose/src/recipe/mod.rs:27-29",
"notes": "Users can omit this field and it will default to 1.0.0. This explains why the OpenAPI schema marks it as optional (can be omitted) while the struct marks it as required (not nullable)."
},
{
"type": "SubRecipe",
"field": "sequential_when_repeated",
"rust_type": "bool",
"serde_attribute": "#[serde(default)]",
"default_value": false,
"default_function": null,
"location": "crates/goose/src/recipe/mod.rs:102",
"notes": "Uses Rust's Default trait for bool, which returns false. Controls whether repeated sub-recipe calls execute sequentially or in parallel."
},
{
"type": "SubRecipe",
"field": "values",
"rust_type": "Option<HashMap<String, String>>",
"serde_attribute": "#[serde(default, deserialize_with = \"deserialize_value_map_as_string\")]",
"default_value": null,
"default_function": null,
"custom_deserializer": "deserialize_value_map_as_string",
"location": "crates/goose/src/recipe/mod.rs:100-101",
"notes": "Uses Rust's Default trait for Option<T>, which returns None. Also has custom deserializer that converts any JSON value to string representation."
}
],
"custom_deserializers": [
{
"field": "Recipe.extensions",
"function": "recipe_extension_adapter::deserialize_recipe_extensions",
"location": "crates/goose/src/recipe/recipe_extension_adapter.rs",
"purpose": "Handles complex extension type deserialization with multiple variants (sse, stdio, builtin, platform, streamable_http, frontend, inline_python)",
"notes": "This is why extensions have complex validation - the deserializer handles type discrimination and field requirements vary by extension type"
},
{
"field": "SubRecipe.values",
"function": "deserialize_value_map_as_string",
"location": "crates/goose/src/recipe/mod.rs:104-120",
"purpose": "Converts any JSON value (number, bool, object, etc.) to string representation for parameter passing to sub-recipes",
"example": "{\"count\": 42} becomes {\"count\": \"42\"}",
"notes": "This allows flexible parameter passing where all values are normalized to strings"
}
],
"maintenance_checklist": [
"Update this file when:",
" - New fields with #[serde(default)] are added to Recipe or nested types",
" - Default values or default functions change",
" - New custom deserializers are added",
" - Serde attributes are modified (rename_all, skip_serializing_if, etc.)",
"",
"How to check:",
" 1. Run: rg '#\\[serde.*default' crates/goose/src/recipe/",
" 2. Run: rg 'deserialize_with' crates/goose/src/recipe/",
" 3. Compare results against this file",
" 4. Update version field to match checked version",
" 5. Update last_updated timestamp"
],
"validation_notes": [
"The analyze-validation-changes.yaml recipe should:",
" - Read this file to document default values in validation rules",
" - Flag any discrepancies between this file and extracted struct fields",
" - Note custom deserializers when documenting field behavior"
]
}
@@ -0,0 +1,76 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Recipe Validation Rules Output Schema",
"description": "Schema for validation rule discovery output",
"type": "object",
"required": ["version", "extracted_at", "validation_rules"],
"properties": {
"version": {
"type": "string",
"description": "The goose version these rules apply to"
},
"extracted_at": {
"type": "string",
"format": "date-time",
"description": "ISO timestamp when rules were extracted"
},
"validation_rules": {
"type": "array",
"description": "List of validation rules",
"items": {
"type": "object",
"required": ["rule_id", "category", "description", "affected_fields", "error_message"],
"properties": {
"rule_id": {
"type": "string",
"description": "Unique identifier for the rule (e.g., 'prompt_or_instructions_required')"
},
"category": {
"type": "string",
"description": "Category of validation (e.g., 'required_fields', 'parameters', 'structure')",
"enum": ["required_fields", "parameters", "structure", "schema", "cross_field"]
},
"description": {
"type": "string",
"description": "Human-readable description of the validation rule"
},
"affected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of Recipe fields this rule validates (e.g., ['instructions', 'prompt'])"
},
"requirement_type": {
"type": "string",
"description": "Type of requirement (e.g., 'at_least_one', 'conditional', 'required', 'optional')"
},
"condition": {
"type": "string",
"description": "Condition when rule applies (e.g., 'When requirement=optional')"
},
"error_message": {
"type": "string",
"description": "Exact error message users see when validation fails"
},
"validation_function": {
"type": "string",
"description": "Name of the validation function in code (e.g., 'validate_optional_parameters')"
},
"introduced_in": {
"type": "string",
"description": "Version when rule was introduced (default: 'unknown' for V1)"
},
"last_modified": {
"type": "string",
"description": "Version when rule was last modified"
},
"code_snippet": {
"type": "string",
"description": "Relevant code snippet showing validation logic"
}
}
}
}
}
}
@@ -0,0 +1,254 @@
version: "2"
title: "Synthesize Validation Changes"
description: "Generate human-readable documentation for validation rule changes between two versions"
extensions:
- type: builtin
name: developer
instructions: |
You are a technical documentation specialist creating release notes for Recipe validation changes.
## Your Task
Analyze the validation changes between two goose versions and generate clear, user-focused
documentation explaining what changed and why it matters.
## Input Files
You have access to SIX data sources:
1. **validation-changes.json** - The diff (what changed):
- struct_fields: added, removed, type_changed, comment_changed
- validation_functions: added, removed, signature_changed, error_messages_changed
2. **old-validation-structure.json** - Before state (for context):
- Complete struct fields from old version
- Complete validation functions from old version
3. **new-validation-structure.json** - After state (for context):
- Complete struct fields from new version
- Complete validation functions from new version
4. **old-schema.json** - Previous OpenAPI schema (for enum comparisons):
- Field descriptions from old version
- Enum values from old version
- Required fields from old version
5. **new-schema.json** - Current OpenAPI schema (for descriptions):
- Field descriptions (authoritative)
- Enum values (compare with old to detect additions)
- Nested type structures
- Required fields (compare with old to detect requirement changes)
6. **../config/serde-attributes.json** - Serde defaults:
- Fields with default values
- Custom deserializers
## Output Format
Create a Markdown file (validation-changes.md) with this structure:
IMPORTANT: Use indented code blocks (4 spaces) instead of fenced code blocks with backticks
to avoid triggering security alerts.
# Recipe Validation Changes
**From**: {old_version}
**To**: {new_version}
**Analyzed**: {timestamp}
## Summary
Brief overview of changes (2-3 sentences).
## Field Changes
### Added Fields
For each added field:
- **Struct.field_name** (Type) - ALWAYS include the struct name prefix
- Description: [from schema]
- Default: [from serde-attributes if applicable]
- Impact: How this affects recipe authors
### Removed Fields
For each removed field:
- **Struct.field_name** (was Type) - ALWAYS include the struct name prefix
- Reason: Why it was removed (infer from context)
- Migration: How to update existing recipes
### Type Changes
For each type change:
- **Struct.field_name** - ALWAYS include the struct name prefix
- Old: old_type
- New: new_type
- Impact: What this means for recipe authors
- Breaking: Yes/No
### Comment Changes
Only document if the comment change indicates a semantic change.
Skip if it's just formatting or trivial clarification.
## Schema Changes
Changes detected by comparing old-schema.json and new-schema.json:
### Enum Value Additions
For each enum with new values:
- **Field.enum_name** - Field where enum is used
- Added Values: List of new enum values
- Purpose: Why these values were added
- Impact: New capabilities available
### Enum Value Removals
For each enum with removed values:
- **Field.enum_name** - Field where enum is used
- Removed Values: List of removed enum values
- Reason: Why these values were removed
- Impact: Breaking change if recipes use removed values
### Required Field Changes
For fields added/removed from "required" arrays:
- **Field_name**
- Change: Added to required / Removed from required
- Impact: Breaking change or relaxed validation
## Validation Rule Changes
### New Validation Rules
For each added validation function:
- **Rule**: {Descriptive name}
- Function: function_name
- Purpose: What this rule validates
- Requirements: Specific requirements
- Error Messages: What users will see
- Impact: How this affects existing recipes
### Removed Validation Rules
For each removed validation function:
- **Rule**: {Descriptive name}
- Function: function_name
- Reason: Why it was removed
- Impact: What recipes are now allowed
### Changed Validation Rules
For signature or error message changes:
- **Rule**: {Descriptive name}
- Function: function_name
- What Changed: Specific changes
- Impact: How this affects recipe authors
## Breaking Changes
List all breaking changes with migration guidance:
- Change description
- How to update recipes
- Example before/after (use indented code blocks)
## Non-Breaking Changes
List all non-breaking changes (new optional fields, relaxed validation, etc.)
## Analysis Guidelines
1. **ALWAYS Include Struct Context**: Every field change MUST include the struct name
- Read the "struct" field from validation-changes.json
- Format as **Struct.field** (e.g., **Recipe.context**, **SubRecipe.description**)
- This is CRITICAL because multiple structs can have fields with the same name
- Example: Both Recipe and SubRecipe have a "description" field
2. **Detect Schema-Only Changes**: Compare old-schema.json and new-schema.json for:
- **Enum additions**: New values added to enum fields (e.g., input_type: "multiselect")
- **Enum removals**: Values removed from enum fields
- **Required field changes**: Fields added/removed from "required" arrays
- **Extension type changes**: New extension types in the extensions oneOf array
- Document these in a separate "Schema Changes" section
3. **Focus on User Impact**: Explain changes from recipe author perspective
4. **Prioritize Breaking Changes**: Highlight anything that breaks existing recipes
5. **Use Context**: Use old and new structures to understand relationships
- Example: If "context" field removed, check if functionality moved elsewhere
6. **Infer Intent**: Use function names, error messages, and field types to understand why changes were made
7. **Be Specific**: Include concrete examples of what changed
8. **Skip Noise**: Don't document trivial comment formatting changes
9. **Use Schema Descriptions**: Prefer schema descriptions over inline comments
10. **Document Defaults**: Note when fields have serde defaults from config
## Security Scanner Constraints
CRITICAL: The security scanner flags backticks as command substitution. You MUST NEVER use them:
1. **NO BACKTICKS ANYWHERE**:
- NOT in inline code: Write **field_name** instead
- NOT around field names: Write **context** not with backticks
- NOT in code blocks: Use 4-space indentation only
- NOT in any text: Even mentioning backticks triggers alerts
2. **NO SHELL PATTERNS**:
- No "2>/dev/null", "rm -f", "rm -rf"
- No file deletion examples
- No shell redirections
3. **Safe Alternatives**:
- Field names: Use **bold** (e.g., **context**, **description**)
- Code blocks: Use 4-space indentation
- Types: Write plain text (e.g., "Option of String")
- Functions: Use **bold** (e.g., **validate_json_schema**)
Examples:
- ❌ NEVER: Any use of the backtick character
- ✅ ALWAYS: **bold** for emphasis, 4-space indented blocks for code
## Special Cases
- **Empty changes arrays**: If a category has no changes, skip that section entirely
- **Context field removal**: This is the "context" field we discussed - it was never documented
- **Validation functions with no error messages**: Function likely returns early with Ok or uses inline checks
## File Locations
- Input 1: ./validation-changes.json (the diff)
- Input 2: ./old-validation-structure.json (before state)
- Input 3: ./new-validation-structure.json (after state)
- Input 4: ./old-schema.json (previous schema for comparison)
- Input 5: ./new-schema.json (current schema with descriptions)
- Input 6: ../config/serde-attributes.json (defaults)
- Output: ./validation-changes.md
Start by reading all SIX input files, then generate the validation changes documentation.
prompt: |
Please analyze the validation changes and generate comprehensive release notes.
Steps:
1. Read all SIX input files (validation-changes.json, old/new-validation-structure.json, old/new-schema.json, serde-attributes.json)
2. Analyze the changes
3. Write the documentation to ./validation-changes.md using the text_editor tool
Focus on:
- User impact (how does this affect recipe authors?)
- Breaking vs non-breaking changes
- Migration guidance for breaking changes
- Schema-only changes (enum additions, required field changes)
- Clear, actionable documentation
IMPORTANT: You MUST use the text_editor tool to write the output to ./validation-changes.md
@@ -0,0 +1,273 @@
version: "2"
title: "Update Recipe Reference Documentation"
description: "Apply validation changes to recipe-reference.md based on validation-changes.md"
extensions:
- type: builtin
name: developer
instructions: |
You are a technical documentation specialist updating the Recipe Reference Guide with schema and validation rule changes.
## ⚠️ CRITICAL RULES
1. **Only Update What's Documented**: If a field, rule, or section is NOT mentioned in validation-changes.md, DO NOT touch it
2. **Verify Before Removing**: Before removing any field or section, double-check it's explicitly listed as "removed" in validation-changes.md
3. **Preserve Everything Else**: All other content, formatting, examples, and sections must remain exactly as-is
4. **Table Updates - Use Precise str_replace**: When removing a row from a markdown table:
- Use old_str/new_str (NOT diff format)
- Include the COMPLETE row you want to remove
- Include 1-2 rows before and after for context
- Verify your old_str matches EXACTLY what's in the file
- Double-check you're not accidentally removing adjacent rows
## Your Task
You will update THREE main areas in recipe-reference.md:
1. **Core Recipe Schema table** - Field additions, removals, and requirement changes
2. **Field Specifications sections** - Add/remove/update detailed field documentation
3. **Validation Rules section** - New, removed, or changed validation rules
4. **Maintain Consistency**
- Match the existing documentation style and tone
- Use the same formatting (bullet points, bold text, etc.)
- Keep language user-focused and actionable
- Maintain alphabetical or logical ordering within subsections
## Input Files
1. **validation-changes.md** - The change documentation from the pipeline:
- Located at: `./validation-changes.md`
- Contains: Field changes, new validation rules, breaking changes, migration guidance
2. **recipe-reference.md** - The target documentation file:
- Located at: `${RECIPE_REF_PATH}` (environment variable)
- Default: `${GOOSE_REPO}/documentation/docs/guides/recipes/recipe-reference.md`
- Contains: Complete recipe reference with Validation Rules section
3. **update-summary.md** - Output file for change summary:
- Located at: `./update-summary.md`
- You will create this file to document what was updated
## Target Sections
### Section 1: Core Recipe Schema table
Find the **## Core Recipe Schema** section.
This table lists all Recipe fields with:
- Field name
- Required (✅) or Optional (-)
- Default value
- Description
**Important**: Character limits, required/optional status, and type information belong in this table, NOT in Validation Rules.
### Section 2: Field Specifications
Find the **## Field Specifications** section.
This section contains detailed documentation for complex fields (like activities, extensions, parameters, etc.).
Each field specification typically includes:
- Schema table (if the field has sub-fields)
- Field name
- Type
- Required (✅) or Optional (-)
- Default value
- Description
- Description and usage notes
- Examples
- Special considerations
### Section 3: Validation Rules
Find the **## Validation Rules** section.
**Critical**: This section should ONLY document validation functions from `validate_recipe.rs`, NOT schema requirements.
The section should:
- List only actual validation functions (with function names)
- Link to the source code
- Direct readers to the schema table for field requirements
## Update Strategy
### 1. Read and Analyze
Read `validation-changes.md` completely and identify ALL changes documented.
**CRITICAL**: Only make changes that are explicitly documented in validation-changes.md. If a field/rule is not mentioned, DO NOT modify it.
### 2. Apply Updates
For each change documented in validation-changes.md, apply the corresponding update using this guide:
| Change Type | Where to Update | How to Update |
|-------------|-----------------|---------------|
| **Field added** | Core Schema table | Add new row with Field name, Required (✅/-), Default, Description. Maintain alphabetical order. Use information from validation-changes.md. |
| | Field Specifications | If complex field (objects, arrays of objects, enums): Add new section following pattern of similar fields. Include schema table, description, examples. |
| **Field removed** | Core Schema table | Delete the row for this field. **IMPORTANT**: Use str_replace with old_str containing the row to remove PLUS 1-2 surrounding rows for context. Verify you're only removing the intended row. |
| | Field Specifications (top-level) | If the removed field has its own section: Remove entire section. |
| | Field Specifications (nested) | If the removed field is within another field's section: Update that section's schema table to remove the field row. Review section for any other references to the removed field. |
| **Field renamed** | Core Schema table | Delete old name row, add new name row (appears as remove + add in validation-changes.md). |
| | Field Specifications | Update section header and references if section exists. |
| **Type changed** | Core Schema table | Update Type column. If optionality changed: update Required (✅ ↔ -) and Default columns. Update Description if the change affects field behavior. |
| | Field Specifications | Update section to reflect type change if section exists. |
| **Field description changed (if semantically significant)** | Core Schema table | Update Description column. |
| **Enum value added/removed** | Field Specifications | Update enum lists (e.g., Input Types list in Parameters section). |
| **Validation rule added** | Validation Rules | Add bullet point with description and function name in parentheses. Add to appropriate subsection (Recipe-Level or Parameter Validation). Follow existing format. |
| **Validation rule removed** | Validation Rules | Remove the bullet point. |
| **Validation rule modified** | Validation Rules | Update bullet point text to reflect new requirements. |
**Important Notes:**
- Simple fields (strings, booleans, simple arrays) do NOT need Field Specification sections
- Complex fields (objects, arrays of objects, enums with options) DO need Field Specification sections
- Look at existing Field Specifications to see which fields have detailed sections
- Match the structure and style of existing sections when adding new ones
### 3. Verification
**Before finalizing:**
1. Review validation-changes.md one more time
2. Verify the changes you made to recipe-reference.md match the changes in validation-changes.md
3. Confirm you did NOT modify any field/rule that was not mentioned in validation-changes.md
4. Check that alphabetical ordering is maintained in tables
## Output Requirements
### 1. Updated recipe-reference.md
Update THREE areas as detailed in validation-changes.md:
- **## Core Recipe Schema** - Core Structure table (for field changes)
- **## Field Specifications** - Add/remove/update sections for complex fields
- **## Validation Rules** - All subsections (for validation rule changes)
Do NOT change other sections. Preserve all formatting, headers, and structure.
### 2. Create update-summary.md
Generate a summary document with this structure:
```markdown
# Recipe Reference Update Summary
**Date**: {current_date}
**Source**: validation-changes.md (v1.9.0 → v1.15.0)
**Target**: recipe-reference.md
## Changes Applied
### Structural Requirements
- Added: {list new fields}
- Modified: {list changed fields}
- Removed: {list removed fields}
### Parameter Validation
- Added: {list new rules}
- Modified: {list changed rules}
- Removed: {list removed rules}
### [Other Subsections]
...
## Sections Not Modified
- {list subsections that didn't need changes}
## Verification Checklist
- [ ] All new validation rules from validation-changes.md are documented
- [ ] Removed rules are no longer present
- [ ] Language matches existing documentation style
- [ ] Markdown formatting is valid
- [ ] No unintended changes to other sections
## Next Steps
1. Review the updated Validation Rules section
2. Verify accuracy against source code if needed
3. Commit changes to the recipe-reference-updates branch
4. Create PR for review
```
## Guidelines
1. **Only Update What Changed**: CRITICAL - Only make changes that are documented in validation-changes.md
- Do NOT add "improvements" or "clarifications" not in the source
- Do NOT reorganize existing content
- Do NOT rewrite descriptions unless they changed
- Do NOT add validation rules that aren't in validation-changes.md
2. **Be Precise**: Update only the specific items that changed
- Field added? Add one row to table
- Field removed? Remove one row from table
- Validation rule added? Add one bullet point
- That's it - nothing more
3. **Preserve Everything Else**: Keep existing content exactly as-is
- Same section hierarchy and formatting
- Same wording for unchanged items
- Same examples and notes
4. **Match Style**: Use the same voice, tone, and formatting as existing content
5. **Be Complete**: Ensure all changes from validation-changes.md are reflected
6. **Document Changes**: Create a thorough update-summary.md for review
7. **Verify**: Double-check that your updates accurately reflect the validation changes
## File Locations Summary
- Input 1: `./validation-changes.md` (change documentation)
- Input 2: `${RECIPE_REF_PATH}` or `${GOOSE_REPO}/documentation/docs/guides/recipes/recipe-reference.md` (target file)
- Output 1: Same as Input 2 (updated in place)
- Output 2: `./update-summary.md` (change summary)
## Environment Variables
- `RECIPE_REF_PATH`: Full path to recipe-reference.md file (overrides default)
- `GOOSE_REPO`: Path to goose repository (used if RECIPE_REF_PATH not set)
The recipe will check for these environment variables and construct the path accordingly.
Start by reading both input files, then apply the updates and generate the summary.
prompt: |
Please update the Recipe Reference Guide based on the validation changes in validation-changes.md.
CRITICAL INSTRUCTIONS:
1. Only make changes that are explicitly documented in validation-changes.md
2. Do NOT make general improvements, reorganizations, or clarifications
3. Use the EXACT file path from the RECIPE_REF_PATH environment variable when editing recipe-reference.md
- Do NOT create shortened paths or use ~ notation
- The full path is provided in the RECIPE_REF_PATH environment variable
Update THREE areas:
1. Core Recipe Schema table - for field additions/removals/type changes
2. Field Specifications sections - add/remove sections for complex fields (follow existing patterns)
3. Validation Rules section - for validation function changes
For each change in validation-changes.md:
- Field added? Add row to schema table + Field Specification section if complex
- Field removed? Remove row from schema table + Field Specification section if exists
- Field type changed? Update type/required columns + update Field Specification if exists
- Enum value added? Update enum lists in Field Specifications (e.g., Input Types)
- Validation rule added? Add bullet point with function name
- Validation rule removed? Remove bullet point
When adding Field Specification sections:
- Look at similar existing sections (e.g., Activities, Parameters, Settings)
- Match their structure: schema table, description, examples, usage notes
- Keep the same style and formatting
- Use information from validation-changes.md
Do NOT:
- Rewrite existing descriptions
- Add validation rules not in validation-changes.md
- Reorganize sections
- Make "improvements" to existing content
- Add character limits unless they're new in validation-changes.md
Generate update-summary.md documenting exactly what you changed.
@@ -0,0 +1,212 @@
#!/bin/bash
# Compare two validation structure files and output changes
# Usage: ./diff-validation-structures.sh old-validation-structure.json new-validation-structure.json
# Output: validation-changes.json
set -e
OLD_FILE=${1:-"old-validation-structure.json"}
NEW_FILE=${2:-"new-validation-structure.json"}
if [ ! -f "$OLD_FILE" ]; then
echo "Error: Old validation structure file not found: $OLD_FILE" >&2
exit 1
fi
if [ ! -f "$NEW_FILE" ]; then
echo "Error: New validation structure file not found: $NEW_FILE" >&2
exit 1
fi
# Extract versions for metadata
OLD_VERSION=$(jq -r '.version' "$OLD_FILE")
NEW_VERSION=$(jq -r '.version' "$NEW_FILE")
# Build the changes JSON using jq
jq -n \
--arg old_version "$OLD_VERSION" \
--arg new_version "$NEW_VERSION" \
--arg compared_at "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--argjson old_data "$(cat "$OLD_FILE")" \
--argjson new_data "$(cat "$NEW_FILE")" \
'
{
old_version: $old_version,
new_version: $new_version,
compared_at: $compared_at,
has_changes: false,
changes: {
struct_fields: {
added: [],
removed: [],
type_changed: [],
comment_changed: []
},
validation_functions: {
added: [],
removed: [],
signature_changed: [],
error_messages_changed: []
}
}
} |
# Detect field changes
. as $result |
# Find added fields (in new but not in old) - compare by struct.field
($new_data.struct_fields | map(.struct + "." + .field)) as $new_fields |
($old_data.struct_fields | map(.struct + "." + .field)) as $old_fields |
($new_fields - $old_fields) as $added_field_keys |
# Find removed fields (in old but not in new) - compare by struct.field
($old_fields - $new_fields) as $removed_field_keys |
# Find fields with type changes - compare by struct.field
(
$new_data.struct_fields |
map(select((.struct + "." + .field) as $key | $old_fields | contains([$key])) |
{struct: .struct, field: .field, new_type: .type, new_comment: .inline_comment}
)
) as $new_common |
(
$old_data.struct_fields |
map(select((.struct + "." + .field) as $key | $new_fields | contains([$key])) |
{struct: .struct, field: .field, old_type: .type, old_comment: .inline_comment}
)
) as $old_common |
# Compare types and comments for common fields
(
$new_common | map(
. as $new_item |
($old_common | map(select(.struct == $new_item.struct and .field == $new_item.field)) | .[0]) as $old_item |
if $old_item.old_type != $new_item.new_type then
{
struct: $new_item.struct,
field: $new_item.field,
old_type: $old_item.old_type,
new_type: $new_item.new_type
}
else
empty
end
)
) as $type_changed |
(
$new_common | map(
. as $new_item |
($old_common | map(select(.struct == $new_item.struct and .field == $new_item.field)) | .[0]) as $old_item |
if $old_item.old_comment != $new_item.new_comment then
{
struct: $new_item.struct,
field: $new_item.field,
old_comment: $old_item.old_comment,
new_comment: $new_item.new_comment
}
else
empty
end
)
) as $comment_changed |
# Find validation function changes
($new_data.validation_functions | map(.function)) as $new_funcs |
($old_data.validation_functions | map(.function)) as $old_funcs |
($new_funcs - $old_funcs) as $added_funcs |
($old_funcs - $new_funcs) as $removed_funcs |
# Find functions with signature changes
(
$new_data.validation_functions |
map(select(.function as $f | $old_funcs | contains([$f])) |
{function: .function, new_signature: .signature, new_errors: .error_messages}
)
) as $new_common_funcs |
(
$old_data.validation_functions |
map(select(.function as $f | $new_funcs | contains([$f])) |
{function: .function, old_signature: .signature, old_errors: .error_messages}
)
) as $old_common_funcs |
(
$new_common_funcs | map(
. as $new_func |
($old_common_funcs | map(select(.function == $new_func.function)) | .[0]) as $old_func |
if $old_func.old_signature != $new_func.new_signature then
{
function: $new_func.function,
old_signature: $old_func.old_signature,
new_signature: $new_func.new_signature
}
else
empty
end
)
) as $signature_changed |
(
$new_common_funcs | map(
. as $new_func |
($old_common_funcs | map(select(.function == $new_func.function)) | .[0]) as $old_func |
if $old_func.old_errors != $new_func.new_errors then
{
function: $new_func.function,
old_errors: $old_func.old_errors,
new_errors: $new_func.new_errors
}
else
empty
end
)
) as $error_messages_changed |
# Build final result with detected changes
.changes.struct_fields.added = (
$added_field_keys | map(
. as $key |
($key | split(".")) as $parts |
$new_data.struct_fields | map(select(.struct == $parts[0] and .field == $parts[1])) | .[0]
)
) |
.changes.struct_fields.removed = (
$removed_field_keys | map(
. as $key |
($key | split(".")) as $parts |
$old_data.struct_fields | map(select(.struct == $parts[0] and .field == $parts[1])) | .[0]
)
) |
.changes.struct_fields.type_changed = $type_changed |
.changes.struct_fields.comment_changed = $comment_changed |
.changes.validation_functions.added = (
$added_funcs | map(
. as $func |
$new_data.validation_functions | map(select(.function == $func)) | .[0]
)
) |
.changes.validation_functions.removed = (
$removed_funcs | map(
. as $func |
$old_data.validation_functions | map(select(.function == $func)) | .[0]
)
) |
.changes.validation_functions.signature_changed = $signature_changed |
.changes.validation_functions.error_messages_changed = $error_messages_changed |
# Set has_changes flag
.has_changes = (
(.changes.struct_fields.added | length) > 0 or
(.changes.struct_fields.removed | length) > 0 or
(.changes.struct_fields.type_changed | length) > 0 or
(.changes.struct_fields.comment_changed | length) > 0 or
(.changes.validation_functions.added | length) > 0 or
(.changes.validation_functions.removed | length) > 0 or
(.changes.validation_functions.signature_changed | length) > 0 or
(.changes.validation_functions.error_messages_changed | length) > 0
)
'
@@ -0,0 +1,161 @@
#!/bin/bash
# Extract and resolve Recipe schema from OpenAPI spec at a specific git version
# Usage: ./extract-schema.sh <version>
# Example: ./extract-schema.sh v1.15.0
set -e
VERSION=${1:-"main"}
GOOSE_REPO=${GOOSE_REPO:-"$HOME/Development/goose"}
if [ ! -d "$GOOSE_REPO" ]; then
echo "Error: GOOSE_REPO directory not found: $GOOSE_REPO" >&2
exit 1
fi
cd "$GOOSE_REPO"
# Verify version exists (for non-main versions)
if [ "$VERSION" != "main" ]; then
if ! git rev-parse "$VERSION" >/dev/null 2>&1; then
echo "Error: Version $VERSION not found in git history" >&2
exit 1
fi
fi
# Extract OpenAPI spec from git
if [ "$VERSION" = "main" ]; then
if [ ! -f ui/desktop/openapi.json ]; then
echo "Error: ui/desktop/openapi.json not found in working directory" >&2
exit 1
fi
OPENAPI_JSON=$(cat ui/desktop/openapi.json)
else
OPENAPI_JSON=$(git show "$VERSION:ui/desktop/openapi.json" 2>/dev/null || {
echo "Error: Could not find ui/desktop/openapi.json at version $VERSION" >&2
exit 1
})
fi
# Use Node.js to extract and resolve Recipe schema
echo "$OPENAPI_JSON" | node -e "
const openApiSpec = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
/**
* Resolves \$ref references in OpenAPI schemas by expanding them with the actual schema definitions
* Ported from ui/desktop/src/recipe/validation.ts
*/
function resolveRefs(schema, openApiSpec) {
if (!schema || typeof schema !== 'object') {
return schema;
}
// Handle \$ref
if (typeof schema.\$ref === 'string') {
const refPath = schema.\$ref.replace('#/', '').split('/');
let resolved = openApiSpec;
for (const segment of refPath) {
if (resolved && typeof resolved === 'object' && segment in resolved) {
resolved = resolved[segment];
} else {
console.warn(\`Could not resolve \$ref: \${schema.\$ref}\`);
return schema; // Return original if can't resolve
}
}
if (resolved && typeof resolved === 'object') {
// Recursively resolve refs in the resolved schema
return resolveRefs(resolved, openApiSpec);
}
return schema;
}
// Handle allOf (merge schemas)
if (Array.isArray(schema.allOf)) {
const merged = {};
for (const subSchema of schema.allOf) {
if (typeof subSchema === 'object' && subSchema !== null) {
const resolved = resolveRefs(subSchema, openApiSpec);
Object.assign(merged, resolved);
}
}
// Keep other properties from the original schema
const { allOf, ...rest } = schema;
return { ...merged, ...rest };
}
// Handle oneOf/anyOf (keep as union)
if (Array.isArray(schema.oneOf)) {
return {
...schema,
oneOf: schema.oneOf.map((subSchema) =>
typeof subSchema === 'object' && subSchema !== null
? resolveRefs(subSchema, openApiSpec)
: subSchema
),
};
}
if (Array.isArray(schema.anyOf)) {
return {
...schema,
anyOf: schema.anyOf.map((subSchema) =>
typeof subSchema === 'object' && subSchema !== null
? resolveRefs(subSchema, openApiSpec)
: subSchema
),
};
}
// Handle object properties
if (schema.type === 'object' && schema.properties && typeof schema.properties === 'object') {
const resolvedProperties = {};
for (const [key, value] of Object.entries(schema.properties)) {
if (typeof value === 'object' && value !== null) {
resolvedProperties[key] = resolveRefs(value, openApiSpec);
} else {
resolvedProperties[key] = value;
}
}
return {
...schema,
properties: resolvedProperties,
};
}
// Handle array items
if (schema.type === 'array' && schema.items && typeof schema.items === 'object') {
return {
...schema,
items: resolveRefs(schema.items, openApiSpec),
};
}
// Return schema as-is if no refs to resolve
return schema;
}
// Extract Recipe schema
const recipeSchema = openApiSpec.components?.schemas?.Recipe;
if (!recipeSchema) {
console.error('Error: Recipe schema not found in OpenAPI specification');
process.exit(1);
}
// Resolve all \$refs in the schema
const resolvedSchema = resolveRefs(recipeSchema, openApiSpec);
// Convert OpenAPI schema to JSON Schema format
const jsonSchema = {
'\$schema': 'http://json-schema.org/draft-07/schema#',
...resolvedSchema,
title: resolvedSchema.title || 'Recipe',
description: resolvedSchema.description || 'A Recipe represents a personalized, user-generated agent configuration that defines specific behaviors and capabilities within the Goose system.',
};
// Output the resolved schema
console.log(JSON.stringify(jsonSchema, null, 2));
"
@@ -0,0 +1,185 @@
#!/bin/bash
# Extract validation structure from Rust source files
# Usage: ./extract-validation-structure.sh <version>
# Example: ./extract-validation-structure.sh v1.15.0
set -e
VERSION=${1:-"main"}
GOOSE_REPO=${GOOSE_REPO:-"$HOME/Development/goose"}
if [ ! -d "$GOOSE_REPO" ]; then
echo "Error: GOOSE_REPO directory not found: $GOOSE_REPO" >&2
exit 1
fi
cd "$GOOSE_REPO"
# Verify version exists (for non-main versions)
if [ "$VERSION" != "main" ]; then
if ! git rev-parse "$VERSION" >/dev/null 2>&1; then
echo "Error: Version $VERSION not found in git history" >&2
exit 1
fi
fi
# Start JSON output
# Use ISO 8601 format that works on both macOS and Linux
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u -Iseconds 2>/dev/null || date -u)
cat << EOF
{
"version": "$VERSION",
"extracted_at": "$TIMESTAMP",
"struct_fields": [
EOF
# Extract fields from multiple structs
FIRST_FIELD=true
# Get file content from git history or working directory
if [ "$VERSION" = "main" ]; then
MOD_RS_CONTENT=$(cat crates/goose/src/recipe/mod.rs)
else
MOD_RS_CONTENT=$(git show "$VERSION:crates/goose/src/recipe/mod.rs" 2>/dev/null || {
echo "Error: Failed to read mod.rs from version $VERSION" >&2
exit 1
})
fi
# List of structs to extract (in order of appearance in file)
STRUCTS="Recipe Author Settings Response SubRecipe RecipeParameter"
# Create a temporary file to collect all fields
TEMP_FIELDS=$(mktemp)
for STRUCT_NAME in $STRUCTS; do
# Extract just this struct's definition (from "pub struct Name" to the closing "}")
echo "$MOD_RS_CONTENT" | awk "
/^pub struct $STRUCT_NAME/ { in_struct=1; next }
in_struct && /^}/ { exit }
in_struct && /^[[:space:]]+pub [a-z_]+:/ { print }
" | while IFS= read -r line; do
# Extract field name (word after 'pub ')
field_name=$(echo "$line" | sed -E 's/.*pub ([a-z_]+):.*/\1/')
# Extract type (between : and , or // or end of line)
field_type=$(echo "$line" | sed -E 's/.*:\s*([^,\/]+).*/\1/' | sed 's/[[:space:]]*$//')
# Extract inline comment (after //)
inline_comment=""
if echo "$line" | grep -q "//"; then
inline_comment=$(echo "$line" | sed -E 's/.*\/\/\s*(.*)$/\1/' | sed 's/[[:space:]]*$//' | sed 's/"/\\"/g')
fi
# Check if optional
is_optional="false"
if echo "$field_type" | grep -q "Option<"; then
is_optional="true"
fi
# Output to temp file
cat << FIELD_JSON >> "$TEMP_FIELDS"
{
"struct": "$STRUCT_NAME",
"field": "$field_name",
"type": "$field_type",
"optional": $is_optional,
"inline_comment": "$inline_comment"
}
FIELD_JSON
done
done
# Output fields with proper comma separation
if [ -s "$TEMP_FIELDS" ]; then
# Read all JSON objects into an array and format with commas
jq -s '.' "$TEMP_FIELDS" | jq -r 'to_entries | .[] | (if .key > 0 then "," else "" end) + " " + (.value | tostring)'
fi
rm -f "$TEMP_FIELDS"
# Close struct_fields array, start validation_functions
cat << EOF
],
"validation_functions": [
EOF
# Extract validation functions with error messages and code snippets
FIRST_FUNC=true
# Get validation file content from git history or working directory
# Note: validate_recipe.rs may not exist in older versions
if [ "$VERSION" = "main" ]; then
if [ -f crates/goose/src/recipe/validate_recipe.rs ]; then
VALIDATE_RS_CONTENT=$(cat crates/goose/src/recipe/validate_recipe.rs)
else
VALIDATE_RS_CONTENT=""
fi
else
VALIDATE_RS_CONTENT=$(git show "$VERSION:crates/goose/src/recipe/validate_recipe.rs" 2>/dev/null || echo "")
fi
if [ -n "$VALIDATE_RS_CONTENT" ]; then
echo "$VALIDATE_RS_CONTENT" | rg "^fn validate_" -A 30 | \
awk '
/^fn validate_/ {
if (func_name != "") {
# Output previous function
if (first_func == "true") {
first_func = "false"
} else {
print ","
}
printf " {\n"
printf " \"function\": \"%s\",\n", func_name
printf " \"signature\": \"%s\",\n", signature
printf " \"error_messages\": [%s],\n", error_msgs
printf " \"code_snippet\": %s\n", code_snippet
printf " }"
}
# Start new function
func_name = $0
gsub(/^fn /, "", func_name)
gsub(/\(.*/, "", func_name)
signature = $0
gsub(/"/, "\\\"", signature)
error_msgs = ""
code_snippet = "\"...\""
first_func = (first_func == "") ? "true" : first_func
}
/anyhow::anyhow!\("/ {
# Extract error message
msg = $0
gsub(/.*anyhow::anyhow!\("/, "", msg)
gsub(/".*/, "", msg)
gsub(/"/, "\\\"", msg)
if (error_msgs != "") error_msgs = error_msgs ", "
error_msgs = error_msgs "\"" msg "\""
}
END {
# Output last function
if (func_name != "") {
if (first_func != "true") {
print ","
}
printf " {\n"
printf " \"function\": \"%s\",\n", func_name
printf " \"signature\": \"%s\",\n", signature
printf " \"error_messages\": [%s],\n", error_msgs
printf " \"code_snippet\": %s\n", code_snippet
printf " }"
}
}
'
fi
# Close validation_functions array and JSON
cat << EOF
]
}
EOF
@@ -0,0 +1,126 @@
#!/bin/bash
# End-to-end pipeline test
# Usage: ./run-pipeline.sh <old_version> <new_version>
# Example: ./run-pipeline.sh v1.9.0 v1.15.0
set -e
OLD_VERSION=${1:-"v1.9.0"}
NEW_VERSION=${2:-"v1.15.0"}
echo "=========================================="
echo "Recipe Validation Documentation Pipeline"
echo "=========================================="
echo "Old Version: $OLD_VERSION"
echo "New Version: $NEW_VERSION"
echo ""
# Change to output directory
cd "$(dirname "$0")/../output"
echo "Step 1: Extracting validation structure from $OLD_VERSION..."
if ! ../scripts/extract-validation-structure.sh "$OLD_VERSION" > old-validation-structure.json 2>&1; then
echo "✗ Failed to extract validation structure from $OLD_VERSION" >&2
echo "Error output:" >&2
cat old-validation-structure.json >&2
exit 1
fi
echo "✓ Extracted $(jq '.struct_fields | length' old-validation-structure.json) fields, $(jq '.validation_functions | length' old-validation-structure.json) functions"
echo ""
echo "Step 1b: Extracting schema from $OLD_VERSION..."
if ! ../scripts/extract-schema.sh "$OLD_VERSION" > old-schema.json 2>&1; then
echo "✗ Failed to extract schema from $OLD_VERSION" >&2
echo "Error output:" >&2
cat old-schema.json >&2
exit 1
fi
echo "✓ Extracted schema ($(jq '.properties | length' old-schema.json) properties)"
echo ""
echo "Step 2: Extracting validation structure from $NEW_VERSION..."
if ! ../scripts/extract-validation-structure.sh "$NEW_VERSION" > new-validation-structure.json 2>&1; then
echo "✗ Failed to extract validation structure from $NEW_VERSION" >&2
echo "Error output:" >&2
cat new-validation-structure.json >&2
exit 1
fi
echo "✓ Extracted $(jq '.struct_fields | length' new-validation-structure.json) fields, $(jq '.validation_functions | length' new-validation-structure.json) functions"
echo ""
echo "Step 2b: Extracting schema from $NEW_VERSION..."
if ! ../scripts/extract-schema.sh "$NEW_VERSION" > new-schema.json 2>&1; then
echo "✗ Failed to extract schema from $NEW_VERSION" >&2
echo "Error output:" >&2
cat new-schema.json >&2
exit 1
fi
echo "✓ Extracted schema ($(jq '.properties | length' new-schema.json) properties)"
echo ""
echo "Step 3: Comparing validation structures..."
../scripts/diff-validation-structures.sh old-validation-structure.json new-validation-structure.json > validation-changes.json 2>&1
HAS_CHANGES=$(jq -r '.has_changes' validation-changes.json)
echo "✓ Comparison complete. Has changes: $HAS_CHANGES"
if [ "$HAS_CHANGES" = "true" ]; then
echo ""
echo "Changes detected:"
echo " - Fields added: $(jq '.changes.struct_fields.added | length' validation-changes.json)"
echo " - Fields removed: $(jq '.changes.struct_fields.removed | length' validation-changes.json)"
echo " - Fields type changed: $(jq '.changes.struct_fields.type_changed | length' validation-changes.json)"
echo " - Fields comment changed: $(jq '.changes.struct_fields.comment_changed | length' validation-changes.json)"
echo " - Validation functions added: $(jq '.changes.validation_functions.added | length' validation-changes.json)"
echo " - Validation functions removed: $(jq '.changes.validation_functions.removed | length' validation-changes.json)"
echo " - Validation functions signature changed: $(jq '.changes.validation_functions.signature_changed | length' validation-changes.json)"
echo " - Validation functions error messages changed: $(jq '.changes.validation_functions.error_messages_changed | length' validation-changes.json)"
echo ""
echo "Step 4: Synthesizing validation changes documentation..."
# Run goose and capture output, filtering out session logs
goose run --recipe ../recipes/synthesize-validation-changes.yaml 2>&1 | \
sed -E 's/\x1B\[[0-9;]*[mK]//g' | \
grep -v "^starting session" | \
grep -v "^ session id:" | \
grep -v "^ working directory:" | \
grep -v "^─── text_editor" | \
grep -v "^path:" | \
grep -v "^command:" | \
grep -v "^Closing session" | \
grep -v "^Loading recipe:" | \
grep -v "^Description:" | \
sed '/^$/N;/^\n$/D' > validation-changes.md.tmp
# Check if we got meaningful content (more than just whitespace)
if [ -s validation-changes.md.tmp ] && grep -q "# Recipe Validation Changes" validation-changes.md.tmp; then
mv validation-changes.md.tmp validation-changes.md
echo "✓ Generated validation-changes.md ($(wc -l < validation-changes.md) lines)"
echo ""
echo "=========================================="
echo "Pipeline Complete!"
echo "=========================================="
echo ""
echo "Output files:"
echo " - old-validation-structure.json"
echo " - old-schema.json"
echo " - new-validation-structure.json"
echo " - new-schema.json"
echo " - validation-changes.json"
echo " - validation-changes.md"
echo ""
echo "Review validation-changes.md for documentation updates."
else
echo "✗ Failed to generate validation-changes.md"
exit 1
fi
else
echo ""
echo "=========================================="
echo "No Changes Detected"
echo "=========================================="
echo ""
echo "No validation changes between $OLD_VERSION and $NEW_VERSION."
echo "Documentation update not needed."
fi