# File formats Every file Craft Anything saves carries a version number. Reading a file runs the same three steps: parse it into a raw document (a JSON value or a TOML table), run the migration steps from the file's version up to the current one, then turn the document into Rust types. The step runner is `crates/engine/src/versioned.rs`. | File | Owner | Current version | Older files | Newer files | |---|---|---|---|---| | `/craft.json` | `engine::workspace` (`Manifest`) | `MANIFEST_VERSION` = 1 (`version`; missing = 0) | brought forward, saved as current | opened read-only: saving is refused | | `config.toml` (`settings::config_dir()`) | `engine::settings` (`Settings`) | `SETTINGS_VERSION` = 2 (`version`; missing = 0; 2 moved `[engines.ghidra] enabled` to `deep_mode`) | brought forward | loaded, never saved over | | `.cany/project.toml` | `engine::project` (`ProjectMeta`) | `FORMAT_VERSION` = 1 (`format_version`, required) | brought forward | refused, with "update Craft Anything" | | `/transcript.json` | `ai::context` (the API builder's conversation) | `TRANSCRIPT_VERSION` = 1 (`{version, history}`) | read as before | not resumed; a fresh build replaces it | | `/app/mechanics.json` | `engine::mechanics` (written by the builder, read by `MechanicsCheck`) | `MECHANICS_VERSION` = 1 (`version`; missing = 1) | — | the check fails and says so | | `/checks/round-N/self-test.json` | `engine::self_test` (written by the built game's `--self-test`, read by `SelfTestCheck`) | 1 (`version`; missing = 1) | — | the check fails and says so | | `/usage.json` | `engine::workspace` (`ProjectUsage`; written only by the rebuild run) | `USAGE_VERSION` = 1 (`version`; missing = 0) | read as before, saved as current | read as far as its fields are known, never written over | | `/questions.json` | `engine::questions` (`AskedQuestion`, written by `rebuild_question_ask`) | `QUESTIONS_VERSION` = 1 (`{version, items}`) | read as before | read as far as its fields are known | | `/pinned//pin.json` | `engine::pins` (`Pin`) | `PIN_VERSION` = 1 (`version`; missing = 1) | brought forward | read as far as its fields are known | | `/standin.json` | `engine::tools::standin_tools` (`analysis::standin::StandIn`; written only by `web_standin_build`) | `STANDIN_VERSION` = 1 (`version`, required) | brought forward | refused, with "update Craft Anything", and never written over | | `/feed.json` | `ai::rebuild::feed` (written by the run) | `FEED_VERSION` = 1 (`{version, items}`; a bare list = 0) | read as before, saved as current | never written over; the conversation is rebuilt from the project for display | Other JSON in a project (`assets.json`, `inbox/`, `checks/`, evidence records) has no version yet; add one the first time its shape changes. ## Newer files A file from a newer Craft Anything is never downgraded or overwritten, because the fields we don't know would be lost. `craft.json` and `config.toml` are still read as far as their fields are known, so the person can see their project or keep their settings; every save returns a `ToolError` that says to update Craft Anything. A newer `craft.json` whose fields can't be read at all is refused the same way, and a newer `config.toml` that can't be read gives defaults without touching the file. A newer `.cany` is refused outright: its evidence layout may have changed in ways we can't see. A file that can't be parsed is not "newer": `config.toml` is set aside as `config.toml.broken` and defaults are used; an unreadable `craft.json` gives a plain-English error when opened and is left out of the project list. ## Adding a migration step 1. Bump the version constant (`MANIFEST_VERSION`, `SETTINGS_VERSION` or `FORMAT_VERSION`). 2. Append one function to the matching steps list (`MANIFEST_STEPS`, `SETTINGS_STEPS`, `FORMAT_STEPS`). It takes the raw document from version n to n + 1: rename, move or fill keys. Return `Err` with a plain-English reason when it can't. A compile-time assertion fails if the list and the constant disagree. 3. Add a test that reads a literal sample of the old file (keep the existing samples; old files must keep loading forever). 4. For `craft.json`, `mechanics.json` and `self-test.json`, run `cargo xtask schemas` and commit `docs/schemas/`. `cargo xtask schemas --check` (and an engine test) fail when the published schema is stale.