@domandigital/craft 0.11.0 → 0.13.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,41 @@
1
1
  # @domandigital/craft
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 72527d6: Phase L: component-level sameness.
8
+
9
+ - Six rendered tells, all `warn`, read from snapshot version 2: `cta-band-stock` (a centred band on its own colour with one or two buttons), `pricing-trio-popular` (three price cards, one badged "Most popular"), `testimonial-avatar-carousel`, `faq-accordion-closer` (the page ends on a question list), `stats-row` (a row of three to six big figures) and `centred-everything`. A version 1 snapshot never trips them. `CATALOGUE_VERSION` is `2026.09.10`.
10
+ - Tells can now have `surface: "rendered"`: they exist only on a rendered page, so they carry snapshot fixtures and no source fixtures. `runTell` refuses them; use `auditSnapshot`.
11
+ - Snapshot sections gain `badges`, `avatars`, `carousel` and `figures`; fingerprint sections gain `symmetry`, `controls`, `cards`, `background`, `badges`, `avatars`, `carousel` and `figures`.
12
+ - `craft estate compare --component <role>` ranks pairs of sites by how alike one component is and names what they share. `craft estate compare <site> --component <role>` compares one site against the rest. There is no sibling line for components yet, so it ranks and judges nothing.
13
+ - `craft audit --null` scores each component against the same role in the null model (`components` in the JSON).
14
+ - New exports: `RENDERED_TELLS`, `COMPONENT_ROLES`, `PROMO_BADGE`, `componentsOf`, `componentDistance`, `componentShared`, `closestComponents`, `estateComponentPairs`, `componentTypicality`, `componentTypicalities`, and the types `RenderedTell`, `ComponentPair`, `ComponentTypicality`.
15
+
16
+ - 8f5dbf6: Phase M: imagery and provenance.
17
+
18
+ - Snapshots record every picture on the page as `images`: each `img` and each CSS background large enough to be one, with its source (unwrapped from `/_next/image?url=`, `/_vercel/image` and Cloudflare's `/cdn-cgi/image/`), size, position, section, alt text and a role (`photo`, `illustration`, `icon`, `avatar`, `logo`). At most 60, largest first. Older snapshots have no `images` and are never judged on them.
19
+ - `craft audit` reads the first 256 KB of up to 24 of the larger pictures (20 seconds at most per page) and records `provenance`: the IPTC digital source type when an XMP packet or a C2PA manifest says a trained model made the picture. Only a positive is evidence; a picture with no marker is unknown. `snapshotUrl(url, { provenance: false })` skips the reads.
20
+ - Four rendered tells, all `warn`: `stock-photo` (a stock library or placeholder service, by host or by the library's own file name), `stock-avatar` (placeholder-face services), `ai-image` (the picture's own metadata says a model made it) and `no-real-imagery` (a page of four or more sections with no photograph). `CATALOGUE_VERSION` is `2026.09.11`.
21
+ - New exports: `IMAGERY_TELLS`, `STOCK_PHOTO_HOSTS`, `STOCK_AVATAR_HOSTS`, `stockSource`, `stockAvatarSource`, `scanProvenance`, `PROVENANCE_SCAN_BYTES`, `AI_SOURCE_TYPES`, and the types `SnapshotImage`, `ImageRole`, `ImageProvenance`. From `@domandigital/craft/audit`: `PROVENANCE_MAX_IMAGES`, `PROVENANCE_BUDGET_MS`.
22
+
23
+ ## 0.12.0
24
+
25
+ ### Minor Changes
26
+
27
+ - 7622bd9: Phase K: snapshot version 2.
28
+
29
+ - Every section gains a `role`: `cta-band`, `footer-cta`, `pricing`, `testimonials`, `faq`, `process`, `features`, `team`, `contact`, or its `kind`. `kind` keeps its version 1 meaning, so the rendered tells and the calibrated null models and estate read exactly as before.
30
+ - Every section gains `geometry`: `centredShare`, `mirrorSymmetry`, `whitespaceRatio`, `contentWidthRatio`, `background` and `controls`. The page gains `rhythmVariance` (how much section heights vary).
31
+ - `visual`: colourfulness (Hasler and Süsstrunk), edge density and left-right symmetry, from a screenshot of the first 6000px. None of them passes or fails anything. `snapshotUrl(url, { visual: false })` skips the screenshot; a driver without `page.screenshot` gets no `visual`.
32
+ - `SNAPSHOT_VERSION` is 2. `readSnapshot()` reads version 1 files as version 2 marked `migratedFrom: 1`; every command that loads a saved snapshot uses it, so old snapshots still work.
33
+ - `FINGERPRINT_VERSION` is 2: fingerprints carry `sections` (role, centring, width), and `layoutDistance` compares by role and geometry when both sides have them. Against a version 1 fingerprint it compares by `kind`, exactly as before.
34
+ - New exports: `readSnapshot`, `READABLE_SNAPSHOT_VERSIONS`, `visualMeasures`, `EDGE_STEP`, `layoutDistance`, and the types `SectionRole`, `SectionGeometry`, `SnapshotVisual`, `FingerprintSection`, `RgbaImage`.
35
+
36
+ - 07d1cf5: From the 2026-09-11 AI-look research. A new tell, `shadcn-card-stock`: the registry Card recipe left unchanged (`warn`). Cal Sans joins the second-wave reflex fonts. The icon-tile tells now see Unicons, MUI icons and Material Symbols as well as Lucide, Heroicons, Phosphor and Tabler. `craft direction validate` warns when a display or body face is missing from the new licence register, or is capped, per-site or unverified there. The register ships as `licences.json` and as `LICENCES` and `faceLicence()`. Catalogue `2026.09.9`.
37
+ - c63cded: **Behaviour change:** under `craft copy --gate`, a repo can no longer lower the house blocking tier. A `warn` or `off` in `craft.config.json`, or an `art-direction.json` exception, naming a blocking tell (em dash, contrastive negation, chatbot residue and the rest) is refused and listed as "Exception not applied". Raising a tell still works, runs without `--gate` are unchanged, and a genuine one-off still takes `copy-ok` on its line. `holdHouseBlocks` and `HOUSE_LOCKED` are exported from the house policy.
38
+
3
39
  ## 0.11.0
4
40
 
5
41
  ### Minor Changes
package/CHARACTER.md CHANGED
@@ -88,6 +88,13 @@ clean, premium, elegant and the rest), and a value on the tell catalogue
88
88
  without an exception carrying the same reason. Given a snapshot, it warns
89
89
  where the page does not show what the file declares.
90
90
 
91
+ It also reads the display and body faces against the licence register
92
+ (`src/direction/licences.ts`, exported as `licences.json`) and warns on a face
93
+ the register does not know, or knows only as capped, per-site or unverified. A
94
+ licence warning does not undecide the choice: the reason can be sound and the
95
+ face still unlicensed for this client. The register records facts, checked on
96
+ the licensor's own page. It is not a shortlist, and nothing proposes from it.
97
+
91
98
  `craft direction init` writes what the site does today with every reason
92
99
  empty, so the first thing it produces is the list of things nobody decided.
93
100
  Run on RMP's live home page (2026-09-23) it records Fraunces, Manrope, a cream
@@ -185,6 +192,64 @@ against different sets.
185
192
  Known limit: a computed font family is the family the stylesheet asked for.
186
193
  A face that failed to load is still reported under its name.
187
194
 
195
+ ### Stock components (snapshot v2)
196
+
197
+ A version 2 snapshot names what each section is for (`role`: a CTA band,
198
+ pricing, testimonials, an FAQ, a process, features, a team, a contact form)
199
+ and how it is laid out (`geometry`: centring, width, balance, its own ground,
200
+ its buttons), with the parts that make a component a stock one: badge texts,
201
+ round portraits, a carousel, a row of big figures. Six tells read them, and
202
+ only them, so a version 1 snapshot never trips one:
203
+
204
+ - `cta-band-stock`: a centred band on its own colour with one or two buttons.
205
+ - `pricing-trio-popular`: three price cards, one badged "Most popular".
206
+ - `testimonial-avatar-carousel`: round headshots in a sliding carousel.
207
+ - `faq-accordion-closer`: the page ends on a question-and-answer list.
208
+ - `stats-row`: three to six big figures in a row (not straight after the
209
+ hero, which is `hero-then-proof`'s finding).
210
+ - `centred-everything`: 80% or more of the sections centre their text.
211
+
212
+ Each fix keeps the component's job (the ask, the prices, the proof, the
213
+ answers) and changes how it looks. None removes a section, because the
214
+ page's order is the client's, not ours.
215
+
216
+ These six have rendered paths only (surface `rendered`): no source file shows
217
+ a pricing trio reliably. They ship as warnings like every tell, and have not
218
+ yet been read against the estate. Snapshot the estate with this build before
219
+ any is considered for `block`.
220
+
221
+ ### Imagery and provenance
222
+
223
+ A snapshot records every picture on the page (`images`): each `img` and
224
+ each CSS background large enough to be one, with its source (unwrapped from
225
+ an image optimiser such as `/_next/image?url=`), size, position, section and
226
+ a role: photo, illustration, icon, avatar or logo. `craft audit` also reads
227
+ the first 256 KB of up to 24 of the larger pictures and looks for the IPTC
228
+ digital source type that says a trained model made them
229
+ (`trainedAlgorithmicMedia` or `compositeWithTrainedAlgorithmicMedia`), in an
230
+ XMP packet or a C2PA manifest (Content Credentials). Four tells read them:
231
+
232
+ - `stock-photo`: a picture from a stock library or placeholder service, by
233
+ its host (Unsplash, Pexels, iStock, Adobe Stock and others) or, when it was
234
+ downloaded and self-hosted, by the library's own file name
235
+ (`shutterstock_1234567890.jpg`, `…-unsplash.jpg`).
236
+ - `stock-avatar`: round portraits from a placeholder-face service
237
+ (randomuser.me, Pravatar, DiceBear, generated-face sites) or a stock library.
238
+ - `ai-image`: a picture whose own metadata says a model made it.
239
+ - `no-real-imagery`: a page of four or more sections with no photograph at
240
+ all, only icons and illustrations.
241
+
242
+ Provenance only ever counts for. Most pictures carry no metadata, anything
243
+ that re-encodes them (a CMS, an image optimiser) strips it, and a PNG can
244
+ keep it after the pixels, beyond the bytes read. A picture with no marker is
245
+ unknown, not real. `snapshotUrl(url, { provenance: false })` skips the reads.
246
+
247
+ Snapshots taken before craft recorded images have no `images`, and none of
248
+ these tells judges them. There is no `blob-illustration` tell: telling an
249
+ abstract blob from a diagram needs an SVG's path data or its pixels, which a
250
+ cross-origin picture does not give the page, and no flag case has been proved
251
+ reliable. It stays on the list until one is.
252
+
188
253
  ## The counterfactual
189
254
 
190
255
  A list of tells goes stale; the model does not. So signal 2 asks the model
@@ -250,6 +315,29 @@ pipeline that has decided to hold the line.
250
315
  `craft direction propose --estate estate.json` reads the register too, so a
251
316
  proposed accent steers away from one a sibling already uses.
252
317
 
318
+ Sites that share one component are rarer than siblings and easier to fix:
319
+
320
+ ```bash
321
+ craft estate compare --component cta-band # every pair's closest CTA bands
322
+ craft estate compare --component pricing --json
323
+ craft estate compare rmp --component cta-band # one site against the rest
324
+ ```
325
+
326
+ Each pair lists what its two components share ("centred", "1 button", "same
327
+ coloured band"), closest first. There is no sibling line for components yet:
328
+ it needs a calibration set of real components (see the CTA collection in
329
+ `calibration/idiom/cta/` once it exists), so this ranks and names, and judges
330
+ nothing. Only sites added with snapshot v2 have components to compare, and
331
+ the command says so when fewer than two have the role. `--component` takes
332
+ one of `cta-band`, `footer-cta`, `pricing`, `testimonials`, `faq`, `process`,
333
+ `features`, `team`, `contact` or `stats`.
334
+
335
+ With `--null`, `craft audit` also places each of the page's components
336
+ against the same role in the null model, the way it places the whole page
337
+ (`components` in the JSON). A role needs at least five null pages that have
338
+ it. Null models built before snapshot v2 have no components, and the report
339
+ says so rather than printing nothing.
340
+
253
341
  ## The character report
254
342
 
255
343
  `craft report` reads one site against everything above and gives a verdict:
@@ -534,7 +622,7 @@ Generated from the package. Run `pnpm --filter @domandigital/craft run docs`
534
622
  after changing an entry; a test fails until you do.
535
623
 
536
624
  <!-- craft:catalogue:start -->
537
- Catalogue version `2026.09.8`, 55 tells.
625
+ Catalogue version `2026.09.11`, 66 tells.
538
626
 
539
627
  | Id | Gen | Surface | Severity | Tell | Why it is a default |
540
628
  | --- | --- | --- | --- | --- | --- |
@@ -549,6 +637,7 @@ Catalogue version `2026.09.8`, 55 tells.
549
637
  | `reveal-everywhere` | 1 | source + rendered | warn | Reveal on every section | When every block fades up on scroll, the motion stops meaning anything and the page feels slow. MMM had 70, DD 40, sen-sphere 27. |
550
638
  | `pill-everything` | 1 | source + rendered | warn | Pills everywhere | rounded-full on every button, badge and tag is the framework's friendliest default. MMM had 162, DD 125. A shape used everywhere is not a shape language. |
551
639
  | `shadcn-dump` | 1 | source | warn | Stock component dump | A generator exports the whole shadcn/ui kit whether the site uses it or not. Rise & Bloom ships a full set under its own book, vine and petal assets. |
640
+ | `shadcn-card-stock` | 1 | source | warn | Stock shadcn Card | The registry Card (bg-card, a plain border, rounded-lg or rounded-xl, shadow-sm) is the container every generated page is built from. Left as shipped, every card on the site looks like every other shadcn site's. |
552
641
  | `cream-palette` | 2 | source + rendered | warn | Cream ground | Once white-and-violet was named, models moved to a warm off-white: parchment, linen, oat. It reads as 'tasteful' in the same way everywhere. |
553
642
  | `italic-serif-display` | 2 | source + rendered | warn | Italic serif display | A serif heading with one italic word ('Beauty, reimagined') is the second wave's signature move. |
554
643
  | `hero-eyebrow-chip` | 2 | source + rendered | warn | Eyebrow chip above the hero | A small bordered pill ('New · Now booking') above the headline is in almost every generated hero. |
@@ -593,4 +682,14 @@ Catalogue version `2026.09.8`, 55 tells.
593
682
  | `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. |
594
683
  | `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. |
595
684
  | `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. |
685
+ | `cta-band-stock` | 1 | rendered + rendered | warn | Stock call-to-action band | A full-width coloured band with a centred heading, one line and one or two buttons is what a model closes every section run with. It asks for the booking the same way on every site. |
686
+ | `pricing-trio-popular` | 1 | rendered + rendered | warn | Three-tier pricing with a popular badge | Three price cards with the middle one badged 'Most popular' is the SaaS pricing page, applied to a plumber or a salon whether or not anyone chose the middle option. |
687
+ | `testimonial-avatar-carousel` | 1 | rendered + rendered | warn | Avatar testimonial carousel | Round headshots over quotes in a sliding carousel is the stock testimonial block. Most visitors see the first slide only, and stock or AI faces make every quote read as invented. |
688
+ | `faq-accordion-closer` | 2 | rendered + rendered | warn | FAQ accordion as the closer | Ending the page on a collapsed FAQ, often followed only by a band, is the model's default page ending. The answers a buyer needs are hidden behind clicks at the point they decide. |
689
+ | `stats-row` | 1 | rendered + rendered | warn | Row of big numbers | Three or four large figures in a row (500+ clients, 98% satisfaction, 10 years) is the stock proof block, and the numbers are often round, unsourced or invented. |
690
+ | `centred-everything` | 2 | rendered + rendered | warn | Every section centred | When almost every section stacks a centred heading over centred text, the page has no reading line and every block looks like the one before. It is the layout nothing was decided for. |
691
+ | `stock-photo` | 1 | rendered + rendered | warn | Stock photography | A picture from a stock library is the picture every other site in the trade can use. It shows a job the client did not do, in a kitchen that is not theirs, and a visitor who has seen it before stops believing the rest. |
692
+ | `stock-avatar` | 1 | rendered + rendered | warn | Stock or placeholder faces | Round portraits from a placeholder-face service or a stock library next to reviews make every quote read as invented, and some of those faces are generated people who do not exist. |
693
+ | `ai-image` | 2 | rendered + rendered | warn | Generated image | The file itself says a model made it (its XMP or Content Credentials name a trained-model source). A generated picture stands in for work the client did not photograph, and anyone can read the label. |
694
+ | `no-real-imagery` | 1 | rendered + rendered | warn | No photograph on the page | A page with no photograph at all, only icons and illustrations, is the page a model builds when it has no assets. Icon tiles stand in for the evidence a buyer looks for: the work, the premises, the people. |
596
695
  <!-- craft:catalogue:end -->
package/COPY.md CHANGED
@@ -256,8 +256,12 @@ For genuine exceptions only, and the reason matters more than the mechanism:
256
256
  - `.copyauditignore` at the repo root: globs, matched on the repo-relative
257
257
  path and the bare filename.
258
258
  - `.nocopycheck` at the repo root: skips the repo.
259
- - An `exceptions` entry in `art-direction.json`, with a `because` of at least
260
- a sentence.
259
+ - An `exceptions` entry in `art-direction.json`, or a `severity` entry in
260
+ `craft.config.json`, with a `because` of at least a sentence. **Never for
261
+ the blocking tier.** Under `craft copy --gate` a repo can raise a rule but
262
+ not lower one that blocks: the gate refuses the entry and says so. A
263
+ genuine one-off in the blocking tier takes `copy-ok` on its line, where a
264
+ reviewer sees it.
261
265
 
262
266
  Reaching for these more than occasionally means a rule is miscalibrated. Fix
263
267
  it here and in craft, not in forty repos.
package/README.md CHANGED
@@ -129,6 +129,11 @@ repo. `.claude`, `.agents` and `.cursor` are never walked, config or not.
129
129
  A severity change needs a `because`, like an exception. `off` is applied as an
130
130
  exception, so the report lists what it silenced.
131
131
 
132
+ Under `craft copy --gate`, a repo can raise a copy tell but never lower one in
133
+ the house blocking tier. A `warn` or `off` for a blocking tell, here or as an
134
+ exception in `art-direction.json`, is refused and listed in the report. A
135
+ genuine one-off takes `copy-ok` on its line.
136
+
132
137
  ### Copy
133
138
 
134
139
  [COPY.md](./COPY.md) is the house copy standard: the blocking tier, the review
@@ -1,4 +1,7 @@
1
1
  import {
2
+ PROVENANCE_BUDGET_MS,
3
+ PROVENANCE_MAX_IMAGES,
4
+ VISUAL_MAX_HEIGHT,
2
5
  audit,
3
6
  collectInPage,
4
7
  loadPlaywright,
@@ -6,10 +9,13 @@ import {
6
9
  snapshotPage,
7
10
  snapshotUrl,
8
11
  snapshotUrls
9
- } from "./chunk-JP75P2DU.js";
10
- import "./chunk-QMYERCBB.js";
11
- import "./chunk-3NGWXZZE.js";
12
+ } from "./chunk-ZLDA2SYB.js";
13
+ import "./chunk-JYNXGCYZ.js";
14
+ import "./chunk-GMVXADJ6.js";
12
15
  export {
16
+ PROVENANCE_BUDGET_MS,
17
+ PROVENANCE_MAX_IMAGES,
18
+ VISUAL_MAX_HEIGHT,
13
19
  audit,
14
20
  collectInPage,
15
21
  loadPlaywright,