Skip to content

Unified spec.md (SPEC-630)

SPEC-630 introduced the unified spec.md format, eliminating the separate technical.md file that Planu previously required. This page explains the motivation, the new structure, and how to migrate existing specs.

Background — Why We Merged technical.md

Before SPEC-630, every spec lived in two files:

planu/specs/SPEC-NNN-my-feature/
  spec.md        ← acceptance criteria + problem statement
  technical.md   ← implementation detail, types, file lists

This caused three recurring problems:

  1. Drift. Criteria in spec.md evolved but technical.md was not updated in lockstep.
  2. Context waste. Loading both files consumed ~50–100 K tokens per request even for simple queries.
  3. Placeholder pollution. technical.md was often a stub with double-dash separators (--) that tools could not distinguish from real content.

After SPEC-630: a single spec.md with a ## Technical section at the bottom.

The ## Technical Section

The ## Technical section follows the last acceptance criterion. It uses consistent sub-headings that Planu tools know to look for:

markdown
## Technical

### Files to create

- `src/feature/my-module.ts` — short description
- `src/feature/my-module.test.ts` — unit tests

### Files to modify

- `src/index.ts` — export the new module

### Implementation order

1. Create types/interfaces
2. Implement core logic
3. Add tests
4. Wire exports

### Key types / interfaces

​```ts
interface MyFeatureOptions {
  enabled: boolean
  timeout: number
}
​```

### Test stubs

​```ts
describe('myFeature', () => {
  it('should do X given Y', async () => {
    // TODO: implement
  })
})
​```

Sub-sections are optional

Include only the sub-sections that apply. A docs type spec may only need ### Files to create. A refactor spec may need ### Files to modify and ### Key types / interfaces.

Before / After Diff

diff
 planu/specs/SPEC-NNN-my-feature/
-  spec.md          ← criteria only
-  technical.md     ← separate implementation file
+  spec.md          ← criteria + ## Technical section unified

Inside the file, the difference looks like:

diff
 ## Acceptance Criteria
 
 **Scenario 1: ...**
 - GIVEN ...
 - WHEN ...
 - THEN ...
+
+## Technical
+
+### Files to create
+
+- `src/my-feature.ts` — main implementation

Migrating Existing Specs

If your project has specs with a separate technical.md, init_project or list_specs returns a directive listing the legacy files. The host LLM synthesizes mergedBody from them, and migrate_legacy_spec persists it.

migrate_legacy_spec(projectPath: "...", specId: "SPEC-NNN", mergedBody: "...", filesToRetire: ["technical.md"])

What migrate_legacy_spec Does

migrate_legacy_spec does the following:

  1. Persist. Writes mergedBody as the new spec.md, so the retired content lives under its ## Technical section going forward.
  2. Back up. Backs up each file in filesToRetire as a timestamped .bak copy.
  3. Retire. Deletes the retired file and git-untracks it.

Manual review after migration

Always review the merged ## Technical section after running migrate_legacy_spec. The host LLM synthesizes the content, so check it preserves everything that mattered in the legacy files.


Join the communityAsk questions, share feedback, and connect with other developers using Planu.
Join Discord