@domandigital/craft 0.9.0 → 0.10.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.cjs CHANGED
@@ -91,6 +91,7 @@ __export(index_exports, {
91
91
  estatePairs: () => estatePairs,
92
92
  exitDuration: () => exitDuration,
93
93
  extractHtml: () => extractHtml,
94
+ findClaims: () => findClaims,
94
95
  findColours: () => findColours,
95
96
  fingerprint: () => fingerprint,
96
97
  fingerprintDistance: () => fingerprintDistance,
@@ -98,6 +99,7 @@ __export(index_exports, {
98
99
  fluidSpace: () => fluidSpace,
99
100
  fluidType: () => fluidType,
100
101
  fontClass: () => fontClass,
102
+ formatClaims: () => formatClaims,
101
103
  formatComparison: () => formatComparison,
102
104
  formatHex: () => formatHex,
103
105
  formatReport: () => formatReport,
@@ -114,6 +116,7 @@ __export(index_exports, {
114
116
  isCream: () => isCream,
115
117
  makeSnapshot: () => makeSnapshot,
116
118
  motionTokens: () => motionTokens,
119
+ namedSources: () => namedSources,
117
120
  nearest: () => nearest,
118
121
  normaliseFamily: () => normaliseFamily,
119
122
  nullPrompt: () => nullPrompt,
@@ -1932,10 +1935,14 @@ var COPY_TELLS = [
1932
1935
  surface: "copy",
1933
1936
  why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') is the second wave's favourite rhythm.",
1934
1937
  fix: "Write one sentence that says which of the three is true, and how you know.",
1935
- detect: phrases([/(?:\b[A-Z][\w'’-]*(?:\s+[\w'’-]+){0,2}[.!]\s+){2}[A-Z][\w'’-]*(?:\s+[\w'’-]+){0,2}[.!](?=\s|$)/], quoted),
1938
+ // The fragments share one paragraph: "You don't. Again.", a blank line, then "It's frustrating." is two beats, not three.
1939
+ detect: phrases([/(?:\b[A-Z][\w'’-]*(?:[ \t]+[\w'’-]+){0,2}[.!](?:[ \t]+|[ \t]*\n(?![ \t]*\n)[ \t]*)){2}[A-Z][\w'’-]*(?:[ \t]+[\w'’-]+){0,2}[.!](?=\s|$)/], quoted),
1936
1940
  fixtures: {
1937
- flag: [f("content/home.md", "No fuss. No jargon. Just results.")],
1938
- pass: [f("content/home.md", "We fix boilers the same day, and we tell you the price before we start.")]
1941
+ flag: [f("content/home.md", "No fuss. No jargon. Just results."), f("content/home.md", "No fuss.\nNo jargon.\nJust results.")],
1942
+ pass: [
1943
+ f("content/home.md", "We fix boilers the same day, and we tell you the price before we start."),
1944
+ f("content/guide.md", "Your competitor appears in the top three. You don't. Again.\n\nIt's frustrating.")
1945
+ ]
1939
1946
  }
1940
1947
  },
1941
1948
  {
@@ -1954,6 +1961,14 @@ var COPY_TELLS = [
1954
1961
  }
1955
1962
  ];
1956
1963
  var ING_TAIL = /,\s+(?:ensuring|highlighting|underscoring|showcasing|reflecting|demonstrating|emphasi[sz]ing|fostering|cementing|solidifying|reinforcing|symboli[sz]ing|signalling|signaling)\b[^.!?\n]{0,80}/i;
1964
+ var PROMPT_CONTEXT = [
1965
+ /\b(?:the|this|that)\s+(?:attached|uploaded|provided)\s+(?:(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\s+)?(?:research|research file|research pack|source file|sources)\b/i,
1966
+ /\b(?:attached|uploaded)\s+(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\b/i,
1967
+ /\bthe\s+(?:attached|uploaded|provided)\s+brief\s+(?:asks|says|wants|requests|suggests|mentions)\b/i
1968
+ ];
1969
+ function gerundList(block, _match, index) {
1970
+ return /(?:^|[(,;:]\s*)\w+ing\b[^,.;()!?]{0,40}$/.test(block.text.slice(Math.max(0, index - 60), index));
1971
+ }
1957
1972
  var VAGUE_ATTRIBUTION = [
1958
1973
  /\b(?:studies|research|experts|scientists|surveys|statistics|the data)\s+(?:show|shows|suggest|suggests|agree|say|says|confirm|confirms|prove|proves|indicate|indicates)\b/i,
1959
1974
  /\b(?:many|most)\s+(?:experts|people|customers|professionals|homeowners)\s+(?:agree|say|believe)\b/i,
@@ -2039,8 +2054,10 @@ function inlineLabelHits(ctx) {
2039
2054
  run = [];
2040
2055
  };
2041
2056
  for (const line of lines) {
2042
- const m = /^\s*(?:[-*+]|\d+\.)\s+\*\*[^*]{1,40}:?\*\*:?\s+(\S.*)$/.exec(line);
2043
- if (m && m[1].trim().split(/\s+/).length <= 6) run.push(offset);
2057
+ const m = /^\s*[-*+]\s+\*\*([^*]{1,40}?):?\*\*:?\s+(\S.*)$/.exec(line);
2058
+ const data = m && /[\d£$€%`]|https?:|^\*\*/.test(m[2]);
2059
+ const named = m && /\S[A-Z]|[./]/.test(m[1]);
2060
+ if (m && !data && !named && m[2].trim().split(/\s+/).length <= 6) run.push(offset);
2044
2061
  else flush();
2045
2062
  offset += line.length + 1;
2046
2063
  }
@@ -2078,6 +2095,29 @@ var RESEARCH_TELLS = [
2078
2095
  ]
2079
2096
  }
2080
2097
  },
2098
+ {
2099
+ id: "prompt-context",
2100
+ name: "Prompt context",
2101
+ generation: 1,
2102
+ severity: "warn",
2103
+ surface: "copy",
2104
+ why: "'This article uses the attached Perplexity research file as the primary source', 'the attached research is right to say': the model talking about what it was given. A reader of a web page has no attachment. Found 38 times across five live DD articles in 2026-09, none caught by any other tell.",
2105
+ fix: "Delete the reference to the research or brief. Where it carried a claim, name the real source or cut the claim.",
2106
+ detect: phrases(PROMPT_CONTEXT, quoted),
2107
+ fixtures: {
2108
+ flag: [
2109
+ f("content/guide.md", "This article uses the attached Perplexity research file as the primary source."),
2110
+ f("content/guide.md", "The attached research is right to say these platforms suit early-stage businesses."),
2111
+ f("content/guide.md", "That is exactly how the uploaded research frames the category."),
2112
+ f("content/guide.md", "The attached brief asks for the point that most tradespeople miss.")
2113
+ ],
2114
+ pass: [
2115
+ f("content/guide.md", "We research every quote before we send it."),
2116
+ f("app/email.tsx", `export const Invoice = () => <p>Your invoice is attached.</p>;`),
2117
+ f("content/guide.md", "BrightLocal's research shows that reviews drive 20% of local ranking.")
2118
+ ]
2119
+ }
2120
+ },
2081
2121
  {
2082
2122
  id: "placeholder",
2083
2123
  name: "Unfilled placeholder",
@@ -2130,7 +2170,11 @@ var RESEARCH_TELLS = [
2130
2170
  pass: [
2131
2171
  f("content/home.md", "- Pages load in under a second\n- Card details never touch our server\n- Support by phone until 8pm\n"),
2132
2172
  f("content/home.md", "- **Speed:** Faster pages\n- **Security:** Safer data\n"),
2133
- f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n")
2173
+ f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n"),
2174
+ f("content/guide.md", "- **Per page**: \xA3100-\xA3200 per page\n- **Hourly**: \xA350-\xA375/hour\n- **Day rate**: \xA3350-\xA3500\n"),
2175
+ f("content/guide.md", "- **Law Society Find a Solicitor:** For solicitors\n- **AccountingWEB / ICAEW directories:** For accountants\n- **NHS.uk:** For NHS services\n"),
2176
+ f("content/guide.md", "1. **Visit PageSpeed Insights** and paste the address\n2. **Enter their URL** and run it\n3. **Note their scores** for later\n"),
2177
+ f("content/guide.md", "- **Structural problems:** **REBUILD.**\n- **Messaging issues:** **REFRESH.**\n- **Neither:** **LEAVE IT.**\n")
2134
2178
  ]
2135
2179
  }
2136
2180
  },
@@ -2142,7 +2186,7 @@ var RESEARCH_TELLS = [
2142
2186
  surface: "copy",
2143
2187
  why: "'..., ensuring peace of mind', '..., highlighting our commitment': a participle tacked on the end that claims significance and names no mechanism. One of the most common shapes in the Wikipedia guide to AI writing.",
2144
2188
  fix: "Cut the tail, or replace it with the mechanism: 'so you can drop the car off before work'.",
2145
- detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, ""))),
2189
+ detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, "")), void 0, gerundList),
2146
2190
  fixtures: {
2147
2191
  flag: [
2148
2192
  f("content/home.md", "Every engineer is Gas Safe registered, ensuring complete peace of mind."),
@@ -2150,7 +2194,8 @@ var RESEARCH_TELLS = [
2150
2194
  ],
2151
2195
  pass: [
2152
2196
  f("content/home.md", "We open at 7am, so you can drop the car off before work."),
2153
- f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers.")
2197
+ f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers."),
2198
+ f("content/guide.md", "Many items (claiming your GBP, ensuring NAP consistency, writing title tags) are straightforward.")
2154
2199
  ]
2155
2200
  }
2156
2201
  },
@@ -2400,6 +2445,7 @@ function repeatHits(ctx) {
2400
2445
  for (const s of sentences(doc2)) {
2401
2446
  const words2 = normal(s.text);
2402
2447
  if (words2.length < RUN || !/[.!?]["'’”)]*$/.test(s.text)) continue;
2448
+ if (/^\s*\[\^[\w-]+\]:/.test(s.text)) continue;
2403
2449
  const key = words2.join(" ");
2404
2450
  const earlier = whole.get(key);
2405
2451
  if (earlier) {
@@ -2597,7 +2643,15 @@ var DENSITY_TELLS = [
2597
2643
  f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nMore here.\n\nWe check every valve before the engineer leaves the house.")),
2598
2644
  f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nBy then we check every valve before the engineer leaves the site."))
2599
2645
  ],
2600
- pass: [f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nThe engineer leaves once the customer has signed the sheet."))]
2646
+ pass: [
2647
+ f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nThe engineer leaves once the customer has signed the sheet.")),
2648
+ f2(
2649
+ "content/guide.md",
2650
+ doc(
2651
+ "The Independent Healthcare Providers Network Private Patient Insight Report 2024 found that private demand rose.[^1]\n\n[^1]: Independent Healthcare Providers Network, Private Patient Insight Report 2024, published online."
2652
+ )
2653
+ )
2654
+ ]
2601
2655
  }
2602
2656
  },
2603
2657
  {
@@ -3634,6 +3688,9 @@ var HOUSE = {
3634
3688
  "closing-summary": { tier: "review", label: "closing summary" },
3635
3689
  "false-range": { tier: "review", label: "false range" },
3636
3690
  "placeholder": { tier: "review", label: "unfilled placeholder" },
3691
+ // Every estate hit so far is real (38 in five DD articles, 2026-09-24), but
3692
+ // a proposal can honestly say "the attached research". Review until measured there.
3693
+ "prompt-context": { tier: "review", label: "prompt context: the model naming its own inputs" },
3637
3694
  "question-reveal": { tier: "review", label: "staged reveal" },
3638
3695
  "inline-label-list": { tier: "review", label: "bold-label bullets" },
3639
3696
  "phrase-density": { tier: "review", label: "density" },
@@ -4316,7 +4373,7 @@ var RENDERED_PATHS = {
4316
4373
  };
4317
4374
 
4318
4375
  // src/character/check.ts
4319
- var CATALOGUE_VERSION = "2026.09.7";
4376
+ var CATALOGUE_VERSION = "2026.09.8";
4320
4377
  var CATALOGUE = [...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map(
4321
4378
  (t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t
4322
4379
  );
@@ -4581,6 +4638,120 @@ function formatComparison(result, beforePath, afterPath) {
4581
4638
  return lines.join("\n");
4582
4639
  }
4583
4640
 
4641
+ // src/character/claims.ts
4642
+ var CLAIM_KINDS = /* @__PURE__ */ new Set(["money", "number", "date"]);
4643
+ var PROPER = "[A-Z][\\w&.\u2019'-]*(?:\\s+(?:of|and|&|the|for)?\\s*[A-Z][\\w&.\u2019'-]*)*";
4644
+ var NOT_A_SOURCE = /^(?:We|I|It|This|That|These|Those|They|He|She|You|Our|Your|The|A|An|Each|Every|Most|Many|Some|One|Research|Studies|Experts|Data|Analysis|If|When|So|But|And|Or|In|On|At|From|For|By|As|Since|After|Before|Across|Over|Under|Its|Their|External|Source|UK|US|EU|Sometimes|Public|Recent|Industry|Independent|What|How|Why|Who|Where|Businesses|Customers|Clients|Patients|Consumers|People|Users|Online)$/;
4645
+ var NOUN = "study|survey|report|research|analysis|benchmark|benchmarks|guidelines|docs|documentation|help centre|help center|pricing page|figures|statistics|case study";
4646
+ var OWNED_NOUN = "guide|audit|support|pricing|data|site|website|blog";
4647
+ var VERB = "found|finds|reports?|reported|says|said|states?|stated|estimates?|estimated|recommends?|publishes|published|advertises|advertised|shows|showed|claims";
4648
+ var ATTRIBUTION = [
4649
+ new RegExp(`\\b[Aa]ccording to\\s+(?:the\\s+)?(${PROPER})`, "g"),
4650
+ new RegExp(`\\b(?:${NOUN})\\s+(?:by|from|commissioned by)\\s+(?:the\\s+)?(${PROPER})`, "g"),
4651
+ // "Akamai’s 2017 retail study", "the 2020 Deloitte study", "Bark’s UK guide", "Wix’s own developer guidelines".
4652
+ new RegExp(`\\b(${PROPER})(?:(?:\u2019s|'s)\\s+(?:own\\s+)?(?:\\d{4}\\s+)?(?:[A-Za-z]+\\s+)?(?:${NOUN}|${OWNED_NOUN})|\\s+(?:\\d{4}\\s+)?(?:[a-z]+\\s+)?(?:${NOUN}))\\b`, "g"),
4653
+ // "Google also reported", "Shopify UK currently advertises".
4654
+ new RegExp(`\\b(${PROPER})\\s+(?:also\\s+|now\\s+|currently\\s+|still\\s+)?(?:${VERB})\\b`, "g"),
4655
+ /\((?:[Ss]ources?:\s*|via\s+(?=[A-Z]))([^)]+)\)/g,
4656
+ /^\s*(?:source|sources)\s*:\s*(.+)$/gim
4657
+ ];
4658
+ var LINK2 = /\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)|\bhttps?:\/\/[^\s<>"')\]]+/g;
4659
+ function cleanName(raw) {
4660
+ const words2 = raw.trim().replace(/[.,;:]+$/, "").split(/\s+/);
4661
+ while (words2.length && NOT_A_SOURCE.test(words2[0])) words2.shift();
4662
+ return words2.join(" ").replace(/(?:’s|'s)(?=\s|$).*$/, "").trim();
4663
+ }
4664
+ var IMPERATIVE = /^(?:Add|Use|Check|Make|Get|Set|Include|Create|Update|List|Keep|Ask|Write|Track|Test|Claim|Build|Link|Submit|Verify|Review|Fix|Remove|Upload|Respond|Reply|Choose|Pick|Start|Run|Open|Install|Connect|Enable|Turn|Put|Give|Send|Book|Call|Read|See|Try|Compare|Aim|Target|Avoid|Post|Share|Mark|Tag)$/;
4665
+ var atStart = (sentence, index) => /^[\s*_"“”’(>#|-]*(?:\d{1,3}[.)]\s*)?[\s*_]*(?:[\w ]+:\**\s*)?$/.test(sentence.slice(0, index)) || /\|\s*\**$/.test(sentence.slice(0, index));
4666
+ function namedSources(sentence) {
4667
+ const found = /* @__PURE__ */ new Set();
4668
+ ATTRIBUTION.forEach((pattern, i) => {
4669
+ for (const m of sentence.matchAll(pattern)) {
4670
+ const raw = m[1].trim();
4671
+ const word = !/\s/.test(raw) && !/[’']s$/.test(m[0].split(/\s+/)[0]);
4672
+ if (i === 2 && word && atStart(sentence, m.index ?? 0)) continue;
4673
+ if (IMPERATIVE.test(raw.split(/\s+/)[0]) && atStart(sentence, m.index ?? 0)) continue;
4674
+ const name = cleanName(raw);
4675
+ if (!name || name.length < 2 || !/\s/.test(name) && /[a-z]ing$/.test(name)) continue;
4676
+ found.add(name);
4677
+ }
4678
+ });
4679
+ for (const m of sentence.matchAll(LINK2)) found.add(m[2] ?? m[0]);
4680
+ return [...found];
4681
+ }
4682
+ var FOOTNOTE_REF = /\[\^([\w-]+)\](?!:)/g;
4683
+ var FOOTNOTE_DEF = /^[ \t]*\[\^([\w-]+)\]:[ \t]*(.+)$/gm;
4684
+ var ASSUMPTION = /^(?:\*\*)?(?:assume|suppose|imagine|say|let's say|for example, say|picture)\b/i;
4685
+ var LEAD = /^\s*(?:#{1,6}\s+|>\s*|[-*+]\s+|\d+[.)]\s+)*/;
4686
+ function sentences2(text) {
4687
+ const out = [];
4688
+ let start = 0;
4689
+ let paragraph = 0;
4690
+ const push = (end) => {
4691
+ const raw = text.slice(start, end);
4692
+ const lead = raw.length - raw.trimStart().length;
4693
+ if (raw.trim()) out.push({ text: raw.trim(), offset: start + lead, paragraph });
4694
+ };
4695
+ for (const m of text.matchAll(/(?<!(?:^|\n)[ \t]*\d{1,3})[.!?](?:\[\^[\w-]+\])*(?=["”’)]?[ \t]+["“(*]*[A-Z0-9£$€])|\n+/g)) {
4696
+ const end = (m.index ?? 0) + m[0].length;
4697
+ push(end);
4698
+ start = end;
4699
+ if (/\n\s*\n/.test(m[0]) || /^\s*(?:#|\|)/.test(text.slice(end))) paragraph += 1;
4700
+ }
4701
+ push(text.length);
4702
+ return out;
4703
+ }
4704
+ var MAX = 180;
4705
+ var trim = (s) => s.length > MAX ? `${s.slice(0, MAX - 1).trimEnd()}\u2026` : s;
4706
+ function findClaims(files) {
4707
+ const claims = [];
4708
+ for (const file of files) {
4709
+ for (const block of extractCopy(file)) {
4710
+ const notes = new Map([...block.text.matchAll(FOOTNOTE_DEF)].map((m) => [m[1], m[2].trim()]));
4711
+ let carried;
4712
+ for (const s of sentences2(block.text)) {
4713
+ if (/^\s*#{1,6}\s/.test(s.text) || /^\s*\[\^[\w-]+\]:/.test(s.text)) continue;
4714
+ const refs = [...s.text.matchAll(FOOTNOTE_REF)].map((m) => notes.get(m[1]) ?? `footnote ${m[1]}`);
4715
+ const text = s.text.replace(FOOTNOTE_REF, "");
4716
+ const body = text.replace(LEAD, "");
4717
+ const own = [...namedSources(text), ...refs];
4718
+ if (own.length) carried = { paragraph: s.paragraph, sources: own };
4719
+ if (ASSUMPTION.test(body)) continue;
4720
+ const facts = protectedFacts(body).filter((f4) => CLAIM_KINDS.has(f4.kind));
4721
+ if (facts.length === 0 && own.length === 0) continue;
4722
+ const nearby = own.length === 0 && carried?.paragraph === s.paragraph ? carried.sources : void 0;
4723
+ const at = block.offset + s.offset;
4724
+ claims.push({
4725
+ path: file.path,
4726
+ line: file.text.slice(0, at).split("\n").length,
4727
+ sentence: trim(text.replace(/\s+/g, " ")),
4728
+ facts,
4729
+ sources: own,
4730
+ ...nearby ? { nearby } : {}
4731
+ });
4732
+ }
4733
+ }
4734
+ }
4735
+ return { files: files.length, claims, unsourced: claims.filter((c) => c.sources.length === 0).length };
4736
+ }
4737
+ function formatClaims(report2, label = "craft copy claims") {
4738
+ const { claims, unsourced } = report2;
4739
+ if (claims.length === 0) return `${label}: no prices, figures, dates or named sources in ${report2.files} file${report2.files === 1 ? "" : "s"}.`;
4740
+ const lines = [
4741
+ `${label}: ${claims.length} claim${claims.length === 1 ? "" : "s"} to check, ${unsourced} with no source named.`,
4742
+ " Check each against its primary source before it ships. Sourced means a source is named, not that it agrees."
4743
+ ];
4744
+ for (const c of claims) {
4745
+ const what = [
4746
+ ...c.facts.map((f4) => f4.value),
4747
+ ...c.sources.map((s) => `source: ${s}`),
4748
+ ...c.nearby ? [`${c.nearby.join(", ")} named earlier in the paragraph: does it cover this?`] : []
4749
+ ].join(" \xB7 ");
4750
+ lines.push("", ` ${c.path}:${c.line} ${c.sources.length ? "sourced " : "UNSOURCED"} ${what}`, ` ${c.sentence}`);
4751
+ }
4752
+ return lines.join("\n");
4753
+ }
4754
+
4584
4755
  // src/fingerprint/index.ts
4585
4756
  var FINGERPRINT_VERSION = 1;
4586
4757
  var CHROMATIC = 0.03;
@@ -5544,6 +5715,7 @@ function retrofitPlan(report2, direction) {
5544
5715
  estatePairs,
5545
5716
  exitDuration,
5546
5717
  extractHtml,
5718
+ findClaims,
5547
5719
  findColours,
5548
5720
  fingerprint,
5549
5721
  fingerprintDistance,
@@ -5551,6 +5723,7 @@ function retrofitPlan(report2, direction) {
5551
5723
  fluidSpace,
5552
5724
  fluidType,
5553
5725
  fontClass,
5726
+ formatClaims,
5554
5727
  formatComparison,
5555
5728
  formatHex,
5556
5729
  formatReport,
@@ -5567,6 +5740,7 @@ function retrofitPlan(report2, direction) {
5567
5740
  isCream,
5568
5741
  makeSnapshot,
5569
5742
  motionTokens,
5743
+ namedSources,
5570
5744
  nearest,
5571
5745
  normaliseFamily,
5572
5746
  nullPrompt,
package/dist/index.d.cts CHANGED
@@ -1031,7 +1031,7 @@ interface CheckReport {
1031
1031
  * Bumped whenever an entry is added, removed or its detection changes, so a
1032
1032
  * report can say which list it was judged against.
1033
1033
  */
1034
- declare const CATALOGUE_VERSION = "2026.09.7";
1034
+ declare const CATALOGUE_VERSION = "2026.09.8";
1035
1035
  declare const CATALOGUE: readonly Tell[];
1036
1036
  declare function tellById(id: string): Tell | undefined;
1037
1037
  /**
@@ -1147,6 +1147,47 @@ interface FactComparison {
1147
1147
  declare function compareFacts(before: string, after: string): FactComparison;
1148
1148
  declare function formatComparison(result: FactComparison, beforePath: string, afterPath: string): string;
1149
1149
 
1150
+ /**
1151
+ * The claims list: every sentence a reader would take as a checkable fact.
1152
+ *
1153
+ * No tell can say whether "Bark charges £2,000 to £7,000" is true. The worst
1154
+ * faults in the DD article that set this off (2026-09-24) were figures like
1155
+ * that: a wrong range, an unsourced day rate, a study quoted past what it
1156
+ * found. Every one passed the copy check, because the prose was fine.
1157
+ *
1158
+ * So this does not judge. It lists each sentence holding a price, a figure, a
1159
+ * date or a named source, marks whether the sentence names where it came
1160
+ * from, and leaves the verdict to a person with the primary source open.
1161
+ * "Sourced" means a source is named, not that it says what the sentence
1162
+ * claims: that is exactly what the person checks.
1163
+ *
1164
+ * Pure over text. `craft copy claims <paths...>` is the command.
1165
+ */
1166
+
1167
+ interface Claim {
1168
+ path: string;
1169
+ line: number;
1170
+ /** The sentence, trimmed to a readable length. */
1171
+ sentence: string;
1172
+ /** The prices, figures and dates in it. */
1173
+ facts: Fact[];
1174
+ /** Who the sentence says it comes from: a name, a publication or a link. Empty when nobody is named. */
1175
+ sources: string[];
1176
+ /**
1177
+ * A source named earlier in the same paragraph. A hint for the person, never
1178
+ * a pass: the sentence may be the writer's own arithmetic on top of it.
1179
+ */
1180
+ nearby?: string[];
1181
+ }
1182
+ interface ClaimsReport {
1183
+ files: number;
1184
+ claims: Claim[];
1185
+ unsourced: number;
1186
+ }
1187
+ declare function namedSources(sentence: string): string[];
1188
+ declare function findClaims(files: SourceFile[]): ClaimsReport;
1189
+ declare function formatClaims(report: ClaimsReport, label?: string): string;
1190
+
1150
1191
  /**
1151
1192
  * The rendered detection path for tells that can be seen on a live page.
1152
1193
  *
@@ -1779,4 +1820,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
1779
1820
 
1780
1821
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
1781
1822
 
1782
- export { AI_PHRASES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BUZZWORDS, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, type CandidateKind, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type ContrastCheck, type ContrastReport, type CopyTell, type CraftTokensInput, type CraftTokensResult, 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, 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 FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOUSE, HOUSE_BUDGET, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, LIGHTNESS_CURVE, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, 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, 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, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPRING, STOCK_PHRASES, type SectionKind, type SectionRhythmOptions, type SemanticInput, type SemanticResult, type Severity, 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 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, applyHouseGate, auditSnapshot, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, compareFacts, compareToEstate, craftTokens, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estatePairs, exitDuration, extractHtml, findColours, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatComparison, formatHex, formatReport, groundName, harvest, hexToOklch, houseRule, hueName, hueSwatch, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, makeSnapshot, motionTokens, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, paletteFromPixels, parseColour, parseHex, propose, protectedFacts, ramp, rampFromAnchors, retrofitPlan, runTell, scanSource, sectionRhythm, semantic, shapeName, sharedParts, shouldAnimate, siblingLine, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, typeFeatureTokens, typicality, validateDirection, visibleText, wcagContrast, wordListsJson };
1823
+ export { AI_PHRASES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BUZZWORDS, 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 CraftTokensInput, type CraftTokensResult, 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, 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 FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOUSE, HOUSE_BUDGET, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, LIGHTNESS_CURVE, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, 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, 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, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPRING, STOCK_PHRASES, type SectionKind, type SectionRhythmOptions, type SemanticInput, type SemanticResult, type Severity, 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 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, applyHouseGate, auditSnapshot, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, compareFacts, compareToEstate, craftTokens, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estatePairs, exitDuration, extractHtml, findClaims, findColours, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatHex, formatReport, groundName, harvest, hexToOklch, houseRule, hueName, hueSwatch, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, makeSnapshot, motionTokens, namedSources, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, paletteFromPixels, parseColour, parseHex, propose, protectedFacts, ramp, rampFromAnchors, retrofitPlan, runTell, scanSource, sectionRhythm, semantic, shapeName, sharedParts, shouldAnimate, siblingLine, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, typeFeatureTokens, typicality, validateDirection, visibleText, wcagContrast, wordListsJson };
package/dist/index.d.ts CHANGED
@@ -1031,7 +1031,7 @@ interface CheckReport {
1031
1031
  * Bumped whenever an entry is added, removed or its detection changes, so a
1032
1032
  * report can say which list it was judged against.
1033
1033
  */
1034
- declare const CATALOGUE_VERSION = "2026.09.7";
1034
+ declare const CATALOGUE_VERSION = "2026.09.8";
1035
1035
  declare const CATALOGUE: readonly Tell[];
1036
1036
  declare function tellById(id: string): Tell | undefined;
1037
1037
  /**
@@ -1147,6 +1147,47 @@ interface FactComparison {
1147
1147
  declare function compareFacts(before: string, after: string): FactComparison;
1148
1148
  declare function formatComparison(result: FactComparison, beforePath: string, afterPath: string): string;
1149
1149
 
1150
+ /**
1151
+ * The claims list: every sentence a reader would take as a checkable fact.
1152
+ *
1153
+ * No tell can say whether "Bark charges £2,000 to £7,000" is true. The worst
1154
+ * faults in the DD article that set this off (2026-09-24) were figures like
1155
+ * that: a wrong range, an unsourced day rate, a study quoted past what it
1156
+ * found. Every one passed the copy check, because the prose was fine.
1157
+ *
1158
+ * So this does not judge. It lists each sentence holding a price, a figure, a
1159
+ * date or a named source, marks whether the sentence names where it came
1160
+ * from, and leaves the verdict to a person with the primary source open.
1161
+ * "Sourced" means a source is named, not that it says what the sentence
1162
+ * claims: that is exactly what the person checks.
1163
+ *
1164
+ * Pure over text. `craft copy claims <paths...>` is the command.
1165
+ */
1166
+
1167
+ interface Claim {
1168
+ path: string;
1169
+ line: number;
1170
+ /** The sentence, trimmed to a readable length. */
1171
+ sentence: string;
1172
+ /** The prices, figures and dates in it. */
1173
+ facts: Fact[];
1174
+ /** Who the sentence says it comes from: a name, a publication or a link. Empty when nobody is named. */
1175
+ sources: string[];
1176
+ /**
1177
+ * A source named earlier in the same paragraph. A hint for the person, never
1178
+ * a pass: the sentence may be the writer's own arithmetic on top of it.
1179
+ */
1180
+ nearby?: string[];
1181
+ }
1182
+ interface ClaimsReport {
1183
+ files: number;
1184
+ claims: Claim[];
1185
+ unsourced: number;
1186
+ }
1187
+ declare function namedSources(sentence: string): string[];
1188
+ declare function findClaims(files: SourceFile[]): ClaimsReport;
1189
+ declare function formatClaims(report: ClaimsReport, label?: string): string;
1190
+
1150
1191
  /**
1151
1192
  * The rendered detection path for tells that can be seen on a live page.
1152
1193
  *
@@ -1779,4 +1820,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
1779
1820
 
1780
1821
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
1781
1822
 
1782
- export { AI_PHRASES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BUZZWORDS, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, type CandidateKind, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type ContrastCheck, type ContrastReport, type CopyTell, type CraftTokensInput, type CraftTokensResult, 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, 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 FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOUSE, HOUSE_BUDGET, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, LIGHTNESS_CURVE, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, 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, 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, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPRING, STOCK_PHRASES, type SectionKind, type SectionRhythmOptions, type SemanticInput, type SemanticResult, type Severity, 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 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, applyHouseGate, auditSnapshot, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, compareFacts, compareToEstate, craftTokens, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estatePairs, exitDuration, extractHtml, findColours, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatComparison, formatHex, formatReport, groundName, harvest, hexToOklch, houseRule, hueName, hueSwatch, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, makeSnapshot, motionTokens, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, paletteFromPixels, parseColour, parseHex, propose, protectedFacts, ramp, rampFromAnchors, retrofitPlan, runTell, scanSource, sectionRhythm, semantic, shapeName, sharedParts, shouldAnimate, siblingLine, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, typeFeatureTokens, typicality, validateDirection, visibleText, wcagContrast, wordListsJson };
1823
+ export { AI_PHRASES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BUZZWORDS, 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 CraftTokensInput, type CraftTokensResult, 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, 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 FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOUSE, HOUSE_BUDGET, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, LIGHTNESS_CURVE, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, 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, 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, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPRING, STOCK_PHRASES, type SectionKind, type SectionRhythmOptions, type SemanticInput, type SemanticResult, type Severity, 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 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, applyHouseGate, auditSnapshot, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, compareFacts, compareToEstate, craftTokens, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estatePairs, exitDuration, extractHtml, findClaims, findColours, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatHex, formatReport, groundName, harvest, hexToOklch, houseRule, hueName, hueSwatch, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, makeSnapshot, motionTokens, namedSources, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, paletteFromPixels, parseColour, parseHex, propose, protectedFacts, ramp, rampFromAnchors, retrofitPlan, runTell, scanSource, sectionRhythm, semantic, shapeName, sharedParts, shouldAnimate, siblingLine, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, typeFeatureTokens, typicality, validateDirection, visibleText, wcagContrast, wordListsJson };