Return the catalog record for one substrate data source. Compact by default (grain, queryable, claim ceiling, freshness SLA, top caveats, recommended next action, columns); pass verbosity='full' for the previous full record: description, row count, provenance chain reference, context doc path, known gaps, and the full source_product_card. IDENTIFIERS: accepts the PUBLIC source_code that find_data_sources returns (e.g. cms_ma_plan_payment) as well as a table FQN (e.g. vlada_curated.fact_rate) or a catalog source_id -- all three resolve to the same card. Also accepts a data-point ID, returning its exact bound table plus the selected point's units, grain, nulls and execution route. When the card includes a coverage-check recipe, run its stated query to inspect retained editions; it is not measured coverage. Do not infer all-history coverage from current-only rows. Avoid extra schema calls when this card already supplies the needed columns. A RETIRED source answers with a redirect (status retired + superseded_by + the successor's fqn), never a card. COLUMNS (1.1.0): the card's columns field lists each column's name and type (and a one-line note where the source publishes one), read from a committed snapshot -- never a live Glue call -- so a caller no longer has to SELECT * LIMIT n on query_substrate just to learn a table's shape. A source not yet in the snapshot reads columns_unavailable: run snapshot rather than silently omitting the field; run ops/scripts/snapshot_catalog_columns.py to populate it. WHAT TO QUERY (2026-09-18): every card carries a block saying what to type. It states the literal fully-qualified table name the raw SQL door accepts -- neither the public source code nor the catalog source id is a table -- plus the predicates a correct read carries (a currentness filter, a year pin, a partition column), and one bounded example SELECT that has been run through the door's own SQL guard before it was printed. A source served by a typed tool instead names that tool and its call; a seat not entitled to the table is told which pack grants it. Column lists are split: the data columns first, the row-lineage columns every row carries in their own named group.