# Decision Tracker

Record why a catalog item should be retained, held, revisited, or excluded, together with local evidence and a condition for revisiting the decision. This independent tool uses **Python's standard library only**, with no network, account, API key, or paid service.

## What it does and where to use it

Use it for document-retention reviews, product-catalog cleanup, training-library maintenance, asset audits, and editorial review queues. It turns informal decisions into a portable JSON history while keeping the original catalog and evidence untouched.

Each accepted decision contains a reason, a revisit condition, evidence paths, SHA-256 hashes, and the byte sizes of the cited files. The tool checks that evidence exists inside a directory you explicitly select. It records decisions; it does not judge whether evidence is truthful or sufficient, approve content quality, delete files, or change any online system.

## Install

Install Python **3.11 or newer**, then copy this folder or clone the parent repository. No extra Python packages or other tool folders are needed.

```console
python --version
python tool.py --help
```

On Windows, replace `python` with `py -3` if needed.

## Quick start

From the **repository root**:

```console
python 04-decision-tracker/tool.py --catalog 04-decision-tracker/examples/catalog.json --batch 04-decision-tracker/examples/batch.json --evidence-root 04-decision-tracker/examples/evidence --output outputs/decisions-catalog.json
```

From **inside this folder**:

```console
python tool.py --catalog examples/catalog.json --batch examples/batch.json --evidence-root examples/evidence --output outputs/decisions-catalog.json
```

The example retains one item and places another on hold. Open the result to see each current `decision` and its append-only entry in `actions`. Paths on the command line are relative to the terminal's current folder. Paths inside a decision's `evidence` list are relative to `--evidence-root`.

For a later decision, create a new batch with a new `batch_id`, use the latest result as `--catalog`, and choose a new output filename. Reusing a recorded batch ID is rejected to prevent duplicate or conflicting history. Existing actions are preserved, including actions written by other tools.

## Input format

Use UTF-8 JSON objects (UTF-8 BOM accepted). IDs are case-sensitive, unique, nonempty strings. Catalog and batch must have the same `catalog_id`.

Catalog (`--catalog`):

```json
{"catalog_id":"my-library","scope_ids":["item-1"],"entries":[{"id":"item-1","status":"needs_review","actions":[]}]}
```

`scope_ids` must contain every entry ID exactly once. Additional entry fields are retained. `status` and `actions` are optional and default to `needs_review` and an empty history. The Catalog Reconciler output is directly compatible, but not required.

Decision batch (`--batch`):

```json
{
  "catalog_id": "my-library",
  "batch_id": "review-001",
  "decisions": [{
    "id": "item-1",
    "status": "hold",
    "reason": "A reference needs confirmation.",
    "revisit_if": "An updated reference is available.",
    "evidence": ["notes/review.txt"],
    "unresolved_checks": ["Confirm the latest reference."]
  }]
}
```

The batch must contain at least one decision. Every ID must be inside the catalog's existing scope. Allowed statuses:

| Status | Intended use |
| --- | --- |
| `retain` | Keep an item in the current collection. |
| `hold` | Pause action pending specified unresolved checks. |
| `revisit` | Record an item for a future review under the stated condition. |
| `exclude` | Mark an item outside the current review/use case; no deletion occurs. |

Every decision requires nonempty `reason`, `revisit_if`, and `evidence`. `hold` also requires a nonempty list of `unresolved_checks`. Other statuses may optionally include that list. A later decision may change the current status; the previous action remains in history.

## Evidence and scope rules

- `--evidence-root` must be an existing directory.
- Every evidence entry must name an existing file by a relative path. Use `/` for portable paths.
- Absolute paths, missing files, directories, duplicate evidence paths within a decision, and resolved paths escaping the evidence directory are rejected. Symlinks that resolve outside the directory are rejected.
- File hashes identify the bytes read during the run. Store evidence unchanged alongside your reports if you need to reproduce the record later. The tool does not copy evidence into its output or monitor later changes.
- Duplicate catalog IDs, duplicate batch IDs within history, duplicate decision IDs, mismatched collections, or IDs outside scope stop the entire run before an output is written.

## Output and safeguards

The output is a new catalog JSON with updated per-entry `status`, current `decision`, appended `actions`, an updated timestamp, and a summary of statuses. Unaffected entries are retained. No quality-approval status is automatically inferred.

`--output` is mandatory. Existing outputs require `--overwrite`. Even with that flag, the output cannot be the input catalog, the input batch, or any evidence file. All source files remain unchanged.

Exit codes: **0** decisions recorded; **2** invalid input or file error. A rejected batch is not partially applied to the original catalog.

## Verify the installation

From this folder:

```console
python -m unittest discover -s tests -p "test_*.py" -v
```

From the repository root:

```console
python -m unittest discover -s 04-decision-tracker/tests -p "test_*.py" -v
```
