Workflow — Development Lifecycle & Release Management
Standard operating procedures for branching, GitHub operations, CI/CD, releases, and Rust workspace scaffolding. This file is the primer + index; depth lives in the reference files at the bottom.
Scope note. This skill carries project-policy (branch names, merge strategies, plan-file structure, release pipeline behavior). Generic cargo command semantics live in the
rustskill'scargo.md. When in doubt: conventions and branch lifecycle here, manifest/workspace mechanics there.
I. Branching Convention
Naming: v{x}.{y}.{z}-dev.{i}
x.y.z— semver version being developed (this is the patch branch).i— sprint/phase number, 0-indexed.dev.0is the first sprint of a new patch.- Example:
v5.1.0-dev.0is sprint 0 of patchv5.1.0.
Branch hierarchy
main ← released, tagged, immutable history (squash commits)
└── v{x}.{y}.{z} ← patch branch; accumulates dev.0..dev.9 via rebase
├── v{x}.{y}.{z}-dev.0 ← sprint 0 (the only one open at any time)
├── v{x}.{y}.{z}-dev.1 ← sprint 1 (cut from patch branch AFTER dev.0 merges)
└── ... ← up to dev.9 (the automated mod-10 cascade)
Merge strategy decision tree
| Source → Target | Strategy | Why | Approval |
|---|---|---|---|
track / topic → v{x}.{y}.{z}-dev.{i} |
--rebase --delete-branch |
Tracks are ephemeral; keep linear sprint history | Reviewer |
v{x}.{y}.{z}-dev.{i} → v{x}.{y}.{z} |
--rebase |
Preserve sprint commit history as a readable tree | Operator |
v{x}.{y}.{z} → main |
--squash |
Releases are one commit each; triggers release.yml |
Operator (explicit) |
Rules.
- Prefer
ghCLI over rawgitfor all branch/PR operations. GitHub ignores git-originated operations w.r.t. autodelete and PR lifecycle. Fall back togitonly ifghfails. - Never force-push to
mainor to a version branch. Both are operator-protected and the release pipeline depends on linear history onmain. - Don't start
dev.(N+1)untildev.Nis merged. Parallel phase branches hide cross-cutting drift — the phase structure exists to prevent that. - Each sprint PR has a clear title:
feat({scope}): {description}(the version is implicit in the head branch name).
Branch lifecycle commands
# Open sprint N
gh pr create --base "v{x}.{y}.{z}" --head "v{x}.{y}.{z}-dev.{N}" --draft
# Close sprint N: rebase-merge into patch branch
gh pr merge "v{x}.{y}.{z}-dev.{N}" --rebase --delete-branch
# Open next sprint
git checkout "v{x}.{y}.{z}"
git pull --rebase origin "v{x}.{y}.{z}"
git checkout -b "v{x}.{y}.{z}-dev.{N+1}"
git push -u origin "v{x}.{y}.{z}-dev.{N+1}"
The final sprint of a patch is followed by a squash-merge of the patch branch
into main. That squash-merge fires the automated release pipeline —
see §IV.
II. GitHub Issues, PRs, Milestones
Issue title convention
{type}({scope}): {description}
| Type | When |
|---|---|
feat |
New capability |
fix |
Bug fix |
bug |
Bug report (not yet fixed) |
chore |
Maintenance, version bumps |
cleanup |
Dead code removal, restructuring |
refactor |
Code restructuring without behavior change |
docs |
Documentation-only change |
Standard labels
bug, enhancement, cleanup, refactor, tracking, documentation.
PR body template
## Summary
- Bullet points describing what changed and why.
## Issues
- Closes #N
- Fixes #M
## Test Plan
- [ ] <project-appropriate gate 1>
- [ ] <project-appropriate gate 2>
The test plan is project-shaped. Common gates by project type:
| Project type | Gates |
|---|---|
| Marketplace / docs-only | Manual review; frontmatter lint; install-script dry run |
| Rust workspace | cargo fmt --all --check, cargo clippy --workspace --features full -- -D warnings, cargo test --workspace --features full |
| Hybrid (TS + Rust) | The Rust set plus pnpm lint && pnpm test (or equivalent) |
Milestones
One milestone per patch version: v{x}.{y}.{z}. Attach every issue targeted
for that release. The automated pipeline rolls open issues forward to the
next milestone on release (see §IV.5).
# Create
gh api "repos/${OWNER}/${REPO}/milestones" --method POST -f title="v0.1.5"
# Attach
gh issue edit N --milestone "v0.1.5"
# Triage stale
gh issue close N --comment "Superseded by #M" --reason "not planned"
gh issue close N --comment "Resolved in v0.1.5" --reason "completed"
III. Plan & Sprint Structure
Every patch v{x}.{y}.{z} is decomposed into ordered sprints. Plans live in
.artifacts/plans/, design docs in .artifacts/docs/. (Adapt the directory
to the project; the structure is what matters.)
File naming
Drop the date prefix on versioned artifacts — use frontmatter for the date.
| File | Scope |
|---|---|
v{xyz}.plan.md |
Version-level plan (roadmap across all sprints) |
v{xyz}-dev{N}.plan.md |
Sprint N plan (scoped to one phase) |
v{xyz}-design.md |
Version-level design doc |
v{xyz}-dev{N}-design.md |
Sprint-specific design doc (if needed) |
{xyz} is the compact version (e.g., 015 = 0.1.5, 510 = 5.1.0).
Frontmatter (required on versioned plans + designs):
---
title: v0.1.5 Stability Sprint Plan
createdAt: 2026-04-14
version: v0.1.5
phase: 0 # omit for version-level plans
description: One-sentence summary. Helps skim across many plans.
---
Required keys: title, createdAt, version. Optional: phase,
description (recommended), updatedAt.
Non-versioned artifacts (audits, point-in-time research, reports) keep date-prefixed names — they're frozen in time by design:
.artifacts/reports/2026-04-14-v015-audit.md.artifacts/research/2026-04-14-quad-math-audit.md
Sprint internal structure — the five sections
Every sprint plan follows the same shape. This forces critical work to land before enhancements and ensures validation ships with every sprint.
1. Critical issues — must-fix blockers, security, data loss, broken pipelines
2. Enhancements — capabilities the sprint adds
3. Optimizations — performance, cleanup, dead-code removal
4. Wiring / Interfaces — CLI/MCP/GUI surface wire-up for new behavior
5. Audit — validation: tests, manual checks, live observation
Even if a section is "N/A for this sprint" say so explicitly. Tracks within a sprint usually map to one of these five sections.
Version-level plan
v{xyz}.plan.md is the roadmap. For each sprint, list:
- Number (
dev.0,dev.1, …). - Theme (one sentence).
- Entry criteria (what must be true before starting).
- Exit criteria (what defines "done" for this sprint).
- Link to the sprint plan file.
Canonical git flow
1. Write v{xyz}.plan.md (version roadmap with sprint list).
2. For each sprint N:
a. Open branch v{xyz}-dev.{N} off v{xyz}.
b. Write v{xyz}-dev{N}.plan.md (the 5 sections).
c. Dispatch track PRs against v{xyz}-dev.{N}.
d. When sprint complete + audited, rebase-merge dev.N → v{xyz}. Delete dev branch.
3. When all sprints done, squash-merge v{xyz} → main (operator-approved).
4. release.yml does the rest (tag, release, next-patch, dev.0, sweep, milestone roll).
Rename historical plans cautiously. Old date-prefixed plans are historical record — leave them. Only new plans use the dateless convention.
IV. The Automated Release Pipeline
Source of truth: .github/workflows/release.yml. Read that file if anything
below disagrees with reality — the workflow is canonical.
Trigger
The pipeline runs on push to main (and on workflow_dispatch). On every
push to main it inspects the HEAD commit subject and proceeds only if it
matches:
vX.Y.Z(operator convention; gh appends(#N)on squash — tolerated), orrelease: vX.Y.Z(back-compat for automation-authored PRs).
The extracted version is cross-checked against .claude-plugin/plugin.json.
A stray commit subject can't fire the pipeline against the wrong version — the
job logs ::warning:: and exits cleanly.
Steps executed (in order)
| # | Step | Effect |
|---|---|---|
| 1 | Detect release squash commit | Parses HEAD subject, validates against plugin.json |
| 2 | Compute next version (mod-10 cascade) | Z<9 → patch, else Y<9 → minor, else major; emits current, next, tag, branch names |
| 3 | Extract release notes from CHANGELOG.md |
Slices the ## v{CURRENT} section; fails the run if missing |
| 4 | Tag v{CURRENT} on main |
Idempotent: skips if tag already exists locally or on origin |
| 5 | Create GitHub Release | Idempotent: skips if release exists; uses extracted notes |
| 6 | Cut next patch branch + bump versions | Branches v{NEXT} off main; rewrites .claude-plugin/plugin.json, .claude-plugin/marketplace.json (root + every plugin entry), every skills/*/plugin.json, every skills/*/SKILL.md frontmatter version, and README.md |
| 7 | Commit + push patch branch | chore({PATCH}/version): bump plugin {CURRENT} → {NEXT} |
| 8 | Open draft PR v{NEXT} → main |
Title release: v{NEXT}; the empty container for the new patch's sprints |
| 9 | Cut v{NEXT}-dev.0 off v{NEXT} |
First sprint branch of the new patch, ready for work |
| 10 | Orphan sweep | Deletes v{CURRENT}-dev.{0..9} from origin |
| 11 | Milestone roll | Creates milestone v{NEXT} if missing; moves every open issue from v{CURRENT} → v{NEXT} |
Every step is idempotent — a partial prior run can be safely re-driven via
workflow_dispatch once the underlying issue is fixed.
Operator responsibilities before the squash
- The patch branch
v{x}.{y}.{z}must contain a## v{x}.{y}.{z}section inCHANGELOG.md. If it's missing, step 3 fails the whole run. .claude-plugin/plugin.jsonversionmust equal the patch version. (The version was set automatically when the previous release cut this patch branch — only intervene if you renumbered manually.)- Branch protection on
mainmust require PR + linear history + no force-push. The pipeline depends ongit log -1 --format=%s HEADreturning the squash subject. - PR title format: bare
v{x}.{y}.{z}(preferred) orrelease: v{x}.{y}.{z}. gh appends(#N)on squash; the regex tolerates that suffix.
Operator responsibilities after the squash
Usually: nothing. Verify the run succeeded:
gh run list --workflow=release.yml --limit 3
gh release view "v{x}.{y}.{z}"
gh pr view "v{next}" # the draft PR should exist
git fetch --prune origin # observe v{next}-dev.0 appearing
If a step failed mid-pipeline, fix the cause and re-dispatch:
gh workflow run release.yml --ref main
The idempotency guards (if tag exists, if release exists, if branch exists, if milestone exists) make re-runs safe.
Mod-10 cascade — what to expect
Current Z |
Result |
|---|---|
Z < 9 |
Patch bump: (X, Y, Z+1) |
Z == 9 and Y < 9 |
Minor bump: (X, Y+1, 0) |
Z == 9 and Y == 9 |
Major bump: (X+1, 0, 0) |
The pipeline is named "patch pipeline" but the cascade handles all three. This is intentional — every release looks the same to the operator.
V. CI/CD Workflows (Rust Workspace Projects)
This marketplace's only workflow is release.yml. The patterns below apply
to Rust workspace projects (the axiom family); pull them in when standing
up CI for a new Rust project.
Recommended workflow set
| Workflow | Triggers | Purpose |
|---|---|---|
cargo-clippy.yml |
PR (synchronize), push (main + tags) | Lint + SARIF upload |
cargo-test.yml |
PR (main), push (main + tags), release | Test matrix: stable + nightly |
cargo-build.yml |
push (main + tags) | Multi-target: native + WASM (wasip1, wasip2) |
cargo-publish.yml |
release (published) | Sequential crates.io publish |
release.yml |
release (published) | GH release notes + crates.io/docs.rs links |
docker.yml |
push (tags), workflow_dispatch | Multi-container Docker builds |
fly-io.yml |
workflow_dispatch | Deploy to Fly.io |
cleanup.yml |
PR (closed) | Branch cleanup |
Templates: references/ci-patterns.md.
Standing patterns
- Matrix builds. Features × targets × toolchains.
- Sequential publishing.
max-parallel: 1on the publish matrix to serialize crates.io uploads in dependency order. - Concurrency control.
cancel-in-progress: falseper workflow/ref group — interrupting a release mid-flight is worse than queuing. - SARIF upload. Clippy results to GitHub code scanning on public repos.
- WASM builds.
cargo-componentforwasm32-wasip2; nativecargo buildforwasm32-wasip1and the wasm32-unknown target. - Nightly matrix.
no_std,alloc+nightlyfeature combos on the nightly toolchain. Catches feature-gate drift early.
Standard env block
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: full
VI. Rust Workspace Crate Scaffolding
Cargo command semantics (cargo new, cargo init, the manifest schema,
workspace inheritance, feature-gate syntax) live in rust/cargo.md. This
section covers project-policy for the axiom-family Rust workspaces —
conventions cargo itself doesn't enforce.
When adding a crate under crates/*
- Create with
cargo init --lib crates/{name}, thenmkdir crates/{name}/wit. - Cargo.toml — copy
references/crate-template.mdverbatim. Critical policy rules:- All metadata inherits from workspace (
field.workspace = true). - Only
nameanddescriptionare crate-specific. [lib] bench = falseunless the crate carries benchmarks.[package.metadata.docs.rs]and[package.metadata.release]are required.build = "build.rs"with the standard build script.
- All metadata inherits from workspace (
- Mandatory environment features. Every crate defines
default,full,std,alloc,nightly,wasm,wasi. Feature propagation rules:std→ enablesallocand propagates/stdto all deps.full→ enables all optional features AND propagates/fullto axiom deps.- Optional deps:
config = ["dep:axiom-config"]. - Conditional propagation:
"axiom-config?/serde".
- Register in workspace root.
[workspace.dependencies] axiom-{name} = { default-features = false, path = "crates/{name}", version = "{X.Y.Z}" } [workspace] members = [..., "crates/{name}"] - Register in the umbrella SDK (
crates/axiom/Cargo.toml).
Re-export in[dependencies] axiom-{name} = { optional = true, workspace = true } [features] {name} = ["dep:axiom-{name}"] full = [..., "axiom-{name}?/full"]crates/axiom/src/lib.rs:#[cfg(feature = "{name}")] pub use axiom_{name} as {name}; - WIT directory.
crates/{name}/wit/with at minimumtypes.wit,{name}.wit,world.wit. Even if the crate isn't a wasm component today, the world file documents the intended surface.
SDK Facade Rule
No crate within crates/* is imported directly by external consumers.
Consumers depend ONLY on axiom and use feature gates.
# CORRECT
axiom = { version = "{X.Y.Z}", features = ["bot", "engine"] }
# WRONG — never do this externally
axiom-bot = "{X.Y.Z}"
axiom-engine = "{X.Y.Z}"
Exceptions:
clients/*— independent standalone libraries; not part of the SDK surface.components/*— consumers of the axiom SDK (they depend on it like any external project would).
For the cargo-side mechanics of workspace inheritance and feature gates, see
rust/cargo.md §3 (workspaces) and §4 (feature gates).
VII. Reference Files
references/ci-patterns.md— full GitHub Actions templates (Clippy, Test, Build, Publish, Release, Docker, Cleanup) derived from the production pzzld-rs implementation. Drop-in starting point for Rust workspace projects.references/crate-template.md— the completeCargo.tomltemplate for a new axiom-family crate, plus the feature-gate checklist.references/release-checklist.md— operator pre-flight checklist for the squash-merge that fires the release pipeline. Pairs with §IV above.
VIII. Cross-Skill Pointers
rust— language fluency, ownership, async, traits, no_std tiers.rust/cargo.md— every cargo command, theCargo.tomlschema, workspace mechanics, feature-gate syntax,[profile.*],.cargo/config.toml, registries. Workflow defers to it for cargo semantics.rust/rustc.md— compiler knobs (codegen flags, target triples, editions, lints, sanitizers).webassembly— language-agnostic WASM toolchain.wasmtime— Rust-side host embedding.
Source: FL03/claude — distributed by TomeVault.