Octavo
Contents

Octavo reference

This page is written for AI assistants working in an Octavo project, and for anyone who wants every detail. People who use Octavo from VS Code should read the guide instead.

The same text comes with Octavo, matching the installed version:

octavo docs                       # the list of sections
octavo docs 3.8                   # one section, by number
octavo docs posters               # or by words from its heading
octavo docs --search slides_select   # every section that mentions a word
octavo docs --all                 # everything

--lang ja gives the Japanese text. The project’s AGENTS.md holds the rules for this project; this page is how Octavo works.


1. What to keep to

1.1 The rules

1.2 Finding out about a project

Prefer these to reading the files by hand; each has --json.

octavo documents --json       # every document: its file, outputs, sessions and their lines
octavo config --json          # the settings that can be changed, their values and choices
octavo config --doc my-paper --json   # the same for one document (front matter over project)
octavo analysis --json        # each .qmd: up to date, out of date, manual
octavo check values --json    # {{…}} in the text against the values the analysis wrote
octavo check cites --json     # citation keys against the .bib
octavo check --json           # everything before submitting
octavo doctor --json          # what is installed

2. Projects

2.1 What is in a project

study/
  octavo.config.py   the project's settings (a Python dict, CONFIG = {...})
  literature/        literature.bib, and priorwork/ (what Priorwork delivers)
  AGENTS.md          the project's rules for AI assistants
  CLAUDE.md          one line, @AGENTS.md
  README.md
  docs/<name>/       <name>.md (a manuscript), appendix.md, main.typ (a paper's layout), main.tex
  analysis/          the .qmd files and octavo.R / octavo_helper.py
  data/raw/          data as obtained (not in git; README.md records where it came from)
  data/derived/      data the analysis made (not in git)
  data/HASHES.json   fingerprints of data/ (in git)
  figures/           hand-made figures: <name>.typ drawn in Typst, _parts.typ shared parts, photos
  tables/            hand-made tables: <name>.csv
  notes/writing/     writing rules and phrases (packs)
  templates/         the project's own copies of Octavo's templates
  assets/values/     <qmd>.json: the numbers behind {{…}}
  assets/figures/    <name>.pdf and .png
  assets/tables/     <name>.typ, .tex, .md (the table's contents only)
  build/             the output, a folder per output; can be deleted
  requirements.txt   the Python packages (in git)
  renv.lock          the R packages (in git)

The config registers every folder by a pattern (docs/*/, analysis/*.qmd), so adding a document or an analysis never means editing it.

2.2 Documents and their names

A document is docs/<name>/<name>.md, named <name> (octavo build my-paper). appendix.md beside it is its appendix. What it makes is outputs: in its front matter (4.1). A document made of sessions also has one document per session for its slides and script: <name>-01, <name>-02, … in order, or <name>-<id> for a session with an id (3.10). octavo documents lists them all.

2.3 Adding to a project

octavo init study --lang en --with analysis,paper=my-paper   # the frame and some parts
octavo init study --all --example                           # every kind, filled with examples
octavo new analysis model                     # analysis/model.qmd (the first also brings octavo.R, data/)
octavo new analysis model --engine python     # in Python (brings octavo_helper.py)
octavo new paper my-paper                     # docs/my-paper/my-paper.md and main.typ
octavo new paper my-paper --appendix          # add appendix.md (to an existing paper too)
octavo new paper my-paper --tex               # add main.tex
octavo new paper other --lang ja              # a manuscript in another language than the project
octavo new slides talk
octavo new lecture course
octavo new poster poster
octavo new figure dag                         # figures/dag.typ
octavo new table compare                      # tables/compare.csv
octavo env                                    # the analysis environment (.venv, renv)

--example fills a part with a worked example; what it writes is marked (octavo:example comments, _placeholder in values, a crossed box as figure) and octavo check counts what is left. Placeholder values are fatal in octavo check. --env sets up the environment in the same step. new on an existing document adds only the files it lacks. new --json says what it made and whether the environment is still to be set up (env_needed).

2.4 Projects from earlier versions

octavo migrate --docs --dry-run    # papers/<n>/paper.md, slides/, lectures/ -> docs/<n>/<n>.md
octavo migrate --syntax --dry-run  # ::: {.session}, ::: {.slide}, .handout-only -> \session{}, \newslide, .no-slides
octavo migrate --rules --dry-run   # Octavo's sections of AGENTS.md -> the current text (shows the diff)
octavo migrate                     # a CLAUDE.md-only project's rules into AGENTS.md

All refuse an uncommitted project unless --allow-dirty. --docs keeps the document names, fixes figure paths, writes outputs/sessions and adds docs/*/ to the config. --syntax builds every document before and after and keeps the rewrite only if every output is identical. --rules replaces each <!-- octavo:section X --> part with the template’s (anything written inside it is replaced; text before the first section is kept). Old configs (papers/*/paper.md with profile / targets) still build unchanged.


3. Writing manuscripts

3.1 Front matter

---
title: Title of the Paper
subtitle: A subtitle
author: [Author One, Author Two]
institute: Example University
email: one@example.org
date: today
lang: en
outputs: [pdf, word]
abstract_word_limit: 150
---

3.2 Headings, labels and references

# Analysis {#sec-analysis}

@fig-trend shows the trend and @tbl-desc the descriptive statistics.
We estimate @eq-model (see also [-@eq-model]).

![Trend](../../assets/figures/trend.png){#fig-trend}

| Variable | Mean |
|----------|------|
| x        | 1.2  |

: Descriptive statistics {#tbl-desc}

$$
y_i = \beta_0 + \beta_1 x_i + \varepsilon_i
$$ {#eq-model}

3.3 Numbered blocks

::: {.question #question-why title="Why is government the main actor?"}
Why is government at the centre of public policy?
:::

::: nb
Unnumbered blocks need no label.
:::

::: {.restate #question-why}
:::

::: {.list-of .question}
:::
Kinds Numbering
case, question one sequence (Case 1.1, Question 1.2)
theorem, lemma, proposition, corollary, definition, example another sequence
aside, nb, memo, remark none
'theorem_envs': {
    'case': 'Example case',
    'claim': {'name': {'ja': '主張', 'en': 'Claim'}, 'counter': 'case'},
    'hint': {'name': 'Hint', 'numbered': False},
},

3.4 Math

The estimate is $\hat\beta = {{coef_x:.3f}}$, with $y_i \sim \mathcal{N}(\mu, \sigma^2)$.

$$
\begin{aligned}
y_i &= \beta_0 + \beta_1 x_i + \varepsilon_i \\
\operatorname{Var}(\varepsilon_i) &= \sigma^2
\end{aligned}
$$

3.5 Figures

![Officials by level of government[^src]](../assets/figures/staff.png){#fig-staff width=70%}

::: {.figure-note}
Source: National Personnel Authority, *Annual Report 2025*.[^checked]
:::

[^src]: Full-time staff only.
[^checked]: Retrieved 1 October 2026.
#diagram(
  (
    z: (2, 0, [Background $Z$]),
    x: (0, 1.6, [Education $X$]),
    y: (4, 1.6, [Income $Y$]),
  ),
  (("z", "x"), ("z", "y"), ("x", "y")),
)

3.6 Tables

3.7 Citations

3.8 Content for some outputs only

::: {.no-slides}
Fill-in-the-blank space and detailed footnotes: handout only.
:::

::: {.slides-only}
Figures and short prompts: slides only.
:::
Class Kept in
.no-slides everything but slides and scripts (the usual “handout only”)
.slides-only (.slide-only) slides and scripts
.pdf-only, .word-only, .tex-only, .poster-only that output only
.print-only PDF, Word, LaTeX
.screen-only / .no-screen the projected deck only / everything but it (with slides-handout)
.no-anonymous / .anonymous-only dropped / kept under --anonymous
.on-slides / .on-poster picked for slides / posters when slides_select / poster_select is marked

3.9 Page breaks, slide breaks and slide titles

## A long heading for the handout {slide-title="Short title"}

\newslide

### A heading that stays on the same slide {.same-slide}

\newslide{Another title}

3.10 Sessions

\session{Session 3: policy and government} {#third subtitle="Public policy" date="2026-10-14"}

3.11 Footnotes, URLs, appendix

3.12 One sentence to a line

octavo fmt my-paper            # shows where lines break mid-sentence or hold several sentences
octavo fmt my-paper --apply    # rewrites them

fmt joins lines broken inside a sentence and breaks a Japanese line after each 。 outside brackets. It leaves code, math, comments, tables, headings, ::: lines and hard breaks alone, and never splits English lines.


4. Outputs

4.1 Outputs

Output Makes Lands in
pdf a PDF: a paper’s layout if main.typ is beside the manuscript, otherwise the built-in A4 handout build/pdf/<name>.pdf (a paper: build/pdf/<name>/main.pdf and a copy at build/pdf/<name>.pdf)
slides slides (one deck per session) build/slides/<name>.pdf, <name>-slides-<session>.pdf
slides-handout the decks without .screen-only, with .no-screen build/slides-handout/
script each slide shrunk, with its ::: notes (A4) build/script/<name>-notes.pdf, <name>-notes-<session>.pdf
poster a poster build/poster/<name>.pdf
word Word build/word/<name>.docx
tex LaTeX source (main.tex beside the manuscript if any); needs TeX to compile build/tex/
beamer LaTeX slides build/beamer/
octavo build                          # every document, every output it names
octavo build my-paper --to word       # one document, one output
octavo build --compile                # typeset to PDF as well
octavo build course-third --to slides # one session's deck
octavo build --to all --compile
octavo targets                        # the output names

4.2 Papers

4.3 Lecture handouts

A document whose pdf has no main.typ gets the built-in A4 layout: a cover, the contents on pages i, ii, …, the body from page 1, type after jsarticle (11pt, 36 lines a page), BIZ UDGothic with Inter.

Key Default What
first_section 1 0 numbers a guidance session “0”
pagebreak 'session' 'section': at each #; None: only at markers
font, fontsize BIZ UDGothic + Inter, '11pt'
toc on with sessions the contents
date_format '%B %-d, %Y' (Japanese '%Y年%-m月%-d日')
appendix_separate True the appendix as <name>-appendix.pdf
appendix_after_references True False: text, appendix, references

One PDF per session, cut from the typeset whole (page numbers, contents and numbers stay those of the whole):

octavo build course --sessions                     # build/handouts/course-<session>.pdf
octavo extract course --session third --cover      # one or more sessions (comma-separated), with the cover
octavo extract course --pages 12-19                # by printed page numbers

4.4 Slides

Key Default What
slides_aspect '16-9' '4-3'
slides_accent '#0e2f92' None: black only
slides_numbering None heading numbers ('1.', '1.1')
slides_section_slides False a divider slide per # section
slides_running_header True the section in the top-left corner
slides_font BIZ UDGothic + Inter
slides_bibliography lecture decks slide, talks off slide, off, sheet (an A4 <deck>-references.pdf), both
slides_select all marked: only .on-slides content and the headings above it

The older names typst_slides_* are read too. Figures fill the room left on their slide.

4.5 Posters

---
title: Counting how policy is made
author: [Author One, Author Two]
institute: Example University
event: Example Conference 2026
date: 2026-10-14
logo: ../../figures/logo.png
qr: https://example.org/paper
qr_label: The paper
outputs: [poster]
poster_grid: 3x2
poster_rows: [2, 1]
---

# Question

# Results {span=2}

# Notes {cell="3,2"}
Write Means
each # heading one cell, filling the grid from the top left
{span=2} / {rows=2} two columns wide / two rows high
{cell="3,2"} column 3, row 2 (from 1)
poster_size a0 (default), a1, a2, b0, b1 (JIS B), or 1189x841mm
poster_orientation portrait (default), landscape
poster_grid, poster_rows columns x rows (2x3), the rows’ height ratios
logo, qr, qr_label logos on the left of the title band, a QR code on the right
poster_select: marked only .on-poster content (a poster from a paper: outputs: [pdf, poster])
poster_font the typeface

Content that does not fit its cell is reported when building and marked in red. A cell that cannot be placed stops the build.

4.6 Word and LaTeX


5. Settings

5.1 octavo.config.py

CONFIG = {...}, a Python dict. Unknown keys are warned about. The main keys:

Key What
documents which files are documents ({'docs': {'src': 'docs/*/'}})
analysis the .qmd files: [{'src': 'analysis/*.qmd'}], with manual, deps
bib_file the .bib
csl, csl_locale citation style (an ID from zotero.org/styles, or a path), its locale
japanese_citation_form, citations_by_language Japanese works in the bibliography (7.1)
lang the project’s language (ja / en)
meta title, author, affiliation, email defaults
crossref_numbering 'document' / 'section'
theorem_envs numbered block kinds
figure_width, figure_ext figures
values_dir, figure_dir, table_dir, figure_src_dir, table_src_dir the folders
value_float_format, value_thousands_sep how numbers look
word_limit, char_limit, abstract_word_limit, abstract_char_limit submission limits
anonymous_drop_meta front-matter keys --anonymous removes
lint_accepted, bib_accepted accepted findings
slides_*, poster_*, first_section, pagebreak, font, fontsize, toc, date_format looks (4)
appendix_separate, appendix_after_references the appendix
docx_reference Word styles
zotero_collection, zotero_linked, zotero_write Zotero (7.2)
writing, writing_off writing-rule packs (9)
replication_exclude left out of the replication package
out_dirs where outputs land

5.2 Settings for one document

These keys can also go in a manuscript’s front matter, and then apply to it alone (the same names as in the config): outputs, sessions, lang, csl, csl_locale, reference_section_title, japanese_citation_form, citations_by_language, crossref_numbering, word_limit, char_limit, abstract_word_limit, abstract_char_limit, slides_*, poster_*, first_section, pagebreak, font, fontsize, toc, date_format, appendix_separate, appendix_after_references, writing, writing_off. A misspelt key is reported when building. A build prints [settings] from the manuscript: ….

5.3 Changing settings from the command line

octavo config                                  # the settings and their values
octavo config set slides_aspect 4-3            # one line in octavo.config.py
octavo config unset slides_aspect
octavo config --doc my-paper set word_limit 8000   # one line in the manuscript's front matter

set validates before writing and refuses a value that spans several lines.


6. The analysis

6.1 How results reach the manuscript

analysis/*.qmd  --quarto-->  assets/values/<qmd>.json   {{name}} in the text
                             assets/figures/*.pdf|png   figures
                             assets/tables/*            tables

Octavo never parses R or reads Quarto’s output: it runs quarto render and reads these files. Each .qmd owns assets/values/<its name>.json; the manuscript sees all of them (a name in two files: warned, the later wins). A name with no value stays as {{name}} and is reported.

6.2 The R helper (octavo.R)

source(if (file.exists("octavo.R")) "octavo.R" else file.path("analysis", "octavo.R"))

ov_value("n_obs", nrow(d))              # {{n_obs}}
ov_value("coef_x", coef(m)[["x"]])      # {{coef_x}}, {{coef_x:.2f}}
ov_value("p_x", ov_pval(pv))            # ".023", "< .001"
ov_figure(p, "trend")                   # a ggplot or a function that draws -> trend.pdf, trend.png
ov_table(tab, "summary")                # a data frame -> summary.typ, .tex, .md

6.3 The Python helper (octavo_helper.py)

ov_value("n_obs", len(d))
ov_value("coef_x", model.params["x"])
ov_figure(fig, "trend")
ov_table(tab, "summary")

Same names and files as R; ov_table groups count from 0. ov_style() (before making a figure) sets a Japanese font and embeddable fonts. Needs the standard library only (matplotlib, pandas when handed one). requirements.txt gets ipykernel, nbformat, nbclient, pyyaml.

6.4 How numbers look

Value Example Shown as
integer nrow(d), len(d) 1,523
double coef(m)[["x"]] 0.342 (value_float_format, 3 places)
text ov_pval(p) as it is

{{name:.2f}} in the text or ov_value(..., fmt = ".2f") sets the format. A whole double still shows decimals (2.000): that is deliberate.

6.5 When the analysis runs

octavo analysis                   # which .qmd are out of date
octavo analysis run               # run every out-of-date one (manual ones too)
octavo analysis run analysis/02-model.qmd   # this one, even if up to date
octavo analysis mark-fresh analysis/00-fetch.qmd   # record it as up to date without running
octavo analysis run --keep-cache   # also keep the results, for analysis prose
octavo analysis prose analysis/02-model.qmd   # only the prose changed: update the HTML, run no code
octavo check values --diff        # numbers in the text that changed at the last run
octavo check values --diff my-paper-v1-submitted   # since a git tag
'analysis': [
    {'src': 'analysis/01-clean.qmd', 'manual': True},
    {'src': 'analysis/02-model.qmd', 'deps': ['data/derived/*']},
],
---
title: "Fetch the raw data"
octavo:
  manual: true
---

```{r}
out <- "../data/raw/survey.csv"
if (file.exists(out)) knitr::knit_exit("Already fetched. Move the file away to fetch again.")
```

```{r}
download.file("https://example.org/survey.csv", out)
```

6.6 The environment and packages

octavo env                          # .venv (uv) from requirements.txt; renv with knitr, rmarkdown and every package the .qmd use
octavo analysis missing analysis/model.qmd   # R packages it uses that are not installed
octavo analysis install analysis/model.qmd   # install them into the project's renv

6.7 Projects of .qmd files alone, other languages

A project need not have a manuscript: a report written in Quarto lives in analysis/, and octavo env / octavo analysis run look after it; octavo check and octavo build do not count the missing manuscript as a problem. Its output lands beside the .qmd. To use the manuscripts’ typefaces in a Quarto Typst PDF:

format:
  typst:
    papersize: a4
    include-in-header:
      text: |
        #set text(font: ((name: "Libertinus Serif", covers: "latin-in-cjk"), "BIZ UDMincho", "Noto Serif CJK JP"))

Any other language works by writing {"name": value} into assets/values/<anything>.json, figures (.pdf and .png) into assets/figures/, tables into assets/tables/.


7. Bibliography, Zotero and Priorwork

7.1 The .bib and the style

7.2 Zotero

octavo bib collections            # the collections, nested
octavo bib pull                   # the .bib from zotero_collection (and the collections under it)
octavo bib login --user-id 1234567   # the web API key, read from stdin
octavo bib tidy                   # the plan: cited but not in the main collection, in it but cited nowhere
octavo bib tidy --apply --keys smith2020,doe2019

7.3 Priorwork

Priorwork (a separate tool for literature surveys) writes into literature/priorwork/: <survey>.md (the text, citing with the .bib’s keys), <survey>.json (a manifest: draft, synthesis or complete) and <survey>.cards.json (a card per paper). It writes nowhere else.

octavo priorwork                  # what arrived
octavo priorwork resolve --dry-run   # @PRIORWORK:<doi> -> the .bib key, once the paper is in the .bib
octavo priorwork link             # the survey's collection -> zotero_linked
octavo priorwork cards --json     # the cards, by citation key
octavo priorwork ask --workspace <folder> --kind start
octavo priorwork requests

Taking a delivery into the manuscript: read the text with the manuscript; for a complete delivery each addition quotes the sentence it belongs to (“Where” / 「場所」). Propose where each part goes and wait for the user’s answer; keep the citations as written; never edit the inbox. octavo check cites lists PRIORWORK: marks apart, and notes cited papers that Priorwork read only in the abstract.


8. Checking and after submitting

8.1 octavo check

octavo check                 # everything; non-zero exit on a fatal problem
octavo check --strict        # warnings count too
octavo check values          # {{…}} against the values
octavo check cites           # citation keys against the .bib, problems in the .bib
octavo check lint            # results typed into the text, nested lists out of line

Fatal: missing figure or table files, unresolved {{…}}, placeholder values, an analysis out of date, missing citation keys, missing or duplicate labels, submission limits exceeded. Warnings: template leftovers, unused labels, ::: problems, writing-rule names, the data fingerprints.

8.2 Blind review

::: {.no-anonymous}
Acknowledgements: this research was funded by ...
:::
octavo build my-paper --anonymous
octavo bundle my-paper --anonymous     # and checks the package for the author's name

--anonymous drops .no-anonymous, keeps .anonymous-only, removes anonymous_drop_meta keys and switches main.typ’s title block (flags.typ). The next ordinary build switches back.

8.3 After sending it

octavo bundle my-paper                 # submission-my-paper.zip, flattened for a journal system
octavo review returned.docx            # a coauthor's tracked changes and comments
octavo release my-paper v1-submitted   # tag my-paper-v1-submitted, the PDF on a GitHub Release (gh)
octavo data hash                       # data/HASHES.json
octavo data status                     # has data/ changed since
octavo data track data/raw/typed.csv   # let git take one file under data/
octavo bundle --replication            # analysis, values, figures, tables, config, fingerprints
octavo bundle --replication --standalone  # the same, with .qmd files that do not need the helpers
octavo bundle --replication --standalone --verify   # and run the package itself to check it

9. Writing rules for AI assistants

The user’s rules on how they write, and a bank of trusted phrasings, are Markdown files in packs. Before writing or revising prose, run octavo writing --file <file> --line <line> --print and follow what it prints. How to set packs up, with examples: Writing support.

9.1 Packs and files

9.2 What a manuscript reads

9.3 Commands

octavo writing
octavo writing --for my-paper --section results --print
octavo writing --file docs/my-paper/my-paper.md --line 120 --print
octavo writing --for my-paper --kind field/results#the-main-result --print
octavo outline my-paper --paragraphs

9.4 Paragraph marks and questions

9.5 JSON

9.6 When nothing comes out


10. Templates

octavo template list                              # every template and where the one in use comes from
octavo template copy slides/typst-slides.typ      # into templates/ of this project
octavo template copy paper/en/main.typ --user     # into ~/.config/octavo/templates/
octavo template diff slides/typst-slides.typ      # what changed in Octavo's own since the copy

Lookup: the project’s templates/, then ~/.config/octavo/templates/, then Octavo’s; a copy replaces the whole file.

Template For
slides/typst-slides.typ, slides/typst-notes.typ decks and scripts
poster/typst-poster.typ posters
handout/handout.typ the A4 handout
paper/article.typ, paper/<lang>/main.typ papers
manuscripts/<lang>/*.md what octavo new writes
typst/crossref.typ how numbers and references look
citations/japanese.lua Japanese works in the bibliography
claude/<lang>/*.md the sections of AGENTS.md

Fonts: papers BIZ UDMincho + Libertinus Serif, handouts and slides BIZ UDGothic + Inter (Noto CJK, Hiragino or Yu as fallbacks).


11. Commands

octavo build [documents...] [--to outputs] [--compile] [--appendix] [--sessions] [--no-analysis] [--anonymous] [--json]
octavo watch [documents...] [--to outputs]
octavo extract <lecture> [--session IDS] [--pages 12-19] [--cover]
octavo documents [--json]
octavo outline [documents...] [--paragraphs] [--json]
octavo targets
octavo config [--doc NAME] [set KEY VALUE | unset KEY] [--json]
octavo analysis [--json] | run [QMD...] [--keep-cache] | mark-fresh QMD | prose QMD | missing QMD | install QMD
octavo env
octavo check [--strict] [--anonymous] [--json]
octavo check values [--unused] [--diff [REF]] [--json]
octavo check cites [--list] [--unused] [--json]
octavo check lint [--json]
octavo fmt [name] [--apply]
octavo bib pull|tidy|collections|login
octavo csl get|list|which [ID]
octavo priorwork [resolve|link|cards|ask|requests] [--dry-run] [--json]
octavo writing [--for DOC --section S | --file F --line N] [--kind KIND] [--print] [--json]
octavo bundle [name] [--anonymous] [--replication] [--with-raw-data] [--standalone] [--lang en|ja] [--verify]
octavo review returned.docx [--json]
octavo release <document> <label>
octavo data hash|status|track
octavo init <dir> [--lang ja|en] [--with PARTS | --all] [--engine r|python] [--example] [--env]
octavo new paper|slides|lecture|poster|analysis|figure|table <name> [--lang ja|en] [--example] [--appendix] [--tex] [--engine r|python] [--env] [--json]
octavo template list|copy|diff [name] [--user]
octavo migrate [--docs|--syntax|--rules] [--dry-run] [--allow-dirty]
octavo setup [--with-tex] [--check] [--r-editor]
octavo doctor [--json]
octavo selftest
octavo docs [section] [--search WORD] [--all] [--lang ja|en]

The older names octavo values, octavo checkbib, octavo lint and octavo reference-docx still work. Messages follow OCTAVO_LANG (ja / en), else the system’s language.


12. When something goes wrong