Format and migrations
Read the format line on a graph file, see what each version added, and move an older graph forward without losing what you authored.
On this page
Every graph file declares the vocabulary it needs. Platformer’s patrol says %% source-format: bitquirky.behavior/1.3, and that line is the contract between the file and the reader.
The problem
Section titled “The problem”Formats that rewrite your files on open lose things quietly. A default becomes a constant, a generic loses a type argument, an out parameter gains an invented input, and the graph still opens, so nobody notices until a build behaves differently.
This format is additive. A later reader accepts valid earlier text unchanged, and a document moves forward only when an edit needs the later vocabulary. Opening and saving an unchanged 1.3 graph leaves it at 1.3.
The delivered format line
Section titled “The delivered format line”%% source-format: bitquirky.behavior/1.9| Format | What it added |
|---|---|
| 1.0 | Legacy text without semantic node parameter blocks |
| 1.1 | Typed semantic node parameters |
| 1.2 | Dedicated member-binding records |
| 1.3 | Declared blackboard values, variable member targets, entity records |
| 1.4 | Exact open generic method identity with ordered concrete choices |
| 1.5 | Explicit action-state lifecycle phases and phase-specific groups |
| 1.6 | Exact method parameter names and the Declared default source |
| 1.7 | Explicit output-only parameter records and their destinations |
| 1.8 | Expanded individual params elements and the active input form |
| 1.9 | Member-binding references as argument sources |
Package version, behavior version, and format version are separate identifiers. Compatibility keeps the mapping.
One version gates a feature outright: an entity graph needs at least 1.3, and a file with %% runtime-path: entity on an earlier format is reported as needing %% source-format: bitquirky.behavior/1.3.
Additive means your intent survives
Section titled “Additive means your intent survives”Newer records carry information older ones could not, and they are never guessed from an older shape.
- A 1.4 generic identity keeps the open definition and each concrete type argument separately.
- A 1.6 parameter record distinguishes choosing the declaration’s Default from authoring a constant that happens to equal it.
- A 1.7
outrow has an output destination and no invented input value. - A 1.8
paramsbinding keeps each expanded element’s source and its position. - A 1.9 argument can read a sibling binding on the same node, instead of being reinterpreted as a blackboard field.
An unknown, incomplete, or inconsistent record fails with a structured diagnostic. The reader will not fall back to the nearest older shape it recognizes.
Type identity
Section titled “Type identity”Modern records keep assembly identity for non-keyword types, and older text may lack it. Resolution has three outcomes: the recorded assembly and type resolve; the identity is gone and a surviving same-named candidate is offered without being adopted; or several candidates are possible and you confirm which one you meant.
Confirming writes the chosen identity into the file, which is the migration. Nothing switches to whichever assembly happens to load first.
Member signatures
Section titled “Member signatures”A saved binding resolves to one exact declaration, and parameter type, name, order, and modifier are part of that identity. Renaming or reordering a parameter, turning ref into out, changing the return type, or moving to another overload blocks the binding until you rebind it deliberately.
One compatibility case is safe: exactly one longer declaration of the same member that appends only ordinary optional parameters with concrete compile-time defaults. An ambiguous longer declaration, a required suffix, params, or optional metadata with no value does not qualify.
A changed declared default never edits graph text. It changes the derived digest and the generated call after a reimport. A default that disappears blocks the binding until you choose a constant, a variable, or another member.
Declared values
Section titled “Declared values”Declared values keep payloads the new type cannot read instead of dropping them. Retyping keeps what the new type can read and seeds the rest with that type’s real default. Retyping a dictionary key keeps every entry and reports collisions or unreadable keys locally. None and empty stay different states throughout.
Collection shape changes follow the rules in variables and types.
Migrate one graph safely
Section titled “Migrate one graph safely”- Keep the
.bqbehaviorfile and its.metafile together. - Open the graph and read every importer diagnostic before changing anything.
- Confirm ambiguous type identities explicitly.
- Rebind stale members in the picker instead of editing a displayed name.
- Inspect collection and output destinations that stayed invalid by design.
- Save through the editor, then reopen the graph and read its validation.
- Reimport and let generated C# compile.
The writer picks the minimum format the records actually need, so a bulk replacement of format tokens is a claim the file cannot back up.
No stale fallback
Section titled “No stale fallback”A graph whose migrated text is invalid has no valid executable cache. Hosts and player builds refuse it, and none of them runs the last successful cache, invents defaults, or drops records it cannot read.
When it goes wrong
| Symptom | Check | Fix |
|---|---|---|
| The importer reports an unknown record. | Read the format line and the record the diagnostic names. | Open and save the graph in this editor so the writer emits the format those records need. |
| A type is reported as ambiguous. | Read which candidates the editor offers for the recorded name. | Confirm the one you meant, which writes its assembly identity into the file. |
| A binding stayed stale after you renamed the parameter back. | Compare the parameter names and order with the saved record. | Rebind once in the picker. Parameter names are part of the identity. |
| A retyped variable kept a value you expected to lose. | Declared values keep payloads instead of dropping them. | Clear the entry deliberately if the old value is wrong for the new type. |
| An entity graph reports that it needs format 1.3. | Read the header for runtime-path and source-format together. | Save the graph from this editor, which writes the format its entity records require. |
- Graph files: what the file stores and what import prepares.
- Arguments and results: the records versions 1.6 through 1.9 are about.
- Reference Scan: repairing stale identities across many graphs at once.