@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 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
- Since catalogue `2026.09.2` the rule lists of claude-kit's
283
- `house-style/copy-rules.md` are carried by tells in this catalogue:
284
- `ai-phrase`, `plainer-word`, `plain-english`, `buzzword`,
285
- `negative-reassurance`, `em-dash`, `emoji`, `not-just-but`, `no-x-no-y` and
286
- `no-x-badge` are its blocking tier; `review-phrase` and `vague-word` its review
287
- tier. craft ships all of them as `warn`. `copy-check` in claude-kit is a thin
288
- wrapper over `craft copy --json` and decides which tells fail a commit, so the
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.3`, 39 tells.
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-OA7SGPN4.js";
10
- import "./chunk-Q54JSNC5.js";
11
- import "./chunk-3AP4PJMX.js";
9
+ } from "./chunk-Q4MN3OXF.js";
10
+ import "./chunk-OGSYVCMU.js";
11
+ import "./chunk-CZHRNPDD.js";
12
12
  export {
13
13
  audit,
14
14
  collectInPage,