@domandigital/craft 0.7.0 → 0.8.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/CHANGELOG.md +11 -0
- package/CHARACTER.md +26 -9
- package/COPY.md +280 -0
- package/README.md +12 -0
- package/dist/{audit-YNODYB3U.js → audit-W57YDZ2K.js} +3 -3
- package/dist/audit.cjs +856 -86
- package/dist/audit.d.cts +2 -0
- package/dist/audit.d.ts +2 -0
- package/dist/audit.js +856 -86
- package/dist/{chunk-IQTZWNEK.js → chunk-35OX6C4H.js} +5 -5
- package/dist/{chunk-3AP4PJMX.js → chunk-CZHRNPDD.js} +854 -77
- package/dist/{chunk-GV3QOTQU.js → chunk-K253ZXB3.js} +4 -4
- package/dist/{chunk-Q54JSNC5.js → chunk-OGSYVCMU.js} +1 -1
- package/dist/{chunk-CU5TZHOV.js → chunk-OVOH7KZY.js} +5 -5
- package/dist/{chunk-OA7SGPN4.js → chunk-Q4MN3OXF.js} +2 -2
- package/dist/{chunk-J2BOAIHJ.js → chunk-ULYKASLA.js} +1 -1
- package/dist/{chunk-YYC4EGER.js → chunk-X65RDW65.js} +126 -11
- package/dist/{cli-MW3JAGX3.js → cli-F7ONSKZS.js} +4 -4
- package/dist/cli-FKJOMOOF.js +15 -0
- package/dist/{cli-6Y5E32FS.js → cli-KA3JDNDH.js} +4 -4
- package/dist/{cli-TM2HWYN7.js → cli-LZ7R6KJM.js} +9 -9
- package/dist/cli-SHLNRHK6.js +16 -0
- package/dist/cli.js +2 -2
- package/dist/index.cjs +974 -105
- package/dist/index.d.cts +80 -4
- package/dist/index.d.ts +80 -4
- package/dist/index.js +967 -105
- package/package.json +2 -1
- package/word-lists.json +12 -3
- package/dist/cli-IQT6QZ6U.js +0 -16
- package/dist/cli-PD43TWX7.js +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @domandigital/craft
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2b6f823: The house copy standard moves into craft. `COPY.md` ships in the package, `craft copy --gate` applies the house policy (the blocking tier fails the run), and `craft tells list --json` publishes each copy tell's house tier so `copy-check` reads the policy instead of keeping its own list. Catalogue 2026.09.4 adds the density tier (`phrase-density`, `aphorism-density`, `contraction-scarcity`, `sentence-rhythm`, `repeated-sentence`, `heading-shape`, `heading-echo`) and four review tells from research (`ing-tail`, `vague-attribution`, `closing-summary`, `false-range`), all warn. The emoji tell now catches sparkles, the green tick box and other emoji in the older symbol blocks.
|
|
8
|
+
|
|
9
|
+
Words inside a URL or a Markdown link target are no longer read as copy, so a slug such as `/seamless-booking` no longer blocks.
|
|
10
|
+
|
|
11
|
+
- 7765dda: Catalogue 2026.09.6, tuned against the Doman Digital site's live Sanity content. `chatbot-residue` reads a run of ChatGPT citation tokens as one finding, through the invisible private-use characters ChatGPT wraps them in. `placeholder` skips a placeholder inside quotation marks, which is a template being taught. `vague-attribution` skips a claim that names its source or carries a footnote or link. `inline-label-list` fires only when the text after the label is six words or fewer, so a definition list passes.
|
|
12
|
+
- ef5b85b: Catalogue 2026.09.5. Four more review tells: `chatbot-residue` (pasted chat replies, chat citation tokens, links tracked `utm_source=chatgpt.com`), `placeholder` (`[Insert ...]`, lorem ipsum, `TODO_PLACEHOLDER`, including a placeholder that is a whole element), `question-reveal` and `inline-label-list`. New command `craft copy compare <before> <after>`: the preservation gate, listing the protected facts (prices, numbers, dates, times, phones, emails, links, postcodes, names) a rewrite lost or added, exit 1 if any. Exported as `compareFacts` and `protectedFacts`.
|
|
13
|
+
|
|
3
14
|
## 0.7.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/CHARACTER.md
CHANGED
|
@@ -279,14 +279,16 @@ navigation, the order of sections, where the call to action sits.
|
|
|
279
279
|
|
|
280
280
|
## The house copy rules live here
|
|
281
281
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
`
|
|
285
|
-
`
|
|
286
|
-
`
|
|
287
|
-
|
|
288
|
-
wrapper
|
|
289
|
-
lists exist once, here, with a test per entry.
|
|
282
|
+
The house copy standard is [COPY.md](./COPY.md), in this package. Its
|
|
283
|
+
blocking tier is carried by ten tells here: `ai-phrase`, `plainer-word`,
|
|
284
|
+
`plain-english`, `buzzword`, `negative-reassurance`, `em-dash`, `emoji`,
|
|
285
|
+
`not-just-but`, `no-x-no-y` and `no-x-badge`. craft ships every tell as
|
|
286
|
+
`warn`; the house policy in `src/character/house.ts` says which fail a commit,
|
|
287
|
+
and `craft copy --gate` applies it. `copy-check` in claude-kit is a thin
|
|
288
|
+
wrapper that reads the same policy from `craft tells list --json`, so the
|
|
289
|
+
lists and the policy exist once, here, with a test per entry. Since catalogue
|
|
290
|
+
`2026.09.4` the density tier and four research tells are here too, and since
|
|
291
|
+
`2026.09.5` four more, including `chatbot-residue` and `placeholder`. All review.
|
|
290
292
|
|
|
291
293
|
The swap was checked against the old checker on 2,570 files across the seven
|
|
292
294
|
client repos (2026-09-23). The old checker found 193 issues, craft finds every
|
|
@@ -532,7 +534,7 @@ Generated from the package. Run `pnpm --filter @domandigital/craft run docs`
|
|
|
532
534
|
after changing an entry; a test fails until you do.
|
|
533
535
|
|
|
534
536
|
<!-- craft:catalogue:start -->
|
|
535
|
-
Catalogue version `2026.09.
|
|
537
|
+
Catalogue version `2026.09.6`, 54 tells.
|
|
536
538
|
|
|
537
539
|
| Id | Gen | Surface | Severity | Tell | Why it is a default |
|
|
538
540
|
| --- | --- | --- | --- | --- | --- |
|
|
@@ -575,4 +577,19 @@ Catalogue version `2026.09.3`, 39 tells.
|
|
|
575
577
|
| `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. |
|
|
576
578
|
| `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. |
|
|
577
579
|
| `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. |
|
|
580
|
+
| `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. |
|
|
581
|
+
| `placeholder` | 1 | copy | warn | Unfilled placeholder | '[Insert testimonial]', '[Your Name]', lorem ipsum, TODO_PLACEHOLDER: scaffolding a reader must never see, and the clearest sign a page shipped before anyone read it. |
|
|
582
|
+
| `question-reveal` | 2 | copy | warn | Staged reveal | 'The result? Faster growth.', 'What does this mean for you? It means...': the writer asks the reader's question for them and answers it. An infomercial hook, and a favourite of generated copy. |
|
|
583
|
+
| `inline-label-list` | 2 | copy | warn | Bold-label bullets | '**Speed:** Faster pages' three times in a row: a short label restated in a few words, the list shape generated copy defaults to. A definition list, with a real explanation after each label, is not this. |
|
|
584
|
+
| `ing-tail` | 2 | copy | warn | Empty -ing tail | '..., 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. |
|
|
585
|
+
| `vague-attribution` | 1 | copy | warn | Unnamed source | 'Studies show', 'experts agree', 'it is widely known': a claim credited to nobody. A reader cannot check it, and the house proof rule forbids a claim nobody can check. |
|
|
586
|
+
| `closing-summary` | 1 | copy | warn | Closing summary | 'Overall,', 'In conclusion,', 'In short,': a paragraph that announces it is summing up, then restates what the reader has just read. |
|
|
587
|
+
| `false-range` | 1 | copy | warn | False range | 'From first-time buyers to seasoned investors alike' names two ends of no real scale, to sound as if it covers everyone. It describes nobody. |
|
|
588
|
+
| `phrase-density` | 2 | copy | warn | Leaned-on phrase | 'Rather than', 'actually', 'That is...' openers, participle triads: each is ordinary English once, and a document that uses one far above its rate has one move and repeats it. |
|
|
589
|
+
| `aphorism-density` | 2 | copy | warn | Aphorism cadence | Two clipped sentences alone on a line ('All of this already exists. The job is making it findable.') land once. Closing every section, they are a cadence a reader learns to hear. |
|
|
590
|
+
| `contraction-scarcity` | 1 | copy | warn | No contractions | A long document with almost no contractions reads stiff and machine-made. People write 'we're' and 'don't'. |
|
|
591
|
+
| `sentence-rhythm` | 2 | copy | warn | Metronomic rhythm | Sentence after sentence of the same length reads generated. A person's sentences run long, then short, then long again. |
|
|
592
|
+
| `repeated-sentence` | 1 | copy | warn | Repeated sentence | The same sentence on two pages, or the same nine words lightly edited, is one of the strongest signs a document was assembled rather than written. |
|
|
593
|
+
| `heading-shape` | 2 | copy | warn | One heading shape | Headings that all share one shape ('X, and Y', 'X, because Y', 'Keep, rewrite, consolidate, retire') read as a template filled in section by section. |
|
|
594
|
+
| `heading-echo` | 2 | copy | warn | Heading echoed | A heading repeated word for word as the first line under it spends the reader's attention twice on the same words. |
|
|
578
595
|
<!-- craft:catalogue:end -->
|
package/COPY.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# The house copy standard
|
|
2
|
+
|
|
3
|
+
**Canonical.** Every client repo, every session, every piece of outbound
|
|
4
|
+
writing. This file ships in `@domandigital/craft`, next to the tells that
|
|
5
|
+
enforce it. Anything else claiming to be the copy rules is a copy: link here.
|
|
6
|
+
|
|
7
|
+
- The rules are this file.
|
|
8
|
+
- The detectors are craft's copy tells: `craft tells list` shows every one,
|
|
9
|
+
with why it is a default and what to do instead.
|
|
10
|
+
- The house policy, which tells fail a commit, is `src/character/house.ts`.
|
|
11
|
+
`craft copy --gate` applies it. `copy-check` in claude-kit is a thin wrapper
|
|
12
|
+
over that, and the pre-commit hook runs it.
|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
A reader who has seen a thousand generated pages recognises one in a line.
|
|
17
|
+
None of the patterns below is AI-only, and people use all of them. The signal
|
|
18
|
+
is **stacking**: one is plausible, three on a page reads as machine output.
|
|
19
|
+
|
|
20
|
+
What makes copy read as written by a person is not the absence of tells. It is
|
|
21
|
+
the presence of facts: a place, a time, a number, a name, who does the work.
|
|
22
|
+
The rules below remove the defaults; the writing directives say what to put in
|
|
23
|
+
their place.
|
|
24
|
+
|
|
25
|
+
## Blocking tier
|
|
26
|
+
|
|
27
|
+
A single hit fails the check. These are not judgment calls.
|
|
28
|
+
|
|
29
|
+
- **Em dashes.** Use a full stop, a comma, a colon or a middot. En dashes for
|
|
30
|
+
genuine ranges (`Monday–Saturday`, `9am–5pm`) are correct and stay.
|
|
31
|
+
- **Emoji** in shipped copy. Typographic marks are fine: `★` for a rating, `·`
|
|
32
|
+
as a separator, `→` in a link.
|
|
33
|
+
- **Contrastive negation as a template**, in every form:
|
|
34
|
+
- copular: "It's not a website, it's a growth engine"
|
|
35
|
+
- verbal: "I don't teach the treatment you'd choose: I teach the standard"
|
|
36
|
+
- "not just X" / "not merely" / "not simply" / "more than just"
|
|
37
|
+
- "X isn't a job, it's art"
|
|
38
|
+
- "less about X, more about Y"
|
|
39
|
+
- parallel participle: "shipped from here, not imported from Seoul". Both
|
|
40
|
+
sides must be real participles, so "cosmetics, not medicines" (a plain
|
|
41
|
+
factual contrast) passes.
|
|
42
|
+
- **A "No X" claim as the whole string.** "NO JARGON GUIDE", "No catch." A
|
|
43
|
+
short standalone "No X" reads as a slapped-on badge. Empty-state and error
|
|
44
|
+
text is exempt ("No results found", "No bookings yet"), as is anything under
|
|
45
|
+
a signed-in route (`/admin/`, `/portal/`, `/dashboard/`).
|
|
46
|
+
- **"No X, no Y" as a list**, anywhere in a sentence. "No obligation, no
|
|
47
|
+
spam." Defining the business by what it is not, itemised.
|
|
48
|
+
- The phrase list: `let's dive in`, `let's unpack`, `let's break this down`,
|
|
49
|
+
`here's the thing`, `it's important to note`, `at its core`,
|
|
50
|
+
`the key takeaway`, `the reality is`, `the truth is`,
|
|
51
|
+
`in today's rapidly evolving…`, `increasingly digital`,
|
|
52
|
+
`whether you're a X or a Y`, `seamless(ly)`, `unlock your…`,
|
|
53
|
+
`meaningful impact`, `foster collaboration`, `empower`, `leverage`,
|
|
54
|
+
`harness`, `delve`, `a comprehensive guide to`, `plain English`.
|
|
55
|
+
|
|
56
|
+
Also blocking: `in the event that`, `at no additional cost to you`, `rest
|
|
57
|
+
assured`, `it is worth noting that`, `when it comes to`, `we've got you
|
|
58
|
+
covered`, `dive into`, `unlock the power`, `look no further`.
|
|
59
|
+
|
|
60
|
+
**Buzzwords**: synergy, cutting-edge, best-in-class, best of breed,
|
|
61
|
+
world-class, state of the art, game-changer, paradigm shift, mission-critical,
|
|
62
|
+
low-hanging fruit, move the needle, circle back, touch base, reach out (use
|
|
63
|
+
"get in touch"), value-add, robust, turnkey, holistic, disruptive.
|
|
64
|
+
|
|
65
|
+
**Negative reassurance**: `no risk`, `never locked in`, `nothing goes wrong`,
|
|
66
|
+
`no hidden fees`, `never pay`, `without the hassle`, `no surprises`. Do not
|
|
67
|
+
name a fear and deny it. Say what they get: "The price on the quote is the
|
|
68
|
+
price you pay", not "no hidden fees".
|
|
69
|
+
|
|
70
|
+
`empower`, `leverage` and `harness` block because a plainer word almost always
|
|
71
|
+
exists. Name the mechanism: "Your team can publish without us", not "we
|
|
72
|
+
empower your team".
|
|
73
|
+
|
|
74
|
+
`plain English` has one house replacement: **"properly explained"**.
|
|
75
|
+
"Explained plainly" swaps a synonym and keeps the tell.
|
|
76
|
+
|
|
77
|
+
## Review tier
|
|
78
|
+
|
|
79
|
+
Printed, never blocks. One is normal English; several stacked is not. Only a
|
|
80
|
+
person reading decides which instance earns its place.
|
|
81
|
+
|
|
82
|
+
| Tell | What it catches |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `review-phrase` | truly, genuinely, deeply, ultimately, great question, absolutely, one-size-fits-all, and superlative over-claiming: "the single most", "by far the most", "hands down the", "arguably the best", "bar none" |
|
|
85
|
+
| `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 |
|
|
86
|
+
| `stock-phrase` | to the next level, one-stop shop, we pride ourselves, passionate about, peace of mind, "so here's", a journey used as a metaphor |
|
|
87
|
+
| `hollow-imperative` | Discover, Experience, Transform, Elevate as the opening verb of a heading or button |
|
|
88
|
+
| `rhetorical-opener` | "Looking for...?", "Tired of...?", "Ready to...?" |
|
|
89
|
+
| `staccato-triplet` | "Fast. Friendly. Local." |
|
|
90
|
+
| `where-x-meets-y` | "Where luxury meets comfort" |
|
|
91
|
+
| `ing-tail` | "..., ensuring peace of mind", "..., highlighting our commitment": significance claimed, no mechanism |
|
|
92
|
+
| `vague-attribution` | "studies show", "experts agree", "it is widely known": a claim credited to nobody |
|
|
93
|
+
| `closing-summary` | "Overall,", "In conclusion,", "In short," opening a paragraph that restates what came before |
|
|
94
|
+
| `false-range` | "from first-time buyers to seasoned investors alike", "everyone from students to retirees" |
|
|
95
|
+
| `vague-word` | innovative, scalable, end-to-end, streamline, solutions. Reported only when a file is named outright |
|
|
96
|
+
| `question-reveal` | "The result? Twice the bookings.", "What does this mean for you? It means...": a staged reveal |
|
|
97
|
+
| `inline-label-list` | three or more bullets in a row opening "**Label:**", where the label repeats the line |
|
|
98
|
+
| `placeholder` | "[Insert client name]", lorem ipsum, `TODO_PLACEHOLDER`. Fine in a draft commit; **zero in anything delivered** |
|
|
99
|
+
| `chatbot-residue` | "Certainly! Here's a revised version", "as an AI language model", "let me know if you'd like me to", chat citation tokens, links tracked `utm_source=chatgpt.com`. **Zero in anything delivered**, and the first candidate for the blocking tier |
|
|
100
|
+
|
|
101
|
+
### Style is not evidence
|
|
102
|
+
|
|
103
|
+
Most tells here are house style: they make copy generic, whoever wrote it. The
|
|
104
|
+
research is clear that an em dash, a rule of three or a rhetorical question
|
|
105
|
+
proves nothing about authorship, and em dash rates even run in opposite
|
|
106
|
+
directions between models. Only `chatbot-residue` is evidence that a chat reply
|
|
107
|
+
was pasted. Report findings as an editorial diagnosis ("three benefit claims
|
|
108
|
+
lack a mechanism; two stock phrases recur"), never as a score or an
|
|
109
|
+
accusation.
|
|
110
|
+
|
|
111
|
+
Two more review patterns no regex can separate from honest English, so they
|
|
112
|
+
are for the read-through only:
|
|
113
|
+
|
|
114
|
+
- **Trailing contrastive negation**: "Real Square data, not a vanity metric."
|
|
115
|
+
The same shape is how a genuine disclaimer is written ("general information,
|
|
116
|
+
not a recommendation").
|
|
117
|
+
- **Abstract noun triads** with no concrete referent: "clarity, consistency,
|
|
118
|
+
confidence".
|
|
119
|
+
|
|
120
|
+
## Density tier
|
|
121
|
+
|
|
122
|
+
Review only, never blocks. Every rule above fires on one line read in
|
|
123
|
+
isolation. These fire on a **rate**: each instance is legitimate English, and
|
|
124
|
+
only the count gives it away. They read documents (Markdown, MDX, text, HTML)
|
|
125
|
+
of 400 words or more, and the allowed count is a floor plus a rate per 1,000
|
|
126
|
+
words, so a proposal is not held to a landing page's limit.
|
|
127
|
+
|
|
128
|
+
Why they exist: a 30-page client proposal passed every per-line rule clean.
|
|
129
|
+
A person then found "rather than" 54 times in 10,500 words, no contractions,
|
|
130
|
+
one sentence pasted onto two pages, a heading repeated as its own first line,
|
|
131
|
+
and an italic one-liner closing almost every page.
|
|
132
|
+
|
|
133
|
+
| Tell | Fires above |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `phrase-density` | "rather than" (4, +0.9 per 1,000 words), "instead of" (4, +0.7), "actually" (2, +0.35), "properly" (3, +0.4), "genuinely" (1, +0.2), "the single ...est" (1, +0.15), "That is / This is" openers (4, +0.7), "Every" openers (4, +0.6), "Worth noting" openers (3, +0.5), negation-led openers "None of / Not one / Nothing" (5, +1), participle triads "researched, written and referenced" (3, +0.6) |
|
|
136
|
+
| `aphorism-density` | two clipped sentences alone on a line, "It already exists. The job is making it findable." (5, +1.2) |
|
|
137
|
+
| `contraction-scarcity` | under one contraction per 1,000 words, in 800+ words. Formal legal pages are the deliberate exception |
|
|
138
|
+
| `sentence-rhythm` | sentence lengths that barely vary: spread (standard deviation over mean) under 0.42 across 40+ sentences |
|
|
139
|
+
| `repeated-sentence` | a sentence of nine words or more appearing twice, or two sentences sharing a nine-word run |
|
|
140
|
+
| `heading-shape` | headings sharing one shape: "X, and Y", "X, because Y", a bare comma list (3, +0.5) |
|
|
141
|
+
| `heading-echo` | a heading of four words or more repeated as the first words under it |
|
|
142
|
+
|
|
143
|
+
The fix is never to delete every instance. Keep the ones that land hardest and
|
|
144
|
+
vary the rest. That is why a density finding prints a count and an example,
|
|
145
|
+
not a line to go and edit.
|
|
146
|
+
|
|
147
|
+
## Writing directives
|
|
148
|
+
|
|
149
|
+
These are what to do, not what to avoid. Read them **before** drafting. Do not
|
|
150
|
+
read the tell lists before drafting: naming a phrase to avoid puts it in the
|
|
151
|
+
writer's head, and it comes out anyway. Check against the lists afterwards.
|
|
152
|
+
|
|
153
|
+
- **Lead with the fact.** A place, a time, a number, a name. "Gas Safe
|
|
154
|
+
registered since 2009. Same-day callouts in Brackley." beats "Trusted local
|
|
155
|
+
experts".
|
|
156
|
+
- **Concrete nouns beat abstractions.** "Rated 5.0 from 58 reviews" beats
|
|
157
|
+
"trusted by the community".
|
|
158
|
+
- **Name the mechanism.** Not "hassle-free booking", but "Book in three taps.
|
|
159
|
+
We confirm by text within the hour."
|
|
160
|
+
- **Persuade with something checkable.** Sales copy has to sell, and
|
|
161
|
+
enthusiasm is allowed. Persuasion backed by a fact passes; persuasion with no
|
|
162
|
+
fact behind it is the tell.
|
|
163
|
+
- **Concise, scannable, objective.** In usability testing, cutting word count,
|
|
164
|
+
making text scannable and dropping promotional tone each helped, and all
|
|
165
|
+
three together roughly doubled usability (Nielsen Norman Group).
|
|
166
|
+
- **Vary sentence length.** Some long, some short, some flat. Uniform rhythm
|
|
167
|
+
reads generated, and so does uniform wit.
|
|
168
|
+
- **Use contractions** where you would in speech. Formal legal pages are the
|
|
169
|
+
exception.
|
|
170
|
+
- **If the sentence would read the same with a competitor's name in it**, cut
|
|
171
|
+
it or make it specific.
|
|
172
|
+
- **Never reuse a sentence across two clients.** Templated language across
|
|
173
|
+
clients is a stronger tell than any phrase.
|
|
174
|
+
- **Never open with a thesis-statement negation.** "This is a working build,
|
|
175
|
+
not a mockup." Judge by function: if the sentence exists only for the
|
|
176
|
+
contrast, cut it.
|
|
177
|
+
- **Compliance hedging must not eat the sell.** Put trial design and caveats
|
|
178
|
+
where substantiation belongs (an evidence section, an ingredients panel, a
|
|
179
|
+
footnote). In the selling paragraph, name the claim and stop.
|
|
180
|
+
- **End each major section with one action sentence**: what to do next.
|
|
181
|
+
"Book a call to see if we're a fit."
|
|
182
|
+
|
|
183
|
+
## Register: British and professional
|
|
184
|
+
|
|
185
|
+
The tool cannot grade register; a person does. British spelling and phrasing
|
|
186
|
+
(colour, organise, licence as a noun, centre, towards, enquiries). Professional
|
|
187
|
+
and confident, never matey: no over-familiar asides ("with me nowhere near
|
|
188
|
+
it"), no American idiom ("shallow wins", "looks busy"), no "honestly" as an
|
|
189
|
+
intensifier. Do not change a brand name, domain or legal entity wording.
|
|
190
|
+
|
|
191
|
+
## Word limits
|
|
192
|
+
|
|
193
|
+
| Element | Limit |
|
|
194
|
+
| --- | --- |
|
|
195
|
+
| H1 | 10 words |
|
|
196
|
+
| Subhead | 24 words |
|
|
197
|
+
| Paragraph | 2 sentences |
|
|
198
|
+
| Bullet | 7 words |
|
|
199
|
+
|
|
200
|
+
Hyphenated compounds count as one word. Not automated.
|
|
201
|
+
|
|
202
|
+
## Proof rule
|
|
203
|
+
|
|
204
|
+
- **No invented results, testimonials, logos, certifications or numbers.
|
|
205
|
+
Ever.**
|
|
206
|
+
- Every proof point comes from the project's own proof source (a proofs or
|
|
207
|
+
reviews file, the CMS, the client). If one is missing, write
|
|
208
|
+
`TODO_PLACEHOLDER: <the exact field needed>` and say so. Never fill it.
|
|
209
|
+
- A quotation under a named person must be theirs. Copy drafted for someone,
|
|
210
|
+
in quote marks with their job title beneath it, is a fabricated attribution.
|
|
211
|
+
|
|
212
|
+
## CTA rule
|
|
213
|
+
|
|
214
|
+
- One primary CTA label site-wide on commercial pages, in the form **action +
|
|
215
|
+
outcome**: "Book a free strategy call".
|
|
216
|
+
- The secondary CTA is the same everywhere it appears.
|
|
217
|
+
- Labels and links come from the project's config, never retyped per page.
|
|
218
|
+
|
|
219
|
+
## Never edit to pass: quoted words
|
|
220
|
+
|
|
221
|
+
**A real person's own words are exempt from every rule here.** Reviews,
|
|
222
|
+
testimonials, a practitioner's bio, a quote from the client. Editing them to
|
|
223
|
+
pass an audit falsifies a quotation, which is far worse than an em dash. Mark
|
|
224
|
+
the line `copy-ok` or add the file to `.copyauditignore`, and leave it as they
|
|
225
|
+
said it.
|
|
226
|
+
|
|
227
|
+
**Owner-editable copy is advised on, never overridden.** Where a client types
|
|
228
|
+
their own words into a CMS, report what the check finds and tell them. Copy
|
|
229
|
+
that ships from the repo is ours to fix; copy the owner typed is theirs.
|
|
230
|
+
|
|
231
|
+
## Rendered social assets: no raw links
|
|
232
|
+
|
|
233
|
+
A carousel slide, Story, Reel or caption never shows a raw URL: no `https://`,
|
|
234
|
+
no `www.`, no path. Show a bare domain (`RMP-ELECTRICAL.CO.UK`) or a plain
|
|
235
|
+
call to action ("Link in bio"). Keep the full address in source data and let
|
|
236
|
+
the render step transform it. craft cannot enforce this, because raw URLs are
|
|
237
|
+
legitimate almost everywhere else in a repo. The social generator's own render
|
|
238
|
+
gate enforces it, before its first render ships.
|
|
239
|
+
|
|
240
|
+
## Suppressing a finding
|
|
241
|
+
|
|
242
|
+
For genuine exceptions only, and the reason matters more than the mechanism:
|
|
243
|
+
|
|
244
|
+
- `copy-ok` (or `craft-ok`) on the line or the line above: a one-off. Listed
|
|
245
|
+
in every report, never hidden.
|
|
246
|
+
- `.copyauditignore` at the repo root: globs, matched on the repo-relative
|
|
247
|
+
path and the bare filename.
|
|
248
|
+
- `.nocopycheck` at the repo root: skips the repo.
|
|
249
|
+
- An `exceptions` entry in `art-direction.json`, with a `because` of at least
|
|
250
|
+
a sentence.
|
|
251
|
+
|
|
252
|
+
Reaching for these more than occasionally means a rule is miscalibrated. Fix
|
|
253
|
+
it here and in craft, not in forty repos.
|
|
254
|
+
|
|
255
|
+
## Rewriting without losing facts
|
|
256
|
+
|
|
257
|
+
A rewrite that removes every tell and drops the price is worse than the
|
|
258
|
+
draft. Before handing over any rewrite, compare it with the original:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
craft copy compare original.md rewrite.md
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
It lists every protected fact (prices, numbers, dates, times, phone numbers,
|
|
265
|
+
emails, links, postcodes, names) the rewrite **lost**, and every one it
|
|
266
|
+
**added**. Exit 1 if either list is not empty. Restore a lost fact or say why
|
|
267
|
+
it went; source an added fact or remove it, because an unsourced number is an
|
|
268
|
+
invented one.
|
|
269
|
+
|
|
270
|
+
## Changing a rule
|
|
271
|
+
|
|
272
|
+
1. Edit this file.
|
|
273
|
+
2. Add or change the tell in `src/character/tells/`, with a flag fixture per
|
|
274
|
+
detection path and a pass fixture that is real persuasive copy. A tell that
|
|
275
|
+
fires on good sales copy does not ship.
|
|
276
|
+
3. Place it in `src/character/house.ts`. New tells start as `review`. A tell
|
|
277
|
+
moves to `block` only after its hits across the estate have been read by a
|
|
278
|
+
person and the baseline is recorded in `ROADMAP.md`.
|
|
279
|
+
4. `pnpm --filter @domandigital/craft test`. The binding test fails if a
|
|
280
|
+
phrase in this file's blocking tier does not block.
|
package/README.md
CHANGED
|
@@ -94,6 +94,18 @@ reason, in `art-direction.json`:
|
|
|
94
94
|
{ "exceptions": [{ "tell": "ai-violet", "because": "Violet is on the van, the cards and the fascia." }] }
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
### Copy
|
|
98
|
+
|
|
99
|
+
[COPY.md](./COPY.md) is the house copy standard: the blocking tier, the review
|
|
100
|
+
tier, the density tier that reads whole documents, and what to write instead.
|
|
101
|
+
`craft copy --gate` applies the house policy, so the blocking tier fails the
|
|
102
|
+
run. A test fails if a phrase the standard blocks does not block.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx craft copy --gate content # the house gate: exit 1 on the blocking tier
|
|
106
|
+
npx craft copy compare draft.md rewrite.md # facts a rewrite lost or added
|
|
107
|
+
```
|
|
108
|
+
|
|
97
109
|
The same checks run in code:
|
|
98
110
|
|
|
99
111
|
```ts
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
snapshotPage,
|
|
7
7
|
snapshotUrl,
|
|
8
8
|
snapshotUrls
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-Q4MN3OXF.js";
|
|
10
|
+
import "./chunk-OGSYVCMU.js";
|
|
11
|
+
import "./chunk-CZHRNPDD.js";
|
|
12
12
|
export {
|
|
13
13
|
audit,
|
|
14
14
|
collectInPage,
|