Skip to main content

Durable project-state storage and CLI for coding workflows

Project description

PyPI - Version PyPI - Python Version PyPI - Downloads codecov

taskledger

taskledger is a task-first durable state layer for staged coding work. It keeps project-local configuration in taskledger.toml at the workspace root and stores plans, approval state, implementation logs, validation results, locks, and fresh-context handoffs under a configurable taskledger_dir (default: .taskledger/ beside that config file).

Canonical workflow

task -> plan -> approval -> implement -> validate -> done

The normal agent path is deliberately small:

actor whoami
task active | task show | task create | task activate | task follow-up
next-action | context | can
plan start -> plan template -> plan upsert -> plan lint -> plan accept
question add | question add-many | question answer | question answer-many | question status | question answers
todo next | todo show | todo done | todo status
implement start | implement resume | implement change | implement scan-changes | implement finish
validate start | validate status | validate check | validate finish
review record
handoff create | handoff show | handoff claim | handoff close

Everything else is support, human inspection, advanced transfer/storage work, repair/migration, or beta project search. Those commands remain public, but they are not part of the baseline lifecycle agents should reach for first.

The broader command surface is organized as:

Core workflow:

  • task, plan, question, implement, validate, todo

Context and decision-making:

  • intro, file, link, require, handoff, config

Operations and advanced overlays:

  • context, pipeline, next-action, can, search, grep, symbols, deps, actor, view, usage, monitor, storage, sync

Repair and inspection:

  • lock, doctor, repair, reindex

Project lifecycle:

  • init, status, export, import, snapshot, release

Non-goals

Taskledger is not a general project-management suite, issue tracker, CI system, release manager, or source-code intelligence platform. It is a local durable ledger for staged coding work. Optional reporting, sync, search, and worker pipeline features must not change the default task-first lifecycle unless a project explicitly opts in.

Opaque links and external artifacts

Taskledger stores task-local work state and opaque links. Cross-ledger semantics belong to an organizer such as ledgerdeck. Use taskledger link or taskledger file for references to external artifacts; taskledger does not interpret those references.

taskledger link add --url specs/behavior/features/checkout/payment.feature --label "behavior spec"
taskledger file link specs/behavior/features/checkout/payment.feature --kind doc --label "behavior spec"
taskledger validate check --criterion ac-0001 --status pass --evidence "pytest tests/test_checkout_payment.py::test_payment_flow"

Which read command to use

Need Command
Next step next-action
Next implementation item todo next
Fresh session summary usage
Active task summary task show
Specific task summary task show TASK_REF or task show --task TASK_REF
Project/ledger overview status, tree
Human monitor monitor
Linked-file drift check file status TASK_REF
Reviewable markdown report task report
LLM/agent compiled export task export
Fresh worker context context or durable handoff show
Command audit task transcript

Planning guidance profiles

Taskledger supports project-local advisory planning guidance under [prompt_profiles.planning] in the active project config file (taskledger.toml or .taskledger.toml when legacy config is still present).

[prompt_profiles.planning]
profile = "strict"
question_policy = "always_before_plan"
max_required_questions = 3
min_acceptance_criteria = 2
todo_granularity = "atomic"
require_files = true
require_test_commands = true
require_expected_outputs = true
require_validation_hints = true
plan_body_detail = "detailed"
required_question_topics = ["scope", "compatibility", "test strategy"]
extra_guidance = "Every plan must mention docs, tests, and rollback or repair behavior."

Inspect guidance for the active task:

taskledger plan guidance
taskledger --json plan guidance

This guidance is advisory and cannot override lifecycle gates, user approval, validation requirements, lock rules, or higher-priority harness instructions. See docs/usage.rst for the full key reference and workflow details.

Quick config inspection/edit examples:

taskledger config list
taskledger config get prompt_profiles.planning.max_required_questions
taskledger config set prompt_profiles.planning.max_required_questions 3
taskledger config set prompt_profiles.planning.question_policy always_before_plan

Optional worker pipelines

Projects may optionally configure worker pipelines in taskledger.toml to guide fresh-context handoffs. Worker pipelines are advisory overlays on the existing planning, implementation, and validation lifecycle. They can be three steps, four steps, five steps, or custom. When no worker pipeline is configured, the default taskledger behavior is unchanged.

[worker_pipeline]
enabled = true
name = "tdd-four-context"
mode = "guided"

[[worker_pipeline.steps]]
id = "planner"
lifecycle_stage = "planning"
base_context = "planner"

[[worker_pipeline.steps]]
id = "tester"
label = "Test Writer"
lifecycle_stage = "implementation"
base_context = "implementer"
actor_role = "implementer"
kind = "check"
description = "Add or update failing tests before code changes."
required_output = ["New or updated failing tests with a short summary."]
must_not = ["Do not change production code in this step."]
todo_tag = "tests"
test_command_policy = "may_fail"

[[worker_pipeline.steps]]
id = "coder"
lifecycle_stage = "implementation"
base_context = "implementer"
kind = "todo"
description = "Implement the approved change and make the tests pass."
required_output = ["Code changes plus passing targeted checks."]
must_not = ["Do not skip required validation evidence."]
todo_tag = "implementation"
test_command_policy = "must_pass"

[[worker_pipeline.steps]]
id = "reviewer"
lifecycle_stage = "review"
base_context = "code-reviewer"
kind = "review"

Supported top-level keys are enabled, name, mode, and steps. Supported step keys are id, label, lifecycle_stage, base_context, actor_role, kind, description, required_output, must_not, todo_tag, and test_command_policy. mode = "guided" does not add lifecycle gates; it adds worker-step hints to taskledger next-action, including the pending step id plus ready-to-run worker context and handoff commands.

taskledger pipeline show
taskledger pipeline next
taskledger next-action
taskledger context --worker tester
taskledger pipeline context tester
taskledger handoff create --worker tester --summary "Add failing tests only."
taskledger plan template --with-worker-pipeline --file ./plan.md

Install

python -m pip install -e .
python -m pip install -e ".[dev]"

Shell completion

After installing taskledger, install completion for your current shell:

taskledger --install-completion

To inspect the generated completion script instead of installing it:

taskledger --show-completion

Restart your shell session after installation.

Quick start

Initialize durable state in the current workspace:

taskledger init
# or keep storage outside the source repo
taskledger init --taskledger-dir /mnt/cloud/taskledger/my-repo
# or point at another workspace explicitly
taskledger --root /path/to/repo init

init writes taskledger.toml in the workspace root. By default that config points at .taskledger/, but --taskledger-dir can move durable state to an external directory without nesting another .taskledger inside it.

Create and activate a task, ask required planning questions, regenerate the plan from answers, approve it, implement todos with evidence, and validate it:

taskledger task create "Rewrite V2" --slug rewrite-v2 --description "Migrate to the task-first design."
taskledger task activate rewrite-v2 --reason "Start planning"
taskledger plan start
taskledger question add-many --required-for-plan --text $'Should exports include the new state?\nShould snapshots include implementation artifacts?'
taskledger question answer-many --text $'q-0001: Yes.\nq-0002: No.'
taskledger question status
taskledger plan template --from-answers --file ./plan.md
taskledger plan upsert --from-answers --file ./plan.md
taskledger plan review --version 1
taskledger plan lint --version 1
taskledger plan accept --version 1 --note "Ready."

taskledger next-action
taskledger --json next-action

taskledger context --for implementation --format markdown
taskledger implement start
taskledger implement checklist
taskledger implement change --path taskledger/storage/task_store.py --kind edit --summary "Normalized v2 markdown storage."
taskledger todo done todo-0001 --evidence "Updated taskledger/storage/task_store.py"
taskledger implement finish --summary "Implemented the approved plan."
taskledger review record --result pass --summary "No blocking code-quality issues."

taskledger context --for validation --format markdown
taskledger validate start
taskledger validate status
taskledger validate check --criterion ac-0001 --status pass --evidence "pytest -q tests/test_taskledger_v2_cli.py"
taskledger validate finish --result passed --summary "Validated the rewrite."

Review evidence may also be recorded after validation has moved a task to done; this appends a review record without reopening the completed task.

When a user explicitly asks an agent for a review, the agent should persist the final review with taskledger review record before answering. A chat-only review is not durable task evidence. Post-completion review records are append-only and do not reopen the task.

To revise a proposed plan, re-enter planning and edit an exported workspace copy. Never edit .taskledger/ files directly:

taskledger plan revise
taskledger plan export --version latest --file ./plan.md
# edit ./plan.md
taskledger plan upsert --file ./plan.md

For manually completed work (e.g., manual testing, operations tasks, or work completed outside the task-first lifecycle), use task record to create a done task directly without acquiring lifecycle locks. Note: task record does not replace the normal task lifecycle; it is for recording work already completed, not as a shortcut for active task management.

taskledger task record "Deploy to production" --summary "Deployed v0.4.1 to prod" --change "infra/deploy.sh:run:Updated prod config" --evidence "Monitoring shows no errors"
taskledger task record "Manual API testing" --summary "Tested new endpoints" --allow-empty-record --reason "Exploratory testing, no formal changes tracked"

Archive is a visibility operation: it hides tasks from default list/tree/dashboard views without deleting history. Task ids stay monotonic and are never reused. Slugs can be reused after archive.

taskledger task archive task-0030 --reason "Hide historical task"
taskledger task list --archived
taskledger task unarchive task-0030 --reason "Need to continue work" --slug task-0030-reopened
taskledger tree --include-archived

If validation finds an implementation bug, keep the accepted plan and restart implementation explicitly:

taskledger validate finish --result failed --summary "Parser edge case still fails."
taskledger next-action
taskledger context --for implementation --format markdown
taskledger implement restart --summary "Fix failed validation findings."

Release boundary tags

Release commands are advanced human/project operations, not part of the normal task -> plan -> approval -> implement -> validate -> done agent path.

Use taskledger only to record the task boundary for a release:

taskledger release tag 0.4.1 --at-task task-0030 --note "0.4.1 released"
taskledger release list
taskledger release show 0.4.1

Taskledger does not manage changelog entries or edit CHANGELOG.md. When you need release notes, changelog entries, or CHANGELOG.md updates, use the separate releaseledger tool, which owns changelog entries, changelog context rendering, and CHANGELOG.md builds. The ledgers stay isolated; load both skills when cross-ledger release notes are needed.

taskledger next-action is the preferred fresh-context entrypoint. It stays read-only and points at the next concrete question, todo, criterion, or repair step.

Human output example:

todo-work: Implementation is in progress; 1 todos remain.
Next todo: todo-0001 -- Update next-action JSON payload.
Command: taskledger todo show todo-0001
Mark todo done after evidence exists: taskledger todo done todo-0001 --evidence "..."
Progress: 0/1 todos done

JSON result example:

{
  "kind": "task_next_action",
  "action": "todo-work",
  "next_command": "taskledger todo show todo-0001",
  "next_item": {
    "kind": "todo",
    "id": "todo-0001",
    "text": "Update next-action JSON payload.",
    "validation_hint": "Run: pytest tests/test_todo_implementation_gate.py -q; Expected: pass",
    "done_command_hint": "taskledger todo done todo-0001 --evidence \"...\""
  },
  "commands": [
    {
      "kind": "inspect",
      "label": "Show next todo",
      "command": "taskledger todo show todo-0001",
      "primary": true
    },
    {
      "kind": "complete",
      "label": "Mark todo done after evidence exists",
      "command": "taskledger todo done todo-0001 --evidence \"...\"",
      "primary": false
    }
  ],
  "progress": {
    "todos": {
      "total": 1,
      "done": 0,
      "open": 1,
      "open_ids": ["todo-0001"]
    }
  },
  "blocking": []
}

Compact implementation loop

For routine same-session implementation, prefer next-action and the single next todo over broad generated context:

taskledger --json next-action
taskledger --json todo next
taskledger todo show todo-0003
# implement only that todo
pytest tests/...
taskledger todo done todo-0003 --evidence "pytest tests/... passed"
taskledger --json next-action

Rules for agents:

  • Prefer next-action and todo next over generated context during normal work.
  • Use the todo validation_hint before marking a todo done.
  • Record concise evidence with todo done.
  • Do not create handoffs or context bundles unless the user asked to switch harness or session.

Fresh-session startup and human monitoring

Use taskledger usage as the compact fresh-session startup command for agents and taskledger monitor as the lightweight human terminal dashboard.

taskledger usage
taskledger --json usage
taskledger usage -q
taskledger usage --task task-0040

taskledger monitor --once
taskledger monitor --refresh-seconds 2
taskledger monitor --task task-0040

monitor is dependency-free, read-only, and suitable for narrow terminals such as Termux. Agents should keep using next-action, todo next, and --json for routine automation; monitor is the human observation surface.

File links can now capture a baseline snapshot and later report drift:

taskledger file link task-0040 src/foo.py --kind code --snapshot
taskledger file status task-0040
taskledger file refresh task-0040 src/foo.py --reason "Rebaseline after accepted implementation"

Agents should keep using taskledger next-action, taskledger todo next, and --json commands as the canonical automation interface for routine same-session work. Reach for context or handoffs when the task actually needs a broader fresh-context transfer.

Storage layout

taskledger keeps project-local configuration in the workspace root and durable records under the configured storage root. The checked-in taskledger.toml stores project identity plus the current branch-scoped ledger pointer and next task number. Operational task state remains ignored under .taskledger/ledgers/<ledger_ref>/:

taskledger.toml
.taskledger/
  storage.yaml
  ledgers/
    main/
      intros/
      releases/
      tasks/
      events/
      indexes/   # optional derived caches and registries

Markdown files are canonical. Task, plan, and run listings scan only the current ledger by default. JSON files under the current ledger's indexes/ directory are optional derived caches or registries and are not required for task correctness.

Branch-scoped ledgers

.taskledger/ stays ignored and local. taskledger.toml is safe to commit and contains the current ledger_ref, optional parent ref, and the next logical task number for the checked-out source branch.

When starting long-lived branch-local work, fork the ledger pointer after creating the Git branch:

git checkout -b feature-a
taskledger ledger fork feature-a
git add taskledger.toml

Returning to a branch whose taskledger.toml points back to main hides the feature branch's active task and task list. Two ledgers may both contain a logical task-0030; this is expected because task IDs are scoped by ledger_ref. Use taskledger ledger adopt --from REF TASK_REF when branch-local task history should be copied into the current ledger.

You can also point taskledger.toml at an external storage root:

taskledger init --taskledger-dir /mnt/cloud/taskledger/project-a
/home/me/src/project-a/taskledger.toml
/mnt/cloud/taskledger/project-a/storage.yaml
/mnt/cloud/taskledger/project-a/ledgers/main/releases/
/mnt/cloud/taskledger/project-a/ledgers/main/tasks/
/mnt/cloud/taskledger/project-a/ledgers/main/events/
/mnt/cloud/taskledger/project-a/ledgers/main/indexes/

Use one taskledger_dir per source project. Do not share one storage directory across unrelated repositories.

Sync across PCs without committing .taskledger/

Use a sibling private Git repository for the external storage root instead of committing .taskledger/ into the source repository:

# /home/me/src/project-a/taskledger.toml
taskledger_dir = "../taskledger-state/project-a"
/home/me/src/project-a/                  # source repo
/home/me/src/taskledger-state/           # private state repo
/home/me/src/taskledger-state/project-a/ # taskledger_dir

Keep one active writer at a time. Before starting on a PC, pull the private state repo, then run taskledger doctor and taskledger next-action. After stopping at a clean lifecycle boundary, commit and push the state repo. If work must move mid-run, prefer taskledger export TASK_REF / taskledger import ARCHIVE because imported runtime locks are quarantined by default.

Helpful local commands:

taskledger storage where
taskledger sync preflight
taskledger sync status
taskledger sync commit --message "Sync project-a taskledger state"
taskledger sync export --output ./taskledger-transfer.tar.gz
taskledger sync import ./taskledger-transfer.tar.gz --dry-run
taskledger sync git init --repo ../taskledger-state --project-path project-a
taskledger sync git status
taskledger sync git pull
taskledger sync git push
taskledger sync git push --message "Sync project-a taskledger state"

See docs/sync.rst for the full second-PC bootstrap, daily sync protocol, and Syncthing/rclone caveats.

JSON output

Use --json for machine-readable payloads:

taskledger --json status --full
taskledger --json task active
taskledger --json task show
taskledger --json task show task-0001
taskledger --json context --for validation --format json

Example status payload:

{
  "ok": true,
  "command": "status",
  "result": {
    "kind": "taskledger_status",
    "workspace_root": "/home/me/src/project-a",
    "config_path": "/home/me/src/project-a/taskledger.toml",
    "taskledger_dir": "/home/me/src/project-a/.taskledger",
    "project_dir": "/home/me/src/project-a/.taskledger",
    "counts": {
      "tasks": 1,
      "introductions": 0,
      "plans": 1,
      "questions": 1,
      "runs": 2,
      "changes": 1,
      "locks": 0
    },
    "active_task": null,
    "healthy": true
  },
  "events": []
}

Action/event logging

Action/event logging is enabled by default. Taskledger writes immutable TaskEvent records for lifecycle mutations (task create, implementation start, todo completion, validation checks, etc.) so taskledger monitor shows recent activity without extra configuration.

To disable new action events, set in taskledger.toml:

[event_logging]
enabled = false

Existing event files remain readable regardless of the setting. taskledger task events continues to work when logging is disabled.

Action/event logging is separate from agent command transcript logging, which remains disabled by default (see [agent_logging]).

Handoff-driven work

Fresh-context handoff is a primary feature:

taskledger context --for planning --format markdown
taskledger context --for implementation --format markdown
taskledger context --for validation --format json
taskledger task report --task task-0030 -o task30.md
taskledger task export task-0030 -o task-0030.llm.md
taskledger usage --task task-0030
taskledger handoff create --mode implementation --intended-actor agent --intended-harness codex
taskledger handoff claim handoff-0001
taskledger handoff close handoff-0001 --reason "Implementation started."

task dossier, root view, and the legacy handoff *-context renderers remain advanced/compatibility read surfaces. Prefer context --for ... and handoff show for agent continuation.

Fresh-worker contexts

Use focused contexts when handing one todo or one review run to a fresh worker:

taskledger context --for implementer --todo todo-0003
taskledger context --for spec-reviewer --run run-0008
taskledger context --for code-reviewer --run run-0008
taskledger handoff create --mode implementation --todo todo-0003
taskledger handoff show handoff-0001 --format markdown

handoff create now stores the generated Markdown context snapshot in the handoff record so another harness can continue from the exact same input.

Multi-Actor Handoff Protocol

The handoff protocol enables safe work transitions between human and agent actors across different harnesses:

Features

  • Actor Identity: Track WHO performs each stage (human, agent, system)
  • Harness Tracking: Record FROM WHERE each stage ran (manual, Codex, OpenCode, etc.)
  • Handoff Records: Explicitly hand off work with context and intent
  • Claim Protocol: New actors claim handoffs before starting work
  • Lock Management: Transfer or release locks during handoffs
  • Event Trail: Full audit trail recording all state changes
  • Durable Records: Markdown-first storage with YAML metadata

Quick Start

# See your current identity
$ taskledger actor whoami

# Create a handoff
$ taskledger handoff create --task task-0001 --mode implementation --todo todo-0003

# Claim it
$ taskledger handoff claim handoff-0001 --task task-0001

# Show details
$ taskledger handoff show handoff-0001 --task task-0001 --format text

# Close when done
$ taskledger handoff close handoff-0001 --task task-0001 --reason "Continued."

See docs/usage.rst and skills/taskledger/SKILL.md for task-first handoff guidance.

Export, import, and snapshots

taskledger init --project-name "Taskledger"
taskledger export
taskledger export --task task-0040
taskledger export task-0040
taskledger import ./taskledger-transfer.tar.gz --dry-run
taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
taskledger import ./taskledger-transfer.tar.gz --replace
taskledger snapshot ./artifacts

When no explicit output path is passed, default export archives are written inside the resolved workspace root with this filename policy:

taskledger-export-{project_slug}-{ledger_ref}-{timestamp}.tar.gz
taskledger-task-{project_slug}-{ledger_ref}-{task_id}-{timestamp}.tar.gz

project_slug is derived from project_name in taskledger.toml. If project_name is missing, taskledger falls back to the workspace directory name. Import safety still relies on project_uuid, not the name/slug.

--include-bodies and --include-run-artifacts now change archive content:

  • --no-include-bodies strips record body text (body / context_body) from exported payloads.
  • --include-run-artifacts embeds task and agent-log artifact files under artifacts/ in the archive.

Cross-machine imports preserve durable task/run data, but imported runtime locks are quarantined by default. After importing an in-progress implementation, run:

taskledger next-action
taskledger implement resume --reason "Continue imported implementation."

Use --lock-policy keep only for diagnostic full-fidelity lock restoration.

Single-task transfer from a config-only checkout:

# fresh checkout on another PC
taskledger init
taskledger task create "Fix import edge case" --slug fix-import-edge-case --description "..."
# ... normal plan / implementation / validation lifecycle ...
taskledger export task-0040

# main dev repo
taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
taskledger task list
taskledger task show task-0040

Skill packaging

Agent workflows work best when the taskledger skill is installed in the coding harness. The CLI has a task-first lifecycle with explicit planning, approval, implementation, validation, locks, and handoff gates; without the skill, an agent may not know the intended command sequence or gate semantics.

The canonical skill file lives at:

skills/taskledger/SKILL.md

Keep this skill outside the Python package. No additional skills/taskledger/examples/ directory is required.

Development

python -m pytest -m "not slow"
python -m pytest -m "not slow" -n auto
python -m pytest -n auto
python -m pytest
ruff check .

Full release-readiness sweep:

make release-check

Task-centered traceability

Taskledger owns temporal work truth: tasks, plans, acceptance criteria, implementation changes, validation checks, reviews, locks, and handoffs. Cross-ledger links are opaque file or ID references.

Local IDs remain the stored identity. Global refs are derived as <ledger.code>:<local_id>.

Examples:

  • task-0001 -> tl:task-0001
  • todo-0001 -> tl:todo-0001
  • ac-0001 -> tl:ac-0001

File-safe aliases:

  • tl-task-0001
  • tl-todo-0001
  • tl-ac-0001

Do not store global_id in task records. Uppercase/file-safe forms may be accepted as aliases, but canonical output is lowercase colon refs.

Run taskledger trace TASK --format json for a read-only taskledger.trace.v1 bundle that links task history, accepted AC IDs, opaque link refs, source refs, evidence refs, reviews, and handoffs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

taskledger-0.5.0.tar.gz (937.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

taskledger-0.5.0-py3-none-any.whl (367.6 kB view details)

Uploaded Python 3

File details

Details for the file taskledger-0.5.0.tar.gz.

File metadata

  • Download URL: taskledger-0.5.0.tar.gz
  • Upload date:
  • Size: 937.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for taskledger-0.5.0.tar.gz
Algorithm Hash digest
SHA256 07f5c3fbe726075705dfb638ecf376994412197166f57cea9ed4ee8d68878f50
MD5 931c7c4e8bd1530dafec84c2f6fe48e9
BLAKE2b-256 f590b93ede8c6d9a6df140c6a35952e50c900479b19e8210633e52038e433dc7

See more details on using hashes here.

File details

Details for the file taskledger-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: taskledger-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 367.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for taskledger-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f8642a365150fc4946505c21bea3d11f8546fac3398d31907fcbe4a1e14e0716
MD5 c1e1e28e26efd46454c4d2e9f65620c8
BLAKE2b-256 cd21a9b15a36c3c3a3a26ab6ad1389c7d306643601953e3c450808da5d5b2b7b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page