planledger
Planledger stores independent, structured, versioned implementation plans and renders each plan into one standalone rendered Markdown handoff file.
Product contract
- Planledger stores plans only.
- Each plan is independent.
- The main user is a coding agent through
skills/planledger/SKILL.md. - A done plan must contain todo items, acceptance criteria, target files, and validation commands.
- Planledger has no external task-manager integration.
What it does
- stores independent plans under
../ledger/planledger/<project-uuid>/data/plans/plan-0001/through Ledgercore 0.6.1+'s schema-3 storage; - versions every meaningful plan change;
- keeps each plan as modular component files;
- renders a standalone Markdown artifact for human or coding-agent handoff;
- enforces handoff quality guardrails before a plan can be marked
done.
planledger is not a task manager, does not store goals, and has no external task-manager integration.
Plan identity
Each plan has a stored local ID and derived cross-ledger references:
local ID: plan-0001
global ref: pl:plan-0001
file ref: pl-plan-0001
Canonical global references use <ledger>:<kind>-<number>, such as
tl:task-0001, al:adr-0002, sw:spec-0003, and pl:plan-0004.
File aliases such as pl-plan-0001 are accepted as selectors. Uppercase aliases
are input compatibility only; Planledger always emits lowercase canonical refs.
The global and file refs are derived from the configured ledger code and local
ID, not stored as a second identity source. Cross-ledger refs are identifiers,
not task-manager integration.
Release maturity
Planledger is currently a beta package (Development Status :: 4 - Beta). It is intended for planning-only workflows and standalone handoff artifacts. Beta status means the project is suitable for early adopter use, but releases should pass the documented maintainer gate before publication.
Install
pip install -e .
Quick start for coding agents
The CLI is the only supported mutation path. The rendered Markdown artifact is the deliverable.
# Check workspace state
planledger status
planledger status --check
# Initialize if needed
planledger init
# Create a new independent plan. The new plan becomes active.
planledger plan create --title "Review storage migration" --request-file planning/review-request.md
# Populate components on the active plan (inspect repository files first)
planledger plan component set context --file context.md
planledger plan component set approach --file approach.md
planledger plan component set todo_items --file todos.md
planledger plan component set target_files --file target_files.md
planledger plan component set validation --file validation.md
planledger plan component set risks --file risks.md
# Override the active plan when needed
planledger plan show --plan plan-0001
planledger plan activate plan-0001
# Global and file selectors are also accepted:
planledger plan show pl:plan-0001
planledger plan show pl-plan-0001
# Build, validate, mark done
planledger plan build
planledger plan validate
planledger plan status done --reason "Ready for coding agent handoff."
# Export rendered plan to workspace root for the harness
planledger plan export --out storage_migration_implementation_brief.md
## File-driven implementation reviews
When a planning or review request already exists as a file, prefer `--request-file` so Planledger snapshots the exact UTF-8 content into the request component. The source file is not needed after creation. For failing-test recovery or implementation reviews, inspect the repository first, record observed evidence separately from proposed changes, distinguish stale tests from production defects, inspect call sites before dead-code claims, prioritize findings as P0/P1/P2, and use staged TODOs with file-level validation.
Implementation-brief exports should use an explicit descriptive path when they are intended for direct handoff:
```bash
planledger plan create --title "Review storage migration" --request-file planning/review-request.md
planledger plan export --out storage_migration_implementation_brief.md
```
The default `plan-000X.md` export remains available for generic plans. `done` means the brief is structurally ready for a coding agent, not that the implementation tests have passed.
Use a **workshop** first when a request is about shaping a feature, finding examples, clarifying behavior, product rules, BDD scenarios, acceptance scenarios, or requirement exploration. Use a **plan** directly when the request is implementation-oriented, asks for a coding-agent handoff, names target files, revises an existing `plan-000X`, or explicitly asks for a PLAN.md-style artifact. Do not ask which mode to use unless both paths are equally valid; prefer workshop-first when `prompt_profiles.planning_workshop.enabled = true` and the request is product or behavior shaping.
## Todo item template
Every todo item in the `todo_items` component should follow this structure:
```md
### TODO-001: <action-oriented title>
**Target files**
- [`path/to/file.py`](path/to/file.py) — why this file changes.
**Acceptance criteria**
- [ ] Observable outcome.
- [ ] Regression or edge case covered.
**Validation**
- `python -m pytest path/to/test_file.py -q`
Handoff quality guardrails
done is a handoff-readiness state, not an implementation-completed state. A plan cannot be marked done unless:
todo_itemscontains at least one### TODO-NNNheading.- Every todo item has an Acceptance criteria section with at least one checkbox.
- Every todo item has a Target files section with at least one file reference.
target_filescontains at least one repo-relative file path or Markdown link.validationcontains at least one validation command.- No required component contains placeholder content (
TBD,TODO:,<fill>, etc.). open_questionscontains no unresolved required questions (- [ ] REQUIRED:).
Plan validation means the plan artifact is structurally ready for handoff. It does not mean implementation tests have passed.
Plan components
Each plan stores these components:
| Component | Required | Description |
|---|---|---|
request |
yes | Original human request |
summary |
yes | Executive verdict |
context |
yes | Repository context and evidence |
open_questions |
no | Unresolved questions |
assumptions |
no | Assumed facts |
approach |
yes | Proposed implementation approach |
todo_items |
yes | Structured todo items |
target_files |
yes | Files that will change |
validation |
yes | Validation plan and commands |
risks |
yes | Risks and mitigations |
rollback |
no | Rollback or repair strategy |
notes |
no | Additional notes |
Rendered Markdown example
---
planledger_schema: planledger.rendered_plan.v2
plan_id: plan-0003
id: plan-0003
kind: plan
ledger_code: pl
global_ref: pl:plan-0003
file_ref: pl-plan-0003
title: Add feature A
status: done
version: 5
generated_at: 2026-06-09T12:00:00Z
---
# Add feature A
Plan: `plan-0003`
Ref: `pl:plan-0003`
Version: `v0005`
Status: `done`
## Executive verdict
**Ready for coding-agent implementation.**
One paragraph summarizing the decision and scope.
## Repository context and evidence
| Area | Finding | Evidence |
| ---- | --------------------------------------------------------- | ----------------------------------------- |
| CLI | Current command implementation is in `planledger/cli.py`. | Function `plan_create`, `plan_build`, ... |
## Proposed approach
Explain the design and why it is acceptable.
## Todo items
### TODO-001: Implement feature A
**Target files**
- [`planledger/cli.py`](planledger/cli.py)
**Acceptance criteria**
- [ ] CLI exposes the new behavior.
**Validation**
- `python -m pytest -q`
Filesystem layout
Planledger uses Ledgercore 0.6.1+ schema-3 storage. Committed project metadata
stays in .ledger; authoritative Planledger data lives in the external
data mount and ends in /data:
<project-root>/.ledger/ledger.toml
<project-root>/.ledger/ledger.local.toml # machine-local, normally ignored
<project-root>/.ledger/planledger/config.toml
<project-root>/../ledger/.ledger-store.toml
<project-root>/../ledger/planledger/<project-uuid>/
.ledger-project.toml
data/
storage.yaml
allocations/plans/
allocations/workshops/
plans/
workshops/
migrations/
The shared manifest contains one Planledger data mount:
[project]
uuid = "..."
name = "..."
[ledgers.planledger.mounts.data]
storage = "external"
root = "../ledger"
The optional local override is stored in .ledger/ledger.local.toml:
schema_version = 3
[ledgers.planledger.mounts.data]
storage = "user-data"
Planledger resolves relative, absolute, and supported home-relative external
roots through Ledgercore. Use planledger --json status or planledger storage where for authoritative paths. Use planledger migrate to inspect
legacy layouts and planledger migrate apply to perform a source-preserving copy transaction.
CLI surface
planledger init [--project-name NAME] [--create-external-store]
planledger storage where [--json]
planledger storage validate [--json]
planledger storage set STORAGE [--root PATH] [--local-storage-override | --project]
planledger storage clear-override [--json]
planledger storage migration-status [--journal PATH] [--json]
planledger storage recover [--journal PATH] [--policy auto|resume|rollback] [--dry-run] [--json]
planledger migrate [--data-storage STORAGE] [--external-root PATH]
planledger migrate apply [--mode copy] [--data-storage STORAGE] [--external-root PATH] [--dry-run]
planledger status [--check] [--json]
planledger info [--plan PLAN_ID | --workshop WORKSHOP_ID] [--paths-only] [--no-components] [--json]
planledger doctor [--json]
planledger next-action [PLAN_ID] [--json]
planledger plan create --title TITLE [--request TEXT | --request-file PATH | --stdin] [--status new|in_progress]
planledger plan activate PLAN_ID
planledger plan list [--status STATUS] [--json]
planledger plan show [PLAN_ID] [--plan PLAN_ID] [--component KEY] [--rendered] [--json]
planledger plan status [PLAN_ID] [--plan PLAN_ID] STATUS --reason TEXT
planledger plan cancel [PLAN_ID] [--plan PLAN_ID] --reason TEXT
planledger plan component list [PLAN_ID] [--plan PLAN_ID] [--json]
planledger plan component show COMPONENT [--plan PLAN_ID]
planledger plan component set COMPONENT [--plan PLAN_ID] (--text TEXT | --file PATH | --stdin) [--reason TEXT]
planledger plan component append COMPONENT [--plan PLAN_ID] (--text TEXT | --file PATH | --stdin) [--reason TEXT]
planledger plan build [PLAN_ID] [--plan PLAN_ID] [--out PATH] [--print] [--include-empty] [--json]
planledger plan export [PLAN_ID] [--plan PLAN_ID] [--out PATH] [--include-empty] [--json]
planledger plan validate [PLAN_ID] [--plan PLAN_ID] [--json]
planledger plan versions [PLAN_ID] [--plan PLAN_ID] [--json]
planledger plan diff [PLAN_ID] [--plan PLAN_ID] --from v0001 --to v0002
planledger plan apply --file PATH_OR_DASH [--dry-run]
planledger info is a read-only inventory of the canonical schema-3
workspace. It reports the generic storage object, the schema-4 state, the
active plan/workshop, record status, rendered artifacts, and disk footprint.
It never displays persisted allocation counters. Use
status for a quick health/counts snapshot and doctor for read-only health.
planledger info # full human inventory
planledger --json info | jq . # machine-readable inventory
planledger info --plan plan-0001 # full detail for one plan
planledger info --paths-only # just the resolved paths
planledger info --no-components # drop per-component fill-state
Structured bundle workflow
Agents can create or update plans through planledger.structured_plan.v1 bundles.
Use plan apply --file - --dry-run before large multi-component updates or when
the JSON is hand-written. For small targeted updates, direct plan apply --file -
is acceptable and does not require a temporary file:
cat <<'JSON' | planledger plan apply --file - --dry-run
{ "schema": "planledger.structured_plan.v1", "operation": "update", "plan_id": "plan-0001", "components": { "summary": "..." } }
JSON
cat <<'JSON' | planledger plan apply --file -
{ "schema": "planledger.structured_plan.v1", "operation": "update", "plan_id": "plan-0001", "components": { "summary": "..." } }
JSON
Stdin input
Component commands and plan create accept --stdin and --file - for multiline
input without temporary files:
cat <<'MD' | planledger plan component set context --stdin --reason "Record evidence."
Repository evidence...
MD
cat <<'JSON' | planledger plan apply --file -
{ "schema": "planledger.structured_plan.v1", ... }
JSON
Plan export
planledger plan export writes the rendered Markdown to a workspace-root-relative
path (default WORKSPACE_ROOT/PLAN_ID.md). The canonical storage path is the
sibling Ledger path, while exports remain explicit workspace artifacts.
planledger plan export --plan plan-0004
# writes: ./plan-0004.md
Planning interview profile
Planledger ships an optional prompt profile named planning_workshop. When enabled, the existing Planledger skill (the single skills/planledger/SKILL.md) asks the user one plan-quality question at a time, includes a recommended answer, inspects the repository first when possible, records required questions in the open_questions component, and stops after each question.
This is a prompt profile obeyed by the skill, not a separate skill and not a CLI command that interviews you. The CLI only parses, persists, and exposes the policy.
Enable it in .ledger/planledger/config.toml:
[prompt_profiles.planning_workshop]
enabled = true
activation = "always" # or "triggered"
question_policy = "ask_one_at_a_time"
codebase_first = true
include_recommended_answer = true
max_required_questions = 20
min_resolved_required_questions_before_done = 0
trigger_phrases = ["shape", "shape this feature", "shape this feature"]
required_question_topics = ["scope", "tests", "rollback", "risks"]
extra_guidance = """Interview the user until missing decisions are resolved. Ask exactly one question at a time and stop. Inspect the codebase first."""
activation = "always"asks during every new or updated plan while useful.activation = "triggered"activates only when the plan request contains one oftrigger_phrases. The phraseshape this featurestays valid user language, butplanning_workshopis the canonical feature name.- When the profile is active,
planledger --json next-actionreturnsnext_item == "ask_plan_question"with anagent_instruction, ornext_item == "answer_required_question"whenopen_questionsalready contains an unresolved- [ ] REQUIRED:line. planledger status --jsonexposes the configured profile underprompt_profiles.- When
min_resolved_required_questions_before_doneis a positive number,doneis blocked until that many- [x] REQUIRED:questions exist (default0stays permissive).planledger doctorwarns about unknown or invalid profile field values.
The profile does not create planning-workshop records, does not replace open_questions, and does not change Planledger's workshop-first, plan-second scope.
Development
python -m pytest
python -m ruff check .
python -m mypy planledger
Metadata
Release files for planledger 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| planledger-0.4.0.tar.gz | 154.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| planledger-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 243.2 kB
Release files / planledger-0.4.0.tar.gz
| Download URL | planledger-0.4.0.tar.gz |
|---|---|
| Size | 154.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d49ba018ce86af28a38a31972273344a9515b6cf3f97d755eb2ff6d5d254b19b
|
|
BLAKE2b-256 checksum How to use checksums |
665e49216f323098b9cfc20bd93cf4d0214470c5146108e00deaa38b5a6e424e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Release files / planledger-0.4.0-py3-none-any.whl
| Download URL | planledger-0.4.0-py3-none-any.whl |
|---|---|
| Size | 88.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
94d8e3e60e5bae376e45962874de56a0bb1ce313e6f7b085591723c7e93a12a9
|
|
BLAKE2b-256 checksum How to use checksums |
1b8a2730a321408fb1bef55a6db8305693160915fe00324cd5aa54ee160ddeed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|