{ "_note": "🤖 The card schema contract (web/static/cards.js normalizeCard ↔ LChatCore Card.normalize). Each vector's `card` is the decrypted payload.card as received off the wire; `want` is the canonical normalized form, or null when the card must FAIL CLOSED (the client renders the message's plain text instead). Both renderers must agree on every vector — a mismatch means web and native have drifted, which is a hard error.", "schema": 1, "vectors": [ { "name": "morning digest — the canonical full card", "card": { "schema": 1, "blocks": [ { "b": "heading", "text": "Tuesday 7 July", "sub": "NYT · RTÉ · The Guardian" }, { "b": "section", "title": "Ireland", "blocks": [ { "b": "item", "text": "Budget talks resume as coalition splits on USC cut", "source": "RTÉ", "url": "https://www.rte.ie/news/1" }, { "b": "item", "text": "Dublin Airport cap ruling expected Thursday", "source": "The Guardian", "url": "https://www.theguardian.com/2" } ] }, { "b": "divider" }, { "b": "kv", "rows": [["Reading time", "3 min"], ["Items", "9"]] }, { "b": "chips", "chips": [{ "label": "Open RTÉ", "url": "https://www.rte.ie/news/" }] } ] }, "want": { "schema": 1, "blocks": [ { "b": "heading", "text": "Tuesday 7 July", "sub": "NYT · RTÉ · The Guardian" }, { "b": "section", "title": "Ireland", "blocks": [ { "b": "item", "text": "Budget talks resume as coalition splits on USC cut", "source": "RTÉ", "url": "https://www.rte.ie/news/1" }, { "b": "item", "text": "Dublin Airport cap ruling expected Thursday", "source": "The Guardian", "url": "https://www.theguardian.com/2" } ] }, { "b": "divider" }, { "b": "kv", "rows": [["Reading time", "3 min"], ["Items", "9"]] }, { "b": "chips", "chips": [{ "label": "Open RTÉ", "url": "https://www.rte.ie/news/" }] } ] } }, { "name": "minimal card — one text block", "card": { "schema": 1, "blocks": [{ "b": "text", "text": "Hello." }] }, "want": { "schema": 1, "blocks": [{ "b": "text", "text": "Hello." }] } }, { "name": "optional fields omitted stay omitted", "card": { "schema": 1, "blocks": [ { "b": "heading", "text": "Plain heading" }, { "b": "item", "text": "No source, no link" }, { "b": "list", "items": ["one", "two"] } ] }, "want": { "schema": 1, "blocks": [ { "b": "heading", "text": "Plain heading" }, { "b": "item", "text": "No source, no link" }, { "b": "list", "items": ["one", "two"] } ] } }, { "name": "unknown extra fields on a block are dropped, not fatal", "card": { "schema": 1, "blocks": [{ "b": "text", "text": "hi", "future_hint": "compact" }] }, "want": { "schema": 1, "blocks": [{ "b": "text", "text": "hi" }] } }, { "name": "unknown schema major fails closed", "card": { "schema": 2, "blocks": [{ "b": "text", "text": "from the future" }] }, "want": null }, { "name": "missing schema fails closed", "card": { "blocks": [{ "b": "text", "text": "hi" }] }, "want": null }, { "name": "unknown block type fails the whole card closed", "card": { "schema": 1, "blocks": [ { "b": "text", "text": "fine" }, { "b": "hologram", "text": "not yet" } ] }, "want": null }, { "name": "nested sections fail closed (one level only)", "card": { "schema": 1, "blocks": [ { "b": "section", "blocks": [{ "b": "section", "blocks": [{ "b": "text", "text": "deep" }] }] } ] }, "want": null }, { "name": "empty blocks fails closed", "card": { "schema": 1, "blocks": [] }, "want": null }, { "name": "item with a non-https url fails closed", "card": { "schema": 1, "blocks": [{ "b": "item", "text": "x", "url": "javascript:alert(1)" }] }, "want": null }, { "name": "chip without a url fails closed (v1 chips are links only)", "card": { "schema": 1, "blocks": [{ "b": "chips", "chips": [{ "label": "Approve", "emit": { "type": "x" } }] }] }, "want": null }, { "name": "kv row that is not a pair fails closed", "card": { "schema": 1, "blocks": [{ "b": "kv", "rows": [["k", "v", "extra"]] }] }, "want": null }, { "name": "empty text fails closed", "card": { "schema": 1, "blocks": [{ "b": "text", "text": "" }] }, "want": null }, { "name": "list with a non-string item fails closed", "card": { "schema": 1, "blocks": [{ "b": "list", "items": ["ok", 7] }] }, "want": null }, { "name": "card that is not an object fails closed", "card": "just a string", "want": null } ] }