Skip to content

Plans Discipline — Project-Local Ephemeral Working Memory

Canonical normative reference for downstream contributors. This document mirrors the project specification's Plans Discipline section (Spec §3); it is the standalone reference that ships with the published repo. The discipline introduced here is foundational; every directive below is a MUST unless explicitly marked otherwise. The architectural rationale is recorded at ADR-0001.


1. Definition & Boundaries

A plan is any deliberate, transient working artifact produced during reasoning about a project, including but not limited to:

  • Architecture sketches, system-design walkthroughs, component decompositions.
  • Work-breakdown structures, multi-step task lists, decomposition trees.
  • Refactor strategies, migration walkthroughs, rollout sequences.
  • Debugging journals, hypothesis logs, reproduction notes.
  • Exploration notes, spike write-ups, prompt drafts, AI-conversation transcripts kept for later use.
  • Any document written to think with, not to ship.

Plans are categorically distinct from these adjacent classes:

Artifact Lifecycle Location Committed?
Plan Transient working memory <project-root>/plans/ Never (gitignored)
ADR (Architecture Decision Record) Permanent, decisive <project-root>/docs/adr/NNNN-*.md Always
Issue / PR description Tracker-bound GitHub / equivalent n/a (tracker)
CLAUDE.md / agent prompt / copilot-instructions.md Durable behavioral configuration <harness-config-root>/ (e.g. ~/.claude/ for Claude Code, <project-root>/.github/ for Copilot) Always (in the repo)
Code Executed result of a plan wherever code lives Always

Plans precede ADRs. When a plan converges to a durable decision, the decision is promoted to an ADR (§4 Lifecycle); the plan itself archives or discards.


2. Location, Visibility & Naming

  • Path. <project-root>/plans/. No exceptions. Not the harness configuration directory (e.g. ~/.claude/plans/ for Claude Code, or any other harness's native config root). Not a desktop scratch folder. Not ~/notes/. Not tmp/plans/.
  • Visibility. The folder is registered in the project's .gitignore per the canonical snippet at §6. The agent MUST verify this entry is present before any write; if absent, the agent appends it with a one-line header comment and the link to this document.
  • Filename convention. YYYY-MM-DD--<kebab-case-slug>.md. For larger workspaces, sub-categorize: plans/<area>/YYYY-MM-DD--<slug>.md.
  • Examples: plans/2026-04-30--auth-refactor-strategy.md, plans/api/2026-04-30--rate-limit-rollout.md.
  • Index. An optional plans/INDEX.md (also gitignored) may aggregate links and statuses; if present, the agent maintains it on every plan write.
  • Archive. Converged or abandoned plans relocate to plans/_archive/. Never deleted without explicit AskUserQuestion confirmation.
  • Authorship header on plans. Plans are exempt from the authorship-header banner (per docs/authorship-header.md §Exception List). They are gitignored ephemera; applying a permanent provenance banner to transient working memory is a category error. Frontmatter (§3) carries the plan's provenance.

3. Frontmatter Schema (validated)

Every plan file carries:

---
title: <human-readable title>
project: <git-remote-url-or-canonical-local-path>
created: <ISO-8601 timestamp>
updated: <ISO-8601 timestamp>
status: draft | in-progress | converged | abandoned
tags: [<kebab>, ...]
supersedes: <prior-plan-relative-path or null>
promotes-to: <docs/adr/NNNN-*.md or null>
---

A JSON Schema for plans lives at src/apothem/schemas/plan.schema.json. The /plan slash command (and the plans-discipline-language validator) validates against it on every write.


4. Lifecycle & Promotion Workflow

  1. Draft — initial creation; status: draft.
  2. In-progress — actively iterated; status: in-progress; updated refreshed on each material edit.
  3. Convergence — a durable decision crystallizes:
  4. Extract the decision into a new ADR at <project-root>/docs/adr/NNNN-<slug>.md.
  5. Set the plan's promotes-to to the ADR path.
  6. Set status: converged.
  7. Optionally move to plans/_archive/.
  8. Abandonment — direction abandoned; status: abandoned; archive or, with explicit AskUserQuestion, discard.
  9. Supersession — when a new plan replaces an older one, set the new plan's supersedes to the old plan's path, and set the old plan's status accordingly.

Promotion is the only path by which plan content becomes durable, version-controlled, and visible to teammates. No plan ever migrates wholesale into committed source.


5. Migration Protocol — One-Time, From a Harness plans/ Directory to Per-Project plans/

Note. This protocol applies to downstream projects adopting the Plans Discipline for the first time, where prior planning artifacts live inside a harness configuration directory (canonically ~/.claude/plans/ for the Claude Code adapter; substitute the equivalent path for other harnesses). The recursive case for the ecosystem itself (the harness install root as its own project) is resolved by gitignoring the harness-internal plans/ per ADR-0001, rather than physically migrating.

The migration is idempotent and reversible up to the moment of harness-internal plans/ directory removal (after which a backup tarball at ./.audit/plans-backup-<timestamp>.tar.gz provides the rollback artifact). The examples below show the Claude Code adapter path (~/.claude/plans/); substitute the equivalent path for other harnesses.

Step 1 — Inventory. Every file under ~/.claude/plans/ is enumerated and classified plan-artifact (quarantined).

Step 2 — Provenance Map. A provenance record is built for each plan, with an inferred destination project and a confidence score derived from frontmatter project field, body-scan signals (repo URLs, package names, absolute paths), and contextual cues.

Step 3 — Mapping Confirmation. The full map is presented to the operator as a single consolidated AskUserQuestion (or a tightly-batched sequence when the file count exceeds a comfortable single-prompt size). For each plan, the operator chooses among:

  • Confirm the inferred destination project.
  • Reassign to a different project (option presents the operator's known project list).
  • Defer — move to ~/.claude/.audit/orphan-plans/<original-name> for later sorting.
  • Discard — only with explicit, separately-confirmed AskUserQuestion (this is destructive).

When confidence is high for many plans, the prompt offers a confirm-all-high-confidence affordance to compress round-trips while still surfacing every individual decision in the log.

Step 4 — Pre-Move Project Preparation. For each unique destination project:

  1. Verify the project root exists and is readable.
  2. Verify it is a git repository (for non-git roots, surface via AskUserQuestion whether to proceed, initialize, or skip).
  3. Ensure <project-root>/plans/ exists; create with mkdir -p if not.
  4. Verify the project's .gitignore contains the canonical snippet from §6; if absent, append it with a header comment crediting this migration.
  5. Verify <project-root>/plans/ is not already tracked by git; if it is (a prior contributor mistake), surface via AskUserQuestion with options untrack-and-keep, untrack-and-remove-from-history, abort.

Step 5 — Move. For each confirmed plan → destination pairing:

  1. Compute the destination filename: if the source has frontmatter, normalize per §3; otherwise wrap the body with frontmatter (status draft, project field set, created from mtime, tags [migrated]) and rename to YYYY-MM-DD--<slug>.md.
  2. If a name collision exists at the destination, append -imported-<unix-timestamp> and log the rename.
  3. Copy the file (preserving mtime), verify SHA-256 matches at destination, then unlink the source.
  4. Append an entry to <project-root>/plans/_migration-manifest.md (also gitignored) recording: original path under ~/.claude/plans/, destination path, original SHA-256, timestamp, AskUserQuestion answer that authorized this move.

Step 6 — Verification. After all moves:

  1. Confirm ~/.claude/plans/ is empty (orphan-plans, if any, are inside ~/.claude/.audit/orphan-plans/ — outside the soon-to-be-deleted directory).
  2. Confirm each destination project has a populated plans/ and a manifest.
  3. Spot-check by SHA-256 that the migrated content matches the originals.

Step 7 — Tarball & Removal. Create ./.audit/plans-backup-<ISO-timestamp>.tar.gz containing the entire pre-move ~/.claude/plans/ tree (extracted from a temp snapshot taken at Step 1). Then, via a final AskUserQuestion confirming readiness, remove the now-empty ~/.claude/plans/ directory.

Step 8 — Audit Record. Emit ./.audit/plans-migration.md summarizing every move, every deferred file, every discard, every collision-rename, and every gitignore append.


6. Downstream .gitignore Snippet (canonical, copy-paste)

Every project that adopts this ecosystem MUST add the following block to its .gitignore:

# Claude planning artifacts (project-local working memory)
# See: <repo-url>/blob/main/docs/plans-discipline.md
plans/
!plans/.keep

The optional .keep exemption preserves the directory's existence in tools that prune empty paths, without leaking content. Whether to include .keep is decided per-project via AskUserQuestion on first invocation of /plan.


7. Self-Enforcement Mechanisms

The discipline must survive every future session. The following enforcement surfaces are mandatory and are encoded in the published ecosystem:

  1. CLAUDE.md — top-level mandatory behavior block stating: "Planning artifacts are written to <project-root>/plans/, never to the harness configuration directory (e.g. ~/.claude/ for Claude Code) and never to a global location. This rule is non-negotiable; cite §3 of the project spec on every related decision." This block is detected by the plans-discipline-language validator.
  2. .github/copilot-instructions.md — mirrors the same directive in Copilot-appropriate framing (§4 Plans Discipline); detected by the same validator on the Copilot surface.
  3. Default output-style — when a response would produce plan-shaped content (multi-step strategy, decomposition, design walkthrough, debugging journal), the style routes the artifact to a project-local plans/ write rather than printing-and-discarding inline. The style explicitly instructs: "If no project root is discoverable, halt with an AskUserQuestion asking the operator where to plan."
  4. Every agent prompt — carries an Output Surface stanza naming plans/ as the destination for any planning artifact, and an Anti-Pattern stanza naming the harness configuration plans/ directory (e.g. ~/.claude/plans/ for Claude Code) as forbidden.
  5. A dedicated /plan slash command — accepts a slug + body, writes to <project-root>/plans/YYYY-MM-DD--<slug>.md with correct frontmatter, ensures the destination's .gitignore is correct, refuses to write to harness-config-root paths.
  6. A pre-tool-use hook — plan-write-guard — intercepts every file-write tool call. If the target path matches a harness-internal plans/ location (e.g. ~/.claude/plans/... for Claude Code) or any other global-ecosystem path the heuristic identifies as plan-shaped, the hook blocks the write and raises an AskUserQuestion proposing redirection to the current project's plans/.
  7. CI validatorsno-global-plans fails the build if plans/ reappears in the published tree; plans-discipline-language fails if the canonical directive drops out of CLAUDE.md, the default output-style, or .github/copilot-instructions.md.
  8. Pre-commit hooks — mirror the above locally so the discipline is enforced before any commit lands.

8. Anti-Patterns (categorically prohibited)

  • Writing plans inside a harness configuration directory (e.g. ~/.claude/plans/ for Claude Code) or any other global location.
  • Committing plans to a project's git history (commit-time hooks should reject this).
  • Mixing plans with ADRs (different lifecycles, different locations, different commit-status).
  • Plans without frontmatter, plans without date-prefixed filenames, plans without a project field.
  • Cross-project plans (one plan, two projects). Split them — one plan per project.
  • Sharing plans across machines via a harness-config sync (e.g. ~/.claude for Claude Code) — a structural argument for keeping plans project-local: the project repo is the sync mechanism, on the rare occasions plans warrant sharing — and that warranting is itself a signal the content should be promoted to an ADR.
  • Plan content embedded inside agent prompts, skill bodies, or output-styles. Those surfaces are durable behavioral configuration; plans are transient working memory.
  • Applying the authorship-header banner to a plan file (plans are exempt by design — §2, docs/authorship-header.md §Exception List).

See Also

  • ADR-0001 — architectural rationale, alternatives considered, trade-offs accepted.
  • docs/authorship-header.md — the authorship-header banner discipline (plans are categorically exempt).
  • docs/ai-conventions.md — multi-surface AI conventions (the Plans Discipline appears as a shared claim across CLAUDE.md, copilot-instructions.md, and every opted-in optional surface).
  • Project specification §3 — the full canonical text mirrored here.