demolab-cli 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. demolab_cli/CHANGELOG.md +445 -0
  2. demolab_cli/VERSION +1 -0
  3. demolab_cli/__init__.py +10 -0
  4. demolab_cli/_paths.py +63 -0
  5. demolab_cli/build.py +284 -0
  6. demolab_cli/cli.py +414 -0
  7. demolab_cli/deploy/deploy.yml +61 -0
  8. demolab_cli/deploy/preview.yml +56 -0
  9. demolab_cli/devserver.py +380 -0
  10. demolab_cli/guides/AUTORESEARCH-RULES.md +149 -0
  11. demolab_cli/guides/GLOSSARY.md +55 -0
  12. demolab_cli/guides/HOUSESTYLE.md +108 -0
  13. demolab_cli/guides/RULES.md +142 -0
  14. demolab_cli/guides/SLIDES.md +145 -0
  15. demolab_cli/guides/STRUCTURE.md +78 -0
  16. demolab_cli/guides/SUPPORT.md +36 -0
  17. demolab_cli/overlay.py +47 -0
  18. demolab_cli/runbooks/AUTORESEARCH.md +67 -0
  19. demolab_cli/runbooks/DOCTOR.md +116 -0
  20. demolab_cli/runbooks/EMBED-DOCS.md +39 -0
  21. demolab_cli/runbooks/FROM-JUPYTER.md +84 -0
  22. demolab_cli/runbooks/FROM-PAPER.md +48 -0
  23. demolab_cli/runbooks/GETTING-STARTED.md +281 -0
  24. demolab_cli/runbooks/GROUND-CLAIMS.md +166 -0
  25. demolab_cli/runbooks/LINT.md +176 -0
  26. demolab_cli/runbooks/MIGRATE-CODE.md +49 -0
  27. demolab_cli/runbooks/MIGRATE-STACK.md +50 -0
  28. demolab_cli/runbooks/NEXT.md +52 -0
  29. demolab_cli/runbooks/NIGHT-SHIFT.md +68 -0
  30. demolab_cli/runbooks/PLAN.md +64 -0
  31. demolab_cli/runbooks/RED-TEAM.md +72 -0
  32. demolab_cli/runbooks/STEELMAN.md +44 -0
  33. demolab_cli/runbooks/TOUR.md +42 -0
  34. demolab_cli/runbooks/UPDATE.md +84 -0
  35. demolab_cli/scaffold/demo/artifacts/data/ar018/swe-bench.svg +1 -0
  36. demolab_cli/scaffold/demo/artifacts/data/exp000/lif.svg +1103 -0
  37. demolab_cli/scaffold/demo/artifacts/data/exp000/net.png +0 -0
  38. demolab_cli/scaffold/demo/artifacts/data/exp000/numbers.json +38 -0
  39. demolab_cli/scaffold/demo/artifacts/data/exp000/run.sh +3 -0
  40. demolab_cli/scaffold/demo/artifacts/data/exp001/eif.svg +1122 -0
  41. demolab_cli/scaffold/demo/artifacts/data/exp001/enet.png +0 -0
  42. demolab_cli/scaffold/demo/artifacts/data/exp001/numbers.json +40 -0
  43. demolab_cli/scaffold/demo/artifacts/data/exp001/run.sh +3 -0
  44. demolab_cli/scaffold/demo/artifacts/data/exp002/cartpole.mp4 +0 -0
  45. demolab_cli/scaffold/demo/artifacts/data/exp002/numbers.json +18 -0
  46. demolab_cli/scaffold/demo/artifacts/data/exp002/run.sh +3 -0
  47. demolab_cli/scaffold/demo/artifacts/data/exp003/double_pendulum.mp4 +0 -0
  48. demolab_cli/scaffold/demo/artifacts/data/exp003/numbers.json +21 -0
  49. demolab_cli/scaffold/demo/artifacts/data/exp003/run.sh +3 -0
  50. demolab_cli/scaffold/demo/artifacts/pdfs/ar000.pdf +546 -0
  51. demolab_cli/scaffold/demo/artifacts/pdfs/ar003.pdf +773 -1
  52. demolab_cli/scaffold/demo/artifacts/pdfs/ar004.pdf +2846 -2
  53. demolab_cli/scaffold/demo/artifacts/pdfs/ar005.pdf +4216 -5
  54. demolab_cli/scaffold/demo/artifacts/pdfs/ar006.pdf +1200 -0
  55. demolab_cli/scaffold/demo/artifacts/pdfs/ar010.pdf +718 -0
  56. demolab_cli/scaffold/demo/artifacts/pdfs/ar011.pdf +530 -0
  57. demolab_cli/scaffold/demo/artifacts/pdfs/ar012.pdf +634 -1
  58. demolab_cli/scaffold/demo/artifacts/pdfs/ar013.pdf +575 -1
  59. demolab_cli/scaffold/demo/artifacts/pdfs/ar014.pdf +0 -0
  60. demolab_cli/scaffold/demo/artifacts/pdfs/ar015.pdf +0 -0
  61. demolab_cli/scaffold/demo/artifacts/pdfs/ar016.pdf +520 -0
  62. demolab_cli/scaffold/demo/artifacts/pdfs/ar017.pdf +875 -1
  63. demolab_cli/scaffold/demo/artifacts/pdfs/ar018.pdf +871 -1
  64. demolab_cli/scaffold/demo/artifacts/pdfs/book.pdf +10343 -6
  65. demolab_cli/scaffold/demo/artifacts/pdfs/exp000.pdf +2036 -4
  66. demolab_cli/scaffold/demo/artifacts/pdfs/exp001.pdf +2226 -2
  67. demolab_cli/scaffold/demo/artifacts/pdfs/exp002.pdf +683 -0
  68. demolab_cli/scaffold/demo/artifacts/pdfs/exp003.pdf +767 -0
  69. demolab_cli/scaffold/demo/demolab.yaml +26 -0
  70. demolab_cli/scaffold/demo/experiments/exp000.py +106 -0
  71. demolab_cli/scaffold/demo/experiments/exp001.py +106 -0
  72. demolab_cli/scaffold/demo/experiments/exp002.py +74 -0
  73. demolab_cli/scaffold/demo/experiments/exp003.py +73 -0
  74. demolab_cli/scaffold/demo/experiments/playground.py +157 -0
  75. demolab_cli/scaffold/demo/site/CNAME +1 -0
  76. demolab_cli/scaffold/demo/site/landing.typ +38 -0
  77. demolab_cli/scaffold/demo/tools/mujoco/test_mujoco.py +42 -0
  78. demolab_cli/scaffold/demo/tools/mujoco/tool.py +393 -0
  79. demolab_cli/scaffold/demo/tools/neuron/test_neuron.py +106 -0
  80. demolab_cli/scaffold/demo/tools/neuron/tool.py +526 -0
  81. demolab_cli/scaffold/demo/writings/ar000.typ +86 -0
  82. demolab_cli/scaffold/demo/writings/ar003.typ +82 -0
  83. demolab_cli/scaffold/demo/writings/ar004.slide.typ +418 -0
  84. demolab_cli/scaffold/demo/writings/ar005.slide.typ +294 -0
  85. demolab_cli/scaffold/demo/writings/ar006.typ +148 -0
  86. demolab_cli/scaffold/demo/writings/ar010.typ +54 -0
  87. demolab_cli/scaffold/demo/writings/ar011.typ +32 -0
  88. demolab_cli/scaffold/demo/writings/ar012.typ +52 -0
  89. demolab_cli/scaffold/demo/writings/ar013.typ +47 -0
  90. demolab_cli/scaffold/demo/writings/ar014.typ +56 -0
  91. demolab_cli/scaffold/demo/writings/ar015.typ +33 -0
  92. demolab_cli/scaffold/demo/writings/ar016.typ +29 -0
  93. demolab_cli/scaffold/demo/writings/ar017.typ +98 -0
  94. demolab_cli/scaffold/demo/writings/ar018.typ +128 -0
  95. demolab_cli/scaffold/demo/writings/exp000.typ +77 -0
  96. demolab_cli/scaffold/demo/writings/exp001.typ +81 -0
  97. demolab_cli/scaffold/demo/writings/exp002.typ +47 -0
  98. demolab_cli/scaffold/demo/writings/exp003.typ +61 -0
  99. demolab_cli/scaffold/demo-manifest.json +54 -0
  100. demolab_cli/scaffold/root/AGENTS.md +54 -0
  101. demolab_cli/scaffold/root/CLAUDE.md +18 -0
  102. demolab_cli/scaffold/root/README.md +28 -0
  103. demolab_cli/scaffold/root/github/workflows/tests.yml +21 -0
  104. demolab_cli/scaffold/root/gitignore +30 -0
  105. demolab_cli/scaffold/root/pyproject.toml +32 -0
  106. demolab_cli/scaffold/skeleton/HOUSESTYLE.local.md +25 -0
  107. demolab_cli/scaffold/skeleton/artifacts/data/.gitkeep +0 -0
  108. demolab_cli/scaffold/skeleton/artifacts/pdfs/.gitkeep +0 -0
  109. demolab_cli/scaffold/skeleton/demolab.yaml +40 -0
  110. demolab_cli/scaffold/skeleton/experiments/helpers/__init__.py +1 -0
  111. demolab_cli/scaffold/skeleton/experiments/helpers/provenance.py +78 -0
  112. demolab_cli/scaffold/skeleton/experiments/helpers/style.py +39 -0
  113. demolab_cli/scaffold/skeleton/experiments/helpers/test_provenance.py +37 -0
  114. demolab_cli/scaffold/skeleton/tools/.gitkeep +0 -0
  115. demolab_cli/scaffold/skeleton/writings/.gitkeep +0 -0
  116. demolab_cli/scaffold/starters/monte-carlo-pi/README.md +41 -0
  117. demolab_cli/scaffold/starters/monte-carlo-pi/exp000.py +110 -0
  118. demolab_cli/scaffold/starters/monte-carlo-pi/exp000.typ +63 -0
  119. demolab_cli/slides.py +42 -0
  120. demolab_cli/typ/cite-popover.js +38 -0
  121. demolab_cli/typ/favicon.svg +8 -0
  122. demolab_cli/typ/lib.typ +518 -0
  123. demolab_cli/typ/main.typ +92 -0
  124. demolab_cli/typ/style.css +263 -0
  125. demolab_cli-1.0.0.dist-info/METADATA +101 -0
  126. demolab_cli-1.0.0.dist-info/RECORD +129 -0
  127. demolab_cli-1.0.0.dist-info/WHEEL +4 -0
  128. demolab_cli-1.0.0.dist-info/entry_points.txt +3 -0
  129. demolab_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,445 @@
1
+ # Changelog
2
+
3
+ Notable changes to the **demolab engine** (the `demolab-cli` package). Format follows
4
+ [Keep a Changelog](https://keepachangelog.com); demolab uses [SemVer](https://semver.org):
5
+
6
+ - **major** — a break that may need edits to *your* content (the tool↔experiment contract, the
7
+ `meta` schema, or import paths). The UPDATE runbook flags it for review.
8
+ - **minor** — a new backward-compatible capability.
9
+ - **patch** — fixes and tweaks.
10
+
11
+ The current version lives in [`VERSION`](VERSION); `demolab version` prints it. On *"update demolab"*
12
+ the runbook shows the entries between your version and the latest.
13
+
14
+ ## [Unreleased]
15
+
16
+ ## [1.0.0] — 2026-07-11
17
+
18
+ ### Changed
19
+ - **demolab is now a normal Python package: `demolab-cli` on PyPI.** The git-clone
20
+ distribution is gone. A new lab starts with **`uvx demolab-cli init`** in an empty
21
+ directory (root files + structure + `git init` — born the user's own, no upstream history
22
+ to strip); the engine — build code, Typst templates, runbooks, guides, scaffold + demo —
23
+ lives in site-packages, and a lab contains only user content. **Updating the engine is a
24
+ dependency bump** (`uv lock --upgrade-package demolab-cli && uv sync`); `uv.lock` pins the
25
+ engine version for reproducibility. The clone-to-`/tmp` vendor-copy UPDATE flow and the
26
+ root-file "reconcile" tier are gone with it.
27
+ - **BREAKING — writings import the staged lib:** `#import "/demolab-engine/build/lib.typ"`
28
+ becomes `#import "/.demolab/lib.typ"`. The CLI materialises a tiny gitignored `.demolab/`
29
+ dir (lib.typ + web assets + a version stamp) at the lab root — Typst confines reads to the
30
+ lab tree, so these few files must live inside it; they refresh automatically whenever the
31
+ installed engine version changes. `main.typ` is staged into `temp/bundle/` per build.
32
+ **Migration for existing labs** (the UPDATE runbook drives it): add `demolab-cli` to
33
+ `pyproject.toml` (drop the old `[project.scripts]` + `force-include` blocks),
34
+ `git rm -r demolab-engine`, sed the import path in `writings/*.typ`, add `.demolab/` to
35
+ `.gitignore`, `uv sync`, rebuild.
36
+ - **Runbooks + guides are reached through the CLI.** New **`demolab docs`** command: bare, it
37
+ lists every runbook + guide with a one-liner (this menu replaces the hand-kept tables in
38
+ AGENTS.md, which is now a thin stub); `demolab docs <NAME>` prints the file's path
39
+ (`--print` cats it); `demolab docs DEMO` / `STARTERS` / `CHANGELOG` resolve the reference
40
+ data too. `HELP` in an agent chat maps to `demolab docs`.
41
+ - **New `demolab init`** lays a whole lab down in one shot (root stubs, skeleton, `.demolab/`
42
+ staging, `git init` + first commit) and refuses to run inside an existing lab or the
43
+ source repo. `demolab scaffold` remains as the non-destructive repair.
44
+ - **`demolab dev --demo` serves a materialised copy** (`temp/demo-preview/`, rebuilt fresh
45
+ each run) instead of building inside the shipped demo directory; `--landing` still
46
+ previews the marketing homepage. The dual-root `content-prefix` machinery in the build is
47
+ gone — `--root` is always the lab root.
48
+ - **User CI templates updated** (`demolab deploy-setup`): the deploy/preview workflows now
49
+ `uv sync && uv run demolab build` (the vendored build-script path no longer exists). Labs
50
+ with the old workflows should rerun `deploy-setup` after migrating.
51
+ - The launcher shim (`demolab.py` / `launcher.py`) and its hatchling `force-include` wiring
52
+ are gone — the CLI is a normal console script of the installed package.
53
+
54
+ ### Fixed
55
+ - `demo-manifest.json` was missing ar018's three paths (`writings/ar018.typ`,
56
+ `artifacts/data/ar018`, `artifacts/pdfs/ar018.pdf`), so `clear-demo-content` left them
57
+ behind; a coverage test now guards the manifest against this drifting again.
58
+
59
+ ## [0.6.0] — 2026-07-11
60
+
61
+ ### Changed
62
+ - **GETTING-STARTED roots the agent *in* the repo, and closes the parent-folder trap.** The
63
+ clone step now clones into `.` (so the agent's working directory *is* the repo root), forbids
64
+ cloning into a named subfolder and working from above it, and — since an agent can't change
65
+ its own working directory mid-session — tells it to ask the user to reopen the session inside
66
+ the repo rather than prefix every path forever. The "prefix the clone's folder name" escape
67
+ hatch is removed.
68
+ - **Agents register the collection a new experiment introduces.** GETTING-STARTED and RULES §6.5
69
+ now require adding a `label` + one-line `description` (and a `collection-order` entry) whenever
70
+ an experiment's `meta.collection` isn't yet in `demolab.yaml`. An unregistered collection still
71
+ renders, but title-cases its slug with **no description**, which reads as unfinished on the
72
+ homepage; only broader collection *curation and reordering* stays deferred.
73
+ - **`demolab` CLI replaces the go-task `Taskfile` — one fewer prerequisite.** The command runner
74
+ is now a Python console script (root `demolab.py` → `demolab-engine/build/cli.py`), installed
75
+ into the venv by `uv sync` and invoked as `demolab <command>`. The toolchain drops to just `uv`
76
+ + `typst` — no more `go-task` (and no winget `Task.Task`, which removes the Windows
77
+ PATH-restart friction). The command surface is otherwise unchanged, except experiment args lose
78
+ the `--` (`task run -- expNNN` → `demolab run expNNN`) and `task dev:demo` becomes
79
+ `demolab dev --demo`. **Migration:** after updating, run `uv sync` once to install the `demolab`
80
+ command, then use `demolab <command>` wherever you used `task <command>`.
81
+ - **`uv` + `typst` are now the user's own prerequisites — GETTING-STARTED no longer installs
82
+ them.** The onboarding flow *checks* they resolve (`uv --version`, `typst --version`) and, if
83
+ either is missing, asks the user to install it themselves and waits — the agent doesn't run
84
+ `brew`/`winget` for them. The landing page grew a Prerequisites block linking uv's and typst's
85
+ install pages so a newcomer sets the toolchain up before cloning.
86
+
87
+ ### Added
88
+ - **Semi-autonomous research programs (AUTORESEARCH).** A new flow for steered-by-day,
89
+ run-overnight research: a program is one collection with a pre-registered `plan` article
90
+ (hypothesis + kill criteria + a machine-readable experiment queue), an append-only `log`
91
+ article (decisions/anomalies, digest at top), and a night-shift contract (budgets, scope,
92
+ stop conditions). Three runbooks — **AUTORESEARCH** (start/steer a program), **PLAN** (triage
93
+ last night + queue the next pre-registered experiments), **NIGHT-SHIFT** (work the queue on a
94
+ branch, open a PR whose description is the digest) — plus the **AUTORESEARCH-RULES** guide
95
+ they cite. DOCTOR gains a one-plan-one-log / every-queued-entry-has-a-kill check; LINT exempts
96
+ the `log` from the prose rules; NEXT reads the `log`'s decision arc. Opt-in and additive:
97
+ nothing changes for a lab that doesn't create a `plan` article.
98
+ - **Per-PR site previews (`deploy.yml` rewrite + new `preview.yml`).** `demolab deploy-setup` now
99
+ installs two workflows. Production (`deploy.yml`) switched from the GitHub-Actions Pages flow
100
+ to the **branch-based** flow (build main → commit `artifacts/site` to a `gh-pages` branch, via
101
+ `JamesIves/github-pages-deploy-action`, `single-commit`, `clean-exclude: [pr-preview/, CNAME]`).
102
+ Previews (`preview.yml`, via `rossjrw/pr-preview-action`) build every PR to
103
+ `pr-preview/pr-<N>/`, post a sticky comment with the URL, and tear down on close/merge — so a
104
+ night-shift PR is a clickable doc set, not a raw diff. A new build test asserts the emitted
105
+ HTML carries no root-absolute URLs, since subpath-served previews depend on relative links.
106
+ - **Canonical Monte Carlo π starter reference (`scaffold/starters/monte-carlo-pi/`).** The
107
+ GETTING-STARTED universal-backup starter now has a concrete reference — a runner + write-up the
108
+ agent models the user's `exp000.py`/`exp000.typ` on — so the two figures come out right every
109
+ time: a scatter of the sampled points coloured inside/outside with the quarter-circle arc, and
110
+ a log-x convergence curve settling toward π. Reference-only (read, never overlaid) and outside
111
+ repo-root `writings/`, so it never builds or ships into a lab; its `README.md` states the
112
+ figure contract.
113
+
114
+ > **Migration — action required if you already publish (`.github/workflows/deploy.yml` exists).**
115
+ > The new deploy uses a `gh-pages` branch instead of the GitHub-Actions Pages source, so an
116
+ > *"update demolab"* that refreshes the template will not publish until you flip one setting.
117
+ > After updating:
118
+ > 1. Re-run `task deploy-setup` (rewrites `deploy.yml`, adds `preview.yml`), commit, push to main.
119
+ > 2. **Settings → Pages → Source: "Deploy from a branch" → `gh-pages` / `(root)`.** The first
120
+ > push to main creates the branch; until you switch the source, the site keeps serving the
121
+ > last GitHub-Actions deploy.
122
+ > 3. (Recommended) **Settings → General → Pull Requests → "Automatically delete head branches"**,
123
+ > and protect `main`.
124
+ > On a **custom domain**, your `CNAME` is preserved by `clean-exclude` — no action. A lab that
125
+ > never opted into Pages is unaffected until it runs `task deploy-setup`.
126
+
127
+ ### Changed
128
+ - **Custom landing page: `landing.typ` replaces the `welcome:` config block.** A `landing.typ`
129
+ at the repo root exporting `#let body = [...]` now renders as the homepage (web only) in
130
+ place of the collection directory, below the brand header — full Typst instead of a fixed
131
+ YAML schema. The `demolab.yaml` `welcome:` key (added in 0.5.2) is gone: `welcome-block` is
132
+ removed from `lib.typ`, and the landing-only `scaffold/demo/site/demolab.yaml` is deleted
133
+ (without `welcome:` it duplicated the demo config) — the upstream hero is now
134
+ `scaffold/demo/site/landing.typ`, copied to the root by the Pages deploy only, so it still
135
+ never lands in a user's lab. The `.welcome-*` CSS classes remain as building blocks for
136
+ landing pages. Migration: a lab that set `welcome:` (none are known to) moves the same
137
+ content into a root `landing.typ`; the key is now silently ignored.
138
+ - **GETTING-STARTED rebuilt around the user's own first experiment.** Onboarding no longer
139
+ overlays the demo into a fresh lab — the lab starts clean, and the finished example is the
140
+ landing site (built from `scaffold/demo/`, which stays as the agent's file-shape reference
141
+ and the test fixture). The flow is now three questions (ready? · what should your first
142
+ experiment be? — with field-calibrated starter suggestions for the blank-canvas case, and
143
+ code/notebook/paper as branches into the migrate runbooks · publish?) and a mandatory
144
+ **touch-it moment**: change a parameter, re-run, watch the page's figures and numbers update
145
+ with no prose touched. Branding and publishing moved after the first experiment;
146
+ fresh-vs-migrate, demo-or-clean, and stack are no longer standalone questions.
147
+ `task add-demo-content` remains as an unadvertised escape hatch.
148
+ - **`task dev:demo-site` reads and serves directly from `demolab-engine/scaffold/demo/`.** Sets
149
+ `DEMOLAB_ROOT` there (no root symlinks, no `temp/demo-site/` staging). A `content-prefix`
150
+ Typst input + `data-file()` in `lib.typ` let demo writings resolve `/artifacts/data/…` while
151
+ `--root` stays at the repo checkout for the engine.
152
+ - **The top-level `landing/` directory is gone.** `CNAME` now lives at
153
+ `demolab-engine/scaffold/demo/site/` (served at the Pages root alongside the demo).
154
+ `task add-demo-content` skips `site/` so it doesn't land in a user's lab root.
155
+
156
+ ### Fixed
157
+ - **The upstream marketing homepage no longer leaks into user labs.** `task add-demo-content`
158
+ copied `scaffold/demo/demolab.yaml` — which carried the demolab.eoinmurray.info `welcome:`
159
+ hero and `hide-directory: true` — over the lab's root config, so a fresh lab's homepage was
160
+ the upstream landing page (own collections hidden), and `task clear-demo-content` didn't undo
161
+ it. The hero now lives in a landing-only `scaffold/demo/site/demolab.yaml` (site/ is excluded
162
+ from the overlay), applied only by the Pages deploy; the demo config a user receives is
163
+ branding + demo collections. Build tests now pin both: user fixture gets no hero and a
164
+ visible directory, landing fixture gets the hero.
165
+
166
+ ### Removed
167
+ - **The one-line install scripts are retired.** `install.sh` / `install.ps1` (and their
168
+ end-to-end tests) are gone; the agent-based install is the one supported path — open a coding
169
+ agent in an empty folder and paste the GETTING-STARTED prompt. The runbook's step 2 owns the
170
+ toolchain install, including the portable `.tools/bin/` fallback for machines without a
171
+ package manager.
172
+
173
+ ## [0.5.3] — 2026-07-09
174
+
175
+ Windows portability, driven by a field report from a real Windows setup (thank you!). The flow
176
+ worked conceptually; everything below is plumbing that assumed Unix.
177
+
178
+ ### Fixed
179
+ - **`http://localhost` now works on Windows.** The dev server bound IPv4-only while printing a
180
+ `localhost` URL — and Windows resolves `localhost` to the IPv6 `::1` first, so the printed URL
181
+ was unreachable (only `127.0.0.1` worked). It now binds dual-stack, with an IPv4-only fallback
182
+ where IPv6 is absent.
183
+ - **No more Unix-only shell in tasks.** `task scaffold` / `task add-demo-content` used `rsync`
184
+ and `task slides` used `grep` + `basename` — none exist on Windows. Both are now small stdlib
185
+ Python engines (`overlay.py`, `slides.py`) with the same semantics (re-scaffold never clobbers,
186
+ the demo's prebuilt `site/` never lands in your tree).
187
+ - **Demo runners no longer use the `sh` package** (it does not support Windows) — they invoke
188
+ tools via `subprocess` + `sys.executable`, the same idiom as the playground.
189
+ - **Figures render headless.** The shared figure style forces matplotlib's `Agg` backend;
190
+ the default GUI backend broke where Tcl/Tk is absent (notably uv-managed pythons on Windows).
191
+ - **`typst` resolution.** The build prefers a repo-local `.tools/bin/typst(.exe)` — the blessed
192
+ fallback for machines with no package manager — then PATH.
193
+
194
+ ### Added
195
+ - **CI runs the test suite on Windows** (`tests.yml`, ubuntu + windows matrix), so these
196
+ regressions get caught on a real Windows runner.
197
+ - **GETTING-STARTED covers Windows**: `winget` install line, the portable `.tools/bin/` fallback,
198
+ and sandbox notes (`UV_CACHE_DIR`, repo-local temp, network approvals for dependency fetches).
199
+
200
+ ## [0.5.2] — 2026-07-08
201
+
202
+ ### Added
203
+ - **Homepage welcome block (`demolab.yaml` `welcome:`).** An optional hero on the index page —
204
+ pitch prose, quick links, install commands, and an agent prompt — rendered above the
205
+ collection directory. Absent on a normal lab; the shipped demo sets it so
206
+ demolab.eoinmurray.info is the example site with a welcome, not a separate landing page.
207
+
208
+ ### Changed
209
+ - **Upstream Pages deploy publishes the demo at `/`.** `.github/workflows/landing.yml` now uploads
210
+ `artifacts/site/` at the site root (plus `install.sh`, `install.ps1`, and `CNAME` from
211
+ `landing/`). The old `/example/` prefix and standalone `landing/index.html` are gone.
212
+
213
+ ## [0.5.1] — 2026-07-08
214
+
215
+ ### Added
216
+ - **Staged-experiment flow (RULES §7.5) + a DOCTOR check for it.** An optional, opt-in
217
+ contract for expensive runners: split the runner into `compute → analyse → plot` so a run
218
+ can re-enter at any stage (full / skip-compute / plot-only) without repeating the costly
219
+ prefix. The one boundary the contract enforces is that the **plot** stage reads only from the
220
+ committed `artifacts/data/<id>/` record — which is what makes a plot-only pass reproducible
221
+ from a clean clone with no `temp/` (§5.1, §5.3). The rule deliberately defines the *flow and
222
+ the boundary, not the mechanism*: no mandated flag names, stage harness, or function
223
+ signatures, and most runners stay one-shot. DOCTOR §3 adds a matching **behavioural** check —
224
+ re-run a staged runner's plot-only mode with `temp/` hidden and confirm the figures still
225
+ render; a pass that only fails with scratch hidden is reaching into `temp/`. Advisory unless
226
+ the experiment claims clone-and-replot, since plotting from warm `temp/` while iterating is fine.
227
+
228
+ ## [0.5.0] — 2026-07-08
229
+
230
+ ### Added
231
+ - **Experiment runners now carry provenance like tools do**, via a new
232
+ `experiments/helpers/provenance.py`. Two helpers: `stamp(config)` adds the same
233
+ `_provenance` block (git commit, `dirty` flag, UTC timestamp) that a tool's
234
+ `setup_run_dir` writes — so an **inline** runner (no tool to inherit from) produces a
235
+ `numbers.json` indistinguishable to `numbers-table` / `provenance-footer` / DOCTOR; and
236
+ `write_run_sh(ARTIFACTS)` drops a `run.sh` reproducer into the committed
237
+ `artifacts/data/<id>/` record — the committed twin of the `run.sh` tools write into
238
+ scratch `temp/`. The demo runners (`exp000`–`exp003`) now emit `run.sh`, DOCTOR flags a
239
+ record missing one, and the stamp is kept as a separate copy from `tools/*/tool.py`
240
+ because the firewall (§4.5) forbids a tool importing `experiments/`. (RULES §4.1, §4.7,
241
+ §7.2; FROM-JUPYTER step 4–5.)
242
+
243
+ ## [0.4.6] — 2026-07-07
244
+
245
+ ### Changed
246
+ - **Runbooks lead with a human overview, then a labeled `Triggers` line.** Each runbook opened with
247
+ its agent-routing `Triggers:` line, which reads as machinery to a person. Every runbook now opens
248
+ with a plain-language description of what it does and when to use it, followed by a
249
+ `**Triggers** — say any of these, or just \`NAME\`:` line. The trigger phrases are unchanged and
250
+ the step-by-step bodies are untouched, so agent routing is unaffected; the files just read as
251
+ documentation for a human too. (The guides already led with a human summary.)
252
+
253
+ ## [0.4.5] — 2026-07-07
254
+
255
+ ### Changed
256
+ - **The dev server's rebuild log is now one terse line.** `task dev` reprinted the full output paths
257
+ and every entry/deck id on *every* rebuild, so an active editing session buried the terminal in
258
+ wrapping dumps. `build.py` now prints a concise summary last
259
+ (`built 9 entries + 2 decks -> artifacts/site/`) — which is all the watch loop echoes per rebuild —
260
+ and keeps the full id list on the line above, shown only by a one-shot `task build`.
261
+
262
+ ## [0.4.4] — 2026-07-06
263
+
264
+ ### Fixed
265
+ - **Dev server hardening (round two).** Closed a path-traversal hole — the `.html` serving path read
266
+ `SITE / <request path>` directly, so a crafted `..%2f…​.html` could read files outside the served
267
+ dir; it's now confined to the site (verified a traversal request returns 404). Also bounded the
268
+ per-tab SSE queue (a stalled tab can't grow it without limit) and unified benign-disconnect
269
+ handling across every socket write.
270
+
271
+ ### Changed
272
+ - **A broken entry no longer takes down the whole site.** One entry that referenced a missing figure
273
+ (or had any Typst error) aborted the single bundle compile, so the *entire* site failed to build.
274
+ `build.py` now flags the failing entry and retries, and `main.typ` renders it as a visible stub
275
+ page at its own URL — a red "this page failed to build" notice with the error — kept out of the
276
+ listings and the book, while every other page builds. Decks that fail to compile are skipped the
277
+ same way. The build reports what it stubbed and still exits 0, so the dev server serves the good
278
+ site with the broken page visibly flagged rather than an all-or-nothing error overlay.
279
+
280
+ ## [0.4.3] — 2026-07-06
281
+
282
+ ### Fixed
283
+ - **`task dev:demo-site` previews the demo's config, not a stale root copy.** It symlinked the demo's
284
+ content but not its `demolab.yaml` / `HOUSESTYLE.local.md` (which live in the skeleton), so the
285
+ preview built against whatever config happened to sit at the root — losing branding and collection
286
+ labels/descriptions. It now symlinks those too (and tears them down on exit), so the preview
287
+ matches what ships, and editing the demo's `demolab.yaml` hot-reloads.
288
+
289
+ ## [0.4.2] — 2026-07-06
290
+
291
+ ### Fixed
292
+ - **Dev server no longer spews tracebacks or dies quietly.** A browser resetting a connection
293
+ (closing an SSE stream, reloading, navigating away) used to dump a `ConnectionResetError` stack —
294
+ harmless, but it reads as a crash. The server now swallows those benign disconnects (a custom
295
+ `handle_error`), and hardens against the genuinely bad case: the watch loop survives a transient
296
+ error instead of silently stopping all rebuilds, a hung compile times out (120s) instead of
297
+ freezing the server, and a raced port falls through to the next.
298
+
299
+ ## [0.4.1] — 2026-07-06
300
+
301
+ ### Changed
302
+ - **Command grammar: the NAME is the trigger.** `HELP` lists the runbooks + guides; a runbook's
303
+ SCREAMING-KEBAB name (`LINT`, `DOCTOR`, …) starts it, a guide's name (`RULES`, `SLIDES`, …) walks
304
+ the user through it. AGENTS.md is restructured around the three commands with the name made
305
+ primary (was "a phrase, or the name"), and the grammar is mirrored into the always-loaded
306
+ `CLAUDE.md` so a bare name routes without the agent having to have read AGENTS.md first — the
307
+ reason a bare `LINT` misfired in older repos. `test_command_catalog.py` keeps the AGENTS tables in
308
+ step with the actual runbook/guide files.
309
+
310
+ ## [0.4.0] — 2026-07-06
311
+
312
+ ### Added
313
+ - **Slide layouts are a named, liftable catalog.** Each layout is a block in the gallery deck
314
+ (`ar005.slide.typ`) marked `// layout: <name>`; SLIDES.md D11 is the index (name → when-to-use), and
315
+ you build a slide by copying the named block out of the gallery rather than from a Typst component
316
+ library. One tested source of truth — `test_slide_catalog.py` asserts the gallery's marker names
317
+ match the D11 catalog, so the two can't drift.
318
+ - **`task dev:demo-site`.** Serves the shipped demo (`demolab-engine/scaffold/demo/`) through the
319
+ live engine by symlinking its content into the root — one source of truth, no duplicated files —
320
+ and tears the links down on exit. Handy for previewing the reference lab or developing engine
321
+ features against it without a full `add-demo-content` sandbox.
322
+ - **Demo ships the layout-gallery deck (`ar005`).** `SLIDES.md` D12 points authors at a gallery
323
+ deck to copy layouts from; the demo now actually includes it — one slide per D11 layout, driven by
324
+ real run data. Expanded to cover five more layouts (three-column, quote, section divider, big
325
+ number, diagram) and to match demolab's two-ink web palette instead of touying's teal accent (the
326
+ D4 skeleton now carries the palette recipe). The slide's title labels its layout (no separate tag).
327
+ Decks now default to `header: none` — touying's running section header just reprinted the title on
328
+ every slide; turn it back on for a long, multi-section talk. Slide content is also vertically
329
+ centred by default (`#set align(horizon)`) — adaptive, so sparse slides balance and full ones keep
330
+ their title near the top.
331
+
332
+ ## [0.3.0] — 2026-07-06
333
+
334
+ ### Changed
335
+ - **New dev server (`task dev`).** Replaced `typst watch`'s built-in server with a small Python
336
+ dev server (`demolab-engine/build/devserver.py`) that rebuilds via `build.py` on any source
337
+ change and serves the site with live-reload. It fixes two long-standing dev annoyances: a **new
338
+ entry or deck now appears without restarting** (the build re-globs the filesystem each time, which
339
+ `typst watch` couldn't), and a **failed compile shows up in the browser** as a full-screen overlay
340
+ carrying the Typst error — clearing on the next good build — instead of silently serving the stale
341
+ site with the error buried in the terminal. To keep saves snappy it skips deck recompilation
342
+ when the change touched no `.slide.typ` or data asset, so a prose/CSS/lib edit rebuilds in ~0.4s
343
+ instead of ~1.1s. Trade-off vs `typst watch`: a full bundle compile per save rather than Typst's
344
+ incremental recompile. No Node, no new dependencies.
345
+
346
+ ## [0.2.5] — 2026-07-06
347
+
348
+ ### Added
349
+ - **`guides/SLIDES.md` — deck authoring guide.** Conventions for `writings/*.slide.typ` decks,
350
+ numbered `D1–D13`: the `.slide.typ` marker and skeleton, sizing in absolute `pt` against the
351
+ 842 × 474 pt canvas (~350 pt usable under a title), per-aspect figure rows, the
352
+ silent-pagination overflow trap and the page-count check, the layout vocabulary (bullets,
353
+ two-column, code, equation + terms, four figure layouts, table, focus, closer), and the
354
+ dev-server caveat for decks created mid-`task dev`. Indexed from `AGENTS.md`, RULES §3.1,
355
+ STRUCTURE's tree, and GLOSSARY G9.
356
+ - **`pending-figure` — placeholder for an unrendered figure.** A `#pending-figure(caption: …,
357
+ note: …, ratio: …)` helper (and the `#pending` body it wraps) stands in for a figure whose asset
358
+ isn't ready yet — a re-run in flight, data not cleared for release. It numbers as a normal
359
+ "Figure N" and reserves the figure's footprint (a tinted, dashed, rounded panel with a small
360
+ framed-image mark over the muted reason) so the page doesn't reflow when the real plot lands.
361
+ Replaces the bare floating text that a missing asset used to leave on the web. RULES §6.2.
362
+ - **Entry pdf link moved inline.** On an entry page the `pdf` link now sits in the meta strip next
363
+ to the status (`ar000 · 30 May 2026 · Revising · pdf`) instead of being flexed to the right edge
364
+ of the title row.
365
+ - **Inline citations render demibold.** A `#cite(…)` (brackets included) now stands out from the
366
+ body at weight 600 on both targets. CMU ships only Roman + Bold, so the web loads a real
367
+ mid-weight face — Latin Modern Roman Demi, the CM-lineage demibold — for weight 600; it's fetched
368
+ only on pages that actually carry a citation.
369
+
370
+ ### Changed
371
+ - **Status ordering reversed.** Listings now sort `final → revising → building → draft` (settled
372
+ work first) instead of leading with work-in-progress.
373
+ - **LINT now audits the figures, not just the prose.** The lint runbook gained a Figures pass
374
+ (H10–H15): mechanical greps over the rendered assets (`artifacts/data/<id>/*.svg`, `*.png`) for
375
+ palette, white background, format, and `alt:` text, plus a **required vision pass** (§2b) that
376
+ opens each figure to check labelled axes with units, no baked-in title, legibility, aspect,
377
+ grayscale-safety, and central style. Documents the SVG trap — matplotlib writes axis labels as
378
+ glyph paths, not `<text>`, so plots can't be grep-linted — the reason agents were linting prose
379
+ and silently skipping the plots.
380
+
381
+ ## [0.2.4] — 2026-07-06
382
+
383
+ ### Fixed
384
+ - **`#cite` spacing ([#1](https://github.com/eoinmurray/demolab/issues/1)).** The inline citation
385
+ was set flush against the preceding word (`runs[2]`). The helper now owns a thin gap before the
386
+ bracket — a weak `h()` in the PDF, a `margin-left` on the web span — so authors attach `#cite`
387
+ directly to the word (`runs#cite(2)`) and the bracket keeps its space without ever orphaning onto
388
+ the next line. Documented the convention in HOUSESTYLE H24.
389
+
390
+ ## [0.2.3] — 2026-07-06
391
+
392
+ ### Added
393
+ - **Heading anchors.** Every heading on a web page now carries a slug `id` (its text lowercased,
394
+ non-alphanumerics collapsed to hyphens), so any section is directly linkable as
395
+ `entry.html#the-slug`. A quiet `#` permalink fades in on hover to grab that URL. Applies to entry
396
+ titles, section/subsection headings, and the auto-built References heading.
397
+
398
+ ## [0.2.2] — 2026-07-06
399
+
400
+ ### Fixed
401
+ - **Figure numbering restarts per entry.** The whole bundle compiles in one pass, so Typst's
402
+ global figure counter was carrying across every document — a standalone entry PDF could open at
403
+ "Figure 7". Each entry (its page + standalone PDF) now numbers figures from 1; the book keeps
404
+ numbering continuously 1…N across chapters.
405
+
406
+ ## [0.2.1] — 2026-07-06
407
+
408
+ ### Changed
409
+ - **LINT enforces the references system.** The lint runbook now flags any hand-rolled citation —
410
+ typed `[1]` brackets, a manual `== References` section, literal `doi.org` links, or author–year
411
+ cites like "(Smith 2020)" — as an H24 violation; references must go through `#cite` +
412
+ `#reference-list`. Also documented the system fully in RULES §6.6.
413
+
414
+ ## [0.2.0] — 2026-07-06
415
+
416
+ ### Changed
417
+ - **Listing layout stacked.** Entry rows now put the `id` + title on top with a quiet
418
+ `date · status · pdf` sub-line beneath the title, instead of right-aligning the meta — so long
419
+ titles wrap cleanly without orphaning the metadata. Applies to every listing (collection pages +
420
+ `all.html`).
421
+
422
+ ## [0.1.0] — 2026-07-06
423
+
424
+ Initial versioned release — the engine after its foundational build-out.
425
+
426
+ ### Added
427
+ - **Engine-only distribution.** The repo ships with no content; `task scaffold` lays down the bare
428
+ structure, `task add-demo-content` overlays a worked demo, `task clear-demo-content` removes it.
429
+ The demo lives in `demolab-engine/scaffold/` and doubles as the smoke-test fixture.
430
+ - **One-line installers** — `install.sh` (macOS/Linux) and `install.ps1` (Windows), served from the
431
+ project landing page at demolab.eoinmurray.info.
432
+ - **14 agent runbooks** — getting-started, tour, migrate-code, from-jupyter, from-paper,
433
+ migrate-stack, embed-docs, next, ground-claims, lint, doctor, red-team, steelman, update — plus a
434
+ `HELP` index, each triggerable by bare name.
435
+ - **Citations** — `#cite(...)` inline numbered cites + `#reference-list(...)` with DOI links, and
436
+ Wikipedia-style hover popovers on the web (DOIs open in a new tab).
437
+ - **Entry status** — a free-form `meta.status` (`draft`/`revising`/`final`), shown as plain text
438
+ across every listing and entry page, and driving listing order (Articles → Experiments → Slides,
439
+ then status, then id).
440
+ - **Author/contact branding** — a byline under the homepage title + `<meta name="author">`.
441
+ - **Friendly empty-state homepage** on a freshly-scaffolded repo.
442
+
443
+ ### Changed
444
+ - The figure-data format is the author's choice (CSV, JSON, `.npz`, …); only the contract files
445
+ (`config`/`output`/`manifest`/`numbers.json`) stay JSON.
demolab_cli/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,10 @@
1
+ """demolab-cli — the demolab engine as a normal Python package.
2
+
3
+ A lab is a plain directory of user content (writings/, experiments/, tools/, artifacts/,
4
+ demolab.yaml); the engine lives here, in site-packages. The CLI materialises the few files
5
+ Typst must read from inside the lab tree into a gitignored `.demolab/` staging dir, and
6
+ stages `typ/main.typ` into temp/bundle/ per build — everything else (runbooks, guides,
7
+ scaffold, demo) ships only in the wheel. `demolab init` lays a new lab down; updating is a
8
+ normal dependency bump (`uv lock --upgrade-package demolab-cli && uv sync`).
9
+ """
10
+ from ._paths import VERSION as __version__ # noqa: F401
demolab_cli/_paths.py ADDED
@@ -0,0 +1,63 @@
1
+ """Shared path model: where the package's data lives, where the user's lab is, and the
2
+ `.demolab/` staging dir that bridges the two for Typst.
3
+
4
+ The engine ships in site-packages, but typst `--root` confines all file reads to the lab
5
+ tree — so the handful of files Typst must read (lib.typ, imported by user writings; the web
6
+ assets + VERSION, read by lib.typ/main.typ) are materialised into a gitignored `.demolab/`
7
+ at the lab root, refreshed whenever the installed package version changes. Everything else
8
+ is read from the package directly.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import shutil
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ PACKAGE = Path(__file__).resolve().parent
17
+ TYP = PACKAGE / "typ"
18
+ SCAFFOLD = PACKAGE / "scaffold"
19
+ DEPLOY = PACKAGE / "deploy"
20
+ RUNBOOKS = PACKAGE / "runbooks"
21
+ GUIDES = PACKAGE / "guides"
22
+ VERSION = (PACKAGE / "VERSION").read_text().strip()
23
+
24
+ MARKER = "demolab.yaml"
25
+
26
+ # Everything stage() writes into <lab>/.demolab/ (plus the VERSION stamp).
27
+ _STAGED = ("lib.typ", "style.css", "cite-popover.js", "favicon.svg")
28
+
29
+
30
+ def find_lab_root(start: Path | None = None) -> Path | None:
31
+ """Nearest ancestor of `start` (default: cwd) holding demolab.yaml — the lab marker
32
+ `demolab init`/`demolab scaffold` write. Like git finding its root."""
33
+ start = (start or Path.cwd()).resolve()
34
+ for d in (start, *start.parents):
35
+ if (d / MARKER).is_file():
36
+ return d
37
+ return None
38
+
39
+
40
+ def require_lab_root() -> Path:
41
+ root = find_lab_root()
42
+ if root is None:
43
+ sys.exit("demolab: not inside a lab (no demolab.yaml found from here upward).\n"
44
+ " Start one with `demolab init` in an empty directory.")
45
+ return root
46
+
47
+
48
+ def stage(root: Path) -> Path:
49
+ """Materialise <root>/.demolab/ (idempotent; refreshed on version-stamp mismatch).
50
+ Returns the staging dir."""
51
+ dot = root / ".demolab"
52
+ stamp = dot / "VERSION"
53
+ prev = stamp.read_text().strip() if stamp.is_file() else None
54
+ if prev == VERSION and all((dot / n).is_file() for n in _STAGED):
55
+ return dot
56
+ dot.mkdir(exist_ok=True)
57
+ for name in _STAGED:
58
+ shutil.copy2(TYP / name, dot / name)
59
+ stamp.write_text(VERSION + "\n")
60
+ if prev and prev != VERSION:
61
+ print(f"→ engine {prev} → {VERSION} — run `demolab docs CHANGELOG --print` to see what changed",
62
+ flush=True)
63
+ return dot