Ziqi (Katrina) Ding

Back

The Problem#

Every new chat with an AI coding agent starts from zero. I re-explain the project structure, re-state the rules, and re-describe the plan we agreed on yesterday. When the context window fills up mid-task, the agent forgets the plan it wrote an hour ago and starts improvising.

Worse, the decisions made along the way (why we picked library A over B, what we tried that didn’t work) live only in a chat history nobody reads again.

The Solution#

Keep a dev-notes/ folder in the repo that the agent reads at the start of every session. It holds a short index, a map of the project, the plans we’ve agreed on, a log of what actually ran, and the decisions worth keeping.

The folder is plain markdown, so it works the same with Claude Code, GitHub Copilot, or whatever tool comes next. The tool-specific part is just one line pointing at the index.

Record decisions, not just code#

The decisions part borrows from Architecture Decision Records:

An architecture decision record (ADR) is a document that captures an important architectural decision made along with its context and consequences.

An architecture decision (AD) is a software design choice that addresses a significant requirement.

An architecture decision log (ADL) is the collection of all ADRs created and maintained for a particular project (or organization).

An architecturally-significant requirement (ASR) is a requirement that has a measurable effect on a software system’s architecture.

All these are within the topic of architecture knowledge management (AKM).

— Quoted from joelparkerhenderson/architecture-decision-record ↗

I use ADRs to persist:

  • feature designs
  • migrations
  • data model changes
  • security decisions
  • anything else I’d otherwise have to explain to the agent twice

Rules I settled on

  1. Don’t persist every plan by default. Persist by rules: scope of N+ files, cross-cutting impact, schema changes, or security implications.
  2. One plan per file. Prevents merge conflicts when two branches both touch the notes.
  3. Let the AI plan first, then write the note only after I approve it.
  4. Prune periodically. A weekly sweep keeps INDEX.md lean. Old plans move to an archive page so the front page stays short.

How to Do It#

1. Create the folder#

# Your project repo
dev-notes/
- INDEX.md          # index of dev-notes; reference only what you want AI to read
- PROJECT_MAP.md    # persistent, human-readable project map
- project.map.json  # (optional) machine-readable project map
- plans/            # proposals (immutable after approval)
	- 2025-09-07-migrate-keychain.plan.md
- exec/             # (optional) execution logs -- what actually ran
	- 2025-09-07-migrate-keychain.exec.md
- adrs/             # decisions worth keeping
	- ADR-keychain-migration.md
sh

The workflow around it:

You describe task
        │
        ▼
PLAN produced (no files touched) ──> you/human/agent review
        │                               ▲
        ├─ approve: mark PLAN "Approved"│
        │                               │
        ▼                               │
APPLY (execute) ──> write EXECUTION LOG ┘
        │
        └─ (optional) write ADR for lasting decisions
plaintext

2. Point your tool at the index#

Claude Code imports files into memory with @. Add one line to CLAUDE.md:

See @dev-notes/INDEX.md for current plans, project map and decisions.
markdown

Then lock down what the agent may write in .claude/settings.json. It can read the notes freely but only append execution logs, and it can never read secrets:

{
  "permissions": {
    "allow": ["Read(./dev-notes/**)", "Write(./dev-notes/exec/**)"],
    "deny":  ["Read(./secrets/**)", "Read(./.env*)"],
    "additionalDirectories": ["./dev-notes"]
  }
}
json

GitHub Copilot has no import syntax, but .github/copilot-instructions.md is always in context, so a plain instruction does the job:

Before starting any task, read `dev-notes/INDEX.md` and the files it links to.
For tasks touching 3+ files, write a plan to `dev-notes/plans/` first and wait for approval.
markdown

3. Fill in the files#

dev-notes/INDEX.md. Keep it to links; this is the only file the agent must read every time:

# dev-notes index

## Project
- @dev-notes/PROJECT_MAP.md

## Active plans
- @dev-notes/plans/2025-09-07-migrate-keychain.plan.md

## Decisions
- @dev-notes/adrs/ADR-keychain-migration.md

## Archive
- @dev-notes/archive/2025-q3.md
markdown

dev-notes/PROJECT_MAP.md. Short, concrete, and updated when the layout changes:

# Project Map (last updated: 2025-09-07)

## Top-Level Layout
- app/           – RN app entry; providers, navigation
- api/           – REST clients; auth interceptor
- server/        – FastAPI; /v1/search, /v1/auth
- infra/         – IaC; GitHub Actions; Dockerfiles
- tests/         – unit + e2e; fixtures

## Entry Points
- mobile: app/index.tsx → App.tsx
- server: server/main.py (uvicorn) ; config in server/settings.py

## Key Flows
1) Login → Token store (Keychain/SecureStorage) → Auth header via api/interceptor.ts
2) Search → api/search.ts → server/routers/search.py

## Build & Test
- mobile: `yarn install` ; iOS: `cd ios && pod install` ; run: `yarn ios`
- server: `uv venv && uv run pytest -q`
- CI: see .github/workflows/ci.yml

## Schemas / Contracts
- OpenAPI: server/openapi.yaml
- Types: api/types/*.ts

## See Also
- @dev-notes/INDEX.md   # plans & ADRs
markdown

dev-notes/project.map.json (optional). The same map in a shape that’s cheap for an agent to parse:

{
  "entryPoints": {"mobile": "app/index.tsx", "server": "server/main.py"},
  "flows": ["login", "search"],
  "build": {"mobile": ["yarn install","pod install","yarn ios"], "server": ["uv run pytest -q"]},
  "contracts": {"openapi": "server/openapi.yaml"}
}
json

dev-notes/plans/*.plan.md. Written by the agent in plan mode, approved by me, then frozen:

---
id: PLAN-2025-09-07-keychain
status: Proposed   # update to "Approved" after approval
links: { issue: "#1234" }
---

# Migrate rn-secure-storage → react-native-keychain

## Context
…

## Options
A) …  B) …

## Decision (if any so far)
Preferred: …

## Implementation Plan
- Files:
- Commands:
- Tests:
- Risks & Rollback:
markdown

dev-notes/exec/*.exec.md. What actually ran, so the next session can pick up from reality rather than from the plan:

---
plan: PLAN-2025-09-07-keychain
status: Executed
pr: https://github.com/acme/app/pull/567
commits:
  - fix(security): replace secure storage with keychain (PLAN-2025-09-07-keychain)
    sha: abc123…
results:
  tests: "unit 152/152 pass"
  ios: "pod install OK"
---

## Steps Run
1) yarn add react-native-keychain
2) cd ios && pod install
…

## Diffs / Notes
- See PR for unified diff; highlights: …
markdown

dev-notes/adrs/*.md. The classic Michael Nygard template ↗:

# Title

## Status

What is the status, such as proposed, accepted, rejected, deprecated, superseded, etc.?

## Context

What is the issue that we're seeing that is motivating this decision or change?

## Decision

What is the change that we're proposing and/or doing?

## Consequences

What becomes easier or more difficult to do because of this change?
markdown

Or the tiny version I use for small projects:

# PLAN: <feature/issue> (YYYY-MM-DD)
## Context
## Options considered
## Decision (if any)
## Implementation plan (steps/commands/files)
## Risks & rollback
## Verification (tests, metrics)
markdown

Example#

My macOS app SideTime ↗ uses a trimmed version of this. The index is a dozen lines:

# Dev Notes Index

## Plans
- [2025-09-11 Sparkle Auto-Update Implementation](.dev-notes/plans/2025-09-11-sparkle-auto-update.plan.md)
- [2025-10-04 Sparkle Appcast Generation Process](.dev-notes/plans/2025-10-04-sparkle-appcast-generation.plan.md)

## Archive
- [Feature Roadmap](.dev-notes/archive/feature-roadmap.md)
markdown

When I came back to the Sparkle work weeks later, a fresh Claude Code session read the two plans and continued from where the last one stopped. No re-explaining.

Tips#

  • /memory check: run /memory in Claude Code after editing CLAUDE.md to confirm the @ imports actually loaded.
  • /permissions UI: run /permissions once to verify the allow/deny rules resolve the way you expect.
  • Keep the index tiny. Everything in it costs context on every session. If it grows past a screen, archive.
  • Dates in filenames. 2025-09-07-migrate-keychain.plan.md sorts itself and tells the agent how stale it is.
  • One source of truth. If you also keep a copilot-project-context.md, have it link to PROJECT_MAP.md rather than duplicating it, or the two drift apart.

Summary#

A plain-markdown dev-notes/ folder with an index, a project map, approved plans, execution logs and ADRs gives any AI coding agent persistent memory across sessions, for the cost of one line in CLAUDE.md or copilot-instructions.md.

References#

Give AI Coding Agents a Memory - The dev-notes Folder I Use With Claude Code and Copilot
https://katrina-ziqi-ding.com/blog/workspace-setup
Author Ziqi (Katrina) Ding
Published at 07-09-2025