# Catalog Reconciler

Match a saved catalog to local records and preserve a stable review list over time. This independent command-line tool uses **Python's standard library only**. It needs no account, API key, network connection, or paid service.

## What it does and where to use it

Use it when auditing document libraries, product catalogs, training resources, media collections, or any records with stable IDs. It accepts exported JSON; it does not fetch records from an online platform.

- Records the original title and description on the first run.
- Matches explicitly supplied ID pairs and separately lists title-based candidates.
- Identifies records missing from a later snapshot without discarding their history.
- Keeps the original scope and per-entry status/actions when a previous result is supplied.
- Lists newly encountered IDs in `ignored_new_ids` so they cannot silently expand an ongoing review.

A title candidate is a suggestion, not proof that two records represent the same content. An explicit mapping records your supplied assertion; the tool does not inspect file contents. Local IDs and titles are sufficient; no media files are needed.

## Install

Install Python **3.11 or newer** and copy this folder, or clone the parent repository. No `pip install` step is required. The folder is independent of the other tools.

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

If Windows exposes Python as `py`, replace `python` with `py -3` in these commands.

## Quick start

From the **repository root**:

```console
python 02-catalog-reconciler/tool.py --snapshot 02-catalog-reconciler/examples/snapshot.json --local 02-catalog-reconciler/examples/local.json --mapping 02-catalog-reconciler/examples/mapping.json --output outputs/catalog.json
```

From **inside this folder**:

```console
python tool.py --snapshot examples/snapshot.json --local examples/local.json --mapping examples/mapping.json --output outputs/catalog.json
```

Paths are relative to your terminal's current folder. Output parents are created automatically. The example produces three catalog entries: an explicit match, a title-only candidate, and an unmapped record.

For a subsequent snapshot, use the prior result and choose a new output filename:

```console
python tool.py --snapshot examples/snapshot.json --local examples/local.json --mapping examples/mapping.json --previous outputs/catalog.json --output outputs/catalog-next.json
```

Supply the full current local-record and mapping files on each run; `local_evidence` is recalculated from the files supplied on that run. To begin a new scope, omit `--previous` and write a separate catalog.

## Input format

All files are UTF-8 JSON objects; a UTF-8 BOM is accepted. IDs must be nonempty unique strings within each list. IDs are case-sensitive. Titles must be nonempty strings.

Snapshot (`--snapshot`, required):

```json
{"catalog_id":"my-library","items":[{"id":"item-1","title":"Quick Start","description":"Instructions."}]}
```

`description` is an optional string and defaults to empty. `catalog_id` is your stable identifier for this collection.

Local records (`--local`, optional):

```json
{"items":[{"id":"local-1","title":"Quick Start"}]}
```

Mappings (`--mapping`, optional):

```json
{"mappings":[{"id":"item-1","local_id":"local-1"}]}
```

Every mapped local ID must exist in the supplied local records. Title matching ignores case and collapses whitespace, but does not remove punctuation or words. A mapping takes precedence over title candidates in the confidence label.

`--previous` accepts an earlier output (including one updated by the Decision Tracker), with the same `catalog_id`. Its `scope_ids` must contain every entry ID exactly once. All existing per-entry fields are retained except refreshed title, description, presence, and local evidence. The summary counts each entry's `status`.

## Output and safeguards

The result contains `catalog_id`, `scope_ids`, timestamps, a status `summary`, `ignored_new_ids`, and `entries`. Each entry contains original/current metadata, status/actions, snapshot presence, and local matching evidence. On a first run, statuses are `needs_review`.

Inputs are never modified. An explicit `--output` is mandatory. Existing outputs are refused unless `--overwrite` is supplied; even then, the output cannot be any input file. Duplicate IDs, inconsistent scope, unknown mapping targets, or collection mismatches stop the run.

Exit codes: **0** completed; **2** invalid input or file error. This tool makes no quality approval and performs no publishing or file deletion.

## 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 02-catalog-reconciler/tests -p "test_*.py" -v
```
