Drive a whole CyanPrint template-chain (ketone) through an ecosystem upgrade — baseline bump (e.g. nixos-26.05 + nix-registry/v3), modern nix-based generated CI, and republish — safely, in dependency order, and to a fixed point. Use when running /ecosystem-upgrade, performing a chain-wide baseline/registry bump across the ketone templates, upgrading the template ecosystem, or re-running the chain upgrade.
Install
npx skillscat add atomicloud/ketone-new-cyanprint/ecosystem-upgrade Install via the SkillsCat registry.
Ecosystem Upgrade (maintainer skill)
Make the whole ketone CyanPrint template chain bump repeatable: a maintainer drives the chain
through an ecosystem upgrade (baseline + generated CI + republish) safely and to a fixed point,
from this skill alone — without re-deriving the wave order, the gate levels, or the convergence
rules each time.
This skill orchestrates; for the per-repo mechanics it links to the in-repo skills that own them
(rather than duplicating them), and inlines the small amount of cyanprint update mechanics this
workflow needs in §1 (no separate "cyanprint" skill exists in this repo):
- `upgrade` — bumping SDK packages and Docker runtimes inside a single
repo (the per-repo dependency refresh). - `ci-cd-workflows` — the nix/nscloud CI pattern the generated CI
is modeled on (AtomiCloud/actions.setup-nix@v2+nix develop .#ci -c <script>).
When to Use
- Running
/ecosystem-upgrade. - Bumping the whole chain to a new baseline (nixpkgs release,
nix-registrymajor) or registry. - Fixing the generated CI centrally in
newso the rest of the chain inherits it. - Re-running a chain upgrade after a previous one (the steps are idempotent by design).
Prerequisites
- A dev toolchain providing cyanprint ≥ 2.22.0 (managed-files tracking in
.cyan_state— which
the convergence guarantee in §3 depends on). Verify
first:direnv exec . cyanprint --version. - The CyanPrint coordinator (
coord.cyanprint.dev:9000) reachable —cyanprint updateand
snapshot regen read published coordinator state. - One git worktree per repo; PR per repo. Never
cyanprint pushlocally and never merge
yourself — publishing happens only via the user's merge → release CI.
Mental model — two dependency axes
The chain has a cycle, so there is no clean leaf-first order. Two axes point opposite ways:
- Authoring dep (
.cyan_state.yaml) — "this repo was scaffolded from these." Refreshed bycyanprint update, which re-applies the latest published versions of its sources (flake,nix/, generated CI…) back into the repo, with conflict resolution. - Composition dep (
cyan.yamltemplates:) — "when this template runs, it materializes
these other templates into its output." Pinned at push time; since cyanprint 2.21.1 the
composition also materializes into snapshot fixtures (expect wide snapshot diffs when crossing
that boundary).
Authoring (.cyan_state): nix ← new | workspace ← new | new ← nix, workspace (CYCLE)
Composition (cyan.yaml): workspace → atomi/nix | nix → none | new → noneBecause of the cycle, ordering is by publish gates (below), and a bounded convergence pass
settles the remaining cyclic pins.
The three things this skill guarantees
1. How to update
There are two distinct modes by repo role — do not conflate them:
- Wave-1
new(the chain root) — direct edit, NOTcyanprint update.new's own authoring
sources (atomi/nix,atomi/workspace) are downstream (waves 2–3) and not yet published on the
new baseline, so runningcyanprint updateonnewnow reads stale published coordinator state
and reverts the very upgrade you are making (see
§2). In wave 1 you therefore hand-apply
the baseline tonew's managed files.newonly re-enters acyanprint updateconvergence pass
later, once the downstream waves have published on the new baseline (see
§3). - Downstream waves (
nix,workspace, …) — driven bycyanprint update. These inherit the new
baseline + generated CI from their already-published upstream; do not hand-port their managed
files.
Wave-1 new (direct edit):
- Bump
new's own inputs inflake.nix(nixpkgs-25xx → nixos-26.05,atomipkgs v2 → v3, and thecyanprintinput to a ref providing ≥ 2.22.0), then re-lock (nix flake update) and reload
the dev shell. - Hand-apply the same baseline rename to the managed
nix/*.nix— do not runcyanprint updateyet (it would revert it). Keep repo-specific edits minimal. - Fix the generated CI and regenerate snapshots (
cyanprint test … --parallel 1 --update-snapshots); verify the new baseline is present (e.g.grep -rl 'nixos-26.05' snapshots).
Downstream waves (cyanprint update):
- Confirm the upstream wave has published (the
releasedgate,
§2). - Run
cyanprint updateto propagate managed files owned by the resolvers (theatomi/nixresolver
ownsnix/*.nix); reconcile conflicts keeping repo-specific edits minimal. - Regenerate snapshots/fixtures (
cyanprint test … --parallel 1 --update-snapshots) and verify the
new baseline is present.
cyanprint update mechanics (the minimum this workflow needs):
cyanprint update [PATH](aliascyan u;PATHdefaults to.) re-applies the latest
published version of each authoring source recorded in.cyan_state.yamlback into the repo.- It reads published state from the coordinator (
-c/--coordinator-endpoint, envCYANPRINT_COORDINATOR, defaulthttp://coord.cyanprint.dev:9000) — so it only ever pulls a
version that the upstream's release CD has already published (this is why the wave gate in
§2 matters: updating before the upstream
publishes re-pins the OLD version). - Flags:
-i/--interactiveto pick specific versions;--forceto run on a dirty tree without the
confirmation prompt. Prefer a clean tree so the managed-file diff is reviewable; use-iwhen a
conflict needs a human choice..cyan_state.yamlrecords the per-source version/answer history and
is what makes each managed file single-owned (the termination guarantee in
§3). - Use
--parallel 1for test/regen (serial is required while dep-materialization is in play).
2. Wait for CD before the next wave (the released gate)
A downstream repo must wait for its upstream's release CD to publish before it updates.
Updating before the upstream is published reads stale coordinator state and re-pins the old
version. The order of the waves is therefore gated on publication, not merely on merge:
merge upstream PR → CI green → release CD runs → coordinator records new published version
└─► only now may the downstream `cyanprint update`In-scope waves (each gated on the prior being published, confirmed by the user/CD, before
starting the next):
| Wave | Repo | Action | Publishes |
|---|---|---|---|
| 1 | new |
bump flake → 26.05+v3; fix generated CI; regen snapshots | cyan/new |
| 2 | nix |
cyanprint update (inherits CI from new); regen snapshots |
atomi/nix@v3 |
| 3 | workspace |
cyanprint update (pulls nix@v3 + CI); regen fixtures |
atomi/workspace |
After each wave: open the PR, babysit CI to green, then stop and surface for the user to merge.
Do not begin the next wave until the upstream's publish is confirmed.
3. Convergence — bounded, no infinite loop
The authoring cycle (new ↔ nix/workspace) means a repo published before its dependency moved may
need a bounded re-converge. Settle it like this:
- After all waves, re-run
cyanprint updateon any repo whose dependency was published after that
repo was last pushed. - Re-run
cyanprint updateuntil it is a no-op (no managed-file changes, clean snapshot regen). - Apply a hard cap (e.g. 3 iterations per repo). Single-ownership of each managed file
guarantees termination — every managed file has exactly one owning resolver, so updates cannot
ping-pong indefinitely. - If a repo has not settled within the cap, stop and surface the diff to the maintainer —
do not keep looping. A non-converging file is a bug in ownership/pins to investigate, not
something to brute-force.
Convergence is reached when, for every in-scope repo: cyanprint update is a no-op, snapshots/
fixtures show the new baseline, and PR CI is green.
Per-repo acceptance (apply to each wave)
cyanprint test … --parallel 1green.- Snapshots/fixtures regenerated and show the new baseline (e.g.
nixos-26.05+nix-registry/v3). - The repo's own dev env builds:
direnv exec . nix develop -c true. - Generated CI is nix-based (
nix develop .#ci -c cyanprint test … --parallel 1on an nscloud runner
withactions.setup-nix); no aptsetup-ubuntu.shin the test path. - After convergence:
cyanprint updateis a no-op; PR CI green. Never merge / never push locally.
Git safety
- PR per repo on a dedicated branch; never push to
main/master; never force-push. - Never
cyanprint pushlocally — publishing is the user's merge → release CD. - Leave the actual merge to the user; this skill stops at "PR green, waiting for merge".