Back Send feedback to ilkka.kuivanen@me.com

The diff is the deliverable – part 2

Four months ago I wrote about a workflow I called "Slice". The idea was to put the context in one file and treat the file edits as the valuable part of non-code AI output, while keeping the whole thing disposable enough to survive a change of tools. After using it everyday since creation and making over 50 iterations to the template I decided to gather up the learnings and update what I think about AI assisted development, especially how can the workflow be structured around the docs.

The single-file idea held up surprisingly well, especially for small to mid-sized tasks. For larger tasks, two questions kept coming up: how things work now, and if a change was to be made, how should it work then?

Teleology of documentation

Before I move on, I want to touch briefly on the purpose of documentation. Various ways to approach the problem of organizing documentation exists. While these are not the only ways to structure our thinking, here are some:

All considered, they point to a simple practical lesson: each kind of documentation needs a clear role, so we know where to invest effort and which parts are disposable.

Let's use Diátaxis as a basis for the framing, as it offers nice framework to structure thinking around.

Layer 1: what documentation is for

Docs exist to close a gap between a human and a system, and the gap has two dimensions:

Good docs carry what the reader can't get by using or reading the system itself. A tutorial carries the path a beginner can't see. Explanation carries the "why" that code never states. If the info is trivially derivable from the running system, the doc is redundant and will rot. Even in the age of AI, it is still expensive to keep updated in form of extra tokens and review time.

Layer 2: what AI generates (most of it isn't documentation)

AI (or AI spec framework) can be considered to produce three kinds of non-code output. Only one is real documentation (in Diátaxis sense).

The confusion is easy to make: all three are markdown in a repo, so they read as "docs" and might be located everywhere in the repo.

They are not equal though: process state and machine input cannot be mapped to Diátaxis.

Layer 3: distill noise out of docs

Here are the gates for any AI generated artifact:

Only the artifacts that passes these four gates can be placed in /docs.

What about crafting docs for the AI?

A case exists to optimise text for the AI. Some say AI will soon be the main consumer of the docs we have. Even if that were the case, I still think optimising for what the AI likes is a bad goal. First, absolutely nothing enforces or ensures AI focused content is consumed by the AI (think e.g. llms.txt). Second, because you can't actually know what an AI "likes". You assess quality the same way you would with a human. Ask another person if she gets your docs. That's all there is to it. It's about peer-reviewing and getting comments and figuring out what works for your audience. With AI it is not as straightforward, rather opposite:

Thesis

AI may generate state as working memory (throwaway), and documentation only where the info is non-derivable and human-owned. The purpose of a doc is the thing that decides if it earns its place: it has to carry knowledge you can't get from the running system, and a human has to own keeping it true. Everything that fails that is either state, config, or rot.

Updating my approach

Getting back to practical needs while developing:

For the question 1: create docs/[topic+scope].md. The AS-IS, written from reading real code, not from memory of what was intended. This exists to support approaching complex cases with AI where understanding of things needs to be built gradually, mostly for human to make sense out of the complexity without blindly trusting the AI's internal model of understanding.

For the question 2: work/[topic+scope].md. This is what should exist. The TO-BE. This is the contract for implementation.

Notice the distinction: only docs, "as-is", are considered meaningful in Diátaxis sense. The work is mostly throwaway and matters only a brief period of time.

Finally, the last pre-defined format: work/tasklist-[name].md. A tasklist is generated from one design or system doc, breaking the work into small, independently committable steps. Each task names its files and its acceptance criteria up front, so a task list can be handed off, paused, or picked up by someone else without figuring out intent from a chat log. It is also a throwaway right after the end of its lifecycle, like everything else in work. Tasklist is useful for several reasons: it saves context window and it can be used as a practical way to capture intent from long sessions.

The prompts are available at https://github.com/ilkkakuivanen/cito-prompts.

Re-arrange the modes and outputs

Original app had three narrow agents: create, think, do. What replaced it has four core modes, each reading exactly one file before acting and touching exactly one kind of artifact:

The instruction is explicit about picking a mode rather than assuming one: read the mode file before acting, and if it is unclear which mode applies, ask. That sounds like a small thing, but it removes a failure I kept facing with Slice, where a session would quietly drift from thinking into doing without either of us deciding that on purpose. Language model workflows tend to work best with modes that are clear, rather than all over the place. It keeps the focus on the topic.

Not everything deserves a doc

The heaviest thing I noticed myself doing wrong with Slice was routing every change, including a one-line typo fix, through the same ceremony. That is pure overhead, and it directly fights the "use quota sparingly" goal I raised as unresolved last time.

The update adds a second, lighter track: grunt tasks. cito-typo, cito-comment, cito-add-test and cito-setup-project. These skip the main workflow for convenience. No design doc needed, no system doc update needed. In exchange, each grunt agent carries a tight boundary. The typo agent is not allowed to touch logic, identifiers, or formatting. The comment agent is only allowed to add a WHY, never restate the WHAT, and has to leave code uncommented if it cannot verify the reason. The add-test agent has to find the repo's existing test convention and stop and ask if none exists, rather than inventing one.

Matching the mode to the type and size of the change turned out to matter more than optimizing any single mode.

Retrospective

Original Slice was something I applied inside a project. The new version ships its own optional project setup. A cito-setup-project mode reads the repo's manifests, config, and source layout, then fills in a project instructions file from that evidence. The needed coupling and wiring is somewhat of a downgrade from the portable approach I wrote down last time. But then again these agents/commands work without setting up the instructions specific way.

I renamed Slice to Cito. A single file was the whole idea behind that name, and that stopped being true once the once the design and system docs as well as tasks were separated. What is here now is called Cito. Cito is a name I've used in other scaffolding projects as well, e.g. https://www.ilkkakuivanen.com/cito/

Looking back at the list of open problems from the first post:

The experimentation, learning and iterations continues. Let's see in 4 months.