$engineering
memos

how we author

Colophon

The harness that drafts and narrates these memos, read from the repo at build: an agent-driven authoring skill, a voice spec, in-body components, a narration toolchain, and a rendering pipeline. Reproducible steps are code; judgment stays prose.

substrate as of 0627dc0 · 21 files · build-time snapshot

.agents/skills/memo/SKILL.md192 lines
---
name: memo
description: Use when researching, drafting, or shipping an engineering memo through a conversational workflow. Covers the full lifecycle from a rough idea to a published memo, both the text and its narrated audio.
argument-hint: '"rough idea" | <id>'
disable-model-invocation: true
---

# Memo Skill

Manage the research -> draft -> ship lifecycle for engineering memos. Each command is one
conversational cycle: detect state, show current working state, present workflow options based on
available artifacts. Respects manual edits. This file is the concise entry point; detailed
per-action procedures, the audio toolchain, and shipping live in `references/` (see Additional
resources).

## Artifacts

Memos live in `memos/wip/<id>-<slug>/`, at the repo root, outside the Astro collection glob
(`site/src/content/memos/**/*.md`), so WIP work never leaks into the built site. A memo that is done,
shipped or abandoned, moves as-is to `memos/archived/<id>-<slug>/`; both directories are the author's
local record and neither is committed. Each memo can carry up to eight artifacts:

- `meta.yaml`: the verbatim rough idea, plus the author byline and GitHub handle, captured at
  create (`/memo "idea"`). The idea is the memo's immutable seed (Rough Idea in `research.md` is its
  living interpretation); `author` and `handle` drive the promote frontmatter author and the
  per-author voice used by every render. See `references/lifecycle.md` and `references/audio.md`.
- `research.md`: exploration, notes, references. Rendered from `references/research.yaml` on
  create; template contract and per-action detail in `references/lifecycle.md`.
- `draft.md`: memo with H1 title plus body. Generated by the Move to Draft action; detail in
  `references/lifecycle.md`.
- `speech.md`, `adaptations.yml`, `<slug>.mp3`, `.speech-hash`, `.audio.json`: the spoken-form
  projection, its per-memo pronunciation and pacing overrides, the rendered audio, and their
  integrity records (`.audio.json` is the rendered audio's provenance record: hash plus measured
  duration). Full grammar, listen loop, and staleness formulas in `references/audio.md`.

All artifact filenames and paths are defined here. Other sections refer to them by name.

## Workflow States

The skill shows where a memo is and offers the moves for that point. A filled dot (●) marks a stage
that exists, a hollow dot (○) one that does not; the stages are the research file, the draft file,
and the promoted site file. Position is the rightmost filled dot, the next move the first hollow one.

Always present the current state's full option set, verbatim from the list below. Recommending one is
fine; omitting the rest is not. The user chooses from the whole menu, never from a subset curated to
the move you happen to favour.

```
● research --- ○ draft --- ○ promoted
  revise research, web research, move to draft, or archive

● research --- ● draft --- ○ promoted
  revise research, web research, revise draft, integrate research, corroborate, listen, promote,
  or archive

● research --- ● draft --- ● promoted
  listen, preview (npm run dev), or ship
```

**Research input comes from 2 sources:** user input (focus areas, ideas, direction, captured via
Revise Research) and web research around the focus areas and adjacent topics (captured via Web
Research). Both feed into the research artifact during exploration.

## Interaction Cycle

Each cycle follows the same pattern:

1. User inputs command `/memo "idea"` or `/memo <id>`
2. Skill creates/detects state: folder, files, content
3. Skill shows current state (the notation above)
4. Skill presents options based on artifacts
5. User responds, either picking one of the listed actions, or continuing the conversation.
6. If an action: process and update the file. If conversation: respond in chat only; artifacts
   stay untouched.
7. Skill shows result: preview and confirmation for actions; just the reply for conversation.
8. Loop returns to step 3 after actions. After a conversation turn, stay in conversation; no need
   to re-render state until the next action.

**Artifacts are only written when the user picks a listed action.** Free-form follow-ups,
questions, reactions, pushback, thinking out loud, are conversation, not instructions to revise.
Treat a follow-up as a revise only when the user either explicitly picks a revise option or uses
imperative capture language ("add...", "note that...", "put in research that...", "write
down..."). When in doubt, stay in conversation and ask before writing. **Don't auto-capture.**

## Commands

### `/memo` (no arguments)

Glob `memos/wip/*/*` to find all files inside WIP memo folders, then deduplicate by parent folder
to get the list of memos. The parent folder name is `<id>-<slug>`. For each memo, check which
artifacts exist to determine its state, then display it using the state notation above.

This lists `wip/` only, which is the point: a shipped memo has been retired to `memos/archived/` and is
no longer work in progress. Count the folders there and close with a pointer, so a published memo stays
discoverable without a second table competing with the working set.

**What user sees:**

```
WIP memos:

  ab12  webhook-retry-storm         ● research --- ○ draft --- ○ promoted
  cd34  queue-backpressure-incident ● research --- ● draft --- ○ promoted

3 archived (shipped or abandoned). /memo <id> opens any of them.
Continue with /memo <id>, or start a new one with /memo "your rough idea".
```

If no memos exist: `No WIP memos. Start one with /memo "your rough idea".`, keeping the archived line
above it when `memos/archived/` is not empty.

### `/memo "Your rough idea"`

Derive a slug from the idea (via `slug.py`, see Implementation Notes), generate a 4-char id, render `references/research.yaml` into
`memos/wip/<id>-<slug>/research.md`, populating only the section the idea directly addresses and
leaving the rest for later exploration. Also create `meta.yaml`, recording the verbatim rough idea
as the memo's immutable seed and capturing the author byline and GitHub handle by prompting the
driver, defaulting `handle` from `gh api user -q .login` and `author` from `gh api user -q .name`
(falling back to `git config user.name`); each default is overridable. Full procedure in
`references/lifecycle.md`.

User responds with a choice (revise research, web research, move to draft, archive) or with
free-form conversation. Skill processes the choice (and updates state) or replies in chat only,
then presents options again.

### `/memo <id>`

Resolve the id against `memos/wip/*` first, then `memos/archived/*`, and work from whichever folder
holds it. An id resolves to exactly one folder; the two directories never both hold the same id. A
memo found under `archived/` has been shipped or abandoned, and its artifacts read the same either
way, so nothing about the options depends on which directory it came from.

Detect state (research exists? draft exists?), show content preview (first 100 words of each),
present available options for that state. A memo is in the Promoted state once its canonical site
file `site/src/content/memos/<slug>.md` exists; in that state the options are listen, preview
(`npm run dev`), and ship. Promote leaves the WIP folder in place as the paper trail and ship retires
it to `memos/archived/`, so a promoted memo resolves from `wip/` and a shipped one from `archived/`.
Shipping again after editing the canonical body is expected and stays a valid move: ship works from
the archived folder and leaves it there (see the Archive procedure in `references/lifecycle.md`).

User responds with a choice (action) or with free-form conversation. Skill processes the action or
replies in chat only, then shows updated state on actions.

## Implementation Notes

**Slug generation:** run `uv run tools/memo/slug.py "<title-or-idea>"` (which calls
`lib.slugify`); never hand-roll it. One tested computation (ASCII-fold, lowercase, non-alphanumeric
runs collapsed to single hyphens, ends trimmed) keeps the published slug identical across the memo
file, its `/memos/<slug>` route, and its mp3, and stays stable on awkward titles (punctuation,
accents, doubled spaces).
**ID:** 4 random alphanumeric characters (a-z, 0-9). Ensures uniqueness even if slugs collide.
**Two slugs, by design:** the WIP folder slug is derived from the idea at create, as a human label
(the 4-char id is the stable key). The published URL slug is derived from the memo's final title at
promote (see `references/shipping.md`). They can differ, because the title is only known once the memo
exists; the published render works off the title slug, and the WIP mp3 is a preview only.

**Toolchain:** the deterministic scripts (narration: derive speech, render mp3, verify provenance;
identity: the published slug) are first-class repo tooling at `tools/memo/`, not skill-internal. The
skill, CI, and a human at a terminal all invoke them the same way, `uv run tools/memo/<script>.py`.
References name them by bare filename (`project.py`, `render.py`, `slug.py`, `verify_audio.py`); each
is `tools/memo/<name>`. See `references/audio.md`.

**Voice alignment:** content follows this repo's voice guidelines: the shared spine
(`.agents/rules/voice.md`) and the memo register (`.agents/rules/voice.memo.md`), both path-scoped
to memo files so they load automatically. Rules in context shape writing without enforcing it, so
Move to Draft and Revise Draft end with a separate voice pass that checks the prose against them
before the author sees it (see `references/lifecycle.md`). Voice quality stays collaborative
feedback, not a hard gate: the pass protects the author's read, it does not replace it.

**Manual edits:** skill always reads current file state. Manual edits are respected. Next
invocation sees them.

**Error recovery:** all errors are recoverable. Show the problem clearly, show what's needed, offer
an immediate fix path inline. No abort; continue the workflow to keep the user in flow.

## Additional resources

- [references/lifecycle.md](references/lifecycle.md): the template contract for rendered
  artifacts, the full create-time author-capture procedure (`meta.yaml`, gh/git defaults, prompts),
  and the detailed per-action behaviour for revise research, web research, move to draft, revise
  draft, integrate research, and archive.
- [references/devices.md](references/devices.md): the in-body device catalogue for authoring
  (callout, exchange, table, code, footnote): syntax, when to reach for each, and its audio
  behaviour. The device vocabulary (callout types and exchange roles) is single-sourced in
  `site/src/lib/memo-devices.json`.
- [references/audio.md](references/audio.md): adaptations format and grammar, deriving speech,
  the listen loop (including passing `--handle` and `--config` to `render.py`), per-author voice
  resolution, propagation, how to change how a memo sounds, and staleness hash formulas.
- [references/shipping.md](references/shipping.md): corroborate, promote (site file, frontmatter
  author read from `meta.yaml`), and the ship-gate checks.
- [references/architecture.md](references/architecture.md): the rule that decides which memo
  operations are deterministic scripts and which stay agent prose, and the corpus mapped against it.