@domandigital/craft 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -788,6 +788,57 @@ interface SnapshotSection {
788
788
  role?: SectionRole;
789
789
  /** Version 2. */
790
790
  geometry?: SectionGeometry;
791
+ /** Version 2: short pill or label texts in the section ("Most popular", "New"), at most 5. */
792
+ badges?: string[];
793
+ /** Version 2: small round portraits (at most 96px, roughly square, radius 40% or more). */
794
+ avatars?: number;
795
+ /** Version 2: a horizontal scroller, snap track or prev/next control holds the content. */
796
+ carousel?: boolean;
797
+ /** Version 2: large standalone figures ("500+", "98%", "4.9/5") at 24px or more. */
798
+ figures?: number;
799
+ /**
800
+ * Version 2, craft 0.13 and later: the section's visible text, one block per
801
+ * heading, paragraph, list item or quote, each at most 300 characters, at
802
+ * most 40. Navigation, buttons and footers are left out. What the
803
+ * specificity and proof tells read.
804
+ */
805
+ text?: string[];
806
+ }
807
+ type ImageRole = "photo" | "illustration" | "icon" | "avatar" | "logo";
808
+ /**
809
+ * What the image's own bytes say about where it came from. Only a positive
810
+ * is evidence: most images carry no metadata at all, and a re-encode strips
811
+ * it, so no marker proves nothing.
812
+ */
813
+ interface ImageProvenance {
814
+ /** The IPTC digital source type found, if any: "trainedAlgorithmicMedia" or "compositeWithTrainedAlgorithmicMedia". */
815
+ digitalSourceType: string | null;
816
+ /** Where the marker sat: an XMP packet, a C2PA manifest, or somewhere else in the file. */
817
+ source: "xmp" | "c2pa" | "other" | null;
818
+ /** The file carries a C2PA manifest (Content Credentials), whatever it says. */
819
+ c2pa: boolean;
820
+ /** Bytes read, from the start of the file. */
821
+ bytes: number;
822
+ }
823
+ /** One image as the visitor sees it: an `img`, or a CSS background large enough to be a picture. */
824
+ interface SnapshotImage {
825
+ /** The URL the browser loaded, unwrapped from an image optimiser (`/_next/image?url=`), or a `data:` URI cut to 64 characters. */
826
+ src: string;
827
+ /** Host of `src`, or null for a `data:` URI. */
828
+ host: string | null;
829
+ width: number;
830
+ height: number;
831
+ top: number;
832
+ role: ImageRole;
833
+ alt: string | null;
834
+ /** Empty alt, `aria-hidden` or `role="presentation"`. */
835
+ decorative: boolean;
836
+ /** Index into `sections`, or null when the image sits outside them. */
837
+ section: number | null;
838
+ /** A CSS `background-image`, not an `img`. */
839
+ background: boolean;
840
+ /** Present when the bytes were read. */
841
+ provenance?: ImageProvenance;
791
842
  }
792
843
  /**
793
844
  * Measures of the rendered pixels, from a full-page screenshot (capped at
@@ -862,6 +913,18 @@ interface Snapshot {
862
913
  rhythmVariance?: number | null;
863
914
  /** Version 2: absent when the page could not be screenshotted. */
864
915
  visual?: SnapshotVisual;
916
+ /**
917
+ * Version 2, craft 0.13 and later: every image on the page, largest first, at
918
+ * most 60. Absent in older snapshots, which the imagery tells never judge.
919
+ */
920
+ images?: SnapshotImage[];
921
+ /**
922
+ * Version 2, craft 0.13 and later: the text a visitor reads before
923
+ * scrolling, at most 12 blocks. Navigation, a header without the headline,
924
+ * and the words on buttons and links are left out: this is the page's
925
+ * claim, not its menu or its calls to action.
926
+ */
927
+ firstScreenText?: string[];
865
928
  /** Set when a version 1 snapshot was read and upgraded in memory. */
866
929
  migratedFrom?: 1;
867
930
  /** The design-system measures trawl has collected since v7, kept so it can switch to this. */
@@ -899,7 +962,13 @@ interface Snapshot {
899
962
  */
900
963
  type Generation = 1 | 2 | 3;
901
964
  /** Where a tell is detected first. A source tell may also carry a rendered path. */
902
- type Surface = "source" | "copy";
965
+ /**
966
+ * Where a tell is detected first. `rendered` tells exist only on a rendered
967
+ * page (a pricing trio, a centred CTA band): source code cannot show them
968
+ * reliably, so they have no source detector and no source fixtures, only a
969
+ * rendered path with its own flag and pass snapshots.
970
+ */
971
+ type Surface = "source" | "copy" | "rendered";
903
972
  /**
904
973
  * `warn` is reported and never fails a run unless `--strict` is passed.
905
974
  * `block` fails a run.
@@ -1019,7 +1088,12 @@ interface CopyTell extends TellBase {
1019
1088
  surface: "copy";
1020
1089
  detect(ctx: CopyContext): Hit[];
1021
1090
  }
1022
- type Tell = SourceTell | CopyTell;
1091
+ /** A tell with a rendered path only. See `Surface`. */
1092
+ interface RenderedTell extends Omit<TellBase, "fixtures" | "rendered"> {
1093
+ surface: "rendered";
1094
+ rendered: RenderedPath;
1095
+ }
1096
+ type Tell = SourceTell | CopyTell | RenderedTell;
1023
1097
  /**
1024
1098
  * A declared reason to keep a tell. Lives in the site's `art-direction.json`.
1025
1099
  *
@@ -1084,7 +1158,7 @@ interface CheckReport {
1084
1158
  * Bumped whenever an entry is added, removed or its detection changes, so a
1085
1159
  * report can say which list it was judged against.
1086
1160
  */
1087
- declare const CATALOGUE_VERSION = "2026.09.9";
1161
+ declare const CATALOGUE_VERSION = "2026.09.12";
1088
1162
  declare const CATALOGUE: readonly Tell[];
1089
1163
  declare function tellById(id: string): Tell | undefined;
1090
1164
  /**
@@ -1266,6 +1340,154 @@ declare function namedSources(sentence: string): string[];
1266
1340
  declare function findClaims(files: SourceFile[]): ClaimsReport;
1267
1341
  declare function formatClaims(report: ClaimsReport, label?: string): string;
1268
1342
 
1343
+ /**
1344
+ * Specificity: how much of a page only this business could have written.
1345
+ *
1346
+ * "Quality you can trust" fits every site in every trade. "Boilers fixed the
1347
+ * same day in Brackley, from £85" fits one. The difference is countable: names,
1348
+ * places, numbers, prices, dates, and the nouns of the trade. A page built
1349
+ * from no brief has almost none, because the model had none to give.
1350
+ *
1351
+ * Pure over text. Two things keep the count honest:
1352
+ *
1353
+ * - A Title Case or all-capitals line carries no capital-letter information,
1354
+ * so its capitals are not read as names ("Quality You Can Trust" is not a
1355
+ * proper noun). Its numbers, trade nouns and brief terms still count.
1356
+ * - The stock figures every site uses ("24/7", "100%", "5-star", "No. 1") are
1357
+ * masked before counting. They are numbers, not specifics.
1358
+ *
1359
+ * A brief (from `--brief`, `art-direction.json` or a null model) adds its own
1360
+ * terms: the business's name, its town and its trade, recognised wherever
1361
+ * they appear, even at the start of a sentence or in a shouted heading.
1362
+ */
1363
+
1364
+ type SpecificKind = FactKind | "trade" | "brief";
1365
+ interface Specific {
1366
+ kind: SpecificKind;
1367
+ /** As written. */
1368
+ value: string;
1369
+ /** Lower case, for comparing across pages. */
1370
+ key: string;
1371
+ }
1372
+ interface Specificity {
1373
+ words: number;
1374
+ /** Each distinct specific once: repeating a town ten times does not make a page ten times more particular. */
1375
+ specifics: Specific[];
1376
+ /** Specifics that are not trade nouns: a name, place, number, price, date, contact detail or brief term. */
1377
+ hard: number;
1378
+ /** Distinct trade nouns. */
1379
+ trade: number;
1380
+ /** Distinct specifics per 100 words, to one decimal place. */
1381
+ perHundred: number;
1382
+ /**
1383
+ * No hard specific and fewer than two trade nouns. One trade noun alone
1384
+ * ("quality plumbing you can trust") is what every competitor in the trade
1385
+ * also says.
1386
+ */
1387
+ generic: boolean;
1388
+ }
1389
+ /**
1390
+ * Concrete nouns of the work, across the trades and local services the estate
1391
+ * builds for. A seed list, grown from what the null models and client sites
1392
+ * show. Deliberately absent: "services", "solutions", "projects", "work",
1393
+ * "quality", which say nothing about what is done.
1394
+ */
1395
+ declare const TRADE_NOUNS: readonly string[];
1396
+ /**
1397
+ * Stock figures: numbers every site in every trade uses, masked before
1398
+ * counting so they cannot pass for a fact.
1399
+ */
1400
+ declare const STOCK_FIGURES: readonly RegExp[];
1401
+ declare const countWords: (text: string) => number;
1402
+ /**
1403
+ * The brief's own terms: its names and places (capitalised words, even at the
1404
+ * start of a sentence), its numbers, and its trade nouns.
1405
+ */
1406
+ declare function briefTerms(brief: string): string[];
1407
+ /** Every distinct specific in the text. Lines are read one at a time: pass blocks separated by newlines. */
1408
+ declare function specifics(text: string, brief?: string): Specific[];
1409
+ /** How particular a text is. Pure. */
1410
+ declare function specificity(text: string, brief?: string): Specificity;
1411
+ /** What a page says where it makes its case: the first screen, and the sections that sell the work. */
1412
+ interface PageCopy {
1413
+ url: string;
1414
+ hero: string[];
1415
+ services: string[];
1416
+ /** Every line of page text: what the other page is checked against. */
1417
+ all: string[];
1418
+ }
1419
+ /** Blocks as sentences, short fragments dropped. */
1420
+ declare function sentencesOf(blocks: string[]): string[];
1421
+ /**
1422
+ * The hero and service copy of a snapshot. Null when the snapshot has no page
1423
+ * text: one taken before craft recorded it.
1424
+ */
1425
+ declare function pageCopy(snapshot: Snapshot): PageCopy | null;
1426
+ interface CopySide {
1427
+ url: string;
1428
+ /** Hero and service sentences read. */
1429
+ sentences: number;
1430
+ /** Of those, how many hold nothing the other page does not also say. */
1431
+ interchangeable: string[];
1432
+ /** interchangeable / sentences, or null with no sentences. */
1433
+ share: number | null;
1434
+ /** Specificity of the hero and service copy together. */
1435
+ perHundred: number;
1436
+ }
1437
+ interface CompetitorComparison {
1438
+ ours: CopySide;
1439
+ competitor: CopySide;
1440
+ /** A brief's terms were recognised on both pages. */
1441
+ brief: boolean;
1442
+ }
1443
+ /**
1444
+ * Which hero and service sentences either page could swap with the other:
1445
+ * each holds no specific the other page lacks. A shared town or trade noun is
1446
+ * not unique; the business's own name, its prices and its jobs are. Pure.
1447
+ */
1448
+ declare function compareCompetitor(ours: PageCopy, competitor: PageCopy, brief?: string): CompetitorComparison;
1449
+ declare function formatCompetitor(result: CompetitorComparison): string;
1450
+
1451
+ /**
1452
+ * Specificity tells: a page that could belong to anyone.
1453
+ *
1454
+ * A model given no brief writes the claim every site makes, because it has
1455
+ * nothing particular to say: "Quality you can trust", "Your vision, our
1456
+ * expertise". And it asserts standing ("trusted", "fully qualified",
1457
+ * "award-winning") with nothing next to the assertion to back it, because it
1458
+ * has no reviews and no registration number to put there.
1459
+ *
1460
+ * Both are read from what the page says, not how it looks: `specificity.ts`
1461
+ * counts the particulars.
1462
+ */
1463
+
1464
+ /**
1465
+ * The home page's source, where the first claim is made: a root route file
1466
+ * (`app/page.tsx`, `pages/index.tsx`, `src/pages/index.astro`, SvelteKit's
1467
+ * root `+page.svelte`), a top-level `index.html`, or a hero or home component.
1468
+ * Every other page's headline is a label ("Privacy policy", "Boiler
1469
+ * repairs"), not a claim, and is not judged.
1470
+ */
1471
+ declare const HOME_FILE: RegExp;
1472
+ /** A home page URL: the site root, `index.html`, or a locale root such as `/en-gb/`. */
1473
+ declare const HOME_URL: RegExp;
1474
+ /**
1475
+ * A claim of standing: what a visitor has to take on trust unless something
1476
+ * next to it backs it. Numbers of years are left out: those are checkable
1477
+ * facts, and `craft copy claims` lists them.
1478
+ */
1479
+ declare const STANDING_CLAIM: RegExp;
1480
+ /** Accreditation bodies, trade registers and review platforms, by the names they trade under. */
1481
+ declare const PROOF_NAMES: readonly string[];
1482
+ /** Every claim of standing with no review or accreditation in its section or the one either side. */
1483
+ declare function unprovenClaims(s: Snapshot): {
1484
+ claim: string;
1485
+ sentence: string;
1486
+ section: string;
1487
+ }[];
1488
+ declare const SPECIFICITY_COPY_TELLS: CopyTell[];
1489
+ declare const SPECIFICITY_RENDERED_TELLS: RenderedTell[];
1490
+
1269
1491
  /**
1270
1492
  * Baselines: adopt craft on a repo that already has findings, and fail only
1271
1493
  * on new ones.
@@ -1463,6 +1685,71 @@ declare function toJson(data: object): string;
1463
1685
 
1464
1686
  declare const RENDERED_PATHS: Record<string, RenderedPath>;
1465
1687
 
1688
+ /**
1689
+ * Tells that exist only on a rendered page: the stock components.
1690
+ *
1691
+ * A pricing trio with a "Most popular" badge, a centred band with one button,
1692
+ * an avatar carousel of testimonials: none of these is visible in one source
1693
+ * file, and all of them are visible on the page. They read the version 2
1694
+ * snapshot's `role`, `geometry` and section parts, so a version 1 snapshot
1695
+ * never trips them.
1696
+ *
1697
+ * Each changes the look, never the page grammar. The fix for a stock pricing
1698
+ * trio keeps the prices; the fix for a stock band keeps the call to action.
1699
+ */
1700
+
1701
+ declare const RENDERED_TELLS: RenderedTell[];
1702
+
1703
+ /**
1704
+ * Imagery and provenance: what the pictures on a rendered page say about
1705
+ * where they came from.
1706
+ *
1707
+ * A page built with no assets reaches for three things: a stock library, a
1708
+ * generator, or no pictures at all and a grid of icons where the work should
1709
+ * be. Each is visible in the snapshot's `images`. A snapshot taken before
1710
+ * craft recorded images has none, and none of these tells judges it.
1711
+ *
1712
+ * Each fix keeps the page's job and changes the evidence: a photo of the
1713
+ * client's own work in place of a library one.
1714
+ */
1715
+
1716
+ /** Stock libraries and placeholder services, by the domain they serve images from. */
1717
+ declare const STOCK_PHOTO_HOSTS: Readonly<Record<string, string>>;
1718
+ /** Services that hand out a face on request: placeholder portraits and generated people. */
1719
+ declare const STOCK_AVATAR_HOSTS: Readonly<Record<string, string>>;
1720
+ /** Where a stock picture came from, by host or by file name, or null. */
1721
+ declare function stockSource(image: Pick<SnapshotImage, "src" | "host">): string | null;
1722
+ /** The placeholder-face service an avatar came from, or a stock library, or null. */
1723
+ declare function stockAvatarSource(image: Pick<SnapshotImage, "src" | "host">): string | null;
1724
+ declare const IMAGERY_TELLS: RenderedTell[];
1725
+
1726
+ /**
1727
+ * Where an image came from, as far as its own bytes say.
1728
+ *
1729
+ * Generators that label their output do it in one of two places: an XMP
1730
+ * packet (Adobe's metadata format, in JPEG, PNG, WebP and AVIF) or a C2PA
1731
+ * manifest (Content Credentials, a JUMBF box). Both name the IPTC digital
1732
+ * source type, and two of its values mean a trained model made the picture:
1733
+ *
1734
+ * http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia
1735
+ * http://cv.iptc.org/newscodes/digitalsourcetype/compositeWithTrainedAlgorithmicMedia
1736
+ *
1737
+ * This is a byte scan with no parser and no dependency: the IRI is plain text
1738
+ * in XMP and a CBOR text string in C2PA, so it reads the same either way.
1739
+ *
1740
+ * Only a positive is evidence. Most images carry no metadata, every CMS and
1741
+ * image optimiser that re-encodes strips it, and a PNG can keep its metadata
1742
+ * after the pixels, beyond what is read. No marker proves nothing, and this
1743
+ * never says an image is real.
1744
+ */
1745
+
1746
+ /** Bytes read from the start of a file. JPEG and WebP metadata sit well inside it. */
1747
+ declare const PROVENANCE_SCAN_BYTES: number;
1748
+ /** The IPTC digital source types that mean a trained model made the image, most specific first. */
1749
+ declare const AI_SOURCE_TYPES: readonly ["compositeWithTrainedAlgorithmicMedia", "trainedAlgorithmicMedia"];
1750
+ /** Read an image's leading bytes for an AI digital source type. Pure. */
1751
+ declare function scanProvenance(bytes: Uint8Array): ImageProvenance;
1752
+
1466
1753
  /**
1467
1754
  * A plain, decided-looking page as a snapshot, for fixtures.
1468
1755
  *
@@ -1553,6 +1840,21 @@ interface FingerprintSection {
1553
1840
  centred: number;
1554
1841
  /** Content width over viewport width, 0 to 1. */
1555
1842
  width: number;
1843
+ /** Left-right balance of the content, 0 to 1. Component comparison only. */
1844
+ symmetry?: number;
1845
+ /** Buttons in the section. */
1846
+ controls?: number;
1847
+ /** Cards side by side. */
1848
+ cards?: number;
1849
+ /** The section's own background, or null when it is the page ground. */
1850
+ background?: Oklch | null;
1851
+ /** Badge texts ("Most popular"), lower-cased. */
1852
+ badges?: string[];
1853
+ /** Small round portraits. */
1854
+ avatars?: number;
1855
+ carousel?: boolean;
1856
+ /** Large standalone figures. */
1857
+ figures?: number;
1556
1858
  }
1557
1859
  interface Fingerprint {
1558
1860
  version: 1 | 2;
@@ -1621,6 +1923,270 @@ declare function nearest<T>(target: Fingerprint, set: {
1621
1923
  distance: FingerprintDistance;
1622
1924
  }[];
1623
1925
 
1926
+ /**
1927
+ * The estate register: every site the agency has shipped, fingerprinted, so a
1928
+ * new one can be checked against its siblings. Signal 3 of CHARACTER.md.
1929
+ *
1930
+ * An agency's biggest tell is its clients looking like each other. No single
1931
+ * site shows it; only the set does. So the register holds one fingerprint per
1932
+ * site, and `compareToEstate` says which sites a page sits nearest and what
1933
+ * it shares with them.
1934
+ *
1935
+ * The line for "siblings" is not a number picked here. Two different
1936
+ * businesses are siblings when they sit closer together than two pages Claude
1937
+ * builds for the same brief usually do: if a barber and an electrician look
1938
+ * more alike than two drafts of one barber, nobody chose either look. The
1939
+ * median comes from null models when they are given, and from the 2026-09-23
1940
+ * calibration when they are not.
1941
+ *
1942
+ * Pure. The CLI reads and writes the register file.
1943
+ */
1944
+
1945
+ declare const ESTATE_VERSION = 1;
1946
+ /**
1947
+ * Median fingerprint distance between two null pages built from the same
1948
+ * brief, across seven briefs and 1,330 pairs (calibration/null, 2026-09-23).
1949
+ */
1950
+ declare const SIBLING_AT = 0.31;
1951
+ interface EstateSite {
1952
+ /** Short kebab-case id: "rmp", "chair-and-blade". */
1953
+ id: string;
1954
+ client: string;
1955
+ url: string;
1956
+ /** Where the site's source lives, if known: "rmp474/RMP-Electrical". */
1957
+ repo?: string;
1958
+ fingerprint: Fingerprint;
1959
+ /** Rendered tells found when it was added, for the record. */
1960
+ tells: string[];
1961
+ addedAt: string;
1962
+ }
1963
+ interface EstateRegister {
1964
+ version: typeof ESTATE_VERSION;
1965
+ sites: EstateSite[];
1966
+ }
1967
+ declare function emptyEstate(): EstateRegister;
1968
+ /** Add a site, or replace the one with the same id. Sorted by id so the file diffs cleanly. */
1969
+ declare function addSite(register: EstateRegister, site: EstateSite): EstateRegister;
1970
+ /**
1971
+ * What two fingerprints effectively share, named the way a person would see
1972
+ * it: "Fraunces headline", "no accent", "pill buttons". A part counts when its
1973
+ * distance is 0.2 or less.
1974
+ */
1975
+ declare function sharedParts(a: Fingerprint, b: Fingerprint, d?: FingerprintDistance): string[];
1976
+ interface EstateMatch {
1977
+ id: string;
1978
+ distance: number;
1979
+ sibling: boolean;
1980
+ shared: string[];
1981
+ }
1982
+ /** The sites nearest a fingerprint, closest first, leaving out `exclude` (the site itself). */
1983
+ declare function compareToEstate(target: Fingerprint, register: EstateRegister, options?: {
1984
+ exclude?: string;
1985
+ siblingAt?: number;
1986
+ }): EstateMatch[];
1987
+ interface EstatePair {
1988
+ a: string;
1989
+ b: string;
1990
+ distance: number;
1991
+ sibling: boolean;
1992
+ shared: string[];
1993
+ }
1994
+ /** Every pair in the register, closest first. */
1995
+ declare function estatePairs(register: EstateRegister, siblingAt?: number): EstatePair[];
1996
+ /**
1997
+ * The sibling line from null models: the median distance between two pages
1998
+ * built from the same brief. Each inner array is one brief's fingerprints.
1999
+ */
2000
+ declare function siblingLine(briefs: Fingerprint[][]): number;
2001
+
2002
+ /**
2003
+ * The counterfactual: what a model builds for this brief when nobody tells it
2004
+ * anything else. Signal 2 of CHARACTER.md.
2005
+ *
2006
+ * A null model is about twenty pages generated from one brief, each reduced to
2007
+ * a fingerprint. A site is typical when it sits as close to those pages as they
2008
+ * sit to each other. That question keeps working when model defaults move,
2009
+ * which a fixed list of tells cannot: ban Inter and the null model moves with
2010
+ * the model, so the comparison moves too.
2011
+ *
2012
+ * `harvest` reads the same pages the other way round: which choices recur
2013
+ * across them that the catalogue does not know yet. Those are the candidates
2014
+ * for the next generation of tells. A person decides; nothing is added here.
2015
+ *
2016
+ * Pure. Generating pages and snapshotting them lives in the CLI.
2017
+ */
2018
+
2019
+ declare const NULL_VERSION = 1;
2020
+ /**
2021
+ * The prompt a null run gets: the brief, and nothing about how it should look.
2022
+ * Changing this changes every null model built after it, so it is versioned
2023
+ * by being recorded in each one.
2024
+ */
2025
+ declare function nullPrompt(brief: string): string;
2026
+ interface NullRun {
2027
+ /** The page's file name without extension: "01". */
2028
+ id: string;
2029
+ fingerprint: Fingerprint;
2030
+ /** Catalogue tells found on the page, source and rendered, each once. */
2031
+ tells: string[];
2032
+ /** Every visible string on the page, for the phrase harvest. */
2033
+ copy: string[];
2034
+ }
2035
+ interface NullModel {
2036
+ version: typeof NULL_VERSION;
2037
+ brief: string;
2038
+ prompt: string;
2039
+ /** What was asked for with --model, or "default". */
2040
+ model: string;
2041
+ catalogueVersion: string;
2042
+ builtAt: string;
2043
+ runs: NullRun[];
2044
+ }
2045
+ /** The HTML document in a model's reply, without code fences or commentary. */
2046
+ declare function extractHtml(reply: string): string | null;
2047
+ /** Fewer runs than this and the spread of the null is noise, so nothing is judged. */
2048
+ declare const MIN_RUNS = 5;
2049
+ /** Typical when at least this share of the null's own pages sit further out. */
2050
+ declare const TYPICAL_AT = 0.1;
2051
+ interface Typicality {
2052
+ /**
2053
+ * 0 to 1: the share of the null's own pages that sit further from the rest
2054
+ * than this page sits from them. 1 is more typical than anything the model
2055
+ * built; 0 is further out than every one of them.
2056
+ */
2057
+ score: number;
2058
+ typical: boolean;
2059
+ /** Mean distance to the k nearest null pages. */
2060
+ distance: number;
2061
+ /** The same measure for each null page against the others, median. */
2062
+ baseline: number;
2063
+ nearest: {
2064
+ id: string;
2065
+ distance: number;
2066
+ }[];
2067
+ /** The choices this page shares with most of the null: why it reads as typical. */
2068
+ shared: {
2069
+ part: string;
2070
+ value: string;
2071
+ runs: number;
2072
+ of: number;
2073
+ }[];
2074
+ }
2075
+ /**
2076
+ * How typical a page is against one or more null models (pooled). Every null
2077
+ * page is scored against the others the same way, leaving itself out, so the
2078
+ * threshold comes from the model's own spread and not from a number picked
2079
+ * here: a page drawn from the null is flagged nine times in ten by
2080
+ * construction, and the calibration checks whether real sites are not.
2081
+ */
2082
+ declare function typicality(target: Fingerprint, runs: NullRun[], k?: number): Typicality;
2083
+ declare function hueName(o: Oklch | null): string;
2084
+ declare function groundName(o: Oklch): string;
2085
+ declare function shapeName(roundness: number | null): string;
2086
+ /** The discrete choices a fingerprint makes, as part → value. What a person would name. */
2087
+ declare function choicesOf(fp: Fingerprint): Record<string, string[]>;
2088
+ type CandidateKind = "face" | "accent" | "ground" | "shape" | "motion" | "opening" | "tell" | "phrase";
2089
+ interface HarvestCandidate {
2090
+ kind: CandidateKind;
2091
+ value: string;
2092
+ /** Null pages that make this choice, across every model given. */
2093
+ runs: number;
2094
+ of: number;
2095
+ /** Distinct briefs it turned up for. A habit shows across briefs; a brief's own words do not. */
2096
+ briefs: number;
2097
+ /** The catalogue tell that already catches it, or null when nothing does. */
2098
+ known: string | null;
2099
+ /** An example, for a phrase: the string it came from. */
2100
+ example?: string;
2101
+ }
2102
+ interface HarvestOptions {
2103
+ /** Report a choice made by at least this share of pages. Default 0.25. */
2104
+ minShare?: number;
2105
+ /** Which copy tell, if any, already catches a phrase. The CLI passes the catalogue's. */
2106
+ copyTell?: (phrase: string) => string | null;
2107
+ }
2108
+ /**
2109
+ * Choices that recur across null pages, with the tell that already catches
2110
+ * each, if any. The ones with `known: null` are the next generation's
2111
+ * candidates. Nothing here decides: a person reads them, and a candidate only
2112
+ * becomes a tell with a flag case and a pass case like every other.
2113
+ */
2114
+ declare function harvest(models: Pick<NullModel, "brief" | "runs">[], options?: HarvestOptions): HarvestCandidate[];
2115
+ /** A representative colour for a hue family, for a report. */
2116
+ declare function hueSwatch(runs: NullRun[], hue: string): string | null;
2117
+
2118
+ /**
2119
+ * Component-level sameness: the CTA band, the pricing table, the testimonial
2120
+ * strip, compared one kind at a time.
2121
+ *
2122
+ * The page fingerprint can say two sites look alike overall. It cannot say
2123
+ * "every site in the estate ends on the same centred band with one orange
2124
+ * button". That is the sameness a client notices when they put two of the
2125
+ * agency's sites side by side, and it lives in the components.
2126
+ *
2127
+ * Pure, over the `sections` of version 2 fingerprints. A version 1
2128
+ * fingerprint has none, so it simply has no components to compare.
2129
+ */
2130
+
2131
+ /** Badge texts that promote one option over the others. */
2132
+ declare const PROMO_BADGE: RegExp;
2133
+ /** The sections of one role in a fingerprint, in running order. */
2134
+ declare function componentsOf(fp: Fingerprint, role: SectionRole): FingerprintSection[];
2135
+ /**
2136
+ * 0 (the same recipe) to 1 (nothing in common), over the parts both
2137
+ * components carry: layout (centring, width, balance), furniture (buttons,
2138
+ * cards, figures, avatars, carousel, a promo badge) and colour (its own
2139
+ * ground or the page's). Different roles are 1: a pricing table and an FAQ
2140
+ * are not the same component however they are laid out.
2141
+ */
2142
+ declare function componentDistance(a: FingerprintSection, b: FingerprintSection): number;
2143
+ /** What two components share, as a person would name it. */
2144
+ declare function componentShared(a: FingerprintSection, b: FingerprintSection): string[];
2145
+ /** The closest pair of components of one role between two fingerprints. */
2146
+ declare function closestComponents(a: Fingerprint, b: Fingerprint, role: SectionRole): {
2147
+ distance: number;
2148
+ shared: string[];
2149
+ } | null;
2150
+ interface ComponentPair {
2151
+ a: string;
2152
+ b: string;
2153
+ distance: number;
2154
+ shared: string[];
2155
+ }
2156
+ /**
2157
+ * Every pair of estate sites that both have a component of this role, closest
2158
+ * first. No sibling line yet: that needs a calibration set of real
2159
+ * components, so this ranks and names, and judges nothing.
2160
+ */
2161
+ declare function estateComponentPairs(register: EstateRegister, role: SectionRole): ComponentPair[];
2162
+ interface ComponentTypicality {
2163
+ role: SectionRole;
2164
+ score: number;
2165
+ typical: boolean;
2166
+ distance: number;
2167
+ baseline: number;
2168
+ /** How many null pages had a component of this role to compare with. */
2169
+ runs: number;
2170
+ }
2171
+ /**
2172
+ * How typical one component is against the same role in a null model, by
2173
+ * the method `typicality` uses for whole pages: each null component is scored
2174
+ * against the others leaving itself out, and the page's component is placed
2175
+ * in that spread. Null when fewer than `MIN_RUNS` null pages have the role.
2176
+ */
2177
+ declare function componentTypicality(target: FingerprintSection, runs: NullRun[], k?: number): ComponentTypicality | null;
2178
+ /**
2179
+ * The roles that name a component rather than a generic block: what
2180
+ * `--component` accepts and what `craft audit --null` scores one by one.
2181
+ */
2182
+ declare const COMPONENT_ROLES: readonly SectionRole[];
2183
+ /**
2184
+ * Typicality for each component on a page that the null model can speak to:
2185
+ * the first section of each component role, skipping roles too few null
2186
+ * pages have.
2187
+ */
2188
+ declare function componentTypicalities(fp: Fingerprint, runs: NullRun[], k?: number): ComponentTypicality[];
2189
+
1624
2190
  /**
1625
2191
  * Every colour syntax the estate actually writes, read into OKLCh.
1626
2192
  *
@@ -1911,198 +2477,6 @@ declare function propose(input: ProposeInput): {
1911
2477
  direction: ArtDirection;
1912
2478
  };
1913
2479
 
1914
- /**
1915
- * The counterfactual: what a model builds for this brief when nobody tells it
1916
- * anything else. Signal 2 of CHARACTER.md.
1917
- *
1918
- * A null model is about twenty pages generated from one brief, each reduced to
1919
- * a fingerprint. A site is typical when it sits as close to those pages as they
1920
- * sit to each other. That question keeps working when model defaults move,
1921
- * which a fixed list of tells cannot: ban Inter and the null model moves with
1922
- * the model, so the comparison moves too.
1923
- *
1924
- * `harvest` reads the same pages the other way round: which choices recur
1925
- * across them that the catalogue does not know yet. Those are the candidates
1926
- * for the next generation of tells. A person decides; nothing is added here.
1927
- *
1928
- * Pure. Generating pages and snapshotting them lives in the CLI.
1929
- */
1930
-
1931
- declare const NULL_VERSION = 1;
1932
- /**
1933
- * The prompt a null run gets: the brief, and nothing about how it should look.
1934
- * Changing this changes every null model built after it, so it is versioned
1935
- * by being recorded in each one.
1936
- */
1937
- declare function nullPrompt(brief: string): string;
1938
- interface NullRun {
1939
- /** The page's file name without extension: "01". */
1940
- id: string;
1941
- fingerprint: Fingerprint;
1942
- /** Catalogue tells found on the page, source and rendered, each once. */
1943
- tells: string[];
1944
- /** Every visible string on the page, for the phrase harvest. */
1945
- copy: string[];
1946
- }
1947
- interface NullModel {
1948
- version: typeof NULL_VERSION;
1949
- brief: string;
1950
- prompt: string;
1951
- /** What was asked for with --model, or "default". */
1952
- model: string;
1953
- catalogueVersion: string;
1954
- builtAt: string;
1955
- runs: NullRun[];
1956
- }
1957
- /** The HTML document in a model's reply, without code fences or commentary. */
1958
- declare function extractHtml(reply: string): string | null;
1959
- /** Fewer runs than this and the spread of the null is noise, so nothing is judged. */
1960
- declare const MIN_RUNS = 5;
1961
- /** Typical when at least this share of the null's own pages sit further out. */
1962
- declare const TYPICAL_AT = 0.1;
1963
- interface Typicality {
1964
- /**
1965
- * 0 to 1: the share of the null's own pages that sit further from the rest
1966
- * than this page sits from them. 1 is more typical than anything the model
1967
- * built; 0 is further out than every one of them.
1968
- */
1969
- score: number;
1970
- typical: boolean;
1971
- /** Mean distance to the k nearest null pages. */
1972
- distance: number;
1973
- /** The same measure for each null page against the others, median. */
1974
- baseline: number;
1975
- nearest: {
1976
- id: string;
1977
- distance: number;
1978
- }[];
1979
- /** The choices this page shares with most of the null: why it reads as typical. */
1980
- shared: {
1981
- part: string;
1982
- value: string;
1983
- runs: number;
1984
- of: number;
1985
- }[];
1986
- }
1987
- /**
1988
- * How typical a page is against one or more null models (pooled). Every null
1989
- * page is scored against the others the same way, leaving itself out, so the
1990
- * threshold comes from the model's own spread and not from a number picked
1991
- * here: a page drawn from the null is flagged nine times in ten by
1992
- * construction, and the calibration checks whether real sites are not.
1993
- */
1994
- declare function typicality(target: Fingerprint, runs: NullRun[], k?: number): Typicality;
1995
- declare function hueName(o: Oklch | null): string;
1996
- declare function groundName(o: Oklch): string;
1997
- declare function shapeName(roundness: number | null): string;
1998
- /** The discrete choices a fingerprint makes, as part → value. What a person would name. */
1999
- declare function choicesOf(fp: Fingerprint): Record<string, string[]>;
2000
- type CandidateKind = "face" | "accent" | "ground" | "shape" | "motion" | "opening" | "tell" | "phrase";
2001
- interface HarvestCandidate {
2002
- kind: CandidateKind;
2003
- value: string;
2004
- /** Null pages that make this choice, across every model given. */
2005
- runs: number;
2006
- of: number;
2007
- /** Distinct briefs it turned up for. A habit shows across briefs; a brief's own words do not. */
2008
- briefs: number;
2009
- /** The catalogue tell that already catches it, or null when nothing does. */
2010
- known: string | null;
2011
- /** An example, for a phrase: the string it came from. */
2012
- example?: string;
2013
- }
2014
- interface HarvestOptions {
2015
- /** Report a choice made by at least this share of pages. Default 0.25. */
2016
- minShare?: number;
2017
- /** Which copy tell, if any, already catches a phrase. The CLI passes the catalogue's. */
2018
- copyTell?: (phrase: string) => string | null;
2019
- }
2020
- /**
2021
- * Choices that recur across null pages, with the tell that already catches
2022
- * each, if any. The ones with `known: null` are the next generation's
2023
- * candidates. Nothing here decides: a person reads them, and a candidate only
2024
- * becomes a tell with a flag case and a pass case like every other.
2025
- */
2026
- declare function harvest(models: Pick<NullModel, "brief" | "runs">[], options?: HarvestOptions): HarvestCandidate[];
2027
- /** A representative colour for a hue family, for a report. */
2028
- declare function hueSwatch(runs: NullRun[], hue: string): string | null;
2029
-
2030
- /**
2031
- * The estate register: every site the agency has shipped, fingerprinted, so a
2032
- * new one can be checked against its siblings. Signal 3 of CHARACTER.md.
2033
- *
2034
- * An agency's biggest tell is its clients looking like each other. No single
2035
- * site shows it; only the set does. So the register holds one fingerprint per
2036
- * site, and `compareToEstate` says which sites a page sits nearest and what
2037
- * it shares with them.
2038
- *
2039
- * The line for "siblings" is not a number picked here. Two different
2040
- * businesses are siblings when they sit closer together than two pages Claude
2041
- * builds for the same brief usually do: if a barber and an electrician look
2042
- * more alike than two drafts of one barber, nobody chose either look. The
2043
- * median comes from null models when they are given, and from the 2026-09-23
2044
- * calibration when they are not.
2045
- *
2046
- * Pure. The CLI reads and writes the register file.
2047
- */
2048
-
2049
- declare const ESTATE_VERSION = 1;
2050
- /**
2051
- * Median fingerprint distance between two null pages built from the same
2052
- * brief, across seven briefs and 1,330 pairs (calibration/null, 2026-09-23).
2053
- */
2054
- declare const SIBLING_AT = 0.31;
2055
- interface EstateSite {
2056
- /** Short kebab-case id: "rmp", "chair-and-blade". */
2057
- id: string;
2058
- client: string;
2059
- url: string;
2060
- /** Where the site's source lives, if known: "rmp474/RMP-Electrical". */
2061
- repo?: string;
2062
- fingerprint: Fingerprint;
2063
- /** Rendered tells found when it was added, for the record. */
2064
- tells: string[];
2065
- addedAt: string;
2066
- }
2067
- interface EstateRegister {
2068
- version: typeof ESTATE_VERSION;
2069
- sites: EstateSite[];
2070
- }
2071
- declare function emptyEstate(): EstateRegister;
2072
- /** Add a site, or replace the one with the same id. Sorted by id so the file diffs cleanly. */
2073
- declare function addSite(register: EstateRegister, site: EstateSite): EstateRegister;
2074
- /**
2075
- * What two fingerprints effectively share, named the way a person would see
2076
- * it: "Fraunces headline", "no accent", "pill buttons". A part counts when its
2077
- * distance is 0.2 or less.
2078
- */
2079
- declare function sharedParts(a: Fingerprint, b: Fingerprint, d?: FingerprintDistance): string[];
2080
- interface EstateMatch {
2081
- id: string;
2082
- distance: number;
2083
- sibling: boolean;
2084
- shared: string[];
2085
- }
2086
- /** The sites nearest a fingerprint, closest first, leaving out `exclude` (the site itself). */
2087
- declare function compareToEstate(target: Fingerprint, register: EstateRegister, options?: {
2088
- exclude?: string;
2089
- siblingAt?: number;
2090
- }): EstateMatch[];
2091
- interface EstatePair {
2092
- a: string;
2093
- b: string;
2094
- distance: number;
2095
- sibling: boolean;
2096
- shared: string[];
2097
- }
2098
- /** Every pair in the register, closest first. */
2099
- declare function estatePairs(register: EstateRegister, siblingAt?: number): EstatePair[];
2100
- /**
2101
- * The sibling line from null models: the median distance between two pages
2102
- * built from the same brief. Each inner array is one brief's fingerprints.
2103
- */
2104
- declare function siblingLine(briefs: Fingerprint[][]): number;
2105
-
2106
2480
  /**
2107
2481
  * The character report: the three signals and the reason rule, read together.
2108
2482
  *
@@ -2178,4 +2552,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
2178
2552
 
2179
2553
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
2180
2554
 
2181
- export { AI_PHRASES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, type CandidateKind, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type ContrastCheck, type ContrastReport, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionProblem, type DirectionReport, type DirectionSource, type DurationName, EASE, EASE_TUPLE, EDGE_STEP, ESTATE_VERSION, EXIT_RATIO, type EaseName, type EmitCssOptions, type EstateMatch, type EstatePair, type EstateRegister, type EstateSite, FINGERPRINT_VERSION, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOUSE, HOUSE_BUDGET, HOUSE_LOCKED, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, JSON_SCHEMA_VERSION, LICENCES, LIGHTNESS_CURVE, type LicenceEntry, type LicenceSubject, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, type Oklch, type OklchToHexOptions, PILL_LIMIT, PLAINER_WORDS, PROPOSED, type PaletteColour, type Proposal, type ProposeInput, RAMP_STEPS, READABLE_SNAPSHOT_VERSIONS, REFLEX_FONTS_1, REFLEX_FONTS_2, RENDERED_PATHS, REVEAL_LIMIT, REVIEW_PHRASES, type Ramp, type RampFromAnchorsOptions, type RampOptions, type RampStep, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPRING, STOCK_PHRASES, type SarifOptions, type SarifTell, type SectionGeometry, type SectionKind, type SectionRhythmOptions, type SectionRole, type SemanticInput, type SemanticResult, type Severity, type SeverityLevel, type SeverityOverride, type ShouldAnimateInput, type SignalResult, type Snapshot, type SnapshotColour, type SnapshotControl, type SnapshotEffects, type SnapshotFont, type SnapshotGradient, type SnapshotHeading, type SnapshotMotion, type SnapshotPatch, type SnapshotSection, type SnapshotVisual, type SourceFile, type SourceKind, type SourceTell, type SpaceStep, type Surface, TELL_HEAVY, TYPE_STEPS, TYPICAL_AT, type TailwindPreset, type Tell, type TellException, type TypeStep, type Typicality, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, compareFacts, compareToEstate, craftTokens, createBaseline, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estatePairs, exitDuration, extractHtml, faceLicence, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatHex, formatReport, globToRegExp, groundName, harvest, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, layoutDistance, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, paletteFromPixels, parseBaseline, parseColour, parseCraftConfig, parseHex, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, retrofitPlan, runTell, scanSource, sectionRhythm, semantic, severityExceptions, shapeName, sharedParts, shouldAnimate, siblingLine, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, typeFeatureTokens, typicality, validateDirection, visibleText, visualMeasures, wcagContrast, wordListsJson };
2555
+ export { AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, COMPONENT_ROLES, type CandidateKind, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type CompetitorComparison, type ComponentPair, type ComponentTypicality, type ContrastCheck, type ContrastReport, type CopySide, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionProblem, type DirectionReport, type DirectionSource, type DurationName, EASE, EASE_TUPLE, EDGE_STEP, ESTATE_VERSION, EXIT_RATIO, type EaseName, type EmitCssOptions, type EstateMatch, type EstatePair, type EstateRegister, type EstateSite, FINGERPRINT_VERSION, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOME_FILE, HOME_URL, HOUSE, HOUSE_BUDGET, HOUSE_LOCKED, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, IMAGERY_TELLS, type ImageProvenance, type ImageRole, JSON_SCHEMA_VERSION, LICENCES, LIGHTNESS_CURVE, type LicenceEntry, type LicenceSubject, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, type Oklch, type OklchToHexOptions, PILL_LIMIT, PLAINER_WORDS, PROMO_BADGE, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageCopy, type PaletteColour, type Proposal, type ProposeInput, RAMP_STEPS, READABLE_SNAPSHOT_VERSIONS, REFLEX_FONTS_1, REFLEX_FONTS_2, RENDERED_PATHS, RENDERED_TELLS, REVEAL_LIMIT, REVIEW_PHRASES, type Ramp, type RampFromAnchorsOptions, type RampOptions, type RampStep, type RenderedTell, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPECIFICITY_COPY_TELLS, SPECIFICITY_RENDERED_TELLS, SPRING, STANDING_CLAIM, STOCK_AVATAR_HOSTS, STOCK_FIGURES, STOCK_PHOTO_HOSTS, STOCK_PHRASES, type SarifOptions, type SarifTell, type SectionGeometry, type SectionKind, type SectionRhythmOptions, type SectionRole, type SemanticInput, type SemanticResult, type Severity, type SeverityLevel, type SeverityOverride, type ShouldAnimateInput, type SignalResult, type Snapshot, type SnapshotColour, type SnapshotControl, type SnapshotEffects, type SnapshotFont, type SnapshotGradient, type SnapshotHeading, type SnapshotImage, type SnapshotMotion, type SnapshotPatch, type SnapshotSection, type SnapshotVisual, type SourceFile, type SourceKind, type SourceTell, type SpaceStep, type Specific, type SpecificKind, type Specificity, type Surface, TELL_HEAVY, TRADE_NOUNS, TYPE_STEPS, TYPICAL_AT, type TailwindPreset, type Tell, type TellException, type TypeStep, type Typicality, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, briefTerms, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, closestComponents, compareCompetitor, compareFacts, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, countWords, craftTokens, createBaseline, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estateComponentPairs, estatePairs, exitDuration, extractHtml, faceLicence, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatCompetitor, formatHex, formatReport, globToRegExp, groundName, harvest, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, layoutDistance, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, pageCopy, paletteFromPixels, parseBaseline, parseColour, parseCraftConfig, parseHex, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, retrofitPlan, runTell, scanProvenance, scanSource, sectionRhythm, semantic, sentencesOf, severityExceptions, shapeName, sharedParts, shouldAnimate, siblingLine, specificity, specifics, stockAvatarSource, stockSource, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, typeFeatureTokens, typicality, unprovenClaims, validateDirection, visibleText, visualMeasures, wcagContrast, wordListsJson };