# FablePool CLI Reference This document specifies the user-facing command-line interface of the FablePool reference node: every command, every flag, the machine-readable (`--json`) output contract, and process exit codes. It is the contract the integration tests in `tests/` assert against — if the CLI changes, this document and those tests change together. The CLI is the inspection-and-refutation surface for **your own** node. It never talks to the network; every command operates on a local node directory. --- ## Invocation ``` fablepool [--node-dir PATH] COMMAND [OPTIONS] [ARGS] python -m fablepool [--node-dir PATH] COMMAND [OPTIONS] [ARGS] ``` Both forms are equivalent; `python -m fablepool` is useful when the console script is not on `PATH` (e.g. inside test harnesses). ### Node directory resolution Every command operates on exactly one node directory, resolved in this order: 1. `--node-dir PATH` (global flag, before the command name) 2. `FABLEPOOL_NODE_DIR` environment variable 3. `~/.fablepool` (default) The node directory contains the node identity (signing keys), the append-only operation log, and the derived-state store. It is safe to point the CLI at a throwaway directory; `seed-demo` will initialize it. ### Output modes Every read command and every mutating command supports `--json`. With `--json`, the command writes **exactly one JSON document to stdout** and nothing else to stdout (diagnostics go to stderr). Without `--json`, output is human-readable text and its exact formatting is *not* a stability contract — only the `--json` form is. The single exception is `export`, whose output is JSON Lines (one signed operation envelope per line) in both modes; it has no `--json` flag because it is always machine-readable. ### Exit codes | Code | Meaning | |------|---------| | `0` | Success. | | `1` | Runtime error: unknown claim/evidence/topic id, refused state transition (e.g. refuting an already-refuted claim), unreadable node directory. A diagnostic is written to stderr. | | `2` | Usage error: unknown command, missing required argument, bad flag value. (Standard `argparse` behavior.) | | `3` | Audit failure: `audit` ran to completion but found at least one operation that does not verify (bad signature, broken hash chain, malformed envelope). | --- ## Commands ### `fablepool init` Initialize an empty node: generate the node identity (Ed25519 signing key) and create an empty operation log. Idempotent: running it on an already-initialized node is a no-op with a notice on stderr. Most users never run `init` directly — `seed-demo` and mutating commands initialize on demand. ``` fablepool init [--json] ``` JSON output: `{"node_id": "", "created": true|false}`. --- ### `fablepool seed-demo` Initialize the node (if needed), ingest the bundled sample dataset (calendar events, notes, mock photo metadata — the same fixtures used by the import-adapter milestone), and run the derivation engine so the node contains a realistic graph of evidence, claims, and provenance. ``` fablepool seed-demo [--force] [--json] ``` | Flag | Effect | |------|--------| | `--force` | Re-seed even if the node already contains operations. Without it, seeding a non-empty node fails with exit code 1. | JSON output: `{"node_id": ..., "evidence": , "claims": , "ops": }`. --- ### `fablepool topics` List the topics under which claims are grouped, with per-topic counts. ``` fablepool topics [--json] ``` JSON output contract: ```json {"topics": [{"topic": "schedule", "claims": 4, "active": 3}, ...]} ``` Each entry carries at least `topic` (string). Counts are informational. --- ### `fablepool claims` List claims, optionally filtered. ``` fablepool claims [--topic TOPIC] [--status STATUS] [--min-confidence X] [--json] ``` | Flag | Effect | |------|--------| | `--topic TOPIC` | Only claims in the given topic. Unknown topic → exit 1. | | `--status STATUS` | One of `active`, `refuted`, `invalidated`, `superseded`, `all`. Default: `active`. | | `--min-confidence X` | Only claims with confidence ≥ X (0.0–1.0). | JSON output contract: ```json {"claims": [ {"claim_id": "...", "topic": "...", "statement": "...", "confidence": 0.82, "status": "active"}, ... ]} ``` Each entry carries at least `claim_id`, `topic`, `statement`, `confidence`, and `status`. --- ### `fablepool show CLAIM_ID` Full detail for one claim: statement, topic, confidence, status, the operation that asserted it, direct supporting evidence/claims, and direct dependents. ``` fablepool show CLAIM_ID [--json] ``` Unknown `CLAIM_ID` → exit 1. JSON output includes at least `claim_id`, `statement`, `topic`, `confidence`, `status`, `supports` (list of input claim/evidence ids) and `dependents` (list of downstream claim ids). --- ### `fablepool why CLAIM_ID` The derivation explanation: a tree from the claim down through every intermediate claim to the raw evidence records, with the rule that produced each step and the confidence contributed at each step. This is the answer to "why do you believe this about me?". ``` fablepool why CLAIM_ID [--depth N] [--json] ``` | Flag | Effect | |------|--------| | `--depth N` | Limit the explanation tree to N levels. Default: unlimited (down to raw evidence). | In human mode the tree is rendered with indentation and per-node confidence. In JSON mode the output is a recursive structure: ```json {"claim_id": "...", "statement": "...", "confidence": 0.82, "rule": "...", "inputs": [ {"claim_id"|"evidence_id": "...", ...recursive...} ]} ``` Leaves are evidence nodes carrying `evidence_id`, `source` (adapter name, e.g. `calendar`, `notes`, `photos`), and a short `summary`. --- ### `fablepool evidence EVIDENCE_ID` Inspect a raw evidence record: its source adapter, ingestion operation, content summary, and the claims it supports. ``` fablepool evidence EVIDENCE_ID [--raw] [--json] ``` | Flag | Effect | |------|--------| | `--raw` | Also print the original ingested payload verbatim. | Unknown `EVIDENCE_ID` → exit 1. --- ### `fablepool correct CLAIM_ID --value VALUE` Record a user correction: the claim is **superseded** by a user-asserted claim with the corrected value (confidence 1.0, provenance = the user's correction operation), and every downstream claim that depended on the old value is **invalidated** and queued for re-derivation. The original claim is never deleted — corrections are append-only operations like everything else. ``` fablepool correct CLAIM_ID --value VALUE [--reason TEXT] [--json] ``` JSON output contract: ```json {"op_id": "...", "claim_id": "", "new_claim_id": "", "status": "superseded", "cascade": ["", ...]} ``` `cascade` is always present (possibly empty). Exit 1 if the claim does not exist or is not in a correctable state. --- ### `fablepool refute CLAIM_ID` Record a user refutation: the claim is marked **refuted** and every claim derived from it (transitively) is **invalidated**. As with `correct`, this appends operations; nothing is deleted. ``` fablepool refute CLAIM_ID [--reason TEXT] [--json] ``` JSON output contract: ```json {"op_id": "...", "claim_id": "...", "status": "refuted", "cascade": ["", ...]} ``` Exit 1 if the claim does not exist or is already refuted. --- ### `fablepool export` Serialize operations in the wire format (milestone #2): JSON Lines, one signed operation envelope per line, in log order. This is the interoperability surface — another implementation should be able to replay the export and reconstruct the same graph. ``` fablepool export [--topic TOPIC] [--out FILE] ``` | Flag | Effect | |------|--------| | `--topic TOPIC` | Export only the operations needed to reconstruct the claims of the given topic: the claim-assertion operations, their full derivation ancestry, the referenced evidence-ingestion operations, and any corrections/refutations touching them. A topic with no claims → exit 1. | | `--out FILE` | Write to FILE instead of stdout. | **Wire-format export contract** (asserted by `tests/test_cli_export_audit.py`): * Every line is a single JSON object (an operation envelope). * Every envelope carries at least: `op_id` (string, unique), `kind` (string), `author` (string, signer's public id), `sig` (string, signature). Envelopes may carry additional fields (`ts`, `prev`, payload body, etc.) as specified in `docs/wire-format` from milestone #2. * A full export (no `--topic`) emits **every** operation in the log — its line count equals the `ops` count reported by `audit`. * Export is **deterministic**: exporting an unchanged node twice produces byte-identical output. * A topic-subset export is always a subset of the full export (same envelopes, same order, fewer of them). --- ### `fablepool audit` Verify the entire operation log: envelope well-formedness, signature validity for every operation, and hash-chain integrity. This is the "prove to me my history hasn't been tampered with" command. ``` fablepool audit [--json] ``` Exit `0` if every operation verifies; exit `3` if any operation fails (the audit still runs to completion and reports all failures). JSON output contract: ```json {"ok": true, "ops": 42, "verified": 42, "failures": []} ``` On failure, `ok` is `false` and `failures` lists `{"op_id": ..., "error": ...}` entries. --- ### `fablepool log` Browse the operation log itself (oldest first). ``` fablepool log [--limit N] [--kind KIND] [--json] ``` | Flag | Effect | |------|--------| | `--limit N` | Show at most N operations. | | `--kind KIND` | Filter by operation kind (e.g. evidence ingestion, claim assertion, refutation). | JSON output contract: ```json {"ops": [{"op_id": "...", "kind": "...", "author": "...", "ts": "...", "summary": "..."}, ...]} ``` Each entry carries at least `op_id` and `kind`. --- ### `fablepool shell` Interactive REPL over the same node. Inside the shell, the commands above are available without the `fablepool` prefix (`topics`, `claims`, `show`, `why`, `evidence`, `correct`, `refute`, `export`, `audit`, `log`), plus `help [COMMAND]` and `quit`/`exit`. The shell keeps a "last listed" context so claims can be addressed by their list index as well as by id. Errors inside the shell never exit the process; they print the same diagnostics the one-shot commands would, and the shell continues. The shell is line-oriented (built on the standard library, no terminal takeover), so it works over SSH and in dumb terminals, and its transcripts are copy-pasteable. --- ## Stability * The `--json` contracts and exit codes above are stable within this milestone series; additive changes (new fields, new optional flags) are allowed, removals or renames are not. * Human-readable output is for humans and may change freely. * The wire-format export is governed by the milestone #2 specification; this CLI adds no fields of its own to envelopes.