Versioning & Upgrades¶
How GodotMaker tracks versions and handles upgrades between releases.
Version Scheme¶
GodotMaker uses Semantic Versioning: MAJOR.MINOR.PATCH
| Level | Meaning | Example | Upgrade behavior |
|---|---|---|---|
| PATCH | Backward-compatible bug fixes, no new behavior | 0.3.0 → 0.3.1 |
Auto-proceed; applies any pending migrations |
| MINOR | Backward-compatible new features or behavior changes | 0.3.0 → 0.4.0 |
Show changelog, require confirmation; applies any pending migrations |
| MAJOR | Breaking changes (incompatible) | 0.x → 1.x |
Strong warning; requires --force clean re-init (skips migrations, baselines instead) |
What "backward-compatible" means in this project¶
GodotMaker is a framework that deploys files into a target project, not a library with a public API. So the compatibility contract has two specific clauses:
- User-saved files keep their format.
CLAUDE.md,.claude/godotmaker.yaml,.godotmaker/config.yaml, and the user's own game code/scenes/assets are never rewritten silently. Their schema/format from the previous version must still parse. - Runtime state stays readable. State files written by old hooks/skills
(
state.jsonfields,metrics*.jsonlschema,stage.jsonl,current_role, etc.) must still be readable by the new version — either directly, or via a migration script that rewrites them on upgrade.
Framework-managed files (skills, hooks, agents, templates, tools) are overwritten on every publish, so changes to those never count as "breaking" — they are simply re-deployed.
Choosing a bump level¶
A short decision tree for contributors:
- Old projects work after re-publish, no user-saved file or runtime state touched (or the change is a pure bug fix) → PATCH.
- New skill / hook / behavior added; old projects' user-saved files and runtime state still parse and work → MINOR.
- Some user-saved file format or runtime state field changed in a way the previous version's data cannot satisfy → MAJOR.
Migrations are independent of the bump level. A migration is a tool for
fixing or rewriting something in an existing target project; whether you need
one is a separate question from which level to bump. The migration system
itself is timestamp-driven (migrations/<YYYYMMDDhhmmss>_<slug>.py) and
tracked per target in .godotmaker/applied_migrations.json; the product
VERSION is not consulted. See "Version Migrations" below.
Where Versions Live¶
| File | Location | Purpose |
|---|---|---|
VERSION |
GodotMaker repo root | Source of truth for current release |
.godotmaker/version |
Target game project | Records which version was last published |
CHANGELOG.md |
GodotMaker repo root | Human-readable change log per version |
migrations/ |
GodotMaker repo root | Version migration scripts |
Publishing & Upgrading¶
Fresh Install¶
No version exists in the target — publish proceeds directly and stamps
the version into .godotmaker/version.
Upgrade (re-publish to existing project)¶
The publish script compares the source VERSION against the target's
.godotmaker/version and behaves according to the upgrade level:
| Level | Behavior |
|---|---|
| PATCH | Proceeds automatically. Applies pending migrations (if any). |
| MINOR | Shows changelog, asks confirmation. Applies pending migrations (if any). |
| MAJOR | Blocks incremental migration. Requires --force for clean re-init. |
Version Migrations¶
Migrations are timestamped scripts under migrations/, e.g.:
Each target project records which migration IDs it has applied in
.godotmaker/applied_migrations.json. On every publish, migrate.py
discovers all scripts on disk, subtracts the ones already recorded as
applied, and runs the rest in chronological order.
The system is decoupled from MAJOR.MINOR.PATCH. The product version is not consulted when picking which migrations to run — the only inputs are "what's on disk" and "what's already applied". This means PATCH releases can ship migrations as a first-class case (e.g., a hook bug that left orphan files in old projects gets a fix-up migration in the PATCH that fixes the hook).
Fresh installs and MAJOR --force re-inits baseline instead: every
current migration is marked as applied without being executed, since the
target starts at the latest format and has nothing to migrate from.
If a migration fails, the chain aborts immediately. Already-successful
scripts keep their entries in applied_migrations.json, so re-running
publish picks up exactly where it left off.
Migrations can also be applied standalone (without re-publishing):
To scaffold a new migration:
python tools/migrate.py --new fix-state-path
# Creates migrations/<current-utc-timestamp>_fix_state_path.py
Full details — naming convention, scripting rules, the legacy-target
auto-bootstrap rules (empty tracker is auto-created on first contact;
pending migrations run through the normal path) — live in
migrations/README.md.
MAJOR Upgrades¶
MAJOR version bumps indicate breaking changes that cannot be handled
by incremental migration. publish.py refuses to upgrade across MAJOR
boundaries without --force, which performs a clean re-initialization.
Full rebuild re-deploys framework code and removes incompatible current state:
- The selected runner's skills/, agents/, config/, and templates/
- .godotmaker/hooks/, .godotmaker/stage_schemas.json
- Current pipeline state: .godotmaker/state.json, pipeline_state.json,
stage.jsonl, current_role, evaluation.json, verify_report.json,
and final_report.json
- .godotmaker/metrics*.jsonl
- .godotmaker/applied_migrations.json (re-baselined after re-deploy)
- tools/
- The selected runner's hook config or plugin adapter (force-overwritten)
Preserved (user configuration):
- CLAUDE.md / AGENTS.md, the selected runner's godotmaker.yaml,
.godotmaker/config.yaml
- Game code, scenes, assets, planning documents, and historical evidence such
as .godotmaker/evaluation-runs/, .godotmaker/gaps/, and
.godotmaker/asset-generation/
After re-deploy, publish.py calls baseline_applied() to mark every
current migration as applied without running it — same as a fresh install.
The migration timestamp series itself is monotonic and global; old
scripts stay on disk as historical record.
The clean re-initialization does not convert project-owned documents or Godot resource bindings. For a 0.x workspace, follow the 1.0 agent-assisted upgrade guide before resuming the pipeline.
Downgrade¶
Downgrading (e.g., 0.4.0 → 0.3.0) is blocked by default. Use --force to override:
A forced downgrade still calls run_migrations() for routing consistency,
but in practice the call is a no-op: applied_migrations.json already
records every migration applied at the higher version, and the older
release ships with a subset of those scripts on disk, so pending = disk
- applied is empty. Migrations are not "rolled back" — that would
require explicit down-migration scripts, which this system does not
ship. If you need to roll back a schema change, restore the target
project from a VCS snapshot.
Re-publish Same Version¶
Re-publishing the same version is always allowed. This is useful for picking up local changes during development.
Session Version Display¶
When a supported coding-agent session starts in a published project, the
session_start.py hook reads .godotmaker/version and injects
[GodotMaker vX.Y.Z] into the session context where the runtime supports that
hook context. This helps the active role skill know which framework version is
deployed.
Workflow for Releasing a New Version¶
The authoritative commands and release gates are in the
release checklist. In summary:
- Make the intended changes in the GodotMaker repository and decide the bump level using the decision tree above.
- Add any required migration scripts, archive
docs/update/next.md, and update every version and license field listed in the release checklist. - Run the complete local validation suite and test the release against an appropriate disposable target project.
- Create a release-preparation branch and PR. A maintainer merges it only after CI and review; PR creation does not authorize the release agent to merge it.
- After the PR is merged and tagging is separately authorized, fetch
main, verify thatorigin/mainpoints at the merged release-preparation commit, and tag that fetched ref explicitly: - Verify the generated GitHub Release and release notes.
What Gets Overwritten on Upgrade¶
Every publish overwrites:
| Directory | Content |
|---|---|
Selected runner skills/ |
All skills (flattened from core + reviewer) |
Selected runner agents/ |
Agent definitions (worker, verifier, reviewer, analyst, asset-producer) |
.godotmaker/hooks/ |
All hook scripts |
Selected runner config/ |
Config files |
Selected runner templates/ |
Document templates |
tools/ |
Python tools (check_project, check_env, etc.) |
These are not overwritten (created only on fresh install):
| File | Reason |
|---|---|
CLAUDE.md / AGENTS.md |
User may have customized it |
| Selected runner hook config or plugin adapter | User hook behavior (overwritten only with --force) |
Selected runner godotmaker.yaml |
Host-specific paths |
.godotmaker/config.yaml |
Project-specific settings |
Note on addon_versions.json¶
config/addon_versions.json tracks Godot addon versions (gecs, gdUnit4, etc.)
— this is separate from GodotMaker's own version. Addon versions are pinned
per Godot engine version and managed independently.