# Generate Write

> Produce content.md (slide/section bodies + bullets + titles) and speaker-notes.md from outline.md. Applies voice corpus. Shared content-pipeline sub-skill, solo-invokable.

- Skill: `jokerman89/generate-write` (Agent Skill)
- Install (CLI): `npx skillmds@latest add jokerman89/generate-write`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jokerman89/generate-write/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: jokerman89 (https://skillmd.com/u/jokerman89)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jokerman89/generate-write

---


You are the `generate-write` skill — second stage of the v3.5 shared content pipeline. Produces content.md (and speaker-notes.md if PPT in target_formats) from outline.md.

## What this skill does

Reads outline.md (frontmatter + section/slide list with key_messages + voice_techniques) → writes content.md with titles + bodies + bullets + voice-annotated blocks. If `ppt` in target_formats, also produces speaker-notes.md with 40-80-word note per slide.

Applies the active pack's voice corpus (`resolve_pack_field voice.corpus`; none by default) — voice modes, ground rules, and vocabulary blocklist as defined by the pack. Per-slide/per-section voice technique is picked from outline's `voice_technique` field; rewritten if missing or inappropriate. With no pack corpus, falls back to neutral ground-rules-only mode.

Used by `generate` orchestrator as Step 4, or solo when operator wants to evolve an existing outline into content.

## When to use

- Second step of `/li:generate` orchestrator chain (after outline)
- Operator wants to rewrite content while keeping outline structure
- A/B-test different voice-techniques for the same outline
- Translate content language (re-run with `--language en`)

## When NOT to use

- Operator has no outline — invoke `/li:generate-outline` first
- Final voice-gate (pack compliance + customer-share check) — that runs at orchestrator level, not here
- Format-specific styling — that's `generate-design`, not write

## Inputs

- Required `--outline <path>` — outline.md from generate-outline
- Optional `--voice-corpus <path>` — voice guide reference (default: the active pack's voice corpus, `resolve_pack_field voice.corpus`; none by default)
- Optional `--vocabulary-blocklist <path>` — words to never emit (default: voice-corpus default, if any)
- Optional `--voice-tier <tier>` — voice tier (default: the active pack's voice tier, `internal` by default; upgraded per pack when `--customer-share` upstream)
- Optional `--out-dir <path>` — output directory (default: alongside outline.md)

## Content.md schema

```markdown
---
title: <inherited from outline>
audience: <inherited>
arc: <inherited>
language: <inherited>
target_formats: <inherited>
voice_tier: <pack-resolved; internal default>
generated_at: <iso-8601>
source_outline_hash: <sha256 of outline.md>
---

# <title>

## §1 — <title from outline> {#sec-1}

<!-- voice: <pack-defined mode/technique, or null> -->
<!-- type: title -->
<!-- key_message: <inherited> -->

**Title:** <slide/section title, max 8 words, no period>

**Subtitle:** <optional, max 12 words>

**Body:**
<paragraph body — max 40 words per section. Or null if bullets used instead.>

**Bullets:**
- <bullet 1 — max 12 words>
- <bullet 2 — max 12 words>
- ...
<max 4 bullets>

**Data-viz:** <optional spec for charts/diagrams: type + axes + data-source>

## §2 — <next section> {#sec-2}
...
```

Each section/slide is a markdown subsection with HTML-comment annotations (`<!-- voice: ... -->`, `<!-- type: ... -->`, `<!-- key_message: ... -->`). Comments are machine-parseable by `generate-design` for layout-mapping and by `generate-qa` for voice-validation. They are stripped from final format output.

## Speaker-notes.md schema (only if PPT in target_formats)

```markdown
---
source_content_hash: <sha256 of content.md>
language: <inherited>
---

## §1 — <slide title>

<40-80 words of conversational speaker notes. Presenter-voice — first-person plural ("vi"), conversational tone, transition cues to next slide.>

## §2 — <slide title>
...
```

## Voice corpus rules

Read the active pack's voice corpus at session start (`resolve_pack_field voice.corpus`; none by default). Apply whatever modes, ground rules, and mode-selection guidance the corpus defines.

With **no pack corpus** (the neutral default), fall back to generic ground rules:
1. Strive for clarity
2. Be concise
3. Limit jargon
4. Find the focus
5. Have a perspective

A pack corpus may additionally define named voice modes (mapped per section via the outline's `voice_technique` field) and a per-slide-type mode-selection table. Honor those when present.

## Vocabulary blocklist (hard enforcement)

Read the blocklist from the active pack's voice corpus, if it defines one. When present, zero matches allowed in output — hard-block on commit attempt if blocklist words appear. The corpus defines its own tiers (hard-block words, replace-words, phrase-patterns). With no pack corpus, the only baseline blocklist is the generic AI-tell vocabulary (e.g. delve, robust, comprehensive, leverage, utilize, "in today's rapidly evolving landscape") to keep output from drifting into LLM-style prose.

## Writing constraints

| Element | Constraint |
|---------|-----------|
| Slide title | Max 8 words. No sentence-case period. |
| Bullet point | Max 12 words per bullet. Max 4 bullets per slide. |
| Body paragraph | Max 40 words per section. |
| Speaker notes | 40-80 words per slide. Conversational. |
| Overall | No jargon unless audience is technical. |

## Workflow

### Step 1 — Read outline.md + voice corpus

Parse outline.md frontmatter + section list. Load the active pack's voice corpus + blocklist if one is configured.

### Step 2 — Per section: generate title + body

For each §N section:
- Generate title (respect 8-word + no-period rule)
- Decide body vs bullets (data-heavy → bullets, narrative → body)
- Apply voice technique from outline's `voice_technique` field
- Emit content with HTML-comment annotations

### Step 3 — Vocabulary blocklist enforcement

After generation, scan content for any blocklist matches. If found:
- Tier 1 (hard-block words): regenerate the affected sentence with replacement
- Tier 2 (replace words): apply substitution table
- Tier 3 (phrase patterns): regenerate the affected paragraph

Repeat until zero matches.

### Step 4 — Generate speaker-notes.md if PPT target

For each slide: write 40-80 words of conversational presenter notes. First-person plural ("vi"). Include transition cue to next slide (last sentence).

### Step 5 — Write content.md (+ speaker-notes.md) + return paths

Write to `--out-dir`. Surface summary (word count, voice-tier, blocklist-pass) to operator.

## Voice tier behavior

If upstream `--customer-share`: voice_tier in content frontmatter is set to the active pack's customer-facing tier (signals to downstream that content was written for a customer-facing surface).

## Status protocol

- **DONE** — content.md (+ speaker-notes.md if PPT) written, all constraints passed, zero blocklist matches
- **DONE_WITH_CONCERNS** — written but some constraint borderline (e.g., 4-bullet limit exceeded once, flagged for review)
- **BLOCKED** — outline.md missing, malformed, or unreadable
- **NEEDS_CONTEXT** — `--voice-tier` ambiguous for content (e.g., outline says customer-bound but flag is `internal`)

## Pause-points

- Outline has missing `voice_technique` on > 30% of sections: re-invoke generate-outline with that requirement
- Blocklist enforcement loops > 3 iterations on same section: surface to operator for manual rewrite

## Integration

**Reads:**
- `outline.md` (from generate-outline)
- The active pack's voice corpus (`resolve_pack_field voice.corpus`; none by default)
- The active pack's vocabulary blocklist (from the same corpus, if defined)

**Writes:**
- `content.md` (always)
- `speaker-notes.md` (only if PPT in target_formats)

**Consumed by:**
- `/li:generate` orchestrator (Step 4)
- `/li:generate-design` (input → design-spec.json)
- The active pack's compliance gates (orchestrator-level voice-gate)

## Anti-patterns

- **Emit blocklist words "for natural language"** — blocklist is hard-rule, zero-tolerance. Rewrite instead.
- **Skip HTML-comment annotations** — downstream (generate-design, generate-qa) depends on them. They strip in format output.
- **Generate format-specific markup (e.g., `<slide>` tags)** — content.md is format-agnostic. Format-specific markup belongs to per-format builders.
- **Mix English + Swedish without `--language` override** — language must be consistent throughout.
- **Pre-bake speaker-notes for non-PPT runs** — speaker-notes.md only when ppt in target_formats.

## Failure recovery

- Blocklist loops > 3 times on same sentence: emit `<!-- voice-loop-fail -->` annotation, leave for operator review, continue with rest
- Voice corpus file missing: fall back to ground-rules-only mode (modes + techniques skipped), flag in frontmatter
- Outline malformed: surface error with specific line ref, exit BLOCKED

## Recommended next steps after invocation

- For full chain: invoke `/li:generate-design --content <content.md> --target-formats <formats>` to produce design-spec
- For solo-iteration: operator edits content.md manually, then resumes chain at generate-design
- For voice-tier upgrade: re-invoke with the active pack's customer-facing `--voice-tier` (still requires upstream voice-gate at orchestrator)

