Skip to main content

task-handoff-check

Lint a task handoff (human or AI agent) for a missing owner, next step, blockers, done criteria or evidence before someone else picks it up.

license: MIT

Catch incomplete or contradictory task handoffs before someone else picks up the work. This small CLI and library checks an explicit JSON document for a clear owner, next action, blockers, completion criteria and evidence references.

A passing handoff is structurally consistent. It is not proof that its claims are true, that its references exist, or that anyone is authorized to act.

An open-source engineering utility from IT Custom Solution. Python 3.10+, no runtime dependencies, no network access, no commands executed. MIT licensed.

Try it in one minute

git clone https://github.com/itcustomsolution/task-handoff-check.git
cd task-handoff-check
python -m task_handoff_check examples/in-progress.json
python -m task_handoff_check examples/complete.json --json
python -m task_handoff_check examples/invalid-complete.json --json

The first two checks pass. The third exits 1 because it claims complete while listing an open blocker. All examples, people and references are fictional; referenced paths deliberately need not exist. The tool never opens them.

Install the latest published release with pipx install task-handoff-check (or pip install task-handoff-check). This README describes the source tree, which can contain unreleased changes; see CHANGELOG.md and use the source installation below to get them.

To install from source in a virtual environment:

python -m venv .venv
. .venv/bin/activate
python -m pip install .
task-handoff-check --version

Installation uses setuptools as a build dependency. Running from the checkout needs only the Python standard library; no package registry release is required.

Handoff format

{
  "schema_version": 1,
  "objective": "Review the sample release notes",
  "owner": "Sample Maintainer",
  "status": "in_progress",
  "next_action": "Compare the notes with the sample changelog",
  "inputs": [
    {
      "label": "changelog",
      "path": "docs/changelog.md"
    }
  ],
  "evidence": [],
  "blockers": [],
  "completion_criteria": [
    {
      "description": "All sample changes reviewed",
      "met": false,
      "evidence": []
    }
  ],
  "updated_at": "2026-01-01T09:00:00Z"
}

All shown fields except updated_at are required. Unknown fields are errors.

Field Rule
schema_version Integer 1
objective, owner Meaningful nonempty text
status ready, in_progress, blocked, or complete
next_action Meaningful text for unfinished work; JSON null for complete
inputs, evidence Arrays of reference objects; may be empty
blockers Array of objects with description and owner; open blockers only
completion_criteria Nonempty array with description, boolean met, and evidence
updated_at Optional timestamp with seconds and timezone

A reference contains a meaningful label and exactly one path or url. Paths must be portable relative paths without traversal, control characters, reserved device names or Windows-reserved punctuation. URLs must be absolute HTTP(S) references without embedded credentials. Labels must be distinct within each array after Unicode NFC normalization and case folding.

Each criterion's evidence is an array of exact labels from the top-level evidence array. A met: true claim requires at least one such reference. Repeated evidence labels and repeated criterion descriptions are rejected. Reference content and accessibility are never checked.

blocked requires at least one open blocker and a concrete next action. Any open blocker requires blocked. complete requires all criteria to be met: true, no blockers and a null next_action. Other statuses may contain met criteria while further work remains. This is deliberately a small task state model; it is not a general workflow engine.

Blank text, control characters and exact placeholders such as TBD, todo, unknown, unassigned, n/a, none, - and ? are rejected. The tool does not understand free text semantically: an unhelpful sentence can still pass.

Optional, reproducible freshness checks

Freshness is never inferred from the machine's current time. Supply --as-of to compare timestamps; optionally specify an inclusive maximum age:

python -m task_handoff_check examples/in-progress.json \
  --as-of 2026-01-02T09:00:00Z --max-age-hours 24 --json

This example passes exactly at the 24-hour boundary. One second later it is stale. --max-age-hours must be a finite, nonnegative number and accompanied by --as-of; choosing a meaningful freshness policy is up to you. Supplying --as-of requires a valid updated_at and rejects a future update. --as-of alone compares chronology, without a maximum-age policy.

Accepted timestamps use YYYY-MM-DDTHH:MM:SS, optional 1–6 fractional digits, and Z or an explicit +HH:MM/-HH:MM offset. Leap seconds are unsupported. The timestamp remains an assertion from the document author.

Reports and exit codes

JSON reports contain ok, scope (handoff_consistency), freshness, and an issues array of code, location and message. freshness is one of not_evaluated, unavailable, timestamp_compared, future, within_limit or stale. Other schema errors can coexist with a valid timestamp comparison.

Exit Meaning
0 Consistency checks pass; or help/version displayed
1 Invalid input, inconsistent handoff or failed timestamp check
2 Invalid command-line usage or freshness options

Common issue codes: invalid_schema, unknown_field, missing_text, status_conflict, invalid_reference, duplicate_label, duplicate_criterion, incomplete_criterion, missing_evidence, unknown_evidence, invalid_timestamp, missing_timestamp, future_timestamp, stale, input_error. Messages are human-facing. --json validation reports use stdout; CLI usage errors use stderr.

Library

from task_handoff_check import load_handoff, validate_handoff

handoff = load_handoff("examples/complete.json")
report = validate_handoff(handoff)
assert report["ok"]
report = validate_handoff(
    handoff, as_of="2026-01-02T09:00:00Z", max_age_hours=24
)

validate_handoff returns issues for invalid documents and raises HandoffError for invalid freshness options. The library accepts max_age_hours as a finite, nonnegative integer or float; integers of any size are compared exactly, a float is compared exactly as the binary value it holds (so 0.1 is very slightly more than one tenth), and booleans and nonfinite values are rejected. load_handoff raises HandoffError for input errors, including numbers such as 1e999 that overflow Python's finite floating-point range. Validation does not mutate the document. Parser errors identify a location without repeating JSON content or the input filename.

Limits and development

Input must be a regular UTF-8 JSON file of at most 1 MiB; final-component symlinks, duplicate JSON keys and nonfinite numbers are rejected. Parent symlinks may be followed for the explicitly supplied input document. Use trusted local input locations; the input reader is not a filesystem sandbox.

The tool performs no authentication, authorization, link checking, ownership verification, work execution, scheduling, agent dispatch or compliance assessment. It never changes a task's state. See SECURITY.md.

python -m unittest discover -s tests -v

Tests use synthetic inputs and temporary directories. The pull-request workflow is configured for Python 3.10, 3.12 and 3.14 on Linux, plus Python 3.14 on macOS. Configured jobs are not executed by this change; workflow run results are the evidence of executed coverage. See CONTRIBUTING.md.

Contributing

Issues and small pull requests are welcome; see CONTRIBUTING.md. Run python -m unittest discover -s tests before opening a PR. Release history is in CHANGELOG.md.

License

MIT, see LICENSE.

Metadata

Release files for task-handoff-check 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for task-handoff-check 0.1.1
File Size Uploaded Provenance
task_handoff_check-0.1.1.tar.gz 19.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for task-handoff-check 0.1.1
File Interpreter ABI Platform
task_handoff_check-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 30.9 kB

Release files / task_handoff_check-0.1.1.tar.gz

Download URL task_handoff_check-0.1.1.tar.gz
Size 19.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b6f866e1dd0d0c3def125aa4847edfeb608abd2eca6e3b0b5ce776d924b9958a
BLAKE2b-256 checksum
How to use checksums
765ce6386b137372fba254daac576268325fcf397b7a349f7d1e24514b0b0c97
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release files / task_handoff_check-0.1.1-py3-none-any.whl

Download URL task_handoff_check-0.1.1-py3-none-any.whl
Size 11.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
50eb4152e5f6f3b466cf99795da475c4eb40f30e018f5bb651c4679d515af8c7
BLAKE2b-256 checksum
How to use checksums
e13c44f8a3a6b674f9a9c74d3ac0fe70c691be79eadffbfc079af1979af33e3f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Security for task-handoff-check 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page