---
name: memo-contributor
description: Publish a post on Dwarves Memo (memo.d.foundation) for a Dwarves
  member or fellow. Use when a person says "help me publish a post on Dwarves
  memo", "write for memo", "I'm Dwarves staff" or "I'm a Dwarves alum". Writes
  the post in the dwarvesf/brainery repo and opens the pull request, as a branch
  or from a fork.
---

# Publish a post on Dwarves Memo as a member or fellow

A member is current Dwarves staff or a contractor. A fellow is anyone who worked at Dwarves before. Both can put a post straight into the memo repo, dwarvesf/brainery, through a pull request. You act for the writer. The writer owns the words, the byline and the decision to merge.

Read this file whole and follow it exactly. If you reached it through a tool that summarises pages, fetch the raw text instead: `curl -s https://memo.d.foundation/contribute-skill.md`. A summary drops the exact commands below.

The full human guide is https://memo.d.foundation/contribute-to-memo. Read it if the writer asks something this file does not cover.

## Routing

1. Run `gh repo view dwarvesf/brainery --json viewerPermission --jq .viewerPermission`. `ADMIN`, `MAINTAIN` or `WRITE` means branch mode. `READ`, `TRIAGE` or an error means fork mode.
2. The writer has their own blog and would rather publish there: stop and read https://memo.d.foundation/skill.md. That is the guest flow. Memo imports the post from their site.
3. The writer has no GitHub account: write an email draft, see Email draft below.
4. The writer is not staff or alumni and has no blog: tell them to write to team@d.foundation. Do not open a pull request for them.
5. Anything else: stop and tell the writer to contact the editors at team@d.foundation.

## Collect from the writer first

- The post, or the topic and the material to write it from. Ask for the writer's own words and numbers. Never invent data, quotes or results.
- The folder. Fetch https://raw.githubusercontent.com/dwarvesf/brainery/main/_meta/taxonomy.md and match the post to the folder whose Test column fits. Do not rely on memory of the folder list. If two folders fit, or none does, ask the writer. Never guess.
- The writer's GitHub handle for `authors:`. `gh api user --jq .login` shows the signed-in account. Confirm it is the writer's, not a shared or bot account. Never use a Discord name.
- The post title, one sentence of description, and the date (today unless the writer says otherwise).
- Any images or figures the writer wants in the post.

Memo publishes a post only if it can win an AI-retrieval query: firsthand data, a real verdict, or an angle nobody else has. If the post is generic and definitional, say so to the writer before you start.

## Steps, branch mode

1. `gh repo clone dwarvesf/brainery && cd brainery`
2. `git switch -c post/<slug>`
3. Write the post at `<folder>/<slug>.md`. Put images in `<folder>/assets/`.
4. `./pre-commit-hook.sh install`, then `./pre-commit-hook.sh status`. It must report the hook installed and executable. Node must be on the PATH.
5. `git add <folder>/<slug>.md <folder>/assets` and `git commit -m "feat(content): add <title>"`. The hook lints the staged files and may rewrite them. If it rewrote a file, run `git add` on it again, read the diff, and make a new commit with the same message. Never use `--amend`.
6. `git push -u origin post/<slug>`
7. `gh pr create --repo dwarvesf/brainery --draft --title "feat(content): add <title>" --body-file <body>`. See PR body below. Mark it ready only when the writer says so.
8. A formatting bot pushes a commit to the branch within a minute. Run `git pull --rebase` before every later push. Never force-push.
9. Tell the writer the PR is ready and who must merge. You never merge.

## Steps, fork mode

1. `gh repo fork dwarvesf/brainery --clone && cd brainery`. The fork becomes `origin`; the Dwarves repo becomes `upstream`.
2. Steps 2 to 6 of branch mode are the same. Push to your fork.
3. `gh pr create --repo dwarvesf/brainery --head <github-handle>:post/<slug> --draft --title "feat(content): add <title>" --body-file <body>`
4. Two things differ from branch mode. The formatting bot does not run on a fork, so the local hook in step 4 is the only formatter: run it and fix what it reports by hand. And on a first pull request from a new contributor, GitHub holds the checks until a maintainer clicks "Approve and run". Tell the writer the checks stay pending until then. Do not retry or push empty commits to move it.
5. Tell the writer an editor merges fork pull requests. You never merge.

## Post file

Path: `<folder>/<slug>.md`. The slug is flat kebab case and unique across the whole repo. Check with exactly `grep -rl "^slug: <slug>$" --include=*.md .`, the full slug with both anchors, and expect no output. A generic name gets a prefix: `brainery-architecture`, not `architecture`.

```
---
title: "A specific title"
description: "One sentence that says what the post shows."
date: 2026-10-18
slug: a-specific-title
kind: essay
authors:
  - github-handle
tags:
  - topic
---
```

- `title`, `description` and `date` are required. The lint fails without them.
- `slug` is the URL, `/<slug>`. Every new post gets one.
- `kind` is optional. The folder sets a default: `reports/` field report (except `reports/commentary/`, essay), `essays/` essay, `case-studies/` case study, `journals/` digest, `playbook/` and `handbook/memo/` playbook, `site/` reference. Word floors, warn only: note 150, playbook 400, essay 800, case study 800, field report 1200; digest and reference have none. Word ceilings, also warn only: note 2500; playbook, digest and case study 3000; reference 4000; essay and field report 4500. A post over its ceiling should split into a series or lose its weakest section.
- Check the floor before you pick the folder. A post under its folder's floor is thin for that folder. Tell the writer, and let them expand it or choose another folder. Do not set `kind: note` just to silence the warning.
- `tags`: two to five lowercase topic words. Read the `tags:` of three recent posts in the same folder and reuse their words before you invent one.
- `authors` is a list of GitHub-style handles. Never a Discord name, never a display name.
- Quote a numeric tag: `"101"`. An unquoted number crashes the lint.
- Do not add `evidence` or `draft`.
- No H1 in the body. The title already renders as the page heading. A body H1 is a lint error.
- Do not use a mid-heading "I" or a product name in a heading. The bot sentence-cases headings and mangles them.
- Say what you ran, with real numbers, a command, or a pull request link. Leave out claims you cannot back.

## Media

Full rules: https://memo.d.foundation/memo-media-standard. The short version:

- Draw diagrams as SVG with a `viewBox`, no fixed `width` or `height`, and an opaque background rect.
- Use raster only for screenshots and photos. Keep each under 200 KB and 704 to 1600 px wide. One file per image, no twin.
- Give every image a non-empty alt text and an italic `_Fig. N: caption._` line under it.
- Name files `<slug>-figN-<name>.<ext>` and keep them in `assets/` beside the post.
- Credit any image the writer did not make.

Run `xmllint --noout` on every SVG before you commit it.

## PR body

The repo uses an organisation template with placeholder text such as "Add new routes for user". Write a body file that replaces all of it: two or three sentences saying what the post is, which folder, and who the writer is. Never write `[skip ci]` in the title or body. Merging with that text in the message silently skips the publish.

## Email draft

If the writer has no GitHub account, you cannot open a pull request. Write the full post as a Markdown file with the frontmatter above, leaving `authors:` to the editors. Then write a short email to team@d.foundation: who the writer is, their link to Dwarves (staff or alum), the folder you think fits, and the file attached. Give both to the writer to send. Never send the email yourself.

## Verify (after the merge)

The merge starts the publish on its own. It takes 5 to 15 minutes. Wait, then:

`curl -s https://memo.d.foundation/<slug> | grep -c "<a distinctive phrase from the post>"`

Expect a number above 0. Do not use `curl -L`, and do not trust the status code alone: old URLs redirect, so a followed redirect can return 200 from a different page. The grep on the body is the proof. Check the PR state first with `gh pr view <number> --repo dwarvesf/brainery --json state,mergedAt`. If the count is still 0 about 20 minutes after the merge, report it to the writer and the editors. Do not trigger a publish yourself.

## Blockers: report them, never improvise

- The lint reports an error you cannot fix from the post file: show the writer the exact message and stop.
- You have no permission to fork or push: tell the writer, stop.
- The post fits no folder, or two folders: ask the writer. Do not guess.
- The writer's GitHub handle is unknown, or you are signed in as someone else: ask. Do not put another name in `authors:`.
- The PR shows a merge conflict, or the bot commit will not rebase cleanly: stop and report.
- The hook cannot download its linter (no network): tell the writer, stop. Do not skip the hook.
- Rewards: member and fellow posts earn no per-post reward today. Do not promise ICY.
- Never merge a pull request. Never push to `main`. Never use `--no-verify` or `--force`. Never edit a file outside the post and its assets. Never delete a file.

## Links

- Human guide: https://memo.d.foundation/contribute-to-memo
- Media standard: https://memo.d.foundation/memo-media-standard
- Folder taxonomy: https://raw.githubusercontent.com/dwarvesf/brainery/main/_meta/taxonomy.md
- Guest writer flow, for people with their own blog: https://memo.d.foundation/skill.md
- Repo: https://github.com/dwarvesf/brainery
- Site index for agents: https://memo.d.foundation/llms.txt
