markdown-writer — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited markdown-writer (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.
I trigger on any request that touches a README or top-level repo-facing markdown. New repo, stale repo, an audit ("does this read like AI slop?"), a restructure, a Mermaid diagram drop-in, or a multilingual rewrite. If the repo has AGENTS.md, CLAUDE.md, or docs/architecture/, I read those first as ground truth before writing a single line.
My voice rules are tuned for human-facing portfolio markdown. They make some files worse when the audience is a machine or the file follows a stricter external convention. I skip the rule application (and only do factual fixes) on:
AGENTS.md and CLAUDE.md: agent-facing format, table-heavy, inline # comments in code blocks are part ofthe documented convention. Voice rules skipped. Em-dash and AI-tell rules still apply (humans edit these too).
ADR-*.md files under docs/architecture/decisions/: Architecture Decision Records have their own template(Context, Decision, Consequences) and should stay terse and structured.
openspec/changes/**: their format is defined by the OpenSpecworkflow and shouldn't be rewritten.
CHANGELOG.md: Keep-a-Changelog conventions (## [version] headings, ### Added/Changed/Fixed subheads).SKILL.md): agent-facing, so the human-voice and AI-tell prose rules are relaxed. Thestructural rules now apply and are enforced by tools/check-markdown.py: no em-dashes or en-dashes, sections at ### with a --- divider before each, and prose wrapped at 120. Subagent definitions (.claude/agents/*.md, .opencode/agents/*.md, subagents/*.md) keep their own conventions and are generated, so edit subagents/*.md and regenerate rather than hand-editing the generated copies.
.gitignore-adjacent: leave as-is.When asked to "fix all markdown" or similar broad directive, I list the files I'd skip and why before touching them. If the user explicitly says "rewrite this AGENTS.md too", I follow their lead.
I write in this order, dropping sections that don't apply rather than padding them.
#, one-line tagline italicized under it, a tight row of shields.io badges (build,license, language version, last commit). Skip vanity badges (stars, downloads under 1k).
include a request-flow sequenceDiagram too.
wins. This is the section AI tools always botch.
docs/. README is the index, not the encyclopedia.I write like a tired senior engineer, not a marketing intern.
constantly in LLM output because the training data is full of them, and human technical writers rarely use them. Replace with a comma (mid-sentence pause), period (full break), colon (introducing a list or explanation), parentheses (parenthetical aside), or rewrite the sentence. ASCII hyphens (-) inside compound words such as agent-standards, read-only, opinionated-template are fine; the em-dash (U+2014) and en-dash (U+2013) characters themselves are banned. Run grep -P "[\x{2013}\x{2014}]" file.md after editing to verify.
The goal is prose that could plausibly come from a human engineer who writes like they speak.
Mix short sentences (5-10 words) with long ones (25-40) to break the metronome. Read your draft aloud; if it sounds like a steady drumbeat, vary the lengths.
three-bullet blocks where each bullet opens with the same syntax. Use them once per section at most.
also". Replace with a direct statement.
"Moreover", "In today's world", "In today's fast-paced world", "Importantly". Delete these phrases; the information that followed survives without them.
them or end on the actual point.
("can be", "useful", "many", "certain") softens the claim until it says nothing. Pick one qualifier or none.
sentences, ends with a summary. Read like an essay-grading rubric. Break the pattern by leading with the result, starting mid-thought, or using a single sentence as a paragraph.
"elevate", "unlock". If you can't replace the adjective with a concrete fact (covers 12 cases including X, 15ms p99 latency), delete it.
with other punctuation ("fast, reliable, affordable; tested, scalable, secure"). Vary or split.
cd x && ./y && ./z unless that chain is genuinely the command.[docs/X.md](./docs/X.md), not ` [docs/X.md](./docs/X.md) `.The double styling (link + code) is valid GFM but reads as visual noise; it screams "I am both a link and a path" when one of those is already obvious from context. Reserve backticks for inline references that are NOT also links (a function name in prose, a file path mentioned without anchoring).
docs/X.md, .agents/skills/,subagents/, or any concrete path that exists in the project, make it a link: [docs/X.md](docs/X.md), [.agents/skills/](.agents/skills/). Applies on every mention, not just the first. Skip linking only when the path is generic ("the project root", "your build folder") or doesn't exist as a concrete artifact. Reason: in a portfolio README, the reader should be one click from inspecting anything you name.
i. / ii. / iii. / iv. and no (i) / (ii) / (iii)for nested or top-level steps. Reasons: nobody parses Roman numerals fluently past iv, sort order breaks in tools that don't know them, and they're a tired academic-paper affectation that doesn't survive a code diff. Use Arabic digits (1., 2., 3.) for ordered lists. For sub-steps inside a numbered item, use lowercase letters with closing paren (a), b), c)) or just nest with another 1. / 2.; both render correctly in GFM. Reserve I, V, X, L only for actual proper nouns or version names that already contain them.
language's own width (commonly 100-120). Tables stay on one line per row; don't break a table cell. Long URLs in a link don't trigger a re-wrap; the surrounding text does. Reasons: side-by-side diffs stay readable, terminal less / view doesn't horizontally scroll, code review comments anchor to a meaningful line. The 120 target gives ~30 chars of headroom over code's typical 80-100 to keep prose flowing without making the file look like a typewriter draft. Lists, headings, and > blockquote prose all follow the same 120 cap. The exception is a single-line lead under a heading where breaking would split a noun phrase awkwardly; keep it on one line and move on. Re-wrap existing files when you touch a paragraph; don't reformat lines you didn't edit (it pollutes the diff).
### (H3), not # or ##. Reserve # (H1) for thedocument title; one per file. Skip ## (H2) entirely in READMEs and human-facing docs. Reason: GitHub renders H1 very large (used for the page header) and H2 large with an underline rule. Both look too heavy when used for every section heading. H3 onward renders at a comfortable visual weight that doesn't compete with the title. Sub-sections are ####, sub-sub-sections are #####. This now applies to skill manifests (SKILL.md) too, enforced by tools/check-markdown.py. Subagent files and the AGENTS.md / CLAUDE.md families keep their own conventions.
--- horizontal-rule divider before its heading, including the first section after thehero block (title, tagline, badges). "Section" means the heading level chosen for major divisions of the document (### per the rule above, in both READMEs and skill manifests); the rule is the same regardless of the level used. Sub-headings within a section (one level deeper) do not get dividers; the heading itself is enough separation. Do not stack dividers with empty paragraphs around them: one blank line above, one blank line below the ---.
server count, supported tool count, etc.) live in shields.io badges and nowhere else in prose. Do not embed counts inline ("the 73 skills cover...", "26 specialised subagents") or in tree comments ("73 SKILL.md files"). When you want to gesture at the size, write "the catalogue" or "all skills", anything stable across additions and removals. Reason: every count duplicated in prose is a future bug when the inventory changes.
from describing one thing to introducing the next thing, especially when followed by a code block, an enumerated list, or a sequence of instructions, break to a new paragraph instead of running the lead-in inline. The eye should see the seam where description ends and action begins. Applies to short pivot phrases as well as full sentences.
language tag (bash, powershell, shell). No # comment lines inside the block; no && chains of multiple commands; no inline commit messages (commit messages are obvious from context and don't belong in user-facing docs). Per-shell labels go in surrounding prose ("Windows (PowerShell 7+):" then the block). One block = one paste. Sample commit-creation instructions are exempt only when the commit message itself is the example being demonstrated (rare, mostly in git-workflow skill examples).
the repo distinguishes shipped docs from project-only docs) and "This repo only" (lowercase filenames). For repos with a single docs flavour, one table is fine. Every project documentation file appears in one of those tables. Files already linked inline earlier in the README still appear in the Docs map; the map is the canonical index, not an addendum. Goal: a first-time visitor can find every document the project ships from one place.
Bash example:
./gradlew bootRunPowerShell equivalent:
.\gradlew.bat bootRunI prefer Mermaid over images because diffs work and GitHub renders it natively. Two patterns cover 90% of READMEs.
System overview uses graph TB with subgraphs for logical zones (external prereqs, app services, MCP layer). Keep it under 12 nodes. If it's bigger, split into two diagrams.
Request flow uses sequenceDiagram for the happy path of one canonical request. Show timeouts and async hops with dashed arrows. Skip error branches; that's what docs/ is for.
I add accTitle and accDescr lines for accessibility on any diagram in a polished or portfolio README.
shields.io is the only badge source I use. Keep the set tight: build status, license, primary-language version, last-commit. Drop anything that's pure vanity (GitHub stars under 500, "made with love"). For multi-module monorepos, only badge the top repo, not each module.
When polishing an existing README I walk this list:
[x](url) ); collapse to plain x`parens. Run grep -P "[\x{2013}\x{2014}]" file.md to find them.
filler intensifiers ("It's worth noting that..."), three-bullet rhythm chains
i., ii., iii., (i), (ii)...); replace with Arabic digits or lowercaseletters with )
the sentence
--- before any section heading, including the first; restore the dividers## in human-facing docs (READMEs, docs/); promote to ### and shift sub-headingsdown accordingly. # stays reserved for the file title.
but Y"), boilerplate closers ("one thing is clear"), hedging stacks, rigid topic-sentence-then-support-then-summary paragraph rhythm; rewrite the passage to vary sentence length and lead with the point
break to a new paragraph
cp x y && git commit -am "..."); split into one commandper fence
I report findings as a diff plan before rewriting, so the user can veto sections.
When the user asks for a translated copy (e.g. README.es.md, README.zh-CN.md), I follow these rules so the localized version doesn't drift from the source.
File naming. I keep README.md as the canonical English source and add locale-suffixed siblings: README.<locale>.md (BCP-47, e.g. README.zh-CN.md, README.pt-BR.md). At the very top of every variant I add a one-line language switcher that links across all locales. The same line goes into README.md.
What gets translated. Translate prose, headings, badge alt text, table headers, and ⚠️/❌/✅ labels. Do NOT translate:
natural-language labels inside diagrams.
Anchors. GitHub builds anchors from translated headings, so internal [link to header](#header) links break. For each translated heading, I either rewrite all in-document anchor links to the localized slug, or insert an explicit <a id="english-anchor"></a> above the heading so the original anchors still resolve. I prefer the explicit anchor: it makes cross-linking from the English README work without locale awareness.
Drift control. Every translated file ends with a footer line: > Translated from [README.md](./README.md) at commit <sha>. If they diverge, the English version is authoritative. On any future README update, I refresh the same sections in every locale file in the same commit.
Heavy material lives next to this SKILL.md in optional sibling folders so the skill stays compact and the model only loads what it needs:
templates/: full README scaffolds for common project shapes (Java service, Python MCP server, monorepo index,library). I load these only when the user asks for a from-scratch README.
references/: longer reference docs (badge palette catalog, Mermaid diagram cookbook with all 24 types,voice-rewrite before / after examples, translation glossaries per language). Load on demand when a specific subtopic comes up.
assets/: example diagram SVGs and screenshot conventions, only fetched if the user wants visual examplesrendered.
If a folder isn't present, I proceed with what's in SKILL.md alone. The skill never hard-depends on the sibling folders.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.