| name | upgrade-microbus |
| description | TRIGGER when the user asks to upgrade the project to a newer or the latest version of Microbus, or to update the framework. Each Microbus release ships this one self-contained skill; it applies that release's single-version migration, then chains to the next release's copy of this skill until the target version is reached. |
CRITICAL: Do NOT explore or analyze the project unless a migration step below tells you to. This skill is self-contained.
CRITICAL: This skill is self-propagating. It migrates the project by exactly one version increment (Phase 1), then replaces itself on disk with the next release's copy of this same skill and runs that (Phase 2). Only the "Release constants" and Step 3 differ between releases; the Phase 1 and Phase 2 machinery is identical in every release.
Release Constants
The only part of this skill a release author edits. Set these when cutting a release.
SOURCE = the version this release upgrades from (the previous release), e.g. v1.44.0.
DEST = this release's own version, e.g. v1.45.0.
- Migration = the source edits taking a project from
SOURCE to DEST, authored in Step 3. Empty when the release has no breaking changes.
SOURCE = v1.44.0
DEST = v1.45.0
Workflow
Copy this checklist and track your progress:
Upgrade Microbus:
- [ ] Step 1: Read the CURRENT and TARGET versions
- [ ] Step 2: Phase 1 - apply this release's increment (SOURCE -> DEST)
- [ ] Step 3: (release-specific migration, invoked by Step 2)
- [ ] Step 4: Phase 2 - chain to the next release, or finish
Step 1: Read the CURRENT and TARGET Versions
Read the github.com/microbus-io/fabric version from go.mod; call it CURRENT. If the dependency is absent, this is not a Microbus project; exit.
Determine the TARGET. In order of precedence: the TARGET carried forward from the previous hop of the chain (see Step 4); the version the user named when starting the upgrade; or, if the user asked only to update or go to "latest", the latest published version:
go list -m -versions github.com/microbus-io/fabric
The versions are listed oldest to newest. A user-supplied TARGET is fixed for the whole run: carry it across every hop and do not silently re-derive it as "latest" on a later hop. If CURRENT is already TARGET (or newer), there is nothing to do; exit.
Step 2: Phase 1 - Apply This Release's Increment (SOURCE -> DEST)
If CURRENT is not equal to SOURCE, skip straight to Step 4. (This happens on the very first hop: the project's installed copy of the skill is CURRENT's own release, so its DEST equals CURRENT and its SOURCE is one behind - Phase 1 has nothing to do, and its only job is to bootstrap the chain in Step 4.)
Otherwise the project is exactly one version behind this release. Migrate it:
-
Pin the framework to DEST:
go get github.com/microbus-io/fabric@DEST
-
Apply the migration in Step 3.
-
Regenerate each microservice's boilerplate with this release's generator, resolve dependencies, and compile-gate:
find . -path ./vendor -prune -o -name definition.go -path '*api/definition.go' -print \
| while read -r def; do
svcdir=$(dirname "$(dirname "$def")")
go run github.com/microbus-io/fabric/cmd/genservice "$svcdir"
done
go mod tidy
go vet ./...
go vet must pass before Step 4. A grep-guided or manual migration can leave a compile error at a site it could not fix mechanically; resolve those now - with the user when the fix is a design choice - so the project compiles at DEST. (A migration may deliberately leave a runtime // TODO: that still compiles; the final go test in Step 4 surfaces it.)
Step 3: Release-Specific Migration (v1.44.0 -> v1.45.0)
Invoked by Step 2. This is DEST's migration; a release author replaces it when cutting the next release (see "Authoring framework upgrade skills" in the repo-root CLAUDE.md). Confine it to source edits - Step 2 owns the per-increment genservice + go vet, and Step 4 owns the final go test.
v1.45.0 removes flow-stop notification from the workflow subsystem: workflow.FlowOptions.NotifyOnStop (the option field) and the foreman OnFlowStopped outbound event (with its NewHook/NewMulticastTrigger methods) are gone. Both are loud compile errors with no mechanical rewrite - the replacement is a per-call-site design choice. A project that never set NotifyOnStop and never subscribed to OnFlowStopped needs no change from this step.
3a. Remove FlowOptions.NotifyOnStop and Choose How the Caller Learns the Outcome (Grep-Guided)
Find every site that set the flag:
grep -rn --include='*.go' --exclude-dir=vendor 'NotifyOnStop' .
For each hit, delete the NotifyOnStop field from the FlowOptions literal (if it was the only field, the whole &workflow.FlowOptions{...} may become nil), then decide how that caller now learns the outcome - do not guess; ask the user when it is unclear:
- A caller standing by for the result (it holds, or can hold, the flowKey and can wait): switch to
Await. Create auto-runs and returns the key:
flowKey, err := foremanapi.NewClient(svc).Create(ctx, url, initialState,
&workflow.FlowOptions{NotifyOnStop: true})
flowKey, err := foremanapi.NewClient(svc).Create(ctx, url, initialState, nil)
outcome, err := foremanapi.NewClient(svc).Await(ctx, flowKey)
Await returns without the outcome if the caller's context deadline fires first while the flow keeps running; a caller that may outlive its request budget must be prepared to Await again (it still holds the key).
- A follow-up that must happen reliably regardless of who is waiting (fire-and-forget submit, a push notification, a downstream call, a compensation): move the reaction into the workflow, not a caller. Author an orchestrating graph whose entry task launches the real work as a subgraph and whose success/failure tasks do the follow-up as their own durable, retryable steps (
AddTransition for success, AddTransitionOnError for failure). The add-workflow and add-task skills scaffold this; the shape is in .claude/rules/workflows.txt under "Detecting Flow Completion".
3b. Replace OnFlowStopped Subscriptions and Triggers (Grep-Guided)
Find every reference to the removed event:
grep -rn --include='*.go' --exclude-dir=vendor 'OnFlowStopped' .
Each hit is one of:
- A subscription -
foremanapi.NewHook(svc).ForHost(...).OnFlowStopped(func(ctx, flowKey string, outcome *workflow.FlowOutcome) error { ... }). Delete the subscription and relocate its handler body to wherever the outcome is now learned in 3a: inline after the Await (standing-by caller), or into the workflow's success/failure task (reliable follow-up). The handler's logic is preserved; only its trigger moves.
- A trigger -
foremanapi.NewMulticastTrigger(svc).ForHost(...).OnFlowStopped(...). Only the foreman itself fired this event, so a downstream project should not have one; if it does, remove the call.
- A leftover handler method or hook type left unused once its subscription is gone. Remove the dead code.
Step 4: Phase 2 - Chain to the Next Release, or Finish
Find the next release to apply: from go list -m -versions github.com/microbus-io/fabric, the smallest published version NEXT in the range DEST < NEXT <= TARGET (semver). The upper bound <= TARGET is what stops the chain from overshooting a user-supplied TARGET.
If there is no such NEXT the chain is complete - either TARGET is at or below DEST, or no published release lies between DEST and TARGET. The correctness gate is go vet, which already passed at every increment including this one - it is deterministic and always runnable locally. As a final behavior check, run the tests where feasible:
go test ./...
- Tests can't run here, or are known-flaky / need external services: say so and rely on the
go vet gate; the upgrade stands.
- Tests pass: the upgrade is confirmed.
- Tests fail: triage each failure rather than declaring success or failure blindly.
- Caused by a migration (a test referencing a removed or renamed symbol, an incompletely migrated call site): fix it - that is finishing the migration - and re-run.
- Not clearly the migration's doing (a deliberate
// TODO: a migration left to fill, or a pre-existing / environmental failure): report it and ask the user how to proceed - fix it together, accept the upgrade with the failure recorded, or roll back. Do not silently declare success on failures you have not explained.
If CURRENT is still below TARGET here, TARGET names no published release the chain could reach (for example a version that was never published); say so and leave the project at CURRENT.
If NEXT exists, hand off to it. Install NEXT's agent rules and skills - which include NEXT's copy of this skill (NEXT already carries its v prefix, e.g. v1.46.0):
git clone --depth 1 --branch NEXT https://github.com/microbus-io/fabric temp-clone
rm -rf .claude/rules/{auth.txt,microbus.md,python.txt,sequel.txt,workflows.txt}
rm -rf .claude/skills/{microbus,python,sequel,upgrade}
cp -r temp-clone/.claude .
rm -rf temp-clone
(Removing the fabric-managed rules files and skill groups before the copy purges anything NEXT dropped; cp overwrites and adds but never deletes. Rules and skills the project added of its own are left untouched.)
CRITICAL: The copy just overwrote this skill on disk with NEXT's copy. Now Read .claude/skills/microbus/upgrade-microbus/SKILL.md (the new content) and follow it from Step 1, carrying the same TARGET forward - if the user supplied a specific TARGET, it stays the TARGET for NEXT and every later hop; only an unspecified TARGET defaults to "latest". Do not continue from this in-context copy, because NEXT's SOURCE/DEST and migration are what must run next. CURRENT is now DEST, which is NEXT's SOURCE, so NEXT's Phase 1 fires and advances one more increment. The chain ends at the hop that finds no NEXT in range.