Skip to content
memoDwarves Foundation

Handbook

Memo post rubric for writers and their agents

The checks a draft must pass before review on Dwarves Memo, a list of mechanical rules with a script to run and a scored list of judgment questions.

This page is the rubric for a new memo post. Run it on your draft before you open a pull request, whether you are staff, alumni or a guest. An agent can run it for you, and the last section says how.

The rubric has two parts. The mechanical checks come first: a script finds each problem and says where it is. The judgment checks come second: you answer yes or no and fix what fails. Both parts apply to every path. The media standard holds the file-level rules for images and SVG. Contribute to memo holds the paths.

Mechanical checks

Run the checker on your note. It needs Node 18 or newer and nothing else.

node _meta/scripts/post-check.mjs path/to/your-note.md

It prints PASS <file> or one WARN <id> <file:line> <message> per problem. It exits 0 by default, so it never blocks a commit. Add --strict to exit 1 on any warning. The pre-commit hook runs it in the warning-only mode on the notes you stage.

Fix every warning, or know why you keep it. A warning you keep goes in the pull request body in one line.

Table 1. Mechanical checks, with the id the script prints
IdRuleHow to fix
dashNo em dash or en dash, in the body or the frontmatter.Use a comma, colon, semicolon, brackets or a new sentence.
dek-longThe description is the dek: one light sentence, under 30 words.Cut it to the one thing the reader gets.
heading-numberA heading carries no number or "Part 2" label.Drop the number. Link with @sec-<heading-slug>.
sec-refProse never says "section 4".Write @sec-<heading-slug>.
fig-lineNo separate _Fig. N_ caption line under a figure.Put the caption in the image title.
fig-proseProse never says "Fig. 6".Write @fig-<id>.
inline-codeAt most one inline code span in a body paragraph.Say it in words, or move the detail into a fence or the appendix.
hash-bodyNo commit hash in the body.Move it to the appendix table.
version-bodyNo version string in the body.Move it to the appendix table.
fileline-bodyNo file:line reference or #L12 link in the body.Move it to the appendix table.
url-bareNo bare URL in prose.Link it as [Title](url), or move it to References.
ai-tellNo phrase from the short tell list, such as "here's the thing" or "in conclusion".Rewrite the sentence. The judgment checks cover the rest of the tells.
ascii-artNo box-drawing diagram in a code fence.Make it a figure.
fence-langEvery code fence sets a language.Add one. Use text when nothing fits.
eq-idDisplay math is one line: $$...$$ {#eq-id}. The formatting bot splits a multi-line block whose closing line carries the id.Put the formula and the id on one line.
eq-whereA numbered equation is followed by a where: list.List each symbol under the formula.
id-dupEach {#fig-...}, {#tbl-...} or {#eq-...} id is used once.Rename one.
ref-missingEvery @fig-, @tbl-, @eq- and @sec- points at an id that exists. The build fails on a miss.Fix the id or add the target.
fig-altEvery image has alt text.Write what the figure shows.
fig-embedEvery image uses the head-figure form, with a title and a {#fig-id}.![Title {#fig-id}](assets/x.svg 'Short title. Caption sentence.')
fig-titleThe caption's first sentence is the same short title as the alt text.Start the caption with the title.
asset-missingEvery image path exists.Add the file or fix the path.
asset-caseAsset paths are lowercase, because production is case-sensitive.Rename the file and the link.
live-twinA .live figure has a figkit .fig.html file beside its SVG.Add the twin, or drop .live.
svg-viewboxThe SVG viewBox is 760 wide.Redraw on a 760 wide plate.
svg-fixed-sizeNo fixed width, height or min-width in the SVG.Delete them and keep the viewBox.
svg-font-sizeText is 9 to 10.5 in a plate, or about 11 to 13.5 in a dense live diagram. The script flags anything outside 9 to 13.5.Resize the text.
svg-font-familyThe SVG font includes ui-monospace.Set font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace".
svg-dasharray-attrstroke-dasharray is set in style, never as an attribute.style="stroke-dasharray:5 4".
svg-smilNo SMIL (<animate>, <set>).Use a figkit .fig.html for motion.
svg-scriptNo script or event handler in an SVG.Remove it.
table-cellWide comparison tables use few words per cell.Cut cells to a few words; move prose out.
table-labelRow labels in the first column are short.Shorten them to six words or fewer.
ref-sectionA post that cites [n] ends with one ## References list.Add the list.
ref-formatEach entry reads n. **Term or Name**: definition or role, date. [Title](url).Rewrite the entry.
ref-orderEntries are numbered 1, 2, 3 with no gap.Renumber.
ref-bare-urlNo bare URL in an entry.Wrap it as [Title](url).
ref-multiOne source per entry: at most a source and one companion link, and under 450 characters.Split it into entries.
ref-sublistNo list nested inside an entry, such as a list of files read.Keep one definition and one link. The file list goes in the appendix.
ref-unusedEvery entry is cited somewhere, as [n] or [term](#ref-n).Cite it or delete it.
cite-missingEvery [n] has an entry.Add the entry.

The script reads the note and the SVG files it links. It cannot judge a figure's content, so the judgment checks still apply.

What the script does not check

The script does not decide whether a rule is worth breaking once. A hash in the body is fine when the post is about that commit. Use your judgment, then say so in the pull request. It flags the bad examples quoted on this page too, because it cannot tell a quote from a mistake. It also skips a missing raster image on a note that is already merged and unchanged, because media-sync moves rasters out of git after the merge.

Judgment checks

Answer each question yes or no. A "no" means rework, not rewording. The weakest items are usually 1, 2 and 11.

  1. Do the sections follow the reader's questions?

    • Good: a reader who stops after any section got a full answer to the question that section's heading asks.
    • Bad: sections follow the order you did the work in. Good: "Why it costs so much", then "How it works", then "When it pays".
  2. Does each section open with its answer, in one or two plain sentences?

    • Good: the first lines answer. Detail follows.
    • Bad: "To understand this, we first need to look at how requests flow." Good: "The agent pays for the tool list on every request, so cost grows with the tool count."
  3. Is the dek one light sentence?

    • Good: it names the reader's question or the result, with no setup.
    • Bad: "A deep dive into agent costs, with benchmarks and a framework." Good: "An agent that writes one program costs less than one that calls tools one by one."
  4. Is the core mental model stated before the mechanism?

    • Good: one sentence of "what this is" comes before any stage, step or internals.
    • Bad: the pipeline stages arrive in section two and the idea in section five.
  5. Are the headings plain words, and do the cross-references use ids?

    • Good: a heading names a topic or question, and prose links with @sec-....
    • Bad: "4. Implementation details" and "see section 4".
  6. Is each paragraph one idea at body density?

    • Good: identifiers, hashes, versions and file locations live in appendix tables with the columns What we checked, What it does, Where.
    • Bad: "fabric_exec calls bridge.ts:212 on v0.109.3" in a body paragraph. Good: "The tool call crosses one bridge to the host", with the file and version in the appendix.
  7. Does each figure earn its place, and is it complete without motion?

    • Good: one idea per figure, one accent color, text readable at phone width, and a caption that says what to look at.
    • Bad: an ASCII diagram in a fence, or a figure that repeats the paragraph above it. A live figure's static SVG must tell the whole story.
  8. Do figures sit at column width, with .wide only for content that is wider?

    • Good: a small diagram or a chart stays at column width.
    • Bad: .wide on a four-box diagram.
  9. Do tables read at a glance?

    • Good: a Table: title line where the table needs a name, short row labels, and a few words per cell.
    • Bad: an eight-column comparison with full sentences. Good: three to five columns of two to six words, with the prose above it.
  10. Does every fence the prose discusses name its file and highlight its lines?

    • Good: {file="turn-8.ts" hl="2"} on the fence the paragraph walks through.
    • Bad: a code block the text refers to as "the snippet above" with no file name.
  11. Does every claim carry evidence?

    • Good: a number names its run, a claim names its source, and a source is in References.
    • Bad: "Code mode is much cheaper." Good: "Code mode used 4,100 input tokens against 29,000 on the same task, mean of three runs, source cited."
  12. Are the references useful?

    • Good: a term peek ([term](#ref-n)) exists only for a word the reader may not know, defined once at first use. Each entry is one source.
    • Bad: a peek on every noun, or one entry that bundles five links and a file list.
  13. Does it read like a person who did the work wrote it?

    • Good: it holds a firsthand fact, a position a peer could dispute, and one thing that went wrong. See the voice gate.
    • Bad: corrective negation ("it is not X, it is Y"), three parallel items in a row, and a tidy closing line that restates the point.
  14. Is every sentence free of filler and em or en dashes?

    • Good: short sentences, concrete nouns, and no summary paragraph at the end.
    • Bad: "In today's fast-moving landscape, it is worth noting that..."
  15. Did you check the render?

    • Good: you ran the note locally, took screenshots at 1440, 1100 and 390 pixels wide in light and dark, and saw no sideways page scroll, legible figures, and margin notes that do not crowd the right rail.
    • Bad: "the build passed". A passing build does not show a clipped figure or a wall of margin notes.
    • How: run the memo app against your branch (see apps/memo/README.md in dwarvesf/foundation-apps), or ask an editor for a preview.

Verdict format

Return this at the end of the check, in this shape.

Mechanical: PASS | N warnings (list the ids and counts)
Judgment:   1 pass, 2 fail, 3 pass, ... 15 pass
Weakest:    one sentence naming the single weakest spot and where it is
Blocking:   the failing items that must be fixed before review (empty if none)

An item is blocking when it is 1, 2, 11 or 13, when a talk fails J1 or J6, or when ref-missing fires (the build fails on it). Every other failure, J2 to J5 included, is a fix before review that an editor may waive.

Talk posts

A note with layout: talk in its frontmatter is a talk: each slide sits beside the notes for it. How to publish a talk on memo is the worked example, and Contribute to memo has the file layout. A talk gets every check above, plus the eight below and the six judgment questions after them.

Talk mechanical checks

The same script runs these, and only on a talk. A slide image is not a figure, so the figure rules (fig-embed, fig-title, fig-alt, asset-missing) skip it and the talk checks take over. To build the slide files and see these results in one step, run node _meta/scripts/talk-build.mjs <note>.

Table 2. Talk checks, with the id the script prints
IdRuleHow to fix
T1talk.deck is assets/<slug>/deck.pdf, a PDF with one page per slide.Export the deck again, or add or drop slides to match.
T2Slides run slide-01 to slide-NN in order, each used once, no unused file.Rename the files and links. Remove a slide file you cut.
T3The talk has 8 to 40 slides.Merge thin slides, or split a long talk into two posts.
T4Alt text has 8 or more characters and says what the slide says.Write the slide's point in words, not "Slide 4".
T5Webp or png, 500 KB at most, long edge 1200 to 3200, one ratio.Export at 16:9, 16:10 or 4:3; set talk.ratio to match.
T615 to 150 words of notes per slide; the first and last may skip.Say what you said over the slide. Move a long aside to the outro.
T72 to 8 ## sections, each holding 2 to 10 slides.Group the slides by the beats of the argument.
T8image: points at og.png, a 1200 by 630 PNG of 300 KB or less.Run talk-build.mjs, which renders it from slide 1.

T5 accepts 16:9, 16:10 and 4:3. It also compares the slides with talk.ratio, which defaults to 16:9, because the site reads the ratio from the frontmatter once media-sync has moved the slides out of git. talk-build.mjs writes the field. Like the figure checks, these skip a missing slide, deck or share image on a talk that is already merged and unchanged, because media-sync moves those files out of git. The script's own test builds a small talk and breaks one rule at a time: node --test _meta/scripts/post-check-talk.test.mjs.

Talk judgment checks

Answer each one yes or no, the same way as the fifteen questions above.

Table 3. Talk judgment checks
IdQuestion
J1Skim test: do the slides alone carry the whole argument?
J2One idea per slide, about 25 words at most on a text slide?
J3Do the notes add what the slide cannot, and never read it aloud?
J4Is there one visual language within the deck: one template, one type scale?
J5Does slide 1 name the speaker and role, and the last give the takeaway?
J6Is the piece a linear argument with distinct beats?

Quotes and screenshots are exempt from the word limit in J2. The last slide also says where to find the speaker (J5). A reference page, a playbook or a data-heavy report fails J6 and stays as prose. J1 and J6 are blocking, like items 1, 2, 11 and 13. A J6 failure never rejects the piece: drop layout: talk and publish it as a normal post.

Add a line for the talk checks to the verdict block:

Talk:       T1 to T8 PASS | N warnings; J1 pass, J2 pass, ... J6 pass

For writers' agents

Fetch this page and run both parts before you open a pull request.

  1. Fetch the rubric with curl, and read it whole. A summary drops the rules. The page is at https://memo.d.foundation/memo-post-rubric, and the source is handbook/memo/post-rubric.md in dwarvesf/brainery.
  2. Run the checker from a clone of dwarvesf/brainery: node _meta/scripts/post-check.mjs <your note>. Fix every warning, or write one line in the pull request body for each one you keep. A guest writer without a clone applies the mechanical checks by hand from the table.
  3. Answer the fifteen judgment questions, plus J1 to J6 for a talk, and return the verdict block above. Put it in the pull request body.
  4. Do the render check from item 15 and attach the screenshots, or say that you could not.
  5. Stop and ask the human when the weakest spot needs facts only the author has, such as a number, a decision date, or something that broke. Never invent evidence to pass an item.