🤖 AI-assisted reproducible publishing with Quarto
Ravi Kalia, PhD
🌍 Prefer Spanish or another language? The Google Translate extension translates these slides; code stays as written.
These slides were written with Claude Code (Claude Opus 5.5, Sonnet 5.5, Opus 5 and Fable 5.1), which drafted slides, Quarto files and code and re-rendered the deck. The author directed the work, reviewed and ran the code, and takes full responsibility for the content.
🏷️ Tool + version · 🛠️ Tasks · ✅ Checks · 🙋 Accountability · 📜 what a statement needs
🇬🇧 Part 1: a London guide · 💧 Part 2: Colombian water-quality research
| ⏱️ | Block | Min |
|---|---|---|
| 0:00 | 👋 Welcome · 🎯 objective · 🏁 outcome | 10 |
| 0:10 | 🧭 What Is Quarto? | 5 |
| 0:15 | 🧰 Ready to Prompt · ✋ fix failed checks · boxes · 🗣️ prompting | 15 |
| 0:30 | 🇬🇧 Part 1 · London: Markdown → HTML, rules, environment | 35 |
| 1:05 | ☕ Break · 🛟 catch up | 15 |
| 1:20 | 🇬🇧 Part 1 · .qmd · 🗂️ three audiences · 🌍 publish · 🔁 change an input |
65 |
| 2:25 | 🍽️ Meal Break · 🛟 catch up | 30 |
| 2:55 | 💧 Part 2 · Colombian water quality: why · data · 🔍 explore · 🧹 prepare · 🧩 cluster | 85 |
| 4:20 | ☕ Break · 🛟 catch up | 15 |
| 4:35 | 💧 Part 2 · 🏷️ Interpret · ⚠️ correlation ≠ causation · 📤 publish many | 75 |
| 5:50 | 🏁 Done · 🔗 show your sites · 👋 finish at 6:00 | 10 |
🏠 before the workshop: 🛠️ Setup, 6 steps · then 📖 Read Before the Day · ⭐ = optional, self-paced
🎯 Objective
Publish reproducible content with Quarto, written by a coding assistant you direct and check.
🏁 Outcome
Plain-text Markdown becomes
📄 docx 📕 pdf 📊 pptx 🎞️ revealjs 📖 epub ⭐ 🌐 website ✍️ blog 📈 dashboard
🤖 with the code written by your coding assistant
🔁 change an input, rebuild, and explain what updated
🌍 ¿Prefieres leer en español? La extensión Google Translate traduce estas diapositivas; el código no cambia. · Google Translate
Create and publish online content like these examples:
🧩 All three, and these slides (📄 index.qmd), are built with Quarto.
🔗 Finish with your own shareable websites.
⏱️ 0:10
🆓 Free and open source · no licence to buy, for anyone · MIT License
📚 All Quarto output formats · Quarto logo: Posit, PBC
⚙️
Traditional software
same input → same output, every time
📚
Built for publishing
quality documents, slides and websites
🤖
The assistant is separate
it writes the files · Quarto turns them into formats
🔁 No AI inside Quarto: it follows your source exactly
📄
One source
text + code + data in plain files
🧮
Numbers are computed
never typed or pasted in
▶️
One command
quarto render rebuilds everything
🕰️
Versioned with git
every change saved · uv.lock pins the tools
✏️ Change one value in the data → render → table, chart and sentence all update
🏷️ git (set up next) records which version of the files produced which result
💬 You correct one number in the data. What do you retype in the report?
🏠 before the workshop
🧭 6 steps, in order · ✅ the 🧪 test run says 42 = ready
🏠 before the workshop
| 🧭 | Do this | ✅ It worked when |
|---|---|---|
| 1 | 🧰 Get your computer ready | git --version runs · 🪟 wsl -l -v shows VERSION 2 |
| 2 | 📁 Move around the terminal | after cd ~ the prompt shows ~ |
| 3 | 🧩 Install a coding assistant | it starts, signed in |
| 4 | 🐧 Assistant installs the tools | it prints every version |
| 5 | 🛠️ Setup check | every check says OK |
| 6 | 🧪 Test run | hello.pdf says 42 |
🧭 = do it · 📖 = just read · 🆘 stuck → AI chat, then Discord · ✅ all 6 done? 📖 Read before the day
🎒 Have ready: a free GitHub account · a paid assistant plan or API key · the Discord invite
🏠 before the workshop 📖 reading
⬇️
Big downloads
Ubuntu, Quarto, the assistant, Python packages, data
☁️
The assistant is online
every reply needs the connection · it stops when the Wi-Fi drops
🏠
Do setup at home
wired or strong Wi-Fi · avoid hotels, trains, firewalled work networks
🔌 Plugged in · 📵 No VPN or proxy if you can · 🧪 Test run done at home = ready
🏠 before the workshop 🆘 when stuck
📋
Copy
the command you ran + the full error text
🌐
Paste into a web chat
Gemini, Claude or ChatGPT in your browser, with your system (Windows + Ubuntu, or Mac)
🔧
Try the fix
one suggestion at a time · read it before you run it
🔁
Still failing?
paste the new error · then Discord #help
🔒 No passwords, keys or private data in the paste
🏠 before the workshop 💬 get help
🔗
Join the server
open the invite link from the organiser · pick a name we know
#️⃣
Text channels
one topic each, like #help · type, paste, share screenshots
🔊
Voice channels
click to join, click to leave · mute until you speak
📣
Tag someone
type @ and their name · they get a notification
🆘 Stuck? Post in the help channel: what you ran, the error, a screenshot
🏠 before the workshop 💬 get help
| 💬 Discord | |
|---|---|
| Server | Community |
| Text channel | Group chat |
| Voice channel | Group call you can join and leave |
@name tag |
@name mention |
| Reply to a message | Reply to a message |
| Direct message | Chat with one person |
| Invite link | Group invite link |
| Reaction | Reaction |
⚠️ Unlike a WhatsApp group, a Discord server has many channels: pick the right one
🏠 before the workshop 🧭 Step 1/6 do only your system’s line
🪟 Windows 👉 Open the Windows setup page
🪟 9 short steps · 🛡️ admin account · 🔁 one restart: 📱 open it on your phone too
🍎 ⌘ Space, type Terminal, Enter · paste · click Install · wait
🐧 Ctrl+Alt+T · password stays hidden · other Linux: install curl and git your usual way
git --version prints a version. 🪟 In Ubuntu, and wsl -l -v shows VERSION 2.
🏠 before the workshop 🧭 Step 2/6 🚀 Open it: 🪟 Start → Ubuntu · 🍎 ⌘ Space → Terminal · 🐧 Ctrl+Alt+T
ana@LAPTOP-4K7Q2PM:~$ pwd /home/ana ana@LAPTOP-4K7Q2PM:~$ date Sat Nov 7 09:14:52 -05 2026 ana@LAPTOP-4K7Q2PM:~$ mkdir demo ana@LAPTOP-4K7Q2PM:~$ ls demo ana@LAPTOP-4K7Q2PM:~$ cd demo ana@LAPTOP-4K7Q2PM:~/demo$ mkdir notes ana@LAPTOP-4K7Q2PM:~/demo$ cd notes ana@LAPTOP-4K7Q2PM:~/demo/notes$ pwd /home/ana/demo/notes ana@LAPTOP-4K7Q2PM:~/demo/notes$ cd .. ana@LAPTOP-4K7Q2PM:~/demo$ cd ~ ana@LAPTOP-4K7Q2PM:~$ rmdir demo/notes demo ana@LAPTOP-4K7Q2PM:~$ ▊
📍 pwd where am I?
📅 date today
👀 ls what is here?
🆕 mkdir name make a folder
🚪 cd name go in · ⬆️ cd .. up one
🏠 cd ~ home, from anywhere
🗑️ rmdir name delete folder
you typed these, rmdir raised no error, and the prompt shows ~.
👀 Watch, then type the bold words, Enter after each · ~ = your home folder · 🍎 Mac: ana@mac ~ %, last folder only
🏠 before the workshop 🧭 Step 3/6 🪟 in Ubuntu · 🍎 claude not found? Paste the fix it printed, then a new tab
1️⃣ Pick one: paste its line, press Enter · 2️⃣ New tab: 🪟 Ctrl+Shift+D · 🍎 ⌘T · 🐧 Ctrl+Shift+T
3️⃣ Start it: mkdir -p ~/setup && cd ~/setup, then claude, codex or agy · trust this folder? ↓ to Yes, then Enter (👀 screens)
4️⃣ Sign in: no browser? Copy the link it prints, whole, into yours · a code appears? Paste it back here
the assistant answers when you type hello. Then type /exit.
🏠 before the workshop 🧭 Step 3/6, screen by screen
ana@LAPTOP-4K7Q2PM:~$ mkdir -p ~/setup && cd ~/setup ana@LAPTOP-4K7Q2PM:~/setup$ claude▊
Welcome to Claude Code v2.1.319 Claude Code can be used with your Claude subscription or billed based on API usage through your Console account. Select login method: ❯ 1. Claude account with subscription · Individual, Team, or Enterprise 2. Anthropic Console account · API usage billing 3. 3rd-party platform · Amazon Bedrock, Microsoft Foundry, Google Vertex AI
──────────────────────────────────────────────────── Accessing workspace: /home/ana/setup Quick safety check: Is this a project you created or one you trust? (Like your own code, a well-known open source project, or work from your team). If not, take a moment to review what's in this folder first. Claude Code'll be able to read, edit, and execute files here. Security guide ❯ No, exit Yes, I trust this folder Enter to confirm · Esc to cancel
Claude Code v2.1.319 ▐▛███▛█ Opus 5.5 with xhigh effort ▝▜██████▀ Claude Max ▝▝ ▝▝ ~/setup ──────────────────────────────────────────────────── ❯ Try "write a test for <filepath>" ────────────────────────────────────────────────────
ana@LAPTOP-4K7Q2PM:~$ mkdir -p ~/setup && cd ~/setup ana@LAPTOP-4K7Q2PM:~/setup$ codex▊
Welcome to Codex, OpenAI's command-line coding agent Choose how you want to use Codex. > 1. Sign in with ChatGPT Usage included with Plus, Pro, Business, and Enterprise plans 2. Sign in with Device Code Sign in from another device with a one-time code 3. Sign in another way Use an API key or another provider Press enter to continue
Folder access /home/ana/london-guide Trust this folder? Codex can read, edit, and run files here, subject to your permission settings. Folder settings can run code automatically, even without a model request. Continue only if you trust these files. Your trust decision will be saved. › 1. Trust and continue 2. Back to Agent Command Center enter continue · esc back
⌨️ Start: in the terminal, type the bold words, then Enter · ✳️ first asks for a text style: Enter · ➡️ each next screen
🔑 Sign in: Enter on 1. · no browser? Copy the link it prints, whole, into yours · 🌀 or 2., a code
🛡️ Trust: ✳️ ↓ to Yes, I trust this folder, then Enter · quit by mistake? Type claude again · 🌀 not in ~/setup: later, in a git folder
✅ Ready: type hello, then Enter, and it answers · /exit quits
🏠 before the workshop 📖 read before step 4
🖥️ The terminal · its line ends in $ (🍎 %)
ana@laptop:~/setup$ pwdcommands: pwd, cd, quarto preview
🤖 The assistant · it runs inside the terminal
> Which tools are missing?
plain language, and / commands: /exit
🚪 In: type claude · Out: type /exit · box colours say which place
➕ Assistant busy and a green box to do? Second tab: 🪟 Ctrl+Shift+D · 🍎 ⌘T · 🐧 Ctrl+Shift+T
😌 Wrong place? Usually harmless: command not found, or the assistant just reads it
💬 The assistant is running and a slide says to type gh auth login. Where do you type it?
🏠 before the workshop 🧭 Step 4/6 🪟 in Ubuntu · 👋 /exit first · not Claude? codex or agy
Check which of `git`, `curl`, `uv`, `quarto` (1.10 or newer) and the GitHub CLI `gh` are missing on this computer. Install only those, using each tool's official method for my system. Never run `sudo`, or an installer that asks for a password, yourself: print each such command alone in a code block, using full paths, then wait until I say done. Then print every version.👀 Read each command it asks about, then approve · 🔐 A command for you (sudo = administrator)? Paste it into a second tab, type your password (hidden), then type done here
🍎 Password = your Mac login · “Command line developer tools” pop-up? Click Install, wait, then type done
it prints a version for all five tools. Then type /exit.
🏠 before the workshop 🧭 Step 5/6 🪟 in Ubuntu · ➕ new tab first · not Claude? codex or agy
Print the versions of `quarto` (1.10 or newer), `uv`, `gh` and this assistant, and run `quarto check` (no Jupyter or LaTeX yet is fine: count it as OK). If `git config --global user.name` or `user.email` is unset, ask me for my name and my GitHub email, then set them. Clone https://github.com/project-delphi/quarto-workshop.git to `~/quarto-workshop` (if it exists, `git pull` only when its working tree is clean) and list its `checkpoints/`. Run `gh auth status`. Run every check even if one fails, then report each as OK or not.🔑 Not “Logged in”? In a second tab type gh auth login: GitHub.com → HTTPS → Yes → web browser, copy the code to github.com/login/device, then ask the assistant to check gh again · 🔧 Tool missing? Rerun the 🐧 install prompt
the assistant reports every check as OK, including gh “Logged in”.
🏠 before the workshop 🧭 Step 6/6 👋 /exit first · not Claude? codex or agy
👀 Finished? /exit, then open the folder: 🪟 explorer.exe . · 🍎 open . · 🐧 xdg-open .
🤷 Part 1 explains hello.qmd · ⚠️ “Kernel is running over TCP”: harmless
you opened both files, each says 42, and you told the organiser.
🏠 before the workshop
🤖 the assistant · 💸 costs · 📜 AI policy · 📝 Markdown
🏠 before the workshop 📖 reading
🧠
Model
reads and writes text, asks for tools
🔧
Harness
files, commands, permissions
🤖
Coding assistant
model + harness in your terminal
🧠 The model decides · 🔧 the harness does · 👀 you direct and check
🔁 Many harnesses can run several models: same tool, different results
🏠 before the workshop 📖 reading
| 🤖 Assistant | Type this | 🏢 Made by | 🧠 Default models |
|---|---|---|---|
| ✳️ Claude Code | claude |
Anthropic | Claude |
| 🌀 Codex | codex |
OpenAI (also makes ChatGPT) | GPT |
| ♊ Antigravity | agy |
Gemini |
✅ The first three are installed in Step 3 · pick one
💬 Same company for assistant and model: does that mean it is the best fit for your task?
🏠 before the workshop 📖 reading in each column: cheapest at the top
| ✳️ Claude | Good for | 🌀 ChatGPT | Good for |
|---|---|---|---|
| Haiku 5.5 ✅ from $ | simple, high-volume tasks | GPT-6 Luna $ | focused, high-volume tasks |
| Sonnet 5.5 ✅ $$$ | daily coding: speed + intelligence | GPT-6.1 Terra $$$ | visual reasoning, computer use, clear writing |
| Opus 5.5 $$$$ | long-running coding and knowledge work | GPT-6.1 Sol ✅ $$$ | complex work, balancing intelligence and cost |
| Fable 5.1 $$$$$ | demanding reasoning, long-horizon work | GPT-6 Astra $$$$$ | the most demanding work |
✅ Enough today: Haiku or Sonnet on medium · Sol on low
📉 Expect the same capability 100–1000× cheaper
💬 Which would you pick to fix a typo, and which to join two datasets?
each $ ≈ 3× the API price per 1M tokens · uses paraphrased from the vendors’ docs · rows are not equivalents · Claude · ChatGPT, checked 2026-11-06 · 📉 so far ≈ 10× or more a year: a16z · Epoch AI
🏠 before the workshop 📖 reading
🧠
/model
bigger + higher effort = more credits · Haiku or Sonnet on medium
🧹
/clear
task done? saves credits · files stay
👋
/exit · /quit
leave the assistant · type claude to return
💸 Every reply re-reads the whole chat: a long conversation costs more tokens each turn
🔁 Next task → /clear · 🪫 Prompt keeps failing → /model, higher effort · 👋 Done for today → /exit
💬 One task is finished and an unrelated one starts. Which command first?
🏠 before the workshop 📖 reading Same words each time: Generate an SVG of a pelican riding a bicycle
| Haiku 5.5 | Sonnet 5.5 | Opus 5.5 | Fable 5.1 | |
|---|---|---|---|---|
| low | ||||
| medium | ||||
| high |
💬 ✅ is enough here. Is 13¢ worth 90 times a 0.14¢ pelican? · 🕰️ Every pelican since 2024
Images: Simon Willison’s pelican series, one run per cell (Haiku, Sonnet, Opus, Fable) · ¢ = its tokens at API list prices, checked 2026-11-06 · 🌀 GPT grid
🏠 before the workshop 📖 reading
📍
Start it in a folder
that folder is its root: it reads, edits and runs commands there
🤝
“Trust this folder?”
answer yes only for a folder you chose for the work
🚧
Not ~, not Documents
a small project folder keeps everything else out of reach
🔄
New project?
/exit · cd to its folder · start the assistant again
🏠 pwd before you start: it prints the root
🏠 before the workshop 📖 reading
🚫
Never an author
AI cannot be accountable
🔍
You verify everything
code, numbers, references
🔒
Keep private data out
unpublished or sensitive work stays out of public tools
📝
Disclose the use
tool + version · task · your checks
⚖️ Never generate or alter primary data or images: that is misconduct
💬 Which needs a statement: fixing a typo, or computing a table? · 📝 An example
🏠 before the workshop 📖 reading
👉 learnxinyminutes.com/markdown · 🇪🇸 en español
#
# headings
✳️
*emphasis* · **bold**
📋
- and 1. lists
🔗
[link](url)

🧮
| tables | · ``` code fences
you can name the Markdown for a heading, a link and a table.
⏱️ 0:15
✋ Setup check or 🧪 test run failed? Ask a helper now
👀 An example: you will paste prompts like this from Part 1 on.
writes london.md · may run quarto render london.md
approve its commands · open london.html in your browser
🧪
Engineered prompts
designed with AI to reduce errors
❓
Don’t understand it?
ask an AI or search the docs
🔍
Dig deeper
what it does, why, when to use it
🎯
Be specific
file names, columns, chart types
☝️
One step at a time
one task per prompt
✅
Ask it to run & check
“…render it and tell me about errors”
👀
Check what changed
you are responsible for the result
🌍 ¿Español? Your first prompt can be: Responde siempre en español; deja el código y los nombres de archivo en inglés.
💬 Web chat: draft · ✅ you read it · 🤖 assistant: run it. Never paste private data.
⏱️ 0:30
📝
0:30 Markdown → HTML · rules · environment
🧪
1:20 .md → .qmd: YAML · code · docx + PDF
🗂️
1:40 🤖 three audiences in parallel
🌍
1:55 publish to GitHub Pages
🔁
2:15 change an input, no prompt given
🏁 a live dashboard URL on your phone 📱 · 🔁 you explain what a changed input updates
☕ 1:05 after the first block · 🍽️ 2:25 before 💧 Part 2
✍️ write one guide → ♻️ reuse it for every audience
📁 ~ (your home folder)
├── 📁 quarto-workshop/ ← clone
└── 📁 london-guide/ ← you
😰 Stuck for 2 min? Paste the exercise’s Catch me up prompt: 💾 backup → 📋 checkpoint → ▶️ render → ➡️ rejoin at the next ⏱️ block · ✋ approve access to ../quarto-workshop when asked
Welcome to London: free museums, royal history and riverside walks.
london.md has a heading and a welcome paragraph.
quarto render london.md → london.html
in a second terminal tab (Windows Terminal: Ctrl+Shift+D):
live view, reloads on every change · Ctrl+C stops it
🚫 YAML · 🚫 code · ✅ a web page
london.html opens and updates whenever london.md changes.
🎯
Scope
what the project is, and is not
📏
Rules
what must always hold
⌨️
Commands
how to build and check it
🔁
Every session
Claude Code, Codex and Antigravity read it first
✍️ say it once → every session follows it
Create AGENTS.md for this project. Scope: a London visitor guide for the tourism office, written once and reused for every audience. Rules: every number comes from a cited file in data/, photos are openly licensed, credited and show no people, never invent facts, keep data/ and images/ unchanged, and call ALVA counts "ALVA member attractions". Show me the file.writes AGENTS.md: a scope and a list of rules
approve · read it: does it say what you meant?
AGENTS.md states the guide’s scope and its three rules.
each list has its own heading.
Plan your route with the [TfL Journey Planner](https://tfl.gov.uk/plan-a-journey/).
{width=30% fig-alt="Big Ben's clock tower …"}
> **Tip from a local:** many big museums are free. Book a free timed ticket to skip the queue.Plan your route with the TfL Journey Planner.
Tip from a local: many big museums are free. Book a free timed ticket to skip the queue.
the photo shows with its credit and the link opens TfL.
Copy ../quarto-workshop/examples/london/data here. In london.md, add a "Sights at a glance" table from data/sights.csv: a photo from images/ (height=60, no unit; alt text), Sight, Price band, Nearest tube and a "Read more" link to its `official_page`. Credit the photos (images/ATTRIBUTION.md) and the sources.every sight has a photo and a working “Read more” link.
[ ] Contactless card or Oyster card
[ ] Free museum tickets booked
[ ] Comfortable shoes and an umbrella
the preview keeps the checklist’s line breaks and brackets.
💻
OS
macOS · Windows · Linux
🔩
Chip
Intel/AMD x86_64 · Apple/ARM arm64
🗣️
Language
Python · R · JS … which version?
📦
Dependencies
packages · versions · compiled C
🕰️
Then
conda · compilers · PATH · kernels → ⏳ hours
⚡
Now 🐍 uv
🔒 one lockfile, every OS and chip → ⏱️ seconds
🤖 either way: the assistant sets it up · you approve
📦 pyproject.toml · 🔒 uv.lock · .venv/ · a rule in AGENTS.md
approve its commands
uv run quarto check jupyter reports the Python in this folder’s .venv.
⏱️ stuck 2 min → 🛟 Catch me up → ➡️ next ⏱️ block
💾 ../london-guide-backup-01-<time> · 📋 copies the checkpoint · ▶️ renders · ➡️ names the slide to rejoin at
👀 watch the demo · behind, or london.md broken? paste it now
⏱️ 1:05
☕ 15 min · back at 1:20 for 🧪 .qmd
🛟 london.md won’t render? Ask for help now, or paste the 🛟 slide’s Catch me up
⏱️ 1:20
renames to london.qmd · edits the top
preview tab: Ctrl+C, then uv run quarto preview london.qmd
london.html shows the title, author and date.
```{python}
import pandas as pd
attractions = pd.read_csv("data/attractions.csv")
top = attractions.loc[attractions["visits_2025"].idxmax()]
top_name = top["attraction"]
top_visits = f"{top['visits_2025']:,}"
total_visits = f"{attractions['visits_2025'].sum():,}"
```
Most visited in 2025: the `{python} top_name`, with `{python} top_visits` visits.
London's ten most visited ALVA member attractions drew `{python} total_visits` visits.Most visited in 2025: the Natural History Museum (South Kensington), with 7,116,929 visits. London’s ten most visited ALVA member attractions drew 39,692,434 visits.
💬 if attractions.csv changes, which numbers update?
the sentence shows numbers computed from the CSV.
the figure has a number, a caption and no code.
first: Ctrl+C in the preview tab, so two renders don’t collide · restart it after
uv run quarto render london.qmd → london.html · london.docx · london.pdf
🎯 one format only: --to docx
london.docx and london.pdf show the sentence and the chart.
uv run quarto render london.qmd → six files
👀 watch the demo · try it later: PowerPoint or LibreOffice, an e-book reader, a browser
all six files show the sentence and the chart.
⏱️ 1:40
👔
Managers
📈 dashboard
format: dashboard
📧
Partners
email attachment
format: docx
🧾
Walk-ins
A5 flyer · pdf
typst · papersize: a5
📱
Social · ⭐
12 cm square ad · pdf
raw {=typst} page size
🌐
Visitors
website · next block
project: website
🔁 change data/ once → every version updates
🧑✈️
Lead
your assistant: splits the work, waits, renders, checks
🤖🤖🤖
Sub-agents
one task each, own context, all at once
📄
One file each
no two agents edit or render the same file
⏱️ three tasks ≈ the slowest one · 🪙 more tokens
Claude Code · Codex · Antigravity · checked 2026-10-31
Run three sub-agents in parallel, one per item (none? do the items in turn). Each reads london.qmd, data/ and images/, hides code, follows AGENTS.md and only writes its own files: no rendering.
1. london-dashboard.qmd (format: dashboard): Attractions page (value boxes: top attraction, total `visits_2025`, number free; Plotly bars by attraction; a Plotly line of visits by `year` from data/visits.csv; end each chart cell with `fig`, assigning `fig = fig.update_layout(…)`); Churches page (data/churches.csv, names linked to `visit_london`).
2. london-email.qmd: docx partner brief (2025 numbers as inline code, five sights with official links, getting around, sources); london-email-message.md: its covering email in a text block, no numbers.
3. london-flyer.qmd: one A5 Typst page (papersize: a5): Big Ben photo, five sights with price band and nearest tube, local tip.
Then render the three .qmd files yourself, one at a time, and check each.you open a two-page dashboard, a Word brief and its email, and a one-page flyer.
writes london-social.qmd · uv run quarto render london-social.qmd
open london-social.pdf
london-social.pdf is one square page showing the top attraction.
🗺️ This 3-D view needs WebGL. 2025 visits: Natural History Museum 7,116,929; British Museum 6,440,120; Tate Modern 4,514,266; National Gallery 4,147,544; Tower of London 2,817,852.
🗺️ 5 sights · ⛪ 10 churches · 〰️ the Thames between the boroughs
⬆️ height = 2025 visits (ALVA): a million = 250 m
🔄 up close · most visited: Natural History Museum, 7.1 M · paid: Tower of London
Visits: ALVA · places: Wikidata · boroughs: ONS, OGL v3.0, contains OS data © Crown copyright and database right 2024 · 🖱️ drag to turn · scroll to zoom
🧊 three.js: 3-D in any browser
🗺️ It drew the last slide’s map
🆓 Free · MIT License
👀 Gallery · 🐦 One-prompt pelican
after the sub-agents finish · open visits-3d.html in your browser
the bars turn, and the tallest bar’s number matches data/visits.csv.
🐧 Ubuntu
cd ~/london-guideexplorer.exe . in File Explorerls /mnt/c/Users/ Windows filescode . VS Code, if installed🪟 PowerShell
wsl -l -v Ubuntu · Running · 2wsl --user root reset a passwordwsl --shutdown stops every Ubuntu tab🪟 Windows only · 🍎 open . · 👀 approve in the assistant’s tab
File Explorer shows your london-guide files and wsl -l -v lists Ubuntu.
📋 Ctrl+Shift+C / V
copy · paste (🍎 ⌘C / ⌘V)
🖱️ Right-click
🪟 copy a selection, or paste
✋ Ctrl+C
stop a command (🪟 copies a selection)
⇥ Tab
complete a name
↔︎️ Ctrl+A / Ctrl+E
line start · end
✂️ Ctrl+U / Ctrl+K
delete before · after the cursor (🍎 U: whole line)
🧹 Ctrl+L
clear the screen
🔎 Ctrl+R
search past commands
Ctrl+Shift+D (🍎 ⌘T) → ls ~/quarto-w Tab Enter → Ctrl+L → Ctrl+R ls → Ctrl+A → Ctrl+E → Ctrl+U
Ctrl+R found your ls command and Ctrl+U cleared it.
⏱️ 1:55
🕰️ every saved version kept · 🐙 shared on GitHub
🌿
branch
main stays untouched
💾
commit
a snapshot + why
☁️
push
copy it to GitHub
🔀
pull request
propose the change
👀
review
someone reads the diff
✅
merge
into main
👤 today: straight to main · 👥 a team: the whole loop
🆓 free static hosting, straight from a GitHub repo
writes 3 files · uv run quarto render → _site/
then: Ctrl+C in the preview tab, uv run quarto preview
the site opens with Guide, Dashboard and Downloads.
Initialise a git repository here. Replace README.md in this folder with a short one linking to https://<your-github-username>.github.io/london-guide/ and listing the files in the site's Downloads menu. Commit everything except _site/, .quarto/, .venv/ and *_files/, then create a public GitHub repository named `london-guide` with gh and push to it. Set the repository's website to the same URL with `gh repo edit --homepage`. Show me the README and the website URL you set.approve gh · second tab: uv run quarto publish gh-pages, Enter at “Publish site … (Y/n)” · open the URL on your phone 📱 · 404? wait a minute, reload
https://<your-github-username>.github.io/london-guide/london-dashboard.html opens on your phone 📱
📁 _site/ is plain files: ✅ any static host can serve it
📍 today · 📤 uv run quarto publish gh-pages
📤 uv run quarto publish netlify
📤 upload _site/ with wrangler
⚠️ personal, non-commercial
⚙️ built by CI → gitlab.io
⚙️ builds from Git → onrender.com
📤 you upload · ⚙️ the host builds: needs Quarto + Python · checked 2026-11-07
⏱️ 2:15
🧪 No prompt is given: write your own.
visits_2025 in data/attractions.csv to 8000000. Which outputs will change?git restore data/attractions.csv, then rebuild so no trial number stays in your filesedit the number yourself: nano data/attractions.csv (Ctrl+O, Enter, Ctrl+X) · AGENTS.md keeps data/ unchanged for the assistant · ↩︎️ git restore needs the 🌍 Publish step
you can name the outputs that changed, the ones that did not, and why.
Create london-whitepaper.qmd, an A4 Typst white paper in "New Computer Modern": an abstract (no inline code), numbered Introduction, Methods, Results and Conclusions, then References via `#set bibliography(title: [References])` (references.bib from `url`). Analyse data/visits.csv vs 2019 and data/attractions.csv by `admission`. Add it to `render` and the "Downloads" menu; render; check citations. Figures must be vector: set `fig-format: svg` and check that the files in `figure-typst/` end in .svg.renders london-whitepaper.qmd
open london-whitepaper.pdf
london-whitepaper.pdf shows an abstract and numbered references.
p= 606 · 607 · 608 · 609 · 610 · 615 · 616 · 617 · 618 · 619, retrieved 2026-10-30data/sights.csv): British Museum · Natural History Museum · Tate Modern · TfL StopPoint (National Gallery) · Tower of London, checked 2026-11-01 · TfL Journey Planner, checked 2026-10-30data/places.csv) · boroughs: Office for National Statistics, LAD Dec 2024 BGC, OGL v3.0, contains OS data © Crown copyright and database right 2024, retrieved 2026-11-01⏱️ 2:25
🍽️ 30 min · back at 2:55 for 💧 Part 2
🛟 London site not live yet? Ask for help now, or paste the 🚀 Publish slide’s Catch me up
⏱️ 2:55
💧
2:55 why · rules · data
🔍
3:15 explore
🧹
3:35 prepare
🧩
3:55 cluster
🏷️
4:35 interpret
⚠️
4:55 correlation ≠ causation
📤
5:10 publish many
🏁 a live site: report · slides · blog · 🔁 an explained rebuild
☕ 4:20 after cluster · 🏁 5:50 show your sites
🧪
INS · water quality (IRCA, from utilities)
🗺️
DANE · town codes · map
🎓
ICFES · MEN · exams · school dropout
💰
DNP · coverage · poverty · spending
💡 useful: find 📍 which towns get unsafe water · 🔁 anyone can rerun it
All open data via datos.gov.co and DNP TerriData · sources and licences: examples/water/data/README.md
💧 IRCA · water-safety risk
🎓 Saber 11 · ICFES exam
🧮 both are averages: IRCA over water samples · Saber 11 over students
Resolución 2115 de 2007, arts. 13–14 (normas.cra.gov.co) · ICFES, Saber 11.° Guía de orientación 2017-1, pp. 101 and 104 (icfes.gov.co)
0–5
✅ sin riesgo · no risk
5.1–14
🟡 bajo · low
5.7
🏙️ urban
7.4
🏭 utilities
14.1–35
🟠 medio · medium
16.4
🇨🇴 national
35.1–80
🔴 alto · high
36.5
🌾 rural
43.7
🪣 self-supplied
80.1–100
⛔ inviable sanitariamente · unfit
INS IRCA for 2023: MinSalud, MinVivienda, INS and Superservicios, INCA 2023 (Bogotá, January 2025), Tabla 4 · bands: Resolución 2115 de 2007, art. 15 (normas.cra.gov.co)
🧩 Which kinds of municipality exist?
🎯 How do 👶 health and 🎓 education outcomes differ between them?
1 row = 🏘️ municipality × 🏙️/🌾 zone × 📅 year
📊 31,416 rows · 29 columns · 1,122 codes · 2010–2023 · 📖 data/README.md
Read data/README.md, then create AGENTS.md for this project. Scope: describe Colombian municipal water safety and infant mortality from data/ only. Rules: keep data/ unchanged, label IRCA as utility-supplied (prestadores), never cluster on outcomes, and say comparisons are descriptive, not causal. Show me the file.reads data/README.md · writes AGENTS.md: scope, rules, the data’s caveats
approve · check that every rule is there
AGENTS.md states the analysis’ scope and its four rules.
Give this project its own Python environment with `uv`: run `uv init --bare`, then `uv add ipykernel nbclient nbformat pyyaml pandas numpy matplotlib plotly scikit-learn pyarrow itables tabulate`. Run `quarto` and `python` through `uv run`, and add that rule to AGENTS.md. Show me the Python line of `uv run quarto check jupyter`.📦 pyproject.toml · 🔒 uv.lock · .venv/ · a rule in AGENTS.md
approve · preview each page you make: uv run quarto preview explore.qmd
uv run quarto check jupyter shows this project’s .venv Python.
🧩 Cluster on
Water
People & place
Government
🎯 Only compare
👶 infant mortality
🎓 Saber 11 score
🎒 school dropout
🚫 never cluster on outcomes
data/water_municipal.csv
│ uv run python scripts/analysis.py
▼
outputs/clusters.parquet + outputs/cluster_profiles.csv
│ every page reads them
▼
explore · clusters · report · slides · blog · ⭐ dashboard
🧮 compute once → 📤 never copy a number by hand
⏱️ 3:15
📐
rows × columns
❔
missing by column × zone
🎚️
IRCA by band × zone
📈
IRCA by year
❔ rural rows without IRCA: 53% · 🔴 rural rows with IRCA in high or unfit: 51%
Tunja (DIVIPOLA 15001), water_municipal.csv, 2010–2023 · 💡 pick a view, then a variable
writes explore.qmd · uv run quarto render explore.qmd
open explore.html in your browser
explore.html shows IRCA by zone for 2010–2023.
⏱️ 3:35
✂️
select 2016–2023 · municipalities · with IRCA
🔄
transform log SGP · capitals use the municipal total 🚩
🩹
impute year × zone median 🚩
📏
standardise z-score within year
🚩 = a flag column, so nothing is filled silently
| step | rows |
|---|---|
| 🏘️ municipality rows, 2016–2023 | 17,648 |
| ❌ no IRCA | 6,411 |
| ❌ more than 2 features missing | 2 |
| ✅ kept: 7,534 urban + 3,701 rural | 11,235 |
🏘️ 1,090 municipalities · 🩹 653 rows imputed · 🏛️ 214 capital rows from totals
⛰️🌧️ raw units: altitude + rainfall = 99.8% of the distance
Read data/README.md. Create scripts/analysis.py with prepare(df): rows with entity_type "municipality", 2016–2023, with irca; features irca, water_coverage, sewer_coverage, ipm, rural_share, altitude_m, rainfall_mm, mdm, log(sgp_water_pc); fill missing coverage of "urban" rows with department_capital == 1 from *_total, flagged in <col>_from_total. Run it.writes scripts/analysis.py · runs uv run python scripts/analysis.py
prepare() returns one row per kept zone-year with the nine features.
In prepare(), drop rows missing more than 2 features, fill the rest with the `year` × `zone` median plus <col>_imputed flags, then add z_<col> (z-score within `year`) beside the original columns; keep keys, outcomes and flags. Save outputs/prepared.parquet, print rows kept and dropped per step. Run it.edits prepare() · uv run python scripts/analysis.py
the printed counts match 🧮 What we keep: 11,235 rows kept.
⏱️ 3:55
🧩 This 3-D view needs WebGL; the four steps are written in the notes. 🎯 points close to their centre · 🧭 many rows → a few kinds to compare
① drop 3 centres at random points
💧 water data: k = 4 is a hyperparameter, picked from k = 2–8 by an elbow rule · silhouette stays low (about 0.2), so k is a judgement call
🫧 This 3-D view needs WebGL; the 2-D picture is on the previous slide.
🫧 2 axes · 51% of variance · cluster 3: 53% of near neighbours in it
🔄 3 axes · 67% · 90%
PC3 ≈ ⛰️ altitude (derived) − 🌧️ rainfall: cluster 3 sits below the others
Each point is a zone-year on the first three principal components of the 9 z-scored features, by cluster · 🖱️ drag to turn · scroll to zoom
🗺️ This 3-D view needs WebGL. Median head-town altitude, 2023 urban zones: cluster 1: 1,256 m; cluster 2: 1,766 m; cluster 3: 130 m; cluster 4: 159 m.
🗺️ 966 urban zones, 2023: a column each, coloured by cluster
⛰️ height = head-town altitude, ×50
🏔️ cluster 2: median 1,766 m · cluster 3: 130 m
Columns stand at municipality centroids; heights are derived SRTM altitudes of the head towns · labels: the 10 most populous department capitals and Tunja · 🖱️ drag to turn · scroll to zoom
📤
Use it · the normal route
copy each block’s finished analysis.py, then build the pages
🧗
Build it · ⭐ challenge
paste the ⭐ prompts · stuck? the 📤 prompt
outputs/clusters.parquet exists and you know k.
In scripts/analysis.py, run k-means on the z_ features for k=2–8 (random_state=42, n_init=20); save inertia and silhouette to outputs/k_selection.csv; pick k by the elbow, check silhouette and 5-seed ARI; number clusters by centroid IRCA; save outputs/clusters.parquet (prepared columns + `cluster`, pc1, pc2). Run it; explain k.edits scripts/analysis.py · uv run python scripts/analysis.py
outputs/clusters.parquet has a cluster for every kept row, and 🤔 you can explain its choice of k.
writes clusters.qmd · uv run quarto render clusters.qmd
open clusters.html in your browser
clusters.html shows the elbow, the silhouette and the PCA picture.
⏱️ 4:20
☕ 15 min · back at 4:35 for 🏷️ Interpret
🛟 No clusters.html yet? Ask for help now, or paste the 🧩 Clusters Page slide’s Catch me up
⏱️ 4:35
🏷️ name = 🏙️ setting + 🚰 coverage + 🎚️ median IRCA band
| Kind | Zone-years | Municipalities | Median IRCA | |
|---|---|---|---|---|
| 1 | Cities and large towns, no-risk water | 2,271 | 284 | 1.6 |
| 2 | Highland towns, no-risk water | 3,750 | 558 | 4.0 |
| 3 | Lowland towns, low-risk water | 2,451 | 479 | 9.2 |
| 4 | Rural, low-coverage, high-risk water | 2,763 | 729 | 35.2 |
“low-coverage” = 0.5 SD or more below the year’s average, not “unserved” · risk word = band of the cluster’s median utility-supplied (prestadores) IRCA, not every zone-year’s; a median near a band edge can flip the word
⚠️ Descriptive only: these differences are not causal effects of water safety. Infant mortality and dropout are municipal values.
⚠️ Descriptive only: not causal effects of water safety. Infant mortality: 2017–2019 and 2021–2023; Saber 11: published years.
▭ middle half · ━ median · ┅ mean · whiskers 5th–95th percentile · all zone-years 2016–2023; outcomes in their years, municipal ones once per municipality
copies · runs · writes outputs/outcomes_by_year.csv and outputs/cluster_profiles.csv
🧗 building it yourself? the next two ⭐ slides instead
the assistant shows a name for every cluster.
adds the outcome rules and outcomes_by_year() · uv run python scripts/analysis.py
outputs/outcomes_by_year.csv has no infant-mortality rows for 2016 or 2020.
adds profiles() and a naming rule · uv run python scripts/analysis.py
every cluster has a name you can defend from its profile.
edits clusters.qmd · uv run quarto render clusters.qmd
reload clusters.html in your browser
💬 while it renders, with a neighbour: describe one cluster using two features.
clusters.html shows the heatmap and outcomes by cluster and year.
🫧 This 3-D view needs WebGL; the heatmap on the earlier slides shows the same clusters.
📊 Outcomes the clustering never used, by year. Pick a cluster, or fix a year.
📅 2017 · cluster dropout means 2.83–3.84 %
x Saber 11 · z infant mortality · height school dropout ×2.4 · 🖱️ drag to turn
🫧 This 3-D view needs WebGL; the heatmap on the earlier slides shows the same clusters.
🫧 Each point is a municipality; each hull wraps its cluster’s nearest 85 %.
📅 2017 · cluster dropout means 2.83–3.84 %
Hull = convex hull of a cluster’s 85 % of municipalities nearest its mean · 🖱️ drag to turn
🫧 This 3-D view needs WebGL; the heatmap on the earlier slides shows the same clusters.
🥞 One plane per year. Bright lines: a municipality that changed cluster.
📅 2017 · cluster dropout means 2.83–3.84 %
120 sampled municipalities joined across planes · 🖱️ drag to turn
🫧 This 3-D view needs WebGL; the heatmap on the earlier slides shows the same clusters.
🪐 Ring radius = how far the cluster mean is from the overall mean.
📅 2017 · cluster dropout means 2.83–3.84 %
Sun = overall mean on Saber 11 and infant mortality · height = mean dropout ×2.4 · ring radius ×3 · 🖱️ drag to turn
🫧 This 3-D view needs WebGL; the heatmap on the earlier slides shows the same clusters.
🧱 Each cell = the places nearest one cluster mean; height = its mean dropout.
📅 2017 · cluster dropout means 2.83–3.84 %
Voronoi cells of the cluster means · 36 × 36 squares · height ×2.4 · 🖱️ drag to turn
⏱️ 4:55
⚠️ descriptive only: not a causal effect of water safety
💸
poverty
🌾
rurality
🏥
health-service access
📝
reporting quality
🔍
ecological level: municipal outcome, zone IRCA
copies · runs · writes outputs/irca_infant.csv and outputs/irca_infant_bins.csv
🧗 building it yourself? the next ⭐ slide instead
ρ and n are printed for every cluster.
adds irca_vs_infant() · uv run python scripts/analysis.py
ρ and n are saved and printed for every cluster.
edits clusters.qmd · uv run quarto render clusters.qmd
💬 while it runs, with a neighbour: pick one confounder: could it hide a real effect, or create a fake one?
clusters.html shows ρ and n per cluster, and you can say how one confounder could bias it.
⏱️ 5:10
Create report.qmd (formats html, docx, typst) from outputs/: summary, question, data (IRCA = utility-supplied prestadores IRCA), method, heatmap, outcomes, correlation ≠ causation (a chart of median infant_mortality by IRCA band per cluster), limits, sources. Matplotlib figures with captions and alt text; hide code. Render all formats; report errors.writes report.qmd · uv run quarto render report.qmd
open report.docx (Word or LibreOffice) and report.pdf
report.docx and report.pdf open with the heatmap, the outcomes and the within-cluster figures.
writes slides.qmd · uv run quarto render slides.qmd
open slides.html in your browser and slides.pptx in PowerPoint or LibreOffice
slides.html and slides.pptx both have 6 slides.
edits report.qmd · uv run quarto render report.qmd
open report.epub in an e-book reader (Calibre, or Books on a Mac)
report.epub opens with the three figures and the sources.
⭐ later, for slow sites: freeze: auto reuses old results
until you run rm -rf _freeze
writes 3 files · uv run quarto render → _site/
uv run quarto preview
the site lists one post whose title states the real k.
Initialise a git repository here with a .gitignore for _site/, .quarto/, .venv/, outputs/ and *_files/. Replace README.md in this folder with a short one linking to https://<your-github-username>.github.io/water-quality/ and listing the files in the site's Downloads menu. Commit everything not ignored, then create a public GitHub repository named `water-quality` with gh and push to it. Set the repository's website to the same URL with `gh repo edit --homepage`. Show me the README and the website URL you set.approve gh · when it shows the website URL, second tab: uv run quarto publish gh-pages, Enter at “Publish site … (Y/n)” · open the live URL
💬 while it publishes: what is the difference between your folder and the live site?
https://<your-github-username>.github.io/water-quality/ opens in a browser.
🧪 No prompt is given: write your own.
scripts/analysis.py change MAX_MISSING = 2 to 1. Which outputs will change?you can name the outputs that changed, the ones that did not, and why.
🗺️ choropleth: municipios.geojson joined on divipola
🎚️ year slider: 2016 → 2023
🔎 search a department → its municipalities
🗂️ two pages: map · table
Create dashboard.qmd (format: dashboard): a Plotly choropleth of the IRCA band of the "urban" rows of outputs/clusters.parquet on data/municipios.geojson (featureidkey="properties.divipola"), a year slider over their years (frames update only z), an itables table of outputs/clusters.parquet (maxBytes=0); end each chart cell with `fig`. Make it polished: IRCA bands in order from no risk to unfit, light to dark, with a legend, titled cards and readable labels. Add it to the navbar; render, check it looks good at laptop size and report errors.writes dashboard.qmd · edits _quarto.yml · uv run quarto render · ask it to run uv run quarto publish gh-pages --no-prompt
reload the preview (or uv run quarto preview) → 📈 Dashboard
the map changes when you move the year slider.
Add Kinds, Outcomes and Method pages to dashboard.qmd from outputs/ only: a heatmap of cluster_profiles.csv's `z_` columns; outcomes_by_year.csv medians by `year` and `cluster`, and irca_infant.csv's ρ; k_selection.csv's inertia and silhouette by k. Use each cluster's `color` and axis units; say it is descriptive, not causal. Render; check each page at laptop size.🧬 Kinds · 🎯 Outcomes · 📐 Method (why k = 4), from outputs/
reload 📈 Dashboard · open each page
the dashboard has Map, Kinds, Outcomes, Method and Municipalities pages.
Create whitepaper.qmd from outputs/: A4 Typst, "New Computer Modern"; an abstract (no inline code), numbered Introduction, Methods with equations, Results and Conclusions, then References via `#set bibliography(title: [References])`. Cite data/README.md's sources and the k-means, silhouette and ARI papers in references.bib; open each DOI. Add it to `render` and the "Downloads" menu; render; check equations and citations. Figures must be vector: set `fig-format: svg` and check that the files in `figure-typst/` end in .svg.uv run quarto render whitepaper.qmd
open whitepaper.pdf
whitepaper.pdf shows numbered equations and references.
🔀
Sankey: cluster moves between years
⭐
positive deviants: good outcomes despite risky water
🧪
GMM · HDBSCAN: soft or density clusters
🦠
SIVIGILA: diarrhoeal disease incidence, once available
nxt2-39c3, CC BY-SA 4.0 (utility-supplied, prestadores)gdxc-w37w · population projections 2005–2017, 2018–2042 · MGN 2023kgxf-xxbe, rnvb-vnyh · 🎒 dropout: MEN nudc-7mev⏱️ 5:50
🇬🇧
London guide: html · docx · pdf · dashboard · email · flyer · live site (⭐ ad · pptx · revealjs · epub)
💧
Water analysis: report (docx · pdf · ⭐ epub) · slides (pptx · revealjs) · blog · live site (⭐ dashboard)
🔁
One source, many outputs, rebuilt with one command: you can say which ones a changed input updates
🔗 Show your live sites: paste both URLs in 💬 Discord
📚 Quarto guide · Dashboards · Typst · GitHub Pages
➡️ Next: AI workflows: promotion experiments and sales outcomes
Quarto Workshop