From 26a1cf9ec418bff0c29be906309ee1ccd795205d Mon Sep 17 00:00:00 2001 From: Christopher Strecker <3030358+StreckerCM@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:01:02 -0500 Subject: [PATCH 1/3] ci: Standardize CI/CD - split into 3 workflows with artifact retention - Split single build.yml into build, preview-release, production-release - Dev builds: test results only on failure (3-day retention), no build artifacts - Preview: MSI + GitHub Pre-release (90-day retention) - Production: MSI + GitHub Release (90-day retention) - Add development branch to pipeline: feature/* -> development -> preview -> master Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- .github/workflows/build.yml | 165 ++--------------------- .github/workflows/preview-release.yml | 122 +++++++++++++++++ .github/workflows/production-release.yml | 125 +++++++++++++++++ 3 files changed, 261 insertions(+), 151 deletions(-) create mode 100644 .github/workflows/preview-release.yml create mode 100644 .github/workflows/production-release.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fb2c048..bee52f0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,23 +1,19 @@ -name: Build and Package +name: Build and Test on: push: - branches: [ master, preview ] + branches: [ development, 'feature/**' ] pull_request: - branches: [ master, preview ] + branches: [ development ] workflow_dispatch: env: SOLUTION_FILE: GeoMagGUI.sln - MAIN_PROJECT: GeoMagGUI CONFIGURATION: Release jobs: - build: + build-and-test: runs-on: windows-latest - outputs: - full_version: ${{ steps.version.outputs.full_version }} - artifact_suffix: ${{ steps.version.outputs.artifact_suffix }} steps: - name: Checkout code @@ -29,41 +25,20 @@ jobs: id: version shell: pwsh run: | - # Read base version from Version.props [xml]$versionProps = Get-Content "Version.props" $major = $versionProps.Project.PropertyGroup.MajorVersion $minor = $versionProps.Project.PropertyGroup.MinorVersion $patch = $versionProps.Project.PropertyGroup.PatchVersion - $baseVersion = "$major.$minor.$patch" - - # Determine channel and build number based on branch - $branch = "${{ github.ref_name }}" $runNumber = "${{ github.run_number }}" + $fullVersion = "$baseVersion.$runNumber" + $version = "$baseVersion-dev.$runNumber" - if ($branch -eq "master") { - $channel = "release" - $fullVersion = "$baseVersion.0" - $artifactSuffix = "" - } elseif ($branch -eq "preview") { - $channel = "preview" - $fullVersion = "$baseVersion.$runNumber" - $artifactSuffix = "-preview.$runNumber" - } else { - $channel = "dev" - $fullVersion = "$baseVersion.$runNumber" - $artifactSuffix = "-dev.$runNumber" - } - - Write-Host "Branch: $branch" - Write-Host "Channel: $channel" + Write-Host "Version: $version" Write-Host "Full Version: $fullVersion" - Write-Host "Artifact Suffix: $artifactSuffix" - echo "base_version=$baseVersion" >> $env:GITHUB_OUTPUT - echo "full_version=$fullVersion" >> $env:GITHUB_OUTPUT - echo "channel=$channel" >> $env:GITHUB_OUTPUT - echo "artifact_suffix=$artifactSuffix" >> $env:GITHUB_OUTPUT + echo "VERSION=$version" >> $env:GITHUB_OUTPUT + echo "FULL_VERSION=$fullVersion" >> $env:GITHUB_OUTPUT - name: Setup MSBuild uses: microsoft/setup-msbuild@v2 @@ -77,16 +52,14 @@ jobs: - name: Update AssemblyInfo versions shell: pwsh run: | - $version = "${{ steps.version.outputs.full_version }}" + $version = "${{ steps.version.outputs.FULL_VERSION }}" - # Update GeoMagGUI AssemblyInfo $guiAssemblyInfo = "GeoMagGUI\Properties\AssemblyInfo.cs" $content = Get-Content $guiAssemblyInfo -Raw $content = $content -replace 'AssemblyVersion\("[^"]+"\)', "AssemblyVersion(`"$version`")" $content = $content -replace 'AssemblyFileVersion\("[^"]+"\)', "AssemblyFileVersion(`"$version`")" Set-Content $guiAssemblyInfo $content -NoNewline - # Update GeoMagSharp AssemblyInfo $sharpAssemblyInfo = "GeoMagSharp\Properties\AssemblyInfo.cs" $content = Get-Content $sharpAssemblyInfo -Raw $content = $content -replace 'AssemblyVersion\("[^"]+"\)', "AssemblyVersion(`"$version`")" @@ -99,13 +72,10 @@ jobs: run: msbuild ${{ env.SOLUTION_FILE }} /p:Configuration=${{ env.CONFIGURATION }} /p:Platform="Mixed Platforms" /m - name: Run tests - continue-on-error: true shell: pwsh run: | - # Find VSTest console $vsTestPath = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Workload.ManagedDesktop -find Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe if (-not $vsTestPath) { - # Fallback to common paths $vsTestPath = Get-ChildItem -Path "${env:ProgramFiles}\Microsoft Visual Studio" -Recurse -Filter "vstest.console.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName } @@ -115,118 +85,11 @@ jobs: Write-Host "VSTest not found, skipping tests" } - - name: Upload test results + - name: Upload test results (only on failure) uses: actions/upload-artifact@v4 - if: always() + if: failure() with: - name: test-results${{ steps.version.outputs.artifact_suffix }} + name: test-results-dev-${{ steps.version.outputs.VERSION }} path: TestResults/*.trx if-no-files-found: ignore - - # Installer steps only run on push events (not pull requests) - - name: Install WiX Toolset - if: github.event_name == 'push' - shell: pwsh - run: | - dotnet tool install --global wix --version 5.0.2 - - - name: Generate WiX file components - if: github.event_name == 'push' - shell: pwsh - run: | - $buildOutput = "GeoMagGUI\bin\Release" - $version = "${{ steps.version.outputs.full_version }}" - - # Run the component generation script - & "Installer\Generate-FileComponents.ps1" -BuildOutputPath $buildOutput -Version $version - - - name: Build MSI installer - if: github.event_name == 'push' - shell: pwsh - run: | - $version = "${{ steps.version.outputs.full_version }}" - $channel = "${{ steps.version.outputs.channel }}" - - cd Installer - - # Build the MSI - wix build -arch x86 ` - -d Version=$version ` - -d Channel=$channel ` - -d BuildOutput=..\GeoMagGUI\bin\Release ` - -out ..\artifacts\GeoMagGUI${{ steps.version.outputs.artifact_suffix }}.msi ` - Product.wxs ` - FileComponents.wxs - - - name: Upload MSI artifact - if: github.event_name == 'push' - uses: actions/upload-artifact@v4 - with: - name: GeoMagGUI${{ steps.version.outputs.artifact_suffix }}-msi - path: artifacts/*.msi - - - name: Upload build output - if: github.event_name == 'push' - uses: actions/upload-artifact@v4 - with: - name: GeoMagGUI${{ steps.version.outputs.artifact_suffix }}-bin - path: | - GeoMagGUI/bin/Release/ - !GeoMagGUI/bin/Release/**/*.pdb - - create-release: - needs: build - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' && github.event_name == 'push' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download MSI artifact - uses: actions/download-artifact@v4 - with: - name: GeoMagGUI-msi - path: artifacts - - - name: Generate release notes - shell: bash - run: | - cat > release_notes.md << EOF - ## What's New in v${{ needs.build.outputs.full_version }} - - ### Features & Improvements - - Added support for WMM2020+ coefficient file format (Issue #1) - - Upgraded to .NET Framework 4.8 - - Added CI/CD pipeline with automated MSI installer generation - - Added comprehensive unit tests for Calculator and ModelReader classes - - ### Bug Fixes - - Fixed critical latitude/longitude type mismatch in DMS input (Issue #2) - - Fixed cursor management with try-finally blocks (Issue #4) - - Fixed decimal date to DateTime conversion rounding issue - - Added input validation to ModelReader with detailed error messages (Issue #5) - - ### Issues Resolved - - #1 - NOAA WMM2020 coefficient file support - - #2 - Type mismatch: Longitude constructor used for Latitude - - #3 - Add unit tests for Calculator class - - #4 - Add try-finally for cursor management - - #5 - Add input validation to ModelReader - - --- - *Full changelog available in the [commit history](https://github.com/${{ github.repository }}/commits/master)* - EOF - - - name: Create Release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ needs.build.outputs.full_version }} - name: GeoMag # ${{ needs.build.outputs.full_version }} - body_path: release_notes.md - draft: false - files: artifacts/*.msi - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + retention-days: 3 diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml new file mode 100644 index 0000000..c324d60 --- /dev/null +++ b/.github/workflows/preview-release.yml @@ -0,0 +1,122 @@ +name: Preview Release + +on: + push: + branches: [ preview ] + workflow_dispatch: + +permissions: + contents: write + +env: + SOLUTION_FILE: GeoMagGUI.sln + CONFIGURATION: Release + +jobs: + build-and-release: + runs-on: windows-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Calculate version + id: version + shell: pwsh + run: | + [xml]$versionProps = Get-Content "Version.props" + $major = $versionProps.Project.PropertyGroup.MajorVersion + $minor = $versionProps.Project.PropertyGroup.MinorVersion + $patch = $versionProps.Project.PropertyGroup.PatchVersion + $baseVersion = "$major.$minor.$patch" + $runNumber = "${{ github.run_number }}" + $fullVersion = "$baseVersion.$runNumber" + $version = "$baseVersion-preview.$runNumber" + Write-Host "Version: $version" + echo "VERSION=$version" >> $env:GITHUB_OUTPUT + echo "FULL_VERSION=$fullVersion" >> $env:GITHUB_OUTPUT + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + + - name: Restore NuGet packages + run: nuget restore ${{ env.SOLUTION_FILE }} + + - name: Update AssemblyInfo versions + shell: pwsh + run: | + $version = "${{ steps.version.outputs.FULL_VERSION }}" + foreach ($path in @("GeoMagGUI\Properties\AssemblyInfo.cs", "GeoMagSharp\Properties\AssemblyInfo.cs")) { + $content = Get-Content $path -Raw + $content = $content -replace 'AssemblyVersion\("[^"]+"\)', "AssemblyVersion(`"$version`")" + $content = $content -replace 'AssemblyFileVersion\("[^"]+"\)', "AssemblyFileVersion(`"$version`")" + Set-Content $path $content -NoNewline + } + Write-Host "Updated assembly versions to $version" + + - name: Build solution + run: msbuild ${{ env.SOLUTION_FILE }} /p:Configuration=${{ env.CONFIGURATION }} /p:Platform="Mixed Platforms" /m + + - name: Run tests + shell: pwsh + run: | + $vsTestPath = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Workload.ManagedDesktop -find Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe + if (-not $vsTestPath) { + $vsTestPath = Get-ChildItem -Path "${env:ProgramFiles}\Microsoft Visual Studio" -Recurse -Filter "vstest.console.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName + } + if ($vsTestPath) { + & $vsTestPath "GeoMagSharp-UnitTests\bin\Release\GeoMagSharp-UnitTests.dll" --logger:trx --ResultsDirectory:TestResults + } else { + Write-Host "VSTest not found, skipping tests" + } + + - name: Install WiX Toolset + shell: pwsh + run: dotnet tool install --global wix --version 5.0.2 + + - name: Generate WiX file components + shell: pwsh + run: | + $buildOutput = "GeoMagGUI\bin\Release" + $version = "${{ steps.version.outputs.FULL_VERSION }}" + & "Installer\Generate-FileComponents.ps1" -BuildOutputPath $buildOutput -Version $version + + - name: Build MSI installer + shell: pwsh + run: | + $version = "${{ steps.version.outputs.FULL_VERSION }}" + cd Installer + wix build -arch x86 -d Version=$version -d Channel=preview -d BuildOutput=..\GeoMagGUI\bin\Release -out ..\artifacts\GeoMagGUI-${{ steps.version.outputs.VERSION }}.msi Product.wxs FileComponents.wxs + + - name: Generate release notes + shell: pwsh + run: | + $lastTag = $null + try { $lastTag = git describe --tags --match "v*-preview.*" --abbrev=0 2>$null } catch { } + if ($lastTag) { $commits = git log --oneline "$lastTag..HEAD" 2>$null } + else { $commits = git log --oneline -20 2>$null } + $version = "${{ steps.version.outputs.VERSION }}" + $notes = "Preview Release $version`n`n### Changes`n" + if ($commits) { $commits -split "`n" | ForEach-Object { $l = $_.Trim(); if ($l) { $notes += "- $l`n" } } } + else { $notes += "- Build from latest preview branch`n" } + Set-Content -Path "release-notes.md" -Value $notes -Encoding UTF8 + + - name: Create GitHub Pre-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: pwsh + run: | + $version = "${{ steps.version.outputs.VERSION }}" + gh release create "v$version" "artifacts\GeoMagGUI-$version.msi" --title "GeoMag GUI $version" --notes-file "release-notes.md" --prerelease + + - name: Upload MSI artifact + uses: actions/upload-artifact@v4 + with: + name: GeoMagGUI-preview-${{ steps.version.outputs.VERSION }}-msi + path: artifacts/*.msi + retention-days: 90 diff --git a/.github/workflows/production-release.yml b/.github/workflows/production-release.yml new file mode 100644 index 0000000..4aea57c --- /dev/null +++ b/.github/workflows/production-release.yml @@ -0,0 +1,125 @@ +name: Production Release + +on: + push: + branches: [ master ] + workflow_dispatch: + +permissions: + contents: write + +env: + SOLUTION_FILE: GeoMagGUI.sln + CONFIGURATION: Release + +jobs: + build-and-release: + runs-on: windows-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Calculate version + id: version + shell: pwsh + run: | + [xml]$versionProps = Get-Content "Version.props" + $major = $versionProps.Project.PropertyGroup.MajorVersion + $minor = $versionProps.Project.PropertyGroup.MinorVersion + $patch = $versionProps.Project.PropertyGroup.PatchVersion + $baseVersion = "$major.$minor.$patch" + $runNumber = "${{ github.run_number }}" + $fullVersion = "$baseVersion.$runNumber" + $version = "$baseVersion" + Write-Host "Version: $version" + echo "VERSION=$version" >> $env:GITHUB_OUTPUT + echo "FULL_VERSION=$fullVersion" >> $env:GITHUB_OUTPUT + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + + - name: Restore NuGet packages + run: nuget restore ${{ env.SOLUTION_FILE }} + + - name: Update AssemblyInfo versions + shell: pwsh + run: | + $version = "${{ steps.version.outputs.FULL_VERSION }}" + foreach ($path in @("GeoMagGUI\Properties\AssemblyInfo.cs", "GeoMagSharp\Properties\AssemblyInfo.cs")) { + $content = Get-Content $path -Raw + $content = $content -replace 'AssemblyVersion\("[^"]+"\)', "AssemblyVersion(`"$version`")" + $content = $content -replace 'AssemblyFileVersion\("[^"]+"\)', "AssemblyFileVersion(`"$version`")" + Set-Content $path $content -NoNewline + } + Write-Host "Updated assembly versions to $version" + + - name: Build solution + run: msbuild ${{ env.SOLUTION_FILE }} /p:Configuration=${{ env.CONFIGURATION }} /p:Platform="Mixed Platforms" /m + + - name: Run tests + shell: pwsh + run: | + $vsTestPath = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Workload.ManagedDesktop -find Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe + if (-not $vsTestPath) { + $vsTestPath = Get-ChildItem -Path "${env:ProgramFiles}\Microsoft Visual Studio" -Recurse -Filter "vstest.console.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName + } + if ($vsTestPath) { + & $vsTestPath "GeoMagSharp-UnitTests\bin\Release\GeoMagSharp-UnitTests.dll" --logger:trx --ResultsDirectory:TestResults + } else { + Write-Host "VSTest not found, skipping tests" + } + + - name: Install WiX Toolset + shell: pwsh + run: dotnet tool install --global wix --version 5.0.2 + + - name: Generate WiX file components + shell: pwsh + run: | + $buildOutput = "GeoMagGUI\bin\Release" + $version = "${{ steps.version.outputs.FULL_VERSION }}" + & "Installer\Generate-FileComponents.ps1" -BuildOutputPath $buildOutput -Version $version + + - name: Build MSI installer + shell: pwsh + run: | + $version = "${{ steps.version.outputs.FULL_VERSION }}" + cd Installer + wix build -arch x86 -d Version=$version -d Channel=stable -d BuildOutput=..\GeoMagGUI\bin\Release -out ..\artifacts\GeoMagGUI-${{ steps.version.outputs.VERSION }}.msi Product.wxs FileComponents.wxs + + - name: Generate release notes + shell: pwsh + run: | + $lastTag = $null + try { + $tags = git tag --list "v[0-9]*" --sort=-version:refname 2>$null + foreach ($t in ($tags -split "`n")) { $t = $t.Trim(); if ($t -and $t -notmatch '-') { $lastTag = $t; break } } + } catch { } + if ($lastTag) { $commits = git log --oneline "$lastTag..HEAD" 2>$null } + else { $commits = git log --oneline -30 2>$null } + $version = "${{ steps.version.outputs.VERSION }}" + $notes = "Release $version`n`n### Changes`n" + if ($commits) { $commits -split "`n" | ForEach-Object { $l = $_.Trim(); if ($l) { $notes += "- $l`n" } } } + else { $notes += "- Stable release from preview testing`n" } + Set-Content -Path "release-notes.md" -Value $notes -Encoding UTF8 + + - name: Create GitHub Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: pwsh + run: | + $version = "${{ steps.version.outputs.VERSION }}" + gh release create "v$version" "artifacts\GeoMagGUI-$version.msi" --title "GeoMag GUI $version" --notes-file "release-notes.md" + + - name: Upload MSI artifact + uses: actions/upload-artifact@v4 + with: + name: GeoMagGUI-stable-${{ steps.version.outputs.VERSION }}-msi + path: artifacts/*.msi + retention-days: 90 From 46e131c6ec50250ff6c7ccedd97139446d04c53b Mon Sep 17 00:00:00 2001 From: Christopher Strecker <3030358+StreckerCM@users.noreply.github.com> Date: Thu, 12 Feb 2026 23:10:43 -0500 Subject: [PATCH 2/3] ci: Add Claude Code permissions, slim CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CLAUDE.md: 129 → 80 lines (Ralph Loop moved to @refs, architecture in AGENTS.md) - New .claude/settings.json with scoped read/edit/bash permissions - VS 2022 full-path MSBuild/vstest fallback rules - Add Session Start Protocol for cross-session continuity - .gitignore: track .claude/settings.json, ignore local files Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- .claude/settings.json | 45 ++++++++++++++ .gitignore | 5 ++ CLAUDE.md | 136 ++++++++++++++---------------------------- 3 files changed, 94 insertions(+), 92 deletions(-) create mode 100644 .claude/settings.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..72c82ef --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,45 @@ +{ + "permissions": { + "allow": [ + "Read(//C:/GitHub/**)", + "Read(//C:/Program Files/Microsoft Visual Studio/**)", + + "Edit(/GeoMagGUI/**)", + "Edit(/GeoMagSharp/**)", + "Edit(/GeoMagSharp-UnitTests/**)", + "Edit(/Installer/**)", + "Edit(/docs/**)", + "Edit(/.github/**)", + "Edit(/.claude/**)", + "Edit(/*.md)", + "Edit(/*.sln)", + "Edit(/*.props)", + "Edit(/*.json)", + "Edit(/.gitignore)", + + "Bash(msbuild *)", + "Bash(\"C:/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe\" *)", + "Bash(vstest.console.exe *)", + "Bash(\"C:/Program Files/Microsoft Visual Studio/2022/Community/Common7/IDE/CommonExtensions/Microsoft/TestWindow/vstest.console.exe\" *)", + "Bash(nuget *)", + "Bash(git status *)", + "Bash(git log *)", + "Bash(git diff *)", + "Bash(git branch *)", + "Bash(git checkout *)", + "Bash(git stash *)", + "Bash(git ls-tree *)", + "Bash(git remote *)", + "Bash(gh pr *)", + "Bash(gh issue *)", + "Bash(gh api *)", + "Bash(gh run *)" + ], + "deny": [ + "Bash(git push --force *)", + "Bash(git reset --hard *)", + "Bash(rm -rf *)", + "Bash(del /s *)" + ] + } +} diff --git a/.gitignore b/.gitignore index d14ac0f..baa6778 100644 --- a/.gitignore +++ b/.gitignore @@ -184,3 +184,8 @@ appsettings.local.json secrets.json .env .env.* + +# Claude Code local files (allow settings.json to be tracked) +.claude/* +!.claude/settings.json +*.local.md diff --git a/CLAUDE.md b/CLAUDE.md index 4499774..057242b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,123 +6,75 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co GeoMagSharpGUI is a Windows desktop application for geomagnetic field calculations using spherical harmonic models (IGRF, WMM, EMM). It provides a GUI for calculating magnetic declination, inclination, and field strength at any location and date. -**Tech Stack:** .NET Framework 4.0 WinForms application (C#), Visual Studio - -For detailed project structure, architecture, coding conventions, and build commands, see `AGENTS.md`. +**Tech Stack:** .NET Framework 4.0 WinForms application (C#), x86 platform ## Build Commands ```bash -# Debug build -msbuild GeoMagGUI.sln /p:Configuration=Debug /p:Platform="x86" - # Release build msbuild GeoMagGUI.sln /p:Configuration=Release /p:Platform="x86" +# Debug build +msbuild GeoMagGUI.sln /p:Configuration=Debug /p:Platform="x86" + # Run unit tests vstest.console.exe GeoMagSharp-UnitTests\bin\Debug\GeoMagSharp-UnitTests.dll ``` -## Development Workflow - -### MANDATORY: Ralph Loop for ALL Feature Work - -**This is NON-NEGOTIABLE.** Every feature branch (`feature/*`) MUST use the Ralph Wiggum loop (`/ralph-loop`) with rotating personas. There are NO exceptions to this rule, regardless of how simple the task appears. - -**Before writing ANY code on a feature branch, you MUST:** +## Session Start Protocol -1. Verify a `docs/features//tasks.md` file exists -2. If it doesn't exist, create one before proceeding -3. Start a Ralph Loop with the rotating persona pattern +At the start of every session: +1. Read `docs/features/ACTIVE_WORK.md` if it exists +2. Check auto memory (`MEMORY.md`) for prior context +3. Run `git log --oneline -10` and `git status` to understand current state +4. Ask the user what they'd like to work on before making assumptions -**If you find yourself on a feature branch writing code without an active Ralph Loop, STOP and start one.** +Before ending a session or when context is getting long: +1. Update `docs/features/ACTIVE_WORK.md` with current progress, decisions made, and next steps +2. Save any important patterns or learnings to auto memory -### Step 1: Create a GitHub Issue (if one doesn't exist) +## Branching Strategy -Every feature must have a corresponding GitHub issue before work begins. +4-branch flow: `feature/*` -> `development` -> `preview` -> `master` -### Step 2: Create and Switch to a Feature Branch +| Branch | Purpose | Version Format | Workflow | +|--------|---------|----------------|----------| +| `master` | Production releases | `X.Y.Z` | `production-release.yml` | +| `preview` | Pre-release testing | `X.Y.Z-preview.N` | `preview-release.yml` | +| `development` | Integration | `X.Y.Z-dev.N` | `build.yml` | +| `feature/*` | Development work | `X.Y.Z-dev.N` | `build.yml` | -```bash -git checkout master -git pull origin master -git checkout -b feature/- -``` - -### Step 3: Create or Verify tasks.md (GATE - Required Before Any Code) +Feature branches are created from `development`. PRs flow: `feature/*` -> `development` -> `preview` -> `master`. -Every feature MUST have a `docs/features//tasks.md` file. This file is the **single source of truth** for what work needs to be done. No code should be written until this file exists and has been reviewed. +### Branch Protection Rules -**tasks.md format:** -```markdown -# Feature: -Issue: # -Branch: feature/- - -## Tasks -- [ ] Task 1 description -- [ ] Task 2 description -- [ ] Task 3 description - -## Completion Criteria -- [ ] All tasks checked -- [ ] Build succeeds -- [ ] Tests pass -- [ ] 2 clean Ralph Loop cycles -``` +- **NEVER** commit directly to `master`, `preview`, or `development`. All changes via PRs only. +- **NEVER** force-push to protected branches. +- **NEVER** create or merge a PR without explicit user confirmation. Draft PRs are acceptable without confirmation. +- All development work happens on `feature/*` branches (only place direct commits are allowed). -### Step 4: Start a Ralph Loop (MANDATORY) - -Use the Ralph Wiggum loop with the rotating persona pattern defined in `docs/prompts/PERSONAS.md`. See the "Ralph Loop / Iterative Development" section below for the full pattern and completion criteria. - -## Ralph Loop / Iterative Development +## Development Workflow -**MANDATORY — NO EXCEPTIONS:** ALL feature branch work (`feature/*`) MUST use Ralph loops with rotating personas. This applies regardless of feature size, complexity, or urgency. Skipping the Ralph Loop is never acceptable. +**MANDATORY:** Every `feature/*` branch MUST use Ralph Loop (`/ralph-loop`) with rotating personas before any code is written. -### Pre-Flight Checklist (Before Starting Ralph Loop) +**Before writing ANY code on a feature branch:** +1. Ensure a GitHub issue exists +2. Create `docs/features//tasks.md` with task breakdown +3. Start a Ralph Loop with rotating persona pattern -Before starting any Ralph Loop, verify: +See `docs/prompts/` for full Ralph Loop documentation and persona definitions. -- [ ] GitHub issue exists for this feature -- [ ] Feature branch created -- [ ] `docs/features//tasks.md` exists with task breakdown -- [ ] PR created (draft is fine) to track work +## Platform Constraints -If any of these are missing, create them first. **Do NOT start coding without tasks.md.** +- x86 architecture (set in project files) +- Windows-only (.NET Framework 4.0) +- Requires Visual Studio Developer Command Prompt for builds -### Required Persona Rotation +## Extended Documentation -``` -Iteration % 6 determines the current persona: - -[0] #5 IMPLEMENTER - Complete tasks, write code -[1] #9 REVIEWER - Review for bugs, code quality -[2] #7 TESTER - Verify functionality, add tests -[3] #3 UI_UX_DESIGNER - Review UI/UX, accessibility -[4] #10 SECURITY - Security review, input validation -[5] #2 PROJECT_MGR - Check requirements, update tasks -``` +For detailed information, read these files on demand (not loaded every session): -### Each Iteration Must: -1. Identify the current persona based on iteration number -2. Follow that persona's mindset and output format from `docs/prompts/PERSONAS.md` -3. Commit with persona prefix: `[IMPLEMENTER]`, `[REVIEWER]`, etc. -4. Reference the feature's `tasks.md` file and mark tasks complete -5. Post a PR comment summarizing findings and changes - -### Completion Criteria -- All tasks in `docs/features/[feature]/tasks.md` marked complete -- Build succeeds with no errors -- Tests pass -- **2 clean cycles** (all 6 personas find no issues twice) - -### Why This Matters - -The Ralph Loop ensures: -- Multiple perspectives review every change (code quality, security, UX, testing) -- Issues are caught early through systematic rotation -- Progress is tracked via tasks.md -- An audit trail exists via PR comments from each persona -- Features meet a consistent quality bar before merging - -See `docs/prompts/README.md` and `docs/prompts/templates/ROTATING_FEATURE.md` for full documentation. +- **Architecture & Conventions:** `@AGENTS.md` -- Solution structure, coding conventions, key classes +- **Ralph Loop:** `@docs/prompts/README.md` -- Iterative development workflow with rotating personas +- **Personas:** `@docs/prompts/PERSONAS.md` -- 11 development personas for Ralph Loop rotation +- **Feature Templates:** `@docs/prompts/templates/ROTATING_FEATURE.md` -- Ralph Loop prompt templates From 3127c3cea25bf49a995ccc41d25d3ccb871bc6cd Mon Sep 17 00:00:00 2001 From: Christopher Strecker <3030358+StreckerCM@users.noreply.github.com> Date: Thu, 12 Feb 2026 23:33:34 -0500 Subject: [PATCH 3/3] ci: Trim ROTATING_FEATURE.md - remove verbose examples Reduced from ~190 to ~70 lines by removing project-specific examples that duplicated the template with filled-in brackets. Kept standard and compact rotation variants and commit/PR format reference. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- docs/prompts/templates/ROTATING_FEATURE.md | 164 ++++----------------- 1 file changed, 29 insertions(+), 135 deletions(-) diff --git a/docs/prompts/templates/ROTATING_FEATURE.md b/docs/prompts/templates/ROTATING_FEATURE.md index 9637580..b2a03f3 100644 --- a/docs/prompts/templates/ROTATING_FEATURE.md +++ b/docs/prompts/templates/ROTATING_FEATURE.md @@ -1,22 +1,9 @@ # Rotating Persona Feature Implementation -This template uses rotating personas that cycle through different perspectives on each iteration, ensuring comprehensive review from multiple angles. - -## How It Works - -Each iteration, the persona changes based on `iteration % N`: -- Iteration 0, 6, 12... → Persona 0 -- Iteration 1, 7, 13... → Persona 1 -- And so on... - -This ensures every perspective reviews the work multiple times. - ---- +Each iteration, the persona changes based on `iteration % N` — ensuring every perspective reviews the work multiple times. ## Standard 6-Persona Rotation -Best for most feature implementations: - ```bash /ralph-loop " Feature: [FEATURE_NUMBER]-[feature-name] @@ -31,113 +18,19 @@ PHASE 1 - TASK COMPLETION: PHASE 2 - ROTATING PERSONA REVIEW (cycle each iteration): Current Persona = ITERATION MOD 6: -[0] #5 IMPLEMENTER: -- Check tasks.md for incomplete items -- Implement next unchecked task -- Follow existing code patterns -- Run build after changes - -[1] #9 CODE REVIEWER: -- Review recent changes for bugs, edge cases -- Check error handling and null safety -- Verify code follows project patterns -- Fix any issues found - -[2] #7 TESTER: -- Run: vstest.console.exe GeoMagSharp-UnitTests\\bin\\Debug\\GeoMagSharp-UnitTests.dll -- Check test coverage for new code -- Write missing unit tests -- Verify edge cases are covered - -[3] #3 UI_UX_DESIGNER (if UI changes): -- Review WinForms layout and controls -- Check tab order and keyboard navigation -- Verify consistent styling -- Check visual states (enabled, disabled, error) - -[4] #10 SECURITY_AUDITOR: -- Check for hardcoded values that should be config -- Verify input validation for coordinates -- Look for potential security issues -- Review any new file I/O code - -[5] #2 PROJECT_MANAGER: -- Verify all tasks in tasks.md are checked -- Check spec.md requirements are met -- Document any gaps or issues found -- Update tasks.md if new work discovered +[0] #5 IMPLEMENTER: Complete next task, follow patterns, run build +[1] #9 CODE REVIEWER: Review for bugs/edge cases, check patterns, fix issues +[2] #7 TESTER: Run vstest.console.exe, check coverage, write missing tests +[3] #3 UI_UX_DESIGNER: Review WinForms consistency, tab order, visual states +[4] #10 SECURITY_AUDITOR: Check config values, input validation, file I/O +[5] #2 PROJECT_MANAGER: Verify tasks complete, check requirements, update tasks.md EACH ITERATION: 1. Identify current persona (Iteration % 6) 2. Perform that persona's review/work -3. Make improvements or fixes as needed -4. Commit changes with message: '[persona] description' -5. If all tasks complete AND no issues found by ANY persona for 2 full cycles (12 iterations), output completion - -OUTPUT FEATURE COMPLETE when: -- All tasks in tasks.md are checked [x] -- Build succeeds with no errors -- All personas report no issues for 2 consecutive cycles -" --completion-promise "FEATURE COMPLETE" --max-iterations 30 -``` - ---- - -## Example: Add New Magnetic Model Support - -```bash -/ralph-loop " -Feature: emm-model-support -Branch: feature/emm-model-support - -PHASE 1 - TASK COMPLETION: -- Read docs/features/emm-model-support/tasks.md -- If any tasks unchecked (- [ ]), complete them first -- Mark completed tasks (- [x]) as you finish them -- Run: msbuild GeoMagGUI.sln /p:Configuration=Debug /p:Platform=\"x86\" - -PHASE 2 - ROTATING PERSONA REVIEW (cycle each iteration): -Current Persona = ITERATION MOD 6: - -[0] #5 IMPLEMENTER: -- Check tasks.md for incomplete items -- Implement next unchecked task -- Follow existing ModelReader patterns -- Run build after changes - -[1] #9 CODE REVIEWER: -- Review recent changes for bugs, edge cases -- Check coefficient parsing is correct -- Verify error handling for malformed files -- Fix any issues found - -[2] #7 TESTER: -- Run unit tests -- Add tests for EMM file parsing -- Verify calculations match reference values -- Test edge cases (min/max dates, boundaries) - -[3] #3 UI_UX_DESIGNER: -- Verify EMM appears correctly in model dropdown -- Check model info display is consistent -- Verify date range validation UI - -[4] #10 SECURITY_AUDITOR: -- Check EMM file parsing is safe -- Verify no path traversal vulnerabilities -- Review coefficient array bounds - -[5] #2 PROJECT_MANAGER: -- Verify all tasks in tasks.md are checked -- Ensure backward compatibility maintained -- Document any remaining work - -EACH ITERATION: -1. Identify current persona (Iteration % 6) -2. Perform that persona's review/work -3. Make improvements or fixes as needed -4. Commit changes with message: '[persona] description' -5. If all tasks complete AND no issues found for 2 full cycles, output completion +3. Commit: '[persona] description' +4. Post PR comment with findings/changes +5. If all tasks complete AND 2 clean cycles (12 iterations), output completion OUTPUT FEATURE COMPLETE when: - All tasks in tasks.md are checked [x] @@ -146,43 +39,44 @@ OUTPUT FEATURE COMPLETE when: " --completion-promise "FEATURE COMPLETE" --max-iterations 30 ``` ---- - ## Compact 4-Persona Rotation (Faster) -For simpler features or when speed is preferred: +For simpler features: ```bash /ralph-loop " Feature: [FEATURE_NUMBER]-[feature-name] ROTATING PERSONA (ITERATION MOD 4): - [0] #5 IMPLEMENTER: Complete next task from tasks.md, run build [1] #9 REVIEWER: Review code for bugs/issues, fix problems [2] #7 TESTER: Verify functionality, add tests if needed [3] #2 PROJECT_MANAGER: Check all requirements met, update tasks.md -EACH ITERATION: -1. Run current persona's checks -2. Make one fix/improvement -3. Commit: '[persona] description' +EACH ITERATION: Run persona checks, commit '[persona] description', post PR comment. OUTPUT DONE when all tasks complete and 2 clean cycles. " --completion-promise "DONE" --max-iterations 20 ``` ---- +## Commit & PR Format -## Commit Message Format +**Commits:** `[IMPLEMENTER] Add feature X` / `[REVIEWER] Fix null check` / `[TESTER] Add unit test for Y` -Each commit should indicate which persona made the change: +**PR Comments:** +```markdown +## [PERSONA] Review - Iteration N -``` -[IMPLEMENTER] Add EMM coefficient file reader -[REVIEWER] Fix array bounds check in ModelReader -[TESTER] Add unit tests for EMM date validation -[UI_UX_DESIGNER] Update model dropdown styling -[SECURITY_AUDITOR] Add input validation for model paths -[PROJECT_MANAGER] Mark EMM support tasks complete +### Summary +[Brief description] + +### Findings +- [Issues or observations] + +### Changes Made +- [file:line - description] + +### Status +- [ ] Issues found requiring follow-up +- [x] Clean pass - no issues found ```