Skip to main content

prodc

pipeline status PyPI version Python versions License: MIT

Product management as code. You write user needs in YAML. Each need lists the evidence that a real user asked for it, and the test that shows it is delivered. The files live in the repo, next to the code.

prodc reads the YAML files and your test suite, and shows what is missing. It does not call a model, run a UI, or replace your issue tracker.

It follows two rules:

  1. A need with no evidence is an opinion. Record who asked (an interview quote, a ticket, an email). If you don't, prodc marks the need as an opinion.
  2. You never mark a need as done by hand. You attach a proof, which points to a test, and prodc status computes the state: DELIVERED, PENDING, HOLE, or BROKEN.

Compared to other tools

  • Jira, Linear and other trackers store work items with a status that someone sets by hand. prodc stores needs and computes their status from tests. It has no assignees, sprints, or notifications. You can use both: a source can point to a GitLab ticket (kind = "gitlab", read-only), a proof can be any script (kind = "command"), and prodc sync runs a hook when a need changes state.
  • A PRD in Notion or a wiki is not checked after it is written. prodc files are validated on every load, linted in CI, and reviewed in merge requests.
  • You could keep the same YAML by hand. prodc adds the schema, proof checks against real test suites, the source kinds, the lint, and write commands that keep the files consistent.

prodc does not run your tests, call an LLM, or contact a tracker by itself. It does not model the full discovery process (opportunity-solution trees, testing several solutions in parallel). It records needs, their evidence, and the decisions made about them.

Install

pip install prodc          # or: uv add prodc

Python 3.12+. The only runtime dependency is PyYAML.

Quickstart

You need three files: the config, the needs, and a test.

prodc.toml

[prodc]
files = ["needs/*.yaml"]

[proofs.web]
kind = "gherkin"
paths = ["features/*.feature"]
delivered = ["@implemented"]
pending = ["@pending"]

needs/jordan.yaml

who: jordan
needs:
  - id: N-1
    text: "see which orders are past due at a glance"
    must: true
    sources:
      - kind: primary
        who: Jordan
        date: 2026-09-20
        quote: "I lose the past-due ones in the list"
    proofs:
      - "web:orders.feature#Past due orders are highlighted"

  - id: N-2
    text: "export the day's delivery run sheet"
    sources:
      - kind: secondary
        locator: issues.md#N-2
        date: 2026-09-21

  - id: N-3
    text: "implement a settings page"

features/orders.feature

Feature: Orders board

  @implemented
  Scenario: Past due orders are highlighted
    Given an order whose due date has passed
    When I open the orders board
    Then the order is highlighted as past due

Run prodc status (output trimmed):

$ prodc status
gaps: 2 HOLE  ·  1 opinion    coverage 1/3 (33%)

jordan    1 DELIVERED   2 HOLE
    provenance: 1 primary  ·  1 secondary  ·  0 assumption  ·  1 opinion

      N-2  HOLE            export the day's delivery run sheet          secondary
      N-3  HOLE            implement a settings page                    opinion
       ⚠ text may be a solution in disguise (starts with 'implement')
  !!  N-1  DELIVERED* 1/1  see which orders are past due at a glance    primary:Jordan
       ⚠ DELIVERED on an unattested proof link (warrant not reviewed)

N-1 is DELIVERED because a tagged scenario proves it. The * means the state comes from the tag, because no test results were loaded. N-2 has evidence but no proof, so it is a HOLE. N-3 has neither, so it is an opinion.

You fix these from the command line. Each write command changes one YAML file, validates the whole model again, and prints the diff.

# trace the opinion to evidence
prodc capture N-3 --kind primary --who Jordan --date 2026-10-01 \
    --quote "I keep changing the printer in three places"

# add the scenario to your suite, tagged @pending until it is green:
#     @pending
#     Scenario: Export the run sheet
# then point the need at it:
prodc set N-2 proofs+="web:orders.feature#Export the run sheet"

prodc status      # gaps: 1 PENDING · 1 HOLE   (N-2 is PENDING: the scenario exists)
                  # (skip writing the scenario and the proof points at nothing: BROKEN, lint exits 1)
prodc lint        # CI gate: exit 1 on a BROKEN proof or a must-have with no evidence

If you rename a scenario and do not update the need, the proof becomes BROKEN and prodc lint exits 1.

Features

  • Source kinds: primary (the user's own words), secondary (a report, a ticket, an internal doc), or assumption (a guess). A need with no primary or secondary source is an opinion. lint fails on a must-have opinion; status --strict fails on any opinion.
  • Proof adapters: Gherkin scenarios and tags, pytest node ids, file existence, human sign-off, prototypes, citations, eval metric gates, external result files, and a generic command. Give it cucumber-json or JUnit XML to use real test results instead of tags.
  • Five states: DELIVERED, PENDING, HOLE, BROKEN, UNRESOLVED. A HOLE does not fail the gate; BROKEN always does. A proof in a sibling repo that is not checked out is UNRESOLVED, not BROKEN.
  • Flows: a need with children rolls up its children, with seam proofs for the steps between them. One BROKEN child breaks the flow. The flow is DELIVERED only when every child and seam is.
  • tests/test_models.py builds a musil model of the lifecycle and checks every reachable state against the real resolver.
  • Write commands: set, capture, accept, attest, withdraw, add. They change only the edited need's block and keep the comments in the rest of the file. All take --dry-run and --expect-sha.
  • Read commands: status, show, brief, plan, report --format html (one self-contained HTML file), portfolio, and diff against a snapshot. They take --json. Exit codes: 0 ok, 1 gate failed, 2 config or load error.
  • prodc agent >> AGENTS.md adds instructions for a coding agent to act as the project's product manager. An agent may record assumption or secondary sources; primary is only for a real user's own words. prodc guide is a short guide for people.

prodc also has goals, releases, roles, principles, and decisions (model docs), and the commands dupes, recurrence, and suggest. See the docs for all of it.

Contributing

Toolchain, CI, and the release process live in CONTRIBUTING.md; build and test instructions for coding agents are in AGENTS.md. In short: make setup then make check (lint + typecheck + test); moon run :ci is what CI runs.

License

MIT © Jorge Cardona. See LICENSE.

Metadata

Release files for prodc 0.4.13

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

Source distribution (sdist)

Source distribution for prodc 0.4.13
File Size Uploaded Provenance
prodc-0.4.13.tar.gz 178.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for prodc 0.4.13
File Interpreter ABI Platform
prodc-0.4.13-py3-none-any.whl Python 3 none any Details

Total release size: 305.8 kB

Release files / prodc-0.4.13.tar.gz

Download URL prodc-0.4.13.tar.gz
Size 178.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a0b57dc6601635c4b90880623cbb888f9b35b7571e2d926cd9ad4d4d1f303b20
BLAKE2b-256 checksum
How to use checksums
bb4495a4a4a7ff68e7796679e16384b4e57caa446242b48023d3282e546ee32a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / prodc-0.4.13-py3-none-any.whl

Download URL prodc-0.4.13-py3-none-any.whl
Size 127.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eb16308162c1242884605cf7a65233ce5206586e8a77394319540184643ea3b1
BLAKE2b-256 checksum
How to use checksums
811ad8ab72a18de3daf28b667724b9b9f10b5743f66171ce81d2da1c721a3d5e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for prodc 0.4.13

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.4.13 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