AshForge Chronicles · the whole tool, one course
A quest is rows and slots.
A menu is not a branch.
Chronicles authors quests, objectives and dialogue. Most of what feels like branching is a menu that reconverges, and its validation promises less than its name suggests — this course is the whole tool with those two facts up front.
What it is
A quest editor. Objectives are rows, dialogue is slots, and both save into your mod's data.
Modules 1–4 are reading and writing a quest, 5–8 are branching, 9–13 are what actually reaches the game.
The honest limit
⛔ Quests can never ship as data. The game has no quest, dialogue or lore definition type — none. Chronicles' output reaches a game only through a compiled assembly.
That is not pending work; it is the shape of the game. Modules 9 onward.
Chronicles is a focused tool. Its content is 13 things worth knowing, not three escalating tiers — the old split implied the later material needed the earlier, and it does not. Read straight through, or jump to the module that matches your problem.
The Quests tab
Read one before writing one
The Quests tab is already active when Chronicles opens, and there are two demo entries waiting in the list.
Drill01
Select First Contact
- Click the first of the two entries, First Contact.
- The inspector fills with the whole quest at once.
- Read down it before touching anything.
Done whenYou can name every section without scrolling back.
What is there01b
Eight sections, one of them different
- Title, and a free-text Description
- Difficulty — Easy, Medium, Hard or Legendary
- Status — Draft, Active or Complete
- Objectives, Rewards, Prerequisites and Notes
Most of those are prose and behave exactly as you would expect. Objectives does not, and that is the whole point of this tutorial.
The one idea
An objective is a row
Press Add Objective and you do not get a text box. You get four separate fields, and only one of them is written for a human being.
Figure 01 — one objective Four fields. Three of them are machine-facing, one is not, and no part of the tool keeps them in step.
Type is a dropdown offering exactly four options: elapsed,
eliminate_faction, manual and deliver. That word is what
the game enforces. Target names what it acts on, and Seconds matters for the
timed types. Description is the sentence the player sees, and the game does nothing with
it whatsoever.
collect, talk_to and custom while omitting
manual. Only the four above are selectable, so trust the dropdown: it is what you
can actually author with.Watch it come apart in the demo data
Look at First Contact's first objective. The inspector columns are narrow, so read the stored values rather than what happens to fit on screen:
Type: elapsed Description: "Speak with the village elder" Target: (empty) Seconds: 0
Read as prose, that is an instruction to go and speak to someone. Read as data, it is a request to wait for zero seconds with nothing to wait on. The sentence says one thing and the fields say another, and nothing in the tool will point it out, because it is not an error. It is simply a quest that does something other than what it reads like.
The second objective is coherent by comparison: deliver, described as
“Deliver the token”, with elder_token as its target. A type,
a matching verb, and something to actually deliver.
And The Lost Cache goes one step further. Its third objective ships as type
manual — the option the model's own documentation does not list. The demo
content was authored against the dropdown rather than against the docs.
Your first quest
Write your own
You are not starting from an empty form. Chronicles hands you a working skeleton and expects you to make it mean something.
Drill03
Press New, then fill it deliberately
- Press New. You get New Quest 3, Difficulty
Medium, StatusDraft. - Note that one objective is already scaffolded for you:
elapsed, sixty seconds. - Give the quest a title and a description, then make that objective do real work.
- Choose
deliver, describe it as “Bring the sealed crate to the elder”, and put the recipient's id in Target.
Done whenThe sentence and the data say the same thing.
Rewards03b
Names, not descriptions
The Rewards box says it plainly: one per line, reference Items/Weapons by name.
Basic Medkit in the demo is not descriptive text, it is a lookup.
Spell it the way the game spells it, or the reward quietly is not there. It is the same idea as the objective Type: you are writing names into a vocabulary someone else defined.
Validation
What Validate promises
Press Validate and you will most likely get “Narrative validation passed — no issues found.” It is worth knowing exactly what that sentence is promising, because it is narrower than it sounds.
| Area | What it catches |
|---|---|
| Quests | No title; no objectives at all; an objective with an empty description. |
| Dialogues | No title; a node with neither text nor choices; a choice pointing at a node that does not exist. |
| Lore | An entry missing its title or its content. |
That is the entire list. It does not check that a Type matches its Description, that a Target exists, that a reward name is real, or that any of it is any good.
Autosave protects your typing. It does not protect an undo, and that is worth knowing before you rely on the button.
The first edit of a session cannot be undone at all. Chronicles only snapshots after a change, and never records the state you opened with, so after one edit there is nothing to go back to — the Undo button simply stays greyed until you have made a second one.
And an undo that does work never reaches the file. Undoing marks the project clean, which stops the autosave from firing, so the window and the file disagree. I removed a quest with Undo and watched the window show three while the file still held four — then reopened Chronicles and got the fourth one back.
So if an undo matters, make one more edit afterwards (or press Save) to force the corrected state onto disk.
The shipped demo
A menu, not a branch
Select Elder Greeting and the inspector splits in two: a node graph in the top pane, and a scrolling form beneath it. The hint under the buttons spells the division out — “node graph is the top pane; wheel zooms/pans there; this form scrolls.”
There is exactly one node. The Elder says “Stranger… you carry the old mark.” and offers two choices. Scroll the form down to the Choices box and you can see why that is not yet a conversation:
I come in peace.|| I seek the cache.||
Each line is Text | NextNodeId | Condition. Both NextNodeId slots are
empty, and empty means end. The player is being offered a fork where both paths stop.
Figure 01 — before and after Two choices with nowhere to go is a menu. The shape on the right is about thirty seconds of work away.
How dialogue is edited
Two editors, one model
The Choices box carries its own warning label: “Choices (Text|NextNodeId lines; connections in graph also update this)”. That parenthesis is the important part of the sentence.
Use whichever suits the job in front of you. The graph is better for shape — seeing where a conversation loops, spotting an orphaned node — and the text is better for precision. The text is also the only place the Condition field can be reached at all.
Add two nodes and wire them up
- Above the form are + NPC Node and + Player Node, plus a generic Add Dialogue Node and an Add Node (text) button lower down. Add two nodes, one reply for each existing choice. a branch needs somewhere for both paths to land
- Connect them by dragging from the Elder node's right-hand port to a new node's left port, or by typing the ids directly. either edits the same model, so use whichever is quicker for you
- To type them you need the ids, and the form gives them to you: each node's header reads
Node 0 (5b868865), where the number is its position and the value in parentheses is its id. position and identity are different things, and only one of them is stable
I come in peace.|5b868865| I seek the cache.|a91c3f02|
Reload the graph and the fork is real.
Conditions
The third slot
Each choice line has room for one more field, and it is the one that connects a conversation to the rest of your narrative.
A condition gates whether the choice is offered at all. The data model gives its shape as a
prerequisite expression, along the lines of quest:foo.complete.
Chronicles does not evaluate it. It does not parse it, check it, or warn you when it is
nonsense. What it does do is carry it: the export writes each choice as
{ text, next, condition }, and the game is what reads that third field and
decides.
Validation
The check that earns its keep
Tutorial one described Validate as a broken-link checker. This is the link it checks, and it is the one bug in a branching conversation that you genuinely cannot find by reading.
Break it on purpose
- Point a choice at a node that does not exist by editing a line to read
I seek the cache.|deadbeef|. an id that was never minted is indistinguishable from one you deleted - Click away so the edit commits, then press Validate. the edit lands when the field loses focus, not as you type
Validation found 1 issue(s). Dialogue 'Elder Greeting': choice "I seek the cache." links to a missing node (deadbeef)
It names the dialogue, quotes the choice text, and prints the id that went nowhere. Put the real id back and it returns to “Narrative validation passed — no issues found.”
This matters more than it looks. A dangling NextNodeId is invisible in the graph,
because there is simply no line drawn, which looks identical to a choice that deliberately ends
the conversation. It survives a read-through, because the text still makes sense. It only shows
up in play, when a conversation stops dead in the middle.
The Lore tab
The hint that isn't a link
Lore is where a narrative stops being a list of records and starts to feel like a place. It is also the point at which Chronicles quietly stops checking your work, and the demo entry that ships with the tool happens to illustrate the problem rather well.
Drill01
Open Lore → The Ashfall
- Switch to the Lore tab and select the one demo entry.
- Note the Category dropdown, which reads
History. - Read the opening of the Content body: “When the sky burned, the old world ended…”
- Then read the last three fields together, because they are the reason this tier exists.
Done whenYou can say which of those three fields the game will ever read, and which one is there purely for you.
The three fields01b
Two of these are data. One is a note to yourself.
- Unlock Hint / Conditions — reading
Complete First Contact - Related Quests (names/IDs, one per line) — empty
- Related Dialogues (names/IDs, one per line) — empty
NoticeThe entry describes a dependency in prose while the field that would record it has been left blank.
The hint tells you this entry depends on First Contact, and that quest does exist — it is two tabs away. What has been left empty is the field that would record the relationship in a form something could act on.
It is the same split the beginner tier found inside a single objective row, one level up: the prose and the data disagree, and only the data ships. Treat anything you write in Unlock Hint as a note to yourself.
Validation, and where it stops
A count is not a check
Tutorial two ended by praising Validate for catching a dangling
NextNodeId, and that praise was earned. This lesson finds the edge of it. Rather
than take anyone's word for where that edge sits, you are going to walk into it deliberately.
Point a lore entry at a quest that does not exist
- In Related Quests, type the name of a quest that appears nowhere in the project.
The Quest That Never Waswill do. the field is free text, so nothing will stop you - Click away so the edit commits, then press Validate. Chronicles commits the edit when the field loses focus, so validating before that only tests the old value
- Read the result, then press Generate Report and open the
.txtfile it names in the log. the report is the only place this cross-reference surfaces at all, and it surfaces as a number
Narrative validation passed (no issues).
That is with a lore entry pointing at a quest which has never existed anywhere in the project. Now generate the report, and look at the last line:
AshForge Chronicles Narrative Report
Mod: Unnamed Mod Generated: 2026-08-29T06:27:54Z
Quests: 2
- First Contact (Easy) : 2 objs, 1 rewards
- The Lost Cache (Medium) : 3 objs, 2 rewards
Dialogues: 1
- Elder Greeting : 1 nodes, 2 choices
Lore: 1
- The Ashfall [History] links Q:1 D:0
That count is measuring how many lines are in the box, not how many of them point at something real. Validate never resolves these references at all: for a lore entry it asks whether there is a title and whether there is a body, and those two questions are the whole rule.
The practical consequence is worth sitting with for a moment. Rename a quest and every lore entry that mentioned it goes stale immediately. The count stays where it was, validation stays green, and nothing in the tool will mention it.
The same tool, two different answers
| Cross-reference | Checked? | Why |
|---|---|---|
choice → NextNodeId | Yes | Nodes carry generated ids, so a choice can only mean one node. |
lore → Related Quests | No | Free text accepting names or ids, one per line, with nothing to resolve against. |
lore → Related Dialogues | No | The same field type, for the same reason. |
Unlock Hint | No | Prose, and never parsed by anything. |
The export seam
Three files, three fidelities
Five buttons run along the header: Export Bundle, Test in Crucible,
Export Narrative, Generate Report and Merge Bundle. Three of them write
files, all into user://output, and every one of them reports only to the log. There
is no dialog and no toast, so unless you read that line you will not know what was written or
where it went.
Figure 01 — the export seam One button writes two files, and the flag named --definitions will accept a file that is not a definition bundle without complaint.
The differences between these files are real rather than cosmetic.
_narrative.json is the project serialised whole, which makes it the safest thing
to archive and the wrong thing to ship. _narrative_runtime.json is assembled field
by field for loading, so quests keep their structured objectives with type,
target and seconds intact.
_definitions.json is the handoff contract shared across the suite, and it is the
lossy one. Objectives collapse into plain strings — the objective's description where it
has one, otherwise type:target — and prerequisites, status and notes are not
part of the bundle shape at all. That is a deliberate contract boundary rather than a bug, but
it does mean a round trip through the bundle is not a round trip through your work.
_narrative.json somewhere dated yourself, because nothing in
the tool is keeping one for you.Test in Crucible
Whatever you touched last
Test in Crucible looks as though it knows what your project is. What it actually knows is which button you pressed most recently, and that difference matters more than it sounds.
Drill04
Watch the argument change
- Press Export Bundle, then Test in Crucible, and read the log line.
- Now press Export Narrative, then Test in Crucible again.
- Read the log a second time. The argument has changed.
Done whenYou can predict the argument before you press the button.
The rule04b
It decides in this order
- The project's mod root, if one is set →
--mod - Otherwise the last file any export button wrote →
--definitions - Otherwise →
--help
NoticeThe third case fails quietly. Crucible opens on its help text and nothing explains why.
Both Export Bundle and Export Narrative write to that same “last exported” slot, so the flag stays constant while its argument moves underneath you. Export the narrative last and Crucible is handed the whole-project dump under a flag named for the bundle. The runtime file, written in the same click and the one actually shaped for loading, is never what gets passed.
ModRoot populated the
button always passes --mod and never consults the export slot. If you are testing
repeatedly it is worth setting once so you can stop thinking about button order; if you would
rather not, make a rule of pressing the export you mean immediately before Test.Ending a session
A shipping pass
None of what follows is enforced by Chronicles. It is simply what working carefully with it looks like, once you know where its checks stop.
The end-of-work sequence
- Run Validate. it catches dangling dialogue links and empty required fields, which is necessary but not sufficient
- Press Generate Report and read the lore lines, comparing each
links Q:nagainst the quests you know exist. this reconciliation is manual, and nothing in the tool can do it for you - Press Export Narrative, which gives you the archive copy and the runtime copy
together. Ship
_narrative_runtime.json. one button writes two files, and only one of them is the game's - Copy the archive somewhere dated. the next export will overwrite it in place
- Run Test in Crucible last, or set a mod root and stop worrying about the order. so the export slot holds what you think it holds
- Read the log after each of these. every one of these buttons succeeds and fails silently in the interface
When it goes wrong
| Symptom | What it actually means | Fix |
|---|---|---|
| Validation is green, but a lore link goes nowhere | Validate never resolves cross-references. For a lore entry it checks only the title and the body. | Reconcile each links Q:n against the real quests by hand, using the report. |
The report still shows links Q:1 for a quest you renamed |
The count is the number of lines in the box, not the number of targets that exist. | Treat a rename as a two-part job and search your lore for the old title. |
| Crucible opens on its help text | There is no mod root set, and no export has been pressed this session. | Set ModRoot, or export immediately before pressing Test. |
| The game ignores objectives you carefully structured | You shipped _definitions.json, where objectives are flattened to strings. |
Ship _narrative_runtime.json instead. |
| Yesterday's export has gone | Exports overwrite in place. Only reports are timestamped. | Copy _narrative.json to a dated file at the end of each session. |
| Nothing appeared to happen when you pressed a button | It most likely worked. Every export reports to the log and nowhere else. | Read the log line, which names the full output path. |
Course complete
What you now know
Check yourself
- An objective is four fields, and only one of them is written for the player.
Typeis a fixed vocabulary the game enforces;Descriptionis free text it ignores entirely.- The dropdown offers four types while the model's comment documents six. The dropdown is the truth.
- The two can disagree silently, and in the shipped demo data they already do.
- Rewards are names looked up in the game's own list rather than descriptions.
- New gives you a working skeleton with an objective already in place.
- Chronicles auto-saves your typing — but the first edit of a session cannot be undone, and an undo never reaches the file until you make another edit or press Save.
- Validate proves structural completeness and never sense.
- A choice is
Text | NextNodeId | Condition, and an emptyNextNodeIdends the conversation. - The shipped demo offers two choices and ends on both, which makes it a menu rather than a branch.
- The graph and the text box edit one model. Sync Graph <-> Model exists because they can drift.
- Node ids are generated and shown in the form as
Node 0 (id). Read them rather than inventing them. Conditionis carried into the export and evaluated by the game, never by Chronicles.- The dangling-link check is invisible by reading and lethal in play. Validate after every rewire, and always after a delete.
- Lore's Unlock Hint is prose, and ships as prose. Only Related Quests and Related Dialogues are data, and both are free text that will accept a name or an id.
- Validate checks a lore entry for a title and a body, and nothing else. Cross-references are never resolved.
links Q:1counts the lines in a box rather than the connections that exist, which you confirmed by pointing a lore entry at a quest that was never created.- Three buttons write four files at three fidelities.
_definitions.jsonis lossy by contract, and_narrative_runtime.jsonis the one to ship. - Reports accumulate and exports overwrite, so archiving is something you do deliberately.
- Test in Crucible passes the last exported path, or the mod root if one is set, or
--helpif it has neither. - Every one of these buttons reports to the log and nowhere else.
That is Chronicles. ⛔ Worth repeating because it changes how you plan: no quest reaches the game as data. Use it to design and record a questline that an assembly will then implement.
