Story Development with Claude: A Methodology for Authored Interactive Fiction

I think Claude Code is very impressive for some things, but actually producing code is absolutely not one of them.

Perhaps I’m approaching things differently from people who have better experiences. For one I’m not approaching my interactions as if I’m talking to a “partner”, much less a romantic partner (in “couple’s therapy”, which sounds more like a Black Mirror plot than a way to get a clear-eyed evaluation of the system’s capabilities). And, as I commented before, I actually try asking questions I already know the answer to. And Claude Code constantly produces just outright incorrect or at best subtilty bad code.

There are some things I’ve noticed that are just stylistic complaints: writing a method that tests for some condition and either returns a default or does some processing before returning a different value, and instead of putting the check-and-return-default at the top, Claude produces a giant conditional block.

I’ve also had the experience where I caution that any answer should be subjected to whatever static analysis, call graph analysis, and so on that Claude is capable of doing (I’m not going to include the entire spell-like invocation rituals for doing this but we can talk about that (and the fact that, like spells, these things do not have deterministic results, which I consider a strict liability in this kind of thing) if you want). And then I ask it a question and it provides an answer I can immediately see is structurally wrong. So I say, there is an obvious problem with this code, what is it? And it thinks for a second and tells me what’s wrong with it. At which point I ask how it managed to provide an obviously incorrect answer when I told it to do all the static analysis it could before suggesting an answer. And it says sorry, but it looked at the class interface and read the comments and thought that was good enough.

Like. If I had hired a junior programmer who constantly did that kind of thing I’d fire them.

I mean there are also very cool things Claude can do. Like you point them to tens of thousand lines of code and then you can get very detailed and as near as I can tell reliable information about the call graph, for example. And that’s cool! Doing that “manually” takes me at least many minutes and sometimes hours for large codebases, and Claude can do it in seconds.

But even the “easy” things have to be constantly checked. If you actually, you know, care about the quality of the code it produces.

And it seems to be even worse at “big” things that involve re-scoping or simultaneously tweaking multiple independent structural elements of the code. Like I asked a question about modifying the TADS3 parser that requires tweaking both very early and very late things that happen in the process. And it very confidently proposed the naive solution (a single tweak that doesn’t work at all, because the TADS3 documentation is somewhat misleading and it relied on its understanding of the documentation instead of what the code actually does). After correcting this, it created a different, more subtle false solution which worked for the specific test case it had devised for it but which failed for the general specification (which, again, it was capable of detecting when prompted). At which point it recognized that it was only coding to solve part of the problem, scrapped its earlier work, and came up with a “solution” that only fixed the other half of the problem. Continued prompting chases Claude in a loop, where it keeps proposing solutions that fix individual parts of the problem without figuring out that it needs to implement several different tweaks in several different places. Eventually circling around to propose a slightly differently-structured version of the original solution. Which, again, it was capable of recognizing when prompted.

To paraphrase Douglas Adams, my experience with AI code generation is that it’s easy to be blinded to the poor quality of their code by the novelty of getting them to produce working code at all.

4 Likes

This is not my experience at all. I guess anyone can look at the Sharpee codebase on GitHub and find flaws. I’d love that criticism.

But my experience with Typescript has been very high quality output.

Updated: Even though I read the code all the time, I just went through critical parts of Sharpee. Honestly the Typescript is beautiful. It is absolutely no worse than a senior dev would write.

2 Likes

Right, and I assume that that’s because the Typescript corpus is large compared to the TADS3 corpus. And, more specifically, that because the TADS3 corpus is so small I’ve had to instruct Claude to restrict its considerations to, as much as it is possible to do so, only reasoning about the adv3 library itself.

Which means it is, or at least within my limited capacity to test the system it appears to be, a bound on the system’s ability to do “pure reasoning” about code. As opposed to cribbing off stackexchange or, in the case of TADS3 questions, intfiction.

In slightly different terms, the system appears to be much better at answering questions have been answered before, probably frequently. And it appears to be much worse at answering novel (relative to the corpus) questions.

Like what we’re seeing is a junior programmer trying to Chinese Room his way to employability by frantically looking things up on stackexchange. They’re good at it, so if someone has answered the question before on stackexchange and the answer was correct and maps well to the current problem then they do well. If the problem isn’t something you can just look up, if it actually requires a novel solution or complex reasoning about the structure of the system, it does very poorly.

3 Likes

Recent versions of GPT (5.2, 5.2-Codex, 5.3-Codex) seem pretty competent at writing ZIL, which I imagine has an even smaller corpus. Keeping brackets balanced while editing still trips it up, but its code is fine with a bit of cleanup.

1 Like

Maybe you are doing something exotic. When it comes to Rust and JS Claude is godlike.
It’s so good, that our engineers openly say that they sometimes wonder who is leading who. The interactions can be quite surreal. Yes, at times very disappointing, but never when code printing. 80% of the effort goes into the design, specifications and tooling for verification.

If you use it like a chatbot session, it will be an absolute car crash. That I agree.

1 Like

I’m not sure I’d make the same assumption. I’ve seen multiple threads about people doing things with ZIL via GPT and can recall zero involving TADS3. I might be misremembering, though.

But my question would be: what kind of questions and how would you know how well represented they are in the corpus?

Actually, thinking about this makes me wonder about presenting Claude (or the equivalent) with a constructed language like MIXAL (although presumably not MIXAL itself, which is probably already known to it).

This makes perfect sense and a core reason I switched to Typescript and why my spec-dev paradigm cannot work on a legacy IF platform.

1 Like

I think that was just one guy, and I don’t know how well it ever actually worked.

For example, I’ve used it to add features to ZILF’s parser, most recently support for referring to quantities of identical objects. Only a couple other IF parsers have ever been written in ZIL, and they’re structured fairly differently and don’t have these features; Inform’s does, but its structure is even more different.

GPT 5.2 today does a dramatically better job of writing ZIL than, say, Claude Sonnet 4 did three months ago.

I wrote ScribeScript for ChronicleHub, and tested that with both Claude, Google Gemini and ChatGPT. Surprisingly, each does an admirable job and get most of it right. It mostly trips up when little context is given from examples of usage, and it’s asked for something like a conditional statement, which in ScribeScript is formatted as { condition : option 1 | fallback option }, it will instead use something resembling classic ternary statements, with quotes for strings (something I explicitly omitted from the language) { condition ? 'option 1' : 'option 2' }

For parser games, I don’t think you can author them without thinking about things in a spec fashion. I feel like that’s just the methodology required. Just for fun, I was roughing out my “dream” parser language recently and it just became a minimalist code structure that read like a spec. I then fed my custom room draft (populated with items and interactions) into Copilot and asked it to compare it to other IF engines. It suggested Dialog was similar in structure and rewrote my spec in Dialog’s syntax. I kind of feel that coding directly in Dialog would be about the same amount of work as feeding an AI a strict spec.

Some of the push back you’re getting is likely from the feeling that, while you may be providing a new tool to replace the hammer, it still requires you swing it at nail while gripping tightly. I just can’t envision the magnitude of what you’re proposing… yet. Hopefully, you keep at it because if I relax my skepticism a little bit, then “there is no spoon” and this scares my caveman brain! :wink:


Edit:

I am a human LLM but bored of doing the small bits.

I subscribe to the “we are pattern recognition machines” too, but “with a much slower CPU”. However, our meatbag algorithms are still better though… for now. :wink:

@DavidC Curious, do you have a sample spec you wrote with a Dialog equivalent, for comparison? Maybe if we see the difference side-by-side, we’ll appreciate what you’re proposing easier. Perspective is everything.

1 Like

I’m really in the middle of it, but my beliefs are grounded in how Textfyre produced its two and nearly a third game.

I’ve drawn a GenAI line that does make it more challenging though. I could easily leave puzzle mechanics to a broader collaboration, but then I would not be able to say GenAI was not used for user-facing logic.

It’s clear I need to get through one or two games to see how it all plays out.

1 Like

It’s clear I need to get through one or two games to see how it all plays out.

I don’t know if you need to make a full fledged game; just a simple 3-room scenario with a couple of puzzles and a few objects to muck around with. (No plot required, but the addition of an NPC would be pretty cool.) Essentially, something digestible for potential authors, and with the source spec you wrote to have the AI generate it. The quality of our feedback is dependent on the information you provide.

As a side note, I applaud your commitment to not using AI for the content itself. I’m not anti-AI, but I want to read stories and play games that humans write. Warts and all.

3 Likes

Challenge accepted. I’ll do a test run and then a screencast of a second run. I’ll do the madlib part on ifMUD and report back.

3 Likes

I wonder if we can build a standard for repos and packages so GenAI can follow working patterns:

Doesn’t this already exist as AGENTS.md?

That’s for a repo and if you’re using Claude Code, it already uses CLAUDE.md.

My proposal is the user of a package and GenAI wouldn’t have to burn tokens on the LM researching the API of the package and how to use it properly. This would allow the package manager to provide concise guidance on the API and proper use as well as anti-patterns to avoid.

I’ve converted some of Jack Toresal and The Secret Letter to the template format (really just markdown to a different markdown). The key is that once Claude knows the template, I can funnel my ideas to it, ask it what text is missing and prompt me for things. It’s not just writing a spec. It’s that Claude asks as an assistant doing dictation as well as code-monkey. Claude will see what’s been designed and ask questions about missing mechanics as well as options for implementation. Then it writes the code and unit tests (something it’s actually very good at). The port of Dungeon would have taken twice as long or more without Claude’s testing abilities.

So imagine you’re play-testing your story and you see a gap. That will still happen, but it maybe less with Claude. A lot less.

“Dave, you forgot to provide dialogue for Jack in the armory where swords are concerned. Do you want to leave it as a TODO or give me the dialogue now?”

I can prompt: create a TODO list from all of the current code and categorize them

And Claude will give me a new markdown document with a list of content I need to write. I can fill in the document, tell Claude to integrate it, and the work is complete.

Here’s one attached…

chapter-01.md (43.8 KB)

1 Like

Yeah, I think what I’m taking away here is I’m exactly the opposite of the target audience here. When you’re writing things like:

Apple

Action Condition Response
EAT / TASTE Mercenaries in marketplace No time for breakfast right now!
EAT / TASTE After opening scene Crisp and delicious!

I would much rather cut out the middleman and write:

Instead of eating the apple when the mercenaries are in the Marketplace:
    say "No time for breakfast right now!"
Instead of tasting the apple: try eating the apple.
Report eating the apple: say "Crisp and delicious!"; rule succeeds.

Or:

#apple
(prevent [eat *])
    (#mercenaries is in room #market)
    No time for breakfast right now!
(instead of [taste *]) (try [eat *])
(narrate eating *) Crisp and delicious!

To me, that’s the same amount of work, and means I don’t have to recheck the LLM’s output afterward.

4 Likes

And this is the kind of translation I’ve become bored with OR in the case of Inform 7, I just can’t consistently get a feel for what syntax I should use for what things.

A story from my consulting past. Back in 2001 I was building a website with XML→XSLT. It was a site that needed the stakeholders to write living documents and at that time, this was a cool trick. But I could not wrap my brain around the XSLT declarative logic AT ALL. So I punted it to a junior dev and he ate it up. He instantly understood the assignment and had it all working in a day. By the way his name was Erle and I use this story all the time. “Y’all are a bunch of Erles!”

It’s not really about declarative. It’s just some paradigms break my brain and XSLT and Inform 7 are two of them. Of course this was never the core aspiration for Sharpee. It was originally supposed to be a C# system with fluent constructs that I would write myself.

world.addRoom(...).addRoom(...).Build;

That kind of thing.

But then ChatGPT and Claude arrived and gradually my interests shifted. First the switch to Typescript and then the epiphany that because I made that switch, Claude could generate Sharpee code at a high level. Then I just had to figure out a reasonable path of balancing GenAI and being creative, which ended up aligning with how the IFComp polling came out on GenAI usage. Something I was very surprised about.

I know many authors feel very comfortable with Inform 7, TADS, and now Dialog.

I do not and as I’ve said, I don’t really care to write code anymore.

That spec is compact, yet still describes intent clearly. It’s pretty close to writing Dialog directly, but I can’t really fault it. Nice spec format, man! :slight_smile:

I appreciate your time and effort because I learned a couple of things about describing IF worlds more concisely. Like, how you just listed boolean traits in one line was clever.

Thanks for sharing that.

1 Like