The builder's plan card can be folded away, and the fold is kept in the project's feed until the builder makes a new plan (refs #504). Supersedes #779. Co-authored-by: Fábio Soares <fabiodev1123@gmail.com>
5.5 KiB
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 |
|---|---|---|---|---|
<project>/craft.json |
engine::workspace (Manifest) |
MANIFEST_VERSION = 2 (version; missing = 0) |
brought forward, saved as current | opened read-only: saving is refused |
<project>/inbox/build-progress.json |
engine::workspace::progress (BuildProgress; written only by the rebuild run) |
BUILD_PROGRESS_VERSION = 1 (version; missing = 0) |
brought forward; an absent file starts with no counted turns | the build stops and the record is left unchanged |
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 |
<name>.cany/project.toml |
engine::project (ProjectMeta) |
FORMAT_VERSION = 1 (format_version, required) |
brought forward | refused, with "update Craft Anything" |
<project>/transcript.json |
ai::context (the API builder's conversation) |
TRANSCRIPT_VERSION = 1 ({version, history}) |
read as before | not resumed; a fresh build replaces it |
<project>/app/mechanics.json |
engine::mechanics (written by the builder, read by MechanicsCheck) |
MECHANICS_VERSION = 1 (version; missing = 1) |
— | the check fails and says so |
<project>/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 |
<project>/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 |
<project>/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 |
<project>/pinned/<n>/pin.json |
engine::pins (Pin) |
PIN_VERSION = 2 (version; missing = 1; 2 records the selected program for a copied direct launch) |
brought forward | read as far as its fields are known |
<folder>/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 |
<project>/feed.json |
ai::rebuild::feed (written by the run) |
FEED_VERSION = 4 ({version, items}; a bare list = 0; 4 adds PlanDismissed, a folded plan card) |
read as before, saved as current | never written over; the conversation is rebuilt from the project for display |
A saved User message with pictures is {text, pictures: [paths]}, where the pictures are the
copies in the project's attachments/; one without pictures stays the plain string earlier
versions wrote.
Other JSON in a project (assets.json, inbox/ except inbox/build-progress.json, checks/,
evidence records) has no version yet; add one the first time its shape changes.
Stand-in cached responses
Version 1 stand-in files retain a recorded 304 when no usable cached body was saved. Those samples
remain in the file for diagnostics, but replay skips them within the best matching path, query and
operation group. If that group has no usable answer, it reports cached_body_unavailable instead
of sending a bare 304 to a fresh client. A usable cached body is still stored and replayed as a 200
with was_not_modified: true. This interpretation also applies to existing v1 files; the persisted
shape and version are unchanged.
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
- Bump the version constant (
MANIFEST_VERSION,SETTINGS_VERSIONorFORMAT_VERSION). - 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. ReturnErrwith a plain-English reason when it can't. A compile-time assertion fails if the list and the constant disagree. - Add a test that reads a literal sample of the old file (keep the existing samples; old files must keep loading forever).
- For
craft.json,inbox/build-progress.json,mechanics.jsonandself-test.json, runcargo xtask schemasand commitdocs/schemas/.cargo xtask schemas --check(and an engine test) fail when the published schema is stale.