# FablePool Conformance Vectors — Format `fpcf-vectors/1` This document specifies the **test-vector format** consumed by any implementation of the FablePool wire format (spec `spec/02-wire-format/`). A second implementation should be able to load these vectors and check itself against the protocol using **only this document and the spec** — no knowledge of the Python reference code is required. Vectors are generated deterministically by the reference tool: ```sh python -m fpcf.cli generate --out vectors/generated --seed <64-hex-chars> ``` Generation is **byte-for-byte reproducible** for a given `(seed, generator version)` pair: all signing keys are derived from the seed and all timestamps are fixed. The canonical published suite uses the seed `5eed…` (the string `5eed` repeated 16 times). If you vendor the vectors into another repository, vendor `manifest.json` and every file it references, unmodified. ## Directory layout A vector suite is a directory containing: | Path | Meaning | |-----------------|------------------------------------------------------| | `manifest.json` | The suite index (described below). **Required.** | | *(other files)* | Vector payload files, at paths given by the manifest | Consumers **MUST** discover vector files via `manifest.json` and **MUST NOT** assume any particular subdirectory layout — the manifest's `file` fields are authoritative relative paths. ## Manifest format `manifest.json` is a JSON object: ```json { "format": "fpcf-vectors/1", "seed": "5eed5eed…", "vectors": [ { "name": "op-evidence-ingest-minimal", "file": "ops/valid/op-evidence-ingest-minimal.json", "kind": "operation", "expect": "valid", "description": "Smallest conforming evidence-ingest operation." }, { "name": "op-bad-signature", "file": "ops/invalid/op-bad-signature.json", "kind": "operation", "expect": "invalid", "error": "ERR_SIG_INVALID", "description": "Valid structure, signature bytes corrupted." } ] } ``` Fields: | Field | Type | Rules | |---------------|--------|--------------------------------------------------------------------| | `format` | string | Always `"fpcf-vectors/1"` for this version of the format. | | `seed` | string | 64 lowercase hex chars; the generation seed (informational). | | `vectors` | array | One entry per vector; order is not significant. | | `name` | string | Unique within the suite; stable across regenerations. | | `file` | string | Relative path (POSIX separators) to the payload file. | | `kind` | string | `"operation"` or `"log"` (see below). | | `expect` | string | `"valid"` or `"invalid"`. | | `error` | string | Present iff `expect == "invalid"`: an `ERR_*` code from spec §05. | | `description` | string | Human-readable; informational only. | ## Vector payloads ### `kind: "operation"` The file contains a **single signed operation envelope** as a JSON object (fields `type`, `body`, `sig`, `op_id`, author/key binding, etc. — see spec §02). For `expect: "valid"` vectors the file's bytes are the **canonical encoding** of the envelope, so they double as canonicalization test data: re-encoding the parsed value canonically must reproduce the file bytes exactly. A consumer **MUST**, for each operation vector: 1. Parse the file as JSON (strict UTF-8; duplicate keys are an error). 2. Validate the envelope against the schemas (spec `schemas/`). 3. Recompute the `op_id` over the canonical bytes and compare. 4. Verify the Ed25519 signature against the bound key. For `expect: "valid"` all steps must succeed. For `expect: "invalid"` at least one step must fail. ### `kind: "log"` The file contains `{"ops": [envelope, …]}` — a sequence of signed operations in transmission order. In addition to verifying every envelope as above, a consumer **MUST** check the append-only log rules (spec §03): per-author hash chaining (`prev` linkage), strictly monotonic per-author sequence numbers, and well-formed derivation-graph references. For `expect: "valid"` the whole log must verify. For `expect: "invalid"` verification must fail at some operation or chain link. ## Error-code matching When a vector is `expect: "invalid"`, the manifest's `error` field names the **primary** defect using the error registry in `spec/02-wire-format/05-versioning-errors-extensibility.md`. Conformance levels: - An implementation **MUST** reject every `expect: "invalid"` vector. - It **SHOULD** report an error whose code (or mapped category, per the registry's category table) matches the manifest's `error` field. - It **MAY** detect additional defects first if it performs checks in a different order; reporting a *different* legitimate `ERR_*` code for the same vector is a documented-divergence, not a failure, provided the vector is rejected. ## Coverage guaranteed by `fpcf-vectors/1` suites Every generated suite includes, at minimum: - At least one **valid** operation vector for **each** operation type: `evidence-ingest`, `claim-assert`, `correction`, `refutation`, `permission-grant`, `revocation`, `inference-call`. - Invalid operation vectors covering: schema violations (missing/unknown fields, wrong types, unknown operation type), signature corruption, `op_id` mismatch, and malformed identifiers. - At least one valid **log** vector with ≥ 3 operations exercising derivation-graph references (claims derived from evidence, corrections cascading). - Invalid log vectors covering: broken `prev` hash chains, non-monotonic sequence numbers, and dangling derivation references. One class of negatives cannot be expressed as parsed-JSON vectors: violations of the canonical **byte encoding** itself (e.g. unsorted keys, extra whitespace, `\u` escapes where literals are required), because a JSON parser normalizes them away. These appear as byte-level cases in the worked examples (spec §06) and are additionally exercised indirectly: a non-canonical re-encoding of a valid vector will fail step 3 (`op_id` recomputation over canonical bytes). ## Versioning The `format` string is bumped (`fpcf-vectors/2`, …) for any breaking change to this layout. New *optional* manifest fields may be added within a format version; consumers **MUST** ignore manifest fields they do not recognize.