@domandigital/craft 0.13.0 → 0.15.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/CHARACTER.md +272 -39
  3. package/COPY.md +5 -0
  4. package/README.md +19 -1
  5. package/art-direction.schema.json +428 -25
  6. package/dist/{audit-5TYDYRXV.js → audit-MTUC24XU.js} +3 -2
  7. package/dist/audit.cjs +674 -92
  8. package/dist/audit.d.cts +15 -1
  9. package/dist/audit.d.ts +15 -1
  10. package/dist/audit.js +674 -92
  11. package/dist/{chunk-IMB4V34S.js → chunk-2IQ5OXFR.js} +7 -7
  12. package/dist/{chunk-GMVXADJ6.js → chunk-LFNSB6W7.js} +711 -92
  13. package/dist/{chunk-3VTPI7BU.js → chunk-MUVFECFC.js} +74 -96
  14. package/dist/chunk-OQXTQQAU.js +27 -0
  15. package/dist/{chunk-ZLDA2SYB.js → chunk-OUQ25LY2.js} +47 -6
  16. package/dist/{chunk-K2BZN4GA.js → chunk-QU6Q2QZZ.js} +49 -14
  17. package/dist/chunk-QYBAVU3P.js +6 -0
  18. package/dist/{chunk-ESCVTKPN.js → chunk-RXXRXV3V.js} +9 -26
  19. package/dist/{chunk-CZDOMIVS.js → chunk-WUACK2HX.js} +2 -1
  20. package/dist/chunk-YDURAOYR.js +711 -0
  21. package/dist/cli-22TSWAX6.js +21 -0
  22. package/dist/cli-5ACKICBO.js +22 -0
  23. package/dist/{cli-DRKAMFAH.js → cli-7OZCYOQY.js} +89 -22
  24. package/dist/{cli-J5ECRCD4.js → cli-FCKHYAVZ.js} +40 -16
  25. package/dist/{cli-ILH7OO5G.js → cli-FLG3ZCAF.js} +6 -4
  26. package/dist/cli.js +3 -2
  27. package/dist/generate.js +6 -0
  28. package/dist/index.cjs +1618 -328
  29. package/dist/index.d.cts +619 -58
  30. package/dist/index.d.ts +619 -58
  31. package/dist/index.js +1555 -328
  32. package/dist/migrate-VIFAYIBM.js +9 -0
  33. package/package.json +1 -1
  34. package/word-lists.json +1 -1
  35. package/dist/chunk-ZZ5CKOJP.js +0 -295
  36. package/dist/cli-BKJMLD3H.js +0 -18
  37. package/dist/cli-N2IDXH7W.js +0 -20
package/dist/index.d.ts CHANGED
@@ -796,6 +796,13 @@ interface SnapshotSection {
796
796
  carousel?: boolean;
797
797
  /** Version 2: large standalone figures ("500+", "98%", "4.9/5") at 24px or more. */
798
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[];
799
806
  }
800
807
  type ImageRole = "photo" | "illustration" | "icon" | "avatar" | "logo";
801
808
  /**
@@ -907,10 +914,17 @@ interface Snapshot {
907
914
  /** Version 2: absent when the page could not be screenshotted. */
908
915
  visual?: SnapshotVisual;
909
916
  /**
910
- * Version 2, craft 0.14 and later: every image on the page, largest first, at
917
+ * Version 2, craft 0.13 and later: every image on the page, largest first, at
911
918
  * most 60. Absent in older snapshots, which the imagery tells never judge.
912
919
  */
913
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[];
914
928
  /** Set when a version 1 snapshot was read and upgraded in memory. */
915
929
  migratedFrom?: 1;
916
930
  /** The design-system measures trawl has collected since v7, kept so it can switch to this. */
@@ -1144,7 +1158,7 @@ interface CheckReport {
1144
1158
  * Bumped whenever an entry is added, removed or its detection changes, so a
1145
1159
  * report can say which list it was judged against.
1146
1160
  */
1147
- declare const CATALOGUE_VERSION = "2026.09.11";
1161
+ declare const CATALOGUE_VERSION = "2026.09.12";
1148
1162
  declare const CATALOGUE: readonly Tell[];
1149
1163
  declare function tellById(id: string): Tell | undefined;
1150
1164
  /**
@@ -1326,6 +1340,252 @@ declare function namedSources(sentence: string): string[];
1326
1340
  declare function findClaims(files: SourceFile[]): ClaimsReport;
1327
1341
  declare function formatClaims(report: ClaimsReport, label?: string): string;
1328
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
+ * Shape features: how a short block of copy is built, not which words it uses.
1453
+ *
1454
+ * A three-sentence blurb can pass every vocabulary tell and still read as
1455
+ * generated, because the tell is the construction: a parallel triad, a
1456
+ * conditional stacked between commas, a close that hedges. The density tier
1457
+ * cannot see this; it skips anything under 400 words.
1458
+ *
1459
+ * Pure and zero-dependency. The calibration scripts under `scripts/shape/`
1460
+ * and any future calibrated tells call the same functions, so a published
1461
+ * effect size describes the detector that ships.
1462
+ *
1463
+ * Several features stand in for part-of-speech measures (phrasal
1464
+ * coordination, template skeletons) with word lists. They are approximate by
1465
+ * design, and measured, not assumed, before anything ships on them.
1466
+ */
1467
+ /** Sentences in a block. Abbreviations and decimals do not end one. */
1468
+ declare function splitSentences(text: string): string[];
1469
+ declare function words(text: string): string[];
1470
+ /** Words that carry grammar rather than content. Also the skeleton's kept set. */
1471
+ declare const FUNCTION_WORDS: Set<string>;
1472
+ /**
1473
+ * "A, B and C" with short items. `parallel` when the items share their first
1474
+ * word ("your business, your website and your systems"), the construction a
1475
+ * model reaches for to sound complete.
1476
+ */
1477
+ interface Tricolon {
1478
+ text: string;
1479
+ parallel: boolean;
1480
+ }
1481
+ declare function tricolons(sentence: string): Tricolon[];
1482
+ declare function stackedConditional(sentence: string): boolean;
1483
+ /** Commas plus subordinators: how many clauses a sentence stacks. */
1484
+ declare function clauseDepth(sentence: string): number;
1485
+ declare function hedgeCount(text: string): number;
1486
+ /** The last sentence ends the block on a qualifier or a condition. */
1487
+ declare function hedgedClose(sentences: string[]): boolean;
1488
+ declare function nominalisations(text: string): number;
1489
+ /**
1490
+ * Present participial clauses: ", making it easy to..." after a comma, or a
1491
+ * sentence that opens on a participle ("Building on this, ..."). Reported at
1492
+ * 5.3 times the human rate in GPT-4o text (PMC11874169). `ing-tail` is a narrow
1493
+ * form of this.
1494
+ */
1495
+ declare function participialClauses(sentence: string): number;
1496
+ /**
1497
+ * "X and Y" where both sides are single content words ("enquiries and admin",
1498
+ * "clear and simple"). A word-list stand-in for phrasal coordination, reported
1499
+ * at 1.9 times the human rate (PMC11874169). Not preceded by a comma, so a
1500
+ * tricolon's last item is not counted twice.
1501
+ */
1502
+ declare function phrasalCoordinations(sentence: string): number;
1503
+ /**
1504
+ * A sentence reduced to its function words, with one `_` for each run of
1505
+ * content words. Experimental: a stand-in for part-of-speech templates
1506
+ * (Shaib et al., EMNLP 2024), which need a tagger craft does not carry.
1507
+ */
1508
+ declare function skeleton(sentence: string): string[];
1509
+ /** Share of the block's skeleton 4-grams that occur more than once. */
1510
+ declare function skeletonRepeat(sentences: string[]): number;
1511
+ /** Coefficient of variation; null below `min` items, where it means nothing. */
1512
+ declare function cv(xs: number[], min: number): number | null;
1513
+ interface ShapeFeatures {
1514
+ sentences: number;
1515
+ words: number;
1516
+ tricolons: number;
1517
+ parallelTriads: number;
1518
+ stackedConditionals: number;
1519
+ /** Mean clauses per sentence. */
1520
+ clauseDepth: number;
1521
+ /** Sentence-length CV; null under three sentences. */
1522
+ sentenceCv: number | null;
1523
+ /** Hedges per 100 words. */
1524
+ hedgeRate: number;
1525
+ hedgedClose: boolean;
1526
+ /** Nominalisations per 100 words. */
1527
+ nominalisationRate: number;
1528
+ pronounOpenerShare: number;
1529
+ transitionOpenerShare: number;
1530
+ impersonalOpenerShare: number;
1531
+ commasPerSentence: number;
1532
+ commasPer100: number;
1533
+ /** Participial clauses per sentence. */
1534
+ participialRate: number;
1535
+ /** Phrasal coordinations per 100 words. */
1536
+ coordinationRate: number;
1537
+ /** Experimental. */
1538
+ skeletonRepeat: number;
1539
+ }
1540
+ /** Every feature for one block of copy: a paragraph, a blurb, an email body. */
1541
+ declare function shapeOf(text: string): ShapeFeatures;
1542
+ /** How many distinct shape moves a block makes. What a reader reacts to is the stack. */
1543
+ declare function shapeMoves(f: ShapeFeatures): string[];
1544
+ /** Paragraph-length CV for a document of four or more paragraphs; null below. */
1545
+ declare function paragraphUniformity(paragraphs: string[]): number | null;
1546
+ /** A block the size of a blurb: 2 to 8 sentences, 20 to 160 words. */
1547
+ declare function isBlurb(f: Pick<ShapeFeatures, "sentences" | "words">): boolean;
1548
+
1549
+ /**
1550
+ * Specificity tells: a page that could belong to anyone.
1551
+ *
1552
+ * A model given no brief writes the claim every site makes, because it has
1553
+ * nothing particular to say: "Quality you can trust", "Your vision, our
1554
+ * expertise". And it asserts standing ("trusted", "fully qualified",
1555
+ * "award-winning") with nothing next to the assertion to back it, because it
1556
+ * has no reviews and no registration number to put there.
1557
+ *
1558
+ * Both are read from what the page says, not how it looks: `specificity.ts`
1559
+ * counts the particulars.
1560
+ */
1561
+
1562
+ /**
1563
+ * The home page's source, where the first claim is made: a root route file
1564
+ * (`app/page.tsx`, `pages/index.tsx`, `src/pages/index.astro`, SvelteKit's
1565
+ * root `+page.svelte`), a top-level `index.html`, or a hero or home component.
1566
+ * Every other page's headline is a label ("Privacy policy", "Boiler
1567
+ * repairs"), not a claim, and is not judged.
1568
+ */
1569
+ declare const HOME_FILE: RegExp;
1570
+ /** A home page URL: the site root, `index.html`, or a locale root such as `/en-gb/`. */
1571
+ declare const HOME_URL: RegExp;
1572
+ /**
1573
+ * A claim of standing: what a visitor has to take on trust unless something
1574
+ * next to it backs it. Numbers of years are left out: those are checkable
1575
+ * facts, and `craft copy claims` lists them.
1576
+ */
1577
+ declare const STANDING_CLAIM: RegExp;
1578
+ /** Accreditation bodies, trade registers and review platforms, by the names they trade under. */
1579
+ declare const PROOF_NAMES: readonly string[];
1580
+ /** Every claim of standing with no review or accreditation in its section or the one either side. */
1581
+ declare function unprovenClaims(s: Snapshot): {
1582
+ claim: string;
1583
+ sentence: string;
1584
+ section: string;
1585
+ }[];
1586
+ declare const SPECIFICITY_COPY_TELLS: CopyTell[];
1587
+ declare const SPECIFICITY_RENDERED_TELLS: RenderedTell[];
1588
+
1329
1589
  /**
1330
1590
  * Baselines: adopt craft on a repo that already has findings, and fail only
1331
1591
  * on new ones.
@@ -1748,6 +2008,16 @@ declare const DISTANCE_WEIGHTS: {
1748
2008
  };
1749
2009
  /** Layout distance, 0 to 1: by role and geometry when both have them, else by kind as in version 1. */
1750
2010
  declare function layoutDistance(a: Fingerprint, b: Fingerprint): number;
2011
+ /** ΔE in OKLab. 0.02 is barely visible; by 0.3 two accents read as different colours. */
2012
+ declare const ACCENT_SCALE = 0.3;
2013
+ /** Grounds sit near white, where a smaller step already reads as a different paper. */
2014
+ declare const GROUND_SCALE = 0.15;
2015
+ /** A part at or below this distance counts as shared between two sites. */
2016
+ declare const SHARED_PART = 0.2;
2017
+ /** Two accents within this ΔE_OK read as the same colour family: about 6 on the familiar ΔE scale. */
2018
+ declare const ACCENT_SHARED: number;
2019
+ /** Two grounds within this ΔE_OK read as the same paper. */
2020
+ declare const GROUND_SHARED: number;
1751
2021
  declare function fingerprintDistance(a: Fingerprint, b: Fingerprint): FingerprintDistance;
1752
2022
  /**
1753
2023
  * The nearest neighbours of a fingerprint in a set, closest first. Typicality
@@ -1761,6 +2031,209 @@ declare function nearest<T>(target: Fingerprint, set: {
1761
2031
  distance: FingerprintDistance;
1762
2032
  }[];
1763
2033
 
2034
+ /**
2035
+ * `art-direction.json`: what a site decided about how it looks, and why.
2036
+ *
2037
+ * CHARACTER.md's reason rule lives here. Every expressive choice records a
2038
+ * `because` drawn from the client's real world (the shopfront, the van, the
2039
+ * trade's own lettering, the place, the materials) and points at a source
2040
+ * that shows it. A choice with no reason is a default, however good it looks.
2041
+ *
2042
+ * The file also carries the tell exceptions craft has read since phase A, in
2043
+ * the same shape, so an existing file stays valid.
2044
+ */
2045
+
2046
+ /** What `craft direction init` writes. Version 1 files still validate, with a warning. */
2047
+ declare const DIRECTION_VERSION = 2;
2048
+ declare const DIRECTION_VERSIONS: readonly [1, 2];
2049
+ /** The customer's own words: evidence for the job map, never for a token. */
2050
+ declare const CUSTOMER_KINDS: readonly ["review", "enquiry", "conversation", "search"];
2051
+ type CustomerKind = (typeof CUSTOMER_KINDS)[number];
2052
+ /**
2053
+ * Where a reason comes from. Deliberately physical: a mood board is not a
2054
+ * source. Customer-voice kinds back the job map. A `reference` is visual
2055
+ * inspiration from outside the client's category, and says where it is from.
2056
+ */
2057
+ declare const SOURCE_KINDS: readonly ["livery", "shopfront", "signage", "packaging", "product", "uniform", "interior", "place", "material", "trade", "print", "photo", "founder", "review", "enquiry", "conversation", "search", "reference"];
2058
+ type SourceKind = (typeof SOURCE_KINDS)[number];
2059
+ interface DirectionSource {
2060
+ /** Short id a choice refers to: "van", "fascia", "price-list". */
2061
+ id: string;
2062
+ kind: SourceKind;
2063
+ /** What it is and where, in a sentence. */
2064
+ note: string;
2065
+ /** A photo or file in the repo, or a URL. */
2066
+ path?: string;
2067
+ /** Colours read off it by a person or by `craft direction propose`, as hex. */
2068
+ colours?: string[];
2069
+ /** Lettering seen on it: "hand-painted sign-writing", "stencilled capitals". */
2070
+ lettering?: string;
2071
+ /** Where a `reference` comes from: "letterpress print", "a railway station", "a bakery". Never the client's own trade. */
2072
+ category?: string;
2073
+ }
2074
+ /** The expressive choices. Structure is the hierarchy layer, decided from the job. */
2075
+ declare const CHOICE_KEYS: readonly ["accent", "ground", "display", "body", "shape", "motif", "signature"];
2076
+ type ChoiceKey = (typeof CHOICE_KEYS)[number];
2077
+ interface DirectionChoice {
2078
+ /** A colour as hex for accent and ground, a family name for display and body, words otherwise. */
2079
+ value: string;
2080
+ because: string;
2081
+ /** Source ids this reason rests on. */
2082
+ evidence: string[];
2083
+ }
2084
+ /** One part of the job: what it is, and the customer's words that show it. */
2085
+ interface JobPart {
2086
+ value: string;
2087
+ evidence: string[];
2088
+ }
2089
+ declare const CONSIDERATION: readonly ["low", "considered", "high"];
2090
+ type Consideration = (typeof CONSIDERATION)[number];
2091
+ /**
2092
+ * What the visitor is trying to get done, from their side. Every hierarchy
2093
+ * reason is checked against it, so it replaces "what do other sites in the
2094
+ * trade do" as the starting point.
2095
+ */
2096
+ interface JobMap {
2097
+ /** Verb + object + context: "find" / "an electrician they can trust with a dangerous fault" / "today, without being overcharged". */
2098
+ statement: {
2099
+ verb: string;
2100
+ object: string;
2101
+ context: string;
2102
+ };
2103
+ functional: JobPart;
2104
+ emotional: JobPart;
2105
+ social: JobPart;
2106
+ /** How long and how carefully they decide: an emergency call-out is low, a retained plan is high. */
2107
+ consideration: {
2108
+ value: Consideration;
2109
+ because: string;
2110
+ evidence: string[];
2111
+ };
2112
+ objections: string[];
2113
+ /** Their own words, quoted from a cited source. */
2114
+ language: string[];
2115
+ /** The customer-voice sources the language is quoted from. */
2116
+ evidence: string[];
2117
+ }
2118
+ /** Page types a hierarchy is declared for. Each page has its own job on the site. */
2119
+ declare const PAGE_TYPES: readonly ["home", "service", "about", "pricing", "booking", "contact", "article", "landing"];
2120
+ type PageType = (typeof PAGE_TYPES)[number];
2121
+ /** Where a call to action can sit: a section role, or the header, or pinned while scrolling. */
2122
+ type ActionPosition = SectionRole | "header" | "sticky";
2123
+ interface PageHierarchy {
2124
+ primaryAction: {
2125
+ value: string;
2126
+ positions: ActionPosition[];
2127
+ because: string;
2128
+ evidence?: string[];
2129
+ };
2130
+ order: {
2131
+ role: SectionRole;
2132
+ because: string;
2133
+ }[];
2134
+ lead: {
2135
+ value: string;
2136
+ subordinate?: string[];
2137
+ because: string;
2138
+ };
2139
+ journey: {
2140
+ stage: string;
2141
+ because: string;
2142
+ }[];
2143
+ }
2144
+ declare const HIERARCHY_FIELDS: readonly ["primaryAction", "order", "lead", "journey"];
2145
+ interface ArtDirection {
2146
+ $schema?: string;
2147
+ version: (typeof DIRECTION_VERSIONS)[number];
2148
+ client: string;
2149
+ /** Who, where, and what they do, in a sentence or two. The brief a model would have been given. */
2150
+ brief: string;
2151
+ /** Version 2. */
2152
+ job?: JobMap;
2153
+ /** Version 2. Keyed by page type. */
2154
+ hierarchy?: Partial<Record<PageType, PageHierarchy>>;
2155
+ sources: DirectionSource[];
2156
+ choices: Partial<Record<ChoiceKey, DirectionChoice>>;
2157
+ exceptions?: TellException[];
2158
+ }
2159
+ type DirectionSeverity = "error" | "warn";
2160
+ interface DirectionProblem {
2161
+ severity: DirectionSeverity;
2162
+ /** Where in the file: "choices.accent.because", "sources[2].kind". */
2163
+ at: string;
2164
+ message: string;
2165
+ }
2166
+ interface DirectionReport {
2167
+ valid: boolean;
2168
+ problems: DirectionProblem[];
2169
+ /** How many of the seven choices carry an accepted reason. */
2170
+ decided: number;
2171
+ /** Each layer on its own. A site is only decided when all three are. */
2172
+ layers: {
2173
+ job: boolean;
2174
+ hierarchy: {
2175
+ page: PageType;
2176
+ decided: number;
2177
+ total: number;
2178
+ }[];
2179
+ tokens: {
2180
+ decided: number;
2181
+ total: number;
2182
+ };
2183
+ /** Job decided, at least a home page hierarchy with every field decided, and five of seven tokens. */
2184
+ complete: boolean;
2185
+ };
2186
+ }
2187
+
2188
+ /**
2189
+ * The estate check before anything is built: declared art directions compared
2190
+ * with each other, not rendered pages.
2191
+ *
2192
+ * `compareToEstate` catches two clients converging after both sites ship. By
2193
+ * then the fix is a retrofit. This compares what each `art-direction.json`
2194
+ * says it will be, so the convergence is caught while it is still a decision.
2195
+ *
2196
+ * The colour thresholds are the rendered check's own (`ACCENT_SHARED`,
2197
+ * `GROUND_SHARED`), so the two checks agree on what "the same accent" means.
2198
+ * It ranks and names what is shared; there is no sibling line until declared
2199
+ * directions have been calibrated against the rendered estate.
2200
+ *
2201
+ * Pure.
2202
+ */
2203
+
2204
+ /** What an art direction commits to, as the estate register stores it. */
2205
+ interface DirectionSummary {
2206
+ accent?: string;
2207
+ ground?: string;
2208
+ display?: string;
2209
+ body?: string;
2210
+ shape?: string;
2211
+ /** The home page's running order. */
2212
+ order?: SectionRole[];
2213
+ primaryAction?: string;
2214
+ positions?: string[];
2215
+ }
2216
+ declare function summariseDirection(d: Partial<ArtDirection>): DirectionSummary;
2217
+ interface DirectionComparison {
2218
+ shared: string[];
2219
+ accentDelta: number | null;
2220
+ groundDelta: number | null;
2221
+ /** Edit distance over the longer order, 0 to 1; null without both orders. */
2222
+ orderDistance: number | null;
2223
+ /** Worth a look before either is built. */
2224
+ flagged: boolean;
2225
+ }
2226
+ declare function compareDirections(a: DirectionSummary, b: DirectionSummary): DirectionComparison;
2227
+ interface DirectionMatch extends DirectionComparison {
2228
+ id: string;
2229
+ }
2230
+ /** Every site in the register with a declared direction, most shared first. */
2231
+ declare function compareToDeclared(target: DirectionSummary, sites: {
2232
+ id: string;
2233
+ direction?: DirectionSummary;
2234
+ }[], exclude?: string): DirectionMatch[];
2235
+ declare function formatDirectionMatches(label: string, matches: DirectionMatch[]): string;
2236
+
1764
2237
  /**
1765
2238
  * The estate register: every site the agency has shipped, fingerprinted, so a
1766
2239
  * new one can be checked against its siblings. Signal 3 of CHARACTER.md.
@@ -1793,10 +2266,13 @@ interface EstateSite {
1793
2266
  url: string;
1794
2267
  /** Where the site's source lives, if known: "rmp474/RMP-Electrical". */
1795
2268
  repo?: string;
1796
- fingerprint: Fingerprint;
2269
+ /** Absent for a direction registered before the site is built. */
2270
+ fingerprint?: Fingerprint;
1797
2271
  /** Rendered tells found when it was added, for the record. */
1798
2272
  tells: string[];
1799
2273
  addedAt: string;
2274
+ /** What its art-direction.json declares, so a new direction is checked before anything is built. */
2275
+ direction?: DirectionSummary;
1800
2276
  }
1801
2277
  interface EstateRegister {
1802
2278
  version: typeof ESTATE_VERSION;
@@ -1808,7 +2284,7 @@ declare function addSite(register: EstateRegister, site: EstateSite): EstateRegi
1808
2284
  /**
1809
2285
  * What two fingerprints effectively share, named the way a person would see
1810
2286
  * it: "Fraunces headline", "no accent", "pill buttons". A part counts when its
1811
- * distance is 0.2 or less.
2287
+ * distance is `SHARED_PART` or less.
1812
2288
  */
1813
2289
  declare function sharedParts(a: Fingerprint, b: Fingerprint, d?: FingerprintDistance): string[];
1814
2290
  interface EstateMatch {
@@ -2131,67 +2607,132 @@ declare const VAGUE_WORDS: readonly ["innovative", "scalable", "end-to-end", "st
2131
2607
  declare const REVIEW_PHRASES: readonly ["great question", "of course!", "absolutely!", "absolutely,", "truly", "genuinely", "deeply", "ultimately", "one-size-fits-all", "one size fits all", "let me know if you'd", "the single most", "by far the most", "hands down the", "arguably the best", "bar none"];
2132
2608
 
2133
2609
  /**
2134
- * `art-direction.json`: what a site decided about how it looks, and why.
2610
+ * The reason rule, one standard for every layer of an art direction.
2135
2611
  *
2136
- * CHARACTER.md's reason rule lives here. Every expressive choice records a
2137
- * `because` drawn from the client's real world (the shopfront, the van, the
2138
- * trade's own lettering, the place, the materials) and points at a source
2139
- * that shows it. A choice with no reason is a default, however good it looks.
2612
+ * A reason has to be specific enough that whoever reads the file next (a
2613
+ * person, Claude, a future tool) cannot drift back to the category default
2614
+ * while following it. "Freight Display, because MMM's photography needs to be
2615
+ * the hero and the type needs to recede" passes. "Freight Display" does not,
2616
+ * and neither does "a familiar structure so visitors feel comfortable".
2140
2617
  *
2141
- * The file also carries the tell exceptions craft has read since phase A, in
2142
- * the same shape, so an existing file stays valid.
2618
+ * Wording rules catch the common ways a default gets dressed up as a reason.
2619
+ * They are lexical, so they can be rephrased around; the tie is the real
2620
+ * guard. A token's reason must mention what its evidence shows. A hierarchy
2621
+ * reason must use this client's job: pasted onto another client's file, it
2622
+ * has to fail.
2623
+ *
2624
+ * Pure.
2143
2625
  */
2144
2626
 
2145
- declare const DIRECTION_VERSION = 1;
2146
- /** Where a reason comes from. Deliberately physical: a mood board is not a source. */
2147
- declare const SOURCE_KINDS: readonly ["livery", "shopfront", "signage", "packaging", "product", "uniform", "interior", "place", "material", "trade", "print", "photo", "founder"];
2148
- type SourceKind = (typeof SOURCE_KINDS)[number];
2149
- interface DirectionSource {
2150
- /** Short id a choice refers to: "van", "fascia", "price-list". */
2151
- id: string;
2152
- kind: SourceKind;
2153
- /** What it is and where, in a sentence. */
2154
- note: string;
2155
- /** A photo or file in the repo, or a URL. */
2156
- path?: string;
2157
- /** Colours read off it by a person or by `craft direction propose`, as hex. */
2158
- colours?: string[];
2159
- /** Lettering seen on it: "hand-painted sign-writing", "stencilled capitals". */
2160
- lettering?: string;
2161
- }
2162
- /** The expressive choices. Layout and navigation are not here: they stay conventional. */
2163
- declare const CHOICE_KEYS: readonly ["accent", "ground", "display", "body", "shape", "motif", "signature"];
2164
- type ChoiceKey = (typeof CHOICE_KEYS)[number];
2165
- interface DirectionChoice {
2166
- /** A colour as hex for accent and ground, a family name for display and body, words otherwise. */
2167
- value: string;
2168
- because: string;
2169
- /** Source ids this reason rests on. */
2170
- evidence: string[];
2171
- }
2172
- interface ArtDirection {
2173
- $schema?: string;
2174
- version: typeof DIRECTION_VERSION;
2175
- client: string;
2176
- /** Who, where, and what they do, in a sentence or two. The brief a model would have been given. */
2177
- brief: string;
2627
+ declare const PROPOSED = "PROPOSED:";
2628
+ /**
2629
+ * Reasons that are preferences, not evidence. "The client likes it" may be
2630
+ * true and still say nothing about why this site should look this way.
2631
+ */
2632
+ declare const PREFERENCE: RegExp;
2633
+ /** Adjectives that describe a mood rather than a source. Two or more and the reason is a mood board. */
2634
+ declare const MOOD: RegExp;
2635
+ /**
2636
+ * A default stated as a reason. Three families: authority ("best practice"),
2637
+ * the crowd ("what competitors do", "most salon sites") and familiarity
2638
+ * ("a familiar layout", "what visitors are used to"). Each is the category
2639
+ * average speaking, which is what the art direction exists to replace.
2640
+ */
2641
+ declare const CONVENTION: RegExp;
2642
+ /** Content words, lower-case, with a possessive stripped and short words dropped. */
2643
+ declare function contentWords(text: string): Set<string>;
2644
+ /** Every word of the job a hierarchy reason may tie to. */
2645
+ declare function jobWords(job: JobMap): Set<string>;
2646
+ interface ReasonInput {
2647
+ /** Where in the file: "choices.accent", "hierarchy.home.order[2]". Problems land at `${at}.because` and `${at}.evidence`. */
2648
+ at: string;
2649
+ because: unknown;
2650
+ evidence?: unknown;
2178
2651
  sources: DirectionSource[];
2179
- choices: Partial<Record<ChoiceKey, DirectionChoice>>;
2180
- exceptions?: TellException[];
2652
+ /**
2653
+ * What the reason must be about.
2654
+ * source mentions what its cited evidence shows (tokens)
2655
+ * customer the same, and every cited source is the customer's own words (the job map)
2656
+ * job uses this client's job (hierarchy); evidence optional
2657
+ */
2658
+ tie: "source" | "customer" | "job";
2659
+ job?: JobMap;
2181
2660
  }
2182
- type DirectionSeverity = "error" | "warn";
2183
- interface DirectionProblem {
2184
- severity: DirectionSeverity;
2185
- /** Where in the file: "choices.accent.because", "sources[2].kind". */
2186
- at: string;
2187
- message: string;
2661
+ /** Problems with one reason. None means it counts as decided. */
2662
+ declare function checkReason(input: ReasonInput): DirectionProblem[];
2663
+
2664
+ /**
2665
+ * The job map: what the visitor is trying to get done, in their words.
2666
+ *
2667
+ * It replaces "what do other sites in the trade do" as the starting point.
2668
+ * Hierarchy reasons are checked against it, so it has to describe this
2669
+ * client's customer, not the category: "find an electrician they can trust
2670
+ * with a dangerous fault, today" is a job; "electrician website" is a label.
2671
+ *
2672
+ * Pure. Quotes are checked against source text the caller can read.
2673
+ */
2674
+
2675
+ /** An object that is only trade nouns and website words: "electrician website", "a local salon site". */
2676
+ declare function isCategoryLabel(object: string, brief?: string): boolean;
2677
+ interface JobContext {
2678
+ brief?: string;
2679
+ /** Text of a cited source's file, when the caller can read it. */
2680
+ readSource?: (source: DirectionSource) => string | undefined;
2188
2681
  }
2189
- interface DirectionReport {
2190
- valid: boolean;
2682
+ declare function validateJob(job: unknown, sources: DirectionSource[], ctx?: JobContext): {
2191
2683
  problems: DirectionProblem[];
2192
- /** How many of the seven choices carry an accepted reason. */
2684
+ decided: boolean;
2685
+ };
2686
+
2687
+ /**
2688
+ * The hierarchy layer: what each page puts first, where it asks, and why.
2689
+ *
2690
+ * A conventional order is allowed and never penalised. What fails is an
2691
+ * order nobody chose: every field needs a reason that uses this client's job.
2692
+ * The primary action's reason must also say how the job's consideration (a
2693
+ * two-minute emergency call against a months-long decision) or one of its
2694
+ * objections leads to where the ask sits. The evidence on placement is mixed,
2695
+ * so craft encodes no default position; it requires the reason.
2696
+ *
2697
+ * Pure. The declared order is checked against a version 2 snapshot's section
2698
+ * roles when one is given.
2699
+ */
2700
+
2701
+ declare const SECTION_ROLES: readonly SectionRole[];
2702
+ interface PageResult {
2703
+ page: PageType;
2193
2704
  decided: number;
2705
+ total: number;
2706
+ }
2707
+ declare function validateHierarchy(hierarchy: unknown, job: JobMap | undefined, sources: DirectionSource[]): {
2708
+ problems: DirectionProblem[];
2709
+ pages: PageResult[];
2710
+ };
2711
+ /** Roles a snapshot shows, in running order, without the unclassified ones. */
2712
+ declare function renderedOrder(snapshot: Snapshot): SectionRole[] | null;
2713
+ /** The declared order against the page that ships. Drift warns; `strict` makes it an error. */
2714
+ declare function checkOrder(declared: PageHierarchy | undefined, page: PageType, snapshot: Snapshot, strict?: boolean): DirectionProblem[];
2715
+
2716
+ /**
2717
+ * The research prompt for a new client: job material only.
2718
+ *
2719
+ * A research model asked "what do good electrician websites look like"
2720
+ * returns the category average, because that is what it was trained on. So
2721
+ * the prompt craft hands over asks only about the customer (their situation,
2722
+ * objections, words, where they look) and forbids describing the category's
2723
+ * sites. Visual references, when wanted, come from outside the category.
2724
+ *
2725
+ * Pure text. Nothing here calls a model.
2726
+ */
2727
+ interface ResearchInput {
2728
+ brief: string;
2729
+ client?: string;
2730
+ /** Add prompts for visual references from outside the category. */
2731
+ visual?: boolean;
2194
2732
  }
2733
+ /** The trade words in a brief, for naming what not to research. */
2734
+ declare function tradeOf(brief: string): string[];
2735
+ declare function researchPrompt(input: ResearchInput): string;
2195
2736
 
2196
2737
  /**
2197
2738
  * Checking an art direction: the file's shape, the reason rule, and whether
@@ -2203,14 +2744,30 @@ interface DirectionReport {
2203
2744
  interface ValidateContext {
2204
2745
  /** The fingerprint of the rendered site, to check the file against what ships. */
2205
2746
  fingerprint?: Fingerprint;
2747
+ /** The rendered page itself, to check the declared order against its section roles. */
2748
+ snapshot?: Snapshot;
2749
+ /** Which page the snapshot is. Default home. */
2750
+ page?: PageType;
2751
+ /** Drift between the file and the page is an error, for a pre-launch gate. */
2752
+ strict?: boolean;
2206
2753
  /** Which cited paths exist, when the caller can look. */
2207
2754
  pathExists?: (path: string) => boolean;
2755
+ /** Text of a cited source's file, for checking quoted customer language. */
2756
+ readSource?: (source: DirectionSource) => string | undefined;
2757
+ /** Declared directions of the other sites in the estate, to catch convergence before build. */
2758
+ estate?: {
2759
+ id: string;
2760
+ direction?: DirectionSummary;
2761
+ }[];
2762
+ /** This site's id in the estate, left out of the comparison. */
2763
+ estateId?: string;
2208
2764
  }
2209
2765
  declare function validateDirection(input: unknown, ctx?: ValidateContext): DirectionReport;
2210
2766
 
2211
2767
  /**
2212
2768
  * A first art-direction.json for a site that exists: what it does today,
2213
- * written down, with every reason left empty.
2769
+ * written down (its tokens and, from a version 2 snapshot, its running order),
2770
+ * with every reason and the whole job map left empty.
2214
2771
  *
2215
2772
  * Empty on purpose. `validateDirection` fails the file until each choice has
2216
2773
  * a source and a reason, so the first thing init produces is the list of
@@ -2277,7 +2834,6 @@ declare function licencesJson(): string;
2277
2834
  * Pure. Pixels come in as numbers; the CLI decodes the images.
2278
2835
  */
2279
2836
 
2280
- declare const PROPOSED = "PROPOSED:";
2281
2837
  interface PaletteColour {
2282
2838
  hex: string;
2283
2839
  /** Share of sampled pixels, 0 to 1. */
@@ -2301,6 +2857,11 @@ interface ProposeInput {
2301
2857
  id: string;
2302
2858
  fingerprint: Fingerprint;
2303
2859
  }[];
2860
+ /** Accents other sites have declared but not yet shipped: a sibling can happen before either is built. */
2861
+ declared?: {
2862
+ id: string;
2863
+ accent: string;
2864
+ }[];
2304
2865
  }
2305
2866
  interface Proposal {
2306
2867
  key: ChoiceKey;
@@ -2390,4 +2951,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
2390
2951
 
2391
2952
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
2392
2953
 
2393
- 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 ComponentPair, type ComponentTypicality, 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, 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, PROPOSED, PROVENANCE_SCAN_BYTES, 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, SPRING, STOCK_AVATAR_HOSTS, 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 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, closestComponents, compareFacts, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, craftTokens, createBaseline, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estateComponentPairs, 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, scanProvenance, scanSource, sectionRhythm, semantic, severityExceptions, shapeName, sharedParts, shouldAnimate, siblingLine, stockAvatarSource, stockSource, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, typeFeatureTokens, typicality, validateDirection, visibleText, visualMeasures, wcagContrast, wordListsJson };
2954
+ export { ACCENT_SCALE, ACCENT_SHARED, AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type ActionPosition, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, COMPONENT_ROLES, CONSIDERATION, CONVENTION, CUSTOMER_KINDS, 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 Consideration, type ContrastCheck, type ContrastReport, type CopySide, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, type CustomerKind, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DIRECTION_VERSIONS, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionComparison, type DirectionMatch, type DirectionProblem, type DirectionReport, type DirectionSource, type DirectionSummary, 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, FUNCTION_WORDS, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, GROUND_SCALE, GROUND_SHARED, type Gamut, type Generation, HIERARCHY_FIELDS, 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, type JobContext, type JobMap, type JobPart, LICENCES, LIGHTNESS_CURVE, type LicenceEntry, type LicenceSubject, MIN_DECIDED, MIN_RUNS, MOOD, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, type Oklch, type OklchToHexOptions, PAGE_TYPES, PILL_LIMIT, PLAINER_WORDS, PREFERENCE, PROMO_BADGE, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageCopy, type PageHierarchy, type PageResult, type PageType, 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 ReasonInput, type RenderedTell, type ResearchInput, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SCALE, SECTION_ROLES, SHADCN_LIMIT, SHADCN_PRIMITIVES, SHARED_PART, 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 ShapeFeatures, 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 Tricolon, type TypeStep, type Typicality, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, briefTerms, catalogueTable, characterReport, checkCopy, checkOrder, checkPair, checkReason, checkRestraint, choicesOf, clauseDepth, closestComponents, compareCompetitor, compareDirections, compareFacts, compareToDeclared, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, contentWords, countWords, craftTokens, createBaseline, cv, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estateComponentPairs, estatePairs, exitDuration, extractHtml, faceLicence, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatCompetitor, formatDirectionMatches, formatHex, formatReport, globToRegExp, groundName, harvest, hedgeCount, hedgedClose, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isBlurb, isCategoryLabel, isCream, jobWords, layoutDistance, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, nominalisations, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, pageCopy, paletteFromPixels, paragraphUniformity, parseBaseline, parseColour, parseCraftConfig, parseHex, participialClauses, phrasalCoordinations, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, renderedOrder, researchPrompt, retrofitPlan, runTell, scanProvenance, scanSource, sectionRhythm, semantic, sentencesOf, severityExceptions, shapeMoves, shapeName, shapeOf, words as shapeWords, sharedParts, shouldAnimate, siblingLine, skeleton, skeletonRepeat, specificity, specifics, splitSentences, stackedConditional, stockAvatarSource, stockSource, summariseDirection, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, tradeOf, tricolons, typeFeatureTokens, typicality, unprovenClaims, validateDirection, validateHierarchy, validateJob, visibleText, visualMeasures, wcagContrast, wordListsJson };