@generativereality/content-studio 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +11 -0
- package/LICENSE +21 -0
- package/README.md +52 -2
- package/package.json +23 -4
- package/skills/content-studio/SKILL.md +150 -0
- package/skills/content-studio/references/audio.md +159 -0
- package/skills/content-studio/references/brief.md +66 -0
- package/skills/content-studio/references/channels.md +144 -0
- package/skills/content-studio/references/copy.md +201 -0
- package/skills/content-studio/references/costs-and-licences.md +75 -0
- package/skills/content-studio/references/generation.md +192 -0
- package/skills/content-studio/references/real-people.md +111 -0
- package/skills/content-studio/references/review.md +80 -0
- package/skills/content-studio/references/search.md +135 -0
- package/skills/content-studio/references/stills.md +112 -0
- package/skills/content-studio/references/tools.md +26 -0
- package/skills/content-studio/references/video.md +155 -0
- package/skills/content-studio/references/voice.md +115 -0
- package/skills/content-studio/scripts/render-frames.sh +30 -0
- package/skills/content-studio/scripts/spend.mjs +44 -0
- package/skills/content-studio/scripts/voice-sweep.sh +31 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# The pick: showing work so a person can choose
|
|
2
|
+
|
|
3
|
+
The pick is where most studio work has failed: not because the work was bad, but because the
|
|
4
|
+
person could not choose between options. They couldn't open the page where they were, they couldn't
|
|
5
|
+
tell the samples apart, or they were asked to assemble a combination. A review page is part of the
|
|
6
|
+
product.
|
|
7
|
+
|
|
8
|
+
## One best, then a few real alternatives
|
|
9
|
+
|
|
10
|
+
- **Lead with your recommendation, finished.** The first thing on the page is the version you would
|
|
11
|
+
post, complete (title, body, image, first comment, everything), ready to approve as it is.
|
|
12
|
+
- **Then two or three alternatives**, each one also complete, and each different in **angle or
|
|
13
|
+
format**, not in wording. One line under each says how it differs and when it would be the better
|
|
14
|
+
choice.
|
|
15
|
+
- **Never a parts menu.** A page of 19 titles, 5 bodies and 4 images asked one person to compose about
|
|
16
|
+
380 combinations. They answered by asking whether it was a bug. If the titles matter, show the best
|
|
17
|
+
title on the recommended version and list three alternative titles beneath it, no more.
|
|
18
|
+
- **Keep the variations, out of the way.** All the hooks and angles you tried can go at the bottom
|
|
19
|
+
under a plain heading, for anyone who wants to look. The decision must not depend on reading them.
|
|
20
|
+
|
|
21
|
+
## Make it reviewable where the person is
|
|
22
|
+
|
|
23
|
+
- **It opens on a phone.** Use the review mechanism the project has (a card with a review page, a
|
|
24
|
+
shared document, an artifact link). A local file path opens nowhere but on the machine that has it.
|
|
25
|
+
Put the full clickable link in the message.
|
|
26
|
+
- **Size:** each image under about 1.5 MB, and the whole page under about 6 MB, or phones drop it. Use
|
|
27
|
+
stills and short clips. Never embed a full-resolution master.
|
|
28
|
+
- **Label every sample** with what it is, the tool or model that made it, what it cost and how long it
|
|
29
|
+
took. Unlabelled samples cannot be judged: a reviewer who couldn't tell which voice was which in a
|
|
30
|
+
concatenated sampler could review none of them.
|
|
31
|
+
- **Side by side** for anything compared (two cuts, two thumbnails, before and after). **One player per
|
|
32
|
+
voice or clip**, never one long file with all the takes.
|
|
33
|
+
- **Show it as it will be seen:** a feed image at feed size, a vertical clip in a phone frame, a title
|
|
34
|
+
as it will look in the feed, with the first line of the body under it.
|
|
35
|
+
- **Numbered lists render as lists**, and verdicts and notes stay separate from the work itself.
|
|
36
|
+
|
|
37
|
+
## Reviewing generated media: board, shoot, review
|
|
38
|
+
|
|
39
|
+
- **Board before shoot.** No paid clip before the board is approved (`generation.md`).
|
|
40
|
+
- **Re-render the review after every shoot**, including a small one, before reporting. A take nobody
|
|
41
|
+
has watched isn't a deliverable.
|
|
42
|
+
- **One review doc for the production, not one per round.** Retire superseded material out of it and
|
|
43
|
+
leave a short ledger of what went and why, so nothing is re-shot by accident; with two docs, the
|
|
44
|
+
person reads whichever they opened last.
|
|
45
|
+
- **Say what is wrong with each take in the doc, beside it.** The faults found are the review's output;
|
|
46
|
+
the clips are the evidence.
|
|
47
|
+
- **Import the prompts from the generation script; never retype them.** Guard the script's run loop
|
|
48
|
+
behind `main()` so the review builder can import its job table. A doc that claimed to extract its
|
|
49
|
+
prompts while carrying a hand-pasted copy drifted.
|
|
50
|
+
- Keep the markdown body as the source and let a build step embed the clips. Stage web-sized copies
|
|
51
|
+
somewhere persistent beside the takes, never `/tmp` (one staging folder was wiped mid-project).
|
|
52
|
+
Transcode small first (about 854 px wide, CRF 27).
|
|
53
|
+
- With a markdown renderer, wrap a `<video>` in a `<figure>` on **one line**: `<video>` isn't a CommonMark
|
|
54
|
+
block tag and `<figure>` is, and a newline inside the figure leaks the rest as escaped text.
|
|
55
|
+
- With `review-md` (`tools.md`): media needs `--allow-html` and `media-src 'self' file:` in the CSP, or
|
|
56
|
+
the players look normal and refuse every clip. Confirm in the tab by loading each element
|
|
57
|
+
(`x.load()`, then `x.error`), not by the player being visible. Heading ids carry no section number, so
|
|
58
|
+
list them before linking (`[...document.querySelectorAll("h2")].map(h=>h.id)`), and add `?v=N` to
|
|
59
|
+
reload a re-rendered file the browser has cached.
|
|
60
|
+
- **Verify the render, not its exit code:** count the missing-clip markers the builder writes (it
|
|
61
|
+
should write a visible one, never a gap), count `<video>` tags, grep for `<figure` leaks, then open
|
|
62
|
+
it. **Prove an embed is current by hashing it,** not by mtime and not by opening a heavy page (evals
|
|
63
|
+
timed out on a 13 MB render): decode every `data:` payload, hash it, and match it to the current files;
|
|
64
|
+
one that matches nothing is stale. When the change under review is to the sound or grade, also measure
|
|
65
|
+
the embedded clip, because a current file can still be the cut before the fix.
|
|
66
|
+
|
|
67
|
+
## The question
|
|
68
|
+
|
|
69
|
+
One question, one line: *"Post the recommended version?"*. Offer up to three one-tap answers that are
|
|
70
|
+
real answers (*"Post it as is"*, *"Use alternative B"*, *"Shorter"*). Always leave a free answer, and
|
|
71
|
+
always a way to say it is wrong, which is not one of the buttons.
|
|
72
|
+
|
|
73
|
+
## After the answer
|
|
74
|
+
|
|
75
|
+
- Write the pick or the no, with the person's reason in their own words, into the lessons file.
|
|
76
|
+
- A reason like "sounds like AI" or "not good enough" is new material, not a request to polish: ask
|
|
77
|
+
what is off and what they would say instead (`voice.md` §critique).
|
|
78
|
+
- A preference is one person's taste, and a fault they saw or heard (choppy motion, a wrong accent, a
|
|
79
|
+
face that changed) rules a tool out for everyone. Record which kind it was.
|
|
80
|
+
- If they pick, make that version well. Don't ask again about what they have already decided.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Search: pages that rank and are worth landing on
|
|
2
|
+
|
|
3
|
+
A page meant to be found has two jobs, and it fails if it only does one. It has to answer the query
|
|
4
|
+
well enough that a search engine or an AI assistant puts it in front of someone, and it has to be
|
|
5
|
+
worth that person's time once they arrive. Both jobs fail the same way: writing what we want to say
|
|
6
|
+
about our product, in our own words for it, on a query nobody types.
|
|
7
|
+
|
|
8
|
+
## 1. The query is measured, never guessed
|
|
9
|
+
|
|
10
|
+
Choose the target query from measured volume (Google Keyword Planner, Search Console, Bing Webmaster
|
|
11
|
+
Tools), and write the measurement down with its date the same day, in a numbered file in the
|
|
12
|
+
project's research folder, because scratch files vanish. Keyword Planner without ad spend gives buckets
|
|
13
|
+
(10–100, 100–1K, 1K–10K, 10K–100K, 100K–1M): precise enough to choose with, too coarse to forecast with.
|
|
14
|
+
What has changed decisions:
|
|
15
|
+
|
|
16
|
+
- **People search the job, not the container.** An AI-prefixed phrasing ("ai expense tracker app")
|
|
17
|
+
has measured two to four volume buckets below its plain twin ("expense tracker app"), and in other
|
|
18
|
+
languages the AI-prefixed form often returns nothing. The AI belongs in the answer, not in the title.
|
|
19
|
+
- **People search the label and the product, not the idea.** A bank-statement label or a product name
|
|
20
|
+
carries thousands a month; an idea like "private budgeting" carries almost nothing. A page about an
|
|
21
|
+
idea needs a product or label word in its title.
|
|
22
|
+
- **A brand nobody searches can't carry a comparison page.** Check the brand's own volume first.
|
|
23
|
+
- **Volume isn't intent.** Read the results before choosing: a query whose top ten are all about fees
|
|
24
|
+
can't be won by a page about something else with the same words.
|
|
25
|
+
|
|
26
|
+
## 2. Read the results page before writing
|
|
27
|
+
|
|
28
|
+
Open the query in a real browser and write down what is there:
|
|
29
|
+
|
|
30
|
+
- **Who owns the top three, and whether any small independent site ranks at all.** That second point
|
|
31
|
+
is the winnability test, and it is usually kinder than it looks.
|
|
32
|
+
- **What takes the click before the results.** Where an AI overview answers completely, a page that
|
|
33
|
+
only summarises loses, but **being cited is a way in that ranking isn't**: overviews often cite
|
|
34
|
+
small sites. Note the narrowing question the overview asks at the end; each split it offers is a
|
|
35
|
+
page.
|
|
36
|
+
- **People Also Ask, word for word.** They are the best outline for the page's sections. Put FAQ
|
|
37
|
+
schema only on questions the page visibly answers.
|
|
38
|
+
- **Freshness.** Results dated this week mean a maintained query; undated results mean an open one.
|
|
39
|
+
- **Intent.** "Personal expense tracker" turned out to be a spreadsheet-template query.
|
|
40
|
+
- **What the ranking pages do well:** a method section with numbers in it, a spec block with
|
|
41
|
+
identical fields per item, jump links, an FAQ, a visible date checked, named experts where real.
|
|
42
|
+
The best openings in one read of thirteen ranking pages were first-person failure stories, usable
|
|
43
|
+
only when the failure is real.
|
|
44
|
+
- **What they all leave out.** The gaps are where the page comes from.
|
|
45
|
+
|
|
46
|
+
## 3. The bar: what does this page have that nobody else's does?
|
|
47
|
+
|
|
48
|
+
A page about a job **teaches the job in full**: the job in the heading, what makes it annoying, how to
|
|
49
|
+
do it yourself step by step, the named operators people search for in the detail actually verified,
|
|
50
|
+
then the offer to let the product do it. The call to action is gated; the page isn't. Someone who never
|
|
51
|
+
downloads anything should be able to finish the job with the page alone, and that is what makes it
|
|
52
|
+
worth ranking.
|
|
53
|
+
|
|
54
|
+
**Before writing, name the one thing on this page that neither the ranking pages nor the company's
|
|
55
|
+
own help pages carry.** If you can't name it, don't write the page yet. Sourcing every sentence from
|
|
56
|
+
the operator's own help pages is a reliable way to make a page that adds nothing to those pages. Kinds
|
|
57
|
+
of novelty that have worked:
|
|
58
|
+
|
|
59
|
+
- **A real run:** we did the job and wrote down what happened, including what went wrong.
|
|
60
|
+
- **A synthesis nobody has done:** the same questions asked of every option, in one table.
|
|
61
|
+
- **What the company doesn't say**, found by reading its pages against each other.
|
|
62
|
+
- **The consequence people act against:** a regulator's sentence that changes what to do.
|
|
63
|
+
- **Something made from the reader's own kind of data:** a real household's numbers (anonymised,
|
|
64
|
+
presented as "one household", never identifying anyone).
|
|
65
|
+
|
|
66
|
+
A real run on people's own data is presented as **one household's, never as "ours"**, with every
|
|
67
|
+
identifying detail left out (whose profile, children, names, places). A run on the author's own account
|
|
68
|
+
told in the first person is different, because it concerns nobody else. A paraphrase of the company's
|
|
69
|
+
help pages may sit on the page as the reference half, short and saying whose rules they are, after the
|
|
70
|
+
novel part and never as the reason the page exists.
|
|
71
|
+
|
|
72
|
+
Three tests before publishing:
|
|
73
|
+
1. **Delete the product paragraph.** Is the page still worth reading?
|
|
74
|
+
2. **Can someone actually follow it?** Real steps with the real friction: which button, which export,
|
|
75
|
+
what goes wrong the first time.
|
|
76
|
+
3. **Does it answer the awkward question too**, honestly, even when the honest answer sends the reader
|
|
77
|
+
elsewhere?
|
|
78
|
+
|
|
79
|
+
## 4. Facts from the primary page, quoted and dated
|
|
80
|
+
|
|
81
|
+
- The vendor's, operator's or regulator's own page is the only acceptable source; directories and
|
|
82
|
+
review sites are often wrong or stale (three of sixteen listings checked in one directory were).
|
|
83
|
+
- Quote the sentence and keep its address and the date read beside the draft; a footnote lets a
|
|
84
|
+
reader check it.
|
|
85
|
+
- "Not stated on their site" is a finding, and isn't the same as "no".
|
|
86
|
+
- **Never invent a number for illustration.** If it wasn't measured, write the sentence without it.
|
|
87
|
+
- A page whose facts age shows the date they were checked, where the reader sees it.
|
|
88
|
+
|
|
89
|
+
## 5. The opening is for the reader
|
|
90
|
+
|
|
91
|
+
The first lines say who the page is for and answer the question, then date the facts. It is not a
|
|
92
|
+
note about our research. Headings are plain and say the thing.
|
|
93
|
+
|
|
94
|
+
## 6. Comparison pages
|
|
95
|
+
|
|
96
|
+
A title like *"X or Y?"* tells the reader they are choosing. When the two don't do the same job, say
|
|
97
|
+
so: frame it as "who does what" (tables like "a month with both", not "a month with each"), with no row
|
|
98
|
+
leaning our way and at least two rows where we answer "no". Record per comparison whether the other
|
|
99
|
+
product competes, and let a test pin the page's framing to it. A comparison with no real difference in
|
|
100
|
+
it is a pitch with a new heading.
|
|
101
|
+
|
|
102
|
+
## 7. Writing for AI assistants as well
|
|
103
|
+
|
|
104
|
+
- In a study of generative engines (GEO, KDD 2024), adding quotations, statistics and cited sources
|
|
105
|
+
raised a page's visibility in AI answers the most, roughly 30 to 40%. An authoritative tone didn't
|
|
106
|
+
help, and keyword stuffing did worse than nothing. Answer the question in the first two sentences;
|
|
107
|
+
write self-contained, citable claims, each number with its source.
|
|
108
|
+
- Assistants search in long specific queries (about 12 words on average for one of them, 2026) and lean
|
|
109
|
+
on *best*, *review*, *vs*, *alternative* and the year. A title should contain the words such a query
|
|
110
|
+
would: on an "X alternative" query they cited small pages with *alternative* in the title and missed
|
|
111
|
+
pages titled "X or Y?".
|
|
112
|
+
- Answer each sub-question in its own paragraph or table row, because assistants lift the piece that
|
|
113
|
+
answers their part.
|
|
114
|
+
- Carry a `dateModified` (Article schema) from the date the facts were checked.
|
|
115
|
+
- Where assistants look matters more than markup: ChatGPT search, Copilot and DuckDuckGo use Bing's
|
|
116
|
+
index; one major assistant most likely uses Brave's, which has no submission console and finds pages
|
|
117
|
+
through links from sites it already indexes (forums, code hosts, directories); Perplexity has its own.
|
|
118
|
+
Ping Bing (IndexNow) after every release that adds or changes pages; a 403 on the first run after a new
|
|
119
|
+
key is key validation, so run it again. `llms.txt` exists but no major assistant has committed to
|
|
120
|
+
reading it.
|
|
121
|
+
- **Describe the product exactly**, because an assistant repeats it word for word. Never claim more
|
|
122
|
+
privacy or capability than is true.
|
|
123
|
+
- Search engines accept AI-assisted pages that add value and treat mass pages without it as spam.
|
|
124
|
+
|
|
125
|
+
## 8. After it is live
|
|
126
|
+
|
|
127
|
+
Check the page serves (grep for a phrase only the new build has), is in the sitemap and isn't
|
|
128
|
+
blocked by robots.txt. A page waiting for approval must not ride along on someone else's release, so
|
|
129
|
+
keep it on its own branch. Search Console and Bing Webmaster Tools are the feedback, not opinion:
|
|
130
|
+
impressions with no clicks means a title or answer problem; neither means not indexed or not
|
|
131
|
+
competitive. Search Console data lags about four days and covers Google only. Bing Webmaster Tools'
|
|
132
|
+
AI Performance report shows Copilot citations and the grounding queries behind them, the nearest thing
|
|
133
|
+
to a list of what assistants search. Which crawlers actually read a page is in the CDN's request logs
|
|
134
|
+
by user agent (OAI-SearchBot and ChatGPT-User, ClaudeBot, PerplexityBot, DuckAssistBot). Expect nothing
|
|
135
|
+
for weeks on a young domain: the first pages are an investment in a corpus, not a test of one page.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Stills: thumbnails, covers, carousels, lead images, collages
|
|
2
|
+
|
|
3
|
+
**Code sets the words, AI makes pictures and decoration, and no generative model touches a real photo
|
|
4
|
+
it isn't explicitly restoring.** That rule decides most of this file. Every model asked to "arrange" or
|
|
5
|
+
"restore" photos redrew faces a little. Every current model spells a headline correctly, so the
|
|
6
|
+
reason to set type in code isn't spelling: one word can change without re-rolling the picture, and
|
|
7
|
+
the brand stays exact.
|
|
8
|
+
|
|
9
|
+
## The idea comes from the subject
|
|
10
|
+
|
|
11
|
+
Seven image models answered one thumbnail brief with the same layout (condensed headline left, phone
|
|
12
|
+
in hand right). The only thumbnail that looked designed was built from the product's own output: a
|
|
13
|
+
transcript with its filler words struck out. **The sameness is the slop.** Start from something real
|
|
14
|
+
and specific to the subject: its own output, its own screen, a real photo, a real number. Write
|
|
15
|
+
the idea down before opening any tool.
|
|
16
|
+
|
|
17
|
+
## Recipe A: layout in code (the default)
|
|
18
|
+
|
|
19
|
+
1. One `brand.css` with tokens and fonts. Bundle OFL fonts locally, with no font CDN at render. **Never
|
|
20
|
+
name a font family `Serif` or `Sans`**: unquoted, those are CSS generic keywords and Chrome silently
|
|
21
|
+
draws Times. Give custom families unique names.
|
|
22
|
+
2. One HTML file per frame, sized exactly to the output.
|
|
23
|
+
3. Render with headless Chrome: `scripts/render-frames.sh <out-dir> <w> <h> <frame.html>…`. It uses the
|
|
24
|
+
installed Chrome or Chromium, at 0.1 to 1 s a frame, and runs the same on Windows.
|
|
25
|
+
4. Gate: contrast and anti-slop (`npx impeccable detect <folder>` caught a real 3.2:1 grey), then
|
|
26
|
+
**open every PNG and look**. Faults found only by looking: a title on a busy patch, a note behind a
|
|
27
|
+
print, a cropped footer, a caption box over the screenshot, an accent dot vanishing on an
|
|
28
|
+
accent-coloured slide.
|
|
29
|
+
5. Measure margins in code rather than by eye; uneven ones (71/61/61/52 px) read as sloppy.
|
|
30
|
+
|
|
31
|
+
Without a browser (a server, a Worker), use Satori with resvg (MPL-2.0, fine to bundle). It needs static
|
|
32
|
+
TTF fonts, an explicit width on every text box (or text won't wrap), and footers pinned with absolute
|
|
33
|
+
positioning.
|
|
34
|
+
|
|
35
|
+
## Recipe B: a generated background under typeset text
|
|
36
|
+
|
|
37
|
+
Ask the model for the picture only, and end the prompt with *"No printed matter, no writing, no labels,
|
|
38
|
+
no logos"*. Local models write gibberish on any paper or sign in the scene without it. Then use the
|
|
39
|
+
image as the CSS background in recipe A. See `generation.md` for models.
|
|
40
|
+
|
|
41
|
+
## Carousels
|
|
42
|
+
|
|
43
|
+
- Slide 1 carries the hook; each later slide is one item with its proof; the last slide is the next
|
|
44
|
+
step. A list that overflows the screen gets saved.
|
|
45
|
+
- **Model-drawn carousels:** make slide 1, then pass it as the reference image for slides 2 to 5 with
|
|
46
|
+
*"use the attached slide 1 as the exact design system"*, in parallel. Ask for 2K, because 1K returns
|
|
47
|
+
928 × 1152, below Instagram's 1080 × 1350. One run of five cost $0.17 and took about 30 s (Nov 2026).
|
|
48
|
+
The output is a flat bitmap, so a typo fix is a re-roll and the re-roll will differ. Prefer recipe A
|
|
49
|
+
for anything with text that may change.
|
|
50
|
+
|
|
51
|
+
## Lead images of a real product
|
|
52
|
+
|
|
53
|
+
Use a real screenshot or a real recording frame, framed and captioned in code. Never mock up a screen,
|
|
54
|
+
and never let a model "improve" one. If the screenshot shows a stale or wrong state (another brand's
|
|
55
|
+
name in the header, a debug banner), get a new one; don't paint over it. Blur real people's names
|
|
56
|
+
in it. Test the image at the size the feed shows it (often about 400 px wide on a phone).
|
|
57
|
+
|
|
58
|
+
## Collage (the "paper reel")
|
|
59
|
+
|
|
60
|
+
A layout of 3 to 8 pictures on a generated paper, as a still and as a 5 to 10 s film. People find it
|
|
61
|
+
very shareable.
|
|
62
|
+
1. **Paper,** one per output size. Prompt: *"decorations only along the outer 12% of the frame; the
|
|
63
|
+
whole centre plain, evenly lit; top-down; no photos, screens, text, people or hands"*. A hosted model
|
|
64
|
+
at 2K costs about $0.05 and takes about 30 s; a local one is free but slower, with a less clean
|
|
65
|
+
centre (put the title on a card). **The paper's mood is the choice**, and the layout follows it.
|
|
66
|
+
2. **Spec:** theme (notebook, desk, cork, scrapbook, album, journal), layout (scatter for many photos,
|
|
67
|
+
grid for calm, hero with satellites, timeline for dates), motion (drop, fade or slide in; camera
|
|
68
|
+
drift, push or pan).
|
|
69
|
+
3. **Layout in code:** take a decoration mask from the paper; detect faces (YuNet, MIT) so no crop cuts
|
|
70
|
+
one; seed and score placements against decoration and overlap; put notes where they cover nothing
|
|
71
|
+
and the title in the least decorated spot. Make the still from the end state, not the last frame
|
|
72
|
+
of a camera move. Step the film frame by frame into ffmpeg.
|
|
73
|
+
4. **Accept only if** no face is cut, the title and prints overlap decoration by less than about 15%,
|
|
74
|
+
you have looked at the still, and the film decodes.
|
|
75
|
+
5. A person's own photos are never altered and never sent to a hosted model without their yes. Make
|
|
76
|
+
the paper locally or reuse one.
|
|
77
|
+
|
|
78
|
+
Faults seen: a still taken from a zoomed last frame crops the margins; strings or arrows drawn across
|
|
79
|
+
interface text make it unreadable; the hero print got buried under the others until grid cells fixed
|
|
80
|
+
it. Feeding all the photos into an image model to "arrange" them redrew every face.
|
|
81
|
+
|
|
82
|
+
## Thumbnails
|
|
83
|
+
|
|
84
|
+
Note the conventions of the outliers in the niche (colour, amount of text, face or no face,
|
|
85
|
+
composition) and decide on purpose. The title and the thumbnail are one promise, so don't repeat the
|
|
86
|
+
title in the image. Text readable at phone size. Make two or three that differ in idea, not in colour,
|
|
87
|
+
and let YouTube's Test & Compare decide, not taste.
|
|
88
|
+
|
|
89
|
+
## Covers
|
|
90
|
+
|
|
91
|
+
A cover makes someone stop and feel the claim. An illustration that carries the argument beats a data
|
|
92
|
+
chart; charts belong in the body as evidence. For a personal album or keepsake, the person's untouched
|
|
93
|
+
photo with a well-set title is usually the one to use. A painted version made from it is a separate,
|
|
94
|
+
labelled option.
|
|
95
|
+
|
|
96
|
+
## Restoring an old photo (a person's own, at their request)
|
|
97
|
+
|
|
98
|
+
- **Most faithful:** a top hosted editor with *"Do not change any face, expression or pose"* (Nano
|
|
99
|
+
Banana Pro, about $0.15 at 2K, Nov 2026). **Best value:** Nano Banana 2.1 at 2K, about $0.05 (it slightly
|
|
100
|
+
enlarged a child's eyes). **Best free:** a local FLUX 3 Klein edit, then Real-ESRGAN.
|
|
101
|
+
- **Always compare original and result side by side at the faces** before accepting.
|
|
102
|
+
- **Upscale only after cleaning:** Real-ESRGAN on a raw scan sharpens the scratches as detail.
|
|
103
|
+
- **Colourising invents facts** (eye and hair colour). Offer it as a separate, labelled version, never in
|
|
104
|
+
place of the original.
|
|
105
|
+
- Don't use: GPT Image (reframed the photo and redrew two new children), Kontext's restore app
|
|
106
|
+
(colourised unasked, with modern faces), GFPGAN and CodeFormer (kept every scratch and put blue eyes
|
|
107
|
+
into a black-and-white photo; CodeFormer is also non-commercial), OpenCV inpainting (faces to mush).
|
|
108
|
+
|
|
109
|
+
## Fixing a bad generated detail
|
|
110
|
+
|
|
111
|
+
Remove the object rather than asking for a repair. A backwards hand was fixed by *"lying on the
|
|
112
|
+
surface, no hands"*. Asking a model to repair the hand kept it backwards and re-laid out the whole image.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Tool skills this one routes to, and where it overrides them
|
|
2
|
+
|
|
3
|
+
content-studio holds the process and the craft. For a tool's own mechanics, read that tool's skill
|
|
4
|
+
when it is installed. Where a tool skill's defaults conflict with the rules here, **this skill wins**,
|
|
5
|
+
because its rules protect the person (cost, consent, nothing going out). Those conflicts are listed
|
|
6
|
+
below.
|
|
7
|
+
|
|
8
|
+
| Tool skill | Use it for | Overrides from content-studio |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| `hyperframes` (+ `hyperframes-cli`, `hyperframes-registry`) | HTML-to-video: the render contract, motion craft, captions, the registry of ready-made blocks (transitions, titles, charts, maps). Search the registry before hand-building any named visual. | Telemetry off (`npx hyperframes telemetry disable` or `HYPERFRAMES_NO_TELEMETRY=1`); `init --skip-skills` so it doesn't write skills into the person's global folder; **never** its `publish`, `cloud`, `billing` or `feedback` commands, and no cloud render, without the person's yes; render only after the person picked (the pick in `review.md` replaces its own approval step) |
|
|
11
|
+
| `media-use` | sourcing media for a HyperFrames video: catalog music and images, logos (never redrawn), cutouts, colour treatments, transcripts | **Always show what a run cost** (it says never to); local and free first where quality is equal, not only when asked; no HeyGen-hosted generation of a real person |
|
|
12
|
+
| `review-md` | rendering a markdown review page to one HTML file, or an artifact | media pages need `--allow-html` and `media-src 'self' file:` in the CSP, or `<video>`/`<audio>` are silently refused; verify a media page by decoding each embedded file, because an automation browser may load no audio |
|
|
13
|
+
| `browser-automation` | reading a place's rules and outliers in the person's signed-in browser; preparing a post in its form for the person to send; screen-recording a web app | read-only unless preparing a draft; never press post, send or submit |
|
|
14
|
+
| A personal voice skill or voice notes (whatever the person keeps) | their voice, their banned words, their lessons | it overrides `voice.md` wherever they differ |
|
|
15
|
+
| A strategy or growth skill (positioning, channels, bets) | what to say, to whom, where and why now | content-studio never overrides its decisions; it raises doubts in the brief |
|
|
16
|
+
|
|
17
|
+
## Scripts in this skill
|
|
18
|
+
|
|
19
|
+
The `.sh` scripts need bash: on Windows, run them from Git Bash (which Claude Code on Windows uses).
|
|
20
|
+
`spend.mjs` needs only Node.
|
|
21
|
+
|
|
22
|
+
| Script | What it does |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `scripts/voice-sweep.sh <file>` | the mechanical machine-prose sweep: em dashes against paragraphs, short fragments, banned words, negative-parallel shapes, drumbeat lines. Exit 0 always; read the hits |
|
|
25
|
+
| `scripts/render-frames.sh <out-dir> <w> <h> <frame.html>…` | renders HTML frames to PNG with the installed Chrome/Chromium headless (macOS, Linux, Windows Git Bash) |
|
|
26
|
+
| `scripts/spend.mjs add|total` | the per-piece spend ledger (`spend.jsonl` in the piece's folder) |
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Video: demos, explainers, reels, launch clips, memory films
|
|
2
|
+
|
|
3
|
+
Video prefers pieces that combine real footage, stills and animation over any single one. A narrated
|
|
4
|
+
slideshow is the least exciting format there is, and to many people it reads like the phone's own
|
|
5
|
+
automatic "memories" feature.
|
|
6
|
+
|
|
7
|
+
## The pipeline
|
|
8
|
+
|
|
9
|
+
1. **Script and shots first,** as one timeline file (JSON): shots, text, captions, focus points,
|
|
10
|
+
music, end line. The agent's job is writing the timeline; any renderer can consume it.
|
|
11
|
+
2. **Voice (if any) and its transcript** for word timings (`audio.md`).
|
|
12
|
+
3. **Render:** for HTML-based motion, use **HyperFrames** (HTML + GSAP rendered to MP4 through Chrome
|
|
13
|
+
and ffmpeg, Apache-2.0); read its own skills for the render contract (`tools.md`). For a
|
|
14
|
+
zero-dependency route, use Pillow + ffmpeg. Remotion is the fastest renderer, but it is free only
|
|
15
|
+
for up to three employees and doesn't normalise loudness. The renderers look nearly identical on the
|
|
16
|
+
same timeline. HyperFrames' text came out slightly clearer.
|
|
17
|
+
4. **Check:** `hyperframes check` (it caught captions too short to read, an end card too short for its
|
|
18
|
+
credits, overlaps, type under 28 px and low contrast), a contact sheet of frames to look at, then
|
|
19
|
+
the encode checks below.
|
|
20
|
+
|
|
21
|
+
A timeline shape that has worked well for photo-and-clip films:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{"width":1080,"height":1920,"fps":30,"style":"calm|cut",
|
|
25
|
+
"shots":[
|
|
26
|
+
{"photo":"<file>","seconds":3.2,"motion":"push|pull|pan-left|pan-right|none","focus":[0.5,0.4],"caption":"…","transition":"cut|dissolve"},
|
|
27
|
+
{"clip":"<file>","from":1.2,"seconds":2.5,"sound":true,"caption":"…"},
|
|
28
|
+
{"card":{"title":"Day 2 · Saturday","lines":["…"]},"seconds":2.0}],
|
|
29
|
+
"music":"<file>","music_db":-10,"end_line":"…"}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Calm style pushes 1.00→1.09 with dissolves; cut style punches in 1.04→1.18 with hard cuts. A text that
|
|
33
|
+
needs longer than its shot's seconds is a warning to fix, not a render to ship.
|
|
34
|
+
|
|
35
|
+
## Cut craft
|
|
36
|
+
|
|
37
|
+
- **Open on the most specific moment,** never a title card.
|
|
38
|
+
- **A real person's recorded voice sets the order** (a memo, an interview, a founder talking): edit
|
|
39
|
+
to the order of the telling, not to the timeline, and use the word timestamps to put each shot on
|
|
40
|
+
the word that names it.
|
|
41
|
+
- **Cut on the words.** Hard cuts on the narrator's or speaker's words, using word timestamps.
|
|
42
|
+
- **Punch in** toward the face or the subject (1.04 to 1.18).
|
|
43
|
+
- **Real sound from real clips:** the loudest 2.5 s window of a clip, one to three times a film, ducked
|
|
44
|
+
under any voice. Never use a performance's music.
|
|
45
|
+
- **One label per run of shots** (a day and place on a dark pill). Per-shot labels stack up and become
|
|
46
|
+
unreadable in fast cuts.
|
|
47
|
+
- **Index and stats cards** (*"Day 2 · Saturday · harbour, market"*, counts computed from the data),
|
|
48
|
+
and an **itinerary map** where the route matters: the camera follows the route, solid where there
|
|
49
|
+
are pictures, dashed and captioned where not.
|
|
50
|
+
- **For what wasn't filmed:** an index card, a cut scene done properly in motion design, or generated
|
|
51
|
+
stills in one style. Crude drawn shapes look terrible.
|
|
52
|
+
- **End on a line that lands,** or a face, or a line addressed to whoever the film is for.
|
|
53
|
+
- **Lengths:** social 35 to 45 s, product 45 to 55 s, scrapbook 30 to 40 s, calm slideshow 50 to 70 s.
|
|
54
|
+
- **Offer different styles** (social, product, scrapbook, calm) from the same material, and say which
|
|
55
|
+
you'd pick and why. The person's favourite changes with the material.
|
|
56
|
+
|
|
57
|
+
## Showing a real product
|
|
58
|
+
|
|
59
|
+
Every frame is the real product. Record the real web app with Playwright and a Chrome DevTools
|
|
60
|
+
screencast (3× device pixel ratio, about 45 fps, recorded in real time), or record a native app with the
|
|
61
|
+
OS screen recorder, then compose the result with punch-ins and captions. Best: write the demo screen as
|
|
62
|
+
a page whose state is a pure `render(t)`, so the same page can be recorded live and seeked frame by
|
|
63
|
+
frame by the renderer. Caption pills must not cover the rows you are showing during a punch-in.
|
|
64
|
+
|
|
65
|
+
Recording a native app on macOS (four takes once captured the wrong thing without one error):
|
|
66
|
+
- **Use `screencapture -v`, not ffmpeg `-f avfoundation`.** avfoundation records whoever is on the
|
|
67
|
+
console whatever user it runs as, its device numbers differ per session (one session's "screen" was
|
|
68
|
+
the other's camera), and its feed froze while the file kept growing.
|
|
69
|
+
- **`screencapture -v` writes the file only when `-V <secs>` runs out; an interrupt saves nothing.**
|
|
70
|
+
Record in short parts (`-V 60`) in a loop and stop by letting the part in flight finish.
|
|
71
|
+
- **Prove the picture moved:** hash the menu-bar clock region of each part's first and last frame. A
|
|
72
|
+
frozen capture otherwise looks like a quiet screen.
|
|
73
|
+
- **Address footage by wall-clock time,** and have whatever drives the app write a UTC event log, so a
|
|
74
|
+
cut is written against events and every on-screen line can be checked against what happened.
|
|
75
|
+
- Background tabs and covered windows aren't on camera: lay out the windows before the take, with the
|
|
76
|
+
driven browser tab the one showing.
|
|
77
|
+
- **Compose screen recordings in a renderer with sub-pixel transforms,** not ffmpeg `zoompan`, which
|
|
78
|
+
moves in whole-pixel jumps (still for 6 or 7 frames, then a jump): judder on interface text. Master
|
|
79
|
+
at 4K, crop as clipping boxes, push in with a CSS transform, set captions as real text. Remotion
|
|
80
|
+
writes full-range `yuvj420p` / bt470bg; convert to tv-range bt709 for deliverables. A zoomed-out
|
|
81
|
+
recording at 720p leaves interface text at about 7 px.
|
|
82
|
+
|
|
83
|
+
## Captions
|
|
84
|
+
|
|
85
|
+
- **Caption text comes from the script; timing comes from the transcript,** zipped word by word by
|
|
86
|
+
position. Whisper writes "That's spinach" for "That spinach?" and misspells names, so its text is never
|
|
87
|
+
the caption.
|
|
88
|
+
- **Reading time** at least 0.5 s + 0.3 s a word; type at least 28 px at 1080 wide; contrast at least
|
|
89
|
+
3:1. White on light images measured 1.4 to 2.3:1, so give them a soft dark backing.
|
|
90
|
+
- Bold word-by-word captions for social cuts; phrase captions for calm films. 17 to 20 characters a
|
|
91
|
+
second is a comfortable pace.
|
|
92
|
+
- **Over a voice, a caption is on screen exactly while its line is spoken.** A separate reading-speed
|
|
93
|
+
floor (15 characters a second) made every voiced cut drag and made a faster voice change nothing. A
|
|
94
|
+
text-only cut of the same film takes the voiced cut's timings.
|
|
95
|
+
- Translated captions with no voice: spread the timing by word length.
|
|
96
|
+
|
|
97
|
+
## Crops
|
|
98
|
+
|
|
99
|
+
- 9:16 from landscape: set a focus point per shot (faces and saliency), or the subject is lost. Set it
|
|
100
|
+
on the subject of the line being said: a portrait crop once cut someone out of their own goodbye.
|
|
101
|
+
- A group wider than the slice is shown whole over a blurred, darkened copy of itself. Never letterbox.
|
|
102
|
+
|
|
103
|
+
## Motion that survives a seek
|
|
104
|
+
|
|
105
|
+
HyperFrames renders by seeking. GSAP suppresses `call` and `onUpdate` on a seek, so counters are
|
|
106
|
+
stacked numbers switched with zero-length sets, and flicker is precomputed from a seed. Move with
|
|
107
|
+
transforms, not `top` (which snaps to pixels); use `svgOrigin` in SVG; never overlap tweens on one
|
|
108
|
+
element; start overlays hidden.
|
|
109
|
+
|
|
110
|
+
## HDR phone clips
|
|
111
|
+
|
|
112
|
+
iPhone clips are HDR (HLG), and some renderers then promote the whole film to HDR at a fraction of
|
|
113
|
+
real-time speed. Tone-map to SDR BT.709 first (`avconvert --preset Preset1920x1080` on a Mac in about
|
|
114
|
+
2 s a clip; ffmpeg `zscale` + `tonemap` elsewhere, which needs zimg), then render as SDR.
|
|
115
|
+
|
|
116
|
+
## Encode and verify
|
|
117
|
+
|
|
118
|
+
- Master: x264 CRF 18 at 1080p. Chat copy: H.264 720p at most about 15.5 MB. HEVC 1080p is about 10 to 16 MB.- Always `-movflags +faststart`, or a browser downloads the whole file before playing a frame. A `-c
|
|
119
|
+
copy` concat doesn't inherit it, so name it again, and check the atom order (`moov` before `mdat`)
|
|
120
|
+
rather than trusting the flag was passed. Ask what each intermediate is for: a clean master that
|
|
121
|
+
existed only as a pre-burn concat had no faststart and no home.
|
|
122
|
+
- Loudness: −14 LUFS for social, −16 to −18 for a quiet film, true peak −1.5 dB (`audio.md`).
|
|
123
|
+
- **Verify by decoding, not by exit code:** `ffmpeg -v error -i out.mp4 -f null -` prints nothing when
|
|
124
|
+
the file is sound; a killed render leaves a file of the right size that is truncated. Run a backgrounded
|
|
125
|
+
build under `python3 -u`, or its log stays empty and reads as a hang. Transcribe the finished mix once
|
|
126
|
+
more.
|
|
127
|
+
- **Count frames; never read `Duration:`** (it reported 7.62 s and 7.63 s for parts a whole frame
|
|
128
|
+
apart). Count with `ffmpeg -v error -i f.mp4 -map 0:v -f null - -progress /dev/stdout`. Three causes
|
|
129
|
+
it hides: `-t` before `-i` cuts at a packet boundary (after the filter it is exact); a video-only part
|
|
130
|
+
rounds differently from one with audio, so build every part with the same command, silent track
|
|
131
|
+
included; and `-shortest` with `-c copy` drops frames. Use `-shortest` only for a truly unbounded
|
|
132
|
+
input (a looping bed, `anullsrc`).
|
|
133
|
+
- Some ffmpeg builds (Homebrew 9.x, Remotion's bundled one) have no `drawtext` and no subtitles filter.
|
|
134
|
+
Text comes from the HTML layer or Pillow, never from ffmpeg. An app-bundled ffmpeg that has `drawtext`
|
|
135
|
+
may lack a fontconfig configuration (pass `fontfile=`), and with a variable font a newline inside one
|
|
136
|
+
`textfile` draws a missing-glyph box: one `drawtext` per line.
|
|
137
|
+
- **The poster frame is a deliverable,** with its own build step. Take it from the captioned master,
|
|
138
|
+
and derive the timestamp from the subtitle cue whose line it should carry, so retiming the line moves
|
|
139
|
+
the poster. Then check the page uses it: a site may mount its own local still, so re-uploading changes
|
|
140
|
+
nothing a visitor sees (read the page's `poster=` attribute).
|
|
141
|
+
|
|
142
|
+
## Publishing the file
|
|
143
|
+
|
|
144
|
+
- **Serve a draft film at a stable URL** and overwrite it; mint a dated copy only when a published embed
|
|
145
|
+
must not change underneath it. Check the CDN's caching before building around it: an overwrite may
|
|
146
|
+
be live at once.
|
|
147
|
+
- **Verify an upload by hash, and know when the ETag can't answer.** For a single-part S3 or R2 upload the
|
|
148
|
+
ETag is the MD5; above the multipart threshold (about 8 MB for `aws s3 cp`) it ends in `-<parts>` and
|
|
149
|
+
never matches. Download and hash instead; don't recompute a multipart ETag, which depends on a
|
|
150
|
+
configurable chunk size. Four perfect uploads once reported as failures this way, and a check that
|
|
151
|
+
cries wolf on the normal case gets ignored on the real one.
|
|
152
|
+
- **"Is this new?"** When someone reports a fault in a cut they approved before, fetch the published
|
|
153
|
+
version and compare before reasoning about recent edits. Validate the comparison first: a correlation
|
|
154
|
+
at a guessed offset read 0.02 ("different files") for identical audio. Compare windowed RMS, and scan
|
|
155
|
+
for the alignment if you correlate.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Voice: the person's words, and prose that doesn't read as machine-made
|
|
2
|
+
|
|
3
|
+
Readers now assume low-effort text is machine-made and trust it less. Merriam-Webster's word of 2025
|
|
4
|
+
was *slop*. In a 2025 survey of 3,000 US adults, trust in a piece fell by nearly half when people
|
|
5
|
+
believed AI made it, whether or not it did (Raptive). Some places ban generated text outright (Hacker
|
|
6
|
+
News). So voice is the first craft problem, not the last polish.
|
|
7
|
+
|
|
8
|
+
## 1. The person's material first
|
|
9
|
+
|
|
10
|
+
Before the first draft of anything published under someone's name:
|
|
11
|
+
|
|
12
|
+
1. **Ask for their material, not the copy.** One question with no draft attached, built from what you
|
|
13
|
+
found: *"What did you think when you saw the first real sign-up?"*, *"Which of these three threads
|
|
14
|
+
would you actually reply to, and what would you say?"*. Never *"what should the post say?"*. Say a
|
|
15
|
+
spoken answer is welcome and that a minute is enough.
|
|
16
|
+
2. **Build from their answer.** Keep their phrases, their order of telling and their opinion. Cut and
|
|
17
|
+
arrange; don't paraphrase into smoother words. On the review page, mark which lines are theirs.
|
|
18
|
+
3. **Look for their written voice.** Their published posts, a voice notes file, their lessons from
|
|
19
|
+
earlier picks, and any voice skill they keep for themselves. Read those before writing, and follow
|
|
20
|
+
them over this file wherever they differ.
|
|
21
|
+
4. **Name the sources** of the person's material beside the draft (which answer, which memo, which
|
|
22
|
+
earlier post), so the next version can go back to them.
|
|
23
|
+
|
|
24
|
+
A piece the person didn't shape isn't ready for review, however well it scores.
|
|
25
|
+
|
|
26
|
+
## 2. The critique is new material {#critique}
|
|
27
|
+
|
|
28
|
+
When the person says it sounds like AI, misses the point, or asks how this is good, **do not reword
|
|
29
|
+
the same draft**. Answer their question honestly (including *"it isn't, and here is why"*), ask what is
|
|
30
|
+
off and what they would say instead, and rebuild from their reply. After a piece has been sent back
|
|
31
|
+
once, offer two or three one-line directions or ask for their dictation; don't write another full
|
|
32
|
+
draft on your own.
|
|
33
|
+
|
|
34
|
+
## 3. The tells
|
|
35
|
+
|
|
36
|
+
Machine drafts perform emotion instead of expressing thought: they build suspense, stage reveals and
|
|
37
|
+
punch up clauses, where a person talks through a thought and lets the content be the drama. Each of
|
|
38
|
+
these tells has killed a real draft. Rewrite the sentence; don't just swap a word.
|
|
39
|
+
|
|
40
|
+
1. **Dramatic fragments and one-line paragraphs.** *"That's not what happened."* *"Free. Local. Yours."*
|
|
41
|
+
Carry the beat inside a full sentence.
|
|
42
|
+
2. **Meta-narration.** *"Here's the part that…"*, *"Here is the trap"*, *"Let me explain"*, *"The part
|
|
43
|
+
nobody puts in these posts"*. Do the thing instead of announcing it.
|
|
44
|
+
3. **Negative parallelism.** *"Not X. Not Y. Z."*, *"It's not about X, it's about Y"*, and the
|
|
45
|
+
two-sentence version *"X was not the total. It was the spread."* Say what the thing is.
|
|
46
|
+
4. **Rule of three.** Triads for rhythm read as machine rhythm. Use two items, four, or a sentence.
|
|
47
|
+
5. **Zinger endings.** Paragraphs ending on a quotable punch line. End with the thought completed.
|
|
48
|
+
6. **Rhetorical question answered at once.** *"Is it faster? No."* Make it a statement. Real questions
|
|
49
|
+
to the reader are fine.
|
|
50
|
+
7. **Em-dash punch-ups.** One dash per paragraph is the tell. Commas, parentheses and colons do the work.
|
|
51
|
+
When you remove a dash, re-read the sentence, because removing the pause can change the meaning
|
|
52
|
+
(*"the ones that were not — in a browser on your own machine"* became a sentence about something
|
|
53
|
+
else). Split the sentence or use a full stop. A dash doing comma or colon work isn't the tell; dashes
|
|
54
|
+
added for emphasis, about one a paragraph, are.
|
|
55
|
+
8. **Uniform rhythm.** Every paragraph two to four sentences of the same length. Vary it the way speech
|
|
56
|
+
varies.
|
|
57
|
+
9. **Escalation.** Everything world-changing. Plain understatement carries the extraordinary better.
|
|
58
|
+
10. **AI vocabulary.** pivotal, delve, tapestry, testament, vibrant, boasts, foster, leverage (as
|
|
59
|
+
filler), landscape, intricate, meticulous, showcase, garner, seamless, unlock, supercharge,
|
|
60
|
+
game-changer, "in today's fast-paced world", "the reality is", and the sincerity tic ("to be
|
|
61
|
+
honest").
|
|
62
|
+
11. **Copula avoidance.** *"serves as"*, *"functions as"*, *"marks"*, *"represents"* where *is* would do.
|
|
63
|
+
12. **Elegant variation.** Rotating synonyms to avoid repeating a word. People repeat their words.
|
|
64
|
+
13. **Trailing -ing clauses** (*"…, highlighting the importance of"*), emoji bullets, a bold lead-in
|
|
65
|
+
on every line, a summary at the end.
|
|
66
|
+
|
|
67
|
+
What reads as a person: long sentences that carry a whole thought, in speech order, connected with
|
|
68
|
+
*and*, *so*, *but*, *because*; concrete names, numbers and what actually happened; the writer's own
|
|
69
|
+
uncertainty said plainly; a dry aside; plain descriptive headings; an invitation instead of a call to
|
|
70
|
+
action; numbers stated once, precisely, without ceremony.
|
|
71
|
+
|
|
72
|
+
**Where this applies:** everything published under the person's name, and correspondence sent as them
|
|
73
|
+
too (a support request, a reply to a partner), where one staged sentence is a large share of a short
|
|
74
|
+
message and the register is plainer still. Release notes count: they drift into feature-marketing
|
|
75
|
+
voice, every item a small triumph with a beat at the end. Channels change the register (tighter on a
|
|
76
|
+
professional network, more instructional in a course), never the list of tells.
|
|
77
|
+
|
|
78
|
+
## 4. The sweep, and why it isn't the check
|
|
79
|
+
|
|
80
|
+
Run `scripts/voice-sweep.sh <file>` over every text before review. It counts em dashes against
|
|
81
|
+
paragraphs, lists short fragments, and finds the banned phrases and the commonest negative-parallel
|
|
82
|
+
shapes. It is a net with known holes. Pages have gone out passing every grep while carrying two tells
|
|
83
|
+
in one sentence. A launch post sent back as "way too AI generated" came up almost clean: what gave it
|
|
84
|
+
away was its shape (a slogan argued with in the title, tidy labelled sections, a bullet list explaining
|
|
85
|
+
itself), which no grep sees. So, after the sweep:
|
|
86
|
+
|
|
87
|
+
- **Read it aloud as the person.** Would they say this sentence on a call? Rewrite any sentence you
|
|
88
|
+
can picture on a conference slide.
|
|
89
|
+
- **Count the shapes no grep finds:** how many paragraphs end on a punchy clause (more than one or two
|
|
90
|
+
means the prose is performing), and whether three paragraphs in a row have the same shape.
|
|
91
|
+
- **Three parallel full-length sentences** starting the same way (*"The … you meant to …"* three times)
|
|
92
|
+
are a fragment drumbeat even though each is long.
|
|
93
|
+
- **Negative parallelism split by a full stop** (*"X was not the total. It was the spread."*) and the
|
|
94
|
+
bare *"What it is missing is not A. It is B, C and D"* slip past comma patterns; the sweep has a line
|
|
95
|
+
for the first shape, and reading catches the rest.
|
|
96
|
+
|
|
97
|
+
The fragment list is candidates, not verdicts: brand names with a dot and abbreviations come back as
|
|
98
|
+
false positives, and a short fragment is sometimes right (a deliberate two-word line in an approved
|
|
99
|
+
hero). On `.svelte`, `.ts` or `.html` files the sweep skips comments, so a dash in a code comment doesn't
|
|
100
|
+
count as one in the copy.
|
|
101
|
+
|
|
102
|
+
## 5. A document is not a log of itself
|
|
103
|
+
|
|
104
|
+
A post, page or guide reads as if written this morning by one author who knew everything in it. No
|
|
105
|
+
*"updated on…"*, *"this section used to say…"*, no editor's notes. A date that says how old a fact
|
|
106
|
+
is ("prices as read on 3 November") is a property of the fact and stays. A date that says when the
|
|
107
|
+
text was edited goes.
|
|
108
|
+
|
|
109
|
+
This holds for any document meant to be read through, internal plans and review packs included, not
|
|
110
|
+
only published prose. Most self-history wraps a warning a first-time reader needs, because they would
|
|
111
|
+
make the same wrong inference: **keep the correction, drop the autobiography.** Re-aim it from the
|
|
112
|
+
document's past to the reader (*"What this does not mean: X is not solved"*, not *"this section said X
|
|
113
|
+
was solved for a few hours"*). The one exception is an agent-facing operations file, which may name a
|
|
114
|
+
wrong version that escaped into other docs or habits, because someone may still hold it; if nobody does,
|
|
115
|
+
it is autobiography there too.
|