Agent skill

Write Paper

by frenzymath in frenzymath/Danus

Turn a project's verified fact graph into a publishable LaTeX paper in a configurable house style — a standalone amsart .tex with a real bibliography, compiled to PDF.

Apache-2.0Auto-check passedDocuments & Office

Install Write Paper

skills CLI
$ npx skills add frenzymath/Danus --skill write-paper -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install frenzymath/Danus write-paper --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/frenzymath/Danus.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/write-paper .claude/skills/write-paper && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
write-paper
GitHub stars
476
Token cost
~18k tokens
SKILL.md length
9,547 words
Files
8
Skills in repo
16
Repo updated
First seen
Licence
Apache-2.0

At a glance

Turn a project's verified fact graph into a publishable LaTeX paper in a configurable house style — a standalone amsart .tex with a real bibliography, compiled to PDF.

  • Works in 7 steps: PROJECT_BRIEF — interview the operator… → Seed the reference ledger → Write (PAPER_WRITER) — produce main.tex → …
  • A projects target theorem is established and the operator wants the paper
  • SKILL.md covers When to use, Source of content: the fact…, Paper workspace: /paper/… and Style source, plus 6 more sections
  • Runs Shell and Python scripts from its folder; calls bash, python3 and codex; reaches arxiv.org

What it does

Write Paper is an agent skill from frenzymath/Danus. Turn a project's verified fact graph into a publishable LaTeX paper in a configurable house style — a standalone amsart .tex with a real bibliography, compiled to PDF. Use when a project's target theorem is established and the operator wants the paper, or asks to write/revise/audit references for the paper for a project. NOT human-summary (a reader-facing progress report with no bibliography); this is the publication artifact, with verified citations, headed for arXiv / a LaTeX git repo.

Its SKILL.md is about 18k tokens, which your agent loads only when the skill is triggered. The skill folder holds 9 other files (for example `README.md`, `driver/anchors_stale.sh` and `driver/compile_verify.sh`).

It sits in Documents & Office, covering LaTeX, Citation management and Academic paper search. It works with LaTeX, Git and arXiv. The repository describes itself as: Orchestrating Mathematical Reasoning Agents with Fact-Graph Memory. The licence is Apache-2.0.

When your agent uses it

  • A projects target theorem is established and the operator wants the paper
  • Asks to write/revise/audit references for the paper for a project

Example prompts

  • “/write-paper”

Requirements

  • Python 3
  • A Bash shell

Workflow steps

7 steps, taken from the step headings in SKILL.md.

  1. PROJECT_BRIEF — interview the operator (do not invent)
  2. Seed the reference ledger
  3. Write (PAPER_WRITER) — produce main.tex
  4. Compile-verify (hard gate)
  5. Reference audit (REFERENCE_AUDITOR) — FLAG, never fabricate
  6. Revise (PAPER_REVISER) — on compile failures, verifier fixes, operator annotations, or a math wrong
  7. Deliver + (operator fork) push

What it can do on your machine

Read from SKILL.md and the folder at commit 6d92e8d. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships script files (Shell and Python), which the agent can run.

    Shell commands in SKILL.md call:

    • bash
    • python3
    • codex

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • arxiv.org

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Write Paper loads about 18k tokens when it runs. Until then it costs about 126 tokens; SKILL.md has 9,547 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~126
When it runs · the whole SKILL.md, loaded when a task matches
~18k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from frenzymath/Danus at commit 6d92e8d, republished under its Apache-2.0 licence (© frenzymath). 9,547 words, ~17,725 tokens.

Download SKILL.mdSave it as .claude/skills/write-paper/SKILL.md (or your agent's skills folder). This skill also uses 7 other files; get the full folder from GitHub.
name
write-paper
description
Turn a project's verified fact graph into a publishable LaTeX paper in a configurable house style — a standalone amsart .tex with a real bibliography, compiled to PDF. Use when a project's target theorem is established and the operator wants the paper, or asks to write/revise/audit references for the paper for a project. NOT human-summary (a reader-facing progress report with no bibliography); this is the publication artifact, with verified citations, headed for arXiv / a LaTeX git repo.

write-paper — fact graph → publishable paper

You are the main agent. This skill turns a project's verified fact graph into a publishable LaTeX paper in a configurable house style: a standalone \documentclass{amsart} .tex with a real manual bibliography, compiled to a clean PDF, ready for arXiv / an Overleaf (or other) LaTeX git repo. It is the publication sibling of human-summary (a reader-facing progress report with no bibliography).

The heavy LaTeX work is delegated to a local codex at extra-high (xhigh) reasoning — the same codex machinery the workers and the verify service already use. The writer, auditor, verifier, and reviser roles are wrapped behind the write-paper MCP service (tools paper_write / reference_audit / reference_verify / paper_revise): you call them with structured args, the tool assembles each role's prompt internally (so the style guide and fact-graph bytes never enter your context) and drives the codex with each role isolated by construction. The reference chain is auditor (offline, flags) → verifier (online, checks) → reviser (edits). You orchestrate the stages, call these tools, gate on compilation, and surface the one or two real decisions to the operator.

When to use

  • A project's target theorem is established in the fact graph and the operator wants the paper.
  • The operator asks to write, revise, audit references for, or push the paper for a project.

Do not use it for the progress report (human-summary) or the internal strategy synthesis (elaboration).

Source of content: the fact graph (never your memory)

Build the mathematics from the project's verified facts (<project>/fact_graph/facts/*.md) and the verbatim goal in <project>/PROBLEM.md — exactly as human-summary does, but for publication:

  • Each fact's ## statement is fully-quantified and self-contained → render into the paper's theorems/propositions (clean LaTeX, do not paraphrase loosely). Its ## proof is the argument; ## intuition feeds a proof sketch where useful.
  • Load-bearing facts first: high dependency depth (headline results) and high in-degree (key lemmas). predecessors give you the internal Theorem~\ref{} cross-reference structure with zero invention.
  • Citations come structured, from the source. Each fact's external_refs (key / authors / title / arxiv / year / cited_for) records the published results its proof cited. driver/seed_ledger.py aggregates them across the project into the starting REFERENCE_LEDGER.md. Do not re-mine citations from prose — that is the #1 failure mode (hallucinated references).
  • Preserve all mathematics. Invent nothing — no assumptions, lemmas, citations, theorem labels, or definitions that are not in the fact graph. If a step is unclear, flag [GAP: ...], do not smooth it over.

Paper workspace: <project>/paper/ (default) — and MULTIPLE papers per project

Per-project, alongside fact_graph/ and global_memory/:

<project>/paper/                 # the DEFAULT paper (legacy paths — unchanged)
  PROJECT_BRIEF.md      # per-paper framing (interview the operator — see below)
  REFERENCE_LEDGER.md   # seeded from external_refs, verified by the auditor
  REVISION_LOG.md       # append-only round history
  main.tex / main.pdf   # the paper
<project>/TARGET.md              # the DEFAULT paper's finalized target (danus finalize)

Seed the workspace from templates/ (copy *.template → the real names) if it does not exist yet.

One project can hold MULTIPLE papers (e.g. a main theorem paper + a companion, or several theorems each written up separately). Every paper_* tool and danus finalize takes an optional paper_id:

  • paper_id omitted / "main" → the DEFAULT paper on the legacy paths above (<project>/paper/ + <project>/TARGET.md). Existing single-paper projects are byte-for-byte unchanged.
  • any other paper_id (e.g. thmB) → an isolated workspace <project>/papers/<paper_id>/ with its own PROJECT_BRIEF.md / REFERENCE_LEDGER.md / REVISION_LOG.md / main.tex / TARGET.md. So N papers never collide — each has its own files.

There is one fact graph per project (<project>/fact_graph/); papers never fork or re-filter it. A paper's facts are simply the transitive-predecessor closure of its own headline set — the SAME closure primitive the single-paper pipeline already uses, merely rooted at that paper's recorded target. The three shapes are one model — a list of {paper_id, headline_fact_ids}:

  • 1 paper / 1 theorem — the default: one entry, paper_id="main".
  • N papers / 1 theorem each — N entries, each a distinct paper_id and a single-fact headline; written in separate workspaces (no overwrite).
  • N papers / one multi-theorem paper — an entry whose headline_fact_ids is a LIST of several targets; its fact set is the union closure of that list.

paper_id must be a single safe path segment (same validation as a project name); it cannot escape the project dir.

Style source

Two generic, self-contained layers under style/, neither naming an author or a field:

  • style/STYLE_GUIDE.md — the compact baseline house style (binding for voice: macros, theorem/proof shape, citations, cross-references, sentence-level rules).
  • style/PAPER_STRUCTURE.md — the per-section content plan (binding for structure: what each part of the paper contains, by length tier — abstract, introduction, preliminaries, body, proofs, acknowledgements, bibliography). Field-neutral; uses placeholders, not any specific area's conventions.

Both are plain Markdown the operator may edit to encode their own preferences.

Imitating your own past papers is strictly optional — an add-on for authors who already have published papers, not a dependency:

  • style/anchors/ — optional exemplar papers the operator drops in (one folder per paper, with its .tex) for the STYLE_DISTILLER to learn from (feeding the unified STYLE_GUIDE.md, which is the writer's single source of voice) and, optionally, for ONE of them — named deterministically by the brief's structural_exemplar field — to be imitated for structure. Empty by default. The skill produces a complete, compilable paper from the two generic guides alone — anchors only make the output sound more like the operator's own writing. The writer never "picks the closest" anchor: voice is the distilled guide (all anchors), structure is the single brief-named exemplar (or none).

The role prompts under roles/ are generic and read directly; there is no machine- or author-specific overlay.

Per-call run logs (diagnostics)

Every paper_* tool returns a log_path and writes a full on-disk diagnostic record there — the complete assembled prompt, codex's full stdout and full stderr (not just the tail), the honest result, the tool's post-processing decisions, and the returned envelope. On a non-ok or surprising result, read log_path for the full assembled prompt + codex stdout/stderr + tool decisions before retrying or reporting — it lets you localize the failure (prompt vs codex vs tool logic) instead of retrying blind. The small envelope (status / returncode / stderr_tail / flags / paths) is unchanged; log_path is additive. Run logs live under the paper's own .runs/ (default <project>/paper/.runs/; a non-default paper → <project>/papers/<paper_id>/.runs/), gitignored; set DANUS_WRITE_PAPER_RUN_LOG=0 to opt out (then log_path is None).

Editorial quality bar — write WELL, not LONG (your judgment, applied at every stage)

You are the paper's editor. There is no hard length cap — some results genuinely need many pages, and a fixed ceiling would only force you to mangle them. A good paper is short because it is well-SHAPED, not because it was cut to a number. Shaping it is YOUR job, and it starts before a single paper_write call:

  • A paper is not a stack of facts — and YOU are the one who prevents that. The two controls that decide the paper's shape are both yours: which facts you SELECT (the support layer) and the editorial instructions you write for each paper_write. So first UNDERSTAND the proof strategy as a whole; then select the load-bearing results that carry the argument and, in your instructions, direct that the support layer is developed in proper detail while every other fact contributes only the mathematical content the argument needs — stated, not re-narrated as a full proof. A hundred-fact closure rendered flat, one lemma after another, is exactly the failure this prevents: the writer renders what you hand it and how you tell it to, so the shape is set by your selection and instructions. Calibrate "how much detail" by the bar the whole-paper verifier uses (§5.5): a step a mathematics undergraduate could fill unaided may be abbreviated; anything they could not must be developed or cited. The mechanics of selecting and instructing are the BINDING RULE at stage 2; the levers you apply are below.
  • Length is a SYMPTOM, never a target. If a draft feels too long, the cause is almost always a violation of the shaping principle above — re-proving something citable, or re-narrating a routine computation — not "too many pages" as such. Fix the cause, not the page count. A genuinely deep result that is long AFTER honest citation and the right level of detail is SUPPOSED to be long; do not mangle it to hit a number. When one paper's worth of contribution truly spans a book, the human practice is a SERIES of papers (the paper_id split, lever #4) — an operator-initiated fork, not a cap you enforce on your own.
  • Density signals (health checks, not limits). A strong paper usually carries ~10–30 labeled results, one theorem-sized idea per section, and lemmas that exist because the narrative needs them. Red flags that the shaping broke down: 100+ labeled results; helper-lemma sprawl around a single proof; repeated setup/notation blocks; proofs that read as computation logs (transcription instead of exposition). These signal "go re-apply the shaping principle above", not "cut to N pages".
  • The levers, in order — all serve the shaping principle above, none a page count. (1) cite, don't re-prove (standard machinery → precise citations); (2) render at the right granularity — full detail on the pivots, method+outcome on routine steps, never one-lemma-per-fact; (3) curate which results the paper PRESENTS (a paper is not every fact you proved; the support-layer BINDING RULE at stage 2); (4) SPLIT into companion papers when the contribution genuinely spans more than one paper (the paper_id mechanism — a deep development becomes its own "Part II" / technical companion the main paper cites).
  • The reviser cannot globally compress — so get the granularity right the first time. Measured on a real paper: a dedicated compression-only round asked for −27K and delivered −0.5K; every "pay by compressing while adding" round under-paid while the paper inflated. A patch-style reviser executes located edits; it cannot execute global restructuring. So NEVER issue "compress the paper by N pages" — it silently under-delivers. What CAN work: YOU find specific mechanical redundancy yourself (a lemma proved twice, a definition declared twice, a duplicated setup block) and order its removal as POINT EDITS. The durable fix, though, is upstream: honest citation + right granularity when the section is first written.

The pipeline (you drive these stages in order)

0. PROJECT_BRIEF — interview the operator (do not invent)

If <project>/paper/PROJECT_BRIEF.md is absent, conduct a short interactive interview with the operator to fill it: title, audience/venue, human authors (and affiliations), which facts are the headline results, per-paper style overrides, deadline. Write their answers into the brief. Never fabricate these — they are the operator's call. (If the operator wants to skip and accept defaults, record that; the writer then emits author placeholders.)

Pre-fill headline_fact_ids from the finalized target. When you seed the brief, if the paper's TARGET.md exists (the default paper's <project>/TARGET.md, or <project>/papers/<paper_id>/TARGET.md for a non-default paper — written by danus finalize [--paper <paper_id>] when the operator approved the result as the answer), copy its fact id(s) into the brief's headline_fact_ids field so the finalized target is already the paper's headline. If TARGET.md is absent, leave the field blank and ask the operator — and know that paper_write will refuse with status="needs_target" until either the field is set or the operator runs danus finalize <project> [--paper <paper_id>] <fact_id>. The skill never guesses the target from the graph shape.

Two structured fields the brief carries (machine-read; keep the field: value shape on its own line):

  • headline_fact_ids — the fact ids that ARE this paper's target results (the theorems it foregrounds). This is the backbone of the default: the writer is given the transitive-predecessor closure of these targets — NOT every proven fact — and the reference ledger is seeded from the same closure, so the writer's facts and the bibliography agree and the auditor never sees phantom rows for side lemmas the paper never cites. Pre-fill it from <project>/TARGET.md if present, else ask the operator explicitly. If it is left blank AND no TARGET.md is recorded, the target is UNSET and paper_write refuses (needs_target) rather than guessing — run danus finalize <project> <fact_id> to record the target first.
  • structural_exemplar — optional: the name of ONE folder under style/anchors/ whose STRUCTURE this paper should imitate. Voice always comes from the unified STYLE_GUIDE.md; this names only the single structural exemplar. Blank = none. If the operator already has anchors, ask which (if any) to imitate structurally. If style/anchors/ is empty, still offer once — "you can drop a few of your own papers into style/anchors/ now to match your writing voice; want to? (a complete paper is produced either way)" — so the capability isn't hidden behind an empty folder. If they add some, run Stage 1a to distil them before writing.
1. Seed the reference ledger
bash
python3 .../write-paper/driver/seed_ledger.py <project_dir> --headline <headline_fact_ids> --out <project>/paper/REFERENCE_LEDGER.md
# multiple papers: add --paper <paper_id> to scope the closure to that paper's
# recorded target and (with no --out) write the ledger into its own workspace:
python3 .../write-paper/driver/seed_ledger.py <project_dir> --paper <paper_id>

This aggregates the external_refs of the target-closure facts into unverified rows. Pass the same headline_fact_ids you recorded in the brief (stage 0) as --headline so the ledger's closure equals the writer's closure (one closure, shared): the ledger then lists only references the paper's facts actually cite — no phantom rows for proven-but-unused side lemmas. Omit --headline to let the script resolve it identically (brief field → else the finalized <project>/TARGET.md). If no target is recorded at all, the seed refuses (matching the writer). (Verification is stage 4; --all-facts restores the legacy all-facts seeding if ever needed.)

1a. Style preflight — distil the anchors if they changed (presence-triggered, once, operator-gated)

Before the writer stage, check whether the operator's own papers under style/anchors/ need to be distilled into the unified STYLE_GUIDE.md. This is the only automatic trigger for the STYLE_DISTILLER — without it, papers an operator drops into anchors/ are silently ignored.

Trigger rule (all offline; the distiller only PROPOSES, never auto-applies):

  • If style/anchors/ is empty → skip (the two generic guides produce a complete paper; nothing to distil).
  • If anchors/ is non-empty AND stale — its newest content is newer than the style/.distilled_at marker, or the marker is absent — then run the STYLE_DISTILLER (roles/STYLE_DISTILLER_PROMPT.md) to propose STYLE_GUIDE.md updates. Present the proposals to the operator; on accept, apply the accepted edits to STYLE_GUIDE.md and touch style/.distilled_at (record the distill time). On reject, still touch .distilled_at only if the operator says the current guide stands (so a rejected-but-reviewed anchor set is not re-proposed every run) — otherwise leave the marker so it re-triggers.
  • If anchors/ is unchanged since the last distill (nothing newer than .distilled_at) → skip.

A tiny helper compares mtimes so the check stays clean:

bash
bash .../write-paper/driver/anchors_stale.sh <skill_dir>   # rc 0 = stale (distil), rc 1 = fresh/empty (skip)

Why operator-gated, never auto-applied: the distiller edits the guide that governs every future paper; a bad distill would silently corrupt them all. The distiller proposes; the operator accepts; only then does STYLE_GUIDE.md change. This step is still optional in spirit — with no anchors it is a no-op — but when anchors exist and changed, it runs so they are not ignored.

2. Write (PAPER_WRITER) — produce main.tex

Call the paper_write tool (the write-paper MCP service). You do not build the prompt by hand and you do not read the style guide, the structure plan, or the fact graph into your own context — the tool assembles all of that internally and drives the codex, so the large bytes never enter your window.

First, curate — MANDATORY, and it is YOUR job, at EVERY level. paper_subgraph.

BINDING RULE — never hand a writer the whole closure. Every single paper_write call must be given a hand-picked support layer via fact_ids: the few key load-bearing results that call should PRESENT. It is forbidden to pass the full closure (or to omit fact_ids so the tool embeds it) unless the closure is single-digit (< 10 facts). Dumping all facts on one writer call is the root cause of overflow, the chunked fallback, and flat/bloated output — do not do it. If you think "this piece is small enough to just send its facts," check the count first; if it is ≥ 10, curate.

This applies RECURSIVELY / fractally. A hard sub-result written as its own lemma-chapter is STILL a paper_write call, so it ALSO gets a curated support layer — not that sub-result's whole closure. If that chapter's support layer leans on a deeper load-bearing result, that deeper result becomes its OWN curated paper_write call (its own chapter/sub-paper), which YOU then compose in (a written body slotted as a section — you do the stitching; see below). The paper is a TREE of curated single-pass writes you design and assemble, never one writer call swallowing a big fact set. "Pick the support layer" is the intelligence that stays with you at every node of that tree; the writer only renders what you hand it.

The target's full transitive closure can be hundreds of facts; embedding every proof would overflow a single writer pass. So do what a human author does: read a compact skeleton and SELECT the load-bearing subset to write up. Call

paper_subgraph(project=<project>, headline=[<target fact ids>|omit], paper_id=<paper_id|omit>)

It returns, deterministically (no codex, no writes), {status, headline, headline_source, count, facts} where each facts[i] is {id, statement (one-line), predecessors, dependents (in-closure in-degree — higher = more load-bearing), glossary_introduces} in topological order. Read it, pick the results the paper should PRESENT (the headline theorems + the lemmas their proofs actually turn on — NOT every granular intermediate step; those get cited), and write short editorial instructions (how to section, what to foreground — and encode the editorial shaping principle: support layer in detail, everything else stated minimally; see the editorial bar). An unset target → the same needs_target refusal as paper_write (run danus finalize first).

Then write — pass your selection + direction to paper_write:

paper_write(project=<project>, headline=[<target fact ids>], paper_id=<paper_id|omit for default>,
            fact_ids=[<the load-bearing subset you selected>], instructions="<sectioning / emphasis>")
  • fact_ids — the subset from paper_subgraph to PRESENT in full. The tool embeds only these (statement + proof) plus their direct-predecessor statements as \ref/\cite context (the granular lemmas are cited, not reproduced) — so a curated paper fits ONE pass. Omit fact_ids ONLY when the closure is single-digit (< 10 facts); on any larger closure omitting it (⇒ whole-closure embedding ⇒ chunked fallback ⇒ bloat) is the mistake the BINDING RULE above forbids. Unknown ids → status="bad_fact_ids" (no paper); ids outside the closure are kept with a fact_id_warnings note.

  • instructions — your editorial direction, embedded verbatim as an authoritative MAIN_AGENT_INSTRUCTIONS block (wins over the writer's default structure, never over the PRIME DIRECTIVE / style voice / the mathematics).

  • project — the project name (resolved under DANUS_AGENTS_ROOT).

  • paper_id — WHICH paper in the project (multiple papers per project; one fact graph). Omit / "main" → the default paper on the legacy <project>/paper/ paths; any other id → the isolated <project>/papers/<paper_id>/ workspace. The brief / ledger / TARGET.md the tool reads are rooted at that paper's workspace.

  • headline — the paper's target fact ids (the headline results). This is the DEFAULT scoping: the tool embeds only the target's transitive-predecessor closure, in topological order, with zero invention — NOT every proven fact. Omit headline to let the tool resolve it the same way the ledger did: the brief's headline_fact_ids field, else the finalized <project>/TARGET.md. The tool returns headline (the resolved target ids used) and headline_source (arg / brief / target). If the target is UNSET (no arg, no brief field, no TARGET.md) the tool returns status="needs_target" with a candidates list (the terminal facts) and writes no main.tex — run danus finalize <project> <fact_id> (or fill the brief) and call it again. It never guesses.

  • The structural exemplar is read from the brief's structural_exemplar field (one anchor to imitate for STRUCTURE); there is no per-call anchor arg. Voice always comes from the unified STYLE_GUIDE.md.

  • paper_write does NOT stop the worker swarm by default — entering write-paper does not always mean the whole problem is proven; a partial result can be written up while the swarm keeps exploring the rest. If you already stopped the swarm because the whole problem is proved (the completion rule in AGENTS.md / the main-agent contract), this fork is moot — the swarm is already down; just write the paper. Otherwise (a partial result, exploration ongoing), you decide: at the start of write-paper (Stage 0) surface the fork to the operator — "Start the paper — stop the swarm's exploration, or keep it running? (A partial result can be written up while the swarm keeps proving the rest.)" On stop → call paper_write(stop_workers=True) (or danus stop <project>); on keep → the default (no stop). When you do request a stop it is graceful (never drops an in-flight verified round), idempotent, and failure-isolated; the tool reports it in a swarm_stop field (result / noop / error / skipped).

Internally the tool embeds, in full: the role contract (roles/AGENTS.md — the PRIME DIRECTIVE) and roles/PAPER_WRITER_PROMPT.md; the unified style/STYLE_GUIDE.md (voice, distilled across all anchors) and style/PAPER_STRUCTURE.md; boilerplate/acknowledgement.md; PROJECT_BRIEF.md and the seeded REFERENCE_LEDGER.md; the fact-graph math of the target closure (each fact's ## statement / ## proof / ## intuition + the predecessor DAG, verbatim); and, iff the brief's structural_exemplar names an existing anchor, that ONE anchor as a structural exemplar. Those codex-facing fixed files (roles/, style/, boilerplate/) live under agents/skills/write-paper/, not here — the MCP reads and embeds them; you never do. It writes codex's stdout to <project>/paper/main.tex (a full \documentclass{amsart} … \end{document}, real \ref/\cite, manual \begin{thebibliography}{99}).

The tool returns a small dict — {tex_path, status, returncode, headline, headline_source, selected_facts, fact_id_warnings, gaps, stderr_tail, log_path} (or {status:"needs_target", message, candidates, log_path} when the target is unset, or {status:"bad_fact_ids", unknown_fact_ids} when a selected id is not in the graph). Honesty: it reports status="ok" only on a zero exit with non-empty output; a nonzero codex exit, an empty artifact, or a timeout is status != "ok" and nothing is written — do not treat a non-ok result as a produced paper, and a needs_target result means you must record the target first (danus finalize). headline/headline_source report which target ids were used and where they came from. The gaps list is the [GAP: ...] markers the writer left; act on them. The tool does not compile — the compile gate is stage 3.

Auto-chunking — the extreme fallback. Curation (fact_ids) is the primary answer to a large closure: a well-chosen subset writes in one pass. But if even the prompt you assembled is still over budget (DANUS_PAPER_WRITE_CHUNK_CHARS, default ~800000 chars ≈ ~200K tokens) — a huge selection, or fact_ids omitted on a giant closure — paper_write falls back automatically to chunked generation: a planning pass (one codex call on the STATEMENTS ONLY of the set being written → the fixed preamble + front matter + a section plan assigning every fact + the bibliography), then per-section fill (one codex call per section, each given THIS section's full proofs + the fixed preamble/labels + every other result's statement for \ref), then a deterministic stitch into one main.tex. When you passed fact_ids/instructions, the fallback chunks exactly that curated set and the planner honors your instructions. Each call is still a NON-AGENTIC isolated codex (empty cwd, everything embedded, no tool calls) — chunking is decided in Python and sliced by section, not an agentic retrieval writer. The result carries chunked: true and sections: <n>. Under budget → the single-pass path runs unchanged. Honesty is preserved: if the planner or any section writer returns non-ok, or a deterministic coverage check finds an assigned fact unassigned/duplicated, generation fails honestly (status="chunk_failed", failed_phase) and no main.tex is written — a partial paper is never emitted. Cross-section coherence (a \ref that does not resolve, a seam claim) is caught downstream by the compile gate (stage 3) and paper_verify_math. If chunking keeps failing, prefer selecting a smaller fact_ids subset, or split the work into multiple papers via paper_id.

Manual fallback — you assemble and drive codex yourself (last resort). If paper_write returns status="chunk_failed" (or any non-ok you cannot resolve by curating a smaller fact_ids subset or splitting into multiple papers), fall back to the flexible manual path: you write the paper by driving codex directly. This is the one place you assemble the codex prompt by hand — use your judgment (curate the facts, restructure the sections, adjust emphasis) to get past whatever the deterministic path choked on.

  1. Get the material. Read the failed run's log_path — it holds the full assembled prompt (role contract + style guide + structure + brief + ledger + the fact bodies). Reuse it as-is, or re-assemble your own from the pieces you control: your curated fact_ids (each fact's ## statement/## proof from <project>/fact_graph/facts/*.md, or via paper_subgraph), style/STYLE_GUIDE.md
    • style/PAPER_STRUCTURE.md, the brief, and the seeded REFERENCE_LEDGER.md.
  2. Drive codex yourself. The prompt is large — write it to a temp file and put it on stdin (never argv), and run the repo's codex wrapper at xhigh, read-only (the same flags the tool uses — see danus.authoring.driver):
    bash
    bin/codex exec --model "$DANUS_MAIN_MODEL" --config model_reasoning_effort=xhigh \
      --sandbox read-only --skip-git-repo-check - < /tmp/writer_prompt.md \
      > <project>/paper/main.tex          # or <project>/papers/<paper_id>/main.tex
    In this fallback the bytes DO enter your context (you read/assemble the prompt) — that is the trade for flexibility, and it is acceptable because it is the rare last resort, not the hot path.
  3. Then re-enter the gates — verification is NOT bypassed. Your hand-written main.tex goes through the SAME safety net as any tool-written one: the compile gate (stage 3), the reference audit/verify (stages 4/4.5), and paper_verify_math (stage 5.5). The manual fallback loosens only how the paper is written, never how it is verified — a hand-written paper with a broken proof is still caught and still blocks deliver.

Honesty: exactly as on the tool path, a paper is "produced" only after it compiles AND passes paper_verify_math. Never present a hand-written main.tex as done before the gates pass.

2b. DEEP theorems — the CHAPTER TREE (write chapters with the writer; never dump facts on the reviser)

When the target's load-bearing content is too deep for one curated single-pass write (hundreds of novel facts — e.g. a paper whose verifier gaps keep exposing deeper sub-lemmas), do NOT try to close it by feeding fact piles to paper_revise — an accretion of 100+ reviser-inserted lemmas produces a flat, disorganized blob and converges terribly (measured). And do NOT fall back to the auto-chunker to "handle" it. Instead, YOU author the paper as a tree of curated single-pass writes:

  1. Design the tree (you, from the paper_subgraph skeleton): a HOST frame + one chapter per deep development. The HOST's support layer is the main theorem, its direct combination inputs, and the chapter-level results — its instructions say: STATE each chapter-level result fully, with the one-sentence proof "The complete development is given in the dedicated technical section inserted below." (never a fabricated \ref), and prove ONLY the top-level assembly. Each CHAPTER is its own paper_write call with its own workspace (paper_id=ch_*), its own brief/ledger (seed_ledger.py --paper), and a curated support layer (≤ ~10 facts — the BINDING RULE applies at every node); its instructions say: ONE short intro paragraph, a setup section, the lemmas in logical order, complete proofs; any prerequisite that is a result of the larger paper is declared explicitly in the setup ("we assume, established earlier in this paper: ...") — never re-proved, never hand-waved.

  2. Write the nodes — independent paper_write calls (parallelize freely); a chapter that overflows the writer's single-response limit is a sign to SPLIT it into sub-chapters (deeper tree), not to chunk. Compile each node; run paper_verify_math(paper_id=ch_*) per chapter — its per-chapter verdict flags chapter-INTERNAL defects to fix now (a mangled induction, a dropped hypothesis) vs setup-imports (discharged at assembly, expected).

  3. Stitch (YOU are the editor — mechanical transforms only): extract each chapter's body (drop title/abstract/acks/bib), demote sectioning one level, prefix every \label/\ref with chX:, wrap as \section{<title>}\label{sec:tech-chX}, insert the chapters in topological order before the host's assembly section, merge missing \usepackages / \newtheorems / macros / \bibitems into the host (dedupe by name/key), and replace each host pointer-proof sentence with Section~\ref{sec:tech-chX}. Keep the stitch as a rerunnable script; but every post-stitch edit lives on the merged file — re-stitching discards it, so freeze the tree first, stitch once, then edit.

    Stitch pitfalls (measured in a cold-start test — read before stitching):

    • Record a manifest at design time. The pointer-proof sentences are byte-identical and carry no chapter hint; some chapters are pure support (no pointer at all). When you design the tree, record {host theorem label → chapter paper_id} and the intended insertion order in the tree-design file — don't force the assembler to re-derive it by reading statements.
    • Ordering with delivered cycles. Chapters written independently may have GENUINELY cyclic setup-imports (A assumes B's result, B assumes A's). No insertion order fixes that at stitch time — pick the order that points the most load-bearing arrows backward, and leave the residual forward edges to the seam-discharge stage (conditional restatement + later discharge).
    • "Assembly section" means the main-theorem proof section. If the host has OTHER full-proof sections that consume chapter results, they must ALSO end up AFTER the technical sections (relocate them), or the sequential verifier will reject the forward dependence.
    • Macros: dedupe by name is unsafe on semantic conflicts. Same-name macros with different bodies (e.g. \join as \operatorname{join} vs \vee) silently change a chapter's math if first-wins. Diff the bodies; on a semantic mismatch, rename in the chapter body. Preserve \renewcommand lines as-is (a chapter may legitimately renew a kernel command like \H); never downgrade them to \newcommand.
    • Packages must merge BEFORE hyperref (load-order trap); \newtheorem display-name variants for the same env — pick one.
    • Bibliography: import only \bibitems whose keys are actually \cited; first-wins on duplicate keys; DROP/replace keys the verified ledger marks rejected/unverified (a mechanical stitch happily keeps bad keys — check the ledger).
    • The compile gate does NOT catch multiply-defined labels — after namespacing, grep for duplicate \label{...} yourself.
    • Chapter residue: each chapter body still carries its own disclosure remark / \note annotations; the merged paper has N copies. Strip the duplicates at stitch time or queue them for the reviser — decide, don't ignore.
  4. Discharge the seams (reviser rounds on the merged paper, notes= trigger): rewrite every chapter setup item "established earlier in the larger paper" to a precise Theorem~\ref{...} of THIS document; replace stale [cite/blocker] notes with precise \cite of ledger-verified keys; point the host assembly's invocations at the chapter theorems. SEAM ACYCLICITY (load-bearing): the whole-paper verifier reads SEQUENTIALLY — a proof may rely only on EARLIER results. Insert chapters in dependency order, and a setup item must point BACKWARD; if a chapter genuinely needs a LATER result, restate the affected theorem as explicitly conditional ("Assume (H). Then ...") and add, at the later point where (H) is proved, the one-sentence unconditional combination. Better: prevent it upstream — write chapter briefs so each chapter assumes only EARLIER chapters' outputs.

  5. Gates as usual on the merged paper: compile + reference chain (reuse the project's VERIFIED ledger for the merged workspace) + paper_verify_math → iterate the discharge (and, for a genuinely missing development, grow the tree: one more small curated chapter, stitched in — never a fact pile to the reviser).

3. Compile-verify (hard gate)
bash
bash .../write-paper/driver/compile_verify.sh <project>/paper/main.tex

Runs the LaTeX engine (default pdflatex; xelatex/lualatex/tectonic via TEX_ENGINE); fails on any LaTeX error or any undefined citation/reference. With no TeX Live installed, TEX_ENGINE=tectonic (after bash scripts/install-tex.sh) is the zero-dependency engine. Do not proceed past a failed compile — feed the offending log lines back to a codex revise round (stage 5) and recompile. The compile is the tool's / orchestrator's gate, never the reviser's own self-check (the reviser runs in an empty cwd and cannot compile). Authority boundary: paper_write does NOT compile — run this gate once on the writer's first main.tex. paper_revise retries the compile internally (re-drives the reviser with the failing log until the .tex compiles, or fails honestly), so a paper_revise returning compile="ok" has ALREADY compiled — do not redundantly re-run this gate after it; only run it after paper_write or a hand edit. A broken .tex must never be delivered or pushed.

4. Reference audit (REFERENCE_AUDITOR) — FLAG, never fabricate

Call the reference_audit tool.

reference_audit(project=<project>, paper_id=<paper_id|omit for default>)

The tool assembles the auditor prompt (roles/AGENTS.md + roles/REFERENCE_AUDITOR_PROMPT.md + main.tex + REFERENCE_LEDGER.md — and nothing else: the auditor never sees the fact graph, the style guide, or the structure plan) and drives a codex that has no tools and no network. The auditor only flags entries it cannot vouch for; verification is reference_verify's job (Stage 4.5), not the auditor's. It returns {findings, ledger_path, status, returncode, log_path} and writes no main.tex.

Take the auditor's findings and hand them straight to reference_verify (Stage 4.5) — that is where the flagged entries get checked online. As with paper_write, a non-ok status means the audit run failed — do not treat empty findings as a clean bibliography.

4.5 Reference verify (REFERENCE_VERIFIER) — online per-entry verification

Call the reference_verify tool with the auditor's findings.

reference_verify(project=<project>, findings=<the auditor's findings text>, paper_id=<paper_id|omit for default>)

This is the online half of the reference chain — auditor (offline, flags) → verifier (online, checks) → reviser (edits), symmetric to the proving chain worker → verifier → fact_graph. The tool assembles the verifier prompt (roles/AGENTS.md + roles/REFERENCE_VERIFIER_PROMPT.md + main.tex + REFERENCE_LEDGER.md + the auditor's findings — and no fact graph, style, or structure) and drives a codex over the networked path: --dangerously-bypass-approvals-and-sandbox + the danus gateway at DANUS_ROLE=verifier (exposing only search_arxiv_theorems, minimum privilege) + codex's built-in web_search. The codex still runs in an empty cwd, so it cannot touch the project tree; its only outward reach is the gateway's read-only tool + web.

Per flagged entry it does: search_arxiv_theorems(statement/title) → best arxiv_id → open https://arxiv.org/abs/<id> for the authoritative authors/title/year + journal-ref, confirming it is the same paper (not merely "a similar theorem exists"); non-arXiv references (textbooks / old journals) → targeted web search at an authoritative source (publisher / DOI / zbMATH / DBLP). It emits one verdict object per entry (verified / corrected / rejected / unverifiable / retarget-internal) plus a one-line replacement suggestion for the reviser, writes the confirmed metadata back into REFERENCE_LEDGER.md (each promoted row marked verified-by: verifier + source_url), and returns {verdicts, ledger_path, status, returncode, stderr_tail, log_path}. It never touches main.tex — applying the replacements is the reviser's job (Stage 5). unverifiable entries keep their [cite/blocker] flag.

Honesty: the ledger is updated ONLY on an honest ok run (zero exit, non-empty output). A nonzero exit / empty output / timeout → status != "ok" and the ledger is not touched — no false promotion. A degraded/offline run whose verdicts are all unverifiable promotes nothing.

Handoff to Stage 5. Collect each verdict's one-line replacement_suggestion and pass them to paper_revise as its citation_fixes argument — that is the verify→revise seam. The reviser applies those fixes against the \bibitem/ledger keys already present (never invents metadata); any [cite/...] still needing an external source and not covered by the fixes stays a [cite/blocker] marked \note{[deferred: to reference_verify]}. The three-stage chain is: auditor flags offline → verifier verifies online + writes the ledger → reviser applies the fixes into main.tex.

5. Revise (PAPER_REVISER) — on compile failures, verifier fixes, operator annotations, or a math wrong

Call the paper_revise tool when: a compile failed; the verifier returned citation fixes to apply; the operator added \edit{...} / \note{...} editorial annotations; or paper_verify_math returned wrong (pass its located findings — and, for any re-rendered fact, the fact's verified proof — via notes; Stage 5.5 says how).

paper_revise(project=<project>, compile_log=<failing pdflatex lines>, notes=<operator direction / math findings + verified proofs>, citation_fixes=<verifier replacement suggestions>, paper_id=<paper_id|omit for default>)

The tool assembles the reviser prompt (roles/AGENTS.md + roles/PAPER_REVISER_PROMPT.md + style/STYLE_GUIDE.md + main.tex + the REVISION_LOG.md tail + the trigger you passed as compile_log / citation_fixes / notes — and no fact graph), drives a codex, and — on a clean gate — overwrites <project>/paper/main.tex. Pass the offending log lines from a failed compile as compile_log, the verifier's per-entry replacement suggestions as citation_fixes (Stage 4.5's seam), and any operator editorial direction or math findings as notes.

  • Change scope is governed by the trigger type. A compile_log → the reviser fixes only the compile errors; notes/citation_fixes → it acts only on those items; gap_fill (verifier feedback + facts to add) → it proves the supplied facts into the paper (the one trigger allowed to change formal content); no trigger → the global style-audit rewrite. (The tool prepends a MODE: line the reviser branches on.)
  • The compile is the tool's gate now, not the reviser's self-check, and paper_revise retries it internally. After the leak gate the tool compiles the revised .tex outside the reviser; if it fails, it re-drives the reviser with the failing log (carrying the same notes/citation_fixes) up to DANUS_WRITE_PAPER_COMPILE_ATTEMPTS (default 3). On success it writes main.tex and returns compile="ok" + compile_attempts. If the LaTeX engine is missing it cannot gate what it cannot run: it writes once, returns compile="skipped: no engine", and you should run the standalone compile_verify.sh when a toolchain is available. If attempts are exhausted it does not overwrite main.tex, quarantines the last attempt to main.uncompiled.tex, and returns status="compile_failed" with a log tail.
  • The revise output passes the same leak gate as paper_write. A fact_id / machinery token in the revised .tex → quarantined to main.leaky.tex, main.tex not overwritten, status="leak".
  • REVISION_LOG.md now carries the reviser's real round summary (the %%%REVISION_SUMMARY%%% section of its output), not a boilerplate stub — the tool splits the output and writes the actual summary as the log entry body (or a [degraded: ...] note if the reviser emitted no summary section).

It returns {tex_path, status, returncode, revision_log_path, leak_findings, compile, compile_attempts, stderr_tail, log_path}; on a non-ok codex status nothing is overwritten. Recompiling standalone (stage 3) after a revise round is your independent confirmation, but the tool has already gated the compile internally.

Show full SKILL.md (3,682 more words)Show less
5.5 Math-verify (WHOLE-PAPER) — re-verify the paper AS WRITTEN (HARD GATE)

Call the paper_verify_math tool. This is the gate that makes the paper — not just its facts — correct.

paper_verify_math(project=<project>, paper_id=<paper_id|omit for default>)

Why this stage exists. Each fact was verified individually before it was ever written. But the paper is a different artifact: the writer re-renders and re-stitches those facts for publication — concising, dropping "obvious" steps, adding "it suffices to…", "WLOG…", and inline reductions that were never themselves a fact. Those seams are exactly where a correct set of facts becomes an incorrect paper. A fact's earlier correct verdict does not transfer to its re-stitched paper rendering.

What the tool does. One fresh paper-math verifier codex (a dedicated role — separate from the fact-submission verifier and the reference verifier; a one-shot run, no resident service) reads the whole main.tex development in reading order plus the confirmed REFERENCE_LEDGER.md. It trusts the confirmed precise citations and scrutinizes the paper's own reasoning and self-containedness — no fact graph, no slicing. The verifier CLASSIFIES every finding it raises under one strict criterion: ignorable if a mathematics undergraduate could fill or follow the step unaided (a routine computation, a standard manipulation), must-fix for everything else (a missing definition, a load-bearing step with no derivation and no citation, an argument it cannot follow, a wrong deduction). The paper passes (correct) iff it has zero must-fix findings; any must-fix ⇒ wrong. The tool writes ONE whole-paper row to <paper>/VERIFY_LEDGER.md (only the tool writes verdict rows), carrying the must-fix findings in repair_hints and the ignorable ones in the row's ignorable field, and returns {status, verdict, repair_hints, must_fix, ignorable, ignorable_findings, body_chars, ledger_path, log_path, deliver_ok, blockers}.

Honesty: a failed verify RUN (codex error, unparseable verdict) is status="verify_error" — NOT a paper that passed; status="passed" requires an actual all-clear (no must-fix findings). Do not treat a verify_error as a clean paper.

The ignorable findings — record and surface, never chase. These are the steps the verifier judged an undergraduate could fill. They do not block deliver and they are not yours to fill on a whim: leave the text as written, keep the verifier's ignorable list verbatim in your deliver report, and — if any of them still feels worth expanding — hand THAT decision to the operator. Chasing ignorable findings to force a longer paper is exactly the transcription bloat the writing principles forbid. Your active work is the must-fix list only.

On must-fix (verdict wrong) — the verify → revise loop (you drive it, reading the ledger, not your memory). repair_hints carries the verifier's located must-fix findings (which theorem/paragraph, what is broken). The verifier has already done the ignorable-vs-real classification; you do not re-litigate it — you ACT on each must-fix. For each:

−1. A must-fix means there is a genuine gap to CLOSE — not a fact to drop in. Do not think of it as "find the backing fact and paste it." A must-fix is the paper itself being incomplete at that point: it may be one fact rendered unclearly, or a whole sub-development that was never written up, or a step that is actually wrong. Your job is to make the paper complete THERE — by writing the missing argument from verified facts (curate + render, §0–§2 below), or, when no fact establishes it, by sending it back to the swarm to be proved. You never author the mathematics yourself (the fact graph is the only source; see paper-never-author-math). What you never do is smooth a must-fix over with a summarizing phrase — that is precisely what the verifier rejected.

A must-fix that needs MANY facts — KEEP CURATING, KEEP WRITING. If filling a gap would take dozens or hundreds of facts, that is NOT a reason to stop or defer. It means the gap is itself a development that gets the SAME treatment as the paper: extract its support layer and keep writing — and recurse again if a sub-gap is deep in turn. The development's full-closure size is NOT the decision metric — measuring the transitive closure and giving up is the original overflow fallacy in new clothes. Concretely, recurse the curation principle onto the development itself: select ITS OWN support layer (the 10–20 facts that carry the argument — the base cases, the induction mechanism, the endpoint), and write it as a dedicated section at expert compression (the synthesis doctrine), triaging its sub-steps like any other content (standard-type sub-computations summarized, per the active criteria). THE TOOL FOR A DEEP GAP IS THE WRITER, NOT THE REVISER. The dedicated section is a NEW paper_write call (its own curated support layer and brief, stitched in per stage 2b — same seam rules), after which the reviser only wires the seams (\refs, setup pointers). paper_revise(add_facts=…) is reserved for LOCATED point repairs — re-rendering a mangled proof, adding the one missing step, a handful of facts (≤10) — never for a development: a fact pile fed to the reviser accretes a flat lemma blob and inflates the paper ~5–14K/round (measured), which is exactly the failure stage 2b exists to prevent. THE GAP-FILL BRIEF CONTROLS THE LENGTH — the writer has no idea of your budget. A writer call knows only what you hand it; its default register is "standalone paper" (title, intro, notation section), and nothing in its role prompt sees the host paper. So every gap-fill paper_write brief states: (a) REGISTER — "one SECTION of an existing paper, NOT a standalone paper: no title/abstract/intro/notation section; open inside the host's standing conventions" (attach the host's setup and the labels it may \ref); (b) INTERFACE — exactly the statement(s) it must deliver, what it may assume from earlier sections, and WHICH steps are the section's pivots (name them — those get derived; every unnamed step stays at mechanism+outcome); (c) a NUMERIC target from the expert-compression calibration (a deep-gap section: 2–3 pages / 6–10K — a 2–4K frame plus ~2–4K of derivation per novel pivot; see THE PIVOT IS NOT COMPRESSIBLE); (d) the proof-style spec, verbatim. Input curation is the other half of the lever: a ~10–20-fact support layer physically cannot balloon into 30 pages — hand it a closure instead and no brief will save you. Calibrate the cost at EXPERT COMPRESSION, not transcription: a human expert rendered a comparably deep flagged step in under 2K chars; a gap fill should typically land at 1–5K. If your estimate says +30K, you are estimating transcription — fix the instruction, not the budget. If the budget is genuinely tight, the cause is almost always UPSTREAM STRUCTURE, not the new content: earlier sections are wasting space (verbose renders, redundant setup/notation blocks, lemma sprawl, duplicated disclosures). Recover the space by LOCATED dedup edits you identify yourself (never a global compress order — the reviser cannot execute one; see the editorial bar), then write the gap. You do NOT defer a must-fix on your own authority: the companion-paper / conditional-statement / override forks exist, but they are decisions only the OPERATOR can initiate; your default is always: as long as a must-fix stands, you write. THE SPIN-OFF FORK — raise it, don't decide it. If the paper you have is otherwise a sound, well-structured ~30-page article, but your honest estimate says this ONE detail cannot be filled within a few pages even at expert compression, do not silently grind it in and do not silently drop it: REPORT the state and ASK the human whether the detail should become its own paper (the paper_id mechanism — a technical companion the main paper cites). Asking is not deferring: the decision stays with the human; you supply the honest estimate. THE PROOF-STYLE SPEC — what to write, and what NOT to write. The measured gap: an expert wrote a deep two-part lemma proof in ~1.6K chars; our renders of comparable content ran 10–15×. The difference is never the mathematics — it is CEREMONY. Put this spec verbatim into every rendering instruction you issue: - Write inside the paper's standing conventions. Never restate hypotheses or re-quantify objects the section's setup already fixes — one clause ("with notation as in Setup X") suffices. Fact-graph statements are fully self-quantified BY DESIGN; a rendered proof must NOT inherit that register. - Prose first. Manipulations flow inside sentences; display ONLY the one or two equations the reader must refer back to. A proof that is a chain of displayed equations is a computation log, not a proof. - One proof, no ceremony. Absorb sub-steps as sentences ("since … and …, it follows that …"). A new labeled lemma is justified ONLY when it is cited from ≥2 places or is genuinely independent — never one lemma per source fact. - Mechanism, pivot, close. Name what is computed, give the pivotal identity, conclude. Trust the competent reader for routine expansion — the verifier accepts mechanism+outcome for standard-type steps; what it rejects is a bare claim with NO mechanism. - Calibration: a deep lemma proof lands at 0.5–2K chars; if a single proof render exceeds ~4K, you ordered transcription — rewrite the instruction, not the budget. - THE PIVOT IS NOT COMPRESSIBLE. Mechanism+outcome is accepted for standard-TYPE steps ONLY. The argument's own NOVEL pivotal computation — the step that is this paper's contribution — must be DERIVED, not named (measured: two 2-page gap-fill sections were rejected at exactly their named-not-derived pivots, while every surrounding mechanism+outcome standard step was accepted). Budget a deep-gap SECTION at 2–3 pages / 6–10K: a 2–4K frame plus ~2–4K of actual derivation per pivot; a 1–2-page ask gets the frame right but forces the pivot into a bare name, which fails verification. Deriving a pivot does NOT suspend the style spec — the derivation is written like every other proof here: prose-first, one or two displays, standing conventions (the measured expert proof derived its pivot INSIDE 1.6K chars; deriving ≠ transcribing). If a derived pivot exceeds ~4K, the instruction ordered transcription — rewrite it. And YOU name the pivots in the brief — typically ONE step per section, the step that is that section's contribution; every step you did not name stays at mechanism+outcome. A writer left to guess which steps count will defensively derive everything — that is the transcription failure returning through the back door. - Named anti-patterns (put these in the instruction as prohibitions): requantification blocks; display-per-step; per-fact lemma-ization; "we now verify / recall that" scaffolding; restating definitions before use. THE ONE EXCEPTION — stop and ask the human. When you genuinely judge you cannot get it right; or two-to-three consecutive rounds on the SAME gap have produced no effective progress (verdict unchanged, findings not narrowing); or your edits are making the paper WORSE (regressions, new findings outpacing cleared ones, structure degrading) — STOP. Preserve the best state, and consult the operator with an honest account: what you tried, why it failed, and your best guesses. Grinding past that point burns budget and damages the paper; asking is the professional move, not a failure. Deliver honestly either way: report the verifier's verdict as it stands, with the verifier's ignorable list attached (which must-fix you filled, which ignorable findings remain) — never present a must-fix you smoothed over as "passed".

THE TERMINAL STATE — "passes modulo ignorable computations". On a paper written from verified facts, expect the FIRST verify to yield mostly ignorable findings plus AT MOST one or two must-fix structural gaps. Fill only the must-fix ones (each a small curated supplement — a support layer and a few K of expert-compressed writing), re-verify once, and you should reach zero must-fix with only an ignorable residual. Zero must-fix IS the successful end of the loop (the tool reports correct / deliver_ok): stop, report the verdict with the ignorable list, and hand the fill-or-not calls to the operator. Do NOT expand ignorable items to make the paper longer — the verifier already judged a competent reader fills them, and chasing them is how a paper bloats into a monograph. If you find yourself in a fourth-plus revise round still filling, STOP: you are over-filling ignorable content the verifier never asked you to write.

THE QUALITY BACKSTOP — fewer findings is not the goal; a better paper is. Keep the TOTAL number of writing rounds small (the healthy trajectory: one curated write + small located fixes + at most one real must-fix supplement). Across rounds, judge the PAPER, not just the findings count: if findings are going down but the paper is getting worse — narrative giving way to fact-stacking, structure fragmenting, length creeping — the loop is destroying value, and clearing more findings will not buy it back. STOP at once: preserve the best state, tell the human WHERE the writing difficulty is (which section, which kind of content, what you tried) and what guidance you need. And in ALL cases — success, stall, or stop — leave behind one final paper that is ELEGANT and carries an honest, located list of its remaining gaps, so a human knows exactly what is unproven and where. An elegant paper with known gaps is a deliverable; a gap-free fact-stack is not.

  1. FIRST — fed ≠ rendered: read what ACTUALLY landed, not your memory of what you sent. Handing a fact to paper_revise(add_facts=…) does NOT mean its PROOF is in the paper. The reviser routinely renders the fact's statement as a lemma while its proof still asserts a step that the fact ITSELF delegates to a deeper fact ("by Fact X"). So before deciding what to send, do THREE reads: (a) the verifier's located finding — WHAT is flagged; (b) the reviser's actual rendered proof of that lemma in main.tex — what really landed; (c) the source fact's own proof in the fact graph. If that fact's proof discharges the flagged step by CITING a deeper fact, that deeper fact (and its sub-chain) is the true missing chokepoint — trace the by Fact … chain down to the fact whose proof actually contains the flagged computation, and feed that, bottom-up (a fact renders self-contained only once ITS predecessors are already rendered, so their \refs resolve instead of becoming stripped assertions). Counting a fact "done" because you sent it once under-counts the real depth — its delegated sub-proof can be dozens of facts — and misattributes a depth gap to "reviser fidelity." (Measured example: the MatTan ω(S) degree-formula fact delegated to a 121-fact induction that was never rendered; re-sending the top fact could never close it.) Only when the fact's own proof CONTAINS the flagged step in full, yet the paper still asserts it, is it a genuine fidelity problem → step 1a.

  2. If it concerns the re-rendering of a verified fact (the writer compressed or mangled a proof that was already verified), re-render it — but instruct SYNTHESIS, never transcription: resolve the theorem's \label → source fact id via <paper>/.provenance.json, read that fact's proof (and, per step 0, the sub-facts it delegates to) from runtime/projects/<p>/fact_graph/facts/<id>.md, and — this is YOUR judgment step — name the argument's skeleton yourself (its 2–4 key steps) in the instruction you pass with the verified proof text to paper_revise(notes=…). Ask for ONE compact-but-complete proof at paper granularity, written as an expert author writes: every claim carried by an actual argument (never a bare "by the same argument" / "a similar computation"), short steps written in full, standard-type sub-computations stated with their precise mechanism and outcome; forbid one-lemma-per-fact transcription and helper-lemma sprawl. The support-layer BINDING RULE applies INSIDE proofs too: you curate what the proof presents, the renderer writes it. Per-round add_facts follows the BINDING-RULE spirit: a small bottom-up set (~10-15 facts at most); several small rounds beat one dump. 1a. If a compact synthesis is rejected, refine — do not jump to transcription. The verifier rejects bare assertions, not compactness (measured on the same deep step: a human-expert 1.6K compact rendering passed independent expert review, while a per-fact transcription of identical content ran 28K — 17× — with helper-lemma sprawl; forced-transcription orders were the bloat source, and are how a paper degenerates from an article into an unreadable monograph). So on a re-flag: read WHICH mechanism the verifier says is missing, and instruct adding exactly that step — still synthesized, still one proof. If two such refinements fail, suspect your fact selection first (step 0: fed ≠ rendered — the real chokepoint may be a deeper delegated fact), and only as a LAST resort order a fuller write-out of the one specific step (never the whole chain), accepting the local bloat knowingly.

  3. If it is genuinely new paper math (glue, a reduction, a combined argument), pass the located finding itself to paper_revise(notes=…) for a real fix — or, if the paper is missing a prerequisite, re-select facts / re-run paper_write.

  4. Recompile (Stage 3) → re-run paper_verify_math. Keep the loop bounded (~5 rounds); on overrun stop and escalate to the operator with the ledger as the honest record. (The verifier is an LLM and has run-to-run variance — a wrong that flips to correct with no edit is that variance; the loop absorbs it, so never hand-mark the ledger to defeat the gate.)

On too_large — YOU decompose; the tool never chunks. too_large means the assembled verifier input (role prompt + REFERENCE_LEDGER + the whole main.tex body) exceeds DANUS_PAPER_VERIFY_WHOLE_DOC_CAP (default ~700K chars ≈ 175K tokens): one codex call cannot hold the paper. The tool records the blocker honestly and does not split — decomposition is a judgment call, so it is yours:

  1. First check whether the cap is just conservative: if the verifier model's context genuinely fits the prompt (body_chars in the envelope), raise DANUS_PAPER_VERIFY_WHOLE_DOC_CAP and re-run the tool. Done.
  2. Otherwise decompose the paper by its results — never by position in the text. The verifier's contract is the constraint: it judges whether a document, read on its own, establishes its main result — so every part you send must BE such a document, culminating in a designated result. A sequential slice (consecutive pages or sections chosen for length) fails this: a middle slice culminates in nothing, and the verifier has no main result to judge. Instead, pick the paper's major results R1…Rn (the main theorem plus the big propositions it rests on) and build one part per result. Part k's input document is:
    • the paper's notation base: preamble macros + every Definition/Notation env;
    • the full statements only (no proofs) of the other designated results that part k's proofs rely on, presented as "established elsewhere in this paper (separately verified)";
    • the complete development of R_k as written (its supporting lemmas and R_k itself, statements + proofs), ending at R_k — the part's own main theorem. Each part must stand on its own given those established statements — that is what "self-contained" means here — and sit comfortably under the cap. The part for the paper's main theorem takes the other designated results as established and closes the argument.
  3. Drive the verifier yourself, mirroring the tool: read agents/skills/write-paper/roles/PAPER_MATH_VERIFIER_PROMPT.md (and roles/AGENTS.md), append the confirmed REFERENCE_LEDGER.md and the part document, and run a fresh bin/codex exec --sandbox read-only per part; read the final findings JSON from its output. A part is clear iff it has zero must-fix findings (an ignorable-only residual still clears — record and surface it). A part carrying any must-fix → the revise loop above for those findings, then re-verify that part and every part that took its statement as established.
  4. Clearing the gate: the ledger still holds the too_large row and only the tool writes verdicts — so surface the per-part record (each part's scope, verdict, log) to the operator, and only on their explicit confirmation set the whole-paper row's status to overridden with a note pointing at that record. That is the operator-override channel deliver_ok accepts (correct / trusted / overridden). Never do this silently.

Deliver is BLOCKED unless the whole-paper verification is correct or the operator explicitly overridden (the deliver_ok / blockers fields; paper_math_verify.deliver_ok reads the ledger deterministically). Override is an operator per-paper policy (default mandatory-verify): the operator may choose to ship despite a still-failing or too-large verification — but it is then visibly flagged in the paper (a \note / disclosure line), surfaced as a fork, never silent. Set the ledger status to overridden only on the operator's explicit call.

6. Deliver + (operator fork) push

Deliver the main.tex + main.pdf paths. First confirm the deliver gate: the whole-paper math verification (Stage 5.5) must show deliver_ok=True (the whole-paper verification correct, or an explicit operator overridden) — never deliver a paper whose math was not re-verified as written. Pushing to a LaTeX git repo (e.g. Overleaf) / posting to arXiv is outward — an operator fork (your standing red line: confirm anything that leaves the machine). driver/latex_git_push.sh handles the push; if it lacks the repo URL / token, ask the operator, store the non-secret config in your own notes and the token in the gitignored secrets file (see the script header), then confirm before pushing.

Default house style (configurable — lives in STYLE_GUIDE.md / PAPER_STRUCTURE.md)

The non-negotiable part is integrity, not typography: preserve the mathematics, cite honestly, never fabricate a reference, leak no pipeline metadata (the PRIME DIRECTIVE in roles/AGENTS.md). Everything else — document class (amsart by default), \epsilon, the manual surname-sorted \begin{thebibliography}{99}, manual Theorem~\ref{} cross-references, exact citation numbers, the filler bans, and the acknowledgement disclosure — is the default AMS-style house style. It lives in STYLE_GUIDE.md / PAPER_STRUCTURE.md, is embedded into the codex by the tool, and is the operator's to change (edit those files, or override per paper in PROJECT_BRIEF.md). You never apply it yourself — you do not write LaTeX.

Honesty (load-bearing)

State only what you verified. A paper is "produced" only after compile_verify.sh passed (PDF, zero errors, no undefined citations) and paper_verify_math shows deliver_ok=True (the paper re-verified correct as written, or an operator overridden) — "it should compile" / "the facts were already verified" is not confirmation (the paper re-stitches the facts, so it is re-verified as a distinct artifact). If the auditor could not verify a reference, say so; do not present an unverified bibliography as checked. Never claim a push succeeded unless you confirmed it landed.

Style maintenance (offline, operator-gated; the anchor preflight is stage 1a)

roles/STYLE_DISTILLER_PROMPT.md distills recurring rules from the operator's own papers under style/anchors/ into the unified style/STYLE_GUIDE.md, as proposals the operator accepts or rejects — it never auto-applies an edit, and it never touches any paper's main.tex. A bad distill would silently corrupt the guide that governs every future paper, so the accept gate is mandatory.

It is presence-triggered, once, operator-gated (stage 1a above): it runs in the preflight only when anchors/ is non-empty AND stale (newer than style/.distilled_at, or that marker is absent), so anchors an operator drops in are distilled rather than silently ignored — but it never runs when anchors/ is empty or unchanged, and it is not part of the per-paper hot path (the writer consumes the already-distilled guide). The operator may also run it by hand at any time.

© frenzymath, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 7 other files in .agents/skills/write-paper of frenzymath/Danus.

  • SKILL.md
  • README.md
  • driver/anchors_stale.sh
  • driver/compile_verify.sh
  • driver/latex_git_push.sh
  • driver/seed_ledger.py
  • templates/PROJECT_BRIEF.md.template
  • templates/REVISION_LOG.md.template

Open the folder on GitHubat commit 6d92e8d

Compare with similar skills

Write Paper next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Write Paper compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Write Paper this skillfrenzymath/Danus476—~18kAutomated safety check: PassApache-2.0
Latex Paper ArtifactBingHanOfUESTC/open_agent_team106—~635Automated safety check: PassMIT
SearchMuuuun/luxas1.2k—~2.3kAutomated safety check: WarnMIT
Arxiv MCP Serverblazickjp/arxiv-mcp-server3.2k—~353Automated safety check: PassApache-2.0
Arxiv Paper Writerappautomaton/latex-arxiv-SKILL458—~2.3kAutomated safety check: PassMIT
Arxiv Paper Writeryunshenwuchuxun/latex-paper-skills266—~3.2kAutomated safety check: PassMIT

Similar skills

  • Latex Paper Artifact

    BingHanOfUESTC/open_agent_team

    A skill your agent uses when producing the final arXiv-style LaTeX paper, BibTeX, figures, tables, reproducibility statement, appendix, and artifact manifest.

    106 GitHub stars~635 tokensUpdated 3 mo ago
    Documents & OfficeAuto-check passed
  • Search

    Muuuun/luxas

    Unified academic paper search, citation chains, paper download (arXiv LaTeX/PDF, Sci-Hub), figure extraction from papers, LaTeX source reading, BibTeX fetching, web search, and browser automation…

    1.2k GitHub stars~2.3k tokensUpdated 1 mo ago
    Research & ScienceAuto-check: warnings
  • Arxiv MCP Server

    blazickjp/arxiv-mcp-server

    A skill your agent uses when finding, comparing, reading, or monitoring arXiv papers, including requests for abstracts, citation graphs, original LaTeX, section-level technical details, or…

    3.2k GitHub stars~353 tokensUpdated today
    Research & ScienceAuto-check passed
  • Arxiv Paper Writer

    appautomaton/latex-arxiv-SKILL

    Write LaTeX ML/AI review articles for arXiv using the IEEEtran template and verified BibTeX citations.

    458 GitHub stars~2.3k tokensUpdated 25 days ago
    Research & ScienceAuto-check passed
  • Arxiv Paper Writer

    yunshenwuchuxun/latex-paper-skills

    Writes ML/AI review and survey papers for arXiv using the IEEEtran LaTeX template with verified BibTeX citations.

    266 GitHub stars~3.2k tokensUpdated 6 mo ago
    Research & ScienceAuto-check passed
  • Arxiv Preflight

    Mathews-Tom/armory

    Pre-submission validation audit for arXiv papers across TeX source, PDF, figures, metadata, bibliography, file organization, and common-error scans.

    328 GitHub stars~2k tokensUpdated 3 days ago
    Research & ScienceAuto-check passed

More from frenzymath/Danus

All 16 skills in this repo
  • Validate externally referenced theorems by querying arXiv theorem search first and Codex's built-in web search second.

    476 GitHub stars~852 tokensUpdated 1 mo ago
    Auto-check passed
  • Construct candidate counterexamples to test a proposed conjecture, lemma, or intermediate claim by keeping the assumptions true while making the claimed conclusion fail.

    476 GitHub stars~790 tokensUpdated 1 mo ago
    Auto-check passed
  • Construct Toy Examples

    frenzymath/Danus

    Generate and analyze simpler examples that satisfy both the assumptions and the conclusion of a theorem statement or subgoal.

    476 GitHub stars~541 tokensUpdated 1 mo ago
    Auto-check passed
  • Direct Proving

    frenzymath/Danus

    Screen a decomposition plan by first trying to prove all of its subgoals directly, then identifying the key stuck points if the plan does not fully go through.

    476 GitHub stars~1.3k tokensUpdated 1 mo ago
    Auto-check passed
  • Identify Key Failures

    frenzymath/Danus

    Synthesize the common stuck points across failed decomposition plans.

    476 GitHub stars~586 tokensUpdated 1 mo ago
    Auto-check passed
  • Derive immediate mathematical consequences from a theorem statement or subgoal.

    476 GitHub stars~587 tokensUpdated 1 mo ago
    Auto-check passed

Works with

Questions about Write Paper

What does Write Paper do?

Turn a project's verified fact graph into a publishable LaTeX paper in a configurable house style — a standalone amsart .tex with a real bibliography, compiled to PDF. Write Paper is an agent skill from frenzymath/Danus.tex with a real bibliography, compiled to PDF.

When should I use Write Paper?

Write Paper fits situations like: A projects target theorem is established and the operator wants the paper; asks to write/revise/audit references for the paper for a project.

How do I install Write Paper in Claude Code?

Run `npx skills add frenzymath/Danus --skill write-paper -a claude-code`. Or copy the skill folder (.agents/skills/write-paper in frenzymath/Danus) into .claude/skills/write-paper in your project. Claude Code loads it when a task matches its description.

How do I install Write Paper in Codex?

Run `npx skills add frenzymath/Danus --skill write-paper -a codex`. Or copy the skill folder (.agents/skills/write-paper in frenzymath/Danus) into .agents/skills/write-paper in your project. Codex loads it when a task matches its description.

Can I use Write Paper in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add frenzymath/Danus --skill write-paper -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/write-paper, .gemini/skills/write-paper, .github/skills/write-paper and .opencode/skills/write-paper in your project.

What does Write Paper need to run?

Going by SKILL.md and its folder, Write Paper needs a shell and Python for the scripts in its folder and the command-line tools its instructions call (bash, python3 and codex). Our summary lists: Python 3; A Bash shell.

Does Write Paper access the network?

SKILL.md names 1 domain. In commands or code: arxiv.org; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.

Is Write Paper safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Write Paper use?

Write Paper is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Write Paper use?

About 18k tokens (SKILL.md is roughly 71k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Write Paper?

Skills that share tags, products or a category with Write Paper: Latex Paper Artifact (BingHanOfUESTC/open_agent_team, 106 stars), Search (Muuuun/luxas, 1.2k stars), Arxiv MCP Server (blazickjp/arxiv-mcp-server, 3.2k stars) and Arxiv Paper Writer (appautomaton/latex-arxiv-SKILL, 458 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Write Paper?

frenzymath (a GitHub organization) maintains it in frenzymath/Danus, which has 476 GitHub stars. The repository holds 16 skills in this directory. The repository was last updated on August 27, 2026.

Source: frenzymath/Danus on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.