For the author
Authoring guide
How to add, edit, preview, publish, unpublish and order notes, tools and pages. No programming needed; you edit text files and run one command.
Everything on this site comes from text files in the content folder. You edit them with any plain-text editor, run one command to check and build, and copy the result to the web server. The build refuses to publish anything it cannot check, and it tells you the file and line to fix.
The five commands you need
Run these in a terminal, inside the deliverables/tutorials folder.
| Task | Command |
|---|---|
| Check everything, write nothing | python3 build.py --check |
| Preview, including drafts | python3 build.py --preview |
| Look at the preview in a browser | python3 -m http.server 8000 --bind 127.0.0.1 --directory preview |
| Build the site to publish | python3 build.py |
| Start a new note | python3 tools/new_note.py my-note-name --title "My note title" |
After starting the local server, open http://127.0.0.1:8000/ in a browser. Press Ctrl+C in the terminal to stop it. Use the server rather than opening the files directly; links between pages expect a server.
Where things live
| File or folder | What it holds |
|---|---|
content/notes/ | One .md file per note. The file name becomes the address. |
content/pages/about.md | The about page. |
content/pages/guide.md | This guide. |
content/profile.json | Your name and the short introduction on the home page. |
content/tools.json | The tool shelf. |
content/taxonomy.json | Topics with reading paths, series, and tool use cases. |
content/site.json | Site title, home page choices and the link allow-list. |
dist/ | The built site. Copy this folder to the web server. |
preview/ | The preview with drafts. Never publish this folder. |
Add a note
- Run
python3 tools/new_note.py my-note-name --title "My note title". This createscontent/notes/my-note-name.mdas a draft. - Open the file and write. The part between the two
---lines is the front matter; everything after it is the note. - Run
python3 build.py --previewand look at it locally. - When it is ready, change
status: drafttostatus: publishedand runpython3 build.py.
The file name is the address: my-note-name.md is published at notes/my-note-name/. Use lower-case letters, digits and hyphens only. Renaming a file changes its address, so avoid renaming published notes.
Front matter reference
---
title: Write a reusable task brief
summary: One or two sentences shown in lists and search results.
status: published
kind: tutorial
date: 2026-09-14
updated: 2026-10-02
topics: [setup, prompting]
series: first-week
part: 2
tools: [sample-text-editor]
related: [blind-compare-two-drafts]
featured: false
sample: false
lang: en
---| Field | Required | What it does |
|---|---|---|
title | yes | The note title, up to 120 characters. |
summary | yes | Shown under the title and in lists, up to 280 characters. |
status | yes | draft (never published) or published. |
kind | yes | tutorial, workflow or field-note. |
date | yes | Publication date, written YYYY-MM-DD. |
updated | no | Add only when you actually revise the note. Must be later than date. |
topics | yes | One or more topic ids from taxonomy.json. |
series, part | no | Put the note in a series at a position. Set both or neither. |
tools | no | Tool ids from tools.json, listed under the note and linked from the tool. |
related | no | Note file names (without .md) to show first under "Related notes". |
featured | no | true on one note puts it at the top of the home page. |
sample | no | true labels the note as a sample on every page. |
lang | no | The language of the note, for example zh-Hans or zh-Hant. |
Rules the build enforces: one key: value per line, no indentation, lists in square brackets, true or false for yes/no fields, and real calendar dates. Wrap a value in double quotes if it starts with a symbol such as [ or contains a leading space. Unknown fields, duplicate fields and a missing closing --- are errors.
Edit, publish and unpublish
- Edit: change the file and rebuild. If the change matters to readers, set
updatedto today's date. The build never sets dates for you, so dates stay honest. - Publish: set
status: published, thenpython3 build.py, then copydist/to the server. - Unpublish: set
status: draftand rebuild. The page disappears from the next build. If other published notes link to it, the build names them so you can remove the links first. - Delete: remove the file. The same link check applies.
Order content
| What | How it is ordered |
|---|---|
| Latest notes and the archive | By date, newest first |
| Featured note on the home page | The note with featured: true, or the newest note if none |
| A topic's reading path | The order of path in taxonomy.json |
| Notes in a series | By part |
| Selected workflows on the home page | The order of home_workflows in site.json |
| Tools on the home shelf | The order of home_tool_shelf in site.json |
| Tools on the tools page | Grouped by use case, then the order in tools.json |
Writing notes: the Markdown subset
Notes use a small, strict part of Markdown. Anything outside it is either shown as plain text or refused with a line number, so a page never breaks silently.
## A section heading
### A smaller heading
#### A minor heading (not in the table of contents)
A paragraph. Lines next to each other join into one paragraph.
Leave a blank line to start a new one.
**Bold**, *italic* and `code` work inside text.
- A bullet list item
- Another item
continued on an indented line
1. A numbered step
2. Another step
> A quotation.
---The --- line is a section break; leave a blank line above it.
Links
| You write | It links to |
|---|---|
[text](note:reusable-task-brief) | Another note, by file name |
[text](note:reusable-task-brief#the-four-parts-of-a-brief) | A section of another note |
[text](#a-section) | A section of this note |
[text](topic:setup) | A topic page |
[text](series:first-week) | A series page |
[text](tool:sample-text-editor) | A tool on the tools page |
[text](page:about) | home, notes, topics, series, tools, about or guide |
[text](https://docs.example.org/page) | Another website |
[text](mailto:name@example.org) | An email address |
Links to other websites must use https, and the site name must be listed in allowed_link_hosts in site.json. This stops typing mistakes and unwanted links from being published. A section's link name is its heading in lower case with spaces replaced by hyphens; to fix it, end the heading with {#my-name}.
Code blocks
Put code between two lines of three backticks. Add a language label after the first backticks; it is shown above the block, and every block gets a Copy button.
```sh
python3 build.py --check
```To show a code block inside a code block, as this guide does, use four backticks around the outer one.
Callouts and checkpoints
> [!note] Optional title
> Extra context the reader may need.
> [!tip] Optional title
> A shortcut or a better way.
> [!caution] Optional title
> Something that can go wrong, and how to avoid it.
> [!checkpoint] What should be true now
> How the reader can tell this step worked.Checkpoints show a tick box. A reader's ticks are remembered in their own browser only and are never sent anywhere.
Tables
| Column | Another |
| --- | ---: |
| text | 12 |---: aligns a column to the end, :---: centres it. Every row needs the same number of cells. Write \| for a literal bar inside a cell.
What is not supported
- Raw HTML of any kind, including comments. Write the text in backticks instead, or escape the bracket as
\<. - Images. Planned; they need an image check first.
- Nested lists, underlined headings, and code shown by indenting. Use one list level,
##headings and fenced code blocks. - A
#title inside the note. The title comes from the front matter.
Any character can be written literally by putting a backslash before it, for example \* for an asterisk.
Chinese and mixed-language notes
Notes can be written in Chinese, or mix Chinese and English. Save the file as UTF-8 (most editors do by default) and set lang in the front matter, for example lang: zh-Hans for Simplified or lang: zh-Hant for Traditional Chinese. With lang set:
- the page uses Chinese fonts, line breaking and a line length suited to Chinese;
- line breaks inside a paragraph between Chinese characters do not add a space;
- section links keep the Chinese heading text;
- reading time counts characters instead of words.
File names stay in Latin letters, because they become the address.
The tool shelf
Each tool in content/tools.json separates details, which are facts readers should re-check, from your owner's note, which is opinion.
{
"id": "my-editor",
"name": "Editor name",
"status": "listed",
"sample": false,
"use_case": "writing",
"platforms": ["Windows", "macOS"],
"pricing_note": "Free; paid plan for sync (checked on the date below).",
"privacy_note": "Files stay local unless sync is turned on.",
"last_reviewed": "2026-10-01",
"link": "https://tool.example.org/editor",
"owner_note": "What you use it for, and its limits. Or null."
}Set status to draft to hide a tool without deleting it. Update last_reviewed whenever you re-check the details. link may be null; if set, its site must be in allowed_link_hosts.
Going live with your own content
The starter content is labelled as samples. When your own content replaces it:
- Rewrite or delete every note with
sample: true, and remove that line from notes you rewrote. - Replace the tools in
tools.jsonand setsampletofalseon each. - Edit
profile.jsonandpages/about.md, and setsampletofalseor remove it. - Set
"allow_samples": falseinsite.json.
From then on, the build refuses to publish anything still marked as a sample, so a placeholder cannot slip onto the live site. The "Sample" notice at the top of every page disappears once nothing published is a sample.
python3 build.py --check passes with allow_samples set to false.