Social post from facts
Turns the owner's own facts into short posts. Every word that matters (a number, a price, an offer, a claim) must trace back to the facts file. The skill drafts; the owner approves; nothing posts itself.
Inputs
facts.mdfor the week, approved by the owner: what happened, dates and times, products, offers with their terms, permissions for any photo, tone notes and topics never to mention (seeexamples/facts.md).- The channels and how many posts each (from the owner's calendar).
- A few past posts the owner liked, for tone.
If there is no facts file this week, ask the owner once and stop. Do not fill a quiet week with invented content.
Steps
- [ ] 1 Read the facts and the never-mention list
- [ ] 2 Draft one post per fact worth sharing
- [ ] 3 Run the claim check and fix every line
- [ ] 4 Add the notes the owner needs to approve
- [ ] 5 Hand the drafts to the owner through the gateway
-
Read. Note each fact, its date, any photo permission and its limits ("bike, not the customer"), and every topic the owner said never to mention.
-
Draft. One idea per post. Short sentences, the owner's tone, at most what the facts say. Fit each channel's habit (a neighbourhood page wants when and where; a photo account wants the picture's story). Put labels in
#headings and notes to the owner on lines starting with>, so the check reads only the copy. -
Check. Run:
node scripts/claim-check.mjs facts.md drafts.mdIt flags numbers, prices, percentages and times missing from the facts, and risky words (guarantees, superlatives, "free", "safe", "certified") that the owner did not write.
examples/draft-posts.mdgivesexamples/expected-check.txt. Remove the claim, or ask the owner to add the fact. Re-run until it is clean (examples/expected-check-final.txt). -
Notes. Under each post, a
>line with the facts used and anything you changed or dropped, as inexamples/expected-posts.md. -
Hand over. Send the drafts to the owner as a draft through the AHL Action Gateway (
gmail.draftormanual.draft; see the gateway-call skill), or save them in the shared folder the owner set. Scheduling happens only after approval, in the owner's own tool, by the owner or through access they granted and can remove. Never hold the owner's social media passwords.
Output
A drafts file with one heading per post, its channel, the copy, and the notes lines; plus the clean check output.
Edge cases
- Customer in a photo, or a child: only with permission the owner confirms; for a minor, a parent's or guardian's written consent. Otherwise describe the work, not the person.
- Staff or gifted products: if a post will be shared by staff or features a
gifted product, add the disclosure in the caption (
reference/endorsement-rules.md). - Health, safety, legal or alcohol claims: only the owner's exact words; flag them for the owner even when the check passes.
- Offers: the owner writes the price, the dates and the terms; a post never outlives the offer's end date.
What the check misses
It matches text, so it does not catch a claim made without numbers or listed words, a true fact used in a misleading way, or a story the facts do not contain. A risky word that appears anywhere in the facts file passes everywhere. Read every post against the facts yourself.
Never
- Never post, schedule or reply on a social account yourself.
- Never invent a customer story, a quote, a review or a result.
- Never write a price, discount or offer the owner did not supply.
- Never mention a never-mention topic, a private booking or a customer by name without the owner's confirmed permission.
- Never ask for or store social media logins.
Hand over
The owner reads each post next to its notes, edits or rejects, and approves the rest. Keep the facts file, the drafts, the check output and the owner's edits together, so the tone notes can be updated monthly.
The skill folder
Every file in the folder. Download each one into a folder with the same layout, or copy it.
social-post-from-facts/ SKILL.md examples/draft-posts.md examples/expected-check-final.txt examples/expected-check.txt examples/expected-posts.md examples/facts.md reference/endorsement-rules.md scripts/claim-check.mjs
SKILL.md
---
name: social-post-from-facts
description: Drafts short social media posts for a small business only from facts the owner supplied that week (new stock, events, finished jobs, tips), in the owner's tone, and runs a local check that flags any number, price, offer or bold claim the facts do not support. Use when an owner drops weekly notes or photos and wants posts drafted, when a workflow turns news, specials, locations or portfolio work into posts, or before any post goes to the owner for approval.
metadata:
ahl-category: marketing
ahl-jobs: content, campaigns
ahl-industries: food-truck, salon, veterinary, retail-shop
ahl-workflows: marketing--05-weekly-news-to-posts-and-newsletter, marketing--1520-food-truck-weekly-location-posts, marketing--1727-salon-portfolio-posts-with-photo-consent, marketing--1648-veterinary-seasonal-pet-health-posts
ahl-human-check: The owner approves every post before it is scheduled or posted; customer photos need the customer's permission, confirmed by the owner.
ahl-proposed-status: approved
ahl-version: 1
ahl-sources: |
FTC — FTC's Endorsement Guides: What People Are Asking — https://www.ftc.gov/business-guidance/resources/ftcs-endorsement-guides-what-people-are-asking — 2026-10-06
FTC — The Consumer Reviews and Testimonials Rule: Questions and Answers — https://www.ftc.gov/business-guidance/resources/consumer-reviews-testimonials-rule-questions-answers — 2026-10-06
---
# Social post from facts
Turns the owner's own facts into short posts. Every word that matters (a
number, a price, an offer, a claim) must trace back to the facts file. The
skill drafts; the owner approves; nothing posts itself.
## Inputs
- `facts.md` for the week, approved by the owner: what happened, dates and
times, products, offers with their terms, permissions for any photo, tone
notes and topics never to mention (see `examples/facts.md`).
- The channels and how many posts each (from the owner's calendar).
- A few past posts the owner liked, for tone.
If there is no facts file this week, ask the owner once and stop. Do not fill
a quiet week with invented content.
## Steps
```
- [ ] 1 Read the facts and the never-mention list
- [ ] 2 Draft one post per fact worth sharing
- [ ] 3 Run the claim check and fix every line
- [ ] 4 Add the notes the owner needs to approve
- [ ] 5 Hand the drafts to the owner through the gateway
```
1. **Read.** Note each fact, its date, any photo permission and its limits
("bike, not the customer"), and every topic the owner said never to mention.
2. **Draft.** One idea per post. Short sentences, the owner's tone, at most what
the facts say. Fit each channel's habit (a neighbourhood page wants when and
where; a photo account wants the picture's story). Put labels in `#`
headings and notes to the owner on lines starting with `>`, so the check
reads only the copy.
3. **Check.** Run:
```bash
node scripts/claim-check.mjs facts.md drafts.md
```
It flags numbers, prices, percentages and times missing from the facts, and
risky words (guarantees, superlatives, "free", "safe", "certified") that the
owner did not write. `examples/draft-posts.md` gives
`examples/expected-check.txt`. Remove the claim, or ask the owner to add the
fact. Re-run until it is clean (`examples/expected-check-final.txt`).
4. **Notes.** Under each post, a `>` line with the facts used and anything you
changed or dropped, as in `examples/expected-posts.md`.
5. **Hand over.** Send the drafts to the owner as a draft through the AHL
Action Gateway (`gmail.draft` or `manual.draft`; see the gateway-call skill),
or save them in the shared folder the owner set. Scheduling happens only
after approval, in the owner's own tool, by the owner or through access they
granted and can remove. Never hold the owner's social media passwords.
## Output
A drafts file with one heading per post, its channel, the copy, and the notes
lines; plus the clean check output.
## Edge cases
- **Customer in a photo, or a child:** only with permission the owner confirms;
for a minor, a parent's or guardian's written consent. Otherwise describe the
work, not the person.
- **Staff or gifted products:** if a post will be shared by staff or features a
gifted product, add the disclosure in the caption (`reference/endorsement-rules.md`).
- **Health, safety, legal or alcohol claims:** only the owner's exact words;
flag them for the owner even when the check passes.
- **Offers:** the owner writes the price, the dates and the terms; a post never
outlives the offer's end date.
## What the check misses
It matches text, so it does not catch a claim made without numbers or listed
words, a true fact used in a misleading way, or a story the facts do not
contain. A risky word that appears anywhere in the facts file passes
everywhere. Read every post against the facts yourself.
## Never
- Never post, schedule or reply on a social account yourself.
- Never invent a customer story, a quote, a review or a result.
- Never write a price, discount or offer the owner did not supply.
- Never mention a never-mention topic, a private booking or a customer by name
without the owner's confirmed permission.
- Never ask for or store social media logins.
## Hand over
The owner reads each post next to its notes, edits or rejects, and approves
the rest. Keep the facts file, the drafts, the check output and the owner's
edits together, so the tone notes can be updated monthly.
examples/draft-posts.md
# Draft posts (first pass, before the check) ## Post 1 (neighbourhood page) Three new gravel bikes just landed: the Sample Trail line in S, M and L. Come and sit on one. ## Post 2 (neighbourhood page) Saturday 10am to 1pm: free tyre pressure checks outside the shop. Cold air drops your pressure, so autumn is the time to check it every week. ## Post 3 (photo account) A 1980s touring bike, wheels rebuilt and ready for another 40 years. The best wheel builds in town, guaranteed. ## Post 4 (photo account) Gravel season is here. Sample Trail bikes from $1,299, 15% off this weekend only.
examples/expected-check-final.txt
No unsupported numbers or risky words found. Still read the draft against the facts: this check does not catch every claim.
examples/expected-check.txt
line 10: number "40" is not in the facts file line 10: "guaranteed" is not in the facts file; the owner must write or approve it line 10: "best" is not in the facts file; the owner must write or approve it line 13: number "$1,299" is not in the facts file line 13: number "15%" is not in the facts file 5 claim(s) to fix or get the owner to confirm.
examples/expected-posts.md
# Posts for the owner to approve (week of 2026-10-05) > Status: draft. Nothing is scheduled or posted until the owner approves each one. ## Post 1 (neighbourhood page) Three new gravel bikes just landed: the Sample Trail line in S, M and L. Come and sit on one. > Facts used: new arrival line. ## Post 2 (neighbourhood page) Saturday 10am to 1pm: free tyre pressure checks outside the shop. Cold air drops your pressure, so autumn is the time to check it every week. > Facts used: Saturday event; owner's tip. ## Post 3 (photo account) A 1980s touring bike, wheels rebuilt and back on the road. Thanks to its owner for letting us share it. > Facts used: finished job; customer agreed to the bike photo (not of them). > Changed: removed "another 40 years", "best" and "guaranteed" (not in the facts). ## Dropped > Post 4: the price and the 15% offer are not in the owner's facts. Ask the owner whether there is an offer; if so, the owner writes the price and terms.
examples/facts.md
# Week facts: Riverbend Bike Shop (invented) Approved by the owner on 2026-10-02. Only these facts may appear in posts. - New arrival: three gravel bikes in the Sample Trail line, sizes S, M and L. - Saturday 2026-10-10: free tyre pressure checks, 10am to 1pm, outside the shop. - Tip from the owner: check tyre pressure every week in autumn; cold air drops it. - Finished job: rebuilt the wheels on a 1980s touring bike for a customer. Customer agreed to the photo of the bike (not of them). - Tone: friendly, plain, no exclamation marks in a row, no emoji beyond one per post. - Never mention: prices of used bikes, other shops, local politics. - Channels: one neighbourhood page, one photo account.
reference/endorsement-rules.md
# Endorsement and review rules that touch social posts (summary) A summary for drafting, not legal advice. Doubtful cases go to the owner and their adviser. Sources opened 2026-10-06: - FTC — FTC's Endorsement Guides: What People Are Asking — https://www.ftc.gov/business-guidance/resources/ftcs-endorsement-guides-what-people-are-asking - FTC — The Consumer Reviews and Testimonials Rule: Questions and Answers — https://www.ftc.gov/business-guidance/resources/consumer-reviews-testimonials-rule-questions-answers ## What it means for a draft | Rule (paraphrased) | What the drafter does | |---|---| | An endorsement must reflect the endorser's honest opinion and real experience. | Never write a customer's words for them. Quote only what the customer said and agreed to share. | | A standout result shown in an ad needs to say what people generally get, unless the business can show it is typical. | No "lost 10 pounds" or "saved $500" stories unless the owner supplies the fact and how typical it is. | | A connection the audience would not expect (employee, family, free product, payment) must be disclosed clearly. | Staff sharing posts say they work there; gifted or paid posts say so in the post itself, not in a link or comment. | | Disclosures must be clear and conspicuous, hard to miss. | Put the disclosure in the caption text, near the start. | | Fake or AI-written reviews or testimonials are prohibited, as are reviews by people who never used the product. | Never draft a review or testimonial. Never invent a customer story. | | Incentives cannot be conditioned on a positive review; insider reviews need disclosure; reviews cannot be suppressed by threats. | Never draft "leave 5 stars and get…" posts. | | Fake followers or engagement are prohibited. | Never suggest buying followers or likes. |
scripts/claim-check.mjs
#!/usr/bin/env node
// Flags claims in a draft that the owner's facts file does not support.
// Usage: node scripts/claim-check.mjs <facts.md> <draft.md>
// Local files only, no network. Prints line numbers; exit 1 when anything is flagged.
// It catches: numbers, prices, percentages and times not written in the facts file,
// and risky words (guarantees, superlatives, health and safety promises) not in the facts file.
// It does not catch: a wrong claim made in words without numbers ("our fastest service"
// is caught only through "fastest"), a fact used in the wrong context, or a made-up story.
import { readFileSync } from "node:fs";
const RISKY = [
"guarantee", "guaranteed", "best", "#1", "number one", "cheapest", "lowest", "fastest",
"free", "cure", "cures", "safe", "100%", "always", "never fails", "certified", "licensed",
"award", "award-winning", "voted", "only", "proven", "risk-free", "save", "discount", "sale",
];
const [factsFile, draftFile] = process.argv.slice(2);
if (!factsFile || !draftFile) {
console.error("Usage: node scripts/claim-check.mjs <facts.md> <draft.md>");
process.exit(2);
}
const read = (f) => {
try { return readFileSync(f, "utf8"); } catch (e) { console.error(`Cannot read ${f}: ${e.message}`); process.exit(2); }
};
const NUMBER = /\$?\d[\d,.:]*(?:%|am|pm)?/gi;
const clean = (t) => t.replace(/[.,:]+$/, "").toLowerCase();
const facts = read(factsFile).toLowerCase();
const factNumbers = new Set([...facts.matchAll(NUMBER)].map((m) => clean(m[0])));
const draft = read(draftFile).split(/\r?\n/);
const findings = [];
draft.forEach((line, i) => {
// Headings (#) are labels and quoted lines (>) are notes for the owner, not copy.
if (/^\s*[#>]/.test(line)) return;
const lower = line.toLowerCase();
for (const m of line.matchAll(NUMBER)) {
const token = clean(m[0]);
if (!factNumbers.has(token)) findings.push(`line ${i + 1}: number "${token}" is not in the facts file`);
}
for (const word of RISKY) {
const re = new RegExp(`(^|[^a-z#])${word.replace(/[#%]/g, "\\$&")}([^a-z]|$)`, "i");
if (re.test(lower) && !re.test(facts)) findings.push(`line ${i + 1}: "${word}" is not in the facts file; the owner must write or approve it`);
}
});
if (findings.length) {
console.log(findings.join("\n"));
console.log(`${findings.length} claim(s) to fix or get the owner to confirm.`);
process.exit(1);
}
console.log("No unsupported numbers or risky words found. Still read the draft against the facts: this check does not catch every claim.");
Use it in Claude Code
Put the folder at .claude/skills/social-post-from-facts/ in your project (or ~/.claude/skills/social-post-from-facts/ to use it in every project). Claude Code finds SKILL.md there and uses the skill when a request matches its description.
Other agents that read the Agent Skills format take the same folder.
Sources
- FTC: FTC's Endorsement Guides: What People Are AskingChecked 6 Oct 2026
- FTC: The Consumer Reviews and Testimonials Rule: Questions and AnswersChecked 6 Oct 2026