From .app-state to .project-state: The DNA Was There All Along
#scsiwyg#devlog#building-in-public#reflection#blog-state#app-state#project-state
David OlssonFour articles ago I wrote about .blog-state/ — a small directory of structured files that lived inside the scsiwyg repo and tracked which posts we'd shipped, which were queued, and which topics were still open. The point was narrow: stop forgetting what we'd already written about.
Then it became .app-state/ — twelve sections, five presets, a directory that gave the AI in your IDE something to read before it started.
Then it became .app-state/ v2 — 22 commands, 6 scheduled agents, signal harvesting from Slack and Gmail and Notion, the directory that maintained itself.
This week it became .project-state/. And the shape it's taken is one I didn't see coming, even though, looking back, the DNA was there in the very first commit.
The pattern that kept reappearing
Every iteration solved a problem the previous one had surfaced.
.blog-state/ worked, and then the agent kept asking questions the directory couldn't answer. What's the schema? Who are the competitors? Why was this built this way? So we widened the directory.
.app-state/ worked, and then we noticed nobody was keeping it current. A system that depends on human discipline to stay current eventually isn't current. So we put agents on it.
.app-state/ v2 worked, and then we tried to use it on a project where the stakes were different. Not "we shipped a feature, should we blog about it" — something more like "this milestone is due to a funding partner, three organizations are co-signed on the deliverable, and there's an audit trail that has to survive the project itself."
The directory shape held. What didn't hold was the assumption that the only people reading it were us and the agent. Real projects have stakeholders. Stakeholders have meetings. Meetings have minutes. Decisions have to be traceable. Documents have to be classified. Phases have to be gated.
.app-state/ was built for a single developer working on a single product, watching the world for signal. .project-state/ is what that directory becomes when the project has a governance layer around it.
What stayed exactly the same
The first surprise, when I sat down to design the new shape, was how much I didn't have to redesign.
The directory still lives in the repo. It's still flat files — JSON for machine-readable data, markdown for human-readable context, JSONL for append-only logs. It's still version-controlled. Skills still read it before they act, and write back to it when they're done. The activity log is still the source of truth. The state file is still the brain.
The decision engine that scored stories in .blog-state/ ("not every change is a story") became the same decision engine that flags milestones at risk, classifies signals, and decides whether a publication is consortium-ready. Different inputs, same logic: significance × novelty × timeliness × audience-fit, scored, thresholded, surfaced.
The split between agent observes and surfaces, human decides, agent executes the mechanical parts — the line we drew at the start with blog drafts — is the same line that runs through the project version. The agent prepares a quarterly claim draft. A human reviews it. The agent never submits.
This is the part that felt like discovering DNA rather than designing it. We didn't pick that division of labor for .project-state/. It was already there, in the first skill we ever shipped, because it was the only division that ever made sense.
What had to grow
The things that did need to grow were the things that come with stakes.
Lifecycle phases. A blog doesn't have phases. A product can pretend it doesn't. A project absolutely does. There's a moment where the work doesn't formally exist yet, and a later moment where it's been approved and is being planned, and a moment after that where it's executing, and a moment after that where it's closing out and going into the archive. Each of those is a different mode of operation. The skills that should run in one phase shouldn't run in another. Documents that matter at the start are background by the end. So .project-state/ got an explicit lifecycle, and a gate between each phase, with checklists that block transition until the right artifacts exist.
People as first-class entities. .app-state/ knew about "the team" in a vague way. .project-state/ knows about every named person, what role they play, which organization they represent, what they're authorized to sign, who they report to. That's not bureaucracy for its own sake — it's because the agent needs to know who to draft an email to, who to invite to a meeting, who needs to acknowledge a change. Anonymous "the team" can't do that.
Documents as a managed surface. Every project I've ever been on accumulates documents — proposals, agreements, signed forms, meeting minutes, public-facing material — and they all sit in some folder somebody else organized. .project-state/ makes the document layer explicit. Everything has a canonical path. Everything gets classified on arrival. Everything has a status (draft, signed, superseded, archived). Skills that need a document ask the document curator for it by canonical name; they don't go hunting.
An orchestrator that thinks in calendars. .app-state/ had three timing layers — daily, weekly, triggered. .project-state/ has those, plus quarterly, plus phase-bound, plus deadline-anchored. The orchestrator's first question every morning isn't "what changed in the repo" — it's "what does the calendar say is coming, and what do we need to start drafting now to make that deadline."
Change as a versioned object. A blog post can be edited in place. An app's roadmap can pivot. A project can change direction too — but when it does, the change is a thing. It has classification, approval, an audit trail. .project-state/ got a change register because the same impulse that wanted us to know "what changed since last post" now needs to know "what changed in the scope, who approved it, and where's the document."
What the DNA actually was
Five articles in, I think the through-line is finally clear enough to name.
The thing we kept building, in different shapes and at different scales, was always the same: a structured intelligence layer that lives next to the work, stays current on its own, and gives any agent or human who arrives a complete answer to "what is this and what's happening with it right now."
For a blog, the answer is: here's what's been published, queued, covered, open. For an app, the answer is: here's what it is, what it does, what's moving in the market around it, what users are saying, what changed in the code this week. For a project, the answer is: here's the lifecycle phase, here's the consortium, here's the document of record for every commitment, here's what's due, here's what's at risk.
Same question. Same shape of answer. Wider scope each time, deeper governance each time, but the same fundamental move: don't make people remember the context — make the directory remember it instead.
The other piece of DNA that turned out to matter more than I expected: flat files. Not a database. Not a SaaS dashboard. Not a graph store. Markdown and JSON, sitting in a folder you can grep, version with git, hand to another developer, walk away from for six months and come back to without losing anything. Every time we considered swapping the substrate for something more sophisticated, the answer came back the same — the substrate is why it works. Tools come and go. The directory is the thing that survives.
And then the third piece, which I didn't even recognize as DNA until .project-state/ made it visible: the system tells you what to do next. Not "here's a dashboard, look at it." Not "here's a wiki, browse it." The output is always a specific action. Run this command. Draft this email. Review this document. The blog version surfaces "this would be a good post." The app version surfaces "your top user request has no matching planned feature." The project version surfaces "claim due in 14 days, here's the draft, please review." The interface is verbs, not nouns.
That's the shape. That was the shape from the beginning. We just kept finding new places it fit.
What .project-state/ runs on now
Without going deep into the regulatory machinery — that's a separate post — the system is operating across roughly twenty skills, organized into the same architecture we've used since .blog-state/:
- A state skill that reads and writes the directory, with everything else routed through it.
- An orchestrator that decides what to do next based on the calendar and the current state.
- A document curator that classifies, indexes, and promotes anything that lands in the project.
- A phase-gate skill that enforces lifecycle transitions.
- Specialist skills for each of the recurring rhythms — milestone tracking, status reporting, claim preparation, steering committee meetings, change management, IP tracking, publication review, lessons learned, closeout, onboarding.
- A notifier that routes everything that needs to leave the directory to the right surface — Slack, Gmail draft, calendar event, blog.
Read that list and notice something: it's the same architecture as .app-state/ v2. State, orchestrator, specialists, notifier. The skills are different because the work is different. The shape is identical because the shape was always going to work.
What this means for the platform
scsiwyg started as a blogging platform for developers who publish from their IDE. Then it became the publishing surface for .app-state/ and the projects that lived there. Now it's the publishing surface for .project-state/ too — including projects where the publication review itself is part of the governance, where what gets posted to which blog at what time is itself a state-machine question.
The blog is no longer the system. The blog is one channel of the system. That's a shift I didn't expect when we shipped the original "headless is the head" post a few weeks ago, but it's the right one. The directory is the thing. The blog is what some of the directory's contents become when they're ready to be public.
Which means the loop closes back on itself: .blog-state/ was a directory that tracked what we'd published. .project-state/ is a system that knows what it's allowed to publish, when, after which review. The earliest version of the idea is now just one mode of the latest version.
What's next
A few directions I'm watching:
Cross-project intelligence. When you run more than one .project-state/, the orchestrator should roll up across all of them — one morning briefing, one set of priorities, one ranked list of where the day's attention should go. We sketched this back at .app-state/ v2 and didn't build it. With multiple live projects in flight now, it's the next obvious step.
The shared substrate. .blog-state/, .app-state/, .project-state/ — they're all the same shape, but each one was implemented as if it were the first of its kind. There's a smaller, shared core in there that wants to be extracted: the state engine, the activity log, the orchestration loop, the notifier routing. I haven't done that extraction yet because every time I've tried, the next variant has taught me something about the core that wasn't obvious from the previous one. At some point that learning curve flattens and the extraction is worth doing. Probably soon.
Closing the loop on closeout. The hardest part of any project is the end. Most projects don't end well — they trail off, lose people, leave artifacts in inboxes, never get formally archived. .project-state/ has a closeout phase and an archive skill, and the early signs are good, but I won't really know whether the system holds up until I've taken a project all the way through it. That's a story for later in the year.
The shape of the thing, again
I keep ending these posts with some version of the same observation, which I think is itself part of the DNA: the shape was right; we just hadn't pushed it far enough.
.blog-state/ was right. We just hadn't pushed it past blogging.
.app-state/ was right. We just hadn't pushed it past a single product.
.app-state/ v2 was right. We just hadn't pushed it into a domain where the stakes made governance non-optional.
.project-state/ is, I think, right too. And I'm already noticing places it isn't far enough yet.
That's the work. That's the whole pattern, five articles in. A shape that fits the current problem, used until it surfaces a problem it doesn't fit, then widened until it does — without throwing away what already worked. The DNA isn't in any one of the directories. The DNA is in the way they keep growing into each other.