@domandigital/craft 0.22.1 → 0.23.1
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/CHANGELOG.md +12 -0
- package/CHARACTER.md +5 -3
- package/COPY.md +25 -6
- package/dist/{audit-CDMZ6JHT.js → audit-DESBVHQJ.js} +3 -3
- package/dist/audit.cjs +112 -6
- package/dist/audit.js +112 -6
- package/dist/{chunk-IAU4ZAIU.js → chunk-7DPB5G4G.js} +2 -2
- package/dist/{chunk-NV6FKMID.js → chunk-EHMEMSM2.js} +106 -5
- package/dist/{chunk-MLYHSPF7.js → chunk-GA3AJN67.js} +7 -2
- package/dist/{chunk-KH56TEFK.js → chunk-GDDBCHQR.js} +5 -5
- package/dist/{chunk-ICEG4S56.js → chunk-LRWKKJIW.js} +5 -5
- package/dist/{chunk-JT2NLOQI.js → chunk-M5QSMIT6.js} +4 -4
- package/dist/{chunk-WTR77PEM.js → chunk-V3GJSISR.js} +14 -14
- package/dist/{chunk-D6TAYXOS.js → chunk-ZV3FPQPM.js} +1 -1
- package/dist/{cli-4PXDNTZX.js → cli-36YQA6BP.js} +4 -4
- package/dist/{cli-3QV4FKM4.js → cli-3PFEOEQI.js} +6 -6
- package/dist/{cli-223CUNU3.js → cli-7IBKMUMM.js} +9 -9
- package/dist/{cli-7PPDBN6N.js → cli-FCFRPPHZ.js} +5 -5
- package/dist/{cli-CQUORKMC.js → cli-RXXGSKD6.js} +6 -6
- package/dist/{cli-FKWJF4V4.js → cli-SOH4QMMW.js} +3 -3
- package/dist/{cli-IQ5U76AZ.js → cli-TP5ZMJOL.js} +5 -5
- package/dist/{cli-6SMNHOCJ.js → cli-YOSHHLIV.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/index.cjs +112 -6
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +112 -6
- package/dist/{viewports-cli-H6UIUMBC.js → viewports-cli-ET664I2Y.js} +3 -3
- package/package.json +1 -1
- package/word-lists.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @domandigital/craft
|
|
2
2
|
|
|
3
|
+
## 0.23.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 297ee1b: `trailing-contrast` no longer goes quiet on a sentence that follows one `not-just-but` blocks (DOM-1358, catalogue 2026.10.8). The check that keeps one sentence from being reported twice looked back a fixed 80 characters and could reach the sentence before; it now reads only the sentence the match sits in. Found by running the released `copy-check` over the 92EL lines: "Edge matters for game servers, not registration." went unreported when it came straight after the gerund pair.
|
|
8
|
+
|
|
9
|
+
## 0.23.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 6890c34: <!-- copy-ok: names the shapes the release starts catching --> Contrastive negation catches the shapes a 92EL report passed the gate with (DOM-1358, catalogue 2026.10.7). **This tightens the gate:** `not-just-but`, which blocks, now covers the parallel gerund ("renting Microsoft's services, not running servers") as well as the parallel participle; its 45 estate hits were read first and every one in shipped copy is the real template. A new review-tier tell, `trailing-contrast`, reports the softer forms COPY.md had left to the read-through: a noun after ", not" closing a sentence ("Edge matters for game servers, not registration"), "not a legal one", and a gerund "instead of" a gerund ("renting computing instead of owning it"). It never blocks, because a genuine disclaimer has the same shape, and it stays quiet where `not-just-but` already reports. Plain negation is untouched: "It is not tested at national scale", "not yet automated", "Nothing is copied by hand". A second new review-tier tell, `parallel-pair`, reports verbless parallel pairs, the staccato triplet's two-part cousin: "One form, seven workflows", "Same site, same £8,000.", "Pay per user, per month", and a verbless list used as a tagline ("Service account, match-day reminders, analytics."). A plain list of names, a sentence with a verb, a subtitle and a numeric result line pass. It never blocks, because a short slide label is often fine.
|
|
14
|
+
|
|
3
15
|
## 0.22.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/CHARACTER.md
CHANGED
|
@@ -1470,7 +1470,7 @@ Generated from the package. Run `pnpm --filter @domandigital/craft run docs`
|
|
|
1470
1470
|
after changing an entry; a test fails until you do.
|
|
1471
1471
|
|
|
1472
1472
|
<!-- craft:catalogue:start -->
|
|
1473
|
-
Catalogue version `2026.10.
|
|
1473
|
+
Catalogue version `2026.10.8`, 76 tells.
|
|
1474
1474
|
|
|
1475
1475
|
| Id | Gen | Surface | Severity | Tell | Why it is a default |
|
|
1476
1476
|
| --- | --- | --- | --- | --- | --- |
|
|
@@ -1514,10 +1514,12 @@ Catalogue version `2026.10.5`, 74 tells.
|
|
|
1514
1514
|
| `rhetorical-opener` | 1 | copy | warn | Rhetorical question opener | 'Looking for...?', 'Tired of...?', 'Ready to...?' opens with the model guessing at the reader instead of telling them something. |
|
|
1515
1515
|
| `em-dash` | 2 | copy | warn | Em dash | Generated copy leans on the em dash to join clauses. House copy does not use it. |
|
|
1516
1516
|
| `emoji` | 1 | copy | warn | Emoji in copy | A sparkle or a rocket beside a heading is decoration a model adds to seem friendly. It dates the page and reads as a social post. |
|
|
1517
|
-
| `not-just-but` | 2 | copy | warn | Contrastive negation | 'It's not just a haircut, it's an experience', 'dispatched from here, not shipped in': the contrast-and-reveal template is the most recognisable construction in generated copy. |
|
|
1517
|
+
| `not-just-but` | 2 | copy | warn | Contrastive negation | 'It's not just a haircut, it's an experience', 'dispatched from here, not shipped in': the contrast-and-reveal template (correctio, antithesis: see COPY.md) is the most recognisable construction in generated copy. |
|
|
1518
|
+
| `trailing-contrast` | 2 | copy | warn | Trailing contrast | 'Edge matters for game servers, not registration', 'the league's own threshold, not a legal one', 'renting computing instead of owning it': the contrast template (correctio, antithesis: see COPY.md) in its softer forms. A genuine disclaimer has the same shape, so a person decides. |
|
|
1518
1519
|
| `no-x-no-y` | 2 | copy | warn | 'No X, no Y' list | 'No obligation, no spam.' Defining the business by what it is not, itemised, is the same template as contrastive negation. |
|
|
1519
1520
|
| `no-x-badge` | 2 | copy | warn | 'No X' badge | 'No catch.', 'NO JARGON GUIDE': a short standalone 'No X' reads as a slapped-on kicker, and worse when the same one is reused across pieces. |
|
|
1520
|
-
| `staccato-triplet` | 2 | copy | warn | Staccato triplet | Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') is the second wave's favourite rhythm. |
|
|
1521
|
+
| `staccato-triplet` | 2 | copy | warn | Staccato triplet | Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') (a tricolon in asyndeton: see COPY.md) is the second wave's favourite rhythm. |
|
|
1522
|
+
| `parallel-pair` | 2 | copy | warn | Verbless parallel pair | 'One form, seven workflows', 'Same site, same £8,000': two matched fragments with no verb (isocolon, often with anaphora or asyndeton: see COPY.md), the staccato triplet's two-part cousin. It sounds decided and says less than a sentence would. |
|
|
1521
1523
|
| `where-x-meets-y` | 2 | copy | warn | 'Where X meets Y' | 'Where luxury meets comfort' is a tagline shape that fits every business and so describes none. |
|
|
1522
1524
|
| `chatbot-residue` | 1 | copy | warn | Chatbot residue | 'Certainly! Here's a revised version', 'as an AI language model', a citeturn0search0 token, a link tracked utm_source=chatgpt.com: text that exists only because a chat reply was pasted whole. This is evidence, not style. |
|
|
1523
1525
|
| `stock-email-line` | 1 | copy | warn | Stock email line | 'I hope this email finds you well', 'let me know if you have any questions', 'don't hesitate to contact us': the opener and closer of a form email. A client reads past them, and a model drafting an email writes them every time. |
|
package/COPY.md
CHANGED
|
@@ -38,7 +38,10 @@ A single hit fails the check. These are not judgment calls.
|
|
|
38
38
|
- "less about X, more about Y"
|
|
39
39
|
- parallel participle: "shipped from here, not imported from Seoul". Both
|
|
40
40
|
sides must be real participles, so "cosmetics, not medicines" (a plain
|
|
41
|
-
factual contrast) passes
|
|
41
|
+
factual contrast) passes here and is left to `trailing-contrast`.
|
|
42
|
+
- parallel gerund: "renting Microsoft's services, not running servers",
|
|
43
|
+
"delivering services, not managing bookings". "Something" and "nothing"
|
|
44
|
+
are not gerunds.
|
|
42
45
|
- **A "No X" claim as the whole string.** "NO JARGON GUIDE", "No catch." A
|
|
43
46
|
short standalone "No X" reads as a slapped-on badge. Empty-state and error
|
|
44
47
|
text is exempt ("No results found", "No bookings yet"), as is anything under
|
|
@@ -97,6 +100,7 @@ person reading decides which instance earns its place.
|
|
|
97
100
|
| `sales-tagline` | advert lines that say nothing a reader can check: "so your budget goes where it counts", "real results", "results that matter", "work harder for you", "pages that convert" |
|
|
98
101
|
| `matey-register` | the put-on British voice: "Alright Dave,", "cheers mate", "no worries", "we'll get you sorted", "proper good", "give me a shout", "chuffed", "Right then,"; and clipped speech that drops the small words: "have look at", "give it go", "Quick one:", "built you a site". Write plain, complete British English |
|
|
99
102
|
| `confession-line` | scoping the business by what it has not done, will not do or is not: "What this is and is not", "we have not yet built", "our clinic experience so far is one practice", "what we would and wouldn't take on", "who we don't work with". It's an open shop: be honest by being precise ("the portal we run for Sensphere") and keep the door open. Contractual facts (eligibility, who runs what) stay, said as what the buyer gets |
|
|
103
|
+
| `trailing-contrast` | the softer contrast template: a noun or phrase after ", not" closing a sentence ("Edge matters for game servers, not registration"), "not a legal one", and "renting computing instead of owning it". A genuine disclaimer has the same shape ("general information, not a recommendation"), so it is reported, never blocked. Say the positive claim; keep the contrast only where the reader would otherwise assume the other thing |
|
|
100
104
|
| `outcome-promise` | a hoped-for result written as delivered: "we'll turn that into enquiries", "once the first areas are working", "competitors you can outdo", "exactly what convinces", "guaranteed rankings", "you'll get more". Promise the work; measure and report the results |
|
|
101
105
|
| `ai-vocabulary` | tapestry, testament, realm, elevate, nestled, meticulous, unparalleled, moreover; and underscore, showcasing, pivotal, measured 10 to 14 times over-used in post-2022 text |
|
|
102
106
|
| `stock-email-line` | the form-email opener and closer: "I hope this email finds you well", "hope you're having a great week", "let me know if you have any questions", "don't hesitate to contact us". Start with the reason for writing; end with the next step and who to ask |
|
|
@@ -104,6 +108,7 @@ person reading decides which instance earns its place.
|
|
|
104
108
|
| `hollow-imperative` | Discover, Experience, Transform, Elevate as the opening verb of a heading or button |
|
|
105
109
|
| `rhetorical-opener` | "Looking for...?", "Tired of...?", "Ready to...?" |
|
|
106
110
|
| `staccato-triplet` | "Fast. Friendly. Local." |
|
|
111
|
+
| `parallel-pair` | the staccato triplet's two-part cousin, two matched fragments with no verb: "One form, seven workflows", "Same site, same £8,000.", "Pay per user, per month", and a verbless list used as a tagline ("Service account, match-day reminders, analytics."). A plain list of names ("Forms, SharePoint, Outlook and Teams") and a sentence with a verb pass. Write the sentence the pair stands for; a short slide label can stay once a person has read it |
|
|
107
112
|
| `where-x-meets-y` | "Where luxury meets comfort" |
|
|
108
113
|
| `ing-tail` | "..., ensuring peace of mind", "..., highlighting our commitment": significance claimed, no mechanism |
|
|
109
114
|
| `vague-attribution` | "studies show", "experts agree", "it is widely known": a claim credited to nobody |
|
|
@@ -116,6 +121,23 @@ person reading decides which instance earns its place.
|
|
|
116
121
|
| `placeholder` | "[Insert client name]", lorem ipsum, `TODO_PLACEHOLDER`. Fine in a draft commit; **zero in anything delivered** |
|
|
117
122
|
| `generic-hero-claim` | on the home page (a root route file, a top-level `index.html`, or a hero or home component), a literal `<h1>` and the paragraph under it that name no place, person, number, price or job: "Quality you can trust", "Your vision, our expertise". One trade noun is not enough ("quality plumbing"); two are ("boiler and radiator repairs"). Also read on the rendered first screen by `craft audit` |
|
|
118
123
|
|
|
124
|
+
### The figures these tells name
|
|
125
|
+
|
|
126
|
+
Several tells are classical figures of speech used as a template. Before
|
|
127
|
+
ruling on a finding you are unsure of, read the definition at the source:
|
|
128
|
+
the figure is the shape, and a line is a hit only when it has the shape.
|
|
129
|
+
Silva Rhetoricae (Brigham Young University) is the reference; Wikipedia
|
|
130
|
+
gives more examples.
|
|
131
|
+
|
|
132
|
+
| Figure | What it is | Tells | Sources |
|
|
133
|
+
| --- | --- | --- | --- |
|
|
134
|
+
| Correctio | defining a thing by saying what it is not, before or after naming it: "dispatched from here, not shipped in" | `not-just-but`, `trailing-contrast` | [Silva](http://rhetoric.byu.edu/Figures/C/correctio.htm) |
|
|
135
|
+
| Antithesis | contrasting words or ideas set side by side, often in parallel structure: "renting services, not running servers" | `not-just-but`, `trailing-contrast` | [Silva](http://rhetoric.byu.edu/Figures/A/antithesis.htm), [Wikipedia](https://en.wikipedia.org/wiki/Antithesis) |
|
|
136
|
+
| Isocolon | a series of elements with the same structure and length: "One form, seven workflows" | `parallel-pair` | [Silva](http://rhetoric.byu.edu/Figures/I/isocolon.htm), [Wikipedia](https://en.wikipedia.org/wiki/Isocolon) |
|
|
137
|
+
| Anaphora | the same word or words opening each of successive phrases: "Same site, same £8,000" | `parallel-pair`, `no-x-no-y` | [Silva](http://rhetoric.byu.edu/Figures/A/anaphora.htm), [Wikipedia](https://en.wikipedia.org/wiki/Anaphora_(rhetoric)) |
|
|
138
|
+
| Asyndeton | leaving out the conjunctions between items: "Service account, match-day reminders, analytics." | `parallel-pair`, `staccato-triplet` | [Silva](http://rhetoric.byu.edu/Figures/A/asyndeton.htm), [Wikipedia](https://en.wikipedia.org/wiki/Asyndeton) |
|
|
139
|
+
| Tricolon | three parallel elements of the same length in a row: "Fast. Friendly. Local." | `staccato-triplet` | [Silva](http://rhetoric.byu.edu/Figures/T/tricolon.htm), [Wikipedia](https://en.wikipedia.org/wiki/Tricolon) |
|
|
140
|
+
|
|
119
141
|
### Style is not evidence
|
|
120
142
|
|
|
121
143
|
Most tells here are house style: they make copy generic, whoever wrote it. The
|
|
@@ -126,12 +148,9 @@ was pasted. Report findings as an editorial diagnosis ("three benefit claims
|
|
|
126
148
|
lack a mechanism; two stock phrases recur"), never as a score or an
|
|
127
149
|
accusation.
|
|
128
150
|
|
|
129
|
-
|
|
130
|
-
|
|
151
|
+
One more review pattern no regex can separate from honest English, so it is
|
|
152
|
+
for the read-through only:
|
|
131
153
|
|
|
132
|
-
- **Trailing contrastive negation**: "Real Square data, not a vanity metric."
|
|
133
|
-
The same shape is how a genuine disclaimer is written ("general information,
|
|
134
|
-
not a recommendation").
|
|
135
154
|
- **Abstract noun triads** with no concrete referent: "clarity, consistency,
|
|
136
155
|
confidence".
|
|
137
156
|
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
snapshotPage,
|
|
10
10
|
snapshotUrl,
|
|
11
11
|
snapshotUrls
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZV3FPQPM.js";
|
|
13
13
|
import "./chunk-JYNXGCYZ.js";
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-GA3AJN67.js";
|
|
15
|
+
import "./chunk-EHMEMSM2.js";
|
|
16
16
|
import "./chunk-QYBAVU3P.js";
|
|
17
17
|
export {
|
|
18
18
|
PROVENANCE_BUDGET_MS,
|
package/dist/audit.cjs
CHANGED
|
@@ -335,8 +335,35 @@ var NEGATION = [
|
|
|
335
335
|
/\b(?:is|are|was|were)(?:n’t|n't)\b[^.;]{2,60},\s*(?:it|they|that|this)(?:’s|'s|’re|'re)\b/i,
|
|
336
336
|
/\bless\s+about\b[^.;]{2,50}\bmore\s+about\b/i,
|
|
337
337
|
// parallel participle, no repeated subject: "dispatched from here, not shipped in from Seoul"
|
|
338
|
-
/\b\w{3,}ed\b[^,.;:]{0,40},\s*not\s+\w{3,}ed\b/i
|
|
338
|
+
/\b\w{3,}ed\b[^,.;:]{0,40},\s*not\s+\w{3,}ed\b/i,
|
|
339
|
+
// parallel gerund: "renting Microsoft's services, not running servers". The
|
|
340
|
+
// -thing pronouns and the -ing prepositions are not verbs.
|
|
341
|
+
/\b(?!(?:some|no|any|every)thing\b)\w{3,}ing\b[^,.;:]{0,40},\s*not\s+(?!(?:some|no|any|every)thing\b|(?:during|according|including|regarding|morning|evening)\b)\w{3,}ing\b/i
|
|
339
342
|
];
|
|
343
|
+
var SOFT_CONTRAST = [
|
|
344
|
+
// ", not registration." closing the sentence. Not when "not" negates what follows.
|
|
345
|
+
/,\s*not\s+(?!(?:yet|least|only|just|merely|simply|to|all|always|even|ever|necessarily|really|quite|exactly|enough|sure|that|because|so|much|many|once|often|every|including|counting|unlike|until|if|when|unless|though|although|or|and|but)\b)(?:(?:a|an|the|your|our|their|its|his|her|my|any)\s+)?[\w'’-]+(?:\s+[\w'’-]+){0,3}(?=\s*(?:[.!?)"”]|$))/im,
|
|
346
|
+
// "the league's own threshold, not a legal one"
|
|
347
|
+
/,\s*not\s+(?:a|an|the)\s+(?:[\w'’-]+\s+)?one\b/i,
|
|
348
|
+
// "renting computing over the internet instead of owning it"
|
|
349
|
+
/\b(?!(?:some|no|any|every)thing\b)\w{3,}ing\b[^.;:!?]{0,60}?\binstead\s+of\s+(?!(?:some|no|any|every)thing\b)\w{3,}ing\b/i
|
|
350
|
+
];
|
|
351
|
+
var PAIR_TOKEN = String.raw`(?:[£$€]?\d[\d,.]*%?|[\w'’£$€-]+)`;
|
|
352
|
+
var PAIR_NUMBER = String.raw`(?:one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|\d[\d,]*)`;
|
|
353
|
+
var PARALLEL_PAIR = [
|
|
354
|
+
// anaphora: "same site, same £8,000", "per user, per month". "No X, no Y" is no-x-no-y's.
|
|
355
|
+
new RegExp(String.raw`\b(?!no\b)(\w+)[ \t]+${PAIR_TOKEN},[ \t]+\1[ \t]+${PAIR_TOKEN}(?=[ \t]*(?:[.!?]|$))`, "im"),
|
|
356
|
+
// isocolon on its own line: "One form, seven workflows"
|
|
357
|
+
new RegExp(String.raw`^[ \t]*(?:#{1,6}[ \t]+)?${PAIR_NUMBER}[ \t]+[\w'’-]+,[ \t]+${PAIR_NUMBER}[ \t]+[\w'’-]+[.!]?[ \t]*$`, "im"),
|
|
358
|
+
// a verbless list as a tagline: "Service account, match-day reminders, analytics."
|
|
359
|
+
/^[ \t]*(?:#{1,6}[ \t]+)?[A-Z][\w'’-]*(?:[ \t]+(?!(?:and|or)\b)[\w'’-]+){0,2}(?:,[ \t]+(?!(?:and|or)\b)[\w'’-]+(?:[ \t]+(?!(?:and|or)\b)[\w'’-]+){0,2}){2,3}\.[ \t]*$/m
|
|
360
|
+
];
|
|
361
|
+
var alreadyBlocked = (block, match, index) => {
|
|
362
|
+
const before = block.text.slice(0, index);
|
|
363
|
+
const start = Math.max(before.search(/[^.!?\n]*$/), 0);
|
|
364
|
+
const sentence = block.text.slice(start, index + match.length);
|
|
365
|
+
return NEGATION.some((pattern) => pattern.test(sentence));
|
|
366
|
+
};
|
|
340
367
|
var DATA_ABSENCE = /\b(?:yet|found|available|configured|selected|assigned|scheduled|recorded|on file|to show|to display|results|match|matches|matching|data|in this period)\b/i;
|
|
341
368
|
var SIGNED_IN = /(?:^|\/)(?:admin|portal|dashboard|embedded)\//;
|
|
342
369
|
function lineAround(text, index) {
|
|
@@ -717,7 +744,7 @@ var COPY_TELLS = [
|
|
|
717
744
|
generation: 2,
|
|
718
745
|
severity: "warn",
|
|
719
746
|
surface: "copy",
|
|
720
|
-
why: "'It's not just a haircut, it's an experience', 'dispatched from here, not shipped in': the contrast-and-reveal template is the most recognisable construction in generated copy.",
|
|
747
|
+
why: "'It's not just a haircut, it's an experience', 'dispatched from here, not shipped in': the contrast-and-reveal template (correctio, antithesis: see COPY.md) is the most recognisable construction in generated copy.",
|
|
721
748
|
fix: "Say the positive claim and drop the contrast.",
|
|
722
749
|
detect: phrases(NEGATION, quoted, allText),
|
|
723
750
|
fixtures: {
|
|
@@ -728,12 +755,56 @@ var COPY_TELLS = [
|
|
|
728
755
|
f("content/home.md", "Hairdressing isn't a job, it's art."),
|
|
729
756
|
f("content/home.md", "It is less about the tools and more about the eye."),
|
|
730
757
|
f("content/home.md", "Order today and it's dispatched from here, not shipped in from Seoul."),
|
|
731
|
-
f("app/offer.tsx", `export const outcome = "The whole path, not just the ads.";`)
|
|
758
|
+
f("app/offer.tsx", `export const outcome = "The whole path, not just the ads.";`),
|
|
759
|
+
// From a 92EL report that passed the gate, October 2026.
|
|
760
|
+
f("content/report.md", "These are reported, not assumed to pass."),
|
|
761
|
+
f("content/report.md", "The deletion is proposed, not automated."),
|
|
762
|
+
f("content/report.md", "For 92EL, that means renting Microsoft's services, not running servers."),
|
|
763
|
+
f("content/home.md", "Your team should spend time delivering services, not managing bookings.")
|
|
732
764
|
],
|
|
733
765
|
pass: [
|
|
734
766
|
f("content/home.md", "A haircut, a hot towel and a straight-razor finish."),
|
|
735
767
|
f("content/home.md", "They are cosmetics, not medicines."),
|
|
736
|
-
f("content/home.md", "The box arrived red, not led astray by a dented corner.")
|
|
768
|
+
f("content/home.md", "The box arrived red, not led astray by a dented corner."),
|
|
769
|
+
f("content/home.md", "We fix boilers in the morning, not in the evening."),
|
|
770
|
+
f("content/report.md", "Players never open the lists."),
|
|
771
|
+
f("content/report.md", "It is not tested at national scale."),
|
|
772
|
+
f("content/report.md", "Do not invent anything."),
|
|
773
|
+
f("content/report.md", "Nothing is copied by hand."),
|
|
774
|
+
f("content/report.md", "Results are checked by a person and not yet automated.")
|
|
775
|
+
]
|
|
776
|
+
}
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
id: "trailing-contrast",
|
|
780
|
+
name: "Trailing contrast",
|
|
781
|
+
generation: 2,
|
|
782
|
+
severity: "warn",
|
|
783
|
+
surface: "copy",
|
|
784
|
+
why: "'Edge matters for game servers, not registration', 'the league's own threshold, not a legal one', 'renting computing instead of owning it': the contrast template (correctio, antithesis: see COPY.md) in its softer forms. A genuine disclaimer has the same shape, so a person decides.",
|
|
785
|
+
fix: "Say the positive claim on its own: 'Edge matters for game servers.' Keep the contrast only where the reader would otherwise assume the other thing, such as a legal disclaimer.",
|
|
786
|
+
detect: phrases(SOFT_CONTRAST, quoted, allText, alreadyBlocked),
|
|
787
|
+
fixtures: {
|
|
788
|
+
flag: [
|
|
789
|
+
// From a 92EL report that passed the gate, October 2026.
|
|
790
|
+
f("content/report.md", "Edge matters for game servers, not registration."),
|
|
791
|
+
f("content/report.md", "Age 18 is the league's own threshold, not a legal one."),
|
|
792
|
+
f("content/report.md", "Cloud computing means renting computing over the internet instead of owning it."),
|
|
793
|
+
f("content/home.md", "Real Square data, not a vanity metric."),
|
|
794
|
+
// Reported even straight after a sentence the blocking rule reports.
|
|
795
|
+
f("content/report.md", "That means renting services, not running servers.\n\nEdge matters for game servers, not registration.")
|
|
796
|
+
],
|
|
797
|
+
pass: [
|
|
798
|
+
f("content/home.md", "Rewire, retile and replumb a bathroom in five days."),
|
|
799
|
+
f("content/report.md", "Players never open the lists."),
|
|
800
|
+
f("content/report.md", "It is not tested at national scale."),
|
|
801
|
+
f("content/report.md", "Do not invent anything."),
|
|
802
|
+
f("content/report.md", "Nothing is copied by hand."),
|
|
803
|
+
f("content/report.md", "Results are checked by a person and not yet automated."),
|
|
804
|
+
f("content/home.md", "Pay by card, not to mention Apple Pay."),
|
|
805
|
+
f("content/home.md", "Card instead of cash is fine."),
|
|
806
|
+
// The blocking rule reports this one; it is not reported twice.
|
|
807
|
+
f("content/report.md", "The deletion is proposed, not automated.")
|
|
737
808
|
]
|
|
738
809
|
}
|
|
739
810
|
},
|
|
@@ -783,7 +854,7 @@ var COPY_TELLS = [
|
|
|
783
854
|
generation: 2,
|
|
784
855
|
severity: "warn",
|
|
785
856
|
surface: "copy",
|
|
786
|
-
why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') is the second wave's favourite rhythm.",
|
|
857
|
+
why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') (a tricolon in asyndeton: see COPY.md) is the second wave's favourite rhythm.",
|
|
787
858
|
fix: "Write one sentence that says which of the three is true, and how you know.",
|
|
788
859
|
// The fragments share one paragraph: "You don't. Again.", a blank line, then "It's frustrating." is two beats, not three.
|
|
789
860
|
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),
|
|
@@ -795,6 +866,36 @@ var COPY_TELLS = [
|
|
|
795
866
|
]
|
|
796
867
|
}
|
|
797
868
|
},
|
|
869
|
+
{
|
|
870
|
+
id: "parallel-pair",
|
|
871
|
+
name: "Verbless parallel pair",
|
|
872
|
+
generation: 2,
|
|
873
|
+
severity: "warn",
|
|
874
|
+
surface: "copy",
|
|
875
|
+
why: "'One form, seven workflows', 'Same site, same \xA38,000': two matched fragments with no verb (isocolon, often with anaphora or asyndeton: see COPY.md), the staccato triplet's two-part cousin. It sounds decided and says less than a sentence would.",
|
|
876
|
+
fix: "Write the sentence the pair stands for: 'One form and seven workflows replace the spreadsheets.' A short label on a slide can stay if a person reads it and it earns its place.",
|
|
877
|
+
detect: phrases(PARALLEL_PAIR, quoted),
|
|
878
|
+
fixtures: {
|
|
879
|
+
flag: [
|
|
880
|
+
// From 92EL deliverables, October 2026.
|
|
881
|
+
f("content/deck.md", "# One form, seven workflows"),
|
|
882
|
+
f("content/deck.md", "One form, seven workflows."),
|
|
883
|
+
f("content/deck.md", "Same site, same \xA38,000."),
|
|
884
|
+
f("content/deck.md", "Pay per user, per month"),
|
|
885
|
+
f("content/deck.md", "Service account, match-day reminders, analytics.")
|
|
886
|
+
],
|
|
887
|
+
pass: [
|
|
888
|
+
f("content/deck.md", "Forms, SharePoint, Outlook and Teams"),
|
|
889
|
+
f("content/deck.md", "One form and seven workflows replace 92EL's spreadsheets and email chains."),
|
|
890
|
+
f("content/deck.md", "PulseCloud Technologies, for 92EL senior management"),
|
|
891
|
+
f("content/deck.md", "41 of 50 passed, 12 after a fix."),
|
|
892
|
+
f("content/deck.md", "Pay monthly for each user"),
|
|
893
|
+
f("content/home.md", "We fix boilers the same day, and we tell you the price before we start."),
|
|
894
|
+
// The blocking no-x-no-y reports this one.
|
|
895
|
+
f("content/home.md", "We reply within a day. No obligation, no spam.")
|
|
896
|
+
]
|
|
897
|
+
}
|
|
898
|
+
},
|
|
798
899
|
{
|
|
799
900
|
id: "where-x-meets-y",
|
|
800
901
|
name: "'Where X meets Y'",
|
|
@@ -3393,6 +3494,11 @@ var HOUSE = {
|
|
|
3393
3494
|
"matey-register": { tier: "review", label: "matey register, write plain and complete British English" },
|
|
3394
3495
|
// New 2026-10-20 (founder, from Eunice Ordor's review): review first, like every new tell.
|
|
3395
3496
|
"confession-line": { tier: "review", label: "confession line, scope the work by saying precisely what is true" },
|
|
3497
|
+
// New 2026-10-23 (a 92EL report passed the gate with three): review, because a
|
|
3498
|
+
// genuine disclaimer has the same shape. The gerund pair joined not-just-but.
|
|
3499
|
+
// New 2026-10-23 (founder, from 92EL deliverables): review, since a short slide label is often fine.
|
|
3500
|
+
"parallel-pair": { tier: "review", label: "verbless parallel pair, write the sentence it stands for" },
|
|
3501
|
+
"trailing-contrast": { tier: "review", label: "trailing contrast. Say the positive claim, unless it is a disclaimer the reader needs" },
|
|
3396
3502
|
"ai-vocabulary": { tier: "review", label: "AI vocabulary" },
|
|
3397
3503
|
"stock-phrase": { tier: "review", label: "stock phrase" },
|
|
3398
3504
|
"hollow-imperative": { tier: "review", label: "hollow imperative" },
|
|
@@ -4161,7 +4267,7 @@ var IMAGERY_TELLS = [
|
|
|
4161
4267
|
];
|
|
4162
4268
|
|
|
4163
4269
|
// src/character/check.ts
|
|
4164
|
-
var CATALOGUE_VERSION = "2026.10.
|
|
4270
|
+
var CATALOGUE_VERSION = "2026.10.8";
|
|
4165
4271
|
var CATALOGUE = [
|
|
4166
4272
|
...[...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map((t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t),
|
|
4167
4273
|
...SPECIFICITY_COPY_TELLS,
|
package/dist/audit.js
CHANGED
|
@@ -300,8 +300,35 @@ var NEGATION = [
|
|
|
300
300
|
/\b(?:is|are|was|were)(?:n’t|n't)\b[^.;]{2,60},\s*(?:it|they|that|this)(?:’s|'s|’re|'re)\b/i,
|
|
301
301
|
/\bless\s+about\b[^.;]{2,50}\bmore\s+about\b/i,
|
|
302
302
|
// parallel participle, no repeated subject: "dispatched from here, not shipped in from Seoul"
|
|
303
|
-
/\b\w{3,}ed\b[^,.;:]{0,40},\s*not\s+\w{3,}ed\b/i
|
|
303
|
+
/\b\w{3,}ed\b[^,.;:]{0,40},\s*not\s+\w{3,}ed\b/i,
|
|
304
|
+
// parallel gerund: "renting Microsoft's services, not running servers". The
|
|
305
|
+
// -thing pronouns and the -ing prepositions are not verbs.
|
|
306
|
+
/\b(?!(?:some|no|any|every)thing\b)\w{3,}ing\b[^,.;:]{0,40},\s*not\s+(?!(?:some|no|any|every)thing\b|(?:during|according|including|regarding|morning|evening)\b)\w{3,}ing\b/i
|
|
304
307
|
];
|
|
308
|
+
var SOFT_CONTRAST = [
|
|
309
|
+
// ", not registration." closing the sentence. Not when "not" negates what follows.
|
|
310
|
+
/,\s*not\s+(?!(?:yet|least|only|just|merely|simply|to|all|always|even|ever|necessarily|really|quite|exactly|enough|sure|that|because|so|much|many|once|often|every|including|counting|unlike|until|if|when|unless|though|although|or|and|but)\b)(?:(?:a|an|the|your|our|their|its|his|her|my|any)\s+)?[\w'’-]+(?:\s+[\w'’-]+){0,3}(?=\s*(?:[.!?)"”]|$))/im,
|
|
311
|
+
// "the league's own threshold, not a legal one"
|
|
312
|
+
/,\s*not\s+(?:a|an|the)\s+(?:[\w'’-]+\s+)?one\b/i,
|
|
313
|
+
// "renting computing over the internet instead of owning it"
|
|
314
|
+
/\b(?!(?:some|no|any|every)thing\b)\w{3,}ing\b[^.;:!?]{0,60}?\binstead\s+of\s+(?!(?:some|no|any|every)thing\b)\w{3,}ing\b/i
|
|
315
|
+
];
|
|
316
|
+
var PAIR_TOKEN = String.raw`(?:[£$€]?\d[\d,.]*%?|[\w'’£$€-]+)`;
|
|
317
|
+
var PAIR_NUMBER = String.raw`(?:one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|\d[\d,]*)`;
|
|
318
|
+
var PARALLEL_PAIR = [
|
|
319
|
+
// anaphora: "same site, same £8,000", "per user, per month". "No X, no Y" is no-x-no-y's.
|
|
320
|
+
new RegExp(String.raw`\b(?!no\b)(\w+)[ \t]+${PAIR_TOKEN},[ \t]+\1[ \t]+${PAIR_TOKEN}(?=[ \t]*(?:[.!?]|$))`, "im"),
|
|
321
|
+
// isocolon on its own line: "One form, seven workflows"
|
|
322
|
+
new RegExp(String.raw`^[ \t]*(?:#{1,6}[ \t]+)?${PAIR_NUMBER}[ \t]+[\w'’-]+,[ \t]+${PAIR_NUMBER}[ \t]+[\w'’-]+[.!]?[ \t]*$`, "im"),
|
|
323
|
+
// a verbless list as a tagline: "Service account, match-day reminders, analytics."
|
|
324
|
+
/^[ \t]*(?:#{1,6}[ \t]+)?[A-Z][\w'’-]*(?:[ \t]+(?!(?:and|or)\b)[\w'’-]+){0,2}(?:,[ \t]+(?!(?:and|or)\b)[\w'’-]+(?:[ \t]+(?!(?:and|or)\b)[\w'’-]+){0,2}){2,3}\.[ \t]*$/m
|
|
325
|
+
];
|
|
326
|
+
var alreadyBlocked = (block, match, index) => {
|
|
327
|
+
const before = block.text.slice(0, index);
|
|
328
|
+
const start = Math.max(before.search(/[^.!?\n]*$/), 0);
|
|
329
|
+
const sentence = block.text.slice(start, index + match.length);
|
|
330
|
+
return NEGATION.some((pattern) => pattern.test(sentence));
|
|
331
|
+
};
|
|
305
332
|
var DATA_ABSENCE = /\b(?:yet|found|available|configured|selected|assigned|scheduled|recorded|on file|to show|to display|results|match|matches|matching|data|in this period)\b/i;
|
|
306
333
|
var SIGNED_IN = /(?:^|\/)(?:admin|portal|dashboard|embedded)\//;
|
|
307
334
|
function lineAround(text, index) {
|
|
@@ -682,7 +709,7 @@ var COPY_TELLS = [
|
|
|
682
709
|
generation: 2,
|
|
683
710
|
severity: "warn",
|
|
684
711
|
surface: "copy",
|
|
685
|
-
why: "'It's not just a haircut, it's an experience', 'dispatched from here, not shipped in': the contrast-and-reveal template is the most recognisable construction in generated copy.",
|
|
712
|
+
why: "'It's not just a haircut, it's an experience', 'dispatched from here, not shipped in': the contrast-and-reveal template (correctio, antithesis: see COPY.md) is the most recognisable construction in generated copy.",
|
|
686
713
|
fix: "Say the positive claim and drop the contrast.",
|
|
687
714
|
detect: phrases(NEGATION, quoted, allText),
|
|
688
715
|
fixtures: {
|
|
@@ -693,12 +720,56 @@ var COPY_TELLS = [
|
|
|
693
720
|
f("content/home.md", "Hairdressing isn't a job, it's art."),
|
|
694
721
|
f("content/home.md", "It is less about the tools and more about the eye."),
|
|
695
722
|
f("content/home.md", "Order today and it's dispatched from here, not shipped in from Seoul."),
|
|
696
|
-
f("app/offer.tsx", `export const outcome = "The whole path, not just the ads.";`)
|
|
723
|
+
f("app/offer.tsx", `export const outcome = "The whole path, not just the ads.";`),
|
|
724
|
+
// From a 92EL report that passed the gate, October 2026.
|
|
725
|
+
f("content/report.md", "These are reported, not assumed to pass."),
|
|
726
|
+
f("content/report.md", "The deletion is proposed, not automated."),
|
|
727
|
+
f("content/report.md", "For 92EL, that means renting Microsoft's services, not running servers."),
|
|
728
|
+
f("content/home.md", "Your team should spend time delivering services, not managing bookings.")
|
|
697
729
|
],
|
|
698
730
|
pass: [
|
|
699
731
|
f("content/home.md", "A haircut, a hot towel and a straight-razor finish."),
|
|
700
732
|
f("content/home.md", "They are cosmetics, not medicines."),
|
|
701
|
-
f("content/home.md", "The box arrived red, not led astray by a dented corner.")
|
|
733
|
+
f("content/home.md", "The box arrived red, not led astray by a dented corner."),
|
|
734
|
+
f("content/home.md", "We fix boilers in the morning, not in the evening."),
|
|
735
|
+
f("content/report.md", "Players never open the lists."),
|
|
736
|
+
f("content/report.md", "It is not tested at national scale."),
|
|
737
|
+
f("content/report.md", "Do not invent anything."),
|
|
738
|
+
f("content/report.md", "Nothing is copied by hand."),
|
|
739
|
+
f("content/report.md", "Results are checked by a person and not yet automated.")
|
|
740
|
+
]
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
id: "trailing-contrast",
|
|
745
|
+
name: "Trailing contrast",
|
|
746
|
+
generation: 2,
|
|
747
|
+
severity: "warn",
|
|
748
|
+
surface: "copy",
|
|
749
|
+
why: "'Edge matters for game servers, not registration', 'the league's own threshold, not a legal one', 'renting computing instead of owning it': the contrast template (correctio, antithesis: see COPY.md) in its softer forms. A genuine disclaimer has the same shape, so a person decides.",
|
|
750
|
+
fix: "Say the positive claim on its own: 'Edge matters for game servers.' Keep the contrast only where the reader would otherwise assume the other thing, such as a legal disclaimer.",
|
|
751
|
+
detect: phrases(SOFT_CONTRAST, quoted, allText, alreadyBlocked),
|
|
752
|
+
fixtures: {
|
|
753
|
+
flag: [
|
|
754
|
+
// From a 92EL report that passed the gate, October 2026.
|
|
755
|
+
f("content/report.md", "Edge matters for game servers, not registration."),
|
|
756
|
+
f("content/report.md", "Age 18 is the league's own threshold, not a legal one."),
|
|
757
|
+
f("content/report.md", "Cloud computing means renting computing over the internet instead of owning it."),
|
|
758
|
+
f("content/home.md", "Real Square data, not a vanity metric."),
|
|
759
|
+
// Reported even straight after a sentence the blocking rule reports.
|
|
760
|
+
f("content/report.md", "That means renting services, not running servers.\n\nEdge matters for game servers, not registration.")
|
|
761
|
+
],
|
|
762
|
+
pass: [
|
|
763
|
+
f("content/home.md", "Rewire, retile and replumb a bathroom in five days."),
|
|
764
|
+
f("content/report.md", "Players never open the lists."),
|
|
765
|
+
f("content/report.md", "It is not tested at national scale."),
|
|
766
|
+
f("content/report.md", "Do not invent anything."),
|
|
767
|
+
f("content/report.md", "Nothing is copied by hand."),
|
|
768
|
+
f("content/report.md", "Results are checked by a person and not yet automated."),
|
|
769
|
+
f("content/home.md", "Pay by card, not to mention Apple Pay."),
|
|
770
|
+
f("content/home.md", "Card instead of cash is fine."),
|
|
771
|
+
// The blocking rule reports this one; it is not reported twice.
|
|
772
|
+
f("content/report.md", "The deletion is proposed, not automated.")
|
|
702
773
|
]
|
|
703
774
|
}
|
|
704
775
|
},
|
|
@@ -748,7 +819,7 @@ var COPY_TELLS = [
|
|
|
748
819
|
generation: 2,
|
|
749
820
|
severity: "warn",
|
|
750
821
|
surface: "copy",
|
|
751
|
-
why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') is the second wave's favourite rhythm.",
|
|
822
|
+
why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') (a tricolon in asyndeton: see COPY.md) is the second wave's favourite rhythm.",
|
|
752
823
|
fix: "Write one sentence that says which of the three is true, and how you know.",
|
|
753
824
|
// The fragments share one paragraph: "You don't. Again.", a blank line, then "It's frustrating." is two beats, not three.
|
|
754
825
|
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),
|
|
@@ -760,6 +831,36 @@ var COPY_TELLS = [
|
|
|
760
831
|
]
|
|
761
832
|
}
|
|
762
833
|
},
|
|
834
|
+
{
|
|
835
|
+
id: "parallel-pair",
|
|
836
|
+
name: "Verbless parallel pair",
|
|
837
|
+
generation: 2,
|
|
838
|
+
severity: "warn",
|
|
839
|
+
surface: "copy",
|
|
840
|
+
why: "'One form, seven workflows', 'Same site, same \xA38,000': two matched fragments with no verb (isocolon, often with anaphora or asyndeton: see COPY.md), the staccato triplet's two-part cousin. It sounds decided and says less than a sentence would.",
|
|
841
|
+
fix: "Write the sentence the pair stands for: 'One form and seven workflows replace the spreadsheets.' A short label on a slide can stay if a person reads it and it earns its place.",
|
|
842
|
+
detect: phrases(PARALLEL_PAIR, quoted),
|
|
843
|
+
fixtures: {
|
|
844
|
+
flag: [
|
|
845
|
+
// From 92EL deliverables, October 2026.
|
|
846
|
+
f("content/deck.md", "# One form, seven workflows"),
|
|
847
|
+
f("content/deck.md", "One form, seven workflows."),
|
|
848
|
+
f("content/deck.md", "Same site, same \xA38,000."),
|
|
849
|
+
f("content/deck.md", "Pay per user, per month"),
|
|
850
|
+
f("content/deck.md", "Service account, match-day reminders, analytics.")
|
|
851
|
+
],
|
|
852
|
+
pass: [
|
|
853
|
+
f("content/deck.md", "Forms, SharePoint, Outlook and Teams"),
|
|
854
|
+
f("content/deck.md", "One form and seven workflows replace 92EL's spreadsheets and email chains."),
|
|
855
|
+
f("content/deck.md", "PulseCloud Technologies, for 92EL senior management"),
|
|
856
|
+
f("content/deck.md", "41 of 50 passed, 12 after a fix."),
|
|
857
|
+
f("content/deck.md", "Pay monthly for each user"),
|
|
858
|
+
f("content/home.md", "We fix boilers the same day, and we tell you the price before we start."),
|
|
859
|
+
// The blocking no-x-no-y reports this one.
|
|
860
|
+
f("content/home.md", "We reply within a day. No obligation, no spam.")
|
|
861
|
+
]
|
|
862
|
+
}
|
|
863
|
+
},
|
|
763
864
|
{
|
|
764
865
|
id: "where-x-meets-y",
|
|
765
866
|
name: "'Where X meets Y'",
|
|
@@ -3358,6 +3459,11 @@ var HOUSE = {
|
|
|
3358
3459
|
"matey-register": { tier: "review", label: "matey register, write plain and complete British English" },
|
|
3359
3460
|
// New 2026-10-20 (founder, from Eunice Ordor's review): review first, like every new tell.
|
|
3360
3461
|
"confession-line": { tier: "review", label: "confession line, scope the work by saying precisely what is true" },
|
|
3462
|
+
// New 2026-10-23 (a 92EL report passed the gate with three): review, because a
|
|
3463
|
+
// genuine disclaimer has the same shape. The gerund pair joined not-just-but.
|
|
3464
|
+
// New 2026-10-23 (founder, from 92EL deliverables): review, since a short slide label is often fine.
|
|
3465
|
+
"parallel-pair": { tier: "review", label: "verbless parallel pair, write the sentence it stands for" },
|
|
3466
|
+
"trailing-contrast": { tier: "review", label: "trailing contrast. Say the positive claim, unless it is a disclaimer the reader needs" },
|
|
3361
3467
|
"ai-vocabulary": { tier: "review", label: "AI vocabulary" },
|
|
3362
3468
|
"stock-phrase": { tier: "review", label: "stock phrase" },
|
|
3363
3469
|
"hollow-imperative": { tier: "review", label: "hollow imperative" },
|
|
@@ -4126,7 +4232,7 @@ var IMAGERY_TELLS = [
|
|
|
4126
4232
|
];
|
|
4127
4233
|
|
|
4128
4234
|
// src/character/check.ts
|
|
4129
|
-
var CATALOGUE_VERSION = "2026.10.
|
|
4235
|
+
var CATALOGUE_VERSION = "2026.10.8";
|
|
4130
4236
|
var CATALOGUE = [
|
|
4131
4237
|
...[...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map((t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t),
|
|
4132
4238
|
...SPECIFICITY_COPY_TELLS,
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
levenshtein,
|
|
11
11
|
normaliseFamily,
|
|
12
12
|
parseColour
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GA3AJN67.js";
|
|
14
14
|
import {
|
|
15
15
|
TRADE_NOUNS
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-EHMEMSM2.js";
|
|
17
17
|
|
|
18
18
|
// src/direction/types.ts
|
|
19
19
|
var DIRECTION_VERSION = 2;
|