Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2291dbe
Fix update-packages crash when outdated package JSON omits projects key
tjementum May 31, 2026
43e01b5
Pin ApplicationInsights to its current major and add quiet mode to th…
tjementum Jun 22, 2026
7e97545
Rewrite the upgrade-packages skill to drive the update-packages comma…
tjementum Jun 22, 2026
11da323
Restore dotnet tools after bumping the tool manifest in the update-pa…
tjementum Jun 22, 2026
88e5089
Upgrade backend NuGet packages, dotnet tools, and SDK to latest minor…
tjementum Jun 22, 2026
3ef446a
Upgrade Aspire.Azure.Storage.Blobs to 13.4.6 and adapt to the new Get…
tjementum Jun 22, 2026
4b2ed89
Upgrade Azure.Monitor.OpenTelemetry.AspNetCore to 1.5.0 and wire Azur…
tjementum Jun 22, 2026
2badcd9
Upgrade Mapster to 10.0.8 and handle the now-nullable Adapt return in…
tjementum Jun 22, 2026
0d90880
Upgrade JetBrains.Annotations to 2026.2.0
tjementum Jun 22, 2026
10d52eb
Upgrade frontend npm dependencies to latest minor and patch versions
tjementum Jun 22, 2026
66dcfe1
Upgrade oxlint to 1.71.0, fix new accessibility findings, and disable…
tjementum Jun 22, 2026
b56d031
Upgrade react-day-picker to 10.0.1
tjementum Jun 22, 2026
4e485e5
Upgrade actions/checkout to v7 and actions/github-script to v9
tjementum Jun 22, 2026
bf2c01f
Adopt the resilient route teardown helper for the Playwright upgrade …
tjementum Jun 22, 2026
a0f17ff
Pin SQLitePCLRaw.lib.e_sqlite3 to 3.50.3 to clear the SQLite native-l…
tjementum Jun 22, 2026
88f09f6
Upgrade Scalar.AspNetCore and Scriban to latest patch versions
tjementum Jun 22, 2026
62cc979
Upgrade react-email to latest patch version
tjementum Jun 22, 2026
3ad00cb
Upgrade TypeScript to 6.0.3 and override the openapi-typescript types…
tjementum Jun 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 80 additions & 23 deletions .claude/skills/upgrade-packages/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,99 @@
---
name: upgrade-packages
description: Upgrade all backend (.NET/NuGet) and frontend (npm) dependencies to the latest versions. Drives the developer CLI's update-packages command, fixes the CLI itself when it produces a wrong outcome, and produces clean per-package commits for any upgrade that needs more than a version bump.
description: Upgrade all backend (.NET/NuGet), frontend (npm), and GitHub Actions dependencies to their latest versions, in that fixed order. Drives the developer CLI's update-packages command, parses its quiet dry-run output to separate trivial bumps from majors, ships trivial bumps as one bulk commit per side, and gives every major (and any code/config change) its own clean commit. Detects required toolchain installs (e.g. a new .NET SDK that needs sudo) and asks the user to run them up front so the backend upgrades first. Fixes the CLI itself when it produces a wrong outcome.
---

# Upgrade Packages

Bring all backend and frontend dependencies to their latest versions using the developer CLI's `update-packages` command. Fix the CLI when it produces a wrong outcome. Make clean per-package commits for upgrades that need more than a version change.
Bring all backend, frontend, and GitHub Actions dependencies to their latest versions. The project always runs the latest version of every package. **Don't quit, give up, or recommend reverting just because something is non-trivial** — research, debug, push through. The only acceptable reasons to skip an upgrade are the permanent exceptions below.

The project always runs the latest version of every package. **Don't quit, give up, or recommend reverting just because something is non-trivial** — research, debug, push through. The exceptions below are the only acceptable reasons to skip an upgrade.
## How To Drive The CLI

Every NuGet and npm bump goes through the developer CLI. Run it directly (the Bash hook allows `dotnet run --project developer-cli`, and the CLI runs its own `dotnet`/`npm` subprocesses without tripping the hook):

```bash
dotnet run --project developer-cli -- update-packages [--backend|--frontend] [--dry-run] [--exclude <csv>] [--include-major-framework-updates] --quiet
```

Always pass `--quiet`. In quiet mode the command prints no tables or banners — only parseable plain-text lines:

```
<side> <patch|minor|major> <package> <current> -> <new> [<extra>]
<side> restricted <package> <current> (latest <X> is a new major, pinned)
<side> excluded <package> <current>
summary <side> patch=<n> minor=<n> major=<n> excluded=<n> uptodate=<n>
```

`<side>` is `backend` or `frontend`. `restricted` lines are the permanent exceptions (pinned to their current major by the CLI). Use a `--dry-run --quiet` run to plan; parse the `major` lines to get the package names that need their own commit.

Run the **build**, **format**, **lint**, **test**, and **e2e** skills for all verification (never raw `dotnet`/`npm`).

## Permanent Exceptions

- **`Microsoft.ApplicationInsights*` (backend only)** — pass to `--exclude`. The next major deprecates `PageView` tracking as part of moving to OpenTelemetry; the codebase uses it heavily and the migration is a separate effort. Frontend `@microsoft/applicationinsights-*` are not subject to this.
These never move to a new major. **The CLI enforces them itself** (`RestrictedNuGetPackages` in `developer-cli/Commands/UpdatePackagesCommand.cs`), so you do **not** pass `--exclude` for them — they show up as `restricted` lines in the dry-run and are pinned to the latest version within their current major:

- **`MediatR`**, **`FluentAssertions`** — later majors changed licensing/APIs.
- **`Microsoft.ApplicationInsights`**, **`Microsoft.ApplicationInsights.AspNetCore`** — the next major drops `PageView` tracking as part of moving to OpenTelemetry; the codebase uses `PageView` heavily and that migration is a separate effort.

Note: frontend `@microsoft/applicationinsights-*` packages are **not** restricted and upgrade normally. `.NET`, `Node.js`, and `@types/node` stay within their current major unless you pass `--include-major-framework-updates`; don't cross a framework major as part of a routine package upgrade.

## Principles

1. **Use `update-packages` for every bump.** Pass `-e` / `--exclude` to scope a run.
2. **The CLI must produce a correct outcome. If it doesn't, fix the CLI.** The fix lives in `developer-cli/Commands/UpdatePackagesCommand.cs`. Hand-editing `package.json` / `Directory.Packages.props` or running raw `npm` / `dotnet` commands as a workaround is unacceptable — the next person hitting the bug deserves the fix.
3. **Backend first, then frontend.**
4. **Atomic commits.** Trivial bumps go into one bulk commit per side. Anything needing more (code change, config change, API rename, dependency change beyond a version) gets its own commit with the change.
5. **Research majors online.** Read the changelog, release notes, and GitHub issues for any major. If a new major exposes cheap, obvious improvements, adopt them in the same commit. If adoption is non-trivial, ship the upgrade and note the follow-up.
6. **Verify smartly.** Run only what's needed per commit, then a full regression at the end:
- **Per upgrade**: `build` + `lint` is enough for trivial bumps.
- **e2e**: only after risky upgrades (majors that touch runtime, build tools, or i18n).
- **format**: only when code changes, or after upgrading formatter / linter tooling (JetBrains, oxfmt, etc.).
- **Final regression**: after all upgrades are committed, run the full set — `build`, `format`, `lint`, `test`, `e2e` for both backend and frontend. If it fails, backtrack to the offending commit and fix.
7. **Push through.** When an upgrade misbehaves, your job is to figure out *why*. Reverting is the last resort, only after evidence the version is genuinely unusable.
1. **Use `update-packages` for every bump.** Never hand-edit `package.json` / `Directory.Packages.props` or run raw `npm` / `dotnet` to move a version.
2. **The CLI must produce a correct outcome. If it doesn't, fix the CLI** in `developer-cli/Commands/UpdatePackagesCommand.cs`, and commit that fix on its own. Working around a CLI bug by hand is unacceptable — the next person deserves the fix.
3. **Order is fixed and never reordered: backend (.NET) first, then frontend, then GitHub Actions last.** If the backend is blocked because a new toolchain (a .NET SDK) isn't installed, that install needs sudo/admin — ask the user to run it up front (Workflow step 3) and wait. Never skip ahead to the frontend to "stay busy" while a backend toolchain install is pending.
4. **Atomic commits.** Trivial bumps (patch + minor) go into one bulk commit per side. Every major — and anything needing a code change, config change, or API rename — gets its own commit with the change.
5. **Research majors online.** Read the changelog, release notes, and GitHub issues for every major before applying it. If a new major exposes cheap, obvious improvements, adopt them in the same commit. If adoption is non-trivial, ship the upgrade and note the follow-up.
6. **Verify smartly, and gate each phase.** `build` + `lint` per trivial commit; add `e2e` after majors that touch runtime, build tooling, or i18n; run `format` whenever code changes or after upgrading formatter/linter tooling. Run a **full backend regression with `e2e` at the end of the backend phase, before the frontend**, and a full regression for both sides at the very end. Fold any fix into the commit that caused it (see **Fixing A Regression**).
7. **Push through.** When an upgrade misbehaves, figure out *why*. Reverting is the last resort, only after evidence the version is genuinely unusable.

## Fixing A Regression

When a regression run fails, the bad change belongs in an earlier commit — fold the fix there, don't tack a loose fix on the end. The branch isn't pushed yet, so rewriting local history is safe. Two patterns by cause:

- **The upgrade is fine but code must adapt** — make the code/config change, then fold it into the commit that introduced the breakage: `git commit --fixup=<offending-commit>` followed by `git rebase --autosquash --interactive <base>`.
- **One package in a bulk commit is the culprit** — pull just that package out of the bulk so the bulk stays green (re-run the bulk with that package added to `--exclude`, or drop its version bump from the bulk commit via a fixup), then give it its own commit on top with the code change it needs — exactly like a major.

Either way, keep every commit independently green and bisectable, and never move to the next phase with a red suite — the fix-up happens in the phase that caused it.

## Workflow

1. **Verify clean baseline** — `git status` clean, build/lint/test/e2e green. Fix or stop if not.
2. **Dry-run** — `update-packages --dry-run` to see what's outdated; identify likely non-trivial upgrades.
3. **Backend bulk** — `update-packages --backend` excluding the permanent exceptions. Pull anything non-trivial out of the bulk for its own commit afterwards. Verify, commit.
4. **Frontend bulk** — `update-packages --frontend` excluding any package already known to need its own commit. Verify, commit.
5. **Per-package commits** for the rest. For each: research the major, apply the upgrade, make required code/config changes, adopt cheap new features, verify, commit.
6. **Commit any CLI fixes** you made along the way as their own commits, separate from package upgrades.
7. **Final regression** — full `build`, `format`, `lint`, `test`, `e2e` for backend and frontend. If anything fails, backtrack to the offending commit and fix. Then summarise to the user: what moved, what was skipped (with reason), what was adopted, what's deferred.
1. **Verify clean baseline** — `git status` clean; `build`, `lint`, `test` green (run `e2e` if anything looks risky). Fix or stop if the baseline is broken before you start.

2. **Dry-run** — `update-packages --dry-run --quiet` (no side flag covers both). Read the output and split each side into:
- **Trivial** = every `patch` and `minor` line.
- **Majors** = every `major` line. Collect the package names; each becomes its own commit.
- **Toolchain** = any `(sdk)` line (e.g. `dotnet-sdk`) and any `⚠️ … is NOT installed` warning — these gate the backend and are handled first (step 3).
- `restricted` lines are the permanent exceptions — ignore them.

3. **Toolchain prerequisites (sudo) — resolve before any upgrade** — if the dry-run reports a `dotnet-sdk` (or other framework) bump whose target version is not installed locally, the backend update will abort: `update-packages --backend` exits when the required SDK is missing. You **cannot** install it yourself — it needs sudo/admin. **Stop and ask the user to run the exact install command the dry-run printed** (e.g. `brew upgrade dotnet-sdk` on macOS, `winget upgrade Microsoft.DotNet.SDK.<major>` on Windows), then wait for them to confirm. Re-run `update-packages --dry-run --quiet` and check that nothing is still flagged as not-installed before continuing. This keeps the backend first and unblocked — do not start the frontend while a backend toolchain install is pending.

4. **Backend bulk (trivial)** — apply all backend patch/minor at once, excluding the majors so only safe bumps land:
```bash
dotnet run --project developer-cli -- update-packages --backend --exclude <comma-separated backend majors> --quiet
```
Then `build --backend` + `lint --backend`. When green, commit, e.g. `Upgrade backend NuGet packages, dotnet tools, and SDK to latest minor and patch versions`.

5. **Backend majors, one at a time** — for each backend major package `P` (the only outstanding backend updates after the bulk are the majors, so exclude the *other* majors to move just `P`):
```bash
dotnet run --project developer-cli -- update-packages --backend --exclude <every other backend major> --quiet
```
Research `P`'s changelog, make the required code changes, adopt cheap new features, run `build`/`format`/`lint`/`test` (+ `e2e` if it touches runtime), and commit `P` and its changes together, e.g. `Upgrade <Package> to <version> and <what changed>`.

6. **Backend regression gate — full suite with `e2e`, before the frontend** — with every backend commit in place, run the full backend suite: `build --backend`, `format --backend`, `lint --backend`, `test`, and `e2e`. The backend must be fully green here, before any frontend work begins — that way a failure is unambiguously a backend regression and its fix lands in a backend commit. If it fails, fix it up now (see **Fixing A Regression**); never carry a red backend suite into the frontend phase.

7. **Frontend bulk (trivial)** — same as step 4 with `--frontend`. The CLI runs `npm install` and `npm audit fix` for you. Then `build --frontend` + `lint --frontend` (+ `format --frontend` since the install may reformat). Commit, e.g. `Upgrade frontend npm dependencies to latest minor and patch versions`.

8. **Frontend majors, one at a time** — same as step 5 with `--frontend`. Formatter/linter majors (oxfmt, oxlint) and i18n/build-tool majors warrant a `format` + `e2e` pass. One commit per major.

9. **GitHub Actions — the last upgrade** — only after backend and frontend are fully done, bump the workflow dependencies in `.github/workflows/*.yml` (not covered by `update-packages`):
- Each `uses: <action>@vN` to its latest major (e.g. `actions/checkout`, `actions/setup-node`, `actions/setup-dotnet`, `actions/setup-java`, `actions/upload-artifact`, `actions/download-artifact`, `actions/github-script`, `azure/login`, `docker/setup-buildx-action`).
- `runs-on:` runners to the current Ubuntu LTS image (e.g. `ubuntu-24.04`).
- Pinned tool versions inside `with:` (`node-version`, and any others) to match the project's runtime.
Verify nothing else references an old version; commit, e.g. `Upgrade GitHub Actions and runner images to latest versions`.

10. **Final regression** — close by running the full set for both sides: `build`, `format`, `lint`, `test`, `e2e`. If anything fails, fix it up in the commit that caused it (see **Fixing A Regression**) rather than tacking a fix on the end. Then summarise to the user: what moved, what was skipped (with reason), what was adopted, what's deferred.

## Success

Every non-exception package on its latest version. Each non-trivial upgrade in its own clear commit. The CLI is better than when you started — every bug you tripped over is fixed at the source. Build, lint, tests, e2e all green at HEAD.
Every non-exception package on its latest version. Trivial bumps in one bulk commit per side; each major and each code/config change in its own clear commit; GitHub Actions current. The CLI is better than when you started — every bug you tripped over is fixed at the source and committed separately. `build`, `format`, `lint`, `test`, and `e2e` all green at HEAD.
2 changes: 1 addition & 1 deletion .github/workflows/_deploy-container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Download Artifacts
uses: actions/download-artifact@v8
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/_deploy-infrastructure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
echo "should_deploy=$should_deploy" >> $GITHUB_OUTPUT

- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Install Bicep CLI
run: |
Expand Down Expand Up @@ -264,7 +264,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Install Bicep CLI
run: |
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/_migrate-database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5
Expand Down Expand Up @@ -171,7 +171,7 @@ jobs:
- name: Generate Migration Information
id: migration-info
if: steps.generate-migration-script.outputs.has_migrations_to_apply == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
env:
MIGRATION_JSON: ${{ steps.generate-migration-script.outputs.migration_json }}
MIGRATION_SCRIPT: ${{ steps.generate-migration-script.outputs.migration_script }}
Expand All @@ -198,7 +198,7 @@ jobs:

- name: Add Migration Information to Pull Request
if: github.event_name == 'pull_request' && steps.generate-migration-script.outputs.has_migrations_to_apply == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
env:
MIGRATION_INFO: ${{ steps.migration-info.outputs.markdown }}
with:
Expand Down Expand Up @@ -234,7 +234,7 @@ jobs:

- name: Add Migration Information to Summary
if: steps.generate-migration-script.outputs.has_migrations_to_apply == 'true' && inputs.azure_environment == 'prod'
uses: actions/github-script@v8
uses: actions/github-script@v9
env:
MIGRATION_INFO: ${{ steps.migration-info.outputs.markdown }}
with:
Expand All @@ -258,7 +258,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Login to Azure
uses: azure/login@v3
Expand Down Expand Up @@ -293,7 +293,7 @@ jobs:
echo "Migrations applied successfully!"

- name: Display Migration Summary
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: core.summary.addRaw(`✅ Migrations successfully applied to \`${{ inputs.database_name }}\` database on \`${{ inputs.azure_environment }}\`.`).write();

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/account.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
# Full history is required to diff base..head for migration-change detection.
fetch-depth: 0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/app-gateway.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Generate Version
id: generate_version
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/code-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
format_label: ${{ steps.scope.outputs.format_label }}
steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
# Need history to diff against the base ref
fetch-depth: 0
Expand Down Expand Up @@ -110,7 +110,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup Node.js Environment
uses: actions/setup-node@v6
Expand Down Expand Up @@ -182,7 +182,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup Node.js Environment
uses: actions/setup-node@v6
Expand Down Expand Up @@ -241,7 +241,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup Node.js Environment
uses: actions/setup-node@v6
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/developer-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
# Full history is required to diff base..head for migration-change detection.
fetch-depth: 0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull-request-conventions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
Expand Down
Loading
Loading