righter — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited righter (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A UX writing skill. Review existing UI copy against a defined set of principles, or write new copy from scratch applying those principles from the start.
Reference files: read these when relevant:
data/components.json: per-component writing rules (Alert Dialog, Toast, Inline Alert, Helper Text, Alert Banner, Tooltip, Empty State, Permission Prompt). Look up by component id, don't scan the whole file.references/accessibility.md: read when reviewing or writing labels, buttons, link text, form errors, or icon-only controls, or whenever accessible-copy might applyreferences/tone.md: read when copy lands on an emotional or high-stakes moment (errors, destructive actions, first use, success)references/voice-chart.md: read only when someone asks to define, document, or audit a brand voicereferences/figma.md: read only when someone shares a Figma link and asks for a copy reviewreferences/email.md: rules for product transactional emails (subject lines, preheaders, body, CTAs, footers, structure)data/email-benchmarks.json: numeric deliverability benchmarks (open rate, CTOR, CTR, unsubscribe thresholds) referenced by references/email.md. Query by metric.data/weakeners.json: structured word/phrase lists for all weakener categories. Query by category id, don't scan the file top to bottom.data/phonaesthetics.json: structured sound concepts and cluster table for word choicedata/rhetorical-devices.json: literary/rhetorical devices (metaphor, personification, epithet, anaphora, etc.) for copy that's allowed to have personality: marketing surfaces, taglines, empty states, feature names. Not for error messages or system copy.references/marketing-copy.md: read when writing or reviewing marketing-site copy (hero, section heads, feature cards, buttons, meta descriptions). Word limits per slot and the mkt-* rules.data/marketing-limits.json: word limits per marketing slot, banned puffery, and check severities. Query by slot id.scripts/check-slots.mjs: counts words per marketing slot and checks limits, puffery, em dashes, and acronyms: node scripts/check-slots.mjs hero-h1 "copy text". Run it instead of counting by hand.scripts/ari.mjs: computes ARI, grade level, and word/character/sentence counts exactly. Run it instead of calculating Reading Metrics by hand when Node is available: node scripts/ari.mjs "copy text" or node scripts/ari.mjs --before "..." --after "...".Every numbered principle below and every rule in Error Message Guidelines carries a stable id in backticks. Cite the id, not the number, when referencing a rule from outside this file (numbers shift when principles are added or reordered).
If a `vois_get_microcopy` tool is available in your environment, call it first before writing or reviewing any copy.
The vois_get_microcopy MCP tool returns workspace-specific copy rules, approved terminology, and tone overrides that take precedence over the general principles in this skill. If the workspace has an entry for the copy type you're working on, use it. Don't improvise.
Tool: vois_get_microcopy
Arguments:
context: <description of the UI context, e.g. "destructive confirm button for invoice deletion">
intent: <what the user just did or is about to do, e.g. "user clicked Delete on an invoice list row">
constraints: <optional object>
maxLength: <optional positive integer, maximum character count>
placement: <optional one of: button | title | description | helper | toast | error | label>
tone: <optional one of: neutral | warning | celebratory>There is no copy_type argument on the real tool: context and intent are both required; placement (nested under constraints, and a different set of values than "copy type") is the closest equivalent, and it's optional.
Resolution order (returned as `provenance` on the response):
WORKSPACE_OVERRIDE: use this verbatim, no changesKNOWLEDGE_BASE: apply as a strong starting point, adapt if neededLLM_FALLBACK / NO_MATCH / tool not available: apply the principles in this skill directlyWhen falling back to the principles, note it at the top of your output: Source: LLM fallback (no vois_get_microcopy tool available, or no workspace/knowledge base match for this copy type).
When the tool is available, it logs every query. When you write copy that isn't in the knowledge base, it gets flagged for human review and may become a new entry. This is how the system improves. This logging has no effect on how you should write or review copy either way.
For each piece of content:
vois_get_microcopy is available, call it with the relevant context and intentvois_get_microcopy is available, call it with the relevant context and intentdata/phonaesthetics.json and apply sound guidancedata/rhetorical-devices.json and consider one device before settling for plain phrasingUse this block for every piece of copy reviewed:
Before: [original copy] After: [rewritten copy]
Source: [Workspace override | Knowledge base | LLM fallback]
Principles applied:
Weakeners removed:
Phonaesthetics:
Rhetorical device:
id from data/rhetorical-devices.json) and why. If not applicable (error messages, system copy), write "Not applicable for this copy type."Reading metrics (Before → After):
Copy: [final copy]
Source: [Workspace override | Knowledge base | LLM fallback]
Principles applied:
Weakeners avoided:
Phonaesthetics:
Rhetorical device:
id from data/rhetorical-devices.json) and why. If not applicable, say so.Reading metrics:
Apply all of these when reviewing or writing. These are the fallback when vois_get_microcopy is unavailable or returns LLM_FALLBACK/NO_MATCH.
id: active-voiceSubject → verb → object. Active voice is shorter and easier to follow.
id: reading-levelShort sentences. Simple words. Clear structure. Target ARI ≤ 6. This is the default consumer target. See Audience and Reading Target for other tiers.
id: avoid-jargonReplace technical terms with plain language. Don't assume domain knowledge.
id: simple-sentencesOne idea per sentence. Avoid dependent clauses stacked on independent clauses.
id: no-double-negativesDouble negatives increase misreads and cognitive load.
id: use-contractionsContractions sound human. Formal constructions feel stiff.
id: present-tenseDescribe what's happening now or what the user can do.
id: user-goal-framingUsers care about what they're doing, not how the system works.
id: no-interface-referencesDon't reference UI elements like "tab," "panel," "menu," "page," or "section."
id: consistent-terminologyPick one word for each concept and stick to it.
id: progressive-disclosureLead with what the user needs now. Offer detail only when needed.
id: no-unnecessary-apologyReserve "sorry" for serious errors. Hollow apologies undermine trust.
id: limit-exclamation-marksUse words to convey energy, not punctuation. One per screen max, only for genuine celebration.
id: check-prepositionsPrepositions sit between two nouns. Never start or end a sentence with one.
id: eliminate-weakenersRemove all hedging words, softeners, empty intensifiers, filler adverbs, throat-clearing, passive-aggressive politeness, vague quantifiers, redundant framing, weak verb phrases, and meta-commentary.
Loaddata/weakeners.json. Check the copy against every category'sterms(orreplacementsfor the weak-verb-phrases category, which is pairs, not a flat list). Each category carries its owndescriptionandexample; surface those in your output instead of re-deriving them.
Key patterns to catch immediately:
id: no-em-dashesEm dashes read as a hedge in short-form UI copy and are a well-known AI writing tell. Use a period, comma, or colon instead.
id: rhetorical-devicesApplies only to copy that's allowed personality: marketing surfaces, taglines, feature names, empty states, onboarding. Skip entirely for error messages, form fields, and system copy. There, plain and literal beats clever every time.
Query data/rhetorical-devices.json. Pick at most one device per surface. Stacking two reads as trying too hard, not as clever.>
On marketing pages the word limits in data/marketing-limits.json win. A device has to fit inside the slot's limit. If it doesn't, cut the device.anaphora-epistrophe)id: accessible-copyScreen reader users often hear labels and links as a list, out of context. Write copy that still makes sense that way. Detail and the brevity trade-off are in references/accessibility.md.
aria-label copy.no-interface-references in force: "Submit application", not "Submit button".id: no-invented-claimsNever write a number, time, guarantee, price, legal term, or compliance or security claim that the product team or legal didn't give you. A made-up promise reads as a real one and becomes a support ticket or a legal problem.
[reply time], not "2 hours".id: no-pii-in-copyDon't put personal data in copy that gets logged, shared, previewed, or shown outside the screen: error messages, toasts, page titles, URLs, push and email subjects, notification previews, and analytics labels. Name the record by its role, or mask the value.
[PATH-PERM-PII-KEEP-OUT] in vois-patterns). Never an email, phone number, address, or ID there.[email protected], "Jane Doe".vois-patterns ([PATH-PERM-PII-MASK]).Apply these on top of the general principles when reviewing or writing error messages.
id: error-structureEvery error must answer:
id: error-what-happenedid: error-whyid: error-next-stepid: error-voice-and-toneid: error-mechanicsid: error-no-blameDescribe the situation, not the mistake.
id: error-field-vs-systemid: error-other-rulesid: error-decision-treeUse this before writing any error message to pick the right component. Then look up that component's id in data/components.json for full writing rules. A shared shared_prefix_format block covers the "Action Required:" / "Approval Required:" / "Review:" / "Verify Now:" prefixes used by Alert Dialog, Inline Alert, and Alert Banner.
Walk the questions in order and stop at the first "Yes". Don't keep checking once one matches.
Does it block progress and require immediate action?
└─ Yes → Alert Dialog
Is it a system-level issue (outage, permissions, account)?
└─ Yes → Alert Banner
Is it confirming something that just happened?
└─ Yes → Toast
Is it attached to a specific form field?
└─ Yes → Helper Text
Is it contextual to a page section, non-blocking?
└─ Yes → Inline Alert
Is it a hover label for an icon or interactive element?
└─ Yes → Tooltip
None of the above?
└─ Default to Inline Alert, the safest non-blocking option until the case is clear enough to fit one of the rows aboveThe tree above is for errors only. For non-error surfaces, look up the component by id in data/components.json:
empty-state: a list, page, or section has nothing to show (first use, user cleared it, no results, empty by nature, no access).permission-prompt: the app asks for a device or account permission (location, notifications, camera, contacts, storage).Table copy. Tables use the components above. An empty table is empty-state. A validation message on one editable cell is Helper Text. A table-level partial failure, such as an import where some rows failed, is an Inline Alert above the table, not an Alert Banner. A bulk delete confirmation is an Alert Dialog. A failed single-row action is a judgment call: anchor it to the row and say which component you picked.
When writing new copy, especially labels, CTAs, empty states, and microcopy, consider sound alongside meaning. Copy that sounds good is easier to remember and more pleasant to use.
Query data/phonaesthetics.json for the full concept guide and sound cluster table.Core rules to apply immediately:
Sound clusters to reach for:
gl- → clarity, light (insight, vision, illumination features)fl- → flow, ease (smooth UX, motion)sp- → speed, energy (action, innovation)cl- → precision, closure (tools, interactions)The reading target depends on who the copy is for. Pick a tier, then score against it.
| Tier | Use when | ARI target | Grade |
|---|---|---|---|
consumer (default) | General public, consumer apps, marketing-adjacent UI | ≤ 6 | Grade 5 |
professional | Work tools where users know the domain | ≤ 10 | Grade 9 |
technical | Developer or admin tools | ≤ 12 | Grade 11 |
Choose the tier in this order:
vois_get_microcopy returns a tier or audience, use it.consumer. If the audience is unclear, stay on consumer. Don't guess upward.consumer, name it at the top of the output. If it's consumer by default, say nothing extra.professional and technical, terms the audience already knows are fine. avoid-jargon still applies to internal names, system terms, and acronyms the audience wouldn't use.node scripts/ari.mjs --target N "<copy>" to print the matching Target line.Calculate and show these for all reviewed and written copy.
If Node is available, run `node scripts/ari.mjs "<copy>"` (or `node scripts/ari.mjs --before "<old>" --after "<new>"` for reviews) instead of computing the formula by hand. Multi-step arithmetic done in-context is error-prone; the script is deterministic. Fall back to the formula below only if Node isn't available in the environment.
ARI = 4.71 × (characters ÷ words) + 0.5 × (words ÷ sentences) − 21.43. ? or !Round to one decimal place. Always include grade and age range.
| ARI | Grade | Age |
|---|---|---|
| 1 | Kindergarten | 5–6 |
| 2 | Grade 1 | 6–7 |
| 3 | Grade 2 | 7–8 |
| 4 | Grade 3 | 8–9 |
| 5 | Grade 4 | 9–10 |
| 6 | Grade 5 | 10–11 |
| 7 | Grade 6 | 11–12 |
| 8 | Grade 7 | 12–13 |
| 9 | Grade 8 | 13–14 |
| 10 | Grade 9 | 14–15 |
| 11 | Grade 10 | 15–16 |
| 12 | Grade 11 | 16–17 |
| 13 | Grade 12 | 17–18 |
| 14+ | Professional | 18+ |
Target: ARI ≤ 6 (Grade 5, age 10–11) for the default `consumer` tier. Other tiers are in Audience and Reading Target above.
A label morph animates only the part of a label that changed. That works only when the words are right, so righter approves every pair before a component builds a morph.
For each pair, check:
(Save to Saved, Review order to Submit order). A pair with no shared text is a crossfade, and righter says so.
Continue to Pay $42 changesthe commitment, so the approved pair must say that.
Record the result in a Motion pairs block with the review or the copy handoff:
Motion pairs
- "Save" -> "Saved": approved, morph (shared prefix "Save")
- "Continue" -> "Pay $42": approved, crossfade (no shared text)
- "Cancel order" -> "Confirm": rejected, rewrite the new label or use a crossfadeGate: a component builds a label morph only from a pair marked approved. An unapproved or unlisted pair gets no morph code. Its label crossfades or swaps instantly, and the copy goes back to righter.
Run through this for every piece of copy before finalizing.
Before starting
vois_get_microcopy (with context and intent) and checked the returned provenance for a workspace/knowledge-base match?Voice and structure
Clarity
Tone
data/weakeners.json)Claims and data
Mechanics
Personality (marketing/taglines/empty states/feature names only)
data/rhetorical-devices.json)Marketing pages (only)
node scripts/check-slots.mjs)references/marketing-copy.md)Errors (if applicable)
evals/cases.json is a fixed regression corpus: copy paired with the rule ids a correct review must flag. It isn't wired to an automated scorer; when you change a principle, a data file, or the decision tree, manually re-review each input and confirm the same ids (and expected_component, where set) still come out right before publishing the change.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.