THOUGHTS

How I Think About Solo Projects in the Age of Coding Agents

How I run my solo projects with Claude Code: an Obsidian vault for the thinking, Linear for the doing, and a pre-commit gate that keeps them apart.

On my solo projects I use Claude for almost everything: the research, the specs, the architecture, the code and the tests. So what I spend my time on is how it gets the right context at every step, and how that context stays correct as the project grows.

I see engineers, very technical ones, fighting with Claude Code because it doesn’t do what they asked. Most of the time they’re just prompting it and not giving it enough context: not about the project, and not about what they actually want. The moment they hand it a spec, break it into tasks and run each task in a fresh context window, the results change. They save a lot of tokens too.

This is the setup I built for my own projects to work that way all the time.

Two owners

A project has two kinds of information.

The first is what and why: the idea, the research, the product definition, and the decisions made along the way. That stays true for a long time.

The second is how and when: tasks, status, what’s next, what’s blocked. That changes every day.

So they live in different places. An Obsidian vault owns everything before the work starts and everything worth keeping after it ships. A tracker owns the work itself. For my own projects that’s Linear, but any tracker works. The design doesn’t care which.

The rule between them is one line:

The vault stores the tracker’s link, never its status.

A link can’t go stale. A status always does.

The same thinking goes one level down. Every project gets its own folder with a fixed set of files, so the planning of one project never leaks into the next one.

The vault itself started from Andrej Karpathy’s LLM Wiki idea1: you drop raw material in, the model keeps a wiki on top of it, and later you ask the wiki instead of researching the same thing again.

The loop

My setup has three touchpoints. The project repo is in the center, with its AGENTS.md and project skills. The vault holds how I work: every decision on every project, plus the repos, articles and videos I liked. Linear holds the tasks and their state.

Claude Code sits in the middle. It takes a task from Linear, works on it in the repo, saves decisions and useful notes in the vault, updates Linear when it’s done, and then the loop continues.

PROJECT REPO · CLAUDE CODE CONTEXT PRD · PLAN ISSUES NEXT TASK HOOK · DECISIONS HOOK · STATUS // VAULT WIKI patterns · decisions // TRACKER LINEAR issues · how & when // 01 PLANNING idea · research · prd // 02 EXECUTING worktree · code · commits // 03 VERIFYING tests · definition of done // 04 OPEN PR review · merge AUTOMATIC · CLAUDE CODE HOOK

This way, when someone asks me why I did X on a project two months ago, I can query the wiki and answer that question.

Who writes what

Inside the vault, the folder tells you who is allowed to write a file:

raw/          I write, the agent only reads
              articles · tweets · repos · books · videos · ideas

wiki/         the agent writes
              sources/ · patterns/ · decisions/ · technologies/
              domains/ · ideas/ · journal/ · index.md · topics.md · log.md

projects/     both write, but only into allowed files
              <slug>.md          the project page: what it is + tracker URL
              <slug>/
                00-idea.md       ME      the spark
                01-research.md   AGENT   landscape + an honest verdict
                02-prd.md        BOTH    what + why, never how/when
                03-plan.md       BOTH    only while there's no tracker
                features/        BOTH    only while there's no tracker
                spine.md         AGENT   only once a tracker is set
                shipped/         AGENT   records of built work
                notes/ assets/

I write raw/. The agent writes wiki/. The agent and I both write projects/, but only into the files that are allowed to exist there. Nothing else goes in a project folder: no status file, no kanban, no project inside a project.

wiki/index.md is the entry point, one line per page. An agent reads the index, picks the pages that matter, and opens only those. Nothing reads the whole vault.

The steps, named by what they produce

I don’t have a fancy name for the process. Each step produces a file, and the next step builds on that file:

StepWhat it producesWho writes itWhat I use
Idea00-idea.mdme/grill-me if the idea needs pushing on
Research01-research.mdagent/idea-deep-research
PRD02-prd.mdboth/grill-me, or plan mode (Shift+Tab) for small features
Plan03-plan.md + features/bothplan mode, /grill-with-docs, or superpowers brainstorming
TicketsLinear issues + spine.mdagent/to-tickets
Buildcode in the repoagentone ticket, one fresh session, mostly in a worktree
Verdicttests against acceptance criteriaboththe spec
Shippedshipped/<feature>.md + decisions, patternsagentthe PR hook and an end-of-day task

Three of these steps need more than one line.

The PRD holds what and why, never how or when. The gate (more on it below) rejects a checkbox, a ## Phase heading or a date anywhere in 02-prd.md. Left alone, a PRD slowly grows a plan inside it, and then I have two plans that disagree.

Tickets are sized to fit one fresh context window. That’s the sizing rule. Effort has nothing to do with it. /to-tickets2 slices the plan into thin end-to-end pieces, each one demoable on its own, with the blocking order between them. Because every ticket fits in one session, the build step stays simple: one ticket, one fresh Claude Code session, mostly in its own git worktree. The session doesn’t have to compact halfway, so the model still has the spec and its own reasoning in view when it finishes.

When the plan turns into tickets, the plan files leave the vault in the same commit. Linear has them now. The one thing a tracker loses is the build order, so spine.md keeps it: the stages in order, whether each one works end to end, and which ticket owns each gap. Every line in it has to be checkable by reading or running the code. “7 of 8 detectors have a rule” is a grep. “Mostly done, I think” doesn’t get in.

The verdict asks two questions. Did the feature do what the plan said? And where was the spec weak? The second one is where I learn something. A lot of the time the code is fine and the spec was missing a line. There’s an example of exactly that further down.

The gate

A rule written in a file the agent reads is a suggestion. A check that fails the commit is a rule.

Every structural rule has a check behind it. One dependency-free Python script, vault-check.py, runs as a pre-commit hook and fails the commit when:

CheckFails when
whitelista file under projects/<slug>/ isn’t one of the allowed files
plan-gateplan files still exist after the project got a tracker
spine-gatespine.md exists while there’s no tracker
prd-purity02-prd.md has a checkbox, a ## Phase heading or a bare date
tracker-stateBlocked on:, Next up:, Working on: or Phase: shows up in projects/
shipped-statusa file in shipped/ is missing status: shipped
broken-links · orphansa wikilink points at nothing, or a page has no links into it

git commit --no-verify is the escape hatch, on purpose. If a check is wrong, I fix the rule it encodes.

Yes, I wrote a linter for my own notes. My notes have never been this well behaved.

The PR hook

The best moment to write down a decision is right after the work, while it’s all still in the model’s context. Ten minutes later that context is gone, and so is the cheap version of the note.

Opening a pull request fires a Claude Code hook. It’s a PostToolUse hook on Bash3, set in ~/.claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/vault-sync-reminder.sh" }
        ]
      }
    ]
  }
}

The script only reacts to gh pr create, pulls the Linear issue ID out of the branch name, and hands Claude an offer instead of doing anything itself:

# trimmed: the full version also skips PRs opened in the vault repo itself
input=$(cat)
cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // ""')
printf '%s' "$cmd" | grep -qE '\bgh[[:space:]]+pr[[:space:]]+create\b' || exit 0

cwd=$(printf '%s' "$input" | jq -r '.cwd // ""')
branch=$(git -C "$cwd" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
issue=$(printf '%s' "$branch" | grep -oE '[A-Z][A-Z0-9]+-[0-9]+' | head -1 || true)

ctx="A PR was just opened. OFFER (do not auto-edit) to capture decisions in
wiki/decisions/ and reusable patterns in wiki/patterns/. If there are neither,
write nothing. Offer to move ${issue:-the issue} in Linear."

jq -cn --arg ctx "$ctx" \
  '{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:$ctx}}'

It writes nothing. If the work produced neither a decision nor a pattern, the right answer is to write nothing, and the offer says so.

The template also ships an end-of-day task that goes over the day’s Claude sessions and writes what was learned into the journal, patterns, decisions and shipped records. It never writes status.

An example: one feature through the loop

RepoLens is an example project I use to show the flow. It turns a codebase into onboarding docs: architecture notes, feature summaries, and explanations grounded in the source.

The PRD sketch:

Users            : eng teams onboarding new developers
Core workflow    : ingest repo → detect structure → generate → review
Scope            : single-repo TypeScript / Python projects
Non-goals        : monorepos, real-time updates, auto-PRs to docs
Constraints      : local-first; no source leaves the machine
Success criteria : a new hire answers "where does X live?" unaided

No dates, no phases, no checkboxes. The gate would reject the file otherwise.

The plan for the first feature, the architecture summarizer, becomes four tickets. Each one is demoable alone and sized for one session:

1  Repo tree walk + entry-point detection     blocks: -
2  LLM pass: draft sections with citations    blocks: 1
3  Verdict file + reviewer marks              blocks: 2
4  Diff-aware re-run                          blocks: 3

Then the verdict against the acceptance criteria:

AC1 (runs offline)          : pass
AC2 (real file paths)       : pass
AC3 (reviewer marks)        : pass
AC4 (diff-aware re-run)     : FAIL on rename - a file move resets
                              every verdict attached to it

The rename failure is a missing line in the spec. Nobody said what should happen to a reviewer’s verdict when a file moves. So the fix starts in the spec: AC5, rename detection carries old verdicts forward. The choice behind it, keying verdicts by symbol instead of line range, goes into wiki/decisions/.

The next project starts with that decision page already there.

Setting it up

The vault is a template you clone4:

git clone https://github.com/YoniRaviv/dev-llm-wiki.git my-wiki && cd my-wiki
.scripts/install-hooks.sh            # the pre-commit gate
.scripts/install-global-skills.sh    # skills → ~/.claude/skills
python3 .scripts/vault-check.py      # expect: vault-check: ✓ clean

Then open the folder twice: once as a vault in Obsidian, once in Claude Code. Both read and write the same markdown, so there’s nothing to integrate.

The slash commands come from my plugin marketplace5:

/plugin marketplace add YoniRaviv/claude-skills
/plugin install yoni-skills@yoni-marketplace     # idea-deep-research, standup, meeting-prep
/plugin install superpowers@yoni-marketplace     # Jesse Vincent's superpowers

/grill-me, /grill-with-docs and /to-tickets are Matt Pocock’s skills2. Superpowers is Jesse Vincent’s6; my marketplace just points to it.

Skills install globally, not inside the vault. The same skill copied into two vaults drifts, so the gate fails if it finds a .claude/skills/ folder in the vault.

So the files are what I really work on. Claude works from them, and so do I.

Footnotes

  1. Andrej Karpathy, LLM Wiki (gist). https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f ↩

  2. Matt Pocock, skills: to-tickets, grill-me, grill-with-docs. https://github.com/mattpocock/skills ↩ ↩2

  3. Claude Code, Hooks reference. https://code.claude.com/docs/en/hooks ↩

  4. dev-llm-wiki, the vault template this runs on. https://github.com/YoniRaviv/dev-llm-wiki ↩

  5. claude-skills, my slash commands and plugin marketplace. https://github.com/YoniRaviv/claude-skills ↩

  6. Jesse Vincent, superpowers. https://github.com/obra/superpowers ↩