44: Story Studio + Vault + AIs - transcript

Download the MP3

Liam: The QUILL Cast, episode forty-four. I'm Liam. We are ten episodes from the end of a fifty-four-episode course, and today we wire three big systems together. Story Studio from episode forty-two, the Accessible Vault from episodes forty and forty-one, and the AIs from episodes thirty-four through thirty-nine. By the end, your worldbuilding, your manuscript, and your agents will all be able to talk to each other through links and backlinks, with the agents able to do a continuity pass against the whole project.

Jessica: I'm Jessica. Last episode, episode forty-three, was Story Studio practice. We opened a project folder, watched Story Studio synthesize a binder from a bare directory when the sidecar was missing, hand-edited project.quillstory.json to register a character element, opened the details form, and compiled the manuscript into a new tab. We also called out a couple of code-versus-doc drifts: the actual dialog title is "Choose a story project folder", not "Choose Story Project", and the binder is a derived view, not a stored one, so it can never drift from your heading outline.

Liam: And one more time, in case you missed the gentle correction in episode forty-three. The team that built this was honest about the gaps. The binder dialog today does not expose an "Add element" button, so creating a character, place, plot thread, research item, or brainstorm is still done by hand-editing the sidecar, by a Quillin, or by an external tool. Editing and compiling are first-class. Creation through the binder is on the roadmap but not shipped. Naming that is the same job as verifying everything else.

Jessica: Today's plan. First, an honest "where the code is" pass, with a small correction to the brief itself. Second, a do-this-now beat, the first one of the episode, so you have something to do before we get into the integration. Third, we look at what Story Studio and the Vault each contribute, and how the wikilink grammar from the Vault can be used inside Story Studio element files. Fourth, we look at the AI layer, and specifically the branchable writing session model that the AIs in QUILL use, and how it can ride alongside your manuscript. Fifth, a four-step homework, and a setup for episode forty-five.

Liam: Five major beats, and a closing. That is a lot for one episode, so we will keep each beat tight. The rule on this course, and it has held for forty-three episodes, is verify every claim against the code. Today is no different.


Jessica: The "where the code is" pass, with a small correction. The brief for this episode asked us to verify against quill/core/ai/session.py, but the actual file is quill/core/ai/sessions.py, plural. There is no singular session.py. There is also a chat_session.py and an agent_session.py, but sessions.py is the branchable, resumable writing session model, and that is the file the brief meant. We will work from sessions.py, with a quick look at chat_session.py for the Ask Quill engine so we are not confusing the two. If you ever read a doc that points at the singular name, the code says otherwise; the code wins, that is the rule.

Liam: The other two packages the brief names are exactly where they should be. quill/core/story/ contains the model, storage, front-matter codec, manuscript heading iterator, binder assembler, field schema for the details form, and compile. Eight modules, all wx-free, all strict-typed. quill/core/vault/ contains the same shape for the vault: vault, note, links, resolve, index, plus the later phases from episode forty-one, tags, templates, dailynotes, render, sync, publish, site_export, refactor, explorer, preview, autocomplete. The vault module is named vault.py and the symbol is the Vault dataclass, not the folder itself. The folder is quill/core/vault, the package, the type is Vault, the function that produces it is scan_vault, and the cache directory it skips is .quill, exposed as CACHE_DIRNAME.

Jessica: A short tour of those modules, because the names alone do not tell the story. In quill/core/story, model.py owns the data model: ElementKind is a string enum with five values, character, location, plot, research, brainstorm. StoryElement is a frozen dataclass with id, kind, title, path, and tags. StoryProject is a frozen dataclass with a schema version constant pinned at one, a title, a manuscript tuple of relative POSIX paths, and an elements tuple. new_element mints a fresh uuid4 hex. storage.py owns the sidecar file, project.quillstory.json, written atomically, and a fallback scan that synthesizes a project from a bare folder, using any file with the suffix .md, .markdown, or .txt, sorted by name. The folder is always the source of truth; the sidecar is a hint.

Liam: The front-matter codec in frontmatter.py is worth a paragraph on its own. It is a small, deliberate subset of YAML: key-colon-value scalars and dash-prefixed lists, fenced by two lines of three dashes, like YAML's document marker. A block with no closing fence is treated as "no front matter", so a stray triple-dash in your prose never swallows the body. Values are read as plain strings, no type coercion. A key whose value is an indented or dashed list becomes a list of strings. Quoted strings round-trip, with backslash escapes decoded on read and re-escaped on write so the same bytes come out. The codec has no third-party dependency, and the codec is shared shape between Story Studio element files and any Vault note that wants to carry structured fields.

Jessica: And the field schema in fields.py is per kind, and modest. A Character has Role, Goal, Motivation, and Arc. A Location has Significance. A Plot thread has Status. A Research item has Source. Brainstorm has no defaults. Every form ends with a universal Tags row, which is a comma-separated list. The form preserves any unknown key already in the file, so a hand-edited front matter does not get clobbered on round-trip, and a cleared field is dropped on save, so the file never accumulates empty keys like "goal: ". The "type" key is preserved but not shown in the form, so a kind marker someone hand-wrote is never accidentally stripped. That is the same promise, in micro: keep what the user wrote, ask for what the form is asking for, never silently lose information.

Jessica: For the AI layer, sessions.py exports a small, opinionated vocabulary. WritingSession is the immutable snapshot. SessionTurn is one turn in the tree, with a parent_id so a turn can have multiple children. SessionSummary is the row in the session browser, session_id, title, updated_at, turn_count, branch_count. The pure functions that operate on those are new_session, append_turn, branch_from, resume, relabel_turn, path_to, current_path, branch_tips, branch_points, compare_branches, summarize_session, describe_branches, branch_rows, and format_comparison. Persistence is save_session, load_session, delete_session, list_sessions, and most_recent_session, all under the directory app_data_dir() divided by "ai-sessions". That is the file we are working from.


Liam: Time for the first do-this-now beat. Pause the audio. Open your file manager. Create a new empty folder somewhere you can find easily, and call it practice-vault. Inside that folder, create one more empty folder called project. Now create three plain Markdown files inside the practice-vault folder, call them heroine.md, city.md, and chapter-1.md. In heroine.md, write one line: "The detective who refuses to look away." In city.md, write one line: "A harbor city of perpetual fog." In chapter-1.md, write a single ATX heading, hashtag, then the words "Chapter One", then a paragraph that mentions the detective and the city by name. Pause the audio, do the steps, then come back. We are going to come back to that folder in a moment.

Jessica: Welcome back. If you did the steps, you have a folder of three notes that mention each other by name but do not link to each other yet. That is the canonical "before" state, and the one Story Studio and the Vault treat differently. We will come back to that folder, but first, the integration thesis.

Liam: The integration thesis in one sentence. Story Studio owns the manuscript, the binder, the character details, and the compile. The Vault owns the worldbuilding web, the wikilinks, the backlinks, the tags, the templates, the daily notes, the embeds, and the site export. The AIs own the conversation model: branchable, resumable sessions that can read both, summarize both, and write back into both. The three systems are not bolted together at the menu level. They are integrated at the format level. Story Studio element files are plain Markdown with optional front matter. Vault notes are plain Markdown with optional front matter. The grammar is the same. The discipline of "everything is a file" is what lets the systems interoperate without an importer-exporter dance.

Jessica: And there is a more specific bridge worth naming. The front-matter codec in quill/core/story/frontmatter.py is a small, deliberate subset of YAML. It parses key-value scalars and dash-prefixed lists. It round-trips. It does not need a third-party dependency. The Vault, by design, can also carry front matter, because a Vault note is "a folder of Markdown and plain-text notes" with no shape restrictions beyond that. That means a Story Studio character file and a Vault character note are the same shape on disk. Move a file from one world to the other, and it is still readable.


Liam: Now the wikilink bridge, which is the part that makes the integration sing. The Vault's link parser, in quill/core/vault/links.py, recognizes four flavors of wikilink. Square-bracket-square-bracket, target, square-bracket-square-bracket. Square-bracket-square-bracket, target, pipe, alias, square-bracket-square-bracket. Square-bracket-square-bracket, target, hash, heading, square-bracket-square-bracket. Square-bracket-square-bracket, target, hash, caret, block id, square-bracket-square-bracket. And the same shapes prefixed with an exclamation point are embeds. Same-note anchors look like square-bracket-square-bracket, hash, heading, square-bracket-square-bracket, with the target portion empty. Code regions are skipped, so a code sample that mentions the link syntax is not mistaken for a real link. That is the grammar, and it is the grammar that lets your manuscript, your character notes, and your worldbuilding all reference each other.

Jessica: The resolver in quill/core/vault/resolve.py turns a parsed wikilink into a LinkTarget, which is the destination note's path, a character offset for the heading or block anchor, an ambiguity flag, and a list of candidates. Two important honesty notes. First, ambiguous names return a LinkTarget with an empty path and the full candidate list, never silently picking the first match. A caller that wants to resolve an ambiguous link has to disambiguate, by design. Second, unresolved names return None, and the UI offers to create the note. The graph never gets built from guesses; it gets built from real edges.

Liam: And the link index in quill/core/vault/index.py builds the forward and reverse adjacency across the vault. build_index walks every note's resolved outgoing links, drops same-note anchors, self-links, and unresolved names, and produces a LinkIndex with forward, dict of source path to tuple of target paths, and reverse, dict of target path to tuple of Backlink records. Each Backlink carries the source_path, the linking line for context, and the offset. The neighbor function, neighborhood, gives you one note's outgoing links with their titles and its incoming backlinks, together, which is the data behind the spoken "neighborhood" command that lets a writer traverse the web of notes purely by keyboard and ear.


Jessica: Now the AI session model, because it is the part most people have not seen in source. quill/core/ai/sessions.py implements a branchable, resumable writing session, where a writing session is a tree of turns, not a flat transcript. Every turn has a parent_id, so a writer can go back to an earlier point, try a second rewrite of a section, and keep both branches without losing state. The active branch tip is current_turn_id. Appending a turn adds a child of the current tip, and branching, through branch_from, simply moves the tip back to an earlier turn so the next append creates a sibling. Both branches stay in the tree.

Liam: Why this matters for a manuscript project. The AI can be your rewrite partner. You write a paragraph, you ask Quill for a stronger version, you keep the original, you keep the rewrite, you compare them in the branch browser, you label the one you prefer, you resume on that branch, you continue. The branch browser speaks every branch tip with a label, a depth, and a current marker. describe_branches returns a screen-reader-friendly string of one line per branch. summarize_session returns one line, "Title: N turns, M branches." format_comparison returns a screen-reader-pageable comparison of two branch tips, with their common ancestor, the turns only on the first branch, and the turns only on the second.

Jessica: And the screen-reader integration is part of the design, not an afterthought. The session is persisted as schema-stable JSON under app_data_dir() divided by "ai-sessions", written atomically through the same write_json_atomic path the rest of core uses. most_recent_session loads the most recently updated session, for the "continue where I left off" affordance. list_sessions returns a tuple of SessionSummary records, most recently updated first, sorted by updated_at descending. And the session id is sanitized to alphanumeric plus dash and underscore before it touches the file system, so a malicious id cannot do path traversal.


Liam: Now the integration moves, in three concrete steps. Step one, choose your worldbuilding home. For a long-running project with many notes, the Vault is the right home. The Vault indexes everything, finds unlinked mentions of your characters and places across the manuscript, and offers to link them. For a focused project with a handful of characters and a few places, Story Studio's element model is enough. The decision is not ideological; it is about the volume of cross-references and the need for full-text search and backlinks.

Jessica: Step two, decide on the link grammar. The Vault's wikilinks, square-bracket-square-bracket, target, square-bracket-square-bracket, work inside any Markdown file, including Story Studio element files. A character note in the Characters group of a Story Studio binder can carry a wikilink to a separate Vault note, or to another Story Studio element by file name, and the link will resolve as long as both files are part of the same scanned context. There is no special "Story Studio link" type. The link grammar is one grammar.

Liam: Step three, decide what the AI is allowed to read and write. The agent session in quill/core/ai/agent_session.py runs a multi-step agent loop, plan, generate, optionally refine, return an AgentResult. Each step can emit a progress callback. The session is cancelable, with a stop_event checked between steps. And the session is part of the same safety discipline as everything else in core, with coded errors for failure, authentication failure, and cancellation, all inheriting AgentSessionError, which is itself a CodedError with the code "QUILL-AI-AGENT-SESSION". When you wire an agent to a manuscript project, the agent sees the files. It does not see anything outside them. The allowlist is the file list.


Jessica: The continuity pass, in detail, is the move the brief hints at. With the Vault, the manuscript, and an AI session, the agent can do a pass over the whole project that catches broken references, name drift, timeline contradictions, and unlinked mentions. The mechanics are the same as any agent task. The agent reads the binder, reads the vault, sees the link index, sees the backlinks, and writes a report. The report lives in a session you can branch from. You can rerun the same pass after editing chapter seven and see what changed.

Liam: And there is a small honesty moment, because we promised it in every prior episode. The agent pass is only as good as the data the index can see. A character that is mentioned in the manuscript only by a nickname that is not in any alias list will not be picked up by the unlinked-mentions scan. A plot thread whose status is set to "resolved" in one binder and left as "open" in another will confuse the agent. The integration is a tool, not a guarantee. Run it. Read its output. Treat the output as a starting list, not a final answer.

Jessica: A second honesty moment, this one about what is not in the codebase today. There is no "Run continuity pass" command in any menu we found. The pieces are there: the agent harness, the binder, the vault, the link index. The agent can be invoked on a folder. The continuity pass as a first-class named operation is a composition of those pieces, not a single registered command. The good news is that the composition is small. The better news is that the pieces are stable, the API is documented, and a Quillin can wire them into a single command. The honest framing is: you can do this today, with the parts we have, and the menu item is a future polish.


Liam: Time for a second do-this-now beat, the one that ties the three systems together. Pause the audio. Open the practice-vault folder you made earlier, the one with heroine.md, city.md, and chapter-1.md. In heroine.md, after the first line, add a second line that says "City: square-bracket-square-bracket, city, square-bracket-square-bracket", using the wikilink syntax, the same shape you learned in episodes forty and forty-one. In chapter-1.md, in the paragraph that mentions the detective and the city, replace the words "the detective" with "square-bracket-square-bracket, heroine, square-bracket-square-bracket" and the words "the city" with "square-bracket-square-bracket, city, square-bracket-square-bracket". Save both files. Then come back. We are going to do one more thing, but the writing is yours.

Jessica: Welcome back. If you did the steps, your chapter now links to its hero and its setting by name, and the hero links to the setting. Open your vault, scan it, and Show Backlinks on city.md should list heroine.md and chapter-1.md, both with the linking line quoted for context. Open heroine.md and Show Backlinks should list chapter-1.md, with the linking sentence around the embed. That is the web, in three files. It scales to three hundred.

Liam: The third step, the one we are doing together, is the AI. Open Ask Quill, or the AI Library, the choice is yours, and start a new writing session. Give it a prompt along the lines of, "Read the three files in my practice-vault folder. List every cross-reference you find. Note any unlinked mentions of any character or place. Suggest up to three edits that would tighten the worldbuilding." Send the prompt. The session is now an entry in the session list, with a title, a turn count, and a branch count. Branch it if the first answer is interesting but not quite right. Rerun it on a different branch. Compare the branches. That is the AI layer, applied to the web, in real time.

Jessica: The composition, then, looks like this. The folder is the source of truth. The binder is the manuscript view. The vault is the worldbuilding web. The link index is the connective tissue. The writing session is the AI's memory of working on this project. None of them own the words. All of them can show you the words. You can drop any of them and the words remain, in plain files, in any tool that reads Markdown. That is the integration promise, and the integration promise is also the course promise.


Liam: A quick correction-of-self moment, because we have earned your trust by being honest, and we will keep earning it. In the intro to this episode, I said "the agents from episode thirty-nine can do a continuity pass against the whole project." That is true at the level of the pieces, and it is also true that the agent harness shipped as part of the agent build-out in PR 801, with the agent path in the AI wizard and the Engines tab. What is not yet a first-class menu item is the "Run continuity pass" command. So when you reach for this, you reach for an agent task you compose, not a button you click. That nuance matters, and I would rather say it out loud than let you search the menus for a button that is not there.

Jessica: And a second correction-of-self moment, this one about the brief. The brief said to verify against quill/core/ai/session.py, singular. The file is sessions.py, plural. Anyone reading along with the brief should know that the code, as always, is the source of truth, and the file name is plural. The brief is a planning document. The code is the contract. If you ever see a doc and a filename disagree, the filename wins.


Liam: Homework. One: take a small Story Studio project, three to five Markdown files with a few ATX headings each, and register at least one Character, one Place, and one Plot thread by hand-editing project.quillstory.json. Reopen Story Studio and arrow through the binder. Two: open a Vault, create three notes that mention each other, and convert at least one of those mentions in each direction into a wikilink, square-bracket-square-bracket, target, square-bracket-square-bracket. Scan the vault and Show Backlinks on the target to confirm the graph. Three: start an Ask Quill writing session on the same project folder, ask for a continuity pass, read the result, branch the session, and ask for a sharper version. Compare the two branches using the branch browser. Four: open the project's session list, find your continuity pass, resume it, and continue the conversation as if it were a meeting that had a coffee break.

Jessica: Next episode, episode forty-five, opens the final part of the course: production and trust. We start with GLOW, the accessibility review system, the guided layout and output workflow, and a small role reversal worth savoring. Most accessibility software helps you consume inaccessible things. GLOW helps you produce accessible things. It is a second reader who speaks your language, and the people most affected by inaccessible documents are the best equipped to never produce one. We are nine episodes from the finale after that, and they cover GLOW for files, dictation, transcription, the audio studio, the listening companion, the AI library, agents, accessible vault basics, vault power, the Quillins and console, trust, community, and the finale. Same time, same channel, episode forty-five.

Liam: I'm Liam.

Jessica: I'm Jessica. Build the web before you write the book, and let the agents read it back to you.

Liam: The QUILL Cast is a fifty-four-episode audio course on QUILL. This has been episode forty-four. Thanks for listening.

Back to all episodes