March 26, 2026
From .plans/ to Gitea: How Our Work Tracking Evolved
A solo developer and an AI agent tracked 760 markdown files in 77 days before switching to self-hosted Gitea with MCP integration. What worked, what broke, and why a filesystem isn't a project management system.
The Numbers
| Metric | .plans/ (Dec 26 – Mar 12) | Gitea (Mar 12 – present) |
|---|---|---|
| Total items | 760 files | 201 issues |
| Time span | 77 days | 14 days |
| Daily rate | ~10 plans/day | ~14 issues/day |
| Total size | 5.7 MB of markdown | Stored in PostgreSQL |
| Busiest day | 52 plans (Mar 12) | — |
| Active design docs | 5 in .plans/design/ | Referenced from issues |
March 12 was both the busiest plan day and the last one. 52 plans were triaged into Gitea issues that day, and no new plan files were created after.
What .plans/ Got Right
Low friction. Creating a plan was as fast as creating a file. No login, no UI, no fields to fill. Just write markdown and start working. For a solo developer moving fast, this was perfect — zero overhead between "I have an idea" and "I'm working on it."
Rich context. Plans could include architecture diagrams, code snippets, SQL queries, decision rationale, and implementation notes in a single document. No character limits, no form fields, no structured data requirements. The WebSocket diagnosis plan had a full ASCII diagram of the proxy chain. The initial project setup plan had the complete directory structure. This density of context in a single artifact is hard to replicate in a ticketing system.
Git history. Plans lived in the repo, so they were versioned, branched, and searchable with grep. You could trace the evolution of a design decision through git log. The plan was always in the same context window as the code it described.
Design documents worked well. The .plans/design/ directory held active
design docs like cascade-truthful-routing.md and git-substrate.md. These
multi-page documents with diagrams, decision records, and phased implementation
plans were genuinely useful and are hard to replicate in issue bodies. We still
use this pattern — design docs live in .plans/design/ and are referenced from
Gitea issues.
Where .plans/ Broke Down
760 files in 77 days. That's the fundamental problem. At 10 plans per day, the archive became a graveyard. Nobody goes back to read plan #347. There's no search, no filtering, no status, no assignment, no priority. It's a pile of markdown files sorted by date.
No lifecycle. A plan file has two states: exists and archived. There's no
"in progress", no "blocked", no "closed with comment." You can't tell if
2026-01-14_fix-websocket-timeout-add-polling.md was completed, abandoned, or
superseded by 2026-01-14_diagnose-websocket-fix-closeread.md without reading
both files and cross-referencing git history.
No cross-referencing. Plan files don't link to each other, to commits, or to deployments. Issue #63 in Gitea has 15 comments spanning investigation, design, implementation, verification, and closure — all threaded on one URL. The equivalent in .plans/ would be 5 separate files with no explicit connection.
No visibility. Plans are invisible to anyone not reading the filesystem.
There's no dashboard, no burndown, no way to answer "what are we working on
this week?" without ls -lt .plans/design/. Gitea issues have labels,
milestones, search, and a web UI.
No accountability. With no status tracking, there's no clear moment of
"this is done." Plans drift. The WebSocket timeout investigation spawned 6 plan
files over 2 days (validate-websocket-fix-remove-polling.md,
diagnose-websocket-fix-closeread.md, fix-websocket-timeout-add-polling.md,
etc.) — each one a snapshot of understanding at a point in time, but none
definitively marking the issue as resolved.
AI context pollution. With 760 files in the archive, any agent exploring the
codebase could stumble into stale plans and treat them as current. The
CLAUDE.md had to explicitly say ".plans/design/ should be empty — all design
docs have been triaged into Gitea issues and archived." This is a guardrail
against a problem that shouldn't exist.
What Gitea Fixed
Lifecycle. Issues have open/closed states, comments, labels, and milestones. Issue #63 (duplicate cascade entries) went through: bug report → investigation → reopened (closing was premature) → data corruption discovered → 3-step fix plan → implementation → verification → closure. That entire journey is one URL, readable top to bottom. In .plans/, it would have been 6 disconnected files.
Cross-referencing. Issues link to each other (#63 → #55 → #77 → #76), to commits, to design docs. Epic #149 tracked 8 sub-issues across 3 phases. Each sub-issue referenced its parent, its design doc, and its verification evidence. This web of references is the thing .plans/ fundamentally couldn't do.
Accountability. Closing an issue requires a comment with: what was done, test evidence, commit hash. The CLAUDE.md codifies this: "Do NOT close issues without verification." This forces a completion ritual that .plans/ never had.
Search and filtering. Labels (bug, feature, tech-debt, ux, provider), milestones (Story Quality, Story Forge Chat, Series Forge), and full-text search make it possible to answer "what provider bugs are open?" in one query. With .plans/, that question requires grep across 760 files.
Multi-agent visibility. Both the human and AI agents can read and write issues via the Gitea MCP server. The workbench validation of #158 was posted as an issue comment by the human, visible to the AI in the same conversation. Plans in .plans/ were only visible to whoever had the repo checked out.
What We Kept From .plans/
Design documents. Complex architectural work still gets a .plans/design/
document. The git substrate design doc (git-substrate.md) was 450 lines with
diagrams, phased implementation plans, and risk analysis. That level of detail
belongs in a document, not an issue body. But now the document is referenced
from the tracking issue, not floating independently.
The archive. Completed design docs move to .plans/archive/ with a
**Tracked:** link to their issue. The archive is reference material — the
issue is the live tracking point.
What Gitea Could Do Better
Rich content. Issue bodies support markdown, but they're not great for
multi-page design documents with complex diagrams. The hybrid approach (design
doc in .plans/design/, referenced from issue) works but adds a level of
indirection.
AI context window. Long issue threads with 15 comments can be expensive to load into an AI context. The MCP server returns everything — there's no "give me the summary" option. For issues like #63 with extensive investigation comments, this can consume significant context.
Offline access. Plans were always available — they're files in the repo. Issues require network access to the Gitea instance. For a self-hosted Gitea on the same network as the dev machine, this is rarely a problem, but it's a dependency that didn't exist before.
Technical Setup
The .plans/ System
No setup required. It's a directory convention:
.plans/
├── design/ # Active design documents (should be empty when triaged)
└── archive/ # Completed plans: YYYY-MM-DD_description.md
AI agents discover plans by reading the filesystem. CLAUDE.md tells them where to look and what the conventions are. The AGENTS.md file documents the plan-to-archive workflow: completion summary, implementation notes, commit reference.
The plans themselves are just markdown files — no schema, no frontmatter
requirements, no tooling. vim .plans/design/my-feature.md and you're working.
The Gitea System
Self-hosted Gitea instance running alongside the application infrastructure. The AI agent accesses it through an MCP (Model Context Protocol) server that exposes issue CRUD as tool calls:
Agent MCP Server Gitea
│ │ │
├── issue_write ────────>│ │
│ (create/update/ ├── REST API ────────>│
│ comment/close) │<── JSON ───────────│
│<── result ────────────│ │
│ │ │
├── issue_read ─────────>│ │
│ (get/comments/ ├── REST API ────────>│
│ labels) │<── JSON ───────────│
│<── result ────────────│ │
From the agent's perspective, creating an issue is a single tool call:
mcp__gitea__issue_write(
method: "create",
owner: "org",
repo: "project",
title: "Fix TTS payload limit",
body: "## Summary\n..."
)
Comments, label management, closing with verification — all the same pattern. The agent can read an issue, investigate the code, implement a fix, post test evidence as a comment, and close the issue in a single conversation turn.
Labels and milestones provide the structure that filenames couldn't:
| Label | Purpose |
|---|---|
| bug | Broken behavior |
| feature | New functionality |
| tech-debt | Cleanup, refactoring |
| Milestone | Scope |
|---|---|
| Story Quality | Detectors, calibration, rewrite pipeline |
| Story Forge Chat | Chat interview UX |
| Series Forge | Multi-installment stories |
The combination of MCP tool access + labels + milestones + cross-referenced comments gives AI agents the same project management capabilities that human developers get from a web UI — without leaving the terminal.
The Hybrid Pattern
Design documents that need diagrams, phased plans, or extensive rationale still
live in .plans/design/. But they're always linked from a Gitea issue:
# .plans/design/git-substrate.md
**Tracked:** [Epic #149](https://git.proseforge.ai/forge/proseforge/issues/149)
## 1. The Problem
...
The issue is the tracking point (open/closed, comments, progress). The design
doc is the reference material (architecture, diagrams, decision records). When
implementation completes, the design doc moves to .plans/archive/ with a
completion summary, and the issue closes with test evidence.
The Verdict
The .plans/ system was the right tool for a solo developer bootstrapping a project at speed. Zero friction, rich context, git-native. It worked for 77 days and 760 files.
It stopped working when the project needed lifecycle tracking, cross-referencing, and multi-agent collaboration. The transition happened naturally — March 12 was both the peak (52 plans) and the end. The volume itself was the signal that a filesystem isn't a project management system.
Gitea brought structure without killing velocity. Issues are fast to create (especially via MCP from the CLI), and the lifecycle/cross-referencing/search capabilities are worth the small overhead. The hybrid approach — issues for tracking, design docs for depth — captures the best of both.
The 760 plan files in the archive aren't wasted. They're a detailed record of how ProseForge was built, decision by decision, day by day. They're just not how we track work anymore.