Skip to content

Docket 0.1.3

Choose a tag to compare

@github-actions github-actions released this 19 Oct 16:51
· 43 commits to main since this release

[0.1.3] - 2026-10-19

Fixed

  • Every receipt since 0.1.0 named a tool version that was not running. The version was written
    three times — src/docket/__init__.py's __version__, src/docket/receipt.py's
    TOOL_VERSION, and src/docket/supervisor.py's DOCKET_TOOL_VERSION — and each had to be
    edited by hand on every release. Two of the three were still 0.1.0 when the distribution
    installed 0.1.2, so docket --version said one thing, pip show said another, and the
    tool.version field in every receipt named a release that was not the one that wrote it. A
    receipt that misreports its author makes a false claim about provenance, which is the one thing
    a receipt exists to make checkable. All three now import from a new src/docket/_version.py,
    which derives the version from the installed distribution, falls back to the nearest
    pyproject.toml, and then to unknown. FORMAT_VERSION moved there for the same reason. The
    guard that had checked only __init__.py now scans every module under src/ for a version
    literal, in both the assignment and the dict-entry form, because scanning one file is what let
    the other two literals survive the first fix.

  • examples/custom-agent/agent_sdk.py crashed on the command its own README advertised. Under
    docket run, the session attaches to the supervised run and session.ledger is None by
    design — the supervisor is the only writer to that file, because two writers would produce two
    competing hash chains. The example printed session.ledger.path unconditionally, so the
    supervised command raised AttributeError while the standalone one worked. Only the standalone
    path was tested. The example now branches on session.attached and says what is true in each
    case, and tests/test_examples.py drives the supervised path and asserts the agent's own events
    reached the run's ledger labelled reported_by: "agent" and that the receipt verifies.

  • The Typing :: Typed classifier was a claim with nothing behind it. pyproject.toml
    declared the classifier and the wheel shipped no py.typed, so no consumer's type checker
    would read the package's annotations at all. The package was annotated all along — and 65 of
    those annotations were wrong, which nobody could see because nothing asked. py.typed now
    ships, mypy runs in the lint job, and the 65 errors are fixed: narrowing that did not survive
    a second method call, __exit__ annotated bool where it always returns False, a signing
    key accepted and then used as any key type, a metrics dict annotated dict[str, int] that
    holds a decimal string, and the rest. A guard test fails if the marker disappears while the
    classifier stays.

  • docket keygen did not exist, so docket run --sign had no way to produce a key.
    src/docket/signing.py could generate an Ed25519 keypair from the first release and nothing
    called it: no command, no documentation. The only way to sign a receipt was to already know
    the library function. docket keygen [PATH] [--force] writes the keypair at mode 0600, prints
    the public key, refuses to replace an existing key without --force (replacing a signing key
    invalidates every receipt signed with the old one), and exits 70 naming the install when the
    optional cryptography package is absent. Documented in the CLI reference, and tested end to
    end: keygen, sign, verify, and a tampered receipt that fails both the totals and the signature.

  • Two asserts were added to narrow a type, and bandit refused them. An assert is
    removed under python -O, and a tool whose whole claim is that it does not skip checks cannot
    rest one on a statement the interpreter is entitled to delete. verify.py now tests
    chain_problem directly — which is the narrowing that was wanted anyway, and drops the
    chain_ok alias that made the tuple unreachable — and policy.py raises for the case that
    cannot happen rather than asserting it. The line citations in docs/testing.md moved with the
    code and were re-checked against the harness's site list again.

  • Eight definitions in the package were dead code. generate_keypair was the visible one —
    docket run --sign was a flag with no way to produce a key, and the function that could have
    produced one was invisible. Seven more had the same shape: short_hash, mb,
    money_or_zero, stable_seed, is_known, default_price_table, price_document_digest
    and _gzip_bytes, each a one- or two-line helper with no caller anywhere in the tree. Dead
    code is not harmless in a project whose pitch is that a receipt says what the tool did: a
    package that carries functions nothing runs is making the same kind of claim about itself. All
    eight are deleted, and tests/test_supply_chain.py now fails when a top-level definition in
    src/ appears nowhere else in src/, tests/, tools/, examples/ or verify-js/.

  • A signing key of the wrong type produced an AttributeError from inside cryptography.
    load_pem_private_key returns a union of every key type the library supports, and most of
    them have no .sign(data). The call now refuses a non-Ed25519 key with a message naming the
    file and the key it holds.

  • Every link in the README was broken on the PyPI page. README.md is the package's long
    description, and PyPI renders it with no repository around it, resolving a relative target
    against https://pypi.org/project/docket-ai/. All 29 relative links therefore 404ed there while
    resolving perfectly on GitHub — invisible from the repository, which is why it survived three
    releases. They are now absolute: a target the documentation site publishes points at the site,
    so a reader arriving from PyPI lands in the documentation rather than in raw markdown, and
    LICENSE points at the repository. Two guards were added: one fails on any relative target, one
    fetches every absolute URL and fails on a non-200.

Changed

  • Documentation in [project.urls] pointed at a GitHub directory listing. It now points at the
    live documentation site, which is what a reader arriving from PyPI is looking for.

  • Two public properties replace a private attribute an example reached into:
    Session.docket_number and Session.ledger_path, the latter returning None when a
    supervisor owns the ledger rather than a path this session does not write to.

  • The line citations in docs/testing.md were re-based onto the code as it now stands. Each one
    was re-checked against tools/mutate.py's site list, so every number the page quotes is a
    line the mutation harness would actually mutate.

  • The conformance vectors were regenerated, because they carry tool.version and the spec states
    that vectors move with the implementation in the same commit. Only the version string and the
    two receipt_core_sha256 digests that cover it changed; all three implementations still agree
    (Python and Node: 88 correct, 0 wrong; Node's own suite: 100/100).