diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..c383bb2
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,44 @@
+name: CI
+
+on:
+ pull_request:
+ push:
+ branches: [main]
+
+permissions:
+ contents: read
+
+jobs:
+ build-test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # MinVer needs full history and tags
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 8.0.x
+
+ - name: Restore
+ run: dotnet restore ArgSentry.sln
+
+ - name: Build
+ run: dotnet build ArgSentry.sln --configuration Release --no-restore
+
+ - name: Test with coverage
+ run: >
+ dotnet test test/ArgSentry.Test/ArgSentry.Test.csproj
+ --configuration Release --no-build
+ /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
+ /p:CoverletOutput=./coverage/
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v4
+ with:
+ files: test/ArgSentry.Test/coverage/coverage.opencover.xml
+ fail_ci_if_error: false
+ token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..747f8db
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,50 @@
+name: Release
+
+on:
+ push:
+ branches: [main]
+ tags: ['v*.*.*']
+
+permissions:
+ contents: write # needed to create GitHub Releases on tag pushes
+
+jobs:
+ pack-publish:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # MinVer needs full history and tags
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 8.0.x
+
+ - name: Restore
+ run: dotnet restore ArgSentry.sln
+
+ - name: Build
+ run: dotnet build src/ArgSentry/ArgSentry.csproj --configuration Release --no-restore
+
+ - name: Pack
+ run: >
+ dotnet pack src/ArgSentry/ArgSentry.csproj
+ --configuration Release --no-build
+ --output ./dist
+
+ - name: Push to NuGet
+ run: >
+ dotnet nuget push "./dist/*.nupkg"
+ --api-key ${{ secrets.NUGET_API_KEY }}
+ --source https://api.nuget.org/v3/index.json
+ --skip-duplicate
+
+ - name: Create GitHub Release
+ if: startsWith(github.ref, 'refs/tags/v')
+ uses: softprops/action-gh-release@v2
+ with:
+ generate_release_notes: true
+ files: ./dist/*.nupkg
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..4f416b0
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,33 @@
+# Contributing
+
+## Branching and Pull Requests
+
+This project uses a trunk-based development model. `main` is the only long-lived branch.
+
+1. Create a short-lived feature branch off `main`.
+2. Open a Pull Request targeting `main`.
+3. The `CI` workflow (`build-test` job) must pass.
+4. Merges must result in linear history. Use squash or rebase merges.
+
+## Releases
+
+Releases are fully automated.
+
+* **Prereleases:** Every merge to `main` triggers the `release.yml` workflow, which publishes a prerelease NuGet package (e.g., `3.1.0-preview.0.7`). No action is required from contributors.
+* **Stable Releases:** The maintainer cuts stable releases by pushing an annotated git tag matching `vX.Y.Z` (e.g. `git tag -a v3.1.0 -m "v3.1.0" && git push origin v3.1.0`). This builds a clean `X.Y.Z` package, publishes it to NuGet, and creates a GitHub Release with auto-generated notes.
+
+## Versioning
+
+Versioning is handled automatically via [MinVer](https://github.com/adamralph/minver) based on git tags.
+
+* Do not manually edit `AssemblyVersion`, `FileVersion`, or `InformationalVersion` in the `.csproj` file.
+* Version numbers are derived from the latest git tag.
+
+## Local Development
+
+To build and test the project locally:
+
+```bash
+dotnet build
+dotnet test
+```
diff --git a/README.md b/README.md
index b76d436..5b13b12 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ ArgSentry


-[](https://ci.appveyor.com/project/eric-davis/argsentry)
-[](https://codecov.io/gh/eric-davis/ArgSentry)
+[](https://github.com/eric-davis/ArgSentry/actions/workflows/ci.yml)
+[](https://codecov.io/gh/eric-davis/ArgSentry)
ArgSentry is a .NET / .NET Core utility library for validating method argument values.
diff --git a/src/ArgSentry/ArgSentry.csproj b/src/ArgSentry/ArgSentry.csproj
index 2c3f1f3..6c3ce7e 100644
--- a/src/ArgSentry/ArgSentry.csproj
+++ b/src/ArgSentry/ArgSentry.csproj
@@ -18,10 +18,10 @@
netstandard1.1
-
-