@domandigital/craft 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/CHARACTER.md +2 -1
- package/COPY.md +6 -0
- package/README.md +6 -0
- package/dist/{audit-5P65UMFL.js → audit-EGDNWSQB.js} +3 -3
- package/dist/audit.cjs +64 -10
- package/dist/audit.js +64 -10
- package/dist/{chunk-5LI2VFN5.js → chunk-2NQFIDLE.js} +5 -5
- package/dist/{chunk-ZT3WLN7R.js → chunk-3NGWXZZE.js} +64 -10
- package/dist/{chunk-VKQDOUHO.js → chunk-APO4BT2F.js} +4 -4
- package/dist/{chunk-YLQE35PN.js → chunk-JKIDWHG3.js} +133 -7
- package/dist/{chunk-JRMU6GYX.js → chunk-JWHC67ZS.js} +1 -1
- package/dist/{chunk-EHNEGKFR.js → chunk-QMYERCBB.js} +1 -1
- package/dist/{chunk-BYY32NIN.js → chunk-TSKRBIOC.js} +2 -2
- package/dist/{chunk-DAENYJUO.js → chunk-U2JTFQKO.js} +5 -5
- package/dist/{cli-3XPEGBUR.js → cli-2WBGTBKD.js} +4 -4
- package/dist/{cli-V55MTIOP.js → cli-ADQQURNR.js} +9 -9
- package/dist/cli-EK3GSAOW.js +15 -0
- package/dist/{cli-4VAOH7ST.js → cli-PRWTNFVP.js} +4 -4
- package/dist/cli-VQUNNIZM.js +16 -0
- package/dist/cli.js +2 -2
- package/dist/index.cjs +184 -10
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +181 -10
- package/package.json +1 -1
- package/word-lists.json +1 -1
- package/dist/cli-J7C6TI2B.js +0 -16
- package/dist/cli-WDSVVHKG.js +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @domandigital/craft
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- cd13c94: `craft copy claims <paths>` lists every sentence holding a price, figure, date or named source, marked sourced or UNSOURCED, for a person to check against the primary source. `findClaims` and `formatClaims` do the same in code. No tell can tell a true figure from a false one; this makes the checking a list instead of a hunt.
|
|
8
|
+
- cd13c94: Catalogue 2026.09.8, from reading all 144 review findings on the live DD site. New tell `prompt-context` (review): the model naming its own inputs, "the attached Perplexity research file", 24 real hits in five live articles. Tuned: `inline-label-list` leaves reference and data lists alone (0 of 11 hits were real), `staccato-triplet` no longer crosses a paragraph break, `ing-tail` passes a list of tasks, `repeated-sentence` ignores footnotes repeating a source title.
|
|
9
|
+
|
|
3
10
|
## 0.9.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
package/CHARACTER.md
CHANGED
|
@@ -534,7 +534,7 @@ Generated from the package. Run `pnpm --filter @domandigital/craft run docs`
|
|
|
534
534
|
after changing an entry; a test fails until you do.
|
|
535
535
|
|
|
536
536
|
<!-- craft:catalogue:start -->
|
|
537
|
-
Catalogue version `2026.09.
|
|
537
|
+
Catalogue version `2026.09.8`, 55 tells.
|
|
538
538
|
|
|
539
539
|
| Id | Gen | Surface | Severity | Tell | Why it is a default |
|
|
540
540
|
| --- | --- | --- | --- | --- | --- |
|
|
@@ -578,6 +578,7 @@ Catalogue version `2026.09.7`, 54 tells.
|
|
|
578
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. |
|
|
579
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
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
|
+
| `prompt-context` | 1 | copy | warn | Prompt context | 'This article uses the attached Perplexity research file as the primary source', 'the attached research is right to say': the model talking about what it was given. A reader of a web page has no attachment. Found 38 times across five live DD articles in 2026-09, none caught by any other tell. |
|
|
581
582
|
| `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
583
|
| `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
584
|
| `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. |
|
package/COPY.md
CHANGED
|
@@ -100,6 +100,7 @@ person reading decides which instance earns its place.
|
|
|
100
100
|
| `vague-word` | innovative, scalable, end-to-end, streamline, solutions. Reported only when a file is named outright |
|
|
101
101
|
| `question-reveal` | "The result? Twice the bookings.", "What does this mean for you? It means...": a staged reveal |
|
|
102
102
|
| `inline-label-list` | three or more bullets in a row opening "**Label:**", where the label repeats the line |
|
|
103
|
+
| `prompt-context` | "This article uses the attached Perplexity research file as the primary source", "the attached research is right to say", "the attached brief asks": the model naming what it was given. A web page has no attachment. **Zero in anything published** |
|
|
103
104
|
| `placeholder` | "[Insert client name]", lorem ipsum, `TODO_PLACEHOLDER`. Fine in a draft commit; **zero in anything delivered** |
|
|
104
105
|
|
|
105
106
|
### Style is not evidence
|
|
@@ -212,6 +213,11 @@ Hyphenated compounds count as one word. Not automated.
|
|
|
212
213
|
`TODO_PLACEHOLDER: <the exact field needed>` and say so. Never fill it.
|
|
213
214
|
- A quotation under a named person must be theirs. Copy drafted for someone,
|
|
214
215
|
in quote marks with their job title beneath it, is a fabricated attribution.
|
|
216
|
+
- **Every price, figure, date and named source is checked against its primary
|
|
217
|
+
source before it ships.** `craft copy claims <paths>` lists them, each marked
|
|
218
|
+
sourced or UNSOURCED. A claim is sourced, corrected or cut, never re-cited
|
|
219
|
+
from memory. The copy check cannot do this part: a false figure in clean
|
|
220
|
+
prose passes every tell.
|
|
215
221
|
|
|
216
222
|
## CTA rule
|
|
217
223
|
|
package/README.md
CHANGED
|
@@ -104,8 +104,14 @@ run. A test fails if a phrase the standard blocks does not block.
|
|
|
104
104
|
```bash
|
|
105
105
|
npx craft copy --gate content # the house gate: exit 1 on the blocking tier
|
|
106
106
|
npx craft copy compare draft.md rewrite.md # facts a rewrite lost or added
|
|
107
|
+
npx craft copy claims content # every price, figure, date and named source, to check
|
|
107
108
|
```
|
|
108
109
|
|
|
110
|
+
No tell can say whether a figure is true. `craft copy claims` lists each
|
|
111
|
+
sentence a reader would take as a checkable fact, marked `sourced` (a source is
|
|
112
|
+
named) or `UNSOURCED`, for a person to check against the primary source. It
|
|
113
|
+
always exits 0: a checklist, not a verdict.
|
|
114
|
+
|
|
109
115
|
The same checks run in code:
|
|
110
116
|
|
|
111
117
|
```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-TSKRBIOC.js";
|
|
10
|
+
import "./chunk-QMYERCBB.js";
|
|
11
|
+
import "./chunk-3NGWXZZE.js";
|
|
12
12
|
export {
|
|
13
13
|
audit,
|
|
14
14
|
collectInPage,
|
package/dist/audit.cjs
CHANGED
|
@@ -564,10 +564,14 @@ var COPY_TELLS = [
|
|
|
564
564
|
surface: "copy",
|
|
565
565
|
why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') is the second wave's favourite rhythm.",
|
|
566
566
|
fix: "Write one sentence that says which of the three is true, and how you know.",
|
|
567
|
-
|
|
567
|
+
// The fragments share one paragraph: "You don't. Again.", a blank line, then "It's frustrating." is two beats, not three.
|
|
568
|
+
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),
|
|
568
569
|
fixtures: {
|
|
569
|
-
flag: [f("content/home.md", "No fuss. No jargon. Just results.")],
|
|
570
|
-
pass: [
|
|
570
|
+
flag: [f("content/home.md", "No fuss. No jargon. Just results."), f("content/home.md", "No fuss.\nNo jargon.\nJust results.")],
|
|
571
|
+
pass: [
|
|
572
|
+
f("content/home.md", "We fix boilers the same day, and we tell you the price before we start."),
|
|
573
|
+
f("content/guide.md", "Your competitor appears in the top three. You don't. Again.\n\nIt's frustrating.")
|
|
574
|
+
]
|
|
571
575
|
}
|
|
572
576
|
},
|
|
573
577
|
{
|
|
@@ -586,6 +590,14 @@ var COPY_TELLS = [
|
|
|
586
590
|
}
|
|
587
591
|
];
|
|
588
592
|
var ING_TAIL = /,\s+(?:ensuring|highlighting|underscoring|showcasing|reflecting|demonstrating|emphasi[sz]ing|fostering|cementing|solidifying|reinforcing|symboli[sz]ing|signalling|signaling)\b[^.!?\n]{0,80}/i;
|
|
593
|
+
var PROMPT_CONTEXT = [
|
|
594
|
+
/\b(?:the|this|that)\s+(?:attached|uploaded|provided)\s+(?:(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\s+)?(?:research|research file|research pack|source file|sources)\b/i,
|
|
595
|
+
/\b(?:attached|uploaded)\s+(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\b/i,
|
|
596
|
+
/\bthe\s+(?:attached|uploaded|provided)\s+brief\s+(?:asks|says|wants|requests|suggests|mentions)\b/i
|
|
597
|
+
];
|
|
598
|
+
function gerundList(block, _match, index) {
|
|
599
|
+
return /(?:^|[(,;:]\s*)\w+ing\b[^,.;()!?]{0,40}$/.test(block.text.slice(Math.max(0, index - 60), index));
|
|
600
|
+
}
|
|
589
601
|
var VAGUE_ATTRIBUTION = [
|
|
590
602
|
/\b(?:studies|research|experts|scientists|surveys|statistics|the data)\s+(?:show|shows|suggest|suggests|agree|say|says|confirm|confirms|prove|proves|indicate|indicates)\b/i,
|
|
591
603
|
/\b(?:many|most)\s+(?:experts|people|customers|professionals|homeowners)\s+(?:agree|say|believe)\b/i,
|
|
@@ -671,8 +683,10 @@ function inlineLabelHits(ctx) {
|
|
|
671
683
|
run = [];
|
|
672
684
|
};
|
|
673
685
|
for (const line of lines) {
|
|
674
|
-
const m = /^\s*
|
|
675
|
-
|
|
686
|
+
const m = /^\s*[-*+]\s+\*\*([^*]{1,40}?):?\*\*:?\s+(\S.*)$/.exec(line);
|
|
687
|
+
const data = m && /[\d£$€%`]|https?:|^\*\*/.test(m[2]);
|
|
688
|
+
const named = m && /\S[A-Z]|[./]/.test(m[1]);
|
|
689
|
+
if (m && !data && !named && m[2].trim().split(/\s+/).length <= 6) run.push(offset);
|
|
676
690
|
else flush();
|
|
677
691
|
offset += line.length + 1;
|
|
678
692
|
}
|
|
@@ -710,6 +724,29 @@ var RESEARCH_TELLS = [
|
|
|
710
724
|
]
|
|
711
725
|
}
|
|
712
726
|
},
|
|
727
|
+
{
|
|
728
|
+
id: "prompt-context",
|
|
729
|
+
name: "Prompt context",
|
|
730
|
+
generation: 1,
|
|
731
|
+
severity: "warn",
|
|
732
|
+
surface: "copy",
|
|
733
|
+
why: "'This article uses the attached Perplexity research file as the primary source', 'the attached research is right to say': the model talking about what it was given. A reader of a web page has no attachment. Found 38 times across five live DD articles in 2026-09, none caught by any other tell.",
|
|
734
|
+
fix: "Delete the reference to the research or brief. Where it carried a claim, name the real source or cut the claim.",
|
|
735
|
+
detect: phrases(PROMPT_CONTEXT, quoted),
|
|
736
|
+
fixtures: {
|
|
737
|
+
flag: [
|
|
738
|
+
f("content/guide.md", "This article uses the attached Perplexity research file as the primary source."),
|
|
739
|
+
f("content/guide.md", "The attached research is right to say these platforms suit early-stage businesses."),
|
|
740
|
+
f("content/guide.md", "That is exactly how the uploaded research frames the category."),
|
|
741
|
+
f("content/guide.md", "The attached brief asks for the point that most tradespeople miss.")
|
|
742
|
+
],
|
|
743
|
+
pass: [
|
|
744
|
+
f("content/guide.md", "We research every quote before we send it."),
|
|
745
|
+
f("app/email.tsx", `export const Invoice = () => <p>Your invoice is attached.</p>;`),
|
|
746
|
+
f("content/guide.md", "BrightLocal's research shows that reviews drive 20% of local ranking.")
|
|
747
|
+
]
|
|
748
|
+
}
|
|
749
|
+
},
|
|
713
750
|
{
|
|
714
751
|
id: "placeholder",
|
|
715
752
|
name: "Unfilled placeholder",
|
|
@@ -762,7 +799,11 @@ var RESEARCH_TELLS = [
|
|
|
762
799
|
pass: [
|
|
763
800
|
f("content/home.md", "- Pages load in under a second\n- Card details never touch our server\n- Support by phone until 8pm\n"),
|
|
764
801
|
f("content/home.md", "- **Speed:** Faster pages\n- **Security:** Safer data\n"),
|
|
765
|
-
f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n")
|
|
802
|
+
f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n"),
|
|
803
|
+
f("content/guide.md", "- **Per page**: \xA3100-\xA3200 per page\n- **Hourly**: \xA350-\xA375/hour\n- **Day rate**: \xA3350-\xA3500\n"),
|
|
804
|
+
f("content/guide.md", "- **Law Society Find a Solicitor:** For solicitors\n- **AccountingWEB / ICAEW directories:** For accountants\n- **NHS.uk:** For NHS services\n"),
|
|
805
|
+
f("content/guide.md", "1. **Visit PageSpeed Insights** and paste the address\n2. **Enter their URL** and run it\n3. **Note their scores** for later\n"),
|
|
806
|
+
f("content/guide.md", "- **Structural problems:** **REBUILD.**\n- **Messaging issues:** **REFRESH.**\n- **Neither:** **LEAVE IT.**\n")
|
|
766
807
|
]
|
|
767
808
|
}
|
|
768
809
|
},
|
|
@@ -774,7 +815,7 @@ var RESEARCH_TELLS = [
|
|
|
774
815
|
surface: "copy",
|
|
775
816
|
why: "'..., ensuring peace of mind', '..., highlighting our commitment': a participle tacked on the end that claims significance and names no mechanism. One of the most common shapes in the Wikipedia guide to AI writing.",
|
|
776
817
|
fix: "Cut the tail, or replace it with the mechanism: 'so you can drop the car off before work'.",
|
|
777
|
-
detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, ""))),
|
|
818
|
+
detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, "")), void 0, gerundList),
|
|
778
819
|
fixtures: {
|
|
779
820
|
flag: [
|
|
780
821
|
f("content/home.md", "Every engineer is Gas Safe registered, ensuring complete peace of mind."),
|
|
@@ -782,7 +823,8 @@ var RESEARCH_TELLS = [
|
|
|
782
823
|
],
|
|
783
824
|
pass: [
|
|
784
825
|
f("content/home.md", "We open at 7am, so you can drop the car off before work."),
|
|
785
|
-
f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers.")
|
|
826
|
+
f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers."),
|
|
827
|
+
f("content/guide.md", "Many items (claiming your GBP, ensuring NAP consistency, writing title tags) are straightforward.")
|
|
786
828
|
]
|
|
787
829
|
}
|
|
788
830
|
},
|
|
@@ -1032,6 +1074,7 @@ function repeatHits(ctx) {
|
|
|
1032
1074
|
for (const s of sentences(doc2)) {
|
|
1033
1075
|
const words = normal(s.text);
|
|
1034
1076
|
if (words.length < RUN || !/[.!?]["'’”)]*$/.test(s.text)) continue;
|
|
1077
|
+
if (/^\s*\[\^[\w-]+\]:/.test(s.text)) continue;
|
|
1035
1078
|
const key = words.join(" ");
|
|
1036
1079
|
const earlier = whole.get(key);
|
|
1037
1080
|
if (earlier) {
|
|
@@ -1229,7 +1272,15 @@ var DENSITY_TELLS = [
|
|
|
1229
1272
|
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nMore here.\n\nWe check every valve before the engineer leaves the house.")),
|
|
1230
1273
|
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nBy then we check every valve before the engineer leaves the site."))
|
|
1231
1274
|
],
|
|
1232
|
-
pass: [
|
|
1275
|
+
pass: [
|
|
1276
|
+
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nThe engineer leaves once the customer has signed the sheet.")),
|
|
1277
|
+
f2(
|
|
1278
|
+
"content/guide.md",
|
|
1279
|
+
doc(
|
|
1280
|
+
"The Independent Healthcare Providers Network Private Patient Insight Report 2024 found that private demand rose.[^1]\n\n[^1]: Independent Healthcare Providers Network, Private Patient Insight Report 2024, published online."
|
|
1281
|
+
)
|
|
1282
|
+
)
|
|
1283
|
+
]
|
|
1233
1284
|
}
|
|
1234
1285
|
},
|
|
1235
1286
|
{
|
|
@@ -2309,6 +2360,9 @@ var HOUSE = {
|
|
|
2309
2360
|
"closing-summary": { tier: "review", label: "closing summary" },
|
|
2310
2361
|
"false-range": { tier: "review", label: "false range" },
|
|
2311
2362
|
"placeholder": { tier: "review", label: "unfilled placeholder" },
|
|
2363
|
+
// Every estate hit so far is real (38 in five DD articles, 2026-09-24), but
|
|
2364
|
+
// a proposal can honestly say "the attached research". Review until measured there.
|
|
2365
|
+
"prompt-context": { tier: "review", label: "prompt context: the model naming its own inputs" },
|
|
2312
2366
|
"question-reveal": { tier: "review", label: "staged reveal" },
|
|
2313
2367
|
"inline-label-list": { tier: "review", label: "bold-label bullets" },
|
|
2314
2368
|
"phrase-density": { tier: "review", label: "density" },
|
|
@@ -2675,7 +2729,7 @@ var RENDERED_PATHS = {
|
|
|
2675
2729
|
};
|
|
2676
2730
|
|
|
2677
2731
|
// src/character/check.ts
|
|
2678
|
-
var CATALOGUE_VERSION = "2026.09.
|
|
2732
|
+
var CATALOGUE_VERSION = "2026.09.8";
|
|
2679
2733
|
var CATALOGUE = [...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map(
|
|
2680
2734
|
(t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t
|
|
2681
2735
|
);
|
package/dist/audit.js
CHANGED
|
@@ -532,10 +532,14 @@ var COPY_TELLS = [
|
|
|
532
532
|
surface: "copy",
|
|
533
533
|
why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') is the second wave's favourite rhythm.",
|
|
534
534
|
fix: "Write one sentence that says which of the three is true, and how you know.",
|
|
535
|
-
|
|
535
|
+
// The fragments share one paragraph: "You don't. Again.", a blank line, then "It's frustrating." is two beats, not three.
|
|
536
|
+
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),
|
|
536
537
|
fixtures: {
|
|
537
|
-
flag: [f("content/home.md", "No fuss. No jargon. Just results.")],
|
|
538
|
-
pass: [
|
|
538
|
+
flag: [f("content/home.md", "No fuss. No jargon. Just results."), f("content/home.md", "No fuss.\nNo jargon.\nJust results.")],
|
|
539
|
+
pass: [
|
|
540
|
+
f("content/home.md", "We fix boilers the same day, and we tell you the price before we start."),
|
|
541
|
+
f("content/guide.md", "Your competitor appears in the top three. You don't. Again.\n\nIt's frustrating.")
|
|
542
|
+
]
|
|
539
543
|
}
|
|
540
544
|
},
|
|
541
545
|
{
|
|
@@ -554,6 +558,14 @@ var COPY_TELLS = [
|
|
|
554
558
|
}
|
|
555
559
|
];
|
|
556
560
|
var ING_TAIL = /,\s+(?:ensuring|highlighting|underscoring|showcasing|reflecting|demonstrating|emphasi[sz]ing|fostering|cementing|solidifying|reinforcing|symboli[sz]ing|signalling|signaling)\b[^.!?\n]{0,80}/i;
|
|
561
|
+
var PROMPT_CONTEXT = [
|
|
562
|
+
/\b(?:the|this|that)\s+(?:attached|uploaded|provided)\s+(?:(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\s+)?(?:research|research file|research pack|source file|sources)\b/i,
|
|
563
|
+
/\b(?:attached|uploaded)\s+(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\b/i,
|
|
564
|
+
/\bthe\s+(?:attached|uploaded|provided)\s+brief\s+(?:asks|says|wants|requests|suggests|mentions)\b/i
|
|
565
|
+
];
|
|
566
|
+
function gerundList(block, _match, index) {
|
|
567
|
+
return /(?:^|[(,;:]\s*)\w+ing\b[^,.;()!?]{0,40}$/.test(block.text.slice(Math.max(0, index - 60), index));
|
|
568
|
+
}
|
|
557
569
|
var VAGUE_ATTRIBUTION = [
|
|
558
570
|
/\b(?:studies|research|experts|scientists|surveys|statistics|the data)\s+(?:show|shows|suggest|suggests|agree|say|says|confirm|confirms|prove|proves|indicate|indicates)\b/i,
|
|
559
571
|
/\b(?:many|most)\s+(?:experts|people|customers|professionals|homeowners)\s+(?:agree|say|believe)\b/i,
|
|
@@ -639,8 +651,10 @@ function inlineLabelHits(ctx) {
|
|
|
639
651
|
run = [];
|
|
640
652
|
};
|
|
641
653
|
for (const line of lines) {
|
|
642
|
-
const m = /^\s*
|
|
643
|
-
|
|
654
|
+
const m = /^\s*[-*+]\s+\*\*([^*]{1,40}?):?\*\*:?\s+(\S.*)$/.exec(line);
|
|
655
|
+
const data = m && /[\d£$€%`]|https?:|^\*\*/.test(m[2]);
|
|
656
|
+
const named = m && /\S[A-Z]|[./]/.test(m[1]);
|
|
657
|
+
if (m && !data && !named && m[2].trim().split(/\s+/).length <= 6) run.push(offset);
|
|
644
658
|
else flush();
|
|
645
659
|
offset += line.length + 1;
|
|
646
660
|
}
|
|
@@ -678,6 +692,29 @@ var RESEARCH_TELLS = [
|
|
|
678
692
|
]
|
|
679
693
|
}
|
|
680
694
|
},
|
|
695
|
+
{
|
|
696
|
+
id: "prompt-context",
|
|
697
|
+
name: "Prompt context",
|
|
698
|
+
generation: 1,
|
|
699
|
+
severity: "warn",
|
|
700
|
+
surface: "copy",
|
|
701
|
+
why: "'This article uses the attached Perplexity research file as the primary source', 'the attached research is right to say': the model talking about what it was given. A reader of a web page has no attachment. Found 38 times across five live DD articles in 2026-09, none caught by any other tell.",
|
|
702
|
+
fix: "Delete the reference to the research or brief. Where it carried a claim, name the real source or cut the claim.",
|
|
703
|
+
detect: phrases(PROMPT_CONTEXT, quoted),
|
|
704
|
+
fixtures: {
|
|
705
|
+
flag: [
|
|
706
|
+
f("content/guide.md", "This article uses the attached Perplexity research file as the primary source."),
|
|
707
|
+
f("content/guide.md", "The attached research is right to say these platforms suit early-stage businesses."),
|
|
708
|
+
f("content/guide.md", "That is exactly how the uploaded research frames the category."),
|
|
709
|
+
f("content/guide.md", "The attached brief asks for the point that most tradespeople miss.")
|
|
710
|
+
],
|
|
711
|
+
pass: [
|
|
712
|
+
f("content/guide.md", "We research every quote before we send it."),
|
|
713
|
+
f("app/email.tsx", `export const Invoice = () => <p>Your invoice is attached.</p>;`),
|
|
714
|
+
f("content/guide.md", "BrightLocal's research shows that reviews drive 20% of local ranking.")
|
|
715
|
+
]
|
|
716
|
+
}
|
|
717
|
+
},
|
|
681
718
|
{
|
|
682
719
|
id: "placeholder",
|
|
683
720
|
name: "Unfilled placeholder",
|
|
@@ -730,7 +767,11 @@ var RESEARCH_TELLS = [
|
|
|
730
767
|
pass: [
|
|
731
768
|
f("content/home.md", "- Pages load in under a second\n- Card details never touch our server\n- Support by phone until 8pm\n"),
|
|
732
769
|
f("content/home.md", "- **Speed:** Faster pages\n- **Security:** Safer data\n"),
|
|
733
|
-
f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n")
|
|
770
|
+
f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n"),
|
|
771
|
+
f("content/guide.md", "- **Per page**: \xA3100-\xA3200 per page\n- **Hourly**: \xA350-\xA375/hour\n- **Day rate**: \xA3350-\xA3500\n"),
|
|
772
|
+
f("content/guide.md", "- **Law Society Find a Solicitor:** For solicitors\n- **AccountingWEB / ICAEW directories:** For accountants\n- **NHS.uk:** For NHS services\n"),
|
|
773
|
+
f("content/guide.md", "1. **Visit PageSpeed Insights** and paste the address\n2. **Enter their URL** and run it\n3. **Note their scores** for later\n"),
|
|
774
|
+
f("content/guide.md", "- **Structural problems:** **REBUILD.**\n- **Messaging issues:** **REFRESH.**\n- **Neither:** **LEAVE IT.**\n")
|
|
734
775
|
]
|
|
735
776
|
}
|
|
736
777
|
},
|
|
@@ -742,7 +783,7 @@ var RESEARCH_TELLS = [
|
|
|
742
783
|
surface: "copy",
|
|
743
784
|
why: "'..., ensuring peace of mind', '..., highlighting our commitment': a participle tacked on the end that claims significance and names no mechanism. One of the most common shapes in the Wikipedia guide to AI writing.",
|
|
744
785
|
fix: "Cut the tail, or replace it with the mechanism: 'so you can drop the car off before work'.",
|
|
745
|
-
detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, ""))),
|
|
786
|
+
detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, "")), void 0, gerundList),
|
|
746
787
|
fixtures: {
|
|
747
788
|
flag: [
|
|
748
789
|
f("content/home.md", "Every engineer is Gas Safe registered, ensuring complete peace of mind."),
|
|
@@ -750,7 +791,8 @@ var RESEARCH_TELLS = [
|
|
|
750
791
|
],
|
|
751
792
|
pass: [
|
|
752
793
|
f("content/home.md", "We open at 7am, so you can drop the car off before work."),
|
|
753
|
-
f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers.")
|
|
794
|
+
f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers."),
|
|
795
|
+
f("content/guide.md", "Many items (claiming your GBP, ensuring NAP consistency, writing title tags) are straightforward.")
|
|
754
796
|
]
|
|
755
797
|
}
|
|
756
798
|
},
|
|
@@ -1000,6 +1042,7 @@ function repeatHits(ctx) {
|
|
|
1000
1042
|
for (const s of sentences(doc2)) {
|
|
1001
1043
|
const words = normal(s.text);
|
|
1002
1044
|
if (words.length < RUN || !/[.!?]["'’”)]*$/.test(s.text)) continue;
|
|
1045
|
+
if (/^\s*\[\^[\w-]+\]:/.test(s.text)) continue;
|
|
1003
1046
|
const key = words.join(" ");
|
|
1004
1047
|
const earlier = whole.get(key);
|
|
1005
1048
|
if (earlier) {
|
|
@@ -1197,7 +1240,15 @@ var DENSITY_TELLS = [
|
|
|
1197
1240
|
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nMore here.\n\nWe check every valve before the engineer leaves the house.")),
|
|
1198
1241
|
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nBy then we check every valve before the engineer leaves the site."))
|
|
1199
1242
|
],
|
|
1200
|
-
pass: [
|
|
1243
|
+
pass: [
|
|
1244
|
+
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nThe engineer leaves once the customer has signed the sheet.")),
|
|
1245
|
+
f2(
|
|
1246
|
+
"content/guide.md",
|
|
1247
|
+
doc(
|
|
1248
|
+
"The Independent Healthcare Providers Network Private Patient Insight Report 2024 found that private demand rose.[^1]\n\n[^1]: Independent Healthcare Providers Network, Private Patient Insight Report 2024, published online."
|
|
1249
|
+
)
|
|
1250
|
+
)
|
|
1251
|
+
]
|
|
1201
1252
|
}
|
|
1202
1253
|
},
|
|
1203
1254
|
{
|
|
@@ -2277,6 +2328,9 @@ var HOUSE = {
|
|
|
2277
2328
|
"closing-summary": { tier: "review", label: "closing summary" },
|
|
2278
2329
|
"false-range": { tier: "review", label: "false range" },
|
|
2279
2330
|
"placeholder": { tier: "review", label: "unfilled placeholder" },
|
|
2331
|
+
// Every estate hit so far is real (38 in five DD articles, 2026-09-24), but
|
|
2332
|
+
// a proposal can honestly say "the attached research". Review until measured there.
|
|
2333
|
+
"prompt-context": { tier: "review", label: "prompt context: the model naming its own inputs" },
|
|
2280
2334
|
"question-reveal": { tier: "review", label: "staged reveal" },
|
|
2281
2335
|
"inline-label-list": { tier: "review", label: "bold-label bullets" },
|
|
2282
2336
|
"phrase-density": { tier: "review", label: "density" },
|
|
@@ -2643,7 +2697,7 @@ var RENDERED_PATHS = {
|
|
|
2643
2697
|
};
|
|
2644
2698
|
|
|
2645
2699
|
// src/character/check.ts
|
|
2646
|
-
var CATALOGUE_VERSION = "2026.09.
|
|
2700
|
+
var CATALOGUE_VERSION = "2026.09.8";
|
|
2647
2701
|
var CATALOGUE = [...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map(
|
|
2648
2702
|
(t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t
|
|
2649
2703
|
);
|
|
@@ -3,18 +3,18 @@ import {
|
|
|
3
3
|
hueName,
|
|
4
4
|
loadNull,
|
|
5
5
|
shapeName
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-APO4BT2F.js";
|
|
7
7
|
import {
|
|
8
8
|
parseFlags
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JKIDWHG3.js";
|
|
10
10
|
import {
|
|
11
11
|
fingerprint,
|
|
12
12
|
fingerprintDistance
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-QMYERCBB.js";
|
|
14
14
|
import {
|
|
15
15
|
SNAPSHOT_VERSION,
|
|
16
16
|
auditSnapshot
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-3NGWXZZE.js";
|
|
18
18
|
|
|
19
19
|
// src/estate/cli.ts
|
|
20
20
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -92,7 +92,7 @@ async function snapshotOf(target, cwd) {
|
|
|
92
92
|
return snap;
|
|
93
93
|
}
|
|
94
94
|
if (!/^https?:\/\//.test(target)) throw new Error(`${target} is neither a URL, a snapshot file, nor a site in the register`);
|
|
95
|
-
const { snapshotUrl } = await import("./audit-
|
|
95
|
+
const { snapshotUrl } = await import("./audit-EGDNWSQB.js");
|
|
96
96
|
return snapshotUrl(target);
|
|
97
97
|
}
|
|
98
98
|
var line = (distance, sibling, shared) => `${distance.toFixed(2)}${sibling ? " SIBLING" : ""}${shared.length ? ` shares ${shared.join(", ")}` : ""}`;
|
|
@@ -866,10 +866,14 @@ var COPY_TELLS = [
|
|
|
866
866
|
surface: "copy",
|
|
867
867
|
why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') is the second wave's favourite rhythm.",
|
|
868
868
|
fix: "Write one sentence that says which of the three is true, and how you know.",
|
|
869
|
-
|
|
869
|
+
// The fragments share one paragraph: "You don't. Again.", a blank line, then "It's frustrating." is two beats, not three.
|
|
870
|
+
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),
|
|
870
871
|
fixtures: {
|
|
871
|
-
flag: [f("content/home.md", "No fuss. No jargon. Just results.")],
|
|
872
|
-
pass: [
|
|
872
|
+
flag: [f("content/home.md", "No fuss. No jargon. Just results."), f("content/home.md", "No fuss.\nNo jargon.\nJust results.")],
|
|
873
|
+
pass: [
|
|
874
|
+
f("content/home.md", "We fix boilers the same day, and we tell you the price before we start."),
|
|
875
|
+
f("content/guide.md", "Your competitor appears in the top three. You don't. Again.\n\nIt's frustrating.")
|
|
876
|
+
]
|
|
873
877
|
}
|
|
874
878
|
},
|
|
875
879
|
{
|
|
@@ -888,6 +892,14 @@ var COPY_TELLS = [
|
|
|
888
892
|
}
|
|
889
893
|
];
|
|
890
894
|
var ING_TAIL = /,\s+(?:ensuring|highlighting|underscoring|showcasing|reflecting|demonstrating|emphasi[sz]ing|fostering|cementing|solidifying|reinforcing|symboli[sz]ing|signalling|signaling)\b[^.!?\n]{0,80}/i;
|
|
895
|
+
var PROMPT_CONTEXT = [
|
|
896
|
+
/\b(?:the|this|that)\s+(?:attached|uploaded|provided)\s+(?:(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\s+)?(?:research|research file|research pack|source file|sources)\b/i,
|
|
897
|
+
/\b(?:attached|uploaded)\s+(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\b/i,
|
|
898
|
+
/\bthe\s+(?:attached|uploaded|provided)\s+brief\s+(?:asks|says|wants|requests|suggests|mentions)\b/i
|
|
899
|
+
];
|
|
900
|
+
function gerundList(block, _match, index) {
|
|
901
|
+
return /(?:^|[(,;:]\s*)\w+ing\b[^,.;()!?]{0,40}$/.test(block.text.slice(Math.max(0, index - 60), index));
|
|
902
|
+
}
|
|
891
903
|
var VAGUE_ATTRIBUTION = [
|
|
892
904
|
/\b(?:studies|research|experts|scientists|surveys|statistics|the data)\s+(?:show|shows|suggest|suggests|agree|say|says|confirm|confirms|prove|proves|indicate|indicates)\b/i,
|
|
893
905
|
/\b(?:many|most)\s+(?:experts|people|customers|professionals|homeowners)\s+(?:agree|say|believe)\b/i,
|
|
@@ -973,8 +985,10 @@ function inlineLabelHits(ctx) {
|
|
|
973
985
|
run = [];
|
|
974
986
|
};
|
|
975
987
|
for (const line of lines) {
|
|
976
|
-
const m = /^\s*
|
|
977
|
-
|
|
988
|
+
const m = /^\s*[-*+]\s+\*\*([^*]{1,40}?):?\*\*:?\s+(\S.*)$/.exec(line);
|
|
989
|
+
const data = m && /[\d£$€%`]|https?:|^\*\*/.test(m[2]);
|
|
990
|
+
const named = m && /\S[A-Z]|[./]/.test(m[1]);
|
|
991
|
+
if (m && !data && !named && m[2].trim().split(/\s+/).length <= 6) run.push(offset);
|
|
978
992
|
else flush();
|
|
979
993
|
offset += line.length + 1;
|
|
980
994
|
}
|
|
@@ -1012,6 +1026,29 @@ var RESEARCH_TELLS = [
|
|
|
1012
1026
|
]
|
|
1013
1027
|
}
|
|
1014
1028
|
},
|
|
1029
|
+
{
|
|
1030
|
+
id: "prompt-context",
|
|
1031
|
+
name: "Prompt context",
|
|
1032
|
+
generation: 1,
|
|
1033
|
+
severity: "warn",
|
|
1034
|
+
surface: "copy",
|
|
1035
|
+
why: "'This article uses the attached Perplexity research file as the primary source', 'the attached research is right to say': the model talking about what it was given. A reader of a web page has no attachment. Found 38 times across five live DD articles in 2026-09, none caught by any other tell.",
|
|
1036
|
+
fix: "Delete the reference to the research or brief. Where it carried a claim, name the real source or cut the claim.",
|
|
1037
|
+
detect: phrases(PROMPT_CONTEXT, quoted),
|
|
1038
|
+
fixtures: {
|
|
1039
|
+
flag: [
|
|
1040
|
+
f("content/guide.md", "This article uses the attached Perplexity research file as the primary source."),
|
|
1041
|
+
f("content/guide.md", "The attached research is right to say these platforms suit early-stage businesses."),
|
|
1042
|
+
f("content/guide.md", "That is exactly how the uploaded research frames the category."),
|
|
1043
|
+
f("content/guide.md", "The attached brief asks for the point that most tradespeople miss.")
|
|
1044
|
+
],
|
|
1045
|
+
pass: [
|
|
1046
|
+
f("content/guide.md", "We research every quote before we send it."),
|
|
1047
|
+
f("app/email.tsx", `export const Invoice = () => <p>Your invoice is attached.</p>;`),
|
|
1048
|
+
f("content/guide.md", "BrightLocal's research shows that reviews drive 20% of local ranking.")
|
|
1049
|
+
]
|
|
1050
|
+
}
|
|
1051
|
+
},
|
|
1015
1052
|
{
|
|
1016
1053
|
id: "placeholder",
|
|
1017
1054
|
name: "Unfilled placeholder",
|
|
@@ -1064,7 +1101,11 @@ var RESEARCH_TELLS = [
|
|
|
1064
1101
|
pass: [
|
|
1065
1102
|
f("content/home.md", "- Pages load in under a second\n- Card details never touch our server\n- Support by phone until 8pm\n"),
|
|
1066
1103
|
f("content/home.md", "- **Speed:** Faster pages\n- **Security:** Safer data\n"),
|
|
1067
|
-
f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n")
|
|
1104
|
+
f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n"),
|
|
1105
|
+
f("content/guide.md", "- **Per page**: \xA3100-\xA3200 per page\n- **Hourly**: \xA350-\xA375/hour\n- **Day rate**: \xA3350-\xA3500\n"),
|
|
1106
|
+
f("content/guide.md", "- **Law Society Find a Solicitor:** For solicitors\n- **AccountingWEB / ICAEW directories:** For accountants\n- **NHS.uk:** For NHS services\n"),
|
|
1107
|
+
f("content/guide.md", "1. **Visit PageSpeed Insights** and paste the address\n2. **Enter their URL** and run it\n3. **Note their scores** for later\n"),
|
|
1108
|
+
f("content/guide.md", "- **Structural problems:** **REBUILD.**\n- **Messaging issues:** **REFRESH.**\n- **Neither:** **LEAVE IT.**\n")
|
|
1068
1109
|
]
|
|
1069
1110
|
}
|
|
1070
1111
|
},
|
|
@@ -1076,7 +1117,7 @@ var RESEARCH_TELLS = [
|
|
|
1076
1117
|
surface: "copy",
|
|
1077
1118
|
why: "'..., ensuring peace of mind', '..., highlighting our commitment': a participle tacked on the end that claims significance and names no mechanism. One of the most common shapes in the Wikipedia guide to AI writing.",
|
|
1078
1119
|
fix: "Cut the tail, or replace it with the mechanism: 'so you can drop the car off before work'.",
|
|
1079
|
-
detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, ""))),
|
|
1120
|
+
detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, "")), void 0, gerundList),
|
|
1080
1121
|
fixtures: {
|
|
1081
1122
|
flag: [
|
|
1082
1123
|
f("content/home.md", "Every engineer is Gas Safe registered, ensuring complete peace of mind."),
|
|
@@ -1084,7 +1125,8 @@ var RESEARCH_TELLS = [
|
|
|
1084
1125
|
],
|
|
1085
1126
|
pass: [
|
|
1086
1127
|
f("content/home.md", "We open at 7am, so you can drop the car off before work."),
|
|
1087
|
-
f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers.")
|
|
1128
|
+
f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers."),
|
|
1129
|
+
f("content/guide.md", "Many items (claiming your GBP, ensuring NAP consistency, writing title tags) are straightforward.")
|
|
1088
1130
|
]
|
|
1089
1131
|
}
|
|
1090
1132
|
},
|
|
@@ -1334,6 +1376,7 @@ function repeatHits(ctx) {
|
|
|
1334
1376
|
for (const s of sentences(doc2)) {
|
|
1335
1377
|
const words = normal(s.text);
|
|
1336
1378
|
if (words.length < RUN || !/[.!?]["'’”)]*$/.test(s.text)) continue;
|
|
1379
|
+
if (/^\s*\[\^[\w-]+\]:/.test(s.text)) continue;
|
|
1337
1380
|
const key = words.join(" ");
|
|
1338
1381
|
const earlier = whole.get(key);
|
|
1339
1382
|
if (earlier) {
|
|
@@ -1531,7 +1574,15 @@ var DENSITY_TELLS = [
|
|
|
1531
1574
|
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nMore here.\n\nWe check every valve before the engineer leaves the house.")),
|
|
1532
1575
|
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nBy then we check every valve before the engineer leaves the site."))
|
|
1533
1576
|
],
|
|
1534
|
-
pass: [
|
|
1577
|
+
pass: [
|
|
1578
|
+
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nThe engineer leaves once the customer has signed the sheet.")),
|
|
1579
|
+
f2(
|
|
1580
|
+
"content/guide.md",
|
|
1581
|
+
doc(
|
|
1582
|
+
"The Independent Healthcare Providers Network Private Patient Insight Report 2024 found that private demand rose.[^1]\n\n[^1]: Independent Healthcare Providers Network, Private Patient Insight Report 2024, published online."
|
|
1583
|
+
)
|
|
1584
|
+
)
|
|
1585
|
+
]
|
|
1535
1586
|
}
|
|
1536
1587
|
},
|
|
1537
1588
|
{
|
|
@@ -2644,6 +2695,9 @@ var HOUSE = {
|
|
|
2644
2695
|
"closing-summary": { tier: "review", label: "closing summary" },
|
|
2645
2696
|
"false-range": { tier: "review", label: "false range" },
|
|
2646
2697
|
"placeholder": { tier: "review", label: "unfilled placeholder" },
|
|
2698
|
+
// Every estate hit so far is real (38 in five DD articles, 2026-09-24), but
|
|
2699
|
+
// a proposal can honestly say "the attached research". Review until measured there.
|
|
2700
|
+
"prompt-context": { tier: "review", label: "prompt context: the model naming its own inputs" },
|
|
2647
2701
|
"question-reveal": { tier: "review", label: "staged reveal" },
|
|
2648
2702
|
"inline-label-list": { tier: "review", label: "bold-label bullets" },
|
|
2649
2703
|
"phrase-density": { tier: "review", label: "density" },
|
|
@@ -2992,7 +3046,7 @@ var RENDERED_PATHS = {
|
|
|
2992
3046
|
};
|
|
2993
3047
|
|
|
2994
3048
|
// src/character/check.ts
|
|
2995
|
-
var CATALOGUE_VERSION = "2026.09.
|
|
3049
|
+
var CATALOGUE_VERSION = "2026.09.8";
|
|
2996
3050
|
var CATALOGUE = [...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map(
|
|
2997
3051
|
(t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t
|
|
2998
3052
|
);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseFlags
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JKIDWHG3.js";
|
|
4
4
|
import {
|
|
5
5
|
fingerprint,
|
|
6
6
|
fingerprintDistance
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-QMYERCBB.js";
|
|
8
8
|
import {
|
|
9
9
|
CATALOGUE_VERSION,
|
|
10
10
|
REFLEX_FONTS_1,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
isCream,
|
|
19
19
|
oklchToRgb,
|
|
20
20
|
scanSource
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-3NGWXZZE.js";
|
|
22
22
|
|
|
23
23
|
// src/null/cli.ts
|
|
24
24
|
import { spawn } from "child_process";
|
|
@@ -352,7 +352,7 @@ async function build(flags, io) {
|
|
|
352
352
|
const toShoot = ids.filter((id) => existsSync(join(pages, `${id}.html`)) && !existsSync(join(shots, `${id}.json`)));
|
|
353
353
|
if (toShoot.length) {
|
|
354
354
|
io.out(`craft null: snapshotting ${toShoot.length} page${toShoot.length === 1 ? "" : "s"}`);
|
|
355
|
-
const { snapshotUrls } = await import("./audit-
|
|
355
|
+
const { snapshotUrls } = await import("./audit-EGDNWSQB.js");
|
|
356
356
|
const results = await snapshotUrls(toShoot.map((id) => pathToFileURL(join(pages, `${id}.html`)).href));
|
|
357
357
|
results.forEach((r, i) => {
|
|
358
358
|
const id = toShoot[i];
|