Metadata-Version: 2.4
Name: postmortem-cli
Version: 0.1.0
Summary: Postmortems produce action items that quietly vanish. Parse them, track them, prove they shipped.
Author: mgkvibez
License: MIT
Keywords: postmortem,incident-response,sre,reliability,github-issues,cli
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# postmortem-cli

Postmortems end with a list of actions. Those actions are the only part of the document that
is supposed to change anything, and they are the part nothing tracks: written in prose, pasted
into a ticket nobody reopens, and quietly dropped at the next incident.

`pmtracker` reads the action items out of your postmortems, gives each one an owner and a
date, opens a tracked issue for it, and tells you the thing no dashboard will: **the same
action promised after every incident.**

## The demo

Two postmortems, one month apart. The second incident is the first one again.

```
$ pmtracker parse examples/
  [ ] Add an alert for queue depth above 500                     @adaenonche92    2026-10-21
  [ ] Add an alert for queue depth above 500 on web-2            @adaenonche92    2026-10-28
  [x] Raise the worker count from 4 to 12                        @sam             no date
  [ ] Document the runbook for queue saturation                  UNASSIGNED       no date
  [ ] Add an alert for queue depth above 500                     @adaenonche92    2026-11-05
  [ ] Write an incident review template                          UNASSIGNED       no date

$ pmtracker recurrence examples/
  3x  STILL OPEN Add an alert for queue depth above 500
       promised in: examples/2026-10-14-queue-backlog.md, examples/2026-10-29-queue-backlog-again.md
```

## Install

```bash
pipx install postmortem-cli
```

The distribution is called `postmortem-cli` while the command is `pmtracker`, which is the
usual split in this project family: a descriptive name to install, a short one to type. Python
3.9+, **zero runtime dependencies**. `gh` is needed only for `sync` and `plan --repo`.

## Commands

| Command | What it does | Exit |
|---|---|---|
| `pmtracker parse PATH...` | list the actions it found | 1 if there are none |
| `pmtracker plan PATH --repo O/R` | show what `sync` would create | 1 if any action lacks an owner or a date |
| `pmtracker sync PATH --repo O/R` | create the missing issues | 1 if any create failed |
| `pmtracker recurrence PATH...` | group actions that repeat | 1 if a repeat is still open |

`PATH` can be a file or a directory; directories are read recursively for `*.md`. Every
command takes `--json` and emits the same data as a structure, for a bot or an agent.

Exit codes: `0` clean, `1` something needs attention, `2` usage error or `gh` unavailable.
Point a CI job at `recurrence` and a repeated promise fails the build, which is the only
mechanism that has ever made an action item urgent.

## Two shapes are read

Both are common in real documents, so both work:

```markdown
## Action items            <- a section: every bullet under it is an action
- [ ] Add an alert for queue depth @ada due: 2026-11-05
- [x] Raise the worker count (owner: sam)

- [ ] A checkbox anywhere else in the file also counts
```

Owners are `@handle` or `(owner: name)`. Dates are `due: YYYY-MM-DD`, `by YYYY-MM-DD` or
`deadline: YYYY-MM-DD`.

**An action with no owner is reported as `UNASSIGNED`, never assigned to whoever wrote the
document.** That inference is exactly how an action gets transferred to nobody, so this tool
refuses to make it and counts it as a problem instead.

## Sync creates real issues, and re-running is safe

```bash
pmtracker sync incidents/ --repo my-org/my-repo            # dry run, lists what it would do
pmtracker sync incidents/ --repo my-org/my-repo --apply    # actually creates them
```

Nothing is written without `--apply`. Each issue body carries a hidden marker derived from the
action's signature, so running `sync` again skips what already exists instead of filing
duplicates. If the same action appears twice in your documents, the second copy is marked
`twice` and skipped rather than creating a second issue: that duplication is a finding about
the documents, not a job for the API.

## How recurrence is decided

Each action is reduced to a signature: lowercase content words, filler removed, first six kept.
Signatures are merged when one is a token prefix of the other, so "add an alert for queue
depth" and "add an alert for queue depth on web-2" are the same promise, which is what the
first version of this tool got wrong.

The signature is deliberately lossy. It is a fingerprint for repetition, not for identity: two
different actions that share a six-word opening will be reported together. That is a fair
trade for a report you read by eye, and the alternative, exact matching, hid every repeat that
mattered.

## What this deliberately does not do

- **It does not host anything.** No web UI, no database, no dashboard. Markdown in, issues out,
  which is what makes it usable in a repo that already has CI and GitHub.
- **"Shipped" means the issue is closed.** Following a fix through to the merge commit needs a
  linked pull request, and a closed issue is the honest proxy we can check from here. A team
  that closes issues without shipping gets a report that says so.
- **It will not parse every postmortem format.** It reads Markdown sections and checkboxes. A
  postmortem living in Confluence, Notion or a Google Doc is out of scope until someone exports
  it.
- **It has no opinion on incident severity, timelines or blame.** Only the actions.

## Tests

```bash
python -m pytest -q      # 26 tests
bash scripts/verify.sh   # the release gate
```

## License

MIT.
