For AI agents & their humans

AI makes PDF.

Teach any LLM to generate professional, PDF/A + PDF/UA compliant documents from natural language. Drop one skill file into your agent, point it at a fast REST API, and ship.

Add it to your agent in 30 seconds

The fastest path: paste the prompt below into your agent and it'll fetch the skill file itself. Want to install it locally? Two coding-agent-friendly options below.

Tell your AI agentPaste into Claude, Cursor, ChatGPT, etc.
Use this skill to author PDFs: https://makespdf.com/skills/pdf-template-author.md

Any coding agent

Download the file, then register it as a skill, rule, or instruction set in your agent of choice — Claude Code, Codex, OpenCode, Cursor, Windsurf, Copilot, etc.

curl -o pdf-template-author.md \
  https://makespdf.com/skills/pdf-template-author.md

The makesPDF CLI

Prints the skill to stdout — redirect to wherever your agent looks. Also gives the agent validate and preview commands.

npx @makespdf/cli skill > pdf-template-author.md
# or
npm i -g @makespdf/cli && makespdf login

How it works

The agent writes a tiny JavaScript snippet using a high-level builder DSL — things like doc(), page(), table(), totals(). Our API renders it to a compliant PDF in about a second wall-clock. No layout knowledge required from the model: the DSL encodes professional document patterns (invoices, receipts, CVs, reports) as composable functions.

curl -X POST https://makespdf.com/api/v1/preview \
  -H "Authorization: Bearer $MAKESPDF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dsl": "const template = doc({ size: \"A4\" }, page(col(s(\"Hello {{name}}\")))); const sampleData = { name: \"World\" };",
    "data": { "name": "Ada" }
  }' -o hello.pdf

Headless agents auth in one click

Agents that can't ship a baked-in API key use the OAuth 2.0 device authorization flow (RFC 8628). The agent prints a short code, the user opens a URL, approves once, and the agent receives a persistent key. Same flow as gh auth login — no secrets to manage, no loopback listener required. Full details in the API reference →

Which models can author a PDF?

Internal benchmark — each model gets the skill file as its system prompt, then runs the five test cases below. Up to 5 cycles per test, with structured catalog feedback fed back to the model between cycles.

Test 1 · Generate

Build a template from scratch

Given only the data, produce a working DSL template. The data uses non-generic field names (gstRate, gstAmount, NZD currency) the model must respect.

Show instructions + data

The invoice is for a New Zealand company (Solaris Energy) that installs solar panels. Include the GST number and use NZD currency. The data has a `gstRate` and `gstAmount` instead of generic "tax" fields — use those exact field names. The company has an `email` and `gst` field.

{
  "invoiceNumber": "INV-7742",
  "date": "31 March 2026",
  "dueDate": "30 April 2026",
  "company": {
    "name": "Solaris Energy Ltd",
    "address": "12 Harbour View, Level 3",
    "city": "Auckland 1010, New Zealand",
    "email": "billing@solaris.nz",
    "gst": "GST 123-456-789"
  },
  "customer": {
    "name": "Pacific Fresh Exports",
    "address": "88 Wharf Road",
    "city": "Tauranga 3110, New Zealand",
    "email": "accounts@pacificfresh.co.nz"
  },
  "items": [
    {
      "description": "Solar panel installation (20kW system)",
      "qty": 1,
      "unitPrice": "18,500.00",
      "amount": "18,500.00"
    },
    {
      "description": "Inverter — SolarEdge SE7600H",
      "qty": 3,
      "unitPrice": "2,100.00",
      "amount": "6,300.00"
    },
    {
      "description": "Mounting hardware and cabling",
      "qty": 1,
      "unitPrice": "3,200.00",
      "amount": "3,200.00"
    },
    {
      "description": "Electrical certification",
      "qty": 1,
      "unitPrice": "450.00",
      "amount": "450.00"
    },
    {
      "description": "Project management",
      "qty": 20,
      "unitPrice": "95.00",
      "amount": "1,900.00"
    }
  ],
  "subtotal": "30,350.00",
  "gstRate": "15%",
  "gstAmount": "4,552.50",
  "total": "34,902.50",
  "currency": "NZD",
  "paymentTerms": "Net 30. Direct credit to Solaris Energy Ltd, ANZ Bank, Account 06-0123-0456789-00. Please reference INV-7742."
}
Test 2 · Adapt

Rewire an existing template

Given the working assets/samples/invoices/invoice-clean-dsl.js template plus data with a different schema (French TVA, EUR, renamed fields), update the template to fit.

Show schema diff
KindBeforeAfter
renamedcustomerclient
renamedcustomer.country(merged into client.city)
renamedtaxRatetvaRate
renamedtaxtvaAmount
renameditem.rateitem.unitPrice
addedcurrency (was hardcoded $, now EUR)
addedcompany.siret, company.tva
addedclient.ustId
addedlegalNotice (must render at bottom)
removednotes
Show instructions + data

The data structure has changed — the customer field is now called `client`, tax is now French TVA (`tvaRate`, `tvaAmount`), currency is EUR, and the company has SIRET and TVA identifiers. There is also a `legalNotice` field that should appear at the bottom.

{
  "invoiceNumber": "FAC-2026-031",
  "date": "31 March 2026",
  "dueDate": "15 April 2026",
  "company": {
    "name": "Lumière Digital",
    "address": "5 Place de la Bourse",
    "city": "33000 Bordeaux, France",
    "siret": "SIRET 123 456 789 00012",
    "tva": "FR 32 123456789"
  },
  "client": {
    "name": "Weinkeller Schmidt GmbH",
    "address": "Römerberg 15",
    "city": "60311 Frankfurt, Germany",
    "ustId": "DE 987654321"
  },
  "items": [
    {
      "description": "Refonte site web e-commerce",
      "qty": 1,
      "unitPrice": "8,500.00",
      "amount": "8,500.00"
    },
    {
      "description": "Intégration passerelle de paiement",
      "qty": 1,
      "unitPrice": "2,200.00",
      "amount": "2,200.00"
    },
    {
      "description": "SEO — audit et optimisation",
      "qty": 1,
      "unitPrice": "1,500.00",
      "amount": "1,500.00"
    },
    {
      "description": "Formation CMS (2 sessions)",
      "qty": 2,
      "unitPrice": "400.00",
      "amount": "800.00"
    }
  ],
  "subtotal": "13,000.00",
  "tvaRate": "20%",
  "tvaAmount": "2,600.00",
  "total": "15,600.00",
  "currency": "EUR",
  "paymentTerms": "Virement bancaire sous 15 jours. IBAN: FR76 3000 6000 0112 3456 7890 189. BIC: AGRIFRPP. Mention: FAC-2026-031.",
  "legalNotice": "TVA intracommunautaire FR 32 123456789. Pénalités de retard: 3x le taux d'intérêt légal."
}
Test 3 · Paginate

Multi-page invoice with footer

50 line items that span 3+ pages. Tests template:each, repeating page header, and a footer with thisPage() / totalPages() — totals must appear only on the final page.

Show instructions + data

This invoice has 50 line items, so the table will span multiple pages. Use a repeating page header (with the company name) and a footer that shows "Page X of Y" using `thisPage()` and `totalPages()`. Make sure the totals block appears only on the last page (after the table), not after every page.

{
  "invoiceNumber": "INV-9001",
  "date": "31 March 2026",
  "dueDate": "30 April 2026",
  "company": {
    "name": "Northwind Consulting",
    "address": "200 King St W, Suite 1500",
    "city": "Toronto, ON M5H 3T4, Canada",
    "email": "ar@northwind.example"
  },
  "customer": {
    "name": "Globex Manufacturing",
    "address": "1 Industrial Pkwy",
    "city": "Hamilton, ON L8E 5K2, Canada"
  },
  "items": [
    {
      "description": "Consulting hours — Sprint 1, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 1, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 1, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 1, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 1, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 2, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 2, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 2, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 2, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 2, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 3, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 3, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 3, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 3, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 3, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 4, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 4, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 4, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 4, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 4, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 5, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 5, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 5, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 5, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 5, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 6, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 6, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 6, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 6, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 6, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 7, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 7, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 7, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 7, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 7, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 8, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 8, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 8, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 8, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 8, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 9, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 9, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 9, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 9, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 9, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 10, day 1",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 10, day 2",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 10, day 3",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 10, day 4",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    },
    {
      "description": "Consulting hours — Sprint 10, day 5",
      "qty": 8,
      "unitPrice": "175.00",
      "amount": "1,400.00"
    }
  ],
  "subtotal": "70,000.00",
  "taxRate": "13%",
  "tax": "9,100.00",
  "total": "79,100.00",
  "currency": "CAD",
  "paymentTerms": "Net 30. Wire transfer to RBC, Account 12345-678901."
}
Test 4 · Non-invoice

Generate a resume / CV

Distinguishes models that learned the DSL from ones that pattern-matched on invoices. Different structure: name + summary, then iterated experience with nested bullets, skills, education.

Show instructions + data

Generate a clean single-column resume (CV) — name + title at the top, contact line, summary paragraph, then sections for Experience, Skills, and Education. Use `template:each` to iterate experience entries and bullets. Section headings should be visually distinct (bold, slight underline or top border).

{
  "name": "Avery Chen",
  "title": "Senior Software Engineer",
  "contact": {
    "email": "avery.chen@example.com",
    "phone": "+1 415 555 0148",
    "location": "San Francisco, CA",
    "linkedin": "linkedin.com/in/averychen"
  },
  "summary": "Backend engineer with 9 years building distributed systems. Led migration of a 4-region payments platform from monolith to event-driven services with zero downtime.",
  "experience": [
    {
      "role": "Staff Engineer",
      "company": "Lumen Payments",
      "period": "2022 – Present",
      "bullets": [
        "Designed event-driven settlement pipeline processing $2B/month",
        "Mentored team of 6 engineers, ran biweekly architecture reviews",
        "Cut p99 latency 4x via per-shard read replicas"
      ]
    },
    {
      "role": "Senior Engineer",
      "company": "Helix Robotics",
      "period": "2018 – 2022",
      "bullets": [
        "Built fleet telemetry ingest handling 50k events/sec",
        "Owned on-call rotation and SLO instrumentation"
      ]
    },
    {
      "role": "Software Engineer",
      "company": "Pixelforge",
      "period": "2015 – 2018",
      "bullets": [
        "Shipped real-time collaboration features for vector editor"
      ]
    }
  ],
  "skills": [
    "Go",
    "Rust",
    "PostgreSQL",
    "Kafka",
    "Kubernetes",
    "Terraform"
  ],
  "education": [
    {
      "degree": "B.S. Computer Science",
      "school": "University of Waterloo",
      "period": "2011 – 2015"
    }
  ]
}
Test 5 · Fix

Repair a broken template

Three deliberate errors: a plain-string child in col(...), a style object passed as a second argument to s(...) (lands in the span's kids instead of applying styles), and a template:each loop variable that doesn't match the {{...}} references. Tests debugging — the most common real-world skill use.

Show instructions + broken DSL

This template fails validation. There are three problems: 1. `col` cannot contain a plain-string child — `"Summary"` must be wrapped in a span: `s({ "font-size": 14, "font-weight": "bold" }, "Summary")`. 2. `s("{{row.value}}", { align: "right" })` passes a style object as a second argument, which lands inside the span's children. Right-align via the cell tuple instead: `[s("{{row.value}}"), "auto", "right"]` (or put align on the column). 3. The loop is declared `"r in metrics"` but the cells reference `{{row.name}}` / `{{row.value}}` — the loop variable must match the references. Use `"row in metrics"`. Fix all three problems and output the corrected DSL. Output ONLY the corrected JavaScript.

// This template is broken. Fix it.
const template = doc({ size: "A4" },
  page(
    hdr(s("Quarterly Report — {{quarter}}")),
    col(
      "Summary",
      s("{{summary}}"),
      gap(12),
      s({ "font-weight": "bold" }, "Metrics"),
      table([["Metric", "1fr"], ["Value", "auto", "right"]], "r in metrics", [
        [s("{{row.name}}")],
        [s("{{row.value}}", { align: "right" })],
      ]),
    ),
  ),
  ftrPages(),
);

const sampleData = {
  quarter: "Q1 2026",
  summary: "Revenue up 18% YoY. Churn flat at 2.1%.",
  metrics: [
    { name: "Revenue", value: "$4.2M" },
    { name: "Churn", value: "2.1%" },
  ],
};
Test 6 · Chat edits

Four-turn conversational edit chain

Closest test to the real /ai/chat use case: starts from a working invoice, fires four scripted plain-English edit requests, and verifies every prior intent still holds on the final template. Each turn gets up to five catalog-feedback cycles before moving on — same two-layer loop the chat applies via commit_dsl.

Show the four turns
  1. Make the totals row bold.
  2. Change the document font to NotoSans, size 10.
  3. Add a centered "Thank you for your business." line below the totals (small, italic).
  4. Add a 1pt bottom border under the table header row so it visually separates from the line items.

Columns

  • Total time — sum of wall-clock time across every attempt in every test. The most direct measure of "how long would this model take on real work?". A self-correcting model that takes 5 cycles to pass shows 5× the cost of a model that converges on the first try; per-call latency can be inferred as Total ÷ (Cycles × 6).
  • Out tokens — sum of output tokens across all attempts in the run.
  • DSL — final-attempt count of tests where the model's DSL parsed and validated against the catalog without errors. This says nothing about whether the PDF actually rendered.
  • Rendered — count of tests that produced a PDF end-to-end (valid DSL and the render pipeline succeeded). The real success metric: a 5/5 here is a model you can ship.
  • Skill — first-attempt compliance score (0–1), mean of the static-check modules that apply to each generated template. Scoring only the first attempt isolates raw skill comprehension from self-correction (later cycles get structured catalog feedback). See the two modules below.
  • Run cost — sum of input + output token cost across all attempts at published OpenRouter prices.
Skill module · Table authoring

Column widths follow the skill's guidance

Numeric columns use auto instead of a fixed % width (so wide values don't wrap and drop), and at least one column uses 1fr / auto / auto-stretch to absorb surplus width when a description column is present.

Skill module · Template vars

Every variable reference resolves

Each {{...}} reference in the generated DSL points at a path that exists in the model's own sampleData — no broken bindings, typos, or made-up fields.

30 / 30 models
Cycles
claude-opus-4.727,7096/6⌀ 1.8 cyc · 100% clean6/60.993m 55s$2.85
gpt-5-mini30,1266/6⌀ 1.7 cyc · 100% clean6/60.934m 14s$0.12
gpt-5.4-nano33,2416/6⌀ 1.8 cyc · 100% clean6/60.994m 17s$0.10
qwen3.5-122b-a10b30,6046/6⌀ 2.5 cyc · 100% clean6/60.995m 39s$0.24
gpt-5.521,0766/6⌀ 1.8 cyc · 100% clean6/60.995m 45s$2.04
minimax-m2.719,9456/6⌀ 1.5 cyc · 83% clean6/61.006m 5s$0.09
grok-4-fast47,0186/6⌀ 1.8 cyc · 100% clean6/60.987m 58s$0.08
deepseek-v4-flash · OpenRouter27,6826/6⌀ 2.0 cyc · 100% clean6/60.998m 46s$0.05
gpt-5.432,1226/6⌀ 1.7 cyc · 100% clean6/60.999m 28s$1.13
claude-haiku-4.599,9666/6⌀ 2.0 cyc · 100% clean6/60.9910m 30s$0.85
deepseek-v4-flash · OpenCode Go88,6666/6⌀ 1.8 cyc · 100% clean6/61.0010m 37s$0.07
claude-sonnet-4.649,2376/6⌀ 1.7 cyc · 100% clean6/61.0010m 40s$1.66
gpt-536,1216/6⌀ 1.8 cyc · 100% clean6/61.0012m 28s$0.71
gemini-2.5-pro119,2306/6⌀ 2.8 cyc · 100% clean6/60.9918m 1s$1.79
deepseek-v4-flash-073158,2996/6⌀ 1.5 cyc · 100% clean6/60.9923m 25s$0.03
glm-4.628,5406/6⌀ 2.5 cyc · 100% clean6/60.9928m 42s$0.20
grok-472,7446/6⌀ 3.0 cyc · 100% clean6/61.0042m 46s$2.42
kimi-k2.6126,4226/6⌀ 2.0 cyc · 100% clean6/61.0085m 33s$0.66
gpt-5.1-codex-mini76,4415/6⌀ 2.5 cyc · 83% clean6/61.008m 50s$0.25
deepseek-v4-pro40,5125/6⌀ 2.5 cyc · 83% clean6/60.9926m 36s$0.20
glm-4.781,1345/6⌀ 2.5 cyc · 83% clean6/61.0058m 57s$0.30
grok-4.2091,5644/6⌀ 2.8 cyc · 67% clean6/60.9918m 14s$0.75
qwen3.6-max-preview82,7974/6⌀ 2.8 cyc · 67% clean6/60.9957m 34s$0.98
mistral-medium-3-518,9545/6⌀ 2.5 cyc · 83% clean5/61.002m 15s$0.73
gpt-5.4-mini45,7285/6⌀ 2.0 cyc · 83% clean5/60.996m 11s$0.43
llama-4-maverick14,3755/6⌀ 2.0 cyc · 83% clean5/60.997m$0.05
qwen3-32b38,0965/6⌀ 2.0 cyc · 67% clean5/60.9417m 49s$0.04
gemma-3-12b-it13,1945/6⌀ 1.5 cyc · 83% clean4/60.909m 27s$0.01
minimax-m352,0434/6⌀ 2.2 cyc · 67% clean4/61.0044m 10s$0.16
kimi-k214,2893/6⌀ 2.7 cyc · 50% clean3/61.006m 37s$0.25

Refreshed periodically. Click a column header to sort, or a model row to see per-test status, errors, and download the rendered PDF / generated DSL. All models are routed through OpenRouter. Download as Markdown or JSON — leaderboard + per-model narrative + failing-DSL snippets (markdown only), sized to drop straight into an AI assistant.

Next steps