at.tensei.mapping

tensei.at

{
  "$type": "com.atproto.lexicon.schema",
  "defs": {
    "fieldDefinition": {
      "description": "A field's declared type and authored requiredness (§3.1). `required` is always written; validation supplies no default.",
      "properties": {
        "required": {
          "type": "boolean"
        },
        "type": {
          "ref": "#fieldType",
          "type": "ref"
        }
      },
      "required": [
        "type",
        "required"
      ],
      "type": "object"
    },
    "fieldType": {
      "description": "A declared output type (§3.1). `namespace` is required when `kind` is key and names the key namespace (§3.6).",
      "properties": {
        "kind": {
          "enum": [
            "text",
            "text[]",
            "number",
            "bool",
            "datetime",
            "uri",
            "did",
            "ref",
            "media",
            "score",
            "key",
            "json"
          ],
          "type": "string"
        },
        "namespace": {
          "ref": "#keyNamespace",
          "type": "ref"
        }
      },
      "required": [
        "kind"
      ],
      "type": "object"
    },
    "fixedPoint": {
      "description": "A number that is not an integer, where no type is declared for it (§1.5): `value` is the number times 10⁶, rounded to six decimal places, so 0.4 is {value: 400000}. An integer is never written this way; it is written as it is. Wherever `$type` names this def, the object is that number. Not stated by the lexicon, and enforced by the reader: it holds no property but `$type` and `value`.",
      "properties": {
        "value": {
          "maximum": 999999999999999,
          "minimum": -999999999999999,
          "type": "integer"
        }
      },
      "required": [
        "value"
      ],
      "type": "object"
    },
    "guard": {
      "description": "A §5 leaf. `field` is a §4.2 source path. `value` is a scalar (a string, an integer, a #fixedPoint for any other number, or a boolean; never null or an object), or for in/notIn an array of scalars, and is absent for exists/notExists; the lexicon language has no type for a scalar of any kind, so `value` is not declared.",
      "properties": {
        "field": {
          "minLength": 1,
          "type": "string"
        },
        "op": {
          "ref": "#predicateOp",
          "type": "ref"
        }
      },
      "required": [
        "field",
        "op"
      ],
      "type": "object"
    },
    "keyNamespace": {
      "enum": [
        "isbn",
        "olwork",
        "goodreads",
        "hive",
        "tmdb",
        "url"
      ],
      "type": "string"
    },
    "main": {
      "description": "One source collection → user-shaped output (§4.1). Also defines the field, expression and presentation types that at.tensei.pipeline refers to.",
      "key": "tid",
      "record": {
        "properties": {
          "createdAt": {
            "description": "When the mapping was created: a datetime as §3.1 reads one, which accepts forms atproto's `datetime` format does not, so the format is not declared.",
            "type": "string"
          },
          "guards": {
            "description": "§5 predicate leaves over source paths (§4.1); no all/any/not groups.",
            "items": {
              "ref": "#guard",
              "type": "ref"
            },
            "type": "array"
          },
          "origin": {
            "description": "Present on an adopted copy: the exact version copied (§1.3). Attribution and update-check input; execution never reads it.",
            "ref": "#origin",
            "type": "ref"
          },
          "presentation": {
            "description": "For a standalone preview and to keep a migrated draft's choices; a pipeline's own presentation is authoritative after downstream edits (§3.8). Its entries must name fields in slots.",
            "ref": "#presentation",
            "type": "ref"
          },
          "schema": {
            "ref": "#schemaPin",
            "type": "ref"
          },
          "slots": {
            "description": "The output fields: an object keyed by field names the user chooses, any non-empty name and not limited to a preset's, each value a #mappingField. The lexicon language has no map type, so the names and values are stated here rather than declared.",
            "type": "unknown"
          },
          "source": {
            "description": "The NSID of the collection this mapping reads, and the only place its field names appear.",
            "format": "nsid",
            "type": "string"
          },
          "version": {
            "const": 1,
            "description": "The document version. A document without one is the Phase-2 fixed-preset form, which is migrated explicitly to version 1 (§8.3) and never published as it is.",
            "type": "integer"
          },
          "viewType": {
            "description": "The built-in preset this mapping started from, as attribution only. It never adds, limits or requires a field (§3.8).",
            "enum": [
              "review",
              "bookmark"
            ],
            "type": "string"
          }
        },
        "required": [
          "version",
          "source",
          "slots"
        ],
        "type": "object"
      },
      "type": "record"
    },
    "mappingExpression": {
      "description": "A source-mapping expression (§4.3): path {path}, constant {value}, operation {op, inputs, args?} or lookup {input, from, to}. Not stated by the lexicon: which properties each kind requires; `value`, any JSON value, in which a number that is not an integer is a #fixedPoint; `args`, whose count and types each operation fixes (a string separator for split, join and concat, a string find and replacement for replace, an optional string format for parseDate, a non-zero divisor for divide, none otherwise; a number that is not an integer is a #fixedPoint); the input count (one, or one or more for coalesce and concat); §4.2 path syntax; and a nesting depth of at most 64 levels.",
      "properties": {
        "from": {
          "ref": "#keyNamespace",
          "type": "ref"
        },
        "input": {
          "ref": "#mappingExpression",
          "type": "ref"
        },
        "inputs": {
          "items": {
            "ref": "#mappingExpression",
            "type": "ref"
          },
          "type": "array"
        },
        "kind": {
          "enum": [
            "path",
            "constant",
            "operation",
            "lookup"
          ],
          "type": "string"
        },
        "op": {
          "ref": "#operationName",
          "type": "ref"
        },
        "path": {
          "minLength": 1,
          "type": "string"
        },
        "to": {
          "ref": "#keyNamespace",
          "type": "ref"
        }
      },
      "required": [
        "kind"
      ],
      "type": "object"
    },
    "mappingField": {
      "description": "One output field of a mapping (§4.1): a #fieldDefinition plus how this source fills it. An unmapped field has no `expr`.",
      "properties": {
        "by": {
          "enum": [
            "constraint",
            "name",
            "stats",
            "model",
            "human"
          ],
          "type": "string"
        },
        "confidence": {
          "description": "Proposal metadata: a number from 0 to 1, in fixed point, the value times 10⁶ (§1.5), as a bare integer: 0.4 is 400000.",
          "maximum": 1000000,
          "minimum": 0,
          "type": "integer"
        },
        "expr": {
          "ref": "#mappingExpression",
          "type": "ref"
        },
        "normalization": {
          "ref": "#normalization",
          "type": "ref"
        },
        "required": {
          "type": "boolean"
        },
        "type": {
          "ref": "#fieldType",
          "type": "ref"
        }
      },
      "required": [
        "type",
        "required"
      ],
      "type": "object"
    },
    "normalization": {
      "description": "Source-specific input normalization (§4.1), not part of the shared field type. A mapped score field needs {kind: score, scale}, a mapped media field {kind: media, mediaKind}, and canonical {namespace} belongs on a uri field only.",
      "properties": {
        "kind": {
          "enum": [
            "score",
            "media",
            "canonical"
          ],
          "type": "string"
        },
        "mediaKind": {
          "enum": [
            "blob",
            "url"
          ],
          "type": "string"
        },
        "namespace": {
          "ref": "#keyNamespace",
          "type": "ref"
        },
        "scale": {
          "ref": "#scale",
          "type": "ref"
        }
      },
      "required": [
        "kind"
      ],
      "type": "object"
    },
    "operationName": {
      "description": "§4.3's closed list of operations. A new operation is a spec change, not an extension point.",
      "enum": [
        "split",
        "join",
        "wrap",
        "first",
        "trim",
        "lower",
        "replace",
        "divide",
        "parseDate",
        "coalesce",
        "concat"
      ],
      "type": "string"
    },
    "origin": {
      "description": "The original record an independent copy was made from (§1.3, §8.3).",
      "properties": {
        "cid": {
          "description": "The CID of the exact version copied. The `cid` format accepts any spelling of a CID; the validator accepts only the form atproto writes: version 1, in base32, over a SHA-256 digest.",
          "format": "cid",
          "type": "string"
        },
        "uri": {
          "description": "The record copied. Not stated by the lexicon, and enforced by the validator: exactly at://<DID>/<collection NSID>/<record key>, with a DID rather than a handle for its authority.",
          "format": "at-uri",
          "type": "string"
        }
      },
      "required": [
        "uri",
        "cid"
      ],
      "type": "object"
    },
    "predicateOp": {
      "description": "§5's closed list of operators.",
      "enum": [
        "eq",
        "ne",
        "lt",
        "lte",
        "gt",
        "gte",
        "contains",
        "notContains",
        "startsWith",
        "matches",
        "exists",
        "notExists",
        "in",
        "notIn"
      ],
      "type": "string"
    },
    "presentation": {
      "description": "How the output is shown (§3.8); it never changes data or requiredness. Entries are unique by `field`, each names a field of the shape it presents, and `display` is allowed on a score field only.",
      "properties": {
        "fields": {
          "items": {
            "ref": "#presentationField",
            "type": "ref"
          },
          "type": "array"
        },
        "layout": {
          "enum": [
            "list",
            "table",
            "review",
            "bookmark"
          ],
          "type": "string"
        }
      },
      "required": [
        "layout",
        "fields"
      ],
      "type": "object"
    },
    "presentationField": {
      "properties": {
        "display": {
          "enum": [
            "stars5",
            "stars10",
            "percent",
            "raw"
          ],
          "type": "string"
        },
        "field": {
          "minLength": 1,
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "visible": {
          "type": "boolean"
        }
      },
      "required": [
        "field",
        "visible"
      ],
      "type": "object"
    },
    "scale": {
      "description": "A source's score scale (§3.4), each bound in fixed point, the value times 10⁶ (§1.5), as a bare integer: a scale of 1 to 10 is 1000000 to 10000000. Not stated by the lexicon, and enforced by the validator: min < max.",
      "properties": {
        "max": {
          "maximum": 999999999999999,
          "minimum": -999999999999999,
          "type": "integer"
        },
        "min": {
          "maximum": 999999999999999,
          "minimum": -999999999999999,
          "type": "integer"
        }
      },
      "required": [
        "min",
        "max"
      ],
      "type": "object"
    },
    "schemaPin": {
      "description": "The schema version the mapping was written against (§4.1, §11). Not stated by the lexicon, and enforced by the validator: `nsid` is the mapping's `source`; a published pin has `cid` and no `hash`, an inferred one `hash` and no `cid`.",
      "properties": {
        "cid": {
          "description": "A published pin's version: the CID of the lexicon record. The `cid` format accepts any spelling of a CID; the validator accepts only the form atproto writes: version 1, in base32, over a SHA-256 digest.",
          "format": "cid",
          "type": "string"
        },
        "hash": {
          "description": "An inferred pin's version: `sha256:` and 64 lowercase hex digits, a form the lexicon language cannot state.",
          "minLength": 1,
          "type": "string"
        },
        "nsid": {
          "format": "nsid",
          "type": "string"
        },
        "provenance": {
          "enum": [
            "published",
            "inferred"
          ],
          "type": "string"
        },
        "resolvedAt": {
          "description": "When the version was resolved: a datetime as §3.1 reads one, which accepts forms atproto's `datetime` format does not, so the format is not declared.",
          "type": "string"
        }
      },
      "required": [
        "nsid",
        "provenance"
      ],
      "type": "object"
    }
  },
  "description": "A Tensei mapping, version 1 (spec §4): records of one source collection mapped into output fields the user authors. Built-in presets are starting points, never a contract: any field name is allowed, and a field is mandatory only when the user marks it `required`. The atproto data model has integers only, so a number in the record is an integer written as it is, or any other number in fixed point, the value times 10⁶ (spec §1.5): a bare integer where this lexicon declares it — a #mappingField's `confidence` and a #scale's `min` and `max` — and a #fixedPoint everywhere else. Where the lexicon language cannot state a rule the version-1 validator enforces, the property is left permissive here and the rule is named in its description.",
  "id": "at.tensei.mapping",
  "lexicon": 1
}

Validate Record

Validate a record against at.tensei.mapping

Validation Options
Treat any remaining unresolved references as valid

Metadata

DID
did:plc:7fk7ea7vlnlpzgy225x66vro
CID
bafyreig4pmoxl4oqrypl3oicthhd5zs537kge76dut7warmwf3a63mt6vy
Indexed At
2026-10-16 13:31 UTC
AT-URI
at://did:plc:7fk7ea7vlnlpzgy225x66vro/com.atproto.lexicon.schema/at.tensei.mapping

Version History (2 versions)

Similar Lexicons

Lexicons whose schemas describe something close to this one. Check them before publishing a new one that overlaps.

  • A Tensei pipeline, version 1 (spec §6, §7): source nodes reading through mappings, downstream nodes over the fields the user authored, and one output. Shapes on wires are derived from the graph, never stored. The atpr…

Lexicon Garden

@